Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions site/scripts/normalize-imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ function getImportStatementText(source: string, node: ts.ImportDeclaration): str

export function normalizeImports(source: string): string {
const sourceFile = createSourceFile('imports.tsx', source);
const directives: string[] = [];
const sideEffectImports = new Set<string>();
// Map<module, Map<identifierName, { isType, alias? }>>
const namedImports = new Map<string, Map<string, { isType: boolean; alias: string | undefined }>>();
Expand All @@ -22,6 +23,7 @@ export function normalizeImports(source: string): string {

for (const statement of sourceFile.statements) {
if (isDirectivePrologueStatement(statement)) {
directives.push(source.slice(statement.getFullStart(), statement.getEnd()).trim());
bodyStart = statement.getEnd();
continue;
}
Expand All @@ -48,7 +50,7 @@ export function normalizeImports(source: string): string {
if (!namedBindings || !ts.isNamedImports(namedBindings)) continue;

const names = namedImports.get(specifier) ?? new Map<string, { isType: boolean; alias: string | undefined }>();
const isStatementTypeOnly = Boolean(importClause.isTypeOnly);
const isStatementTypeOnly = importClause.phaseModifier === ts.SyntaxKind.TypeKeyword;

for (const element of namedBindings.elements) {
const isType = element.isTypeOnly || isStatementTypeOnly;
Expand Down Expand Up @@ -77,10 +79,11 @@ export function normalizeImports(source: string): string {
}),
];
const body = source.slice(bodyStart).replace(/^\s+/, '');
const header = [directives.join('\n'), importLines.join('\n')].filter(Boolean).join('\n\n');

if (importLines.length === 0) {
if (!header) {
return body;
}

return `${importLines.join('\n')}\n\n${body}`;
return `${header}\n\n${body}`;
}
18 changes: 18 additions & 0 deletions site/scripts/tests/normalize-imports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,24 @@ describe('normalizeImports', () => {
expect(normalizeImports(input)).toBe("import * as React from 'react';\n\nconst x = 1;");
});

it('preserves directives before normalized imports', () => {
const input = [
"'use client';",
"import { Foo } from 'mod';",
"import { Bar } from 'mod';",
'',
'const x = 1;',
].join('\n');

expect(normalizeImports(input)).toBe("'use client';\n\nimport { Foo, Bar } from 'mod';\n\nconst x = 1;");
});

it('preserves multiple directives without imports', () => {
const input = ["'use strict';", "'use client';", '', 'const x = 1;'].join('\n');

expect(normalizeImports(input)).toBe("'use strict';\n'use client';\n\nconst x = 1;");
});

it('orders: side-effect, raw, then named imports', () => {
const input = [
"import { useState } from 'react';",
Expand Down
Loading