@@ -60,6 +60,41 @@ namespace ts {
60
60
text . charCodeAt ( start + 3 ) !== CharacterCodes . slash ;
61
61
}
62
62
63
+ /*@internal */
64
+ export function isFileProbablyExternalModule ( sourceFile : SourceFile ) {
65
+ // Try to use the first top-level import/export when available, then
66
+ // fall back to looking for an 'import.meta' somewhere in the tree if necessary.
67
+ return forEach ( sourceFile . statements , isAnExternalModuleIndicatorNode ) ||
68
+ getImportMetaIfNecessary ( sourceFile ) ;
69
+ }
70
+
71
+ function isAnExternalModuleIndicatorNode ( node : Node ) {
72
+ return hasModifierOfKind ( node , SyntaxKind . ExportKeyword )
73
+ || isImportEqualsDeclaration ( node ) && isExternalModuleReference ( node . moduleReference )
74
+ || isImportDeclaration ( node )
75
+ || isExportAssignment ( node )
76
+ || isExportDeclaration ( node ) ? node : undefined ;
77
+ }
78
+
79
+ function getImportMetaIfNecessary ( sourceFile : SourceFile ) {
80
+ return sourceFile . flags & NodeFlags . PossiblyContainsImportMeta ?
81
+ walkTreeForImportMeta ( sourceFile ) :
82
+ undefined ;
83
+ }
84
+
85
+ function walkTreeForImportMeta ( node : Node ) : Node | undefined {
86
+ return isImportMeta ( node ) ? node : forEachChild ( node , walkTreeForImportMeta ) ;
87
+ }
88
+
89
+ /** Do not use hasModifier inside the parser; it relies on parent pointers. Use this instead. */
90
+ function hasModifierOfKind ( node : Node , kind : SyntaxKind ) {
91
+ return some ( node . modifiers , m => m . kind === kind ) ;
92
+ }
93
+
94
+ function isImportMeta ( node : Node ) : boolean {
95
+ return isMetaProperty ( node ) && node . keywordToken === SyntaxKind . ImportKeyword && node . name . escapedText === "meta" ;
96
+ }
97
+
63
98
/**
64
99
* Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes
65
100
* stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise,
@@ -642,17 +677,46 @@ namespace ts {
642
677
}
643
678
}
644
679
645
- export function createSourceFile ( fileName : string , sourceText : string , languageVersion : ScriptTarget , setParentNodes = false , scriptKind ?: ScriptKind ) : SourceFile {
680
+ export interface CreateSourceFileOptions {
681
+ languageVersion : ScriptTarget ;
682
+ /**
683
+ * Controls the format the file is detected as - this can be derived from only the path
684
+ * and files on disk, but needs to be done with a module resolution cache in scope to be performant.
685
+ * This is usually `undefined` for compilations that do not have `moduleResolution` values of `node12` or `nodenext`.
686
+ */
687
+ impliedNodeFormat ?: ModuleKind . ESNext | ModuleKind . CommonJS ;
688
+ /**
689
+ * Controls how module-y-ness is set for the given file. Usually the result of calling
690
+ * `getSetExternalModuleIndicator` on a valid `CompilerOptions` object. If not present, the default
691
+ * check specified by `isFileProbablyExternalModule` will be used to set the field.
692
+ */
693
+ setExternalModuleIndicator ?: ( file : SourceFile ) => void ;
694
+ }
695
+
696
+ function setExternalModuleIndicator ( sourceFile : SourceFile ) {
697
+ sourceFile . externalModuleIndicator = isFileProbablyExternalModule ( sourceFile ) ;
698
+ }
699
+
700
+ export function createSourceFile ( fileName : string , sourceText : string , languageVersionOrOptions : ScriptTarget | CreateSourceFileOptions , setParentNodes = false , scriptKind ?: ScriptKind ) : SourceFile {
646
701
tracing ?. push ( tracing . Phase . Parse , "createSourceFile" , { path : fileName } , /*separateBeginAndEnd*/ true ) ;
647
702
performance . mark ( "beforeParse" ) ;
648
703
let result : SourceFile ;
649
704
650
705
perfLogger . logStartParseSourceFile ( fileName ) ;
706
+ const {
707
+ languageVersion,
708
+ setExternalModuleIndicator : overrideSetExternalModuleIndicator ,
709
+ impliedNodeFormat : format
710
+ } = typeof languageVersionOrOptions === "object" ? languageVersionOrOptions : ( { languageVersion : languageVersionOrOptions } as CreateSourceFileOptions ) ;
651
711
if ( languageVersion === ScriptTarget . JSON ) {
652
- result = Parser . parseSourceFile ( fileName , sourceText , languageVersion , /*syntaxCursor*/ undefined , setParentNodes , ScriptKind . JSON ) ;
712
+ result = Parser . parseSourceFile ( fileName , sourceText , languageVersion , /*syntaxCursor*/ undefined , setParentNodes , ScriptKind . JSON , noop ) ;
653
713
}
654
714
else {
655
- result = Parser . parseSourceFile ( fileName , sourceText , languageVersion , /*syntaxCursor*/ undefined , setParentNodes , scriptKind ) ;
715
+ const setIndicator = format === undefined ? overrideSetExternalModuleIndicator : ( file : SourceFile ) => {
716
+ file . impliedNodeFormat = format ;
717
+ return ( overrideSetExternalModuleIndicator || setExternalModuleIndicator ) ( file ) ;
718
+ } ;
719
+ result = Parser . parseSourceFile ( fileName , sourceText , languageVersion , /*syntaxCursor*/ undefined , setParentNodes , scriptKind , setIndicator ) ;
656
720
}
657
721
perfLogger . logStopParseSourceFile ( ) ;
658
722
@@ -851,7 +915,7 @@ namespace ts {
851
915
// attached to the EOF token.
852
916
let parseErrorBeforeNextFinishedNode = false ;
853
917
854
- export function parseSourceFile ( fileName : string , sourceText : string , languageVersion : ScriptTarget , syntaxCursor : IncrementalParser . SyntaxCursor | undefined , setParentNodes = false , scriptKind ?: ScriptKind ) : SourceFile {
918
+ export function parseSourceFile ( fileName : string , sourceText : string , languageVersion : ScriptTarget , syntaxCursor : IncrementalParser . SyntaxCursor | undefined , setParentNodes = false , scriptKind ?: ScriptKind , setExternalModuleIndicatorOverride ?: ( file : SourceFile ) => void ) : SourceFile {
855
919
scriptKind = ensureScriptKind ( fileName , scriptKind ) ;
856
920
if ( scriptKind === ScriptKind . JSON ) {
857
921
const result = parseJsonText ( fileName , sourceText , languageVersion , syntaxCursor , setParentNodes ) ;
@@ -867,7 +931,7 @@ namespace ts {
867
931
868
932
initializeState ( fileName , sourceText , languageVersion , syntaxCursor , scriptKind ) ;
869
933
870
- const result = parseSourceFileWorker ( languageVersion , setParentNodes , scriptKind ) ;
934
+ const result = parseSourceFileWorker ( languageVersion , setParentNodes , scriptKind , setExternalModuleIndicatorOverride || setExternalModuleIndicator ) ;
871
935
872
936
clearState ( ) ;
873
937
@@ -955,7 +1019,7 @@ namespace ts {
955
1019
}
956
1020
957
1021
// Set source file so that errors will be reported with this file name
958
- const sourceFile = createSourceFile ( fileName , ScriptTarget . ES2015 , ScriptKind . JSON , /*isDeclaration*/ false , statements , endOfFileToken , sourceFlags ) ;
1022
+ const sourceFile = createSourceFile ( fileName , ScriptTarget . ES2015 , ScriptKind . JSON , /*isDeclaration*/ false , statements , endOfFileToken , sourceFlags , noop ) ;
959
1023
960
1024
if ( setParentNodes ) {
961
1025
fixupParentReferences ( sourceFile ) ;
@@ -1039,7 +1103,7 @@ namespace ts {
1039
1103
topLevel = true ;
1040
1104
}
1041
1105
1042
- function parseSourceFileWorker ( languageVersion : ScriptTarget , setParentNodes : boolean , scriptKind : ScriptKind ) : SourceFile {
1106
+ function parseSourceFileWorker ( languageVersion : ScriptTarget , setParentNodes : boolean , scriptKind : ScriptKind , setExternalModuleIndicator : ( file : SourceFile ) => void ) : SourceFile {
1043
1107
const isDeclarationFile = isDeclarationFileName ( fileName ) ;
1044
1108
if ( isDeclarationFile ) {
1045
1109
contextFlags |= NodeFlags . Ambient ;
@@ -1054,7 +1118,7 @@ namespace ts {
1054
1118
Debug . assert ( token ( ) === SyntaxKind . EndOfFileToken ) ;
1055
1119
const endOfFileToken = addJSDocComment ( parseTokenNode < EndOfFileToken > ( ) ) ;
1056
1120
1057
- const sourceFile = createSourceFile ( fileName , languageVersion , scriptKind , isDeclarationFile , statements , endOfFileToken , sourceFlags ) ;
1121
+ const sourceFile = createSourceFile ( fileName , languageVersion , scriptKind , isDeclarationFile , statements , endOfFileToken , sourceFlags , setExternalModuleIndicator ) ;
1058
1122
1059
1123
// A member of ReadonlyArray<T> isn't assignable to a member of T[] (and prevents a direct cast) - but this is where we set up those members so they can be readonly in the future
1060
1124
processCommentPragmas ( sourceFile as { } as PragmaContext , sourceText ) ;
@@ -1213,28 +1277,42 @@ namespace ts {
1213
1277
setParentRecursive ( rootNode , /*incremental*/ true ) ;
1214
1278
}
1215
1279
1216
- function createSourceFile ( fileName : string , languageVersion : ScriptTarget , scriptKind : ScriptKind , isDeclarationFile : boolean , statements : readonly Statement [ ] , endOfFileToken : EndOfFileToken , flags : NodeFlags ) : SourceFile {
1280
+ function createSourceFile (
1281
+ fileName : string ,
1282
+ languageVersion : ScriptTarget ,
1283
+ scriptKind : ScriptKind ,
1284
+ isDeclarationFile : boolean ,
1285
+ statements : readonly Statement [ ] ,
1286
+ endOfFileToken : EndOfFileToken ,
1287
+ flags : NodeFlags ,
1288
+ setExternalModuleIndicator : ( sourceFile : SourceFile ) => void ) : SourceFile {
1217
1289
// code from createNode is inlined here so createNode won't have to deal with special case of creating source files
1218
1290
// this is quite rare comparing to other nodes and createNode should be as fast as possible
1219
1291
let sourceFile = factory . createSourceFile ( statements , endOfFileToken , flags ) ;
1220
1292
setTextRangePosWidth ( sourceFile , 0 , sourceText . length ) ;
1221
- setExternalModuleIndicator ( sourceFile ) ;
1293
+ setFields ( sourceFile ) ;
1222
1294
1223
1295
// If we parsed this as an external module, it may contain top-level await
1224
1296
if ( ! isDeclarationFile && isExternalModule ( sourceFile ) && sourceFile . transformFlags & TransformFlags . ContainsPossibleTopLevelAwait ) {
1225
1297
sourceFile = reparseTopLevelAwait ( sourceFile ) ;
1298
+ setFields ( sourceFile ) ;
1226
1299
}
1227
1300
1228
- sourceFile . text = sourceText ;
1229
- sourceFile . bindDiagnostics = [ ] ;
1230
- sourceFile . bindSuggestionDiagnostics = undefined ;
1231
- sourceFile . languageVersion = languageVersion ;
1232
- sourceFile . fileName = fileName ;
1233
- sourceFile . languageVariant = getLanguageVariant ( scriptKind ) ;
1234
- sourceFile . isDeclarationFile = isDeclarationFile ;
1235
- sourceFile . scriptKind = scriptKind ;
1236
-
1237
1301
return sourceFile ;
1302
+
1303
+ function setFields ( sourceFile : SourceFile ) {
1304
+ sourceFile . text = sourceText ;
1305
+ sourceFile . bindDiagnostics = [ ] ;
1306
+ sourceFile . bindSuggestionDiagnostics = undefined ;
1307
+ sourceFile . languageVersion = languageVersion ;
1308
+ sourceFile . fileName = fileName ;
1309
+ sourceFile . languageVariant = getLanguageVariant ( scriptKind ) ;
1310
+ sourceFile . isDeclarationFile = isDeclarationFile ;
1311
+ sourceFile . scriptKind = scriptKind ;
1312
+
1313
+ setExternalModuleIndicator ( sourceFile ) ;
1314
+ sourceFile . setExternalModuleIndicator = setExternalModuleIndicator ;
1315
+ }
1238
1316
}
1239
1317
1240
1318
function setContextFlag ( val : boolean , flag : NodeFlags ) {
@@ -7575,41 +7653,6 @@ namespace ts {
7575
7653
return withJSDoc ( finishNode ( node , pos ) , hasJSDoc ) ;
7576
7654
}
7577
7655
7578
- function setExternalModuleIndicator ( sourceFile : SourceFile ) {
7579
- // Try to use the first top-level import/export when available, then
7580
- // fall back to looking for an 'import.meta' somewhere in the tree if necessary.
7581
- sourceFile . externalModuleIndicator =
7582
- forEach ( sourceFile . statements , isAnExternalModuleIndicatorNode ) ||
7583
- getImportMetaIfNecessary ( sourceFile ) ;
7584
- }
7585
-
7586
- function isAnExternalModuleIndicatorNode ( node : Node ) {
7587
- return hasModifierOfKind ( node , SyntaxKind . ExportKeyword )
7588
- || isImportEqualsDeclaration ( node ) && ts . isExternalModuleReference ( node . moduleReference )
7589
- || isImportDeclaration ( node )
7590
- || isExportAssignment ( node )
7591
- || isExportDeclaration ( node ) ? node : undefined ;
7592
- }
7593
-
7594
- function getImportMetaIfNecessary ( sourceFile : SourceFile ) {
7595
- return sourceFile . flags & NodeFlags . PossiblyContainsImportMeta ?
7596
- walkTreeForExternalModuleIndicators ( sourceFile ) :
7597
- undefined ;
7598
- }
7599
-
7600
- function walkTreeForExternalModuleIndicators ( node : Node ) : Node | undefined {
7601
- return isImportMeta ( node ) ? node : forEachChild ( node , walkTreeForExternalModuleIndicators ) ;
7602
- }
7603
-
7604
- /** Do not use hasModifier inside the parser; it relies on parent pointers. Use this instead. */
7605
- function hasModifierOfKind ( node : Node , kind : SyntaxKind ) {
7606
- return some ( node . modifiers , m => m . kind === kind ) ;
7607
- }
7608
-
7609
- function isImportMeta ( node : Node ) : boolean {
7610
- return isMetaProperty ( node ) && node . keywordToken === SyntaxKind . ImportKeyword && node . name . escapedText === "meta" ;
7611
- }
7612
-
7613
7656
const enum ParsingContext {
7614
7657
SourceElements , // Elements in source file
7615
7658
BlockStatements , // Statements in block
@@ -7652,7 +7695,7 @@ namespace ts {
7652
7695
currentToken = scanner . scan ( ) ;
7653
7696
const jsDocTypeExpression = parseJSDocTypeExpression ( ) ;
7654
7697
7655
- const sourceFile = createSourceFile ( "file.js" , ScriptTarget . Latest , ScriptKind . JS , /*isDeclarationFile*/ false , [ ] , factory . createToken ( SyntaxKind . EndOfFileToken ) , NodeFlags . None ) ;
7698
+ const sourceFile = createSourceFile ( "file.js" , ScriptTarget . Latest , ScriptKind . JS , /*isDeclarationFile*/ false , [ ] , factory . createToken ( SyntaxKind . EndOfFileToken ) , NodeFlags . None , noop ) ;
7656
7699
const diagnostics = attachFileToDiagnostics ( parseDiagnostics , sourceFile ) ;
7657
7700
if ( jsDocDiagnostics ) {
7658
7701
sourceFile . jsDocDiagnostics = attachFileToDiagnostics ( jsDocDiagnostics , sourceFile ) ;
@@ -8698,7 +8741,7 @@ namespace ts {
8698
8741
if ( sourceFile . statements . length === 0 ) {
8699
8742
// If we don't have any statements in the current source file, then there's no real
8700
8743
// way to incrementally parse. So just do a full parse instead.
8701
- return Parser . parseSourceFile ( sourceFile . fileName , newText , sourceFile . languageVersion , /*syntaxCursor*/ undefined , /*setParentNodes*/ true , sourceFile . scriptKind ) ;
8744
+ return Parser . parseSourceFile ( sourceFile . fileName , newText , sourceFile . languageVersion , /*syntaxCursor*/ undefined , /*setParentNodes*/ true , sourceFile . scriptKind , sourceFile . setExternalModuleIndicator ) ;
8702
8745
}
8703
8746
8704
8747
// Make sure we're not trying to incrementally update a source file more than once. Once
@@ -8762,7 +8805,7 @@ namespace ts {
8762
8805
// inconsistent tree. Setting the parents on the new tree should be very fast. We
8763
8806
// will immediately bail out of walking any subtrees when we can see that their parents
8764
8807
// are already correct.
8765
- const result = Parser . parseSourceFile ( sourceFile . fileName , newText , sourceFile . languageVersion , syntaxCursor , /*setParentNodes*/ true , sourceFile . scriptKind ) ;
8808
+ const result = Parser . parseSourceFile ( sourceFile . fileName , newText , sourceFile . languageVersion , syntaxCursor , /*setParentNodes*/ true , sourceFile . scriptKind , sourceFile . setExternalModuleIndicator ) ;
8766
8809
result . commentDirectives = getNewCommentDirectives (
8767
8810
sourceFile . commentDirectives ,
8768
8811
result . commentDirectives ,
0 commit comments