@@ -44,6 +44,7 @@ import ts, {
4444 InferTypeNode ,
4545 InterfaceDeclaration ,
4646 IntersectionTypeNode ,
47+ isJSDocImportTag ,
4748 JSDocImportTag ,
4849 LiteralTypeNode ,
4950 MappedTypeNode ,
@@ -54,6 +55,7 @@ import ts, {
5455 ModuleExportName ,
5556 NewExpression ,
5657 Node ,
58+ NodeArray ,
5759 NodeFactory ,
5860 ParseConfigHost ,
5961 PropertyAccessExpression ,
@@ -512,7 +514,8 @@ export class ReflectionTransformer implements CustomTransformer {
512514 */
513515 protected compiledDeclarations = new Set < Node > ( ) ;
514516
515- protected addImports : { from : Expression , identifier : Identifier } [ ] = [ ] ;
517+ protected addImports : { importDeclaration : ImportDeclaration | JSDocImportTag , identifier : Identifier } [ ] = [ ] ;
518+ protected additionalImports = new Map < ImportDeclaration | JSDocImportTag , Statement > ( ) ;
516519
517520 protected nodeConverter : NodeConverter ;
518521 protected typeChecker ?: TypeChecker ;
@@ -653,6 +656,7 @@ export class ReflectionTransformer implements CustomTransformer {
653656 if ( ( sourceFile as any ) . deepkitTransformed ) return sourceFile ;
654657 this . embedAssignType = false ;
655658 this . addImports = [ ] ;
659+ this . additionalImports . clear ( ) ;
656660
657661 const start = Date . now ( ) ;
658662 const configResolver = this . getConfigResolver ( sourceFile ) ;
@@ -961,40 +965,60 @@ export class ReflectionTransformer implements CustomTransformer {
961965
962966 if ( this . addImports . length ) {
963967 const handledIdentifier : string [ ] = [ ] ;
968+ // group by importDeclaration so that we have one `{...} per importDeclaration`
969+ const importMap = new Map < ImportDeclaration | JSDocImportTag , Identifier [ ] > ( ) ;
964970 for ( const imp of this . addImports ) {
965971 if ( handledIdentifier . includes ( getIdentifierName ( imp . identifier ) ) ) continue ;
966972 handledIdentifier . push ( getIdentifierName ( imp . identifier ) ) ;
973+ let arr = importMap . get ( imp . importDeclaration ) ;
974+ if ( ! arr ) {
975+ arr = [ ] ;
976+ importMap . set ( imp . importDeclaration , arr ) ;
977+ }
978+ arr . push ( imp . identifier ) ;
979+ }
980+
981+ for ( const [ importDeclaration , identifiers ] of importMap . entries ( ) ) {
982+ if ( this . additionalImports . has ( importDeclaration ) ) {
983+ throw new Error ( 'Internal error: additional import already exists' ) ;
984+ }
967985 if ( this . getModuleType ( ) === 'cjs' ) {
968- //var {identifier} = require('./bar')
969- const test = this . f . createIdentifier ( getIdentifierName ( imp . identifier ) ) ;
970- const variable = this . f . createVariableStatement ( undefined , this . f . createVariableDeclarationList ( [ this . f . createVariableDeclaration (
971- this . f . createObjectBindingPattern ( [ this . f . createBindingElement ( undefined , undefined , test ) ] ) ,
972- undefined , undefined ,
973- this . f . createCallExpression ( this . f . createIdentifier ( 'require' ) , undefined , [ imp . from ] ) ,
974- ) ] , NodeFlags . Const ) ) ;
986+ // var {a, b, c} = require('./bar')
987+ const varDeclaration = this . f . createVariableStatement (
988+ undefined ,
989+ this . f . createVariableDeclarationList (
990+ [ this . f . createVariableDeclaration (
991+ this . f . createObjectBindingPattern ( identifiers . map ( identifier => this . f . createBindingElement (
992+ undefined , undefined , identifier , undefined
993+ ) ) ) ,
994+ undefined ,
995+ undefined ,
996+ this . f . createCallExpression ( this . f . createIdentifier ( "require" ) , undefined , [ importDeclaration . moduleSpecifier ] )
997+ ) ] ,
998+ ts . NodeFlags . None
999+ )
1000+ ) ;
1001+
9751002 const typeDeclWithComment = addSyntheticLeadingComment (
976- variable ,
1003+ varDeclaration ,
9771004 SyntaxKind . MultiLineCommentTrivia ,
9781005 '@ts-ignore' ,
9791006 true ,
9801007 ) ;
981- newTopStatements . push ( typeDeclWithComment ) ;
1008+ this . additionalImports . set ( importDeclaration , typeDeclWithComment ) ;
9821009 } else {
983- //import {identifier} from './bar.js'
984- // import { identifier as identifier } is used to avoid automatic elision of imports (in angular builds for example)
985- // that's probably a bit unstable.
986- const specifier = this . f . createImportSpecifier ( false , undefined , imp . identifier ) ;
987- const namedImports = this . f . createNamedImports ( [ specifier ] ) ;
1010+ // import {a, b, c} from './bar.js'
1011+ const namedImports = this . f . createNamedImports ( identifiers . map ( identifier => this . f . createImportSpecifier ( false , undefined , identifier ) ) ) ;
9881012 const importStatement = this . f . createImportDeclaration ( undefined ,
989- this . f . createImportClause ( false , undefined , namedImports ) , imp . from ,
1013+ this . f . createImportClause ( false , undefined , namedImports ) , importDeclaration . moduleSpecifier ,
9901014 ) ;
9911015 const typeDeclWithComment = addSyntheticLeadingComment (
9921016 importStatement ,
9931017 SyntaxKind . MultiLineCommentTrivia ,
9941018 '@ts-ignore' ,
9951019 true ,
9961020 ) ;
997- newTopStatements . push ( typeDeclWithComment ) ;
1021+ this . additionalImports . set ( importDeclaration , typeDeclWithComment ) ;
9981022 }
9991023 }
10001024 }
@@ -1059,20 +1083,17 @@ export class ReflectionTransformer implements CustomTransformer {
10591083 ) ;
10601084 }
10611085
1062- if ( newTopStatements . length ) {
1063- // we want to keep "use strict", or "use client", etc at the very top
1064- const indexOfFirstLiteralExpression = this . sourceFile . statements . findIndex ( v => isExpressionStatement ( v ) && isStringLiteral ( v . expression ) ) ;
1065-
1066- const newStatements = indexOfFirstLiteralExpression === - 1
1067- ? [ ...newTopStatements , ...this . sourceFile . statements ]
1068- : [
1069- ...this . sourceFile . statements . slice ( 0 , indexOfFirstLiteralExpression + 1 ) ,
1070- ...newTopStatements ,
1071- ...this . sourceFile . statements . slice ( indexOfFirstLiteralExpression + 1 ) ,
1072- ] ;
1073- this . sourceFile = this . f . updateSourceFile ( this . sourceFile , newStatements ) ;
1074- // this.sourceFile = this.f.updateSourceFile(this.sourceFile, [...newTopStatements, ...this.sourceFile.statements]);
1075- }
1086+ // we want to keep "use strict", or "use client", etc at the very top
1087+ const indexOfFirstLiteralExpression = this . sourceFile . statements . findIndex ( v => isExpressionStatement ( v ) && isStringLiteral ( v . expression ) ) ;
1088+
1089+ const newStatements = indexOfFirstLiteralExpression === - 1
1090+ ? [ ...newTopStatements , ...this . attachAdditionalStatements ( this . sourceFile . statements ) ]
1091+ : [
1092+ ...this . sourceFile . statements . slice ( 0 , indexOfFirstLiteralExpression + 1 ) ,
1093+ ...newTopStatements ,
1094+ ...this . attachAdditionalStatements ( this . sourceFile . statements . slice ( indexOfFirstLiteralExpression + 1 ) ) ,
1095+ ] ;
1096+ this . sourceFile = this . f . updateSourceFile ( this . sourceFile , newStatements ) ;
10761097
10771098 // console.log(createPrinter().printNode(EmitHint.SourceFile, this.sourceFile, this.sourceFile));
10781099 const took = Date . now ( ) - start ;
@@ -1081,6 +1102,20 @@ export class ReflectionTransformer implements CustomTransformer {
10811102 return this . sourceFile ;
10821103 }
10831104
1105+ attachAdditionalStatements ( statements : NodeArray < Statement > | Statement [ ] ) : Statement [ ] {
1106+ const result : Statement [ ] = [ ] ;
1107+ for ( const statement of statements ) {
1108+ if ( isImportDeclaration ( statement ) || isJSDocImportTag ( statement ) ) {
1109+ const additional = this . additionalImports . get ( statement ) ;
1110+ if ( additional ) {
1111+ result . push ( additional ) ;
1112+ }
1113+ }
1114+ result . push ( statement ) ;
1115+ }
1116+ return result ;
1117+ }
1118+
10841119 protected getModuleType ( ) : 'cjs' | 'esm' {
10851120 if ( this . compilerOptions . module === ts . ModuleKind . Node16 || this . compilerOptions . module === ts . ModuleKind . NodeNext ) {
10861121 if ( this . sourceFile . impliedNodeFormat === ts . ModuleKind . ESNext ) {
@@ -2315,7 +2350,7 @@ export class ReflectionTransformer implements CustomTransformer {
23152350 return ;
23162351 }
23172352
2318- this . addImports . push ( { identifier : runtimeTypeName , from : resolved . importDeclaration . moduleSpecifier } ) ;
2353+ this . addImports . push ( { identifier : runtimeTypeName , importDeclaration : resolved . importDeclaration } ) ;
23192354 } else {
23202355 const reflection = this . getReflectionConfig ( declarationSourceFile ) ;
23212356 // if this is never, then its generally disabled for this file
@@ -2330,7 +2365,7 @@ export class ReflectionTransformer implements CustomTransformer {
23302365 return ;
23312366 }
23322367
2333- this . addImports . push ( { identifier : runtimeTypeName , from : resolved . importDeclaration . moduleSpecifier } ) ;
2368+ this . addImports . push ( { identifier : runtimeTypeName , importDeclaration : resolved . importDeclaration } ) ;
23342369 }
23352370 }
23362371 } else {
0 commit comments