mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-07 23:08:20 -06:00
Basic parsing/emitting support for 'import.meta'.
This commit is contained in:
parent
9b8670cf92
commit
9e2bbb6f8a
@ -542,7 +542,7 @@ namespace ts {
|
||||
const newSourceFile = IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks);
|
||||
// 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.
|
||||
// We will manually port the flag to the new source file.
|
||||
newSourceFile.flags |= (sourceFile.flags & NodeFlags.PossiblyContainsDynamicImport);
|
||||
newSourceFile.flags |= (sourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags);
|
||||
return newSourceFile;
|
||||
}
|
||||
|
||||
@ -2627,6 +2627,20 @@ namespace ts {
|
||||
return token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken;
|
||||
}
|
||||
|
||||
function nextTokenIsDot() {
|
||||
return nextToken() === SyntaxKind.DotToken;
|
||||
}
|
||||
|
||||
function nextTokenIsOpenParenOrLessThanOrDot() {
|
||||
switch (nextToken()) {
|
||||
case SyntaxKind.OpenParenToken:
|
||||
case SyntaxKind.LessThanToken:
|
||||
case SyntaxKind.DotToken:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function parseTypeLiteral(): TypeLiteralNode {
|
||||
const node = <TypeLiteralNode>createNode(SyntaxKind.TypeLiteral);
|
||||
node.members = parseObjectTypeMembers();
|
||||
@ -3095,7 +3109,7 @@ namespace ts {
|
||||
case SyntaxKind.Identifier:
|
||||
return true;
|
||||
case SyntaxKind.ImportKeyword:
|
||||
return lookAhead(nextTokenIsOpenParenOrLessThan);
|
||||
return lookAhead(nextTokenIsOpenParenOrLessThanOrDot);
|
||||
default:
|
||||
return isIdentifier();
|
||||
}
|
||||
@ -3957,14 +3971,31 @@ namespace ts {
|
||||
// 3)we have a MemberExpression which either completes the LeftHandSideExpression,
|
||||
// or starts the beginning of the first four CallExpression productions.
|
||||
let expression: MemberExpression;
|
||||
if (token() === SyntaxKind.ImportKeyword && lookAhead(nextTokenIsOpenParenOrLessThan)) {
|
||||
// We don't want to eagerly consume all import keyword as import call expression so we look a head to find "("
|
||||
// For example:
|
||||
// var foo3 = require("subfolder
|
||||
// import * as foo1 from "module-from-node
|
||||
// We want this import to be a statement rather than import call expression
|
||||
sourceFile.flags |= NodeFlags.PossiblyContainsDynamicImport;
|
||||
expression = parseTokenNode<PrimaryExpression>();
|
||||
if (token() === SyntaxKind.ImportKeyword) {
|
||||
if (lookAhead(nextTokenIsOpenParenOrLessThan)) {
|
||||
// We don't want to eagerly consume all import keyword as import call expression so we look ahead to find "("
|
||||
// For example:
|
||||
// var foo3 = require("subfolder
|
||||
// import * as foo1 from "module-from-node
|
||||
// We want this import to be a statement rather than import call expression
|
||||
sourceFile.flags |= NodeFlags.PossiblyContainsDynamicImport;
|
||||
expression = parseTokenNode<PrimaryExpression>();
|
||||
}
|
||||
else if (lookAhead(nextTokenIsDot)) {
|
||||
// This is an 'import.*' metaproperty (i.e. 'import.meta')
|
||||
const fullStart = scanner.getStartPos();
|
||||
nextToken(); // advance past the 'import'
|
||||
nextToken(); // advance past the dot
|
||||
const node = createNode(SyntaxKind.MetaProperty, fullStart) as MetaProperty;
|
||||
node.keywordToken = SyntaxKind.ImportKeyword;
|
||||
node.name = parseIdentifierName();
|
||||
expression = finishNode(node);
|
||||
|
||||
sourceFile.flags |= NodeFlags.PossiblyContainsImportMeta;
|
||||
}
|
||||
else {
|
||||
expression = parseMemberExpressionOrHigher();
|
||||
}
|
||||
}
|
||||
else {
|
||||
expression = token() === SyntaxKind.SuperKeyword ? parseSuperExpression() : parseMemberExpressionOrHigher();
|
||||
@ -4508,7 +4539,7 @@ namespace ts {
|
||||
case SyntaxKind.FunctionKeyword:
|
||||
return parseFunctionExpression();
|
||||
case SyntaxKind.NewKeyword:
|
||||
return parseNewExpression();
|
||||
return parseNewExpressionOrNewDotTarget();
|
||||
case SyntaxKind.SlashToken:
|
||||
case SyntaxKind.SlashEqualsToken:
|
||||
if (reScanSlashToken() === SyntaxKind.RegularExpressionLiteral) {
|
||||
@ -4659,7 +4690,7 @@ namespace ts {
|
||||
return isIdentifier() ? parseIdentifier() : undefined;
|
||||
}
|
||||
|
||||
function parseNewExpression(): NewExpression | MetaProperty {
|
||||
function parseNewExpressionOrNewDotTarget(): NewExpression | MetaProperty {
|
||||
const fullStart = scanner.getStartPos();
|
||||
parseExpected(SyntaxKind.NewKeyword);
|
||||
if (parseOptional(SyntaxKind.DotToken)) {
|
||||
@ -5093,7 +5124,7 @@ namespace ts {
|
||||
return true;
|
||||
|
||||
case SyntaxKind.ImportKeyword:
|
||||
return isStartOfDeclaration() || lookAhead(nextTokenIsOpenParenOrLessThan);
|
||||
return isStartOfDeclaration() || lookAhead(nextTokenIsOpenParenOrLessThanOrDot);
|
||||
|
||||
case SyntaxKind.ConstKeyword:
|
||||
case SyntaxKind.ExportKeyword:
|
||||
@ -6079,14 +6110,29 @@ namespace ts {
|
||||
}
|
||||
|
||||
function setExternalModuleIndicator(sourceFile: SourceFile) {
|
||||
sourceFile.externalModuleIndicator = forEach(sourceFile.statements, node =>
|
||||
hasModifier(node, ModifierFlags.Export)
|
||||
|| node.kind === SyntaxKind.ImportEqualsDeclaration && (<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference
|
||||
|| node.kind === SyntaxKind.ImportDeclaration
|
||||
|| node.kind === SyntaxKind.ExportAssignment
|
||||
|| node.kind === SyntaxKind.ExportDeclaration
|
||||
// Usually we'd like to avoid a full tree walk, but it's possible
|
||||
// that we have a deeper external module indicator (e.g. `import.meta`,
|
||||
// and possibly nested import statements in the future).
|
||||
// Ideally the first few statements will be an import/export anyway.
|
||||
sourceFile.externalModuleIndicator =
|
||||
!(sourceFile.flags & NodeFlags.PossiblyContainsImportMeta) ?
|
||||
forEach(sourceFile.statements, isAnExternalModuleIndicatorNode) :
|
||||
walkTreeForExternalModuleIndicators(sourceFile);
|
||||
}
|
||||
|
||||
function isAnExternalModuleIndicatorNode(node: Node) {
|
||||
return hasModifier(node, ModifierFlags.Export)
|
||||
|| node.kind === SyntaxKind.ImportEqualsDeclaration && (<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference
|
||||
|| node.kind === SyntaxKind.ImportDeclaration
|
||||
|| node.kind === SyntaxKind.ExportAssignment
|
||||
|| node.kind === SyntaxKind.ExportDeclaration
|
||||
|| isMetaProperty(node) && node.keywordToken === SyntaxKind.ImportKeyword && node.name.escapedText === "meta"
|
||||
? node
|
||||
: undefined);
|
||||
: undefined
|
||||
}
|
||||
|
||||
function walkTreeForExternalModuleIndicators(node: Node): Node {
|
||||
return isAnExternalModuleIndicatorNode(node) ? node : forEachChild(node, walkTreeForExternalModuleIndicators);
|
||||
}
|
||||
|
||||
const enum ParsingContext {
|
||||
|
||||
@ -989,7 +989,7 @@ namespace ts {
|
||||
// moduleAugmentations has changed
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
if ((oldSourceFile.flags & NodeFlags.PossiblyContainsDynamicImport) !== (newSourceFile.flags & NodeFlags.PossiblyContainsDynamicImport)) {
|
||||
if ((oldSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags) !== (newSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags)) {
|
||||
// dynamicImport has changed
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
@ -490,19 +490,21 @@ namespace ts {
|
||||
ThisNodeOrAnySubNodesHasError = 1 << 17, // If this node or any of its children had an error
|
||||
HasAggregatedChildData = 1 << 18, // If we've computed data from children and cached it in this node
|
||||
|
||||
// This flag will be set when the parser encounters a dynamic import expression so that module resolution
|
||||
// will not have to walk the tree if the flag is not set. However, this flag is just a approximation because
|
||||
// once it is set, the flag never gets cleared (hence why it's named "PossiblyContainsDynamicImport").
|
||||
// During editing, if dynamic import is removed, incremental parsing will *NOT* update this flag. This means that the tree will always be traversed
|
||||
// during module resolution. However, the removal operation should not occur often and in the case of the
|
||||
// These flags will be set when the parser encounters a dynamic import expression or 'import.meta' to avoid
|
||||
// walking the tree if the flags are not set. However, these flags are just a approximation
|
||||
// (hence why it's named "PossiblyContainsDynamicImport") because once set, the flags never get cleared.
|
||||
// During editing, if a dynamic import is removed, incremental parsing will *NOT* clear this flag.
|
||||
// This means that the tree will always be traversed during module resolution, or when looking for external module indicators.
|
||||
// However, the removal operation should not occur often and in the case of the
|
||||
// removal, it is likely that users will add the import anyway.
|
||||
// The advantage of this approach is its simplicity. For the case of batch compilation,
|
||||
// we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used.
|
||||
/* @internal */
|
||||
PossiblyContainsDynamicImport = 1 << 19,
|
||||
JSDoc = 1 << 20, // If node was parsed inside jsdoc
|
||||
/* @internal */ Ambient = 1 << 21, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier.
|
||||
/* @internal */ InWithStatement = 1 << 22, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`)
|
||||
/* @internal */ PossiblyContainsDynamicImport = 1 << 19,
|
||||
/* @internal */ PossiblyContainsImportMeta = 1 << 20,
|
||||
|
||||
JSDoc = 1 << 21, // If node was parsed inside jsdoc
|
||||
/* @internal */ Ambient = 1 << 22, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier.
|
||||
/* @internal */ InWithStatement = 1 << 23, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`)
|
||||
|
||||
BlockScoped = Let | Const,
|
||||
|
||||
@ -514,6 +516,11 @@ namespace ts {
|
||||
|
||||
// Exclude these flags when parsing a Type
|
||||
TypeExcludesFlags = YieldContext | AwaitContext,
|
||||
|
||||
// Represents all flags that are potentially set once and
|
||||
// never cleared on SourceFiles which get re-used in between incremental parses.
|
||||
// See the comment above on `PossiblyContainsDynamicImport` and `PossiblyContainsImportMeta`.
|
||||
/* @internal */ PermanentlySetIncrementalFlags = PossiblyContainsDynamicImport | PossiblyContainsImportMeta,
|
||||
}
|
||||
|
||||
export const enum ModifierFlags {
|
||||
@ -1754,7 +1761,7 @@ namespace ts {
|
||||
// for the same reasons we treat NewExpression as a PrimaryExpression.
|
||||
export interface MetaProperty extends PrimaryExpression {
|
||||
kind: SyntaxKind.MetaProperty;
|
||||
keywordToken: SyntaxKind.NewKeyword;
|
||||
keywordToken: SyntaxKind.NewKeyword | SyntaxKind.ImportKeyword;
|
||||
name: Identifier;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user