Skip to content
Permalink
main
Switch branches/tags
Go to file
…dits (#50098)

* Add test where module resolution cache is not local and hence doesnt report errors in watch mode

* Ensure module resolution cache is passed through in watch mode

* Remove unnecessary setting of impliedFormat which should anyways be done as part of create source file

* Add test for packge.json changing and modifying implied format

* Distinguish between package.json watch and affecting file location watch

* Pass in failed lookup and affected file locations for source file's implied format
Also stop creating options if we already have them

* Add diagnostic for explaining file's implied format if based on package.json

* Watch implied format dependencies for modules and schedule update on change

* For program if implied node format doesnt match create new source file. Handle implied node format in document registry
Fixes #50086

* Modify tests to show package.json being watched irrespective of folder its in

* Check file path if it can be watched before watching package.json file

* Because we are watching package.json files and failed lookups its safe to invalidate package json entries instead of clearing them out everytime program is created

* Remove todos

* Fix the incorrect merge

* Pickup PackageJsonInfo renames from #50088

* Rename
116 contributors

Users who have contributed to this file

@CyrusNajmabadi @ahejlsberg @DanielRosenwasser @sandersn @rbuckton @andy-ms @sheetalkamat @JsonFreeman @RyanCavanaugh @mhegazy @vladima @weswigham
namespace ts {
const enum SignatureFlags {
None = 0,
Yield = 1 << 0,
Await = 1 << 1,
Type = 1 << 2,
IgnoreMissingOpenBrace = 1 << 4,
JSDoc = 1 << 5,
}
const enum SpeculationKind {
TryParse,
Lookahead,
Reparse
}
let NodeConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
let TokenConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
let IdentifierConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
let PrivateIdentifierConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
let SourceFileConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node;
/**
* NOTE: You should not use this, it is only exported to support `createNode` in `~/src/deprecatedCompat/deprecations.ts`.
*/
/* @internal */
export const parseBaseNodeFactory: BaseNodeFactory = {
createBaseSourceFileNode: kind => new (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))(kind, -1, -1),
createBaseIdentifierNode: kind => new (IdentifierConstructor || (IdentifierConstructor = objectAllocator.getIdentifierConstructor()))(kind, -1, -1),
createBasePrivateIdentifierNode: kind => new (PrivateIdentifierConstructor || (PrivateIdentifierConstructor = objectAllocator.getPrivateIdentifierConstructor()))(kind, -1, -1),
createBaseTokenNode: kind => new (TokenConstructor || (TokenConstructor = objectAllocator.getTokenConstructor()))(kind, -1, -1),
createBaseNode: kind => new (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()))(kind, -1, -1),
};
/* @internal */
export const parseNodeFactory = createNodeFactory(NodeFactoryFlags.NoParenthesizerRules, parseBaseNodeFactory);
function visitNode<T>(cbNode: (node: Node) => T, node: Node | undefined): T | undefined {
return node && cbNode(node);
}
function visitNodes<T>(cbNode: (node: Node) => T, cbNodes: ((node: NodeArray<Node>) => T | undefined) | undefined, nodes: NodeArray<Node> | undefined): T | undefined {
if (nodes) {
if (cbNodes) {
return cbNodes(nodes);
}
for (const node of nodes) {
const result = cbNode(node);
if (result) {
return result;
}
}
}
}
/*@internal*/
export function isJSDocLikeText(text: string, start: number) {
return text.charCodeAt(start + 1) === CharacterCodes.asterisk &&
text.charCodeAt(start + 2) === CharacterCodes.asterisk &&
text.charCodeAt(start + 3) !== CharacterCodes.slash;
}
/*@internal*/
export function isFileProbablyExternalModule(sourceFile: SourceFile) {
// Try to use the first top-level import/export when available, then
// fall back to looking for an 'import.meta' somewhere in the tree if necessary.
return forEach(sourceFile.statements, isAnExternalModuleIndicatorNode) ||
getImportMetaIfNecessary(sourceFile);
}
function isAnExternalModuleIndicatorNode(node: Node) {
return canHaveModifiers(node) && hasModifierOfKind(node, SyntaxKind.ExportKeyword)
|| isImportEqualsDeclaration(node) && isExternalModuleReference(node.moduleReference)
|| isImportDeclaration(node)
|| isExportAssignment(node)
|| isExportDeclaration(node) ? node : undefined;
}
function getImportMetaIfNecessary(sourceFile: SourceFile) {
return sourceFile.flags & NodeFlags.PossiblyContainsImportMeta ?
walkTreeForImportMeta(sourceFile) :
undefined;
}
function walkTreeForImportMeta(node: Node): Node | undefined {
return isImportMeta(node) ? node : forEachChild(node, walkTreeForImportMeta);
}
/** Do not use hasModifier inside the parser; it relies on parent pointers. Use this instead. */
function hasModifierOfKind(node: HasModifiers, kind: SyntaxKind) {
return some(node.modifiers, m => m.kind === kind);
}
function isImportMeta(node: Node): boolean {
return isMetaProperty(node) && node.keywordToken === SyntaxKind.ImportKeyword && node.name.escapedText === "meta";
}
/**
* Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes
* stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise,
* embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns
* a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned.
*
* @param node a given node to visit its children
* @param cbNode a callback to be invoked for all child nodes
* @param cbNodes a callback to be invoked for embedded array
*
* @remarks `forEachChild` must visit the children of a node in the order
* that they appear in the source code. The language service depends on this property to locate nodes by position.
*/
export function forEachChild<T>(node: Node, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray<Node>) => T | undefined): T | undefined {
if (!node || node.kind <= SyntaxKind.LastToken) {
return;
}
switch (node.kind) {
case SyntaxKind.QualifiedName:
return visitNode(cbNode, (node as QualifiedName).left) ||
visitNode(cbNode, (node as QualifiedName).right);
case SyntaxKind.TypeParameter:
return visitNodes(cbNode, cbNodes, (node as TypeParameterDeclaration).modifiers) ||
visitNode(cbNode, (node as TypeParameterDeclaration).name) ||
visitNode(cbNode, (node as TypeParameterDeclaration).constraint) ||
visitNode(cbNode, (node as TypeParameterDeclaration).default) ||
visitNode(cbNode, (node as TypeParameterDeclaration).expression);
case SyntaxKind.ShorthandPropertyAssignment:
return visitNodes(cbNode, cbNodes, (node as ShorthandPropertyAssignment).decorators) ||
visitNodes(cbNode, cbNodes, (node as ShorthandPropertyAssignment).modifiers) ||
visitNode(cbNode, (node as ShorthandPropertyAssignment).name) ||
visitNode(cbNode, (node as ShorthandPropertyAssignment).questionToken) ||
visitNode(cbNode, (node as ShorthandPropertyAssignment).exclamationToken) ||
visitNode(cbNode, (node as ShorthandPropertyAssignment).equalsToken) ||
visitNode(cbNode, (node as ShorthandPropertyAssignment).objectAssignmentInitializer);
case SyntaxKind.SpreadAssignment:
return visitNode(cbNode, (node as SpreadAssignment).expression);
case SyntaxKind.Parameter:
return visitNodes(cbNode, cbNodes, (node as ParameterDeclaration).modifiers) ||
visitNode(cbNode, (node as ParameterDeclaration).dotDotDotToken) ||
visitNode(cbNode, (node as ParameterDeclaration).name) ||
visitNode(cbNode, (node as ParameterDeclaration).questionToken) ||
visitNode(cbNode, (node as ParameterDeclaration).type) ||
visitNode(cbNode, (node as ParameterDeclaration).initializer);
case SyntaxKind.PropertyDeclaration:
return visitNodes(cbNode, cbNodes, (node as PropertyDeclaration).modifiers) ||
visitNode(cbNode, (node as PropertyDeclaration).name) ||
visitNode(cbNode, (node as PropertyDeclaration).questionToken) ||
visitNode(cbNode, (node as PropertyDeclaration).exclamationToken) ||
visitNode(cbNode, (node as PropertyDeclaration).type) ||
visitNode(cbNode, (node as PropertyDeclaration).initializer);
case SyntaxKind.PropertySignature:
return visitNodes(cbNode, cbNodes, (node as PropertySignature).modifiers) ||
visitNode(cbNode, (node as PropertySignature).name) ||
visitNode(cbNode, (node as PropertySignature).questionToken) ||
visitNode(cbNode, (node as PropertySignature).type) ||
visitNode(cbNode, (node as PropertySignature).initializer);
case SyntaxKind.PropertyAssignment:
return visitNodes(cbNode, cbNodes, (node as PropertyAssignment).decorators) ||
visitNodes(cbNode, cbNodes, (node as PropertyAssignment).modifiers) ||
visitNode(cbNode, (node as PropertyAssignment).name) ||
visitNode(cbNode, (node as PropertyAssignment).questionToken) ||
visitNode(cbNode, (node as PropertyAssignment).exclamationToken) ||
visitNode(cbNode, (node as PropertyAssignment).initializer);
case SyntaxKind.VariableDeclaration:
return visitNode(cbNode, (node as VariableDeclaration).name) ||
visitNode(cbNode, (node as VariableDeclaration).exclamationToken) ||
visitNode(cbNode, (node as VariableDeclaration).type) ||
visitNode(cbNode, (node as VariableDeclaration).initializer);
case SyntaxKind.BindingElement:
return visitNode(cbNode, (node as BindingElement).dotDotDotToken) ||
visitNode(cbNode, (node as BindingElement).propertyName) ||
visitNode(cbNode, (node as BindingElement).name) ||
visitNode(cbNode, (node as BindingElement).initializer);
case SyntaxKind.IndexSignature:
return visitNodes(cbNode, cbNodes, (node as IndexSignatureDeclaration).decorators) ||
visitNodes(cbNode, cbNodes, (node as IndexSignatureDeclaration).modifiers) ||
visitNodes(cbNode, cbNodes, (node as IndexSignatureDeclaration).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as IndexSignatureDeclaration).parameters) ||
visitNode(cbNode, (node as IndexSignatureDeclaration).type);
case SyntaxKind.ConstructorType:
return visitNodes(cbNode, cbNodes, (node as ConstructorTypeNode).modifiers) ||
visitNodes(cbNode, cbNodes, (node as ConstructorTypeNode).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as ConstructorTypeNode).parameters) ||
visitNode(cbNode, (node as ConstructorTypeNode).type);
case SyntaxKind.FunctionType:
return visitNodes(cbNode, cbNodes, (node as FunctionTypeNode).modifiers) ||
visitNodes(cbNode, cbNodes, (node as FunctionTypeNode).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as FunctionTypeNode).parameters) ||
visitNode(cbNode, (node as FunctionTypeNode).type);
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
return visitNodes(cbNode, cbNodes, (node as CallSignatureDeclaration | ConstructSignatureDeclaration).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as CallSignatureDeclaration | ConstructSignatureDeclaration).parameters) ||
visitNode(cbNode, (node as CallSignatureDeclaration | ConstructSignatureDeclaration).type);
case SyntaxKind.MethodDeclaration:
return visitNodes(cbNode, cbNodes, (node as MethodDeclaration).modifiers) ||
visitNode(cbNode, (node as MethodDeclaration).asteriskToken) ||
visitNode(cbNode, (node as MethodDeclaration).name) ||
visitNode(cbNode, (node as MethodDeclaration).questionToken) ||
visitNode(cbNode, (node as MethodDeclaration).exclamationToken) ||
visitNodes(cbNode, cbNodes, (node as MethodDeclaration).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as MethodDeclaration).parameters) ||
visitNode(cbNode, (node as MethodDeclaration).type) ||
visitNode(cbNode, (node as MethodDeclaration).body);
case SyntaxKind.MethodSignature:
return visitNodes(cbNode, cbNodes, (node as MethodSignature).modifiers) ||
visitNode(cbNode, (node as MethodSignature).name) ||
visitNode(cbNode, (node as MethodSignature).questionToken) ||
visitNodes(cbNode, cbNodes, (node as MethodSignature).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as MethodSignature).parameters) ||
visitNode(cbNode, (node as MethodSignature).type);
case SyntaxKind.Constructor:
return visitNodes(cbNode, cbNodes, (node as ConstructorDeclaration).decorators) ||
visitNodes(cbNode, cbNodes, (node as ConstructorDeclaration).modifiers) ||
visitNode(cbNode, (node as ConstructorDeclaration).name) ||
visitNodes(cbNode, cbNodes, (node as ConstructorDeclaration).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as ConstructorDeclaration).parameters) ||
visitNode(cbNode, (node as ConstructorDeclaration).type) ||
visitNode(cbNode, (node as ConstructorDeclaration).body);
case SyntaxKind.GetAccessor:
return visitNodes(cbNode, cbNodes, (node as GetAccessorDeclaration).modifiers) ||
visitNode(cbNode, (node as GetAccessorDeclaration).name) ||
visitNodes(cbNode, cbNodes, (node as GetAccessorDeclaration).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as GetAccessorDeclaration).parameters) ||
visitNode(cbNode, (node as GetAccessorDeclaration).type) ||
visitNode(cbNode, (node as GetAccessorDeclaration).body);
case SyntaxKind.SetAccessor:
return visitNodes(cbNode, cbNodes, (node as SetAccessorDeclaration).modifiers) ||
visitNode(cbNode, (node as SetAccessorDeclaration).name) ||
visitNodes(cbNode, cbNodes, (node as SetAccessorDeclaration).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as SetAccessorDeclaration).parameters) ||
visitNode(cbNode, (node as SetAccessorDeclaration).type) ||
visitNode(cbNode, (node as SetAccessorDeclaration).body);
case SyntaxKind.FunctionDeclaration:
return visitNodes(cbNode, cbNodes, (node as FunctionDeclaration).decorators) ||
visitNodes(cbNode, cbNodes, (node as FunctionDeclaration).modifiers) ||
visitNode(cbNode, (node as FunctionDeclaration).asteriskToken) ||
visitNode(cbNode, (node as FunctionDeclaration).name) ||
visitNodes(cbNode, cbNodes, (node as FunctionDeclaration).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as FunctionDeclaration).parameters) ||
visitNode(cbNode, (node as FunctionDeclaration).type) ||
visitNode(cbNode, (node as FunctionDeclaration).body);
case SyntaxKind.FunctionExpression:
return visitNodes(cbNode, cbNodes, (node as FunctionExpression).modifiers) ||
visitNode(cbNode, (node as FunctionExpression).asteriskToken) ||
visitNode(cbNode, (node as FunctionExpression).name) ||
visitNodes(cbNode, cbNodes, (node as FunctionExpression).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as FunctionExpression).parameters) ||
visitNode(cbNode, (node as FunctionExpression).type) ||
visitNode(cbNode, (node as FunctionExpression).body);
case SyntaxKind.ArrowFunction:
return visitNodes(cbNode, cbNodes, (node as ArrowFunction).modifiers) ||
visitNodes(cbNode, cbNodes, (node as ArrowFunction).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as ArrowFunction).parameters) ||
visitNode(cbNode, (node as ArrowFunction).type) ||
visitNode(cbNode, (node as ArrowFunction).equalsGreaterThanToken) ||
visitNode(cbNode, (node as ArrowFunction).body);
case SyntaxKind.ClassStaticBlockDeclaration:
return visitNodes(cbNode, cbNodes, (node as ClassStaticBlockDeclaration).decorators) ||
visitNodes(cbNode, cbNodes, (node as ClassStaticBlockDeclaration).modifiers) ||
visitNode(cbNode, (node as ClassStaticBlockDeclaration).body);
case SyntaxKind.TypeReference:
return visitNode(cbNode, (node as TypeReferenceNode).typeName) ||
visitNodes(cbNode, cbNodes, (node as TypeReferenceNode).typeArguments);
case SyntaxKind.TypePredicate:
return visitNode(cbNode, (node as TypePredicateNode).assertsModifier) ||
visitNode(cbNode, (node as TypePredicateNode).parameterName) ||
visitNode(cbNode, (node as TypePredicateNode).type);
case SyntaxKind.TypeQuery:
return visitNode(cbNode, (node as TypeQueryNode).exprName) ||
visitNodes(cbNode, cbNodes, (node as TypeQueryNode).typeArguments);
case SyntaxKind.TypeLiteral:
return visitNodes(cbNode, cbNodes, (node as TypeLiteralNode).members);
case SyntaxKind.ArrayType:
return visitNode(cbNode, (node as ArrayTypeNode).elementType);
case SyntaxKind.TupleType:
return visitNodes(cbNode, cbNodes, (node as TupleTypeNode).elements);
case SyntaxKind.UnionType:
case SyntaxKind.IntersectionType:
return visitNodes(cbNode, cbNodes, (node as UnionOrIntersectionTypeNode).types);
case SyntaxKind.ConditionalType:
return visitNode(cbNode, (node as ConditionalTypeNode).checkType) ||
visitNode(cbNode, (node as ConditionalTypeNode).extendsType) ||
visitNode(cbNode, (node as ConditionalTypeNode).trueType) ||
visitNode(cbNode, (node as ConditionalTypeNode).falseType);
case SyntaxKind.InferType:
return visitNode(cbNode, (node as InferTypeNode).typeParameter);
case SyntaxKind.ImportType:
return visitNode(cbNode, (node as ImportTypeNode).argument) ||
visitNode(cbNode, (node as ImportTypeNode).assertions) ||
visitNode(cbNode, (node as ImportTypeNode).qualifier) ||
visitNodes(cbNode, cbNodes, (node as ImportTypeNode).typeArguments);
case SyntaxKind.ImportTypeAssertionContainer:
return visitNode(cbNode, (node as ImportTypeAssertionContainer).assertClause);
case SyntaxKind.ParenthesizedType:
case SyntaxKind.TypeOperator:
return visitNode(cbNode, (node as ParenthesizedTypeNode | TypeOperatorNode).type);
case SyntaxKind.IndexedAccessType:
return visitNode(cbNode, (node as IndexedAccessTypeNode).objectType) ||
visitNode(cbNode, (node as IndexedAccessTypeNode).indexType);
case SyntaxKind.MappedType:
return visitNode(cbNode, (node as MappedTypeNode).readonlyToken) ||
visitNode(cbNode, (node as MappedTypeNode).typeParameter) ||
visitNode(cbNode, (node as MappedTypeNode).nameType) ||
visitNode(cbNode, (node as MappedTypeNode).questionToken) ||
visitNode(cbNode, (node as MappedTypeNode).type) ||
visitNodes(cbNode, cbNodes, (node as MappedTypeNode).members);
case SyntaxKind.LiteralType:
return visitNode(cbNode, (node as LiteralTypeNode).literal);
case SyntaxKind.NamedTupleMember:
return visitNode(cbNode, (node as NamedTupleMember).dotDotDotToken) ||
visitNode(cbNode, (node as NamedTupleMember).name) ||
visitNode(cbNode, (node as NamedTupleMember).questionToken) ||
visitNode(cbNode, (node as NamedTupleMember).type);
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ArrayBindingPattern:
return visitNodes(cbNode, cbNodes, (node as BindingPattern).elements);
case SyntaxKind.ArrayLiteralExpression:
return visitNodes(cbNode, cbNodes, (node as ArrayLiteralExpression).elements);
case SyntaxKind.ObjectLiteralExpression:
return visitNodes(cbNode, cbNodes, (node as ObjectLiteralExpression).properties);
case SyntaxKind.PropertyAccessExpression:
return visitNode(cbNode, (node as PropertyAccessExpression).expression) ||
visitNode(cbNode, (node as PropertyAccessExpression).questionDotToken) ||
visitNode(cbNode, (node as PropertyAccessExpression).name);
case SyntaxKind.ElementAccessExpression:
return visitNode(cbNode, (node as ElementAccessExpression).expression) ||
visitNode(cbNode, (node as ElementAccessExpression).questionDotToken) ||
visitNode(cbNode, (node as ElementAccessExpression).argumentExpression);
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return visitNode(cbNode, (node as CallExpression).expression) ||
visitNode(cbNode, (node as CallExpression).questionDotToken) ||
visitNodes(cbNode, cbNodes, (node as CallExpression).typeArguments) ||
visitNodes(cbNode, cbNodes, (node as CallExpression).arguments);
case SyntaxKind.TaggedTemplateExpression:
return visitNode(cbNode, (node as TaggedTemplateExpression).tag) ||
visitNode(cbNode, (node as TaggedTemplateExpression).questionDotToken) ||
visitNodes(cbNode, cbNodes, (node as TaggedTemplateExpression).typeArguments) ||
visitNode(cbNode, (node as TaggedTemplateExpression).template);
case SyntaxKind.TypeAssertionExpression:
return visitNode(cbNode, (node as TypeAssertion).type) ||
visitNode(cbNode, (node as TypeAssertion).expression);
case SyntaxKind.ParenthesizedExpression:
return visitNode(cbNode, (node as ParenthesizedExpression).expression);
case SyntaxKind.DeleteExpression:
return visitNode(cbNode, (node as DeleteExpression).expression);
case SyntaxKind.TypeOfExpression:
return visitNode(cbNode, (node as TypeOfExpression).expression);
case SyntaxKind.VoidExpression:
return visitNode(cbNode, (node as VoidExpression).expression);
case SyntaxKind.PrefixUnaryExpression:
return visitNode(cbNode, (node as PrefixUnaryExpression).operand);
case SyntaxKind.YieldExpression:
return visitNode(cbNode, (node as YieldExpression).asteriskToken) ||
visitNode(cbNode, (node as YieldExpression).expression);
case SyntaxKind.AwaitExpression:
return visitNode(cbNode, (node as AwaitExpression).expression);
case SyntaxKind.PostfixUnaryExpression:
return visitNode(cbNode, (node as PostfixUnaryExpression).operand);
case SyntaxKind.BinaryExpression:
return visitNode(cbNode, (node as BinaryExpression).left) ||
visitNode(cbNode, (node as BinaryExpression).operatorToken) ||
visitNode(cbNode, (node as BinaryExpression).right);
case SyntaxKind.AsExpression:
return visitNode(cbNode, (node as AsExpression).expression) ||
visitNode(cbNode, (node as AsExpression).type);
case SyntaxKind.NonNullExpression:
return visitNode(cbNode, (node as NonNullExpression).expression);
case SyntaxKind.MetaProperty:
return visitNode(cbNode, (node as MetaProperty).name);
case SyntaxKind.ConditionalExpression:
return visitNode(cbNode, (node as ConditionalExpression).condition) ||
visitNode(cbNode, (node as ConditionalExpression).questionToken) ||
visitNode(cbNode, (node as ConditionalExpression).whenTrue) ||
visitNode(cbNode, (node as ConditionalExpression).colonToken) ||
visitNode(cbNode, (node as ConditionalExpression).whenFalse);
case SyntaxKind.SpreadElement:
return visitNode(cbNode, (node as SpreadElement).expression);
case SyntaxKind.Block:
case SyntaxKind.ModuleBlock:
return visitNodes(cbNode, cbNodes, (node as Block).statements);
case SyntaxKind.SourceFile:
return visitNodes(cbNode, cbNodes, (node as SourceFile).statements) ||
visitNode(cbNode, (node as SourceFile).endOfFileToken);
case SyntaxKind.VariableStatement:
return visitNodes(cbNode, cbNodes, (node as VariableStatement).decorators) ||
visitNodes(cbNode, cbNodes, (node as VariableStatement).modifiers) ||
visitNode(cbNode, (node as VariableStatement).declarationList);
case SyntaxKind.VariableDeclarationList:
return visitNodes(cbNode, cbNodes, (node as VariableDeclarationList).declarations);
case SyntaxKind.ExpressionStatement:
return visitNode(cbNode, (node as ExpressionStatement).expression);
case SyntaxKind.IfStatement:
return visitNode(cbNode, (node as IfStatement).expression) ||
visitNode(cbNode, (node as IfStatement).thenStatement) ||
visitNode(cbNode, (node as IfStatement).elseStatement);
case SyntaxKind.DoStatement:
return visitNode(cbNode, (node as DoStatement).statement) ||
visitNode(cbNode, (node as DoStatement).expression);
case SyntaxKind.WhileStatement:
return visitNode(cbNode, (node as WhileStatement).expression) ||
visitNode(cbNode, (node as WhileStatement).statement);
case SyntaxKind.ForStatement:
return visitNode(cbNode, (node as ForStatement).initializer) ||
visitNode(cbNode, (node as ForStatement).condition) ||
visitNode(cbNode, (node as ForStatement).incrementor) ||
visitNode(cbNode, (node as ForStatement).statement);
case SyntaxKind.ForInStatement:
return visitNode(cbNode, (node as ForInStatement).initializer) ||
visitNode(cbNode, (node as ForInStatement).expression) ||
visitNode(cbNode, (node as ForInStatement).statement);
case SyntaxKind.ForOfStatement:
return visitNode(cbNode, (node as ForOfStatement).awaitModifier) ||
visitNode(cbNode, (node as ForOfStatement).initializer) ||
visitNode(cbNode, (node as ForOfStatement).expression) ||
visitNode(cbNode, (node as ForOfStatement).statement);
case SyntaxKind.ContinueStatement:
case SyntaxKind.BreakStatement:
return visitNode(cbNode, (node as BreakOrContinueStatement).label);
case SyntaxKind.ReturnStatement:
return visitNode(cbNode, (node as ReturnStatement).expression);
case SyntaxKind.WithStatement:
return visitNode(cbNode, (node as WithStatement).expression) ||
visitNode(cbNode, (node as WithStatement).statement);
case SyntaxKind.SwitchStatement:
return visitNode(cbNode, (node as SwitchStatement).expression) ||
visitNode(cbNode, (node as SwitchStatement).caseBlock);
case SyntaxKind.CaseBlock:
return visitNodes(cbNode, cbNodes, (node as CaseBlock).clauses);
case SyntaxKind.CaseClause:
return visitNode(cbNode, (node as CaseClause).expression) ||
visitNodes(cbNode, cbNodes, (node as CaseClause).statements);
case SyntaxKind.DefaultClause:
return visitNodes(cbNode, cbNodes, (node as DefaultClause).statements);
case SyntaxKind.LabeledStatement:
return visitNode(cbNode, (node as LabeledStatement).label) ||
visitNode(cbNode, (node as LabeledStatement).statement);
case SyntaxKind.ThrowStatement:
return visitNode(cbNode, (node as ThrowStatement).expression);
case SyntaxKind.TryStatement:
return visitNode(cbNode, (node as TryStatement).tryBlock) ||
visitNode(cbNode, (node as TryStatement).catchClause) ||
visitNode(cbNode, (node as TryStatement).finallyBlock);
case SyntaxKind.CatchClause:
return visitNode(cbNode, (node as CatchClause).variableDeclaration) ||
visitNode(cbNode, (node as CatchClause).block);
case SyntaxKind.Decorator:
return visitNode(cbNode, (node as Decorator).expression);
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
return visitNodes(cbNode, cbNodes, (node as ClassLikeDeclaration).modifiers) ||
visitNode(cbNode, (node as ClassLikeDeclaration).name) ||
visitNodes(cbNode, cbNodes, (node as ClassLikeDeclaration).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as ClassLikeDeclaration).heritageClauses) ||
visitNodes(cbNode, cbNodes, (node as ClassLikeDeclaration).members);
case SyntaxKind.InterfaceDeclaration:
return visitNodes(cbNode, cbNodes, (node as InterfaceDeclaration).decorators) ||
visitNodes(cbNode, cbNodes, (node as InterfaceDeclaration).modifiers) ||
visitNode(cbNode, (node as InterfaceDeclaration).name) ||
visitNodes(cbNode, cbNodes, (node as InterfaceDeclaration).typeParameters) ||
visitNodes(cbNode, cbNodes, (node as ClassDeclaration).heritageClauses) ||
visitNodes(cbNode, cbNodes, (node as InterfaceDeclaration).members);
case SyntaxKind.TypeAliasDeclaration:
return visitNodes(cbNode, cbNodes, (node as TypeAliasDeclaration).decorators) ||
visitNodes(cbNode, cbNodes, (node as TypeAliasDeclaration).modifiers) ||
visitNode(cbNode, (node as TypeAliasDeclaration).name) ||
visitNodes(cbNode, cbNodes, (node as TypeAliasDeclaration).typeParameters) ||
visitNode(cbNode, (node as TypeAliasDeclaration).type);
case SyntaxKind.EnumDeclaration:
return visitNodes(cbNode, cbNodes, (node as EnumDeclaration).decorators) ||
visitNodes(cbNode, cbNodes, (node as EnumDeclaration).modifiers) ||
visitNode(cbNode, (node as EnumDeclaration).name) ||
visitNodes(cbNode, cbNodes, (node as EnumDeclaration).members);
case SyntaxKind.EnumMember:
return visitNode(cbNode, (node as EnumMember).name) ||
visitNode(cbNode, (node as EnumMember).initializer);
case SyntaxKind.ModuleDeclaration:
return visitNodes(cbNode, cbNodes, (node as ModuleDeclaration).decorators) ||
visitNodes(cbNode, cbNodes, (node as ModuleDeclaration).modifiers) ||
visitNode(cbNode, (node as ModuleDeclaration).name) ||
visitNode(cbNode, (node as ModuleDeclaration).body);
case SyntaxKind.ImportEqualsDeclaration:
return visitNodes(cbNode, cbNodes, (node as ImportEqualsDeclaration).decorators) ||
visitNodes(cbNode, cbNodes, (node as ImportEqualsDeclaration).modifiers) ||
visitNode(cbNode, (node as ImportEqualsDeclaration).name) ||
visitNode(cbNode, (node as ImportEqualsDeclaration).moduleReference);
case SyntaxKind.ImportDeclaration:
return visitNodes(cbNode, cbNodes, (node as ImportDeclaration).decorators) ||
visitNodes(cbNode, cbNodes, (node as ImportDeclaration).modifiers) ||
visitNode(cbNode, (node as ImportDeclaration).importClause) ||
visitNode(cbNode, (node as ImportDeclaration).moduleSpecifier) ||
visitNode(cbNode, (node as ImportDeclaration).assertClause);
case SyntaxKind.ImportClause:
return visitNode(cbNode, (node as ImportClause).name) ||
visitNode(cbNode, (node as ImportClause).namedBindings);
case SyntaxKind.AssertClause:
return visitNodes(cbNode, cbNodes, (node as AssertClause).elements);
case SyntaxKind.AssertEntry:
return visitNode(cbNode, (node as AssertEntry).name) ||
visitNode(cbNode, (node as AssertEntry).value);
case SyntaxKind.NamespaceExportDeclaration:
return visitNodes(cbNode, cbNodes, (node as NamespaceExportDeclaration).decorators) ||
visitNode(cbNode, (node as NamespaceExportDeclaration).name);
case SyntaxKind.NamespaceImport:
return visitNode(cbNode, (node as NamespaceImport).name);
case SyntaxKind.NamespaceExport:
return visitNode(cbNode, (node as NamespaceExport).name);
case SyntaxKind.NamedImports:
case SyntaxKind.NamedExports:
return visitNodes(cbNode, cbNodes, (node as NamedImportsOrExports).elements);
case SyntaxKind.ExportDeclaration:
return visitNodes(cbNode, cbNodes, (node as ExportDeclaration).decorators) ||
visitNodes(cbNode, cbNodes, (node as ExportDeclaration).modifiers) ||
visitNode(cbNode, (node as ExportDeclaration).exportClause) ||
visitNode(cbNode, (node as ExportDeclaration).moduleSpecifier) ||
visitNode(cbNode, (node as ExportDeclaration).assertClause);
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ExportSpecifier:
return visitNode(cbNode, (node as ImportOrExportSpecifier).propertyName) ||
visitNode(cbNode, (node as ImportOrExportSpecifier).name);
case SyntaxKind.ExportAssignment:
return visitNodes(cbNode, cbNodes, (node as ExportAssignment).decorators) ||
visitNodes(cbNode, cbNodes, (node as ExportAssignment).modifiers) ||
visitNode(cbNode, (node as ExportAssignment).expression);
case SyntaxKind.TemplateExpression:
return visitNode(cbNode, (node as TemplateExpression).head) ||
visitNodes(cbNode, cbNodes, (node as TemplateExpression).templateSpans);
case SyntaxKind.TemplateSpan:
return visitNode(cbNode, (node as TemplateSpan).expression) ||
visitNode(cbNode, (node as TemplateSpan).literal);
case SyntaxKind.TemplateLiteralType:
return visitNode(cbNode, (node as TemplateLiteralTypeNode).head) ||
visitNodes(cbNode, cbNodes, (node as TemplateLiteralTypeNode).templateSpans);
case SyntaxKind.TemplateLiteralTypeSpan:
return visitNode(cbNode, (node as TemplateLiteralTypeSpan).type) ||
visitNode(cbNode, (node as TemplateLiteralTypeSpan).literal);
case SyntaxKind.ComputedPropertyName:
return visitNode(cbNode, (node as ComputedPropertyName).expression);
case SyntaxKind.HeritageClause:
return visitNodes(cbNode, cbNodes, (node as HeritageClause).types);
case SyntaxKind.ExpressionWithTypeArguments:
return visitNode(cbNode, (node as ExpressionWithTypeArguments).expression) ||
visitNodes(cbNode, cbNodes, (node as ExpressionWithTypeArguments).typeArguments);
case SyntaxKind.ExternalModuleReference:
return visitNode(cbNode, (node as ExternalModuleReference).expression);
case SyntaxKind.MissingDeclaration:
return visitNodes(cbNode, cbNodes, (node as MissingDeclaration).decorators) ||
visitNodes(cbNode, cbNodes, (node as MissingDeclaration).modifiers);
case SyntaxKind.CommaListExpression:
return visitNodes(cbNode, cbNodes, (node as CommaListExpression).elements);
case SyntaxKind.JsxElement:
return visitNode(cbNode, (node as JsxElement).openingElement) ||
visitNodes(cbNode, cbNodes, (node as JsxElement).children) ||
visitNode(cbNode, (node as JsxElement).closingElement);
case SyntaxKind.JsxFragment:
return visitNode(cbNode, (node as JsxFragment).openingFragment) ||
visitNodes(cbNode, cbNodes, (node as JsxFragment).children) ||
visitNode(cbNode, (node as JsxFragment).closingFragment);
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.JsxOpeningElement:
return visitNode(cbNode, (node as JsxOpeningLikeElement).tagName) ||
visitNodes(cbNode, cbNodes, (node as JsxOpeningLikeElement).typeArguments) ||
visitNode(cbNode, (node as JsxOpeningLikeElement).attributes);
case SyntaxKind.JsxAttributes:
return visitNodes(cbNode, cbNodes, (node as JsxAttributes).properties);
case SyntaxKind.JsxAttribute:
return visitNode(cbNode, (node as JsxAttribute).name) ||
visitNode(cbNode, (node as JsxAttribute).initializer);
case SyntaxKind.JsxSpreadAttribute:
return visitNode(cbNode, (node as JsxSpreadAttribute).expression);
case SyntaxKind.JsxExpression:
return visitNode(cbNode, (node as JsxExpression).dotDotDotToken) ||
visitNode(cbNode, (node as JsxExpression).expression);
case SyntaxKind.JsxClosingElement:
return visitNode(cbNode, (node as JsxClosingElement).tagName);
case SyntaxKind.OptionalType:
case SyntaxKind.RestType:
case SyntaxKind.JSDocTypeExpression:
case SyntaxKind.JSDocNonNullableType:
case SyntaxKind.JSDocNullableType:
case SyntaxKind.JSDocOptionalType:
case SyntaxKind.JSDocVariadicType:
return visitNode(cbNode, (node as OptionalTypeNode | RestTypeNode | JSDocTypeExpression | JSDocTypeReferencingNode).type);
case SyntaxKind.JSDocFunctionType:
return visitNodes(cbNode, cbNodes, (node as JSDocFunctionType).parameters) ||
visitNode(cbNode, (node as JSDocFunctionType).type);
case SyntaxKind.JSDoc:
return (typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray<JSDocComment> | undefined))
|| visitNodes(cbNode, cbNodes, (node as JSDoc).tags);
case SyntaxKind.JSDocSeeTag:
return visitNode(cbNode, (node as JSDocSeeTag).tagName) ||
visitNode(cbNode, (node as JSDocSeeTag).name) ||
(typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray<JSDocComment> | undefined));
case SyntaxKind.JSDocNameReference:
return visitNode(cbNode, (node as JSDocNameReference).name);
case SyntaxKind.JSDocMemberName:
return visitNode(cbNode, (node as JSDocMemberName).left) ||
visitNode(cbNode, (node as JSDocMemberName).right);
case SyntaxKind.JSDocParameterTag:
case SyntaxKind.JSDocPropertyTag:
return visitNode(cbNode, (node as JSDocTag).tagName) ||
((node as JSDocPropertyLikeTag).isNameFirst
? visitNode(cbNode, (node as JSDocPropertyLikeTag).name) ||
visitNode(cbNode, (node as JSDocPropertyLikeTag).typeExpression) ||
(typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray<JSDocComment> | undefined))
: visitNode(cbNode, (node as JSDocPropertyLikeTag).typeExpression) ||
visitNode(cbNode, (node as JSDocPropertyLikeTag).name) ||
(typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray<JSDocComment> | undefined)));
case SyntaxKind.JSDocAuthorTag:
return visitNode(cbNode, (node as JSDocTag).tagName) ||
(typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray<JSDocComment> | undefined));
case SyntaxKind.JSDocImplementsTag:
return visitNode(cbNode, (node as JSDocTag).tagName) ||
visitNode(cbNode, (node as JSDocImplementsTag).class) ||
(typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray<JSDocComment> | undefined));
case SyntaxKind.JSDocAugmentsTag:
return visitNode(cbNode, (node as JSDocTag).tagName) ||
visitNode(cbNode, (node as JSDocAugmentsTag).class) ||
(typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray<JSDocComment> | undefined));
case SyntaxKind.JSDocTemplateTag:
return visitNode(cbNode, (node as JSDocTag).tagName) ||
visitNode(cbNode, (node as JSDocTemplateTag).constraint) ||
visitNodes(cbNode, cbNodes, (node as JSDocTemplateTag).typeParameters) ||
(typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray<JSDocComment> | undefined));
case SyntaxKind.JSDocTypedefTag:
return visitNode(cbNode, (node as JSDocTag).tagName) ||
((node as JSDocTypedefTag).typeExpression &&
(node as JSDocTypedefTag).typeExpression!.kind === SyntaxKind.JSDocTypeExpression
? visitNode(cbNode, (node as JSDocTypedefTag).typeExpression) ||
visitNode(cbNode, (node as JSDocTypedefTag).fullName) ||
(typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray<JSDocComment> | undefined))
: visitNode(cbNode, (node as JSDocTypedefTag).fullName) ||
visitNode(cbNode, (node as JSDocTypedefTag).typeExpression) ||
(typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray<JSDocComment> | undefined)));
case SyntaxKind.JSDocCallbackTag:
return visitNode(cbNode, (node as JSDocTag).tagName) ||
visitNode(cbNode, (node as JSDocCallbackTag).fullName) ||
visitNode(cbNode, (node as JSDocCallbackTag).typeExpression) ||
(typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray<JSDocComment> | undefined));
case SyntaxKind.JSDocReturnTag:
case SyntaxKind.JSDocTypeTag:
case SyntaxKind.JSDocThisTag:
case SyntaxKind.JSDocEnumTag:
return visitNode(cbNode, (node as JSDocTag).tagName) ||
visitNode(cbNode, (node as JSDocReturnTag | JSDocTypeTag | JSDocThisTag | JSDocEnumTag).typeExpression) ||
(typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray<JSDocComment> | undefined));
case SyntaxKind.JSDocSignature:
return forEach((node as JSDocSignature).typeParameters, cbNode) ||
forEach((node as JSDocSignature).parameters, cbNode) ||
visitNode(cbNode, (node as JSDocSignature).type);
case SyntaxKind.JSDocLink:
case SyntaxKind.JSDocLinkCode:
case SyntaxKind.JSDocLinkPlain:
return visitNode(cbNode, (node as JSDocLink | JSDocLinkCode | JSDocLinkPlain).name);
case SyntaxKind.JSDocTypeLiteral:
return forEach((node as JSDocTypeLiteral).jsDocPropertyTags, cbNode);
case SyntaxKind.JSDocTag:
case SyntaxKind.JSDocClassTag:
case SyntaxKind.JSDocPublicTag:
case SyntaxKind.JSDocPrivateTag:
case SyntaxKind.JSDocProtectedTag:
case SyntaxKind.JSDocReadonlyTag:
case SyntaxKind.JSDocDeprecatedTag:
return visitNode(cbNode, (node as JSDocTag).tagName)
|| (typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray<JSDocComment> | undefined));
case SyntaxKind.PartiallyEmittedExpression:
return visitNode(cbNode, (node as PartiallyEmittedExpression).expression);
}
}
/** @internal */
/**
* Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes
* stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; additionally,
* unlike `forEachChild`, embedded arrays are flattened and the 'cbNode' callback is invoked for each element.
* If a callback returns a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned.
*
* @param node a given node to visit its children
* @param cbNode a callback to be invoked for all child nodes
* @param cbNodes a callback to be invoked for embedded array
*
* @remarks Unlike `forEachChild`, `forEachChildRecursively` handles recursively invoking the traversal on each child node found,
* and while doing so, handles traversing the structure without relying on the callstack to encode the tree structure.
*/
export function forEachChildRecursively<T>(rootNode: Node, cbNode: (node: Node, parent: Node) => T | "skip" | undefined, cbNodes?: (nodes: NodeArray<Node>, parent: Node) => T | "skip" | undefined): T | undefined {
const queue: (Node | NodeArray<Node>)[] = gatherPossibleChildren(rootNode);
const parents: Node[] = []; // tracks parent references for elements in queue
while (parents.length < queue.length) {
parents.push(rootNode);
}
while (queue.length !== 0) {
const current = queue.pop()!;
const parent = parents.pop()!;
if (isArray(current)) {
if (cbNodes) {
const res = cbNodes(current, parent);
if (res) {
if (res === "skip") continue;
return res;
}
}
for (let i = current.length - 1; i >= 0; --i) {
queue.push(current[i]);
parents.push(parent);
}
}
else {
const res = cbNode(current, parent);
if (res) {
if (res === "skip") continue;
return res;
}
if (current.kind >= SyntaxKind.FirstNode) {
// add children in reverse order to the queue, so popping gives the first child
for (const child of gatherPossibleChildren(current)) {
queue.push(child);
parents.push(current);
}
}
}
}
}
function gatherPossibleChildren(node: Node) {
const children: (Node | NodeArray<Node>)[] = [];
forEachChild(node, addWorkItem, addWorkItem); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal
return children;
function addWorkItem(n: Node | NodeArray<Node>) {
children.unshift(n);
}
}
export interface CreateSourceFileOptions {
languageVersion: ScriptTarget;
/**
* Controls the format the file is detected as - this can be derived from only the path
* and files on disk, but needs to be done with a module resolution cache in scope to be performant.
* This is usually `undefined` for compilations that do not have `moduleResolution` values of `node16` or `nodenext`.
*/
impliedNodeFormat?: ModuleKind.ESNext | ModuleKind.CommonJS;
/**
* Controls how module-y-ness is set for the given file. Usually the result of calling
* `getSetExternalModuleIndicator` on a valid `CompilerOptions` object. If not present, the default
* check specified by `isFileProbablyExternalModule` will be used to set the field.
*/
setExternalModuleIndicator?: (file: SourceFile) => void;
/*@internal*/ packageJsonLocations?: readonly string[];
/*@internal*/ packageJsonScope?: PackageJsonInfo;
}
function setExternalModuleIndicator(sourceFile: SourceFile) {
sourceFile.externalModuleIndicator = isFileProbablyExternalModule(sourceFile);
}
export function createSourceFile(fileName: string, sourceText: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, setParentNodes = false, scriptKind?: ScriptKind): SourceFile {
tracing?.push(tracing.Phase.Parse, "createSourceFile", { path: fileName }, /*separateBeginAndEnd*/ true);
performance.mark("beforeParse");
let result: SourceFile;
perfLogger.logStartParseSourceFile(fileName);
const {
languageVersion,
setExternalModuleIndicator: overrideSetExternalModuleIndicator,
impliedNodeFormat: format
} = typeof languageVersionOrOptions === "object" ? languageVersionOrOptions : ({ languageVersion: languageVersionOrOptions } as CreateSourceFileOptions);
if (languageVersion === ScriptTarget.JSON) {
result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, ScriptKind.JSON, noop);
}
else {
const setIndicator = format === undefined ? overrideSetExternalModuleIndicator : (file: SourceFile) => {
file.impliedNodeFormat = format;
return (overrideSetExternalModuleIndicator || setExternalModuleIndicator)(file);
};
result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind, setIndicator);
}
perfLogger.logStopParseSourceFile();
performance.mark("afterParse");
performance.measure("Parse", "beforeParse", "afterParse");
tracing?.pop();
return result;
}
export function parseIsolatedEntityName(text: string, languageVersion: ScriptTarget): EntityName | undefined {
return Parser.parseIsolatedEntityName(text, languageVersion);
}
/**
* Parse json text into SyntaxTree and return node and parse errors if any
* @param fileName
* @param sourceText
*/
export function parseJsonText(fileName: string, sourceText: string): JsonSourceFile {
return Parser.parseJsonText(fileName, sourceText);
}
// See also `isExternalOrCommonJsModule` in utilities.ts
export function isExternalModule(file: SourceFile): boolean {
return file.externalModuleIndicator !== undefined;
}
// Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter
// indicates what changed between the 'text' that this SourceFile has and the 'newText'.
// The SourceFile will be created with the compiler attempting to reuse as many nodes from
// this file as possible.
//
// Note: this function mutates nodes from this SourceFile. That means any existing nodes
// from this SourceFile that are being held onto may change as a result (including
// becoming detached from any SourceFile). It is recommended that this SourceFile not
// be used once 'update' is called on it.
export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks = false): SourceFile {
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 as Mutable<SourceFile>).flags |= (sourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags);
return newSourceFile;
}
/* @internal */
export function parseIsolatedJSDocComment(content: string, start?: number, length?: number) {
const result = Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length);
if (result && result.jsDoc) {
// because the jsDocComment was parsed out of the source file, it might
// not be covered by the fixupParentReferences.
Parser.fixupParentReferences(result.jsDoc);
}
return result;
}
/* @internal */
// Exposed only for testing.
export function parseJSDocTypeExpressionForTests(content: string, start?: number, length?: number) {
return Parser.JSDocParser.parseJSDocTypeExpressionForTests(content, start, length);
}
// Implement the parser as a singleton module. We do this for perf reasons because creating
// parser instances can actually be expensive enough to impact us on projects with many source
// files.
namespace Parser {
// Share a single scanner across all calls to parse a source file. This helps speed things
// up by avoiding the cost of creating/compiling scanners over and over again.
const scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ true);
const disallowInAndDecoratorContext = NodeFlags.DisallowInContext | NodeFlags.DecoratorContext;
// capture constructors in 'initializeState' to avoid null checks
// tslint:disable variable-name
let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let IdentifierConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let PrivateIdentifierConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
// tslint:enable variable-name
function countNode(node: Node) {
nodeCount++;
return node;
}
// Rather than using `createBaseNodeFactory` here, we establish a `BaseNodeFactory` that closes over the
// constructors above, which are reset each time `initializeState` is called.
const baseNodeFactory: BaseNodeFactory = {
createBaseSourceFileNode: kind => countNode(new SourceFileConstructor(kind, /*pos*/ 0, /*end*/ 0)),
createBaseIdentifierNode: kind => countNode(new IdentifierConstructor(kind, /*pos*/ 0, /*end*/ 0)),
createBasePrivateIdentifierNode: kind => countNode(new PrivateIdentifierConstructor(kind, /*pos*/ 0, /*end*/ 0)),
createBaseTokenNode: kind => countNode(new TokenConstructor(kind, /*pos*/ 0, /*end*/ 0)),
createBaseNode: kind => countNode(new NodeConstructor(kind, /*pos*/ 0, /*end*/ 0))
};
const factory = createNodeFactory(NodeFactoryFlags.NoParenthesizerRules | NodeFactoryFlags.NoNodeConverters | NodeFactoryFlags.NoOriginalNode, baseNodeFactory);
let fileName: string;
let sourceFlags: NodeFlags;
let sourceText: string;
let languageVersion: ScriptTarget;
let scriptKind: ScriptKind;
let languageVariant: LanguageVariant;
let parseDiagnostics: DiagnosticWithDetachedLocation[];
let jsDocDiagnostics: DiagnosticWithDetachedLocation[];
let syntaxCursor: IncrementalParser.SyntaxCursor | undefined;
let currentToken: SyntaxKind;
let nodeCount: number;
let identifiers: ESMap<string, string>;
let privateIdentifiers: ESMap<string, string>;
let identifierCount: number;
let parsingContext: ParsingContext;
let notParenthesizedArrow: Set<number> | undefined;
// Flags that dictate what parsing context we're in. For example:
// Whether or not we are in strict parsing mode. All that changes in strict parsing mode is
// that some tokens that would be considered identifiers may be considered keywords.
//
// When adding more parser context flags, consider which is the more common case that the
// flag will be in. This should be the 'false' state for that flag. The reason for this is
// that we don't store data in our nodes unless the value is in the *non-default* state. So,
// for example, more often than code 'allows-in' (or doesn't 'disallow-in'). We opt for
// 'disallow-in' set to 'false'. Otherwise, if we had 'allowsIn' set to 'true', then almost
// all nodes would need extra state on them to store this info.
//
// Note: 'allowIn' and 'allowYield' track 1:1 with the [in] and [yield] concepts in the ES6
// grammar specification.
//
// An important thing about these context concepts. By default they are effectively inherited
// while parsing through every grammar production. i.e. if you don't change them, then when
// you parse a sub-production, it will have the same context values as the parent production.
// This is great most of the time. After all, consider all the 'expression' grammar productions
// and how nearly all of them pass along the 'in' and 'yield' context values:
//
// EqualityExpression[In, Yield] :
// RelationalExpression[?In, ?Yield]
// EqualityExpression[?In, ?Yield] == RelationalExpression[?In, ?Yield]
// EqualityExpression[?In, ?Yield] != RelationalExpression[?In, ?Yield]
// EqualityExpression[?In, ?Yield] === RelationalExpression[?In, ?Yield]
// EqualityExpression[?In, ?Yield] !== RelationalExpression[?In, ?Yield]
//
// Where you have to be careful is then understanding what the points are in the grammar
// where the values are *not* passed along. For example:
//
// SingleNameBinding[Yield,GeneratorParameter]
// [+GeneratorParameter]BindingIdentifier[Yield] Initializer[In]opt
// [~GeneratorParameter]BindingIdentifier[?Yield]Initializer[In, ?Yield]opt
//
// Here this is saying that if the GeneratorParameter context flag is set, that we should
// explicitly set the 'yield' context flag to false before calling into the BindingIdentifier
// and we should explicitly unset the 'yield' context flag before calling into the Initializer.
// production. Conversely, if the GeneratorParameter context flag is not set, then we
// should leave the 'yield' context flag alone.
//
// Getting this all correct is tricky and requires careful reading of the grammar to
// understand when these values should be changed versus when they should be inherited.
//
// Note: it should not be necessary to save/restore these flags during speculative/lookahead
// parsing. These context flags are naturally stored and restored through normal recursive
// descent parsing and unwinding.
let contextFlags: NodeFlags;
// Indicates whether we are currently parsing top-level statements.
let topLevel = true;
// Whether or not we've had a parse error since creating the last AST node. If we have
// encountered an error, it will be stored on the next AST node we create. Parse errors
// can be broken down into three categories:
//
// 1) An error that occurred during scanning. For example, an unterminated literal, or a
// character that was completely not understood.
//
// 2) A token was expected, but was not present. This type of error is commonly produced
// by the 'parseExpected' function.
//
// 3) A token was present that no parsing function was able to consume. This type of error
// only occurs in the 'abortParsingListOrMoveToNextToken' function when the parser
// decides to skip the token.
//
// In all of these cases, we want to mark the next node as having had an error before it.
// With this mark, we can know in incremental settings if this node can be reused, or if
// we have to reparse it. If we don't keep this information around, we may just reuse the
// node. in that event we would then not produce the same errors as we did before, causing
// significant confusion problems.
//
// Note: it is necessary that this value be saved/restored during speculative/lookahead
// parsing. During lookahead parsing, we will often create a node. That node will have
// this value attached, and then this value will be set back to 'false'. If we decide to
// rewind, we must get back to the same value we had prior to the lookahead.
//
// Note: any errors at the end of the file that do not precede a regular node, should get
// attached to the EOF token.
let parseErrorBeforeNextFinishedNode = false;
export function parseSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, syntaxCursor: IncrementalParser.SyntaxCursor | undefined, setParentNodes = false, scriptKind?: ScriptKind, setExternalModuleIndicatorOverride?: (file: SourceFile) => void): SourceFile {
scriptKind = ensureScriptKind(fileName, scriptKind);
if (scriptKind === ScriptKind.JSON) {
const result = parseJsonText(fileName, sourceText, languageVersion, syntaxCursor, setParentNodes);
convertToObjectWorker(result, result.statements[0]?.expression, result.parseDiagnostics, /*returnValue*/ false, /*knownRootOptions*/ undefined, /*jsonConversionNotifier*/ undefined);
result.referencedFiles = emptyArray;
result.typeReferenceDirectives = emptyArray;
result.libReferenceDirectives = emptyArray;
result.amdDependencies = emptyArray;
result.hasNoDefaultLib = false;
result.pragmas = emptyMap as ReadonlyPragmaMap;
return result;
}
initializeState(fileName, sourceText, languageVersion, syntaxCursor, scriptKind);
const result = parseSourceFileWorker(languageVersion, setParentNodes, scriptKind, setExternalModuleIndicatorOverride || setExternalModuleIndicator);
clearState();
return result;
}
export function parseIsolatedEntityName(content: string, languageVersion: ScriptTarget): EntityName | undefined {
// Choice of `isDeclarationFile` should be arbitrary
initializeState("", content, languageVersion, /*syntaxCursor*/ undefined, ScriptKind.JS);
// Prime the scanner.
nextToken();
const entityName = parseEntityName(/*allowReservedWords*/ true);
const isInvalid = token() === SyntaxKind.EndOfFileToken && !parseDiagnostics.length;
clearState();
return isInvalid ? entityName : undefined;
}
export function parseJsonText(fileName: string, sourceText: string, languageVersion: ScriptTarget = ScriptTarget.ES2015, syntaxCursor?: IncrementalParser.SyntaxCursor, setParentNodes = false): JsonSourceFile {
initializeState(fileName, sourceText, languageVersion, syntaxCursor, ScriptKind.JSON);
sourceFlags = contextFlags;
// Prime the scanner.
nextToken();
const pos = getNodePos();
let statements, endOfFileToken;
if (token() === SyntaxKind.EndOfFileToken) {
statements = createNodeArray([], pos, pos);
endOfFileToken = parseTokenNode<EndOfFileToken>();
}
else {
// Loop and synthesize an ArrayLiteralExpression if there are more than
// one top-level expressions to ensure all input text is consumed.
let expressions: Expression[] | Expression | undefined;
while (token() !== SyntaxKind.EndOfFileToken) {
let expression;
switch (token()) {
case SyntaxKind.OpenBracketToken:
expression = parseArrayLiteralExpression();
break;
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.NullKeyword:
expression = parseTokenNode<BooleanLiteral | NullLiteral>();
break;
case SyntaxKind.MinusToken:
if (lookAhead(() => nextToken() === SyntaxKind.NumericLiteral && nextToken() !== SyntaxKind.ColonToken)) {
expression = parsePrefixUnaryExpression() as JsonMinusNumericLiteral;
}
else {
expression = parseObjectLiteralExpression();
}
break;
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
if (lookAhead(() => nextToken() !== SyntaxKind.ColonToken)) {
expression = parseLiteralNode() as StringLiteral | NumericLiteral;
break;
}
// falls through
default:
expression = parseObjectLiteralExpression();
break;
}
// Error recovery: collect multiple top-level expressions
if (expressions && isArray(expressions)) {
expressions.push(expression);
}
else if (expressions) {
expressions = [expressions, expression];
}
else {
expressions = expression;
if (token() !== SyntaxKind.EndOfFileToken) {
parseErrorAtCurrentToken(Diagnostics.Unexpected_token);
}
}
}
const expression = isArray(expressions) ? finishNode(factory.createArrayLiteralExpression(expressions), pos) : Debug.checkDefined(expressions);
const statement = factory.createExpressionStatement(expression) as JsonObjectExpressionStatement;
finishNode(statement, pos);
statements = createNodeArray([statement], pos);
endOfFileToken = parseExpectedToken(SyntaxKind.EndOfFileToken, Diagnostics.Unexpected_token);
}
// Set source file so that errors will be reported with this file name
const sourceFile = createSourceFile(fileName, ScriptTarget.ES2015, ScriptKind.JSON, /*isDeclaration*/ false, statements, endOfFileToken, sourceFlags, noop);
if (setParentNodes) {
fixupParentReferences(sourceFile);
}
sourceFile.nodeCount = nodeCount;
sourceFile.identifierCount = identifierCount;
sourceFile.identifiers = identifiers;
sourceFile.parseDiagnostics = attachFileToDiagnostics(parseDiagnostics, sourceFile);
if (jsDocDiagnostics) {
sourceFile.jsDocDiagnostics = attachFileToDiagnostics(jsDocDiagnostics, sourceFile);
}
const result = sourceFile as JsonSourceFile;
clearState();
return result;
}
function initializeState(_fileName: string, _sourceText: string, _languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor | undefined, _scriptKind: ScriptKind) {
NodeConstructor = objectAllocator.getNodeConstructor();
TokenConstructor = objectAllocator.getTokenConstructor();
IdentifierConstructor = objectAllocator.getIdentifierConstructor();
PrivateIdentifierConstructor = objectAllocator.getPrivateIdentifierConstructor();
SourceFileConstructor = objectAllocator.getSourceFileConstructor();
fileName = normalizePath(_fileName);
sourceText = _sourceText;
languageVersion = _languageVersion;
syntaxCursor = _syntaxCursor;
scriptKind = _scriptKind;
languageVariant = getLanguageVariant(_scriptKind);
parseDiagnostics = [];
parsingContext = 0;
identifiers = new Map<string, string>();
privateIdentifiers = new Map<string, string>();
identifierCount = 0;
nodeCount = 0;
sourceFlags = 0;
topLevel = true;
switch (scriptKind) {
case ScriptKind.JS:
case ScriptKind.JSX:
contextFlags = NodeFlags.JavaScriptFile;
break;
case ScriptKind.JSON:
contextFlags = NodeFlags.JavaScriptFile | NodeFlags.JsonFile;
break;
default:
contextFlags = NodeFlags.None;
break;
}
parseErrorBeforeNextFinishedNode = false;
// Initialize and prime the scanner before parsing the source elements.
scanner.setText(sourceText);
scanner.setOnError(scanError);
scanner.setScriptTarget(languageVersion);
scanner.setLanguageVariant(languageVariant);
}
function clearState() {
// Clear out the text the scanner is pointing at, so it doesn't keep anything alive unnecessarily.
scanner.clearCommentDirectives();
scanner.setText("");
scanner.setOnError(undefined);
// Clear any data. We don't want to accidentally hold onto it for too long.
sourceText = undefined!;
languageVersion = undefined!;
syntaxCursor = undefined;
scriptKind = undefined!;
languageVariant = undefined!;
sourceFlags = 0;
parseDiagnostics = undefined!;
jsDocDiagnostics = undefined!;
parsingContext = 0;
identifiers = undefined!;
notParenthesizedArrow = undefined;
topLevel = true;
}
function parseSourceFileWorker(languageVersion: ScriptTarget, setParentNodes: boolean, scriptKind: ScriptKind, setExternalModuleIndicator: (file: SourceFile) => void): SourceFile {
const isDeclarationFile = isDeclarationFileName(fileName);
if (isDeclarationFile) {
contextFlags |= NodeFlags.Ambient;
}
sourceFlags = contextFlags;
// Prime the scanner.
nextToken();
const statements = parseList(ParsingContext.SourceElements, parseStatement);
Debug.assert(token() === SyntaxKind.EndOfFileToken);
const endOfFileToken = addJSDocComment(parseTokenNode<EndOfFileToken>());
const sourceFile = createSourceFile(fileName, languageVersion, scriptKind, isDeclarationFile, statements, endOfFileToken, sourceFlags, setExternalModuleIndicator);
// 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
processCommentPragmas(sourceFile as {} as PragmaContext, sourceText);
processPragmasIntoFields(sourceFile as {} as PragmaContext, reportPragmaDiagnostic);
sourceFile.commentDirectives = scanner.getCommentDirectives();
sourceFile.nodeCount = nodeCount;
sourceFile.identifierCount = identifierCount;
sourceFile.identifiers = identifiers;
sourceFile.parseDiagnostics = attachFileToDiagnostics(parseDiagnostics, sourceFile);
if (jsDocDiagnostics) {
sourceFile.jsDocDiagnostics = attachFileToDiagnostics(jsDocDiagnostics, sourceFile);
}
if (setParentNodes) {
fixupParentReferences(sourceFile);
}
return sourceFile;
function reportPragmaDiagnostic(pos: number, end: number, diagnostic: DiagnosticMessage) {
parseDiagnostics.push(createDetachedDiagnostic(fileName, pos, end, diagnostic));
}
}
function withJSDoc<T extends HasJSDoc>(node: T, hasJSDoc: boolean): T {
return hasJSDoc ? addJSDocComment(node) : node;
}
let hasDeprecatedTag = false;
function addJSDocComment<T extends HasJSDoc>(node: T): T {
Debug.assert(!node.jsDoc); // Should only be called once per node
const jsDoc = mapDefined(getJSDocCommentRanges(node, sourceText), comment => JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos));
if (jsDoc.length) node.jsDoc = jsDoc;
if (hasDeprecatedTag) {
hasDeprecatedTag = false;
(node as Mutable<T>).flags |= NodeFlags.Deprecated;
}
return node;
}
function reparseTopLevelAwait(sourceFile: SourceFile) {
const savedSyntaxCursor = syntaxCursor;
const baseSyntaxCursor = IncrementalParser.createSyntaxCursor(sourceFile);
syntaxCursor = { currentNode };
const statements: Statement[] = [];
const savedParseDiagnostics = parseDiagnostics;
parseDiagnostics = [];
let pos = 0;
let start = findNextStatementWithAwait(sourceFile.statements, 0);
while (start !== -1) {
// append all statements between pos and start
const prevStatement = sourceFile.statements[pos];
const nextStatement = sourceFile.statements[start];
addRange(statements, sourceFile.statements, pos, start);
pos = findNextStatementWithoutAwait(sourceFile.statements, start);
// append all diagnostics associated with the copied range
const diagnosticStart = findIndex(savedParseDiagnostics, diagnostic => diagnostic.start >= prevStatement.pos);
const diagnosticEnd = diagnosticStart >= 0 ? findIndex(savedParseDiagnostics, diagnostic => diagnostic.start >= nextStatement.pos, diagnosticStart) : -1;
if (diagnosticStart >= 0) {
addRange(parseDiagnostics, savedParseDiagnostics, diagnosticStart, diagnosticEnd >= 0 ? diagnosticEnd : undefined);
}
// reparse all statements between start and pos. We skip existing diagnostics for the same range and allow the parser to generate new ones.
speculationHelper(() => {
const savedContextFlags = contextFlags;
contextFlags |= NodeFlags.AwaitContext;
scanner.setTextPos(nextStatement.pos);
nextToken();
while (token() !== SyntaxKind.EndOfFileToken) {
const startPos = scanner.getStartPos();
const statement = parseListElement(ParsingContext.SourceElements, parseStatement);
statements.push(statement);
if (startPos === scanner.getStartPos()) {
nextToken();
}
if (pos >= 0) {
const nonAwaitStatement = sourceFile.statements[pos];
if (statement.end === nonAwaitStatement.pos) {
// done reparsing this section
break;
}
if (statement.end > nonAwaitStatement.pos) {
// we ate into the next statement, so we must reparse it.
pos = findNextStatementWithoutAwait(sourceFile.statements, pos + 1);
}
}
}
contextFlags = savedContextFlags;
}, SpeculationKind.Reparse);
// find the next statement containing an `await`
start = pos >= 0 ? findNextStatementWithAwait(sourceFile.statements, pos) : -1;
}
// append all statements between pos and the end of the list
if (pos >= 0) {
const prevStatement = sourceFile.statements[pos];
addRange(statements, sourceFile.statements, pos);
// append all diagnostics associated with the copied range
const diagnosticStart = findIndex(savedParseDiagnostics, diagnostic => diagnostic.start >= prevStatement.pos);
if (diagnosticStart >= 0) {
addRange(parseDiagnostics, savedParseDiagnostics, diagnosticStart);
}
}
syntaxCursor = savedSyntaxCursor;
return factory.updateSourceFile(sourceFile, setTextRange(factory.createNodeArray(statements), sourceFile.statements));
function containsPossibleTopLevelAwait(node: Node) {
return !(node.flags & NodeFlags.AwaitContext)
&& !!(node.transformFlags & TransformFlags.ContainsPossibleTopLevelAwait);
}
function findNextStatementWithAwait(statements: NodeArray<Statement>, start: number) {
for (let i = start; i < statements.length; i++) {
if (containsPossibleTopLevelAwait(statements[i])) {
return i;
}
}
return -1;
}
function findNextStatementWithoutAwait(statements: NodeArray<Statement>, start: number) {
for (let i = start; i < statements.length; i++) {
if (!containsPossibleTopLevelAwait(statements[i])) {
return i;
}
}
return -1;
}
function currentNode(position: number) {
const node = baseSyntaxCursor.currentNode(position);
if (topLevel && node && containsPossibleTopLevelAwait(node)) {
node.intersectsChange = true;
}
return node;
}
}
export function fixupParentReferences(rootNode: Node) {
// normally parent references are set during binding. However, for clients that only need
// a syntax tree, and no semantic features, then the binding process is an unnecessary
// overhead. This functions allows us to set all the parents, without all the expense of
// binding.
setParentRecursive(rootNode, /*incremental*/ true);
}
function createSourceFile(
fileName: string,
languageVersion: ScriptTarget,
scriptKind: ScriptKind,
isDeclarationFile: boolean,
statements: readonly Statement[],
endOfFileToken: EndOfFileToken,
flags: NodeFlags,
setExternalModuleIndicator: (sourceFile: SourceFile) => void): SourceFile {
// code from createNode is inlined here so createNode won't have to deal with special case of creating source files
// this is quite rare comparing to other nodes and createNode should be as fast as possible
let sourceFile = factory.createSourceFile(statements, endOfFileToken, flags);
setTextRangePosWidth(sourceFile, 0, sourceText.length);
setFields(sourceFile);
// If we parsed this as an external module, it may contain top-level await
if (!isDeclarationFile && isExternalModule(sourceFile) && sourceFile.transformFlags & TransformFlags.ContainsPossibleTopLevelAwait) {
sourceFile = reparseTopLevelAwait(sourceFile);
setFields(sourceFile);
}
return sourceFile;
function setFields(sourceFile: SourceFile) {
sourceFile.text = sourceText;
sourceFile.bindDiagnostics = [];
sourceFile.bindSuggestionDiagnostics = undefined;
sourceFile.languageVersion = languageVersion;
sourceFile.fileName = fileName;
sourceFile.languageVariant = getLanguageVariant(scriptKind);
sourceFile.isDeclarationFile = isDeclarationFile;
sourceFile.scriptKind = scriptKind;
setExternalModuleIndicator(sourceFile);
sourceFile.setExternalModuleIndicator = setExternalModuleIndicator;
}
}
function setContextFlag(val: boolean, flag: NodeFlags) {
if (val) {
contextFlags |= flag;
}
else {
contextFlags &= ~flag;
}
}
function setDisallowInContext(val: boolean) {
setContextFlag(val, NodeFlags.DisallowInContext);
}
function setYieldContext(val: boolean) {
setContextFlag(val, NodeFlags.YieldContext);
}
function setDecoratorContext(val: boolean) {
setContextFlag(val, NodeFlags.DecoratorContext);
}
function setAwaitContext(val: boolean) {
setContextFlag(val, NodeFlags.AwaitContext);
}
function doOutsideOfContext<T>(context: NodeFlags, func: () => T): T {
// contextFlagsToClear will contain only the context flags that are
// currently set that we need to temporarily clear
// We don't just blindly reset to the previous flags to ensure
// that we do not mutate cached flags for the incremental
// parser (ThisNodeHasError, ThisNodeOrAnySubNodesHasError, and
// HasAggregatedChildData).
const contextFlagsToClear = context & contextFlags;
if (contextFlagsToClear) {
// clear the requested context flags
setContextFlag(/*val*/ false, contextFlagsToClear);
const result = func();
// restore the context flags we just cleared
setContextFlag(/*val*/ true, contextFlagsToClear);
return result;
}
// no need to do anything special as we are not in any of the requested contexts
return func();
}
function doInsideOfContext<T>(context: NodeFlags, func: () => T): T {
// contextFlagsToSet will contain only the context flags that
// are not currently set that we need to temporarily enable.
// We don't just blindly reset to the previous flags to ensure
// that we do not mutate cached flags for the incremental
// parser (ThisNodeHasError, ThisNodeOrAnySubNodesHasError, and
// HasAggregatedChildData).
const contextFlagsToSet = context & ~contextFlags;
if (contextFlagsToSet) {
// set the requested context flags
setContextFlag(/*val*/ true, contextFlagsToSet);
const result = func();
// reset the context flags we just set
setContextFlag(/*val*/ false, contextFlagsToSet);
return result;
}
// no need to do anything special as we are already in all of the requested contexts
return func();
}
function allowInAnd<T>(func: () => T): T {
return doOutsideOfContext(NodeFlags.DisallowInContext, func);
}
function disallowInAnd<T>(func: () => T): T {
return doInsideOfContext(NodeFlags.DisallowInContext, func);
}
function allowConditionalTypesAnd<T>(func: () => T): T {
return doOutsideOfContext(NodeFlags.DisallowConditionalTypesContext, func);
}
function disallowConditionalTypesAnd<T>(func: () => T): T {
return doInsideOfContext(NodeFlags.DisallowConditionalTypesContext, func);
}
function doInYieldContext<T>(func: () => T): T {
return doInsideOfContext(NodeFlags.YieldContext, func);
}
function doInDecoratorContext<T>(func: () => T): T {
return doInsideOfContext(NodeFlags.DecoratorContext, func);
}
function doInAwaitContext<T>(func: () => T): T {
return doInsideOfContext(NodeFlags.AwaitContext, func);
}
function doOutsideOfAwaitContext<T>(func: () => T): T {
return doOutsideOfContext(NodeFlags.AwaitContext, func);
}
function doInYieldAndAwaitContext<T>(func: () => T): T {
return doInsideOfContext(NodeFlags.YieldContext | NodeFlags.AwaitContext, func);
}
function doOutsideOfYieldAndAwaitContext<T>(func: () => T): T {
return doOutsideOfContext(NodeFlags.YieldContext | NodeFlags.AwaitContext, func);
}
function inContext(flags: NodeFlags) {
return (contextFlags & flags) !== 0;
}
function inYieldContext() {
return inContext(NodeFlags.YieldContext);
}
function inDisallowInContext() {
return inContext(NodeFlags.DisallowInContext);
}
function inDisallowConditionalTypesContext() {
return inContext(NodeFlags.DisallowConditionalTypesContext);
}
function inDecoratorContext() {
return inContext(NodeFlags.DecoratorContext);
}
function inAwaitContext() {
return inContext(NodeFlags.AwaitContext);
}
function parseErrorAtCurrentToken(message: DiagnosticMessage, arg0?: any): DiagnosticWithDetachedLocation | undefined {
return parseErrorAt(scanner.getTokenPos(), scanner.getTextPos(), message, arg0);
}
function parseErrorAtPosition(start: number, length: number, message: DiagnosticMessage, arg0?: any): DiagnosticWithDetachedLocation | undefined {
// Don't report another error if it would just be at the same position as the last error.
const lastError = lastOrUndefined(parseDiagnostics);
let result: DiagnosticWithDetachedLocation | undefined;
if (!lastError || start !== lastError.start) {
result = createDetachedDiagnostic(fileName, start, length, message, arg0);
parseDiagnostics.push(result);
}
// Mark that we've encountered an error. We'll set an appropriate bit on the next
// node we finish so that it can't be reused incrementally.
parseErrorBeforeNextFinishedNode = true;
return result;
}
function parseErrorAt(start: number, end: number, message: DiagnosticMessage, arg0?: any): DiagnosticWithDetachedLocation | undefined {
return parseErrorAtPosition(start, end - start, message, arg0);
}
function parseErrorAtRange(range: TextRange, message: DiagnosticMessage, arg0?: any): void {
parseErrorAt(range.pos, range.end, message, arg0);
}
function scanError(message: DiagnosticMessage, length: number): void {
parseErrorAtPosition(scanner.getTextPos(), length, message);
}
function getNodePos(): number {
return scanner.getStartPos();
}
function hasPrecedingJSDocComment() {
return scanner.hasPrecedingJSDocComment();
}
// Use this function to access the current token instead of reading the currentToken
// variable. Since function results aren't narrowed in control flow analysis, this ensures
// that the type checker doesn't make wrong assumptions about the type of the current
// token (e.g. a call to nextToken() changes the current token but the checker doesn't
// reason about this side effect). Mainstream VMs inline simple functions like this, so
// there is no performance penalty.
function token(): SyntaxKind {
return currentToken;
}
function nextTokenWithoutCheck() {
return currentToken = scanner.scan();
}
function nextTokenAnd<T>(func: () => T): T {
nextToken();
return func();
}
function nextToken(): SyntaxKind {
// if the keyword had an escape
if (isKeyword(currentToken) && (scanner.hasUnicodeEscape() || scanner.hasExtendedUnicodeEscape())) {
// issue a parse error for the escape
parseErrorAt(scanner.getTokenPos(), scanner.getTextPos(), Diagnostics.Keywords_cannot_contain_escape_characters);
}
return nextTokenWithoutCheck();
}
function nextTokenJSDoc(): JSDocSyntaxKind {
return currentToken = scanner.scanJsDocToken();
}
function reScanGreaterToken(): SyntaxKind {
return currentToken = scanner.reScanGreaterToken();
}
function reScanSlashToken(): SyntaxKind {
return currentToken = scanner.reScanSlashToken();
}
function reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind {
return currentToken = scanner.reScanTemplateToken(isTaggedTemplate);
}
function reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind {
return currentToken = scanner.reScanTemplateHeadOrNoSubstitutionTemplate();
}
function reScanLessThanToken(): SyntaxKind {
return currentToken = scanner.reScanLessThanToken();
}
function reScanHashToken(): SyntaxKind {
return currentToken = scanner.reScanHashToken();
}
function scanJsxIdentifier(): SyntaxKind {
return currentToken = scanner.scanJsxIdentifier();
}
function scanJsxText(): SyntaxKind {
return currentToken = scanner.scanJsxToken();
}
function scanJsxAttributeValue(): SyntaxKind {
return currentToken = scanner.scanJsxAttributeValue();
}
function speculationHelper<T>(callback: () => T, speculationKind: SpeculationKind): T {
// Keep track of the state we'll need to rollback to if lookahead fails (or if the
// caller asked us to always reset our state).
const saveToken = currentToken;
const saveParseDiagnosticsLength = parseDiagnostics.length;
const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode;
// Note: it is not actually necessary to save/restore the context flags here. That's
// because the saving/restoring of these flags happens naturally through the recursive
// descent nature of our parser. However, we still store this here just so we can
// assert that invariant holds.
const saveContextFlags = contextFlags;
// If we're only looking ahead, then tell the scanner to only lookahead as well.
// Otherwise, if we're actually speculatively parsing, then tell the scanner to do the
// same.
const result = speculationKind !== SpeculationKind.TryParse
? scanner.lookAhead(callback)
: scanner.tryScan(callback);
Debug.assert(saveContextFlags === contextFlags);
// If our callback returned something 'falsy' or we're just looking ahead,
// then unconditionally restore us to where we were.
if (!result || speculationKind !== SpeculationKind.TryParse) {
currentToken = saveToken;
if (speculationKind !== SpeculationKind.Reparse) {
parseDiagnostics.length = saveParseDiagnosticsLength;
}
parseErrorBeforeNextFinishedNode = saveParseErrorBeforeNextFinishedNode;
}
return result;
}
/** Invokes the provided callback then unconditionally restores the parser to the state it
* was in immediately prior to invoking the callback. The result of invoking the callback
* is returned from this function.
*/
function lookAhead<T>(callback: () => T): T {
return speculationHelper(callback, SpeculationKind.Lookahead);
}
/** Invokes the provided callback. If the callback returns something falsy, then it restores
* the parser to the state it was in immediately prior to invoking the callback. If the
* callback returns something truthy, then the parser state is not rolled back. The result
* of invoking the callback is returned from this function.
*/
function tryParse<T>(callback: () => T): T {
return speculationHelper(callback, SpeculationKind.TryParse);
}
function isBindingIdentifier(): boolean {
if (token() === SyntaxKind.Identifier) {
return true;
}
// `let await`/`let yield` in [Yield] or [Await] are allowed here and disallowed in the binder.
return token() > SyntaxKind.LastReservedWord;
}
// Ignore strict mode flag because we will report an error in type checker instead.
function isIdentifier(): boolean {
if (token() === SyntaxKind.Identifier) {
return true;
}
// If we have a 'yield' keyword, and we're in the [yield] context, then 'yield' is
// considered a keyword and is not an identifier.
if (token() === SyntaxKind.YieldKeyword && inYieldContext()) {
return false;
}
// If we have a 'await' keyword, and we're in the [Await] context, then 'await' is
// considered a keyword and is not an identifier.
if (token() === SyntaxKind.AwaitKeyword && inAwaitContext()) {
return false;
}
return token() > SyntaxKind.LastReservedWord;
}
function parseExpected(kind: SyntaxKind, diagnosticMessage?: DiagnosticMessage, shouldAdvance = true): boolean {
if (token() === kind) {
if (shouldAdvance) {
nextToken();
}
return true;
}
// Report specific message if provided with one. Otherwise, report generic fallback message.
if (diagnosticMessage) {
parseErrorAtCurrentToken(diagnosticMessage);
}
else {
parseErrorAtCurrentToken(Diagnostics._0_expected, tokenToString(kind));
}
return false;
}
const viableKeywordSuggestions = Object.keys(textToKeywordObj).filter(keyword => keyword.length > 2);
/**
* Provides a better error message than the generic "';' expected" if possible for
* known common variants of a missing semicolon, such as from a mispelled names.
*
* @param node Node preceding the expected semicolon location.
*/
function parseErrorForMissingSemicolonAfter(node: Expression | PropertyName): void {
// Tagged template literals are sometimes used in places where only simple strings are allowed, i.e.:
// module `M1` {
// ^^^^^^^^^^^ This block is parsed as a template literal like module`M1`.
if (isTaggedTemplateExpression(node)) {
parseErrorAt(skipTrivia(sourceText, node.template.pos), node.template.end, Diagnostics.Module_declaration_names_may_only_use_or_quoted_strings);
return;
}
// Otherwise, if this isn't a well-known keyword-like identifier, give the generic fallback message.
const expressionText = ts.isIdentifier(node) ? idText(node) : undefined;
if (!expressionText || !isIdentifierText(expressionText, languageVersion)) {
parseErrorAtCurrentToken(Diagnostics._0_expected, tokenToString(SyntaxKind.SemicolonToken));
return;
}
const pos = skipTrivia(sourceText, node.pos);
// Some known keywords are likely signs of syntax being used improperly.
switch (expressionText) {
case "const":
case "let":
case "var":
parseErrorAt(pos, node.end, Diagnostics.Variable_declaration_not_allowed_at_this_location);
return;
case "declare":
// If a declared node failed to parse, it would have emitted a diagnostic already.
return;
case "interface":
parseErrorForInvalidName(Diagnostics.Interface_name_cannot_be_0, Diagnostics.Interface_must_be_given_a_name, SyntaxKind.OpenBraceToken);
return;
case "is":
parseErrorAt(pos, scanner.getTextPos(), Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods);
return;
case "module":
case "namespace":
parseErrorForInvalidName(Diagnostics.Namespace_name_cannot_be_0, Diagnostics.Namespace_must_be_given_a_name, SyntaxKind.OpenBraceToken);
return;
case "type":
parseErrorForInvalidName(Diagnostics.Type_alias_name_cannot_be_0, Diagnostics.Type_alias_must_be_given_a_name, SyntaxKind.EqualsToken);
return;
}
// The user alternatively might have misspelled or forgotten to add a space after a common keyword.
const suggestion = getSpellingSuggestion(expressionText, viableKeywordSuggestions, n => n) ?? getSpaceSuggestion(expressionText);
if (suggestion) {
parseErrorAt(pos, node.end, Diagnostics.Unknown_keyword_or_identifier_Did_you_mean_0, suggestion);
return;
}
// Unknown tokens are handled with their own errors in the scanner
if (token() === SyntaxKind.Unknown) {
return;
}
// Otherwise, we know this some kind of unknown word, not just a missing expected semicolon.
parseErrorAt(pos, node.end, Diagnostics.Unexpected_keyword_or_identifier);
}
/**
* Reports a diagnostic error for the current token being an invalid name.
*
* @param blankDiagnostic Diagnostic to report for the case of the name being blank (matched tokenIfBlankName).
* @param nameDiagnostic Diagnostic to report for all other cases.
* @param tokenIfBlankName Current token if the name was invalid for being blank (not provided / skipped).
*/
function parseErrorForInvalidName(nameDiagnostic: DiagnosticMessage, blankDiagnostic: DiagnosticMessage, tokenIfBlankName: SyntaxKind) {
if (token() === tokenIfBlankName) {
parseErrorAtCurrentToken(blankDiagnostic);
}
else {
parseErrorAtCurrentToken(nameDiagnostic, scanner.getTokenValue());
}
}
function getSpaceSuggestion(expressionText: string) {
for (const keyword of viableKeywordSuggestions) {
if (expressionText.length > keyword.length + 2 && startsWith(expressionText, keyword)) {
return `${keyword} ${expressionText.slice(keyword.length)}`;
}
}
return undefined;
}
function parseSemicolonAfterPropertyName(name: PropertyName, type: TypeNode | undefined, initializer: Expression | undefined) {
if (token() === SyntaxKind.AtToken && !scanner.hasPrecedingLineBreak()) {
parseErrorAtCurrentToken(Diagnostics.Decorators_must_precede_the_name_and_all_keywords_of_property_declarations);
return;
}
if (token() === SyntaxKind.OpenParenToken) {
parseErrorAtCurrentToken(Diagnostics.Cannot_start_a_function_call_in_a_type_annotation);
nextToken();
return;
}
if (type && !canParseSemicolon()) {
if (initializer) {
parseErrorAtCurrentToken(Diagnostics._0_expected, tokenToString(SyntaxKind.SemicolonToken));
}
else {
parseErrorAtCurrentToken(Diagnostics.Expected_for_property_initializer);
}
return;
}
if (tryParseSemicolon()) {
return;
}
if (initializer) {
parseErrorAtCurrentToken(Diagnostics._0_expected, tokenToString(SyntaxKind.SemicolonToken));
return;
}
parseErrorForMissingSemicolonAfter(name);
}
function parseExpectedJSDoc(kind: JSDocSyntaxKind) {
if (token() === kind) {
nextTokenJSDoc();
return true;
}
parseErrorAtCurrentToken(Diagnostics._0_expected, tokenToString(kind));
return false;
}
function parseExpectedMatchingBrackets(openKind: SyntaxKind, closeKind: SyntaxKind, openParsed: boolean, openPosition: number) {
if (token() === closeKind) {
nextToken();
return;
}
const lastError = parseErrorAtCurrentToken(Diagnostics._0_expected, tokenToString(closeKind));
if (!openParsed) {
return;
}
if (lastError) {
addRelatedInfo(
lastError,
createDetachedDiagnostic(fileName, openPosition, 1, Diagnostics.The_parser_expected_to_find_a_1_to_match_the_0_token_here, tokenToString(openKind), tokenToString(closeKind))
);
}
}
function parseOptional(t: SyntaxKind): boolean {
if (token() === t) {
nextToken();
return true;
}
return false;
}
function parseOptionalToken<TKind extends SyntaxKind>(t: TKind): Token<TKind>;
function parseOptionalToken(t: SyntaxKind): Node | undefined {
if (token() === t) {
return parseTokenNode();
}
return undefined;
}
function parseOptionalTokenJSDoc<TKind extends JSDocSyntaxKind>(t: TKind): Token<TKind>;
function parseOptionalTokenJSDoc(t: JSDocSyntaxKind): Node | undefined {
if (token() === t) {
return parseTokenNodeJSDoc();
}
return undefined;
}
function parseExpectedToken<TKind extends SyntaxKind>(t: TKind, diagnosticMessage?: DiagnosticMessage, arg0?: any): Token<TKind>;
function parseExpectedToken(t: SyntaxKind, diagnosticMessage?: DiagnosticMessage, arg0?: any): Node {
return parseOptionalToken(t) ||
createMissingNode(t, /*reportAtCurrentPosition*/ false, diagnosticMessage || Diagnostics._0_expected, arg0 || tokenToString(t));
}
function parseExpectedTokenJSDoc<TKind extends JSDocSyntaxKind>(t: TKind): Token<TKind>;
function parseExpectedTokenJSDoc(t: JSDocSyntaxKind): Node {
return parseOptionalTokenJSDoc(t) ||
createMissingNode(t, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, tokenToString(t));
}
function parseTokenNode<T extends Node>(): T {
const pos = getNodePos();
const kind = token();
nextToken();
return finishNode(factory.createToken(kind), pos) as T;
}
function parseTokenNodeJSDoc<T extends Node>(): T {
const pos = getNodePos();
const kind = token();
nextTokenJSDoc();
return finishNode(factory.createToken(kind), pos) as T;
}
function canParseSemicolon() {
// If there's a real semicolon, then we can always parse it out.
if (token() === SyntaxKind.SemicolonToken) {
return true;
}
// We can parse out an optional semicolon in ASI cases in the following cases.
return token() === SyntaxKind.CloseBraceToken || token() === SyntaxKind.EndOfFileToken || scanner.hasPrecedingLineBreak();
}
function tryParseSemicolon() {
if (!canParseSemicolon()) {
return false;
}
if (token() === SyntaxKind.SemicolonToken) {
// consume the semicolon if it was explicitly provided.
nextToken();
}
return true;
}
function parseSemicolon(): boolean {
return tryParseSemicolon() || parseExpected(SyntaxKind.SemicolonToken);
}
function createNodeArray<T extends Node>(elements: T[], pos: number, end?: number, hasTrailingComma?: boolean): NodeArray<T> {
const array = factory.createNodeArray(elements, hasTrailingComma);
setTextRangePosEnd(array, pos, end ?? scanner.getStartPos());
return array;
}
function finishNode<T extends Node>(node: T, pos: number, end?: number): T {
setTextRangePosEnd(node, pos, end ?? scanner.getStartPos());
if (contextFlags) {
(node as Mutable<T>).flags |= contextFlags;
}
// Keep track on the node if we encountered an error while parsing it. If we did, then
// we cannot reuse the node incrementally. Once we've marked this node, clear out the
// flag so that we don't mark any subsequent nodes.
if (parseErrorBeforeNextFinishedNode) {
parseErrorBeforeNextFinishedNode = false;
(node as Mutable<T>).flags |= NodeFlags.ThisNodeHasError;
}
return node;
}
function createMissingNode<T extends Node>(kind: T["kind"], reportAtCurrentPosition: false, diagnosticMessage?: DiagnosticMessage, arg0?: any): T;
function createMissingNode<T extends Node>(kind: T["kind"], reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): T;