mirror of
https://github.com/zoriya/v10.git
synced 2026-08-07 06:37:45 +00:00
140 lines
5.3 KiB
TypeScript
140 lines
5.3 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { normalizeImports } from '../normalize-imports.js';
|
|
|
|
describe('normalizeImports', () => {
|
|
it('consolidates named imports from the same module', () => {
|
|
const input = ["import { Foo } from 'mod';", "import { Bar } from 'mod';", '', 'const x = 1;'].join('\n');
|
|
|
|
expect(normalizeImports(input)).toBe("import { Foo, Bar } from 'mod';\n\nconst x = 1;");
|
|
});
|
|
|
|
it('converts statement-level import type to inline type specifiers', () => {
|
|
const input = ["import type { Poster } from 'mod';", '', 'const x = 1;'].join('\n');
|
|
|
|
expect(normalizeImports(input)).toBe("import { type Poster } from 'mod';\n\nconst x = 1;");
|
|
});
|
|
|
|
it('preserves inline type specifiers without duplicating the type keyword', () => {
|
|
const input = ["import { type CSSProperties, forwardRef, type ReactNode } from 'react';", '', 'const x = 1;'].join(
|
|
'\n'
|
|
);
|
|
|
|
expect(normalizeImports(input)).toBe(
|
|
"import { type CSSProperties, forwardRef, type ReactNode } from 'react';\n\nconst x = 1;"
|
|
);
|
|
});
|
|
|
|
it('deduplicates when same name is imported as both value and type', () => {
|
|
const input = ["import { Poster } from 'mod';", "import type { Poster } from 'mod';", '', 'const x = 1;'].join(
|
|
'\n'
|
|
);
|
|
|
|
// Value import subsumes type import
|
|
expect(normalizeImports(input)).toBe("import { Poster } from 'mod';\n\nconst x = 1;");
|
|
});
|
|
|
|
it('deduplicates when type appears before value', () => {
|
|
const input = ["import type { Poster } from 'mod';", "import { Poster } from 'mod';", '', 'const x = 1;'].join(
|
|
'\n'
|
|
);
|
|
|
|
expect(normalizeImports(input)).toBe("import { Poster } from 'mod';\n\nconst x = 1;");
|
|
});
|
|
|
|
it('keeps type when only imported as type across statements', () => {
|
|
const input = [
|
|
"import type { Poster } from 'mod';",
|
|
"import type { RenderProp } from 'mod';",
|
|
'',
|
|
'const x = 1;',
|
|
].join('\n');
|
|
|
|
expect(normalizeImports(input)).toBe("import { type Poster, type RenderProp } from 'mod';\n\nconst x = 1;");
|
|
});
|
|
|
|
it('handles mixed value and type imports from the same module', () => {
|
|
const input = ["import { Foo } from 'mod';", "import type { Bar } from 'mod';", '', 'const x = 1;'].join('\n');
|
|
|
|
expect(normalizeImports(input)).toBe("import { Foo, type Bar } from 'mod';\n\nconst x = 1;");
|
|
});
|
|
|
|
it('handles aliased imports', () => {
|
|
const input = ["import { Foo as Bar } from 'mod';", '', 'const x = 1;'].join('\n');
|
|
|
|
expect(normalizeImports(input)).toBe("import { Foo as Bar } from 'mod';\n\nconst x = 1;");
|
|
});
|
|
|
|
it('handles type aliased imports', () => {
|
|
const input = ["import type { Foo as Bar } from 'mod';", '', 'const x = 1;'].join('\n');
|
|
|
|
expect(normalizeImports(input)).toBe("import { type Foo as Bar } from 'mod';\n\nconst x = 1;");
|
|
});
|
|
|
|
it('preserves side-effect imports', () => {
|
|
const input = ["import './styles.css';", '', 'const x = 1;'].join('\n');
|
|
|
|
expect(normalizeImports(input)).toBe("import './styles.css';\n\nconst x = 1;");
|
|
});
|
|
|
|
it('preserves default imports as raw imports', () => {
|
|
const input = ["import React from 'react';", '', 'const x = 1;'].join('\n');
|
|
|
|
expect(normalizeImports(input)).toBe("import React from 'react';\n\nconst x = 1;");
|
|
});
|
|
|
|
it('preserves namespace imports as raw imports', () => {
|
|
const input = ["import * as React from 'react';", '', 'const x = 1;'].join('\n');
|
|
|
|
expect(normalizeImports(input)).toBe("import * as React from 'react';\n\nconst x = 1;");
|
|
});
|
|
|
|
it('orders: side-effect, raw, then named imports', () => {
|
|
const input = [
|
|
"import { useState } from 'react';",
|
|
"import './styles.css';",
|
|
"import React from 'react';",
|
|
'',
|
|
'const x = 1;',
|
|
].join('\n');
|
|
|
|
const result = normalizeImports(input);
|
|
const lines = result.split('\n');
|
|
expect(lines[0]).toBe("import './styles.css';");
|
|
expect(lines[1]).toBe("import React from 'react';");
|
|
expect(lines[2]).toBe("import { useState } from 'react';");
|
|
});
|
|
|
|
it('returns only body when there are no imports', () => {
|
|
expect(normalizeImports('const x = 1;')).toBe('const x = 1;');
|
|
});
|
|
|
|
it('handles the exact broken example from the issue', () => {
|
|
const input = [
|
|
"import { type CSSProperties, type ComponentProps, forwardRef, type ReactNode, isValidElement } from 'react';",
|
|
"import { Poster, Container, usePlayer, BufferingIndicator } from '@videojs/react';",
|
|
"import type { Poster, RenderProp } from '@videojs/react';",
|
|
'',
|
|
'const x = 1;',
|
|
].join('\n');
|
|
|
|
const result = normalizeImports(input);
|
|
|
|
// No duplicate type keyword
|
|
expect(result).not.toContain('type type');
|
|
// No duplicate Poster (value subsumes type)
|
|
expect(result).not.toMatch(/Poster.*type Poster|type Poster.*Poster/);
|
|
// Value imports preserved
|
|
expect(result).toContain('Poster');
|
|
expect(result).toContain('Container');
|
|
// Type-only imports preserved as type
|
|
expect(result).toContain('type RenderProp');
|
|
// Inline type specifiers preserved correctly
|
|
expect(result).toContain('type CSSProperties');
|
|
expect(result).toContain('type ComponentProps');
|
|
expect(result).toContain('type ReactNode');
|
|
// Value imports remain as values
|
|
expect(result).toContain('forwardRef');
|
|
expect(result).toContain('isValidElement');
|
|
});
|
|
});
|