Skip to content

Commit 443c1c7

Browse files
Merge pull request #23327 from Microsoft/importDotMeta
Support 'import.meta'
2 parents 8c5ad24 + 12a3e39 commit 443c1c7

18 files changed

+1146
-51
lines changed

src/compiler/checker.ts

+46-12
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ namespace ts {
6060

6161
const compilerOptions = host.getCompilerOptions();
6262
const languageVersion = getEmitScriptTarget(compilerOptions);
63-
const modulekind = getEmitModuleKind(compilerOptions);
63+
const moduleKind = getEmitModuleKind(compilerOptions);
6464
const allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions);
6565
const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");
6666
const strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes");
@@ -443,6 +443,7 @@ namespace ts {
443443
let deferredGlobalAsyncIteratorType: GenericType;
444444
let deferredGlobalAsyncIterableIteratorType: GenericType;
445445
let deferredGlobalTemplateStringsArrayType: ObjectType;
446+
let deferredGlobalImportMetaType: ObjectType;
446447
let deferredGlobalExtractSymbol: Symbol;
447448

448449
let deferredNodes: Node[];
@@ -1094,7 +1095,7 @@ namespace ts {
10941095
const declarationFile = getSourceFileOfNode(declaration);
10951096
const useFile = getSourceFileOfNode(usage);
10961097
if (declarationFile !== useFile) {
1097-
if ((modulekind && (declarationFile.externalModuleIndicator || useFile.externalModuleIndicator)) ||
1098+
if ((moduleKind && (declarationFile.externalModuleIndicator || useFile.externalModuleIndicator)) ||
10981099
(!compilerOptions.outFile && !compilerOptions.out) ||
10991100
isInTypeQuery(usage) ||
11001101
declaration.flags & NodeFlags.Ambient) {
@@ -7860,6 +7861,10 @@ namespace ts {
78607861
return deferredGlobalTemplateStringsArrayType || (deferredGlobalTemplateStringsArrayType = getGlobalType("TemplateStringsArray" as __String, /*arity*/ 0, /*reportErrors*/ true)) || emptyObjectType;
78617862
}
78627863

7864+
function getGlobalImportMetaType() {
7865+
return deferredGlobalImportMetaType || (deferredGlobalImportMetaType = getGlobalType("ImportMeta" as __String, /*arity*/ 0, /*reportErrors*/ true)) || emptyObjectType;
7866+
}
7867+
78637868
function getGlobalESSymbolConstructorSymbol(reportErrors: boolean) {
78647869
return deferredGlobalESSymbolConstructorSymbol || (deferredGlobalESSymbolConstructorSymbol = getGlobalValueSymbol("Symbol" as __String, reportErrors));
78657870
}
@@ -18905,6 +18910,17 @@ namespace ts {
1890518910

1890618911
function checkMetaProperty(node: MetaProperty) {
1890718912
checkGrammarMetaProperty(node);
18913+
18914+
if (node.keywordToken === SyntaxKind.NewKeyword) {
18915+
return checkNewTargetMetaProperty(node);
18916+
}
18917+
18918+
if (node.keywordToken === SyntaxKind.ImportKeyword) {
18919+
return checkImportMetaProperty(node);
18920+
}
18921+
}
18922+
18923+
function checkNewTargetMetaProperty(node: MetaProperty) {
1890818924
const container = getNewTargetContainer(node);
1890918925
if (!container) {
1891018926
error(node, Diagnostics.Meta_property_0_is_only_allowed_in_the_body_of_a_function_declaration_function_expression_or_constructor, "new.target");
@@ -18920,6 +18936,16 @@ namespace ts {
1892018936
}
1892118937
}
1892218938

18939+
function checkImportMetaProperty(node: MetaProperty) {
18940+
if (languageVersion < ScriptTarget.ESNext || moduleKind < ModuleKind.ESNext) {
18941+
error(node, Diagnostics.The_import_meta_meta_property_is_only_allowed_using_ESNext_for_the_target_and_module_compiler_options);
18942+
}
18943+
const file = getSourceFileOfNode(node);
18944+
Debug.assert(!!(file.flags & NodeFlags.PossiblyContainsImportMeta), "Containing file is missing import meta node flag.");
18945+
Debug.assert(!!file.externalModuleIndicator, "Containing file should be a module.");
18946+
return node.name.escapedText === "meta" ? getGlobalImportMetaType() : unknownType;
18947+
}
18948+
1892318949
function getTypeOfParameter(symbol: Symbol) {
1892418950
const type = getTypeOfSymbol(symbol);
1892518951
if (strictNullChecks) {
@@ -22542,7 +22568,7 @@ namespace ts {
2254222568

2254322569
function checkCollisionWithRequireExportsInGeneratedCode(node: Node, name: Identifier) {
2254422570
// No need to check for require or exports for ES6 modules and later
22545-
if (modulekind >= ModuleKind.ES2015 || compilerOptions.noEmit) {
22571+
if (moduleKind >= ModuleKind.ES2015 || compilerOptions.noEmit) {
2254622572
return;
2254722573
}
2254822574

@@ -24738,7 +24764,7 @@ namespace ts {
2473824764
}
2473924765
}
2474024766
else {
24741-
if (modulekind >= ModuleKind.ES2015 && !(node.flags & NodeFlags.Ambient)) {
24767+
if (moduleKind >= ModuleKind.ES2015 && !(node.flags & NodeFlags.Ambient)) {
2474224768
// Import equals declaration is deprecated in es6 or above
2474324769
grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead);
2474424770
}
@@ -24776,7 +24802,7 @@ namespace ts {
2477624802
error(node.moduleSpecifier, Diagnostics.Module_0_uses_export_and_cannot_be_used_with_export_Asterisk, symbolToString(moduleSymbol));
2477724803
}
2477824804

24779-
if (modulekind !== ModuleKind.System && modulekind !== ModuleKind.ES2015 && modulekind !== ModuleKind.ESNext) {
24805+
if (moduleKind !== ModuleKind.System && moduleKind !== ModuleKind.ES2015 && moduleKind !== ModuleKind.ESNext) {
2478024806
checkExternalEmitHelpers(node, ExternalEmitHelpers.ExportStar);
2478124807
}
2478224808
}
@@ -24849,11 +24875,11 @@ namespace ts {
2484924875
}
2485024876

2485124877
if (node.isExportEquals && !(node.flags & NodeFlags.Ambient)) {
24852-
if (modulekind >= ModuleKind.ES2015) {
24878+
if (moduleKind >= ModuleKind.ES2015) {
2485324879
// export assignment is not supported in es6 modules
2485424880
grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_export_default_or_another_module_format_instead);
2485524881
}
24856-
else if (modulekind === ModuleKind.System) {
24882+
else if (moduleKind === ModuleKind.System) {
2485724883
// system modules does not support export assignment
2485824884
grammarErrorOnNode(node, Diagnostics.Export_assignment_is_not_supported_when_module_flag_is_system);
2485924885
}
@@ -27801,10 +27827,18 @@ namespace ts {
2780127827
}
2780227828

2780327829
function checkGrammarMetaProperty(node: MetaProperty) {
27804-
if (node.keywordToken === SyntaxKind.NewKeyword) {
27805-
if (node.name.escapedText !== "target") {
27806-
return grammarErrorOnNode(node.name, Diagnostics._0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2, node.name.escapedText, tokenToString(node.keywordToken), "target");
27807-
}
27830+
const escapedText = node.name.escapedText;
27831+
switch (node.keywordToken) {
27832+
case SyntaxKind.NewKeyword:
27833+
if (escapedText !== "target") {
27834+
return grammarErrorOnNode(node.name, Diagnostics._0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2, node.name.escapedText, tokenToString(node.keywordToken), "target");
27835+
}
27836+
break;
27837+
case SyntaxKind.ImportKeyword:
27838+
if (escapedText !== "meta") {
27839+
return grammarErrorOnNode(node.name, Diagnostics._0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2, node.name.escapedText, tokenToString(node.keywordToken), "meta");
27840+
}
27841+
break;
2780827842
}
2780927843
}
2781027844

@@ -28003,7 +28037,7 @@ namespace ts {
2800328037
}
2800428038

2800528039
function checkGrammarImportCallExpression(node: ImportCall): boolean {
28006-
if (modulekind === ModuleKind.ES2015) {
28040+
if (moduleKind === ModuleKind.ES2015) {
2800728041
return grammarErrorOnNode(node, Diagnostics.Dynamic_import_cannot_be_used_when_targeting_ECMAScript_2015_modules);
2800828042
}
2800928043

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,10 @@
971971
"category": "Error",
972972
"code": 1342
973973
},
974+
"The 'import.meta' meta-property is only allowed using 'ESNext' for the 'target' and 'module' compiler options.": {
975+
"category": "Error",
976+
"code": 1343
977+
},
974978

975979
"Duplicate identifier '{0}'.": {
976980
"category": "Error",

src/compiler/parser.ts

+72-20
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ namespace ts {
540540
const newSourceFile = IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks);
541541
// Because new source file node is created, it may not have the flag PossiblyContainDynamicImport. This is the case if there is no new edit to add dynamic import.
542542
// We will manually port the flag to the new source file.
543-
newSourceFile.flags |= (sourceFile.flags & NodeFlags.PossiblyContainsDynamicImport);
543+
newSourceFile.flags |= (sourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags);
544544
return newSourceFile;
545545
}
546546

@@ -2625,6 +2625,20 @@ namespace ts {
26252625
return token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken;
26262626
}
26272627

2628+
function nextTokenIsDot() {
2629+
return nextToken() === SyntaxKind.DotToken;
2630+
}
2631+
2632+
function nextTokenIsOpenParenOrLessThanOrDot() {
2633+
switch (nextToken()) {
2634+
case SyntaxKind.OpenParenToken:
2635+
case SyntaxKind.LessThanToken:
2636+
case SyntaxKind.DotToken:
2637+
return true;
2638+
}
2639+
return false;
2640+
}
2641+
26282642
function parseTypeLiteral(): TypeLiteralNode {
26292643
const node = <TypeLiteralNode>createNode(SyntaxKind.TypeLiteral);
26302644
node.members = parseObjectTypeMembers();
@@ -3093,7 +3107,7 @@ namespace ts {
30933107
case SyntaxKind.Identifier:
30943108
return true;
30953109
case SyntaxKind.ImportKeyword:
3096-
return lookAhead(nextTokenIsOpenParenOrLessThan);
3110+
return lookAhead(nextTokenIsOpenParenOrLessThanOrDot);
30973111
default:
30983112
return isIdentifier();
30993113
}
@@ -3955,14 +3969,31 @@ namespace ts {
39553969
// 3)we have a MemberExpression which either completes the LeftHandSideExpression,
39563970
// or starts the beginning of the first four CallExpression productions.
39573971
let expression: MemberExpression;
3958-
if (token() === SyntaxKind.ImportKeyword && lookAhead(nextTokenIsOpenParenOrLessThan)) {
3959-
// We don't want to eagerly consume all import keyword as import call expression so we look a head to find "("
3960-
// For example:
3961-
// var foo3 = require("subfolder
3962-
// import * as foo1 from "module-from-node
3963-
// We want this import to be a statement rather than import call expression
3964-
sourceFile.flags |= NodeFlags.PossiblyContainsDynamicImport;
3965-
expression = parseTokenNode<PrimaryExpression>();
3972+
if (token() === SyntaxKind.ImportKeyword) {
3973+
if (lookAhead(nextTokenIsOpenParenOrLessThan)) {
3974+
// We don't want to eagerly consume all import keyword as import call expression so we look ahead to find "("
3975+
// For example:
3976+
// var foo3 = require("subfolder
3977+
// import * as foo1 from "module-from-node
3978+
// We want this import to be a statement rather than import call expression
3979+
sourceFile.flags |= NodeFlags.PossiblyContainsDynamicImport;
3980+
expression = parseTokenNode<PrimaryExpression>();
3981+
}
3982+
else if (lookAhead(nextTokenIsDot)) {
3983+
// This is an 'import.*' metaproperty (i.e. 'import.meta')
3984+
const fullStart = scanner.getStartPos();
3985+
nextToken(); // advance past the 'import'
3986+
nextToken(); // advance past the dot
3987+
const node = createNode(SyntaxKind.MetaProperty, fullStart) as MetaProperty;
3988+
node.keywordToken = SyntaxKind.ImportKeyword;
3989+
node.name = parseIdentifierName();
3990+
expression = finishNode(node);
3991+
3992+
sourceFile.flags |= NodeFlags.PossiblyContainsImportMeta;
3993+
}
3994+
else {
3995+
expression = parseMemberExpressionOrHigher();
3996+
}
39663997
}
39673998
else {
39683999
expression = token() === SyntaxKind.SuperKeyword ? parseSuperExpression() : parseMemberExpressionOrHigher();
@@ -4523,7 +4554,7 @@ namespace ts {
45234554
case SyntaxKind.FunctionKeyword:
45244555
return parseFunctionExpression();
45254556
case SyntaxKind.NewKeyword:
4526-
return parseNewExpression();
4557+
return parseNewExpressionOrNewDotTarget();
45274558
case SyntaxKind.SlashToken:
45284559
case SyntaxKind.SlashEqualsToken:
45294560
if (reScanSlashToken() === SyntaxKind.RegularExpressionLiteral) {
@@ -4674,7 +4705,7 @@ namespace ts {
46744705
return isIdentifier() ? parseIdentifier() : undefined;
46754706
}
46764707

4677-
function parseNewExpression(): NewExpression | MetaProperty {
4708+
function parseNewExpressionOrNewDotTarget(): NewExpression | MetaProperty {
46784709
const fullStart = scanner.getStartPos();
46794710
parseExpected(SyntaxKind.NewKeyword);
46804711
if (parseOptional(SyntaxKind.DotToken)) {
@@ -5122,7 +5153,7 @@ namespace ts {
51225153
return true;
51235154

51245155
case SyntaxKind.ImportKeyword:
5125-
return isStartOfDeclaration() || lookAhead(nextTokenIsOpenParenOrLessThan);
5156+
return isStartOfDeclaration() || lookAhead(nextTokenIsOpenParenOrLessThanOrDot);
51265157

51275158
case SyntaxKind.ConstKeyword:
51285159
case SyntaxKind.ExportKeyword:
@@ -6108,14 +6139,35 @@ namespace ts {
61086139
}
61096140

61106141
function setExternalModuleIndicator(sourceFile: SourceFile) {
6111-
sourceFile.externalModuleIndicator = forEach(sourceFile.statements, node =>
6112-
hasModifier(node, ModifierFlags.Export)
6113-
|| node.kind === SyntaxKind.ImportEqualsDeclaration && (<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference
6114-
|| node.kind === SyntaxKind.ImportDeclaration
6115-
|| node.kind === SyntaxKind.ExportAssignment
6116-
|| node.kind === SyntaxKind.ExportDeclaration
6142+
// Try to use the first top-level import/export when available, then
6143+
// fall back to looking for an 'import.meta' somewhere in the tree if necessary.
6144+
sourceFile.externalModuleIndicator =
6145+
forEach(sourceFile.statements, isAnExternalModuleIndicatorNode) ||
6146+
getImportMetaIfNecessary(sourceFile);
6147+
}
6148+
6149+
function isAnExternalModuleIndicatorNode(node: Node) {
6150+
return hasModifier(node, ModifierFlags.Export)
6151+
|| node.kind === SyntaxKind.ImportEqualsDeclaration && (<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference
6152+
|| node.kind === SyntaxKind.ImportDeclaration
6153+
|| node.kind === SyntaxKind.ExportAssignment
6154+
|| node.kind === SyntaxKind.ExportDeclaration
61176155
? node
6118-
: undefined);
6156+
: undefined;
6157+
}
6158+
6159+
function getImportMetaIfNecessary(sourceFile: SourceFile) {
6160+
return sourceFile.flags & NodeFlags.PossiblyContainsImportMeta ?
6161+
walkTreeForExternalModuleIndicators(sourceFile) :
6162+
undefined;
6163+
}
6164+
6165+
function walkTreeForExternalModuleIndicators(node: Node): Node {
6166+
return isImportMeta(node) ? node : forEachChild(node, walkTreeForExternalModuleIndicators);
6167+
}
6168+
6169+
function isImportMeta(node: Node): boolean {
6170+
return isMetaProperty(node) && node.keywordToken === SyntaxKind.ImportKeyword && node.name.escapedText === "meta";
61196171
}
61206172

61216173
const enum ParsingContext {

src/compiler/program.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -986,7 +986,7 @@ namespace ts {
986986
// moduleAugmentations has changed
987987
oldProgram.structureIsReused = StructureIsReused.SafeModules;
988988
}
989-
if ((oldSourceFile.flags & NodeFlags.PossiblyContainsDynamicImport) !== (newSourceFile.flags & NodeFlags.PossiblyContainsDynamicImport)) {
989+
if ((oldSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags) !== (newSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags)) {
990990
// dynamicImport has changed
991991
oldProgram.structureIsReused = StructureIsReused.SafeModules;
992992
}

0 commit comments

Comments
 (0)