@@ -347,7 +347,24 @@ namespace ts {
347
347
const currentDirectory = host . getCurrentDirectory ( ) ;
348
348
const resolveModuleNamesWorker = host . resolveModuleNames
349
349
? ( ( moduleNames : string [ ] , containingFile : string ) => host . resolveModuleNames ( moduleNames , containingFile ) )
350
- : ( ( moduleNames : string [ ] , containingFile : string ) => map ( moduleNames , moduleName => resolveModuleName ( moduleName , containingFile , options , host ) . resolvedModule ) ) ;
350
+ : ( ( moduleNames : string [ ] , containingFile : string ) => {
351
+ const resolvedModuleNames : ResolvedModule [ ] = [ ] ;
352
+ // resolveModuleName does not store any results between calls.
353
+ // lookup is a local cache to avoid resolving the same module name several times
354
+ const lookup : Map < ResolvedModule > = { } ;
355
+ for ( const moduleName of moduleNames ) {
356
+ let resolvedName : ResolvedModule ;
357
+ if ( hasProperty ( lookup , moduleName ) ) {
358
+ resolvedName = lookup [ moduleName ] ;
359
+ }
360
+ else {
361
+ resolvedName = resolveModuleName ( moduleName , containingFile , options , host ) . resolvedModule ;
362
+ lookup [ moduleName ] = resolvedName ;
363
+ }
364
+ resolvedModuleNames . push ( resolvedName ) ;
365
+ }
366
+ return resolvedModuleNames ;
367
+ } ) ;
351
368
352
369
const filesByName = createFileMap < SourceFile > ( ) ;
353
370
// stores 'filename -> file association' ignoring case
@@ -484,15 +501,25 @@ namespace ts {
484
501
return false ;
485
502
}
486
503
487
- // check imports
504
+ // check imports and module augmentations
488
505
collectExternalModuleReferences ( newSourceFile ) ;
489
506
if ( ! arrayIsEqualTo ( oldSourceFile . imports , newSourceFile . imports , moduleNameIsEqualTo ) ) {
490
507
// imports has changed
491
508
return false ;
492
509
}
510
+ if ( ! arrayIsEqualTo ( oldSourceFile . moduleAugmentations , newSourceFile . moduleAugmentations , moduleNameIsEqualTo ) ) {
511
+ // moduleAugmentations has changed
512
+ return false ;
513
+ }
493
514
494
515
if ( resolveModuleNamesWorker ) {
495
- const moduleNames = map ( newSourceFile . imports , name => name . text ) ;
516
+ const moduleNames : string [ ] = [ ] ;
517
+ for ( const moduleName of newSourceFile . imports ) {
518
+ moduleNames . push ( moduleName . text ) ;
519
+ }
520
+ for ( const moduleName of newSourceFile . moduleAugmentations ) {
521
+ moduleNames . push ( moduleName . text ) ;
522
+ }
496
523
const resolutions = resolveModuleNamesWorker ( moduleNames , getNormalizedAbsolutePath ( newSourceFile . fileName , currentDirectory ) ) ;
497
524
// ensure that module resolution results are still correct
498
525
for ( let i = 0 ; i < moduleNames . length ; ++ i ) {
@@ -887,59 +914,75 @@ namespace ts {
887
914
}
888
915
889
916
const isJavaScriptFile = isSourceFileJavaScript ( file ) ;
917
+ const isExternalModuleFile = isExternalModule ( file ) ;
890
918
891
919
let imports : LiteralExpression [ ] ;
920
+ let moduleAugmentations : LiteralExpression [ ] ;
921
+
892
922
for ( const node of file . statements ) {
893
- collect ( node , /*allowRelativeModuleNames*/ true , /*collectOnlyRequireCalls*/ false ) ;
923
+ collectModuleReferences ( node , /*inAmbientModule*/ false ) ;
924
+ if ( isJavaScriptFile ) {
925
+ collectRequireCalls ( node ) ;
926
+ }
894
927
}
895
928
896
929
file . imports = imports || emptyArray ;
930
+ file . moduleAugmentations = moduleAugmentations || emptyArray ;
897
931
898
932
return ;
899
933
900
- function collect ( node : Node , allowRelativeModuleNames : boolean , collectOnlyRequireCalls : boolean ) : void {
901
- if ( ! collectOnlyRequireCalls ) {
902
- switch ( node . kind ) {
903
- case SyntaxKind . ImportDeclaration :
904
- case SyntaxKind . ImportEqualsDeclaration :
905
- case SyntaxKind . ExportDeclaration :
906
- let moduleNameExpr = getExternalModuleName ( node ) ;
907
- if ( ! moduleNameExpr || moduleNameExpr . kind !== SyntaxKind . StringLiteral ) {
908
- break ;
909
- }
910
- if ( ! ( < LiteralExpression > moduleNameExpr ) . text ) {
911
- break ;
912
- }
934
+ function collectModuleReferences ( node : Node , inAmbientModule : boolean ) : void {
935
+ switch ( node . kind ) {
936
+ case SyntaxKind . ImportDeclaration :
937
+ case SyntaxKind . ImportEqualsDeclaration :
938
+ case SyntaxKind . ExportDeclaration :
939
+ let moduleNameExpr = getExternalModuleName ( node ) ;
940
+ if ( ! moduleNameExpr || moduleNameExpr . kind !== SyntaxKind . StringLiteral ) {
941
+ break ;
942
+ }
943
+ if ( ! ( < LiteralExpression > moduleNameExpr ) . text ) {
944
+ break ;
945
+ }
913
946
914
- if ( allowRelativeModuleNames || ! isExternalModuleNameRelative ( ( < LiteralExpression > moduleNameExpr ) . text ) ) {
915
- ( imports || ( imports = [ ] ) ) . push ( < LiteralExpression > moduleNameExpr ) ;
947
+ // TypeScript 1.0 spec (April 2014): 12.1.6
948
+ // An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
949
+ // only through top - level external module names. Relative external module names are not permitted.
950
+ if ( ! inAmbientModule || ! isExternalModuleNameRelative ( ( < LiteralExpression > moduleNameExpr ) . text ) ) {
951
+ ( imports || ( imports = [ ] ) ) . push ( < LiteralExpression > moduleNameExpr ) ;
952
+ }
953
+ break ;
954
+ case SyntaxKind . ModuleDeclaration :
955
+ if ( ( < ModuleDeclaration > node ) . name . kind === SyntaxKind . StringLiteral && ( inAmbientModule || node . flags & NodeFlags . Ambient || isDeclarationFile ( file ) ) ) {
956
+ const moduleName = < LiteralExpression > ( < ModuleDeclaration > node ) . name ;
957
+ // Ambient module declarations can be interpreted as augmentations for some existing external modules.
958
+ // This will happen in two cases:
959
+ // - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope
960
+ // - if current file is not external module then module augmentation is an ambient module declaration with non-relative module name
961
+ // immediately nested in top level ambient module declaration .
962
+ if ( isExternalModuleFile || ( inAmbientModule && ! isExternalModuleNameRelative ( moduleName . text ) ) ) {
963
+ ( moduleAugmentations || ( moduleAugmentations = [ ] ) ) . push ( moduleName ) ;
916
964
}
917
- break ;
918
- case SyntaxKind . ModuleDeclaration :
919
- if ( ( < ModuleDeclaration > node ) . name . kind === SyntaxKind . StringLiteral && ( node . flags & NodeFlags . Ambient || isDeclarationFile ( file ) ) ) {
920
- // TypeScript 1.0 spec (April 2014): 12.1.6
965
+ else if ( ! inAmbientModule ) {
921
966
// An AmbientExternalModuleDeclaration declares an external module.
922
967
// This type of declaration is permitted only in the global module.
923
968
// The StringLiteral must specify a top - level external module name.
924
969
// Relative external module names are not permitted
925
- forEachChild ( ( < ModuleDeclaration > node ) . body , node => {
926
- // TypeScript 1.0 spec (April 2014): 12.1.6
927
- // An ExternalImportDeclaration in anAmbientExternalModuleDeclaration may reference other external modules
928
- // only through top - level external module names. Relative external module names are not permitted.
929
- collect ( node , /*allowRelativeModuleNames*/ false , collectOnlyRequireCalls ) ;
930
- } ) ;
970
+
971
+ // NOTE: body of ambient module is always a module block
972
+ for ( const statement of ( < ModuleBlock > ( < ModuleDeclaration > node ) . body ) . statements ) {
973
+ collectModuleReferences ( statement , /*inAmbientModule*/ true ) ;
974
+ }
931
975
}
932
- break ;
933
- }
976
+ }
934
977
}
978
+ }
935
979
936
- if ( isJavaScriptFile ) {
937
- if ( isRequireCall ( node ) ) {
938
- ( imports || ( imports = [ ] ) ) . push ( < StringLiteral > ( < CallExpression > node ) . arguments [ 0 ] ) ;
939
- }
940
- else {
941
- forEachChild ( node , node => collect ( node , allowRelativeModuleNames , /*collectOnlyRequireCalls*/ true ) ) ;
942
- }
980
+ function collectRequireCalls ( node : Node ) : void {
981
+ if ( isRequireCall ( node ) ) {
982
+ ( imports || ( imports = [ ] ) ) . push ( < StringLiteral > ( < CallExpression > node ) . arguments [ 0 ] ) ;
983
+ }
984
+ else {
985
+ forEachChild ( node , collectRequireCalls ) ;
943
986
}
944
987
}
945
988
}
@@ -1069,14 +1112,28 @@ namespace ts {
1069
1112
1070
1113
function processImportedModules ( file : SourceFile , basePath : string ) {
1071
1114
collectExternalModuleReferences ( file ) ;
1072
- if ( file . imports . length ) {
1115
+ if ( file . imports . length || file . moduleAugmentations . length ) {
1073
1116
file . resolvedModules = { } ;
1074
- const moduleNames = map ( file . imports , name => name . text ) ;
1117
+ const moduleNames : string [ ] = [ ] ;
1118
+ for ( const name of file . imports ) {
1119
+ moduleNames . push ( name . text ) ;
1120
+ }
1121
+ for ( const name of file . moduleAugmentations ) {
1122
+ moduleNames . push ( name . text ) ;
1123
+ }
1075
1124
const resolutions = resolveModuleNamesWorker ( moduleNames , getNormalizedAbsolutePath ( file . fileName , currentDirectory ) ) ;
1076
- for ( let i = 0 ; i < file . imports . length ; ++ i ) {
1125
+ for ( let i = 0 ; i < moduleNames . length ; ++ i ) {
1077
1126
const resolution = resolutions [ i ] ;
1078
1127
setResolvedModule ( file , moduleNames [ i ] , resolution ) ;
1079
- if ( resolution && ! options . noResolve ) {
1128
+ // add file to program only if:
1129
+ // - resolution was successfull
1130
+ // - noResolve is falsy
1131
+ // - module name come from the list fo imports
1132
+ const shouldAddFile = resolution &&
1133
+ ! options . noResolve &&
1134
+ i < file . imports . length ;
1135
+
1136
+ if ( shouldAddFile ) {
1080
1137
const importedFile = findSourceFile ( resolution . resolvedFileName , toPath ( resolution . resolvedFileName , currentDirectory , getCanonicalFileName ) , /*isDefaultLib*/ false , file , skipTrivia ( file . text , file . imports [ i ] . pos ) , file . imports [ i ] . end ) ;
1081
1138
1082
1139
if ( importedFile && resolution . isExternalLibraryImport ) {
0 commit comments