diff --git a/src/bundle-generator.ts b/src/bundle-generator.ts index e7afda4..313a92c 100644 --- a/src/bundle-generator.ts +++ b/src/bundle-generator.ts @@ -19,6 +19,7 @@ import { resolveIdentifier, SourceFileExport, splitTransientSymbol, + StatementRenaming, } from './helpers/typescript'; import { fixPath } from './helpers/fix-path'; @@ -182,6 +183,7 @@ export function generateDtsBundle(entries: readonly EntryPointConfig[], options: statements: [], renamedExports: [], declarationsRenaming: new Map(), + nameCollision: new Map(), }; const outputOptions: OutputOptions = entry.output || {}; @@ -199,6 +201,15 @@ export function generateDtsBundle(entries: readonly EntryPointConfig[], options: }; }; + const getStatementRenaming = (statement: ts.Statement): StatementRenaming => { + if (ts.isVariableStatement(statement)) { + return statement.declarationList.declarations.map(declaration => { + return collectionResult.declarationsRenaming.get(declaration); + }); + } + return []; + }; + const updateResultCommonParams = { isStatementUsed: (statement: ts.Statement | ts.SourceFile) => isNodeUsed( statement, @@ -249,6 +260,18 @@ export function generateDtsBundle(entries: readonly EntryPointConfig[], options: // so we have to generate some random name and then re-export it with really exported names identifierName = `__DTS_BUNDLE_GENERATOR__GENERATED_NAME$${uniqueNameCounter++}`; collectionResult.declarationsRenaming.set(resolvedDeclaration, identifierName); + } else if ( + ts.isVariableDeclaration(resolvedDeclaration) + && identifierName + ) { + const collision = collectionResult.nameCollision.get(identifierName); + if (!collision) { + collectionResult.nameCollision.set(identifierName, new Set([resolvedDeclaration])); + } else if (!collision.has(resolvedDeclaration)) { + collision.add(resolvedDeclaration); + identifierName = `${identifierName}$${collision.size}`; + collectionResult.declarationsRenaming.set(resolvedDeclaration, identifierName); + } } return identifierName; @@ -358,6 +381,7 @@ export function generateDtsBundle(entries: readonly EntryPointConfig[], options: { ...collectionResult, needStripDefaultKeywordForStatement, + getStatementRenaming, shouldStatementHasExportKeyword: (statement: ts.Statement) => { const statementExports = getExportsForStatement(rootFileExports, typeChecker, statement); @@ -440,6 +464,7 @@ interface CollectingResult { statements: ts.Statement[]; renamedExports: string[]; declarationsRenaming: Map; + nameCollision: Map>; } type NodeWithReferencedModule = ts.ExportDeclaration | ts.ModuleDeclaration | ts.ImportTypeNode | ts.ImportEqualsDeclaration | ts.ImportDeclaration; diff --git a/src/generate-output.ts b/src/generate-output.ts index d1e1951..e40903a 100644 --- a/src/generate-output.ts +++ b/src/generate-output.ts @@ -1,7 +1,12 @@ import * as ts from 'typescript'; import { packageVersion } from './helpers/package-version'; -import { getModifiers, modifiersToMap, recreateRootLevelNodeWithModifiers } from './helpers/typescript'; +import { + getModifiers, + modifiersToMap, + recreateRootLevelNodeWithModifiers, + StatementRenaming, +} from './helpers/typescript'; export interface ModuleImportsSet { defaultImports: Set; @@ -24,6 +29,7 @@ export interface NeedStripDefaultKeywordResult { export interface OutputHelpers { shouldStatementHasExportKeyword(statement: ts.Statement): boolean; + getStatementRenaming(statement: ts.Statement): StatementRenaming; needStripDefaultKeywordForStatement(statement: ts.Statement): NeedStripDefaultKeywordResult; needStripConstFromConstEnum(constEnum: ts.EnumDeclaration): boolean; needStripImportFromImportTypeNode(importType: ts.ImportTypeNode): boolean; @@ -156,8 +162,11 @@ function getStatementText(statement: ts.Statement, includeSortingValue: boolean, modifiersMap[ts.SyntaxKind.ConstKeyword] = false; } - let newName: string | undefined; + let renaming: StatementRenaming = []; + if (modifiersMap[ts.SyntaxKind.DeclareKeyword]) { + renaming = helpers.getStatementRenaming(statement); + } // strip the `default` keyword from node if (modifiersMap[ts.SyntaxKind.DefaultKeyword]) { const needStripDefaultKeywordResult = helpers.needStripDefaultKeywordForStatement(statement); @@ -169,7 +178,7 @@ function getStatementText(statement: ts.Statement, includeSortingValue: boolean, modifiersMap[ts.SyntaxKind.DeclareKeyword] = true; } - newName = needStripDefaultKeywordResult.newName; + renaming = [needStripDefaultKeywordResult.newName]; } } @@ -193,7 +202,7 @@ function getStatementText(statement: ts.Statement, includeSortingValue: boolean, modifiersMap[ts.SyntaxKind.DeclareKeyword] = true; } - return recreateRootLevelNodeWithModifiers(node, modifiersMap, newName, shouldStatementHasExportKeyword); + return recreateRootLevelNodeWithModifiers(node, modifiersMap, renaming, shouldStatementHasExportKeyword); }, } ); diff --git a/src/helpers/typescript.ts b/src/helpers/typescript.ts index 1cb1b67..c72bc1a 100644 --- a/src/helpers/typescript.ts +++ b/src/helpers/typescript.ts @@ -279,6 +279,8 @@ function getExportsForName( export type ModifiersMap = Record; +export type StatementRenaming = (string | undefined)[]; + const modifiersPriority: Partial> = { [ts.SyntaxKind.ExportKeyword]: 4, [ts.SyntaxKind.DefaultKeyword]: 3, @@ -317,8 +319,8 @@ export function modifiersMapToArray(modifiersMap: ModifiersMap): ts.Modifier[] { }); } -export function recreateRootLevelNodeWithModifiers(node: ts.Node, modifiersMap: ModifiersMap, newName?: string, keepComments: boolean = true): ts.Node { - const newNode = recreateRootLevelNodeWithModifiersImpl(node, modifiersMap, newName); +export function recreateRootLevelNodeWithModifiers(node: ts.Node, modifiersMap: ModifiersMap, renaming?: StatementRenaming, keepComments: boolean = true): ts.Node { + const newNode = recreateRootLevelNodeWithModifiersImpl(node, modifiersMap, renaming); if (keepComments) { ts.setCommentRange(newNode, ts.getCommentRange(node)); @@ -328,7 +330,7 @@ export function recreateRootLevelNodeWithModifiers(node: ts.Node, modifiersMap: } // eslint-disable-next-line complexity -function recreateRootLevelNodeWithModifiersImpl(node: ts.Node, modifiersMap: ModifiersMap, newName?: string): ts.Node { +function recreateRootLevelNodeWithModifiersImpl(node: ts.Node, modifiersMap: ModifiersMap, renaming: StatementRenaming = []): ts.Node { const modifiers = modifiersMapToArray(modifiersMap); if (ts.isArrowFunction(node)) { @@ -345,7 +347,7 @@ function recreateRootLevelNodeWithModifiersImpl(node: ts.Node, modifiersMap: Mod if (ts.isClassDeclaration(node)) { return ts.factory.createClassDeclaration( modifiers, - newName || node.name, + renaming[0] || node.name, node.typeParameters, node.heritageClauses, node.members @@ -355,7 +357,7 @@ function recreateRootLevelNodeWithModifiersImpl(node: ts.Node, modifiersMap: Mod if (ts.isClassExpression(node)) { return ts.factory.createClassExpression( modifiers, - newName || node.name, + renaming[0] || node.name, node.typeParameters, node.heritageClauses, node.members @@ -365,7 +367,7 @@ function recreateRootLevelNodeWithModifiersImpl(node: ts.Node, modifiersMap: Mod if (ts.isEnumDeclaration(node)) { return ts.factory.createEnumDeclaration( modifiers, - newName || node.name, + renaming[0] || node.name, node.members ); } @@ -392,7 +394,7 @@ function recreateRootLevelNodeWithModifiersImpl(node: ts.Node, modifiersMap: Mod return ts.factory.createFunctionDeclaration( modifiers, node.asteriskToken, - newName || node.name, + renaming[0] || node.name, node.typeParameters, node.parameters, node.type, @@ -404,7 +406,7 @@ function recreateRootLevelNodeWithModifiersImpl(node: ts.Node, modifiersMap: Mod return ts.factory.createFunctionExpression( modifiers, node.asteriskToken, - newName || node.name, + renaming[0] || node.name, node.typeParameters, node.parameters, node.type, @@ -425,7 +427,7 @@ function recreateRootLevelNodeWithModifiersImpl(node: ts.Node, modifiersMap: Mod return ts.factory.createImportEqualsDeclaration( modifiers, node.isTypeOnly, - newName || node.name, + renaming[0] || node.name, node.moduleReference ); } @@ -433,7 +435,7 @@ function recreateRootLevelNodeWithModifiersImpl(node: ts.Node, modifiersMap: Mod if (ts.isInterfaceDeclaration(node)) { return ts.factory.createInterfaceDeclaration( modifiers, - newName || node.name, + renaming[0] || node.name, node.typeParameters, node.heritageClauses, node.members @@ -452,23 +454,47 @@ function recreateRootLevelNodeWithModifiersImpl(node: ts.Node, modifiersMap: Mod if (ts.isTypeAliasDeclaration(node)) { return ts.factory.createTypeAliasDeclaration( modifiers, - newName || node.name, + renaming[0] || node.name, node.typeParameters, node.type ); } if (ts.isVariableStatement(node)) { - return ts.factory.createVariableStatement( - modifiers, - node.declarationList - ); + return createVariableStatement(node, modifiers, renaming); } throw new Error(`Unknown top-level node kind (with modifiers): ${ts.SyntaxKind[node.kind]}. If you're seeing this error, please report a bug on https://github.com/timocov/dts-bundle-generator/issues`); } +function createVariableStatement(node: ts.VariableStatement, modifiers: ts.Modifier[], renaming: StatementRenaming): ts.Node { + const renamedDeclarations = node.declarationList.declarations.map((declaration, i) => { + const name = renaming[i]; + if (!name) { + return declaration; + } + + const identifier = ts.factory.createIdentifier(name); + return ts.factory.updateVariableDeclaration( + declaration, + identifier, + declaration.exclamationToken, + declaration.type, + declaration.initializer + ); + }); + const declarationList = ts.factory.updateVariableDeclarationList( + node.declarationList, + renamedDeclarations + ); + + return ts.factory.createVariableStatement( + modifiers, + declarationList + ); +} + export function getModifiers(node: ts.Node): readonly ts.Modifier[] | undefined { if (!ts.canHaveModifiers(node)) { return undefined; diff --git a/tests/e2e/test-cases/export-default-unnamed-statement/input.ts b/tests/e2e/test-cases/export-default-unnamed-statement/input.ts index 670b06b..a4d6774 100644 --- a/tests/e2e/test-cases/export-default-unnamed-statement/input.ts +++ b/tests/e2e/test-cases/export-default-unnamed-statement/input.ts @@ -7,3 +7,7 @@ export { default as myClass1 } from './class'; export { default as myClass2 } from './class'; export { default as myClass3 } from './another-class'; export { default as myClass4 } from './another-class'; + +export { default as number } from './number'; +export { default as string } from './string'; +export { default as object } from './object'; diff --git a/tests/e2e/test-cases/export-default-unnamed-statement/number.ts b/tests/e2e/test-cases/export-default-unnamed-statement/number.ts new file mode 100644 index 0000000..7f810d3 --- /dev/null +++ b/tests/e2e/test-cases/export-default-unnamed-statement/number.ts @@ -0,0 +1 @@ +export default 0; diff --git a/tests/e2e/test-cases/export-default-unnamed-statement/object.ts b/tests/e2e/test-cases/export-default-unnamed-statement/object.ts new file mode 100644 index 0000000..81a75a9 --- /dev/null +++ b/tests/e2e/test-cases/export-default-unnamed-statement/object.ts @@ -0,0 +1,3 @@ +export default { + type: 'object', +}; \ No newline at end of file diff --git a/tests/e2e/test-cases/export-default-unnamed-statement/output.d.ts b/tests/e2e/test-cases/export-default-unnamed-statement/output.d.ts index d270232..5a099c0 100644 --- a/tests/e2e/test-cases/export-default-unnamed-statement/output.d.ts +++ b/tests/e2e/test-cases/export-default-unnamed-statement/output.d.ts @@ -6,6 +6,11 @@ declare class __DTS_BUNDLE_GENERATOR__GENERATED_NAME$3 { declare class __DTS_BUNDLE_GENERATOR__GENERATED_NAME$4 { second: number; } +declare const _default: 0; +declare const _default$2: ""; +declare const _default$3: { + type: string; +}; export { __DTS_BUNDLE_GENERATOR__GENERATED_NAME$1 as myFunc1, @@ -16,6 +21,9 @@ export { __DTS_BUNDLE_GENERATOR__GENERATED_NAME$3 as myClass2, __DTS_BUNDLE_GENERATOR__GENERATED_NAME$4 as myClass3, __DTS_BUNDLE_GENERATOR__GENERATED_NAME$4 as myClass4, + _default as number, + _default$2 as string, + _default$3 as object, }; export {}; diff --git a/tests/e2e/test-cases/export-default-unnamed-statement/string.ts b/tests/e2e/test-cases/export-default-unnamed-statement/string.ts new file mode 100644 index 0000000..08d725c --- /dev/null +++ b/tests/e2e/test-cases/export-default-unnamed-statement/string.ts @@ -0,0 +1 @@ +export default '';