diff --git a/src/bundle-generator.ts b/src/bundle-generator.ts index e33dab8..ddddba3 100644 --- a/src/bundle-generator.ts +++ b/src/bundle-generator.ts @@ -645,6 +645,7 @@ export function generateDtsBundle(entries: readonly EntryPointConfig[], options: importItem = { defaultImports: new Set(), namedImports: new Map(), + typeImports: new Map(), nsImport: null, requireImports: new Set(), reExports: new Map(), @@ -660,10 +661,14 @@ export function generateDtsBundle(entries: readonly EntryPointConfig[], options: importItem.requireImports.add(collisionsResolver.addTopLevelIdentifier(preferredLocalName)); } - function addNamedImport(importItem: ModuleImportsSet, preferredLocalName: ts.Identifier, importedIdentifier: ts.Identifier): void { + function addNamedImport(importItem: ModuleImportsSet, preferredLocalName: ts.Identifier, importedIdentifier: ts.Identifier, typeImportOrExport: boolean): void { const newLocalName = collisionsResolver.addTopLevelIdentifier(preferredLocalName); const importedName = importedIdentifier.text; - importItem.namedImports.set(newLocalName, importedName); + if (typeImportOrExport) { + importItem.typeImports.set(newLocalName, importedName); + } else { + importItem.namedImports.set(newLocalName, importedName); + } } function addReExport(importItem: ModuleImportsSet, moduleExportedName: string, reExportedName: string): void { @@ -698,7 +703,8 @@ export function generateDtsBundle(entries: readonly EntryPointConfig[], options: if (ts.isExportSpecifier(imp)) { // export { El1, El2 as ExportedName } from 'module'; - addNamedImport(importItem, imp.name, imp.propertyName || imp.name); + // export { El1, type El2 as ExportedName } from 'module'; + addNamedImport(importItem, imp.name, imp.propertyName || imp.name, ts.isTypeOnlyExportDeclaration(imp)); return; } @@ -715,8 +721,9 @@ export function generateDtsBundle(entries: readonly EntryPointConfig[], options: } if (ts.isImportSpecifier(imp)) { + // import { type ImportedType } from 'module'; // import { El1, El2 as ImportedName } from 'module'; - addNamedImport(importItem, imp.name, imp.propertyName || imp.name); + addNamedImport(importItem, imp.name, imp.propertyName || imp.name, ts.isTypeOnlyImportDeclaration(imp)); return; } diff --git a/src/generate-output.ts b/src/generate-output.ts index f1682b5..b975d13 100644 --- a/src/generate-output.ts +++ b/src/generate-output.ts @@ -7,6 +7,7 @@ export interface ModuleImportsSet { defaultImports: Set; nsImport: string | null; namedImports: Map; + typeImports: Map; requireImports: Set; reExports: Map; } @@ -328,6 +329,21 @@ function generateImports(libraryName: string, imports: ModuleImportsSet): string Array.from(imports.requireImports).sort().forEach((importName: string) => result.push(`import ${importName} = require('${libraryName}');`)); Array.from(imports.defaultImports).sort().forEach((importName: string) => result.push(`import ${importName} ${fromEnding}`)); + // For each type-only import, check if it exists in the `namedImports` map and remove it + // otherwise we might end up with a type import and a regular import in the bundle. + for (const key of imports.typeImports.keys()) { + imports.namedImports.delete(key); + } + + if (imports.typeImports.size !== 0) { + result.push(`import { type ${ + Array.from(imports.typeImports.entries()) + .map(([localName, importedName]: [string, string]) => renamedImportValue(importedName, localName)) + .sort() + .join(', type ') + } } ${fromEnding}`); + } + if (imports.namedImports.size !== 0) { result.push(`import { ${ Array.from(imports.namedImports.entries()) diff --git a/tests/e2e/test-cases/import-type-from-deps/output.d.ts b/tests/e2e/test-cases/import-type-from-deps/output.d.ts index b0e6b9f..a58bc2c 100644 --- a/tests/e2e/test-cases/import-type-from-deps/output.d.ts +++ b/tests/e2e/test-cases/import-type-from-deps/output.d.ts @@ -1,4 +1,4 @@ -import { InterfaceWithFields } from 'fake-package'; +import { type InterfaceWithFields } from 'fake-package'; export declare type FakePackageType = InterfaceWithFields | string; export type TestType = InterfaceWithFields | FakePackageType; diff --git a/tests/e2e/test-cases/preserve-type-imports/config.ts b/tests/e2e/test-cases/preserve-type-imports/config.ts new file mode 100644 index 0000000..7edf47b --- /dev/null +++ b/tests/e2e/test-cases/preserve-type-imports/config.ts @@ -0,0 +1,5 @@ +import type { TestCaseConfig } from '../test-case-config'; + +const config: TestCaseConfig = {}; + +export = config; diff --git a/tests/e2e/test-cases/preserve-type-imports/index.spec.js b/tests/e2e/test-cases/preserve-type-imports/index.spec.js new file mode 100644 index 0000000..c015c26 --- /dev/null +++ b/tests/e2e/test-cases/preserve-type-imports/index.spec.js @@ -0,0 +1 @@ +require('../run-test-case').runTestCase(__dirname); diff --git a/tests/e2e/test-cases/preserve-type-imports/input.ts b/tests/e2e/test-cases/preserve-type-imports/input.ts new file mode 100644 index 0000000..b9799a4 --- /dev/null +++ b/tests/e2e/test-cases/preserve-type-imports/input.ts @@ -0,0 +1,6 @@ +import type { Diagnostic, AffectedFileResult as RenamedImport } from "typescript"; + +export type MyType = { + value: Diagnostic; + alias: RenamedImport +}; diff --git a/tests/e2e/test-cases/preserve-type-imports/output.d.ts b/tests/e2e/test-cases/preserve-type-imports/output.d.ts new file mode 100644 index 0000000..3acdd05 --- /dev/null +++ b/tests/e2e/test-cases/preserve-type-imports/output.d.ts @@ -0,0 +1,8 @@ +import { type AffectedFileResult as RenamedImport, type Diagnostic } from 'typescript'; + +export type MyType = { + value: Diagnostic; + alias: RenamedImport; +}; + +export {}; diff --git a/tests/e2e/test-cases/preserve-type-imports/tsconfig.json b/tests/e2e/test-cases/preserve-type-imports/tsconfig.json new file mode 100644 index 0000000..7997907 --- /dev/null +++ b/tests/e2e/test-cases/preserve-type-imports/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "verbatimModuleSyntax": true + } +}