Merge branch 'master' into improve_type_arguments_parser_1

This commit is contained in:
kingwl
2019-01-20 01:54:49 +08:00
4827 changed files with 270186 additions and 320238 deletions
+204 -101
View File
@@ -100,6 +100,8 @@ namespace ts {
IsObjectLiteralOrClassExpressionMethod = 1 << 7,
}
let flowNodeCreated: <T extends FlowNode>(node: T) => T = identity;
const binder = createBinder();
export function bindSourceFile(file: SourceFile, options: CompilerOptions) {
@@ -143,7 +145,7 @@ namespace ts {
let symbolCount = 0;
let Symbol: { new (flags: SymbolFlags, name: __String): Symbol }; // tslint:disable-line variable-name
let Symbol: new (flags: SymbolFlags, name: __String) => Symbol; // tslint:disable-line variable-name
let classifiableNames: UnderscoreEscapedMap<true>;
const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
@@ -233,14 +235,23 @@ namespace ts {
symbol.members = createSymbolTable();
}
// On merge of const enum module with class or function, reset const enum only flag (namespaces will already recalculate)
if (symbol.constEnumOnlyModule && (symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.RegularEnum))) {
symbol.constEnumOnlyModule = false;
}
if (symbolFlags & SymbolFlags.Value) {
const { valueDeclaration } = symbol;
if (!valueDeclaration ||
(isAssignmentDeclaration(valueDeclaration) && !isAssignmentDeclaration(node)) ||
(valueDeclaration.kind !== node.kind && isEffectiveModuleDeclaration(valueDeclaration))) {
// other kinds of value declarations take precedence over modules and assignment declarations
symbol.valueDeclaration = node;
}
setValueDeclaration(symbol, node);
}
}
function setValueDeclaration(symbol: Symbol, node: Declaration): void {
const { valueDeclaration } = symbol;
if (!valueDeclaration ||
(isAssignmentDeclaration(valueDeclaration) && !isAssignmentDeclaration(node)) ||
(valueDeclaration.kind !== node.kind && isEffectiveModuleDeclaration(valueDeclaration))) {
// other kinds of value declarations take precedence over modules and assignment declarations
symbol.valueDeclaration = node;
}
}
@@ -288,7 +299,7 @@ namespace ts {
// module.exports = ...
return InternalSymbolName.ExportEquals;
case SyntaxKind.BinaryExpression:
if (getSpecialPropertyAssignmentKind(node as BinaryExpression) === SpecialPropertyAssignmentKind.ModuleExports) {
if (getAssignmentDeclarationKind(node as BinaryExpression) === AssignmentDeclarationKind.ModuleExports) {
// module.exports = ...
return InternalSymbolName.ExportEquals;
}
@@ -374,8 +385,8 @@ namespace ts {
// prototype symbols like methods.
symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name));
}
else if (!(includes & SymbolFlags.Variable && symbol.flags & SymbolFlags.JSContainer)) {
// JSContainers are allowed to merge with variables, no matter what other flags they have.
else if (!(includes & SymbolFlags.Variable && symbol.flags & SymbolFlags.Assignment)) {
// Assignment declarations are allowed to merge with variables, no matter what other flags they have.
if (isNamedDeclaration(node)) {
node.name.parent = node;
}
@@ -461,7 +472,7 @@ namespace ts {
// during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation
// and this case is specially handled. Module augmentations should only be merged with original module definition
// and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed.
if (isJSDocTypeAlias(node)) Debug.assert(isInJavaScriptFile(node)); // We shouldn't add symbols for JSDoc nodes if not in a JS file.
if (isJSDocTypeAlias(node)) Debug.assert(isInJSFile(node)); // We shouldn't add symbols for JSDoc nodes if not in a JS file.
if ((!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) || isJSDocTypeAlias(node)) {
if (hasModifier(node, ModifierFlags.Default) && !getDeclarationName(node)) {
return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes); // No local symbol for an unnamed default!
@@ -521,6 +532,7 @@ namespace ts {
blockScopeContainer.locals = undefined;
}
if (containerFlags & ContainerFlags.IsControlFlowContainer) {
const saveFlowNodeCreated = flowNodeCreated;
const saveCurrentFlow = currentFlow;
const saveBreakTarget = currentBreakTarget;
const saveContinueTarget = currentContinueTarget;
@@ -544,6 +556,7 @@ namespace ts {
currentContinueTarget = undefined;
activeLabels = undefined;
hasExplicitReturn = false;
flowNodeCreated = identity;
bindChildren(node);
// Reset all reachability check related flags on node (for incremental scenarios)
node.flags &= ~NodeFlags.ReachabilityAndEmitFlags;
@@ -570,6 +583,7 @@ namespace ts {
currentReturnTarget = saveReturnTarget;
activeLabels = saveActiveLabels;
hasExplicitReturn = saveHasExplicitReturn;
flowNodeCreated = saveFlowNodeCreated;
}
else if (containerFlags & ContainerFlags.IsInterface) {
seenThisKeyword = false;
@@ -636,6 +650,7 @@ namespace ts {
function bindChildrenWorker(node: Node): void {
if (checkUnreachable(node)) {
bindEachChild(node);
bindJSDoc(node);
return;
}
switch (node.kind) {
@@ -743,7 +758,7 @@ namespace ts {
function isNarrowableReference(expr: Expression): boolean {
return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword ||
isPropertyAccessExpression(expr) && isNarrowableReference(expr.expression) ||
(isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) ||
isElementAccessExpression(expr) && expr.argumentExpression &&
(isStringLiteral(expr.argumentExpression) || isNumericLiteral(expr.argumentExpression)) &&
isNarrowableReference(expr.expression);
@@ -848,7 +863,7 @@ namespace ts {
return antecedent;
}
setFlowNodeReferenced(antecedent);
return { flags, expression, antecedent };
return flowNodeCreated({ flags, expression, antecedent });
}
function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
@@ -856,17 +871,17 @@ namespace ts {
return antecedent;
}
setFlowNodeReferenced(antecedent);
return { flags: FlowFlags.SwitchClause, switchStatement, clauseStart, clauseEnd, antecedent };
return flowNodeCreated({ flags: FlowFlags.SwitchClause, switchStatement, clauseStart, clauseEnd, antecedent });
}
function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode {
setFlowNodeReferenced(antecedent);
return { flags: FlowFlags.Assignment, antecedent, node };
return flowNodeCreated({ flags: FlowFlags.Assignment, antecedent, node });
}
function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode {
setFlowNodeReferenced(antecedent);
const res: FlowArrayMutation = { flags: FlowFlags.ArrayMutation, antecedent, node };
const res: FlowArrayMutation = flowNodeCreated({ flags: FlowFlags.ArrayMutation, antecedent, node });
return res;
}
@@ -1070,8 +1085,16 @@ namespace ts {
function bindTryStatement(node: TryStatement): void {
const preFinallyLabel = createBranchLabel();
const preTryFlow = currentFlow;
// TODO: Every statement in try block is potentially an exit point!
const tryPriors: FlowNode[] = [];
const oldFlowNodeCreated = flowNodeCreated;
// We hook the creation of all flow nodes within the `try` scope and store them so we can add _all_ of them
// as possible antecedents of the start of the `catch` or `finally` blocks.
// Don't bother intercepting the call if there's no finally or catch block that needs the information
if (node.catchClause || node.finallyBlock) {
flowNodeCreated = node => (tryPriors.push(node), node);
}
bind(node.tryBlock);
flowNodeCreated = oldFlowNodeCreated;
addAntecedent(preFinallyLabel, currentFlow);
const flowAfterTry = currentFlow;
@@ -1079,12 +1102,32 @@ namespace ts {
if (node.catchClause) {
currentFlow = preTryFlow;
if (tryPriors.length) {
const preCatchFlow = createBranchLabel();
addAntecedent(preCatchFlow, currentFlow);
for (const p of tryPriors) {
addAntecedent(preCatchFlow, p);
}
currentFlow = finishFlowLabel(preCatchFlow);
}
bind(node.catchClause);
addAntecedent(preFinallyLabel, currentFlow);
flowAfterCatch = currentFlow;
}
if (node.finallyBlock) {
// We add the nodes within the `try` block to the `finally`'s antecedents if there's no catch block
// (If there is a `catch` block, it will have all these antecedents instead, and the `finally` will
// have the end of the `try` block and the end of the `catch` block)
if (!node.catchClause) {
if (tryPriors.length) {
for (const p of tryPriors) {
addAntecedent(preFinallyLabel, p);
}
}
}
// in finally flow is combined from pre-try/flow from try/flow from catch
// pre-flow is necessary to make sure that finally is reachable even if finally flows in both try and finally blocks are unreachable
@@ -1132,7 +1175,7 @@ namespace ts {
}
}
if (!(currentFlow.flags & FlowFlags.Unreachable)) {
const afterFinallyFlow: AfterFinallyFlow = { flags: FlowFlags.AfterFinally, antecedent: currentFlow };
const afterFinallyFlow: AfterFinallyFlow = flowNodeCreated({ flags: FlowFlags.AfterFinally, antecedent: currentFlow });
preFinallyFlow.lock = afterFinallyFlow;
currentFlow = afterFinallyFlow;
}
@@ -1176,7 +1219,6 @@ namespace ts {
}
const preCaseLabel = createBranchLabel();
addAntecedent(preCaseLabel, createFlowSwitchClause(preSwitchCaseFlow!, node.parent, clauseStart, i + 1));
addAntecedent(preCaseLabel, createFlowSwitchClause(preSwitchCaseFlow!, node.parent, clauseStart, i + 1));
addAntecedent(preCaseLabel, fallthroughFlow);
currentFlow = finishFlowLabel(preCaseLabel);
const clause = clauses[i];
@@ -1375,6 +1417,7 @@ namespace ts {
}
function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag) {
node.tagName.parent = node;
if (node.fullName) {
setParentPointers(node, node.fullName);
}
@@ -2009,7 +2052,7 @@ namespace ts {
function bindJSDoc(node: Node) {
if (hasJSDocNodes(node)) {
if (isInJavaScriptFile(node)) {
if (isInJSFile(node)) {
for (const j of node.jsDoc!) {
bind(j);
}
@@ -2075,7 +2118,7 @@ namespace ts {
if (isSpecialPropertyDeclaration(node as PropertyAccessExpression)) {
bindSpecialPropertyDeclaration(node as PropertyAccessExpression);
}
if (isInJavaScriptFile(node) &&
if (isInJSFile(node) &&
file.commonJsModuleIndicator &&
isModuleExportsPropertyAccessExpression(node as PropertyAccessExpression) &&
!lookupSymbolForNameWorker(blockScopeContainer, "module" as __String)) {
@@ -2084,31 +2127,31 @@ namespace ts {
}
break;
case SyntaxKind.BinaryExpression:
const specialKind = getSpecialPropertyAssignmentKind(node as BinaryExpression);
const specialKind = getAssignmentDeclarationKind(node as BinaryExpression);
switch (specialKind) {
case SpecialPropertyAssignmentKind.ExportsProperty:
case AssignmentDeclarationKind.ExportsProperty:
bindExportsPropertyAssignment(node as BinaryExpression);
break;
case SpecialPropertyAssignmentKind.ModuleExports:
case AssignmentDeclarationKind.ModuleExports:
bindModuleExportsAssignment(node as BinaryExpression);
break;
case SpecialPropertyAssignmentKind.PrototypeProperty:
case AssignmentDeclarationKind.PrototypeProperty:
bindPrototypePropertyAssignment((node as BinaryExpression).left as PropertyAccessEntityNameExpression, node);
break;
case SpecialPropertyAssignmentKind.Prototype:
case AssignmentDeclarationKind.Prototype:
bindPrototypeAssignment(node as BinaryExpression);
break;
case SpecialPropertyAssignmentKind.ThisProperty:
case AssignmentDeclarationKind.ThisProperty:
bindThisPropertyAssignment(node as BinaryExpression);
break;
case SpecialPropertyAssignmentKind.Property:
case AssignmentDeclarationKind.Property:
bindSpecialPropertyAssignment(node as BinaryExpression);
break;
case SpecialPropertyAssignmentKind.None:
case AssignmentDeclarationKind.None:
// Nothing to do
break;
default:
Debug.fail("Unknown special property assignment kind");
Debug.fail("Unknown binary expression special property assignment kind");
}
return checkStrictModeBinaryExpression(<BinaryExpression>node);
case SyntaxKind.CatchClause:
@@ -2184,7 +2227,20 @@ namespace ts {
return bindFunctionExpression(<FunctionExpression>node);
case SyntaxKind.CallExpression:
if (isInJavaScriptFile(node)) {
const assignmentKind = getAssignmentDeclarationKind(node as CallExpression);
switch (assignmentKind) {
case AssignmentDeclarationKind.ObjectDefinePropertyValue:
return bindObjectDefinePropertyAssignment(node as BindableObjectDefinePropertyCall);
case AssignmentDeclarationKind.ObjectDefinePropertyExports:
return bindObjectDefinePropertyExport(node as BindableObjectDefinePropertyCall);
case AssignmentDeclarationKind.ObjectDefinePrototypeProperty:
return bindObjectDefinePrototypeProperty(node as BindableObjectDefinePropertyCall);
case AssignmentDeclarationKind.None:
break; // Nothing to do
default:
return Debug.fail("Unknown call expression assignment declaration kind");
}
if (isInJSFile(node)) {
bindCallExpression(<CallExpression>node);
}
break;
@@ -2286,14 +2342,19 @@ namespace ts {
bindAnonymousDeclaration(node, SymbolFlags.Alias, getDeclarationName(node)!);
}
else {
const flags = node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node)
const flags = exportAssignmentIsAlias(node)
// An export default clause with an EntityNameExpression or a class expression exports all meanings of that identifier or expression;
? SymbolFlags.Alias
// An export default clause with any other expression exports a value
: SymbolFlags.Property;
// If there is an `export default x;` alias declaration, can't `export default` anything else.
// (In contrast, you can still have `export default function f() {}` and `export default interface I {}`.)
declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.All);
const symbol = declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.All);
if (node.isExportEquals) {
// Will be an error later, since the module already has other exports. Just make sure this has a valueDeclaration set.
setValueDeclaration(symbol, node);
}
}
}
@@ -2301,27 +2362,17 @@ namespace ts {
if (node.modifiers && node.modifiers.length) {
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Modifiers_cannot_appear_here));
}
if (node.parent.kind !== SyntaxKind.SourceFile) {
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Global_module_exports_may_only_appear_at_top_level));
return;
const diag = !isSourceFile(node.parent) ? Diagnostics.Global_module_exports_may_only_appear_at_top_level
: !isExternalModule(node.parent) ? Diagnostics.Global_module_exports_may_only_appear_in_module_files
: !node.parent.isDeclarationFile ? Diagnostics.Global_module_exports_may_only_appear_in_declaration_files
: undefined;
if (diag) {
file.bindDiagnostics.push(createDiagnosticForNode(node, diag));
}
else {
const parent = node.parent as SourceFile;
if (!isExternalModule(parent)) {
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Global_module_exports_may_only_appear_in_module_files));
return;
}
if (!parent.isDeclarationFile) {
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Global_module_exports_may_only_appear_in_declaration_files));
return;
}
file.symbol.globalExports = file.symbol.globalExports || createSymbolTable();
declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
}
file.symbol.globalExports = file.symbol.globalExports || createSymbolTable();
declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
}
function bindExportDeclaration(node: ExportDeclaration) {
@@ -2352,6 +2403,22 @@ namespace ts {
return true;
}
function bindObjectDefinePropertyExport(node: BindableObjectDefinePropertyCall) {
if (!setCommonJsModuleIndicator(node)) {
return;
}
const symbol = forEachIdentifierInEntityName(node.arguments[0], /*parent*/ undefined, (id, symbol) => {
if (symbol) {
addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment);
}
return symbol;
});
if (symbol) {
const flags = SymbolFlags.Property | SymbolFlags.ExportValue;
declareSymbol(symbol.exports!, symbol, node, flags, SymbolFlags.None);
}
}
function bindExportsPropertyAssignment(node: BinaryExpression) {
// When we create a property via 'exports.foo = bar', the 'exports.foo' property access
// expression is the declaration
@@ -2361,7 +2428,7 @@ namespace ts {
const lhs = node.left as PropertyAccessEntityNameExpression;
const symbol = forEachIdentifierInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => {
if (symbol) {
addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.JSContainer);
addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment);
}
return symbol;
});
@@ -2390,11 +2457,11 @@ namespace ts {
const flags = exportAssignmentIsAlias(node)
? SymbolFlags.Alias // An export= with an EntityNameExpression or a ClassExpression exports all meanings of that identifier or class
: SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.ValueModule;
declareSymbol(file.symbol.exports!, file.symbol, node, flags, SymbolFlags.None);
declareSymbol(file.symbol.exports!, file.symbol, node, flags | SymbolFlags.Assignment, SymbolFlags.None);
}
function bindThisPropertyAssignment(node: BinaryExpression | PropertyAccessExpression) {
Debug.assert(isInJavaScriptFile(node));
Debug.assert(isInJSFile(node));
const thisContainer = getThisContainer(node, /*includeArrowFunctions*/ false);
switch (thisContainer.kind) {
case SyntaxKind.FunctionDeclaration:
@@ -2456,7 +2523,12 @@ namespace ts {
node.left.parent = node;
node.right.parent = node;
const lhs = node.left as PropertyAccessEntityNameExpression;
bindPropertyAssignment(lhs, lhs, /*isPrototypeProperty*/ false);
bindPropertyAssignment(lhs.expression, lhs, /*isPrototypeProperty*/ false);
}
function bindObjectDefinePrototypeProperty(node: BindableObjectDefinePropertyCall) {
const namespaceSymbol = lookupSymbolForPropertyAccess((node.arguments[0] as PropertyAccessExpression).expression as EntityNameExpression);
bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ true);
}
/**
@@ -2477,12 +2549,18 @@ namespace ts {
bindPropertyAssignment(constructorFunction, lhs, /*isPrototypeProperty*/ true);
}
function bindObjectDefinePropertyAssignment(node: BindableObjectDefinePropertyCall) {
let namespaceSymbol = lookupSymbolForPropertyAccess(node.arguments[0]);
const isToplevel = node.parent.parent.kind === SyntaxKind.SourceFile;
namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, node.arguments[0], isToplevel, /*isPrototypeProperty*/ false);
bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ false);
}
function bindSpecialPropertyAssignment(node: BinaryExpression) {
const lhs = node.left as PropertyAccessEntityNameExpression;
// Class declarations in Typescript do not allow property declarations
const parentSymbol = lookupSymbolForPropertyAccess(lhs.expression);
if (!isInJavaScriptFile(node) && !isFunctionSymbol(parentSymbol)) {
if (!isInJSFile(node) && !isFunctionSymbol(parentSymbol)) {
return;
}
// Fix up parent pointers since we're going to use these nodes before we bind into them
@@ -2508,26 +2586,28 @@ namespace ts {
bindPropertyAssignment(node.expression, node, /*isPrototypeProperty*/ false);
}
function bindPropertyAssignment(name: EntityNameExpression, propertyAccess: PropertyAccessEntityNameExpression, isPrototypeProperty: boolean) {
let namespaceSymbol = lookupSymbolForPropertyAccess(name);
const isToplevel = isBinaryExpression(propertyAccess.parent)
? getParentOfBinaryExpression(propertyAccess.parent).parent.kind === SyntaxKind.SourceFile
: propertyAccess.parent.parent.kind === SyntaxKind.SourceFile;
if (!isPrototypeProperty && (!namespaceSymbol || !(namespaceSymbol.flags & SymbolFlags.Namespace)) && isToplevel) {
function bindPotentiallyMissingNamespaces(namespaceSymbol: Symbol | undefined, entityName: EntityNameExpression, isToplevel: boolean, isPrototypeProperty: boolean) {
if (isToplevel && !isPrototypeProperty && (!namespaceSymbol || !(namespaceSymbol.flags & SymbolFlags.Namespace))) {
// make symbols or add declarations for intermediate containers
const flags = SymbolFlags.Module | SymbolFlags.JSContainer;
const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.JSContainer;
namespaceSymbol = forEachIdentifierInEntityName(propertyAccess.expression, namespaceSymbol, (id, symbol, parent) => {
const flags = SymbolFlags.Module | SymbolFlags.Assignment;
const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.Assignment;
namespaceSymbol = forEachIdentifierInEntityName(entityName, namespaceSymbol, (id, symbol, parent) => {
if (symbol) {
addDeclarationToSymbol(symbol, id, flags);
return symbol;
}
else {
return declareSymbol(parent ? parent.exports! : container.locals!, parent, id, flags, excludeFlags);
const table = parent ? parent.exports! :
file.jsGlobalAugmentations || (file.jsGlobalAugmentations = createSymbolTable());
return declareSymbol(table, parent, id, flags, excludeFlags);
}
});
}
if (!namespaceSymbol || !isJavascriptContainer(namespaceSymbol)) {
return namespaceSymbol;
}
function bindPotentiallyNewExpandoMemberToNamespace(declaration: PropertyAccessEntityNameExpression | CallExpression, namespaceSymbol: Symbol | undefined, isPrototypeProperty: boolean) {
if (!namespaceSymbol || !isExpandoSymbol(namespaceSymbol)) {
return;
}
@@ -2536,14 +2616,23 @@ namespace ts {
(namespaceSymbol.members || (namespaceSymbol.members = createSymbolTable())) :
(namespaceSymbol.exports || (namespaceSymbol.exports = createSymbolTable()));
const isMethod = isFunctionLikeDeclaration(getAssignedJavascriptInitializer(propertyAccess)!);
const isMethod = isFunctionLikeDeclaration(getAssignedExpandoInitializer(declaration)!);
const includes = isMethod ? SymbolFlags.Method : SymbolFlags.Property;
const excludes = isMethod ? SymbolFlags.MethodExcludes : SymbolFlags.PropertyExcludes;
declareSymbol(symbolTable, namespaceSymbol, propertyAccess, includes | SymbolFlags.JSContainer, excludes & ~SymbolFlags.JSContainer);
declareSymbol(symbolTable, namespaceSymbol, declaration, includes | SymbolFlags.Assignment, excludes & ~SymbolFlags.Assignment);
}
function bindPropertyAssignment(name: EntityNameExpression, propertyAccess: PropertyAccessEntityNameExpression, isPrototypeProperty: boolean) {
let namespaceSymbol = lookupSymbolForPropertyAccess(name);
const isToplevel = isBinaryExpression(propertyAccess.parent)
? getParentOfBinaryExpression(propertyAccess.parent).parent.kind === SyntaxKind.SourceFile
: propertyAccess.parent.parent.kind === SyntaxKind.SourceFile;
namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, propertyAccess.expression, isToplevel, isPrototypeProperty);
bindPotentiallyNewExpandoMemberToNamespace(propertyAccess, namespaceSymbol, isPrototypeProperty);
}
/**
* Javascript containers are:
* Javascript expando values are:
* - Functions
* - classes
* - namespaces
@@ -2552,11 +2641,14 @@ namespace ts {
* - with empty object literals
* - with non-empty object literals if assigned to the prototype property
*/
function isJavascriptContainer(symbol: Symbol): boolean {
function isExpandoSymbol(symbol: Symbol): boolean {
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.NamespaceModule)) {
return true;
}
const node = symbol.valueDeclaration;
if (node && isCallExpression(node)) {
return !!getAssignedExpandoInitializer(node);
}
let init = !node ? undefined :
isVariableDeclaration(node) ? node.initializer :
isBinaryExpression(node) ? node.right :
@@ -2565,7 +2657,7 @@ namespace ts {
init = init && getRightMostAssignedExpression(init);
if (init) {
const isPrototypeAssignment = isPrototypeAccess(isVariableDeclaration(node) ? node.name : isBinaryExpression(node) ? node.left : node);
return !!getJavascriptInitializer(isBinaryExpression(init) && init.operatorToken.kind === SyntaxKind.BarBarToken ? init.right : init, isPrototypeAssignment);
return !!getExpandoInitializer(isBinaryExpression(init) && init.operatorToken.kind === SyntaxKind.BarBarToken ? init.right : init, isPrototypeAssignment);
}
return false;
}
@@ -2657,7 +2749,7 @@ namespace ts {
}
if (!isBindingPattern(node.name)) {
const isEnum = !!getJSDocEnumTag(node);
const isEnum = isInJSFile(node) && !!getJSDocEnumTag(node);
const enumFlags = (isEnum ? SymbolFlags.RegularEnum : SymbolFlags.None);
const enumExcludes = (isEnum ? SymbolFlags.RegularEnumExcludes : SymbolFlags.None);
if (isBlockOrCatchScoped(node)) {
@@ -2868,7 +2960,6 @@ namespace ts {
}
}
/* @internal */
export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Expression): boolean {
return isExportsIdentifier(node) ||
isModuleExportsPropertyAccessExpression(node) ||
@@ -2892,6 +2983,9 @@ namespace ts {
if (local) {
return local.exportSymbol || local;
}
if (isSourceFile(container) && container.jsGlobalAugmentations && container.jsGlobalAugmentations.has(name)) {
return container.jsGlobalAugmentations.get(name);
}
return container.symbol && container.symbol.exports && container.symbol.exports.get(name);
}
@@ -2996,7 +3090,7 @@ namespace ts {
transformFlags |= TransformFlags.AssertTypeScript;
}
if (subtreeFlags & TransformFlags.ContainsSpread
if (subtreeFlags & TransformFlags.ContainsRestOrSpread
|| (expression.transformFlags & (TransformFlags.Super | TransformFlags.ContainsSuper))) {
// If the this node contains a SpreadExpression, or is a super call, then it is an ES6
// node.
@@ -3027,7 +3121,7 @@ namespace ts {
if (node.typeArguments) {
transformFlags |= TransformFlags.AssertTypeScript;
}
if (subtreeFlags & TransformFlags.ContainsSpread) {
if (subtreeFlags & TransformFlags.ContainsRestOrSpread) {
// If the this node contains a SpreadElementExpression then it is an ES6
// node.
transformFlags |= TransformFlags.AssertES2015;
@@ -3070,18 +3164,18 @@ namespace ts {
// syntax.
if (node.questionToken
|| node.type
|| subtreeFlags & TransformFlags.ContainsDecorators
|| (subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax && some(node.decorators))
|| isThisIdentifier(name)) {
transformFlags |= TransformFlags.AssertTypeScript;
}
// If a parameter has an accessibility modifier, then it is TypeScript syntax.
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsParameterPropertyAssignments;
transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsTypeScriptClassSyntax;
}
// parameters with object rest destructuring are ES Next syntax
if (subtreeFlags & TransformFlags.ContainsObjectRest) {
if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) {
transformFlags |= TransformFlags.AssertESNext;
}
@@ -3134,7 +3228,7 @@ namespace ts {
// TypeScript syntax.
// An exported declaration may be TypeScript syntax, but is handled by the visitor
// for a namespace declaration.
if ((subtreeFlags & TransformFlags.TypeScriptClassSyntaxMask)
if ((subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax)
|| node.typeParameters) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -3156,7 +3250,7 @@ namespace ts {
// A class with a parameter property assignment, property initializer, or decorator is
// TypeScript syntax.
if (subtreeFlags & TransformFlags.TypeScriptClassSyntaxMask
if (subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax
|| node.typeParameters) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -3233,7 +3327,7 @@ namespace ts {
}
// function declarations with object rest destructuring are ES Next syntax
if (subtreeFlags & TransformFlags.ContainsObjectRest) {
if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) {
transformFlags |= TransformFlags.AssertESNext;
}
@@ -3257,7 +3351,7 @@ namespace ts {
}
// function declarations with object rest destructuring are ES Next syntax
if (subtreeFlags & TransformFlags.ContainsObjectRest) {
if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) {
transformFlags |= TransformFlags.AssertESNext;
}
@@ -3288,7 +3382,7 @@ namespace ts {
}
// function declarations with object rest destructuring are ES Next syntax
if (subtreeFlags & TransformFlags.ContainsObjectRest) {
if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) {
transformFlags |= TransformFlags.AssertESNext;
}
@@ -3303,7 +3397,7 @@ namespace ts {
// If the PropertyDeclaration has an initializer or a computed name, we need to inform its ancestor
// so that it handle the transformation.
if (node.initializer || isComputedPropertyName(node.name)) {
transformFlags |= TransformFlags.ContainsPropertyInitializer;
transformFlags |= TransformFlags.ContainsTypeScriptClassSyntax;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
@@ -3337,7 +3431,7 @@ namespace ts {
}
// function declarations with object rest destructuring are ES Next syntax
if (subtreeFlags & TransformFlags.ContainsObjectRest) {
if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) {
transformFlags |= TransformFlags.AssertESNext;
}
@@ -3379,7 +3473,7 @@ namespace ts {
}
// function expressions with object rest destructuring are ES Next syntax
if (subtreeFlags & TransformFlags.ContainsObjectRest) {
if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) {
transformFlags |= TransformFlags.AssertESNext;
}
@@ -3420,7 +3514,7 @@ namespace ts {
}
// arrow functions with object rest destructuring are ES Next syntax
if (subtreeFlags & TransformFlags.ContainsObjectRest) {
if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) {
transformFlags |= TransformFlags.AssertESNext;
}
@@ -3440,7 +3534,9 @@ namespace ts {
// ES6 syntax, and requires a lexical `this` binding.
if (transformFlags & TransformFlags.Super) {
transformFlags ^= TransformFlags.Super;
transformFlags |= TransformFlags.ContainsSuper;
// super inside of an async function requires hoisting the super access (ES2017).
// same for super inside of an async generator, which is ESNext.
transformFlags |= TransformFlags.ContainsSuper | TransformFlags.ContainsES2017 | TransformFlags.ContainsESNext;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
@@ -3456,7 +3552,9 @@ namespace ts {
// ES6 syntax, and requires a lexical `this` binding.
if (expressionFlags & TransformFlags.Super) {
transformFlags &= ~TransformFlags.Super;
transformFlags |= TransformFlags.ContainsSuper;
// super inside of an async function requires hoisting the super access (ES2017).
// same for super inside of an async generator, which is ESNext.
transformFlags |= TransformFlags.ContainsSuper | TransformFlags.ContainsES2017 | TransformFlags.ContainsESNext;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
@@ -3468,7 +3566,7 @@ namespace ts {
transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern;
// A VariableDeclaration containing ObjectRest is ESNext syntax
if (subtreeFlags & TransformFlags.ContainsObjectRest) {
if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) {
transformFlags |= TransformFlags.AssertESNext;
}
@@ -3642,6 +3740,10 @@ namespace ts {
}
break;
case SyntaxKind.BigIntLiteral:
transformFlags |= TransformFlags.AssertESNext;
break;
case SyntaxKind.ForOfStatement:
// This node is either ES2015 syntax or ES2017 syntax (if it is a for-await-of).
if ((<ForOfStatement>node).awaitModifier) {
@@ -3658,6 +3760,7 @@ namespace ts {
case SyntaxKind.AnyKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.BigIntKeyword:
case SyntaxKind.NeverKeyword:
case SyntaxKind.ObjectKeyword:
case SyntaxKind.StringKeyword:
@@ -3717,11 +3820,11 @@ namespace ts {
break;
case SyntaxKind.SpreadElement:
transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsSpread;
transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsRestOrSpread;
break;
case SyntaxKind.SpreadAssignment:
transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectSpread;
transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectRestOrSpread;
break;
case SyntaxKind.SuperKeyword:
@@ -3737,8 +3840,8 @@ namespace ts {
case SyntaxKind.ObjectBindingPattern:
transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern;
if (subtreeFlags & TransformFlags.ContainsRest) {
transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectRest;
if (subtreeFlags & TransformFlags.ContainsRestOrSpread) {
transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectRestOrSpread;
}
excludeFlags = TransformFlags.BindingPatternExcludes;
break;
@@ -3751,13 +3854,13 @@ namespace ts {
case SyntaxKind.BindingElement:
transformFlags |= TransformFlags.AssertES2015;
if ((<BindingElement>node).dotDotDotToken) {
transformFlags |= TransformFlags.ContainsRest;
transformFlags |= TransformFlags.ContainsRestOrSpread;
}
break;
case SyntaxKind.Decorator:
// This node is TypeScript syntax, and marks its container as also being TypeScript syntax.
transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsDecorators;
transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsTypeScriptClassSyntax;
break;
case SyntaxKind.ObjectLiteralExpression:
@@ -3774,7 +3877,7 @@ namespace ts {
transformFlags |= TransformFlags.ContainsLexicalThis;
}
if (subtreeFlags & TransformFlags.ContainsObjectSpread) {
if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) {
// If an ObjectLiteralExpression contains a spread element, then it
// is an ES next node.
transformFlags |= TransformFlags.AssertESNext;
@@ -3785,7 +3888,7 @@ namespace ts {
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.NewExpression:
excludeFlags = TransformFlags.ArrayLiteralOrCallOrNewExcludes;
if (subtreeFlags & TransformFlags.ContainsSpread) {
if (subtreeFlags & TransformFlags.ContainsRestOrSpread) {
// If the this node contains a SpreadExpression, then it is an ES6
// node.
transformFlags |= TransformFlags.AssertES2015;
@@ -3833,7 +3936,6 @@ namespace ts {
* For performance reasons, `computeTransformFlagsForNode` uses local constant values rather
* than calling this function.
*/
/* @internal */
export function getTransformFlagsSubtreeExclusions(kind: SyntaxKind) {
if (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) {
return TransformFlags.TypeExcludes;
@@ -3866,6 +3968,7 @@ namespace ts {
return TransformFlags.MethodOrAccessorExcludes;
case SyntaxKind.AnyKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.BigIntKeyword:
case SyntaxKind.NeverKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.ObjectKeyword:
+318 -81
View File
@@ -38,6 +38,10 @@ namespace ts {
* Already seen affected files
*/
seenAffectedFiles: Map<true> | undefined;
/**
* whether this program has cleaned semantic diagnostics cache for lib files
*/
cleanedDiagnosticsOfLibFiles?: boolean;
/**
* True if the semantic diagnostics were copied from the old state
*/
@@ -45,7 +49,27 @@ namespace ts {
/**
* program corresponding to this state
*/
program: Program;
program: Program | undefined;
/**
* compilerOptions for the program
*/
compilerOptions: CompilerOptions;
/**
* Files pending to be emitted
*/
affectedFilesPendingEmit: ReadonlyArray<Path> | undefined;
/**
* Current index to retrieve pending affected file
*/
affectedFilesPendingEmitIndex: number | undefined;
/**
* Already seen affected files
*/
seenEmittedFiles: Map<true> | undefined;
/**
* true if program has been emitted
*/
programEmitComplete?: true;
}
function hasSameKeys<T, U>(map1: ReadonlyMap<T> | undefined, map2: ReadonlyMap<U> | undefined): boolean {
@@ -60,17 +84,23 @@ namespace ts {
const state = BuilderState.create(newProgram, getCanonicalFileName, oldState) as BuilderProgramState;
state.program = newProgram;
const compilerOptions = newProgram.getCompilerOptions();
if (!compilerOptions.outFile && !compilerOptions.out) {
state.compilerOptions = compilerOptions;
// With --out or --outFile, any change affects all semantic diagnostics so no need to cache them
// With --isolatedModules, emitting changed file doesnt emit dependent files so we cant know of dependent files to retrieve errors so dont cache the errors
if (!compilerOptions.outFile && !compilerOptions.out && !compilerOptions.isolatedModules) {
state.semanticDiagnosticsPerFile = createMap<ReadonlyArray<Diagnostic>>();
}
state.changedFilesSet = createMap<true>();
const useOldState = BuilderState.canReuseOldState(state.referencedMap, oldState);
const oldCompilerOptions = useOldState ? oldState!.compilerOptions : undefined;
const canCopySemanticDiagnostics = useOldState && oldState!.semanticDiagnosticsPerFile && !!state.semanticDiagnosticsPerFile &&
!compilerOptionsAffectSemanticDiagnostics(compilerOptions, oldState!.program.getCompilerOptions());
!compilerOptionsAffectSemanticDiagnostics(compilerOptions, oldCompilerOptions!);
if (useOldState) {
// Verify the sanity of old state
if (!oldState!.currentChangedFilePath) {
Debug.assert(!oldState!.affectedFiles && (!oldState!.currentAffectedFilesSignatures || !oldState!.currentAffectedFilesSignatures!.size), "Cannot reuse if only few affected files of currentChangedFile were iterated");
const affectedSignatures = oldState!.currentAffectedFilesSignatures;
Debug.assert(!oldState!.affectedFiles && (!affectedSignatures || !affectedSignatures.size), "Cannot reuse if only few affected files of currentChangedFile were iterated");
}
if (canCopySemanticDiagnostics) {
Debug.assert(!forEachKey(oldState!.changedFilesSet, path => oldState!.semanticDiagnosticsPerFile!.has(path)), "Semantic diagnostics shouldnt be available for changed files");
@@ -78,11 +108,17 @@ namespace ts {
// Copy old state's changed files set
copyEntries(oldState!.changedFilesSet, state.changedFilesSet);
if (!compilerOptions.outFile && !compilerOptions.out && oldState!.affectedFilesPendingEmit) {
state.affectedFilesPendingEmit = oldState!.affectedFilesPendingEmit;
state.affectedFilesPendingEmitIndex = oldState!.affectedFilesPendingEmitIndex;
}
}
// Update changed files and copy semantic diagnostics if we can
const referencedMap = state.referencedMap;
const oldReferencedMap = useOldState ? oldState!.referencedMap : undefined;
const copyDeclarationFileDiagnostics = canCopySemanticDiagnostics && !compilerOptions.skipLibCheck === !oldCompilerOptions!.skipLibCheck;
const copyLibFileDiagnostics = copyDeclarationFileDiagnostics && !compilerOptions.skipDefaultLibCheck === !oldCompilerOptions!.skipDefaultLibCheck;
state.fileInfos.forEach((info, sourceFilePath) => {
let oldInfo: Readonly<BuilderState.FileInfo> | undefined;
let newReferences: BuilderState.ReferencedSet | undefined;
@@ -101,6 +137,11 @@ namespace ts {
state.changedFilesSet.set(sourceFilePath, true);
}
else if (canCopySemanticDiagnostics) {
const sourceFile = newProgram.getSourceFileByPath(sourceFilePath as Path)!;
if (sourceFile.isDeclarationFile && !copyDeclarationFileDiagnostics) { return; }
if (sourceFile.hasNoDefaultLib && !copyLibFileDiagnostics) { return; }
// Unchanged file copy diagnostics
const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath);
if (diagnostics) {
@@ -116,6 +157,38 @@ namespace ts {
return state;
}
/**
* Releases program and other related not needed properties
*/
function releaseCache(state: BuilderProgramState) {
BuilderState.releaseCache(state);
state.program = undefined;
}
/**
* Creates a clone of the state
*/
function cloneBuilderProgramState(state: Readonly<BuilderProgramState>): BuilderProgramState {
const newState = BuilderState.clone(state) as BuilderProgramState;
newState.semanticDiagnosticsPerFile = cloneMapOrUndefined(state.semanticDiagnosticsPerFile);
newState.changedFilesSet = cloneMap(state.changedFilesSet);
newState.affectedFiles = state.affectedFiles;
newState.affectedFilesIndex = state.affectedFilesIndex;
newState.currentChangedFilePath = state.currentChangedFilePath;
newState.currentAffectedFilesSignatures = cloneMapOrUndefined(state.currentAffectedFilesSignatures);
newState.currentAffectedFilesExportedModulesMap = cloneMapOrUndefined(state.currentAffectedFilesExportedModulesMap);
newState.seenAffectedFiles = cloneMapOrUndefined(state.seenAffectedFiles);
newState.cleanedDiagnosticsOfLibFiles = state.cleanedDiagnosticsOfLibFiles;
newState.semanticDiagnosticsFromOldState = cloneMapOrUndefined(state.semanticDiagnosticsFromOldState);
newState.program = state.program;
newState.compilerOptions = state.compilerOptions;
newState.affectedFilesPendingEmit = state.affectedFilesPendingEmit;
newState.affectedFilesPendingEmitIndex = state.affectedFilesPendingEmitIndex;
newState.seenEmittedFiles = cloneMapOrUndefined(state.seenEmittedFiles);
newState.programEmitComplete = state.programEmitComplete;
return newState;
}
/**
* Verifies that source file is ok to be used in calls that arent handled by next
*/
@@ -166,10 +239,11 @@ namespace ts {
// With --out or --outFile all outputs go into single file
// so operations are performed directly on program, return program
const compilerOptions = state.program.getCompilerOptions();
const program = Debug.assertDefined(state.program);
const compilerOptions = program.getCompilerOptions();
if (compilerOptions.outFile || compilerOptions.out) {
Debug.assert(!state.semanticDiagnosticsPerFile);
return state.program;
return program;
}
// Get next batch of affected files
@@ -177,13 +251,34 @@ namespace ts {
if (state.exportedModulesMap) {
state.currentAffectedFilesExportedModulesMap = state.currentAffectedFilesExportedModulesMap || createMap<BuilderState.ReferencedSet | false>();
}
state.affectedFiles = BuilderState.getFilesAffectedBy(state, state.program, nextKey.value as Path, cancellationToken, computeHash, state.currentAffectedFilesSignatures, state.currentAffectedFilesExportedModulesMap);
state.affectedFiles = BuilderState.getFilesAffectedBy(state, program, nextKey.value as Path, cancellationToken, computeHash, state.currentAffectedFilesSignatures, state.currentAffectedFilesExportedModulesMap);
state.currentChangedFilePath = nextKey.value as Path;
state.affectedFilesIndex = 0;
state.seenAffectedFiles = state.seenAffectedFiles || createMap<true>();
}
}
/**
* Returns next file to be emitted from files that retrieved semantic diagnostics but did not emit yet
*/
function getNextAffectedFilePendingEmit(state: BuilderProgramState): SourceFile | undefined {
const { affectedFilesPendingEmit } = state;
if (affectedFilesPendingEmit) {
const seenEmittedFiles = state.seenEmittedFiles || (state.seenEmittedFiles = createMap());
for (let i = state.affectedFilesPendingEmitIndex!; i < affectedFilesPendingEmit.length; i++) {
const affectedFile = Debug.assertDefined(state.program).getSourceFileByPath(affectedFilesPendingEmit[i]);
if (affectedFile && !seenEmittedFiles.has(affectedFile.path)) {
// emit this file
state.affectedFilesPendingEmitIndex = i;
return affectedFile;
}
}
state.affectedFilesPendingEmit = undefined;
state.affectedFilesPendingEmitIndex = undefined;
}
return undefined;
}
/**
* Remove the semantic diagnostics cached from old state for affected File and the files that are referencing modules that export entities from affected file
*/
@@ -193,6 +288,20 @@ namespace ts {
return;
}
// Clean lib file diagnostics if its all files excluding default files to emit
if (state.allFilesExcludingDefaultLibraryFile === state.affectedFiles && !state.cleanedDiagnosticsOfLibFiles) {
state.cleanedDiagnosticsOfLibFiles = true;
const program = Debug.assertDefined(state.program);
const options = program.getCompilerOptions();
if (forEach(program.getSourceFiles(), f =>
program.isSourceFileDefaultLibrary(f) &&
!skipTypeChecking(f, options) &&
removeSemanticDiagnosticsOf(state, f.path)
)) {
return;
}
}
// If there was change in signature for the changed file,
// then delete the semantic diagnostics for files that are affected by using exports of this module
@@ -201,12 +310,13 @@ namespace ts {
}
Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
const seenFileAndExportsOfFile = createMap<true>();
// Go through exported modules from cache first
// If exported modules has path, all files referencing file exported from are affected
if (forEachEntry(state.currentAffectedFilesExportedModulesMap!, (exportedModules, exportedFromPath) =>
exportedModules &&
exportedModules.has(affectedFile.path) &&
removeSemanticDiagnosticsOfFilesReferencingPath(state, exportedFromPath as Path)
removeSemanticDiagnosticsOfFilesReferencingPath(state, exportedFromPath as Path, seenFileAndExportsOfFile)
)) {
return;
}
@@ -215,7 +325,7 @@ namespace ts {
forEachEntry(state.exportedModulesMap, (exportedModules, exportedFromPath) =>
!state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it
exportedModules.has(affectedFile.path) &&
removeSemanticDiagnosticsOfFilesReferencingPath(state, exportedFromPath as Path)
removeSemanticDiagnosticsOfFilesReferencingPath(state, exportedFromPath as Path, seenFileAndExportsOfFile)
);
}
@@ -223,9 +333,50 @@ namespace ts {
* removes the semantic diagnostics of files referencing referencedPath and
* returns true if there are no more semantic diagnostics from old state
*/
function removeSemanticDiagnosticsOfFilesReferencingPath(state: BuilderProgramState, referencedPath: Path) {
function removeSemanticDiagnosticsOfFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Map<true>) {
return forEachEntry(state.referencedMap!, (referencesInFile, filePath) =>
referencesInFile.has(referencedPath) && removeSemanticDiagnosticsOf(state, filePath as Path)
referencesInFile.has(referencedPath) && removeSemanticDiagnosticsOfFileAndExportsOfFile(state, filePath as Path, seenFileAndExportsOfFile)
);
}
/**
* Removes semantic diagnostics of file and anything that exports this file
*/
function removeSemanticDiagnosticsOfFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Map<true>): boolean {
if (!addToSeen(seenFileAndExportsOfFile, filePath)) {
return false;
}
if (removeSemanticDiagnosticsOf(state, filePath)) {
// If there are no more diagnostics from old cache, done
return true;
}
Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
// Go through exported modules from cache first
// If exported modules has path, all files referencing file exported from are affected
if (forEachEntry(state.currentAffectedFilesExportedModulesMap!, (exportedModules, exportedFromPath) =>
exportedModules &&
exportedModules.has(filePath) &&
removeSemanticDiagnosticsOfFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile)
)) {
return true;
}
// If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
if (forEachEntry(state.exportedModulesMap!, (exportedModules, exportedFromPath) =>
!state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it
exportedModules.has(filePath) &&
removeSemanticDiagnosticsOfFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile)
)) {
return true;
}
// Remove diagnostics of files that import this file (without going to exports of referencing files)
return !!forEachEntry(state.referencedMap!, (referencesInFile, referencingFilePath) =>
referencesInFile.has(filePath) &&
!seenFileAndExportsOfFile.has(referencingFilePath) && // Not already removed diagnostic file
removeSemanticDiagnosticsOf(state, referencingFilePath as Path) // Dont add to seen since this is not yet done with the export removal
);
}
@@ -235,7 +386,7 @@ namespace ts {
*/
function removeSemanticDiagnosticsOf(state: BuilderProgramState, path: Path) {
if (!state.semanticDiagnosticsFromOldState) {
return false;
return true;
}
state.semanticDiagnosticsFromOldState.delete(path);
state.semanticDiagnosticsPerFile!.delete(path);
@@ -246,21 +397,27 @@ namespace ts {
* This is called after completing operation on the next affected file.
* The operations here are postponed to ensure that cancellation during the iteration is handled correctly
*/
function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program) {
function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program, isPendingEmit?: boolean) {
if (affected === state.program) {
state.changedFilesSet.clear();
state.programEmitComplete = true;
}
else {
state.seenAffectedFiles!.set((affected as SourceFile).path, true);
state.affectedFilesIndex!++;
if (isPendingEmit) {
state.affectedFilesPendingEmitIndex!++;
}
else {
state.affectedFilesIndex!++;
}
}
}
/**
* Returns the result with affected file
*/
function toAffectedFileResult<T>(state: BuilderProgramState, result: T, affected: SourceFile | Program): AffectedFileResult<T> {
doneWithAffectedFile(state, affected);
function toAffectedFileResult<T>(state: BuilderProgramState, result: T, affected: SourceFile | Program, isPendingEmit?: boolean): AffectedFileResult<T> {
doneWithAffectedFile(state, affected, isPendingEmit);
return { result, affected };
}
@@ -270,15 +427,19 @@ namespace ts {
*/
function getSemanticDiagnosticsOfFile(state: BuilderProgramState, sourceFile: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic> {
const path = sourceFile.path;
const cachedDiagnostics = state.semanticDiagnosticsPerFile!.get(path);
// Report the semantic diagnostics from the cache if we already have those diagnostics present
if (cachedDiagnostics) {
return cachedDiagnostics;
if (state.semanticDiagnosticsPerFile) {
const cachedDiagnostics = state.semanticDiagnosticsPerFile.get(path);
// Report the semantic diagnostics from the cache if we already have those diagnostics present
if (cachedDiagnostics) {
return cachedDiagnostics;
}
}
// Diagnostics werent cached, get them from program, and cache the result
const diagnostics = state.program.getSemanticDiagnostics(sourceFile, cancellationToken);
state.semanticDiagnosticsPerFile!.set(path, diagnostics);
const diagnostics = Debug.assertDefined(state.program).getSemanticDiagnostics(sourceFile, cancellationToken);
if (state.semanticDiagnosticsPerFile) {
state.semanticDiagnosticsPerFile.set(path, diagnostics);
}
return diagnostics;
}
@@ -294,7 +455,7 @@ namespace ts {
configFileParsingDiagnostics: ReadonlyArray<Diagnostic>;
}
export function getBuilderCreationParameters(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: BuilderProgram | CompilerHost, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderCreationParameters {
export function getBuilderCreationParameters(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: BuilderProgram | CompilerHost, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>): BuilderCreationParameters {
let host: BuilderProgramHost;
let newProgram: Program;
let oldProgram: BuilderProgram;
@@ -307,7 +468,14 @@ namespace ts {
}
else if (isArray(newProgramOrRootNames)) {
oldProgram = configFileParsingDiagnosticsOrOldProgram as BuilderProgram;
newProgram = createProgram(newProgramOrRootNames, hostOrOptions as CompilerOptions, oldProgramOrHost as CompilerHost, oldProgram && oldProgram.getProgram(), configFileParsingDiagnostics);
newProgram = createProgram({
rootNames: newProgramOrRootNames,
options: hostOrOptions as CompilerOptions,
host: oldProgramOrHost as CompilerHost,
oldProgram: oldProgram && oldProgram.getProgramOrUndefined(),
configFileParsingDiagnostics,
projectReferences
});
host = oldProgramOrHost as CompilerHost;
}
else {
@@ -337,28 +505,31 @@ namespace ts {
/**
* Computing hash to for signature verification
*/
const computeHash = host.createHash || identity;
const state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState);
const computeHash = host.createHash || generateDjb2Hash;
let state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState);
let backupState: BuilderProgramState | undefined;
// To ensure that we arent storing any references to old program or new program without state
newProgram = undefined!; // TODO: GH#18217
oldProgram = undefined;
oldState = undefined;
const result: BuilderProgram = {
getState: () => state,
getProgram: () => state.program,
getCompilerOptions: () => state.program.getCompilerOptions(),
getSourceFile: fileName => state.program.getSourceFile(fileName),
getSourceFiles: () => state.program.getSourceFiles(),
getOptionsDiagnostics: cancellationToken => state.program.getOptionsDiagnostics(cancellationToken),
getGlobalDiagnostics: cancellationToken => state.program.getGlobalDiagnostics(cancellationToken),
getConfigFileParsingDiagnostics: () => configFileParsingDiagnostics || state.program.getConfigFileParsingDiagnostics(),
getSyntacticDiagnostics: (sourceFile, cancellationToken) => state.program.getSyntacticDiagnostics(sourceFile, cancellationToken),
getSemanticDiagnostics,
emit,
getAllDependencies: sourceFile => BuilderState.getAllDependencies(state, state.program, sourceFile),
getCurrentDirectory: () => state.program.getCurrentDirectory()
const result = createRedirectedBuilderProgram(state, configFileParsingDiagnostics);
result.getState = () => state;
result.backupState = () => {
Debug.assert(backupState === undefined);
backupState = cloneBuilderProgramState(state);
};
result.restoreState = () => {
state = Debug.assertDefined(backupState);
backupState = undefined;
};
result.getAllDependencies = sourceFile => BuilderState.getAllDependencies(state, Debug.assertDefined(state.program), sourceFile);
result.getSemanticDiagnostics = getSemanticDiagnostics;
result.emit = emit;
result.releaseProgram = () => {
releaseCache(state);
backupState = undefined;
};
if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) {
@@ -379,18 +550,39 @@ namespace ts {
* in that order would be used to write the files
*/
function emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult> {
const affected = getNextAffectedFile(state, cancellationToken, computeHash);
let affected = getNextAffectedFile(state, cancellationToken, computeHash);
let isPendingEmitFile = false;
if (!affected) {
// Done
return undefined;
if (!state.compilerOptions.out && !state.compilerOptions.outFile) {
affected = getNextAffectedFilePendingEmit(state);
if (!affected) {
return undefined;
}
isPendingEmitFile = true;
}
else {
const program = Debug.assertDefined(state.program);
// Check if program uses any prepend project references, if thats the case we cant track of the js files of those, so emit even though there are no changes
if (state.programEmitComplete || !some(program.getProjectReferences(), ref => !!ref.prepend)) {
state.programEmitComplete = true;
return undefined;
}
affected = program;
}
}
// Mark seen emitted files if there are pending files to be emitted
if (state.affectedFilesPendingEmit && state.program !== affected) {
(state.seenEmittedFiles || (state.seenEmittedFiles = createMap())).set((affected as SourceFile).path, true);
}
return toAffectedFileResult(
state,
// When whole program is affected, do emit only once (eg when --out or --outFile is specified)
// Otherwise just affected file
state.program.emit(affected === state.program ? undefined : affected as SourceFile, writeFile || host.writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers),
affected
Debug.assertDefined(state.program).emit(affected === state.program ? undefined : affected as SourceFile, writeFile || host.writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers),
affected,
isPendingEmitFile
);
}
@@ -410,7 +602,7 @@ namespace ts {
assertSourceFileOkWithoutNextAffectedCall(state, targetSourceFile);
if (!targetSourceFile) {
// Emit and report any errors we ran into.
let sourceMaps: SourceMapData[] = [];
let sourceMaps: SourceMapEmitResult[] = [];
let emitSkipped = false;
let diagnostics: Diagnostic[] | undefined;
let emittedFiles: string[] = [];
@@ -430,7 +622,7 @@ namespace ts {
};
}
}
return state.program.emit(targetSourceFile, writeFile || host.writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
return Debug.assertDefined(state.program).emit(targetSourceFile, writeFile || host.writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
}
/**
@@ -478,33 +670,74 @@ namespace ts {
*/
function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic> {
assertSourceFileOkWithoutNextAffectedCall(state, sourceFile);
const compilerOptions = state.program.getCompilerOptions();
const compilerOptions = Debug.assertDefined(state.program).getCompilerOptions();
if (compilerOptions.outFile || compilerOptions.out) {
Debug.assert(!state.semanticDiagnosticsPerFile);
// We dont need to cache the diagnostics just return them from program
return state.program.getSemanticDiagnostics(sourceFile, cancellationToken);
return Debug.assertDefined(state.program).getSemanticDiagnostics(sourceFile, cancellationToken);
}
if (sourceFile) {
return getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken);
}
if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) {
// When semantic builder asks for diagnostics of the whole program,
// ensure that all the affected files are handled
let affected: SourceFile | Program | undefined;
while (affected = getNextAffectedFile(state, cancellationToken, computeHash)) {
doneWithAffectedFile(state, affected);
// When semantic builder asks for diagnostics of the whole program,
// ensure that all the affected files are handled
let affected: SourceFile | Program | undefined;
let affectedFilesPendingEmit: Path[] | undefined;
while (affected = getNextAffectedFile(state, cancellationToken, computeHash)) {
if (affected !== state.program && kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
(affectedFilesPendingEmit || (affectedFilesPendingEmit = [])).push((affected as SourceFile).path);
}
doneWithAffectedFile(state, affected);
}
// In case of emit builder, cache the files to be emitted
if (affectedFilesPendingEmit) {
state.affectedFilesPendingEmit = concatenate(state.affectedFilesPendingEmit, affectedFilesPendingEmit);
// affectedFilesPendingEmitIndex === undefined
// - means the emit state.affectedFilesPendingEmit was undefined before adding current affected files
// so start from 0 as array would be affectedFilesPendingEmit
// else, continue to iterate from existing index, the current set is appended to existing files
if (state.affectedFilesPendingEmitIndex === undefined) {
state.affectedFilesPendingEmitIndex = 0;
}
}
let diagnostics: Diagnostic[] | undefined;
for (const sourceFile of state.program.getSourceFiles()) {
for (const sourceFile of Debug.assertDefined(state.program).getSourceFiles()) {
diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken));
}
return diagnostics || emptyArray;
}
}
export function createRedirectedBuilderProgram(state: { program: Program | undefined; compilerOptions: CompilerOptions; }, configFileParsingDiagnostics: ReadonlyArray<Diagnostic>): BuilderProgram {
return {
getState: notImplemented,
backupState: noop,
restoreState: noop,
getProgram,
getProgramOrUndefined: () => state.program,
releaseProgram: () => state.program = undefined,
getCompilerOptions: () => state.compilerOptions,
getSourceFile: fileName => getProgram().getSourceFile(fileName),
getSourceFiles: () => getProgram().getSourceFiles(),
getOptionsDiagnostics: cancellationToken => getProgram().getOptionsDiagnostics(cancellationToken),
getGlobalDiagnostics: cancellationToken => getProgram().getGlobalDiagnostics(cancellationToken),
getConfigFileParsingDiagnostics: () => configFileParsingDiagnostics,
getSyntacticDiagnostics: (sourceFile, cancellationToken) => getProgram().getSyntacticDiagnostics(sourceFile, cancellationToken),
getDeclarationDiagnostics: (sourceFile, cancellationToken) => getProgram().getDeclarationDiagnostics(sourceFile, cancellationToken),
getSemanticDiagnostics: (sourceFile, cancellationToken) => getProgram().getSemanticDiagnostics(sourceFile, cancellationToken),
emit: (sourceFile, writeFile, cancellationToken, emitOnlyDts, customTransformers) => getProgram().emit(sourceFile, writeFile, cancellationToken, emitOnlyDts, customTransformers),
getAllDependencies: notImplemented,
getCurrentDirectory: () => getProgram().getCurrentDirectory()
};
function getProgram() {
return Debug.assertDefined(state.program);
}
}
}
namespace ts {
@@ -532,10 +765,24 @@ namespace ts {
export interface BuilderProgram {
/*@internal*/
getState(): BuilderProgramState;
/*@internal*/
backupState(): void;
/*@internal*/
restoreState(): void;
/**
* Returns current program
*/
getProgram(): Program;
/**
* Returns current program that could be undefined if the program was released
*/
/*@internal*/
getProgramOrUndefined(): Program | undefined;
/**
* Releases reference to the program, making all the other operations that need program to fail.
*/
/*@internal*/
releaseProgram(): void;
/**
* Get compiler options of the program
*/
@@ -564,10 +811,15 @@ namespace ts {
* Get the syntax diagnostics, for all source files if source file is not supplied
*/
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
/**
* Get the declaration diagnostics, for all source files if source file is not supplied
*/
getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<DiagnosticWithLocation>;
/**
* Get all the dependencies of the file
*/
getAllDependencies(sourceFile: SourceFile): ReadonlyArray<string>;
/**
* Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
* The semantic diagnostics are cached and managed here
@@ -623,9 +875,9 @@ namespace ts {
* Create the builder to manage semantic diagnostics and cache them
*/
export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): SemanticDiagnosticsBuilderProgram;
export function createSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): SemanticDiagnosticsBuilderProgram;
export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) {
return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics));
export function createSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>): SemanticDiagnosticsBuilderProgram;
export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>) {
return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
}
/**
@@ -633,33 +885,18 @@ namespace ts {
* to emit the those files and manage semantic diagnostics cache as well
*/
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): EmitAndSemanticDiagnosticsBuilderProgram;
export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): EmitAndSemanticDiagnosticsBuilderProgram;
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) {
return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics));
export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>): EmitAndSemanticDiagnosticsBuilderProgram;
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>) {
return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
}
/**
* Creates a builder thats just abstraction over program and can be used with watch
*/
export function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram;
export function createAbstractBuilder(rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram;
export function createAbstractBuilder(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | BuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram {
const { newProgram: program } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics);
return {
// Only return program, all other methods are not implemented
getProgram: () => program,
getState: notImplemented,
getCompilerOptions: notImplemented,
getSourceFile: notImplemented,
getSourceFiles: notImplemented,
getOptionsDiagnostics: notImplemented,
getGlobalDiagnostics: notImplemented,
getConfigFileParsingDiagnostics: notImplemented,
getSyntacticDiagnostics: notImplemented,
getSemanticDiagnostics: notImplemented,
emit: notImplemented,
getAllDependencies: notImplemented,
getCurrentDirectory: notImplemented
};
export function createAbstractBuilder(rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>): BuilderProgram;
export function createAbstractBuilder(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | BuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>): BuilderProgram {
const { newProgram, configFileParsingDiagnostics: newConfigFileParsingDiagnostics } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences);
return createRedirectedBuilderProgram({ program: newProgram, compilerOptions: newProgram.getCompilerOptions() }, newConfigFileParsingDiagnostics);
}
}
+98 -12
View File
@@ -50,11 +50,15 @@ namespace ts {
/**
* Cache of all files excluding default library file for the current program
*/
allFilesExcludingDefaultLibraryFile: ReadonlyArray<SourceFile> | undefined;
allFilesExcludingDefaultLibraryFile?: ReadonlyArray<SourceFile>;
/**
* Cache of all the file names
*/
allFileNames: ReadonlyArray<string> | undefined;
allFileNames?: ReadonlyArray<string>;
}
export function cloneMapOrUndefined<T>(map: ReadonlyMap<T> | undefined) {
return map ? cloneMap(map) : undefined;
}
}
@@ -88,7 +92,7 @@ namespace ts.BuilderState {
function getReferencedFileFromImportedModuleSymbol(symbol: Symbol) {
if (symbol.declarations && symbol.declarations[0]) {
const declarationSourceFile = getSourceFileOfNode(symbol.declarations[0]);
return declarationSourceFile && declarationSourceFile.path;
return declarationSourceFile && declarationSourceFile.resolvedPath;
}
}
@@ -100,6 +104,13 @@ namespace ts.BuilderState {
return symbol && getReferencedFileFromImportedModuleSymbol(symbol);
}
/**
* Gets the path to reference file from file name, it could be resolvedPath if present otherwise path
*/
function getReferencedFileFromFileName(program: Program, fileName: string, sourceFileDirectory: Path, getCanonicalFileName: GetCanonicalFileName): Path {
return toPath(program.getProjectReferenceRedirect(fileName) || fileName, sourceFileDirectory, getCanonicalFileName);
}
/**
* Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true
*/
@@ -123,7 +134,7 @@ namespace ts.BuilderState {
// Handle triple slash references
if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) {
for (const referencedFile of sourceFile.referencedFiles) {
const referencedPath = toPath(referencedFile.fileName, sourceFileDirectory, getCanonicalFileName);
const referencedPath = getReferencedFileFromFileName(program, referencedFile.fileName, sourceFileDirectory, getCanonicalFileName);
addReferencedFile(referencedPath);
}
}
@@ -136,13 +147,44 @@ namespace ts.BuilderState {
}
const fileName = resolvedTypeReferenceDirective.resolvedFileName!; // TODO: GH#18217
const typeFilePath = toPath(fileName, sourceFileDirectory, getCanonicalFileName);
const typeFilePath = getReferencedFileFromFileName(program, fileName, sourceFileDirectory, getCanonicalFileName);
addReferencedFile(typeFilePath);
});
}
// Add module augmentation as references
if (sourceFile.moduleAugmentations.length) {
const checker = program.getTypeChecker();
for (const moduleName of sourceFile.moduleAugmentations) {
if (!isStringLiteral(moduleName)) { continue; }
const symbol = checker.getSymbolAtLocation(moduleName);
if (!symbol) { continue; }
// Add any file other than our own as reference
addReferenceFromAmbientModule(symbol);
}
}
// From ambient modules
for (const ambientModule of program.getTypeChecker().getAmbientModules()) {
if (ambientModule.declarations.length > 1) {
addReferenceFromAmbientModule(ambientModule);
}
}
return referencedFiles;
function addReferenceFromAmbientModule(symbol: Symbol) {
// Add any file other than our own as reference
for (const declaration of symbol.declarations) {
const declarationSourceFile = getSourceFileOfNode(declaration);
if (declarationSourceFile &&
declarationSourceFile !== sourceFile) {
addReferencedFile(declarationSourceFile.resolvedPath);
}
}
}
function addReferencedFile(referencedPath: Path) {
if (!referencedFiles) {
referencedFiles = createMap<true>();
@@ -192,9 +234,32 @@ namespace ts.BuilderState {
fileInfos,
referencedMap,
exportedModulesMap,
hasCalledUpdateShapeSignature,
allFilesExcludingDefaultLibraryFile: undefined,
allFileNames: undefined
hasCalledUpdateShapeSignature
};
}
/**
* Releases needed properties
*/
export function releaseCache(state: BuilderState) {
state.allFilesExcludingDefaultLibraryFile = undefined;
state.allFileNames = undefined;
}
/**
* Creates a clone of the state
*/
export function clone(state: Readonly<BuilderState>): BuilderState {
const fileInfos = createMap<FileInfo>();
state.fileInfos.forEach((value, key) => {
fileInfos.set(key, { ...value });
});
// Dont need to backup allFiles info since its cache anyway
return {
fileInfos,
referencedMap: cloneMapOrUndefined(state.referencedMap),
exportedModulesMap: cloneMapOrUndefined(state.exportedModulesMap),
hasCalledUpdateShapeSignature: cloneMap(state.hasCalledUpdateShapeSignature),
};
}
@@ -254,6 +319,11 @@ namespace ts.BuilderState {
let latestSignature: string;
if (sourceFile.isDeclarationFile) {
latestSignature = sourceFile.version;
if (exportedModulesMapCache && latestSignature !== prevSignature) {
// All the references in this file are exported
const references = state.referencedMap ? state.referencedMap.get(sourceFile.path) : undefined;
exportedModulesMapCache.set(sourceFile.path, references || false);
}
}
else {
const emitOutput = getFileEmitOutput(programOfThisState, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken);
@@ -325,7 +395,7 @@ namespace ts.BuilderState {
}
// If this is non module emit, or its a global file, it depends on all the source files
if (!state.referencedMap || (!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile))) {
if (!state.referencedMap || isFileAffectingGlobalScope(sourceFile)) {
return getAllFileNames(state, programOfThisState);
}
@@ -387,6 +457,22 @@ namespace ts.BuilderState {
return true;
}
/**
* Return true if file contains anything that augments to global scope we need to build them as if
* they are global files as well as module
*/
function containsGlobalScopeAugmentation(sourceFile: SourceFile) {
return some(sourceFile.moduleAugmentations, augmentation => isGlobalScopeAugmentation(augmentation.parent as ModuleDeclaration));
}
/**
* Return true if the file will invalidate all files because it affectes global scope
*/
function isFileAffectingGlobalScope(sourceFile: SourceFile) {
return containsGlobalScopeAugmentation(sourceFile) ||
!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile);
}
/**
* Gets all files of the program excluding the default library file
*/
@@ -430,7 +516,7 @@ namespace ts.BuilderState {
* When program emits modular code, gets the files affected by the sourceFile whose shape has changed
*/
function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map<string>, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined, exportedModulesMapCache: ComputingExportedModulesMap | undefined) {
if (!isExternalModule(sourceFileWithUpdatedShape) && !containsOnlyAmbientModules(sourceFileWithUpdatedShape)) {
if (isFileAffectingGlobalScope(sourceFileWithUpdatedShape)) {
return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape);
}
@@ -446,14 +532,14 @@ namespace ts.BuilderState {
// Start with the paths this file was referenced by
seenFileNamesMap.set(sourceFileWithUpdatedShape.path, sourceFileWithUpdatedShape);
const queue = getReferencedByPaths(state, sourceFileWithUpdatedShape.path);
const queue = getReferencedByPaths(state, sourceFileWithUpdatedShape.resolvedPath);
while (queue.length > 0) {
const currentPath = queue.pop()!;
if (!seenFileNamesMap.has(currentPath)) {
const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath)!;
seenFileNamesMap.set(currentPath, currentSourceFile);
if (currentSourceFile && updateShapeSignature(state, programOfThisState, currentSourceFile, cacheToUpdateSignature, cancellationToken, computeHash!, exportedModulesMapCache)) { // TODO: GH#18217
queue.push(...getReferencedByPaths(state, currentPath));
queue.push(...getReferencedByPaths(state, currentSourceFile.resolvedPath));
}
}
}
+3291 -2108
View File
File diff suppressed because it is too large Load Diff
+367 -179
View File
@@ -44,7 +44,8 @@ namespace ts {
["esnext.array", "lib.esnext.array.d.ts"],
["esnext.symbol", "lib.esnext.symbol.d.ts"],
["esnext.asynciterable", "lib.esnext.asynciterable.d.ts"],
["esnext.intl", "lib.esnext.intl.d.ts"]
["esnext.intl", "lib.esnext.intl.d.ts"],
["esnext.bigint", "lib.esnext.bigint.d.ts"]
];
/**
@@ -62,7 +63,8 @@ namespace ts {
/* @internal */
export const libMap = createMapFromEntries(libEntries);
const commonOptionsWithBuild: CommandLineOption[] = [
/* @internal */
export const commonOptionsWithBuild: CommandLineOption[] = [
{
name: "help",
shortName: "h",
@@ -76,6 +78,14 @@ namespace ts {
shortName: "?",
type: "boolean"
},
{
name: "watch",
shortName: "w",
type: "boolean",
showInSimplifiedHelpView: true,
category: Diagnostics.Command_line_Options,
description: Diagnostics.Watch_input_files,
},
{
name: "preserveWatchOutput",
type: "boolean",
@@ -84,12 +94,42 @@ namespace ts {
description: Diagnostics.Whether_to_keep_outdated_console_output_in_watch_mode_instead_of_clearing_the_screen,
},
{
name: "watch",
shortName: "w",
name: "listFiles",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Print_names_of_files_part_of_the_compilation
},
{
name: "listEmittedFiles",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Print_names_of_generated_files_part_of_the_compilation
},
{
name: "pretty",
type: "boolean",
showInSimplifiedHelpView: true,
category: Diagnostics.Command_line_Options,
description: Diagnostics.Watch_input_files,
description: Diagnostics.Stylize_errors_and_messages_using_color_and_context_experimental
},
{
name: "traceResolution",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Enable_tracing_of_the_name_resolution_process
},
{
name: "diagnostics",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Show_diagnostic_information
},
{
name: "extendedDiagnostics",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Show_verbose_diagnostic_information
},
];
@@ -138,11 +178,11 @@ namespace ts {
description: Diagnostics.Build_one_or_more_projects_and_their_dependencies_if_out_of_date
},
{
name: "pretty",
name: "showConfig",
type: "boolean",
showInSimplifiedHelpView: true,
category: Diagnostics.Command_line_Options,
description: Diagnostics.Stylize_errors_and_messages_using_color_and_context_experimental
isCommandLineOnly: true,
description: Diagnostics.Print_the_final_configuration_instead_of_building
},
// Basic
@@ -159,6 +199,8 @@ namespace ts {
es2018: ScriptTarget.ES2018,
esnext: ScriptTarget.ESNext,
}),
affectsSourceFile: true,
affectsModuleResolution: true,
paramType: Diagnostics.VERSION,
showInSimplifiedHelpView: true,
category: Diagnostics.Basic_Options,
@@ -177,6 +219,7 @@ namespace ts {
es2015: ModuleKind.ES2015,
esnext: ModuleKind.ESNext
}),
affectsModuleResolution: true,
paramType: Diagnostics.KIND,
showInSimplifiedHelpView: true,
category: Diagnostics.Basic_Options,
@@ -189,6 +232,7 @@ namespace ts {
name: "lib",
type: libMap
},
affectsModuleResolution: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Basic_Options,
description: Diagnostics.Specify_library_files_to_be_included_in_the_compilation
@@ -196,6 +240,7 @@ namespace ts {
{
name: "allowJs",
type: "boolean",
affectsModuleResolution: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Basic_Options,
description: Diagnostics.Allow_javascript_files_to_be_compiled
@@ -213,6 +258,7 @@ namespace ts {
"react-native": JsxEmit.ReactNative,
"react": JsxEmit.React
}),
affectsSourceFile: true,
paramType: Diagnostics.KIND,
showInSimplifiedHelpView: true,
category: Diagnostics.Basic_Options,
@@ -323,6 +369,7 @@ namespace ts {
{
name: "noImplicitAny",
type: "boolean",
affectsSemanticDiagnostics: true,
strictFlag: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Strict_Type_Checking_Options,
@@ -331,6 +378,7 @@ namespace ts {
{
name: "strictNullChecks",
type: "boolean",
affectsSemanticDiagnostics: true,
strictFlag: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Strict_Type_Checking_Options,
@@ -339,14 +387,24 @@ namespace ts {
{
name: "strictFunctionTypes",
type: "boolean",
affectsSemanticDiagnostics: true,
strictFlag: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Strict_Type_Checking_Options,
description: Diagnostics.Enable_strict_checking_of_function_types
},
{
name: "strictBindCallApply",
type: "boolean",
strictFlag: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Strict_Type_Checking_Options,
description: Diagnostics.Enable_strict_bind_call_and_apply_methods_on_functions
},
{
name: "strictPropertyInitialization",
type: "boolean",
affectsSemanticDiagnostics: true,
strictFlag: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Strict_Type_Checking_Options,
@@ -355,6 +413,7 @@ namespace ts {
{
name: "noImplicitThis",
type: "boolean",
affectsSemanticDiagnostics: true,
strictFlag: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Strict_Type_Checking_Options,
@@ -363,6 +422,7 @@ namespace ts {
{
name: "alwaysStrict",
type: "boolean",
affectsSourceFile: true,
strictFlag: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Strict_Type_Checking_Options,
@@ -397,6 +457,7 @@ namespace ts {
{
name: "noFallthroughCasesInSwitch",
type: "boolean",
affectsBindDiagnostics: true,
affectsSemanticDiagnostics: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Additional_Checks,
@@ -410,6 +471,7 @@ namespace ts {
node: ModuleResolutionKind.NodeJs,
classic: ModuleResolutionKind.Classic,
}),
affectsModuleResolution: true,
paramType: Diagnostics.STRATEGY,
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.Specify_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6,
@@ -417,6 +479,7 @@ namespace ts {
{
name: "baseUrl",
type: "string",
affectsModuleResolution: true,
isFilePath: true,
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.Base_directory_to_resolve_non_absolute_module_names
@@ -426,6 +489,7 @@ namespace ts {
// use type = object to copy the value as-is
name: "paths",
type: "object",
affectsModuleResolution: true,
isTSConfigOnly: true,
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.A_series_of_entries_which_re_map_imports_to_lookup_locations_relative_to_the_baseUrl
@@ -441,6 +505,7 @@ namespace ts {
type: "string",
isFilePath: true
},
affectsModuleResolution: true,
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.List_of_root_folders_whose_combined_content_represents_the_structure_of_the_project_at_runtime
},
@@ -452,6 +517,7 @@ namespace ts {
type: "string",
isFilePath: true
},
affectsModuleResolution: true,
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.List_of_folders_to_include_type_definitions_from
},
@@ -462,6 +528,7 @@ namespace ts {
name: "types",
type: "string"
},
affectsModuleResolution: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.Type_declaration_files_to_be_included_in_compilation
@@ -537,42 +604,12 @@ namespace ts {
category: Diagnostics.Advanced_Options,
description: Diagnostics.Specify_the_JSX_factory_function_to_use_when_targeting_react_JSX_emit_e_g_React_createElement_or_h
},
{
name: "diagnostics",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Show_diagnostic_information
},
{
name: "extendedDiagnostics",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Show_verbose_diagnostic_information
},
{
name: "traceResolution",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Enable_tracing_of_the_name_resolution_process
},
{
name: "resolveJsonModule",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Include_modules_imported_with_json_extension
},
{
name: "listFiles",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Print_names_of_files_part_of_the_compilation
},
{
name: "listEmittedFiles",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Print_names_of_generated_files_part_of_the_compilation
},
{
name: "out",
@@ -632,12 +669,14 @@ namespace ts {
{
name: "noLib",
type: "boolean",
affectsModuleResolution: true,
category: Diagnostics.Advanced_Options,
description: Diagnostics.Do_not_include_the_default_library_file_lib_d_ts
},
{
name: "noResolve",
type: "boolean",
affectsModuleResolution: true,
category: Diagnostics.Advanced_Options,
description: Diagnostics.Do_not_add_triple_slash_references_or_imported_modules_to_the_list_of_compiled_files
},
@@ -650,6 +689,7 @@ namespace ts {
{
name: "disableSizeLimit",
type: "boolean",
affectsSourceFile: true,
category: Diagnostics.Advanced_Options,
description: Diagnostics.Disable_size_limitations_on_JavaScript_projects
},
@@ -695,6 +735,7 @@ namespace ts {
{
name: "allowUnusedLabels",
type: "boolean",
affectsBindDiagnostics: true,
affectsSemanticDiagnostics: true,
category: Diagnostics.Advanced_Options,
description: Diagnostics.Do_not_report_errors_on_unused_labels
@@ -702,6 +743,7 @@ namespace ts {
{
name: "allowUnreachableCode",
type: "boolean",
affectsBindDiagnostics: true,
affectsSemanticDiagnostics: true,
category: Diagnostics.Advanced_Options,
description: Diagnostics.Do_not_report_errors_on_unreachable_code
@@ -729,6 +771,7 @@ namespace ts {
{
name: "maxNodeModuleJsDepth",
type: "number",
affectsModuleResolution: true,
category: Diagnostics.Advanced_Options,
description: Diagnostics.The_maximum_dependency_depth_to_search_under_node_modules_and_load_JavaScript_files
},
@@ -758,6 +801,18 @@ namespace ts {
}
];
/* @internal */
export const semanticDiagnosticsOptionDeclarations: ReadonlyArray<CommandLineOption> =
optionDeclarations.filter(option => !!option.affectsSemanticDiagnostics);
/* @internal */
export const moduleResolutionOptionDeclarations: ReadonlyArray<CommandLineOption> =
optionDeclarations.filter(option => !!option.affectsModuleResolution);
/* @internal */
export const sourceFileAffectingCompilerOptions: ReadonlyArray<CommandLineOption> = optionDeclarations.filter(option =>
!!option.affectsSourceFile || !!option.affectsModuleResolution || !!option.affectsBindDiagnostics);
/* @internal */
export const buildOpts: CommandLineOption[] = [
...commonOptionsWithBuild,
@@ -903,17 +958,27 @@ namespace ts {
}
}
export function parseCommandLine(commandLine: ReadonlyArray<string>, readFile?: (path: string) => string | undefined): ParsedCommandLine {
const options: CompilerOptions = {};
/* @internal */
export interface OptionsBase {
[option: string]: CompilerOptionsValue | undefined;
}
/** Tuple with error messages for 'unknown compiler option', 'option requires type' */
type ParseCommandLineWorkerDiagnostics = [DiagnosticMessage, DiagnosticMessage];
function parseCommandLineWorker(
getOptionNameMap: () => OptionNameMap,
[unknownOptionDiagnostic, optionTypeMismatchDiagnostic]: ParseCommandLineWorkerDiagnostics,
commandLine: ReadonlyArray<string>,
readFile?: (path: string) => string | undefined) {
const options = {} as OptionsBase;
const fileNames: string[] = [];
const projectReferences: ProjectReference[] | undefined = undefined;
const errors: Diagnostic[] = [];
parseStrings(commandLine);
return {
options,
fileNames,
projectReferences,
errors
};
@@ -926,7 +991,7 @@ namespace ts {
parseResponseFile(s.slice(1));
}
else if (s.charCodeAt(0) === CharacterCodes.minus) {
const opt = getOptionFromName(s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1), /*allowShort*/ true);
const opt = getOptionDeclarationFromName(getOptionNameMap, s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1), /*allowShort*/ true);
if (opt) {
if (opt.isTSConfigOnly) {
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name));
@@ -934,7 +999,7 @@ namespace ts {
else {
// Check to see if no argument was provided (e.g. "--locale" is the last command-line argument).
if (!args[i] && opt.type !== "boolean") {
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_expects_an_argument, opt.name));
errors.push(createCompilerDiagnostic(optionTypeMismatchDiagnostic, opt.name));
}
switch (opt.type) {
@@ -956,7 +1021,7 @@ namespace ts {
i++;
break;
case "list":
const result = parseListTypeOption(<CommandLineOptionOfListType>opt, args[i], errors);
const result = parseListTypeOption(opt, args[i], errors);
options[opt.name] = result || [];
if (result) {
i++;
@@ -971,7 +1036,7 @@ namespace ts {
}
}
else {
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_compiler_option_0, s));
errors.push(createCompilerDiagnostic(unknownOptionDiagnostic, s));
}
}
else {
@@ -1014,13 +1079,19 @@ namespace ts {
}
}
export function parseCommandLine(commandLine: ReadonlyArray<string>, readFile?: (path: string) => string | undefined): ParsedCommandLine {
return parseCommandLineWorker(getOptionNameMap, [
Diagnostics.Unknown_compiler_option_0,
Diagnostics.Compiler_option_0_expects_an_argument
], commandLine, readFile);
}
/** @internal */
export function getOptionFromName(optionName: string, allowShort?: boolean): CommandLineOption | undefined {
return getOptionDeclarationFromName(getOptionNameMap, optionName, allowShort);
}
/*@internal*/
export function getOptionDeclarationFromName(getOptionNameMap: () => OptionNameMap, optionName: string, allowShort = false): CommandLineOption | undefined {
function getOptionDeclarationFromName(getOptionNameMap: () => OptionNameMap, optionName: string, allowShort = false): CommandLineOption | undefined {
optionName = optionName.toLowerCase();
const { optionNameMap, shortOptionNames } = getOptionNameMap();
// Try to translate short option names to their full equivalents.
@@ -1044,25 +1115,11 @@ namespace ts {
export function parseBuildCommand(args: string[]): ParsedBuildCommand {
let buildOptionNameMap: OptionNameMap | undefined;
const returnBuildOptionNameMap = () => (buildOptionNameMap || (buildOptionNameMap = createOptionNameMap(buildOpts)));
const buildOptions: BuildOptions = {};
const projects: string[] = [];
let errors: Diagnostic[] | undefined;
for (const arg of args) {
if (arg.charCodeAt(0) === CharacterCodes.minus) {
const opt = getOptionDeclarationFromName(returnBuildOptionNameMap, arg.slice(arg.charCodeAt(1) === CharacterCodes.minus ? 2 : 1), /*allowShort*/ true);
if (opt) {
buildOptions[opt.name as keyof BuildOptions] = true;
}
else {
(errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.Unknown_build_option_0, arg));
}
}
else {
// Not a flag, parse as filename
projects.push(arg);
}
}
const { options, fileNames: projects, errors } = parseCommandLineWorker(returnBuildOptionNameMap, [
Diagnostics.Unknown_build_option_0,
Diagnostics.Build_option_0_requires_a_value_of_type_1
], args);
const buildOptions = options as BuildOptions;
if (projects.length === 0) {
// tsc -b invoked with no extra arguments; act as if invoked with "tsc -b ."
@@ -1071,19 +1128,19 @@ namespace ts {
// Nonsensical combinations
if (buildOptions.clean && buildOptions.force) {
(errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "force"));
errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "force"));
}
if (buildOptions.clean && buildOptions.verbose) {
(errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "verbose"));
errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "verbose"));
}
if (buildOptions.clean && buildOptions.watch) {
(errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "watch"));
errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "watch"));
}
if (buildOptions.watch && buildOptions.dry) {
(errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "watch", "dry"));
errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "watch", "dry"));
}
return { buildOptions, projects, errors: errors || emptyArray };
return { buildOptions, projects, errors };
}
function getDiagnosticText(_message: DiagnosticMessage, ..._args: any[]): string {
@@ -1097,7 +1154,7 @@ namespace ts {
}
/* @internal */
export function printHelp(optionsList: CommandLineOption[], syntaxPrefix = "") {
export function printHelp(optionsList: ReadonlyArray<CommandLineOption>, syntaxPrefix = "") {
const output: string[] = [];
// We want to align our "syntax" and "examples" commands to a certain margin.
@@ -1244,6 +1301,9 @@ namespace ts {
const result = parseJsonText(configFileName, configFileText);
const cwd = host.getCurrentDirectory();
result.path = toPath(configFileName, cwd, createGetCanonicalFileName(host.useCaseSensitiveFileNames));
result.resolvedPath = result.path;
result.originalFileName = result.fileName;
return parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd));
}
@@ -1480,7 +1540,12 @@ namespace ts {
elements: NodeArray<Expression>,
elementOption: CommandLineOption | undefined
): any[] | void {
return (returnValue ? elements.map : elements.forEach).call(elements, (element: Expression) => convertPropertyValueToJson(element, elementOption));
if (!returnValue) {
return elements.forEach(element => convertPropertyValueToJson(element, elementOption));
}
// Filter out invalid values
return filter(elements.map(element => convertPropertyValueToJson(element, elementOption)), v => v !== undefined);
}
function convertPropertyValueToJson(valueExpression: Expression, option: CommandLineOption | undefined): any {
@@ -1596,6 +1661,139 @@ namespace ts {
return false;
}
/**
* Generate an uncommented, complete tsconfig for use with "--showConfig"
* @param configParseResult options to be generated into tsconfig.json
* @param configFileName name of the parsed config file - output paths will be generated relative to this
* @param host provides current directory and case sensitivity services
*/
/** @internal */
export function convertToTSConfig(configParseResult: ParsedCommandLine, configFileName: string, host: { getCurrentDirectory(): string, useCaseSensitiveFileNames: boolean }): object {
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
const files = map(
filter(
configParseResult.fileNames,
(!configParseResult.configFileSpecs || !configParseResult.configFileSpecs.validatedIncludeSpecs) ? _ => true : matchesSpecs(
configFileName,
configParseResult.configFileSpecs.validatedIncludeSpecs,
configParseResult.configFileSpecs.validatedExcludeSpecs
)
),
f => getRelativePathFromFile(getNormalizedAbsolutePath(configFileName, host.getCurrentDirectory()), getNormalizedAbsolutePath(f, host.getCurrentDirectory()), getCanonicalFileName)
);
const optionMap = serializeCompilerOptions(configParseResult.options, { configFilePath: getNormalizedAbsolutePath(configFileName, host.getCurrentDirectory()), useCaseSensitiveFileNames: host.useCaseSensitiveFileNames });
const config = {
compilerOptions: {
...arrayFrom(optionMap.entries()).reduce((prev, cur) => ({ ...prev, [cur[0]]: cur[1] }), {}),
showConfig: undefined,
configFile: undefined,
configFilePath: undefined,
help: undefined,
init: undefined,
listFiles: undefined,
listEmittedFiles: undefined,
project: undefined,
build: undefined,
version: undefined,
},
references: map(configParseResult.projectReferences, r => ({ ...r, path: r.originalPath, originalPath: undefined })),
files: length(files) ? files : undefined,
...(configParseResult.configFileSpecs ? {
include: filterSameAsDefaultInclude(configParseResult.configFileSpecs.validatedIncludeSpecs),
exclude: configParseResult.configFileSpecs.validatedExcludeSpecs
} : {}),
compilerOnSave: !!configParseResult.compileOnSave ? true : undefined
};
return config;
}
function filterSameAsDefaultInclude(specs: ReadonlyArray<string> | undefined) {
if (!length(specs)) return undefined;
if (length(specs) !== 1) return specs;
if (specs![0] === "**/*") return undefined;
return specs;
}
function matchesSpecs(path: string, includeSpecs: ReadonlyArray<string> | undefined, excludeSpecs: ReadonlyArray<string> | undefined): (path: string) => boolean {
if (!includeSpecs) return _ => true;
const patterns = getFileMatcherPatterns(path, excludeSpecs, includeSpecs, sys.useCaseSensitiveFileNames, sys.getCurrentDirectory());
const excludeRe = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, sys.useCaseSensitiveFileNames);
const includeRe = patterns.includeFilePattern && getRegexFromPattern(patterns.includeFilePattern, sys.useCaseSensitiveFileNames);
if (includeRe) {
if (excludeRe) {
return path => !(includeRe.test(path) && !excludeRe.test(path));
}
return path => !includeRe.test(path);
}
if (excludeRe) {
return path => excludeRe.test(path);
}
return _ => true;
}
function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map<string | number> | undefined {
if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean" || optionDefinition.type === "object") {
// this is of a type CommandLineOptionOfPrimitiveType
return undefined;
}
else if (optionDefinition.type === "list") {
return getCustomTypeMapOfCommandLineOption(optionDefinition.element);
}
else {
return (<CommandLineOptionOfCustomType>optionDefinition).type;
}
}
function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map<string | number>): string | undefined {
// There is a typeMap associated with this command-line option so use it to map value back to its name
return forEachEntry(customTypeMap, (mapValue, key) => {
if (mapValue === value) {
return key;
}
});
}
function serializeCompilerOptions(options: CompilerOptions, pathOptions?: { configFilePath: string, useCaseSensitiveFileNames: boolean }): Map<CompilerOptionsValue> {
const result = createMap<CompilerOptionsValue>();
const optionsNameMap = getOptionNameMap().optionNameMap;
const getCanonicalFileName = pathOptions && createGetCanonicalFileName(pathOptions.useCaseSensitiveFileNames);
for (const name in options) {
if (hasProperty(options, name)) {
// tsconfig only options cannot be specified via command line,
// so we can assume that only types that can appear here string | number | boolean
if (optionsNameMap.has(name) && optionsNameMap.get(name)!.category === Diagnostics.Command_line_Options) {
continue;
}
const value = <CompilerOptionsValue>options[name];
const optionDefinition = optionsNameMap.get(name.toLowerCase());
if (optionDefinition) {
const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition);
if (!customTypeMap) {
// There is no map associated with this compiler option then use the value as-is
// This is the case if the value is expect to be string, number, boolean or list of string
if (pathOptions && optionDefinition.isFilePath) {
result.set(name, getRelativePathFromFile(pathOptions.configFilePath, getNormalizedAbsolutePath(value as string, getDirectoryPath(pathOptions.configFilePath)), getCanonicalFileName!));
}
else {
result.set(name, value);
}
}
else {
if (optionDefinition.type === "list") {
result.set(name, (value as ReadonlyArray<string | number>).map(element => getNameOfCompilerOptionValue(element, customTypeMap)!)); // TODO: GH#18217
}
else {
// There is a typeMap associated with this command-line option so use it to map value back to its name
result.set(name, getNameOfCompilerOptionValue(value, customTypeMap));
}
}
}
}
}
return result;
}
/**
* Generate tsconfig configuration when running command line "--init"
* @param options commandlineOptions to be generated into tsconfig.json
@@ -1607,63 +1805,6 @@ namespace ts {
const compilerOptionsMap = serializeCompilerOptions(compilerOptions);
return writeConfigurations();
function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map<string | number> | undefined {
if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean") {
// this is of a type CommandLineOptionOfPrimitiveType
return undefined;
}
else if (optionDefinition.type === "list") {
return getCustomTypeMapOfCommandLineOption((<CommandLineOptionOfListType>optionDefinition).element);
}
else {
return (<CommandLineOptionOfCustomType>optionDefinition).type;
}
}
function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map<string | number>): string | undefined {
// There is a typeMap associated with this command-line option so use it to map value back to its name
return forEachEntry(customTypeMap, (mapValue, key) => {
if (mapValue === value) {
return key;
}
});
}
function serializeCompilerOptions(options: CompilerOptions): Map<CompilerOptionsValue> {
const result = createMap<CompilerOptionsValue>();
const optionsNameMap = getOptionNameMap().optionNameMap;
for (const name in options) {
if (hasProperty(options, name)) {
// tsconfig only options cannot be specified via command line,
// so we can assume that only types that can appear here string | number | boolean
if (optionsNameMap.has(name) && optionsNameMap.get(name)!.category === Diagnostics.Command_line_Options) {
continue;
}
const value = <CompilerOptionsValue>options[name];
const optionDefinition = optionsNameMap.get(name.toLowerCase());
if (optionDefinition) {
const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition);
if (!customTypeMap) {
// There is no map associated with this compiler option then use the value as-is
// This is the case if the value is expect to be string, number, boolean or list of string
result.set(name, value);
}
else {
if (optionDefinition.type === "list") {
result.set(name, (value as ReadonlyArray<string | number>).map(element => getNameOfCompilerOptionValue(element, customTypeMap)!)); // TODO: GH#18217
}
else {
// There is a typeMap associated with this command-line option so use it to map value back to its name
result.set(name, getNameOfCompilerOptionValue(value, customTypeMap));
}
}
}
}
}
return result;
}
function getDefaultValueForOption(option: CommandLineOption) {
switch (option.type) {
case "number":
@@ -1677,7 +1818,7 @@ namespace ts {
case "object":
return {};
default:
return (option as CommandLineOptionOfCustomType).type.keys().next().value;
return option.type.keys().next().value;
}
}
@@ -1754,7 +1895,7 @@ namespace ts {
}
result.push(`}`);
return result.join(newLine);
return result.join(newLine) + newLine;
}
}
@@ -1825,7 +1966,8 @@ namespace ts {
const options = extend(existingOptions, parsedConfig.options || {});
options.configFilePath = configFileName && normalizeSlashes(configFileName);
setConfigFileInOptions(options, sourceFile);
const { fileNames, wildcardDirectories, spec, projectReferences } = getFileNames();
let projectReferences: ProjectReference[] | undefined;
const { fileNames, wildcardDirectories, spec } = getFileNames();
return {
options,
fileNames,
@@ -1843,8 +1985,22 @@ namespace ts {
if (hasProperty(raw, "files") && !isNullOrUndefined(raw.files)) {
if (isArray(raw.files)) {
filesSpecs = <ReadonlyArray<string>>raw.files;
if (filesSpecs.length === 0) {
createCompilerDiagnosticOnlyIfJson(Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json");
const hasReferences = hasProperty(raw, "references") && !isNullOrUndefined(raw.references);
const hasZeroOrNoReferences = !hasReferences || raw.references.length === 0;
const hasExtends = hasProperty(raw, "extends");
if (filesSpecs.length === 0 && hasZeroOrNoReferences && !hasExtends) {
if (sourceFile) {
const fileName = configFileName || "tsconfig.json";
const diagnosticMessage = Diagnostics.The_files_list_in_config_file_0_is_empty;
const nodeValue = firstDefined(getTsConfigPropArray(sourceFile, "files"), property => property.initializer);
const error = nodeValue
? createDiagnosticForNodeInSourceFile(sourceFile, nodeValue, diagnosticMessage, fileName)
: createCompilerDiagnostic(diagnosticMessage, fileName);
errors.push(error);
}
else {
createCompilerDiagnosticOnlyIfJson(Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json");
}
}
}
else {
@@ -1885,19 +2041,18 @@ namespace ts {
}
const result = matchFileNames(filesSpecs, includeSpecs, excludeSpecs, configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath, options, host, errors, extraFileExtensions, sourceFile);
if (result.fileNames.length === 0 && !hasProperty(raw, "files") && resolutionStack.length === 0 && !hasProperty(raw, "references")) {
if (shouldReportNoInputFiles(result, canJsonReportNoInutFiles(raw), resolutionStack)) {
errors.push(getErrorForNoInputFiles(result.spec, configFileName));
}
if (hasProperty(raw, "references") && !isNullOrUndefined(raw.references)) {
if (isArray(raw.references)) {
const references: ProjectReference[] = [];
for (const ref of raw.references) {
if (typeof ref.path !== "string") {
createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "reference.path", "string");
}
else {
references.push({
(projectReferences || (projectReferences = [])).push({
path: getNormalizedAbsolutePath(ref.path, basePath),
originalPath: ref.path,
prepend: ref.prepend,
@@ -1905,7 +2060,6 @@ namespace ts {
});
}
}
result.projectReferences = references;
}
else {
createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "references", "Array");
@@ -1922,13 +2076,11 @@ namespace ts {
}
}
/*@internal*/
export function isErrorNoInputFiles(error: Diagnostic) {
function isErrorNoInputFiles(error: Diagnostic) {
return error.code === Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code;
}
/*@internal*/
export function getErrorForNoInputFiles({ includeSpecs, excludeSpecs }: ConfigFileSpecs, configFileName: string | undefined) {
function getErrorForNoInputFiles({ includeSpecs, excludeSpecs }: ConfigFileSpecs, configFileName: string | undefined) {
return createCompilerDiagnostic(
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
configFileName || "tsconfig.json",
@@ -1936,6 +2088,27 @@ namespace ts {
JSON.stringify(excludeSpecs || []));
}
function shouldReportNoInputFiles(result: ExpandResult, canJsonReportNoInutFiles: boolean, resolutionStack?: Path[]) {
return result.fileNames.length === 0 && canJsonReportNoInutFiles && (!resolutionStack || resolutionStack.length === 0);
}
/*@internal*/
export function canJsonReportNoInutFiles(raw: any) {
return !hasProperty(raw, "files") && !hasProperty(raw, "references");
}
/*@internal*/
export function updateErrorForNoInputFiles(result: ExpandResult, configFileName: string, configFileSpecs: ConfigFileSpecs, configParseDiagnostics: Diagnostic[], canJsonReportNoInutFiles: boolean) {
const existingErrors = configParseDiagnostics.length;
if (shouldReportNoInputFiles(result, canJsonReportNoInutFiles)) {
configParseDiagnostics.push(getErrorForNoInputFiles(configFileSpecs, configFileName));
}
else {
filterMutate(configParseDiagnostics, error => !isErrorNoInputFiles(error));
}
return existingErrors !== configParseDiagnostics.length;
}
interface ParsedTsconfig {
raw: any;
options?: CompilerOptions;
@@ -1978,7 +2151,7 @@ namespace ts {
if (ownConfig.extendedConfigPath) {
// copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios.
resolutionStack = resolutionStack.concat([resolvedPath]);
const extendedConfig = getExtendedConfig(sourceFile!, ownConfig.extendedConfigPath, host, basePath, resolutionStack, errors);
const extendedConfig = getExtendedConfig(sourceFile, ownConfig.extendedConfigPath, host, basePath, resolutionStack, errors);
if (extendedConfig && isSuccessfulParsedTsconfig(extendedConfig)) {
const baseRaw = extendedConfig.raw;
const raw = ownConfig.raw;
@@ -2067,11 +2240,6 @@ namespace ts {
createDiagnosticForNodeInSourceFile(sourceFile, valueNode, message, arg0)
);
return;
case "files":
if ((<ReadonlyArray<string>>value).length === 0) {
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueNode, Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json"));
}
return;
}
},
onSetUnknownOptionKeyValueInRoot(key: string, keyNode: PropertyName, _value: CompilerOptionsValue, _valueNode: Expression) {
@@ -2081,6 +2249,7 @@ namespace ts {
}
};
const json = convertToObjectWorker(sourceFile, errors, /*returnValue*/ true, getTsconfigRootOptionsMap(), optionsIterator);
if (!typeAcquisition) {
if (typingOptionstypeAcquisition) {
typeAcquisition = (typingOptionstypeAcquisition.enableAutoDiscovery !== undefined) ?
@@ -2106,24 +2275,28 @@ namespace ts {
errors: Push<Diagnostic>,
createDiagnostic: (message: DiagnosticMessage, arg1?: string) => Diagnostic) {
extendedConfig = normalizeSlashes(extendedConfig);
// If the path isn't a rooted or relative path, don't try to resolve it (we reserve the right to special case module-id like paths in the future)
if (!(isRootedDiskPath(extendedConfig) || startsWith(extendedConfig, "./") || startsWith(extendedConfig, "../"))) {
errors.push(createDiagnostic(Diagnostics.A_path_in_an_extends_option_must_be_relative_or_rooted_but_0_is_not, extendedConfig));
return undefined;
}
let extendedConfigPath = getNormalizedAbsolutePath(extendedConfig, basePath);
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, Extension.Json)) {
extendedConfigPath = `${extendedConfigPath}.json`;
if (!host.fileExists(extendedConfigPath)) {
errors.push(createDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
return undefined;
if (isRootedDiskPath(extendedConfig) || startsWith(extendedConfig, "./") || startsWith(extendedConfig, "../")) {
let extendedConfigPath = getNormalizedAbsolutePath(extendedConfig, basePath);
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, Extension.Json)) {
extendedConfigPath = `${extendedConfigPath}.json`;
if (!host.fileExists(extendedConfigPath)) {
errors.push(createDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
return undefined;
}
}
return extendedConfigPath;
}
return extendedConfigPath;
// If the path isn't a rooted or relative path, resolve like a module
const resolved = nodeModuleNameResolver(extendedConfig, combinePaths(basePath, "tsconfig.json"), { moduleResolution: ModuleResolutionKind.NodeJs }, host, /*cache*/ undefined, /*projectRefs*/ undefined, /*lookupConfig*/ true);
if (resolved.resolvedModule) {
return resolved.resolvedModule.resolvedFileName;
}
errors.push(createDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
return undefined;
}
function getExtendedConfig(
sourceFile: TsConfigSourceFile,
sourceFile: TsConfigSourceFile | undefined,
extendedConfigPath: string,
host: ParseConfigHost,
basePath: string,
@@ -2132,7 +2305,7 @@ namespace ts {
): ParsedTsconfig | undefined {
const extendedResult = readJsonConfigFile(extendedConfigPath, path => host.readFile(path));
if (sourceFile) {
(sourceFile.extendedSourceFiles || (sourceFile.extendedSourceFiles = [])).push(extendedResult.fileName);
sourceFile.extendedSourceFiles = [extendedResult.fileName];
}
if (extendedResult.parseDiagnostics.length) {
errors.push(...extendedResult.parseDiagnostics);
@@ -2142,8 +2315,8 @@ namespace ts {
const extendedDirname = getDirectoryPath(extendedConfigPath);
const extendedConfig = parseConfig(/*json*/ undefined, extendedResult, host, extendedDirname,
getBaseFileName(extendedConfigPath), resolutionStack, errors);
if (sourceFile) {
sourceFile.extendedSourceFiles!.push(...extendedResult.extendedSourceFiles!);
if (sourceFile && extendedResult.extendedSourceFiles) {
sourceFile.extendedSourceFiles!.push(...extendedResult.extendedSourceFiles);
}
if (isSuccessfulParsedTsconfig(extendedConfig)) {
@@ -2256,7 +2429,7 @@ namespace ts {
function normalizeOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue {
if (isNullOrUndefined(value)) return undefined;
if (option.type === "list") {
const listOption = <CommandLineOptionOfListType>option;
const listOption = option;
if (listOption.element.isFilePath || !isString(listOption.element.type)) {
return <CompilerOptionsValue>filter(map(value, v => normalizeOptionValue(listOption.element, basePath, v)), v => !!v);
}
@@ -2398,7 +2571,7 @@ namespace ts {
// new entries in these paths.
const wildcardDirectories = getWildcardDirectories(validatedIncludeSpecs, validatedExcludeSpecs, basePath, host.useCaseSensitiveFileNames);
const spec: ConfigFileSpecs = { filesSpecs, referencesSpecs: undefined, includeSpecs, excludeSpecs, validatedIncludeSpecs, validatedExcludeSpecs, wildcardDirectories };
const spec: ConfigFileSpecs = { filesSpecs, includeSpecs, excludeSpecs, validatedIncludeSpecs, validatedExcludeSpecs, wildcardDirectories };
return getFileNamesFromConfigSpecs(spec, basePath, options, host, extraFileExtensions);
}
@@ -2427,11 +2600,16 @@ namespace ts {
// via wildcard, and to handle extension priority.
const wildcardFileMap = createMap<string>();
// Wildcard paths of json files (provided via the "includes" array in tsconfig.json) are stored in a
// file map with a possibly case insensitive key. We use this map to store paths matched
// via wildcard of *.json kind
const wildCardJsonFileMap = createMap<string>();
const { filesSpecs, validatedIncludeSpecs, validatedExcludeSpecs, wildcardDirectories } = spec;
// Rather than requery this for each file and filespec, we query the supported extensions
// once and store it on the expansion context.
const supportedExtensions = getSupportedExtensions(options, extraFileExtensions);
const supportedExtensionsWithJsonIfResolveJsonModule = getSuppoertedExtensionsWithJsonIfResolveJsonModule(options, supportedExtensions);
// Literal files are always included verbatim. An "include" or "exclude" specification cannot
// remove a literal file.
@@ -2442,8 +2620,25 @@ namespace ts {
}
}
let jsonOnlyIncludeRegexes: ReadonlyArray<RegExp> | undefined;
if (validatedIncludeSpecs && validatedIncludeSpecs.length > 0) {
for (const file of host.readDirectory(basePath, supportedExtensions, validatedExcludeSpecs, validatedIncludeSpecs, /*depth*/ undefined)) {
for (const file of host.readDirectory(basePath, supportedExtensionsWithJsonIfResolveJsonModule, validatedExcludeSpecs, validatedIncludeSpecs, /*depth*/ undefined)) {
if (fileExtensionIs(file, Extension.Json)) {
// Valid only if *.json specified
if (!jsonOnlyIncludeRegexes) {
const includes = validatedIncludeSpecs.filter(s => endsWith(s, Extension.Json));
const includeFilePatterns = map(getRegularExpressionsForWildcards(includes, basePath, "files"), pattern => `^${pattern}$`);
jsonOnlyIncludeRegexes = includeFilePatterns ? includeFilePatterns.map(pattern => getRegexFromPattern(pattern, host.useCaseSensitiveFileNames)) : emptyArray;
}
const includeIndex = findIndex(jsonOnlyIncludeRegexes, re => re.test(file));
if (includeIndex !== -1) {
const key = keyMapper(file);
if (!literalFileMap.has(key) && !wildCardJsonFileMap.has(key)) {
wildCardJsonFileMap.set(key, file);
}
}
continue;
}
// If we have already included a literal or wildcard path with a
// higher priority extension, we should skip this file.
//
@@ -2469,16 +2664,9 @@ namespace ts {
const literalFiles = arrayFrom(literalFileMap.values());
const wildcardFiles = arrayFrom(wildcardFileMap.values());
const projectReferences = spec.referencesSpecs && spec.referencesSpecs.map((r): ProjectReference => {
return {
...r,
path: getNormalizedAbsolutePath(r.path, basePath)
};
});
return {
fileNames: literalFiles.concat(wildcardFiles),
projectReferences,
fileNames: literalFiles.concat(wildcardFiles, arrayFrom(wildCardJsonFileMap.values())),
wildcardDirectories,
spec
};
@@ -2620,7 +2808,7 @@ namespace ts {
}
/**
* Produces a cleaned version of compiler options with personally identifiying info (aka, paths) removed.
* Produces a cleaned version of compiler options with personally identifying info (aka, paths) removed.
* Also converts enum values back to strings.
*/
/* @internal */
@@ -2648,7 +2836,7 @@ namespace ts {
case "boolean":
return typeof value === "boolean" ? value : "";
case "list":
const elementType = (option as CommandLineOptionOfListType).element;
const elementType = option.element;
return isArray(value) ? value.map(v => getOptionValueWithEmptyStrings(v, elementType)) : "";
default:
return forEachEntry(option.type, (optionEnumValue, optionStringValue) => {
-431
View File
@@ -1,431 +0,0 @@
/* @internal */
namespace ts {
export interface CommentWriter {
reset(): void;
setSourceFile(sourceFile: SourceFile): void;
setWriter(writer: EmitTextWriter | undefined): void;
emitNodeWithComments(hint: EmitHint, node: Node | undefined, emitCallback: (hint: EmitHint, node: Node) => void): void;
emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void;
emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean): void;
emitLeadingCommentsOfPosition(pos: number): void;
}
export function createCommentWriter(printerOptions: PrinterOptions, emitPos: ((pos: number) => void) | undefined): CommentWriter {
const extendedDiagnostics = printerOptions.extendedDiagnostics;
const newLine = getNewLineCharacter(printerOptions);
let writer: EmitTextWriter;
let containerPos = -1;
let containerEnd = -1;
let declarationListContainerEnd = -1;
let currentSourceFile: SourceFile;
let currentText: string;
let currentLineMap: ReadonlyArray<number>;
let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[] | undefined;
let hasWrittenComment = false;
let disabled: boolean = !!printerOptions.removeComments;
return {
reset,
setWriter,
setSourceFile,
emitNodeWithComments,
emitBodyWithDetachedComments,
emitTrailingCommentsOfPosition,
emitLeadingCommentsOfPosition,
};
function emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
if (disabled) {
emitCallback(hint, node);
return;
}
if (node) {
hasWrittenComment = false;
const emitNode = node.emitNode;
const emitFlags = emitNode && emitNode.flags || 0;
const { pos, end } = emitNode && emitNode.commentRange || node;
if ((pos < 0 && end < 0) || (pos === end)) {
// Both pos and end are synthesized, so just emit the node without comments.
emitNodeWithSynthesizedComments(hint, node, emitNode, emitFlags, emitCallback);
}
else {
if (extendedDiagnostics) {
performance.mark("preEmitNodeWithComment");
}
const isEmittedNode = node.kind !== SyntaxKind.NotEmittedStatement;
// We have to explicitly check that the node is JsxText because if the compilerOptions.jsx is "preserve" we will not do any transformation.
// It is expensive to walk entire tree just to set one kind of node to have no comments.
const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0 || node.kind === SyntaxKind.JsxText;
const skipTrailingComments = end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0 || node.kind === SyntaxKind.JsxText;
// Emit leading comments if the position is not synthesized and the node
// has not opted out from emitting leading comments.
if (!skipLeadingComments) {
emitLeadingComments(pos, isEmittedNode);
}
// Save current container state on the stack.
const savedContainerPos = containerPos;
const savedContainerEnd = containerEnd;
const savedDeclarationListContainerEnd = declarationListContainerEnd;
if (!skipLeadingComments || (pos >= 0 && (emitFlags & EmitFlags.NoLeadingComments) !== 0)) {
// Advance the container position of comments get emitted or if they've been disabled explicitly using NoLeadingComments.
containerPos = pos;
}
if (!skipTrailingComments || (end >= 0 && (emitFlags & EmitFlags.NoTrailingComments) !== 0)) {
// As above.
containerEnd = end;
// To avoid invalid comment emit in a down-level binding pattern, we
// keep track of the last declaration list container's end
if (node.kind === SyntaxKind.VariableDeclarationList) {
declarationListContainerEnd = end;
}
}
if (extendedDiagnostics) {
performance.measure("commentTime", "preEmitNodeWithComment");
}
emitNodeWithSynthesizedComments(hint, node, emitNode, emitFlags, emitCallback);
if (extendedDiagnostics) {
performance.mark("postEmitNodeWithComment");
}
// Restore previous container state.
containerPos = savedContainerPos;
containerEnd = savedContainerEnd;
declarationListContainerEnd = savedDeclarationListContainerEnd;
// Emit trailing comments if the position is not synthesized and the node
// has not opted out from emitting leading comments and is an emitted node.
if (!skipTrailingComments && isEmittedNode) {
emitTrailingComments(end);
}
if (extendedDiagnostics) {
performance.measure("commentTime", "postEmitNodeWithComment");
}
}
}
}
function emitNodeWithSynthesizedComments(hint: EmitHint, node: Node, emitNode: EmitNode | undefined, emitFlags: EmitFlags, emitCallback: (hint: EmitHint, node: Node) => void) {
const leadingComments = emitNode && emitNode.leadingComments;
if (some(leadingComments)) {
if (extendedDiagnostics) {
performance.mark("preEmitNodeWithSynthesizedComments");
}
forEach(leadingComments, emitLeadingSynthesizedComment);
if (extendedDiagnostics) {
performance.measure("commentTime", "preEmitNodeWithSynthesizedComments");
}
}
emitNodeWithNestedComments(hint, node, emitFlags, emitCallback);
const trailingComments = emitNode && emitNode.trailingComments;
if (some(trailingComments)) {
if (extendedDiagnostics) {
performance.mark("postEmitNodeWithSynthesizedComments");
}
forEach(trailingComments, emitTrailingSynthesizedComment);
if (extendedDiagnostics) {
performance.measure("commentTime", "postEmitNodeWithSynthesizedComments");
}
}
}
function emitLeadingSynthesizedComment(comment: SynthesizedComment) {
if (comment.kind === SyntaxKind.SingleLineCommentTrivia) {
writer.writeLine();
}
writeSynthesizedComment(comment);
if (comment.hasTrailingNewLine || comment.kind === SyntaxKind.SingleLineCommentTrivia) {
writer.writeLine();
}
else {
writer.write(" ");
}
}
function emitTrailingSynthesizedComment(comment: SynthesizedComment) {
if (!writer.isAtStartOfLine()) {
writer.write(" ");
}
writeSynthesizedComment(comment);
if (comment.hasTrailingNewLine) {
writer.writeLine();
}
}
function writeSynthesizedComment(comment: SynthesizedComment) {
const text = formatSynthesizedComment(comment);
const lineMap = comment.kind === SyntaxKind.MultiLineCommentTrivia ? computeLineStarts(text) : undefined;
writeCommentRange(text, lineMap!, writer, 0, text.length, newLine);
}
function formatSynthesizedComment(comment: SynthesizedComment) {
return comment.kind === SyntaxKind.MultiLineCommentTrivia
? `/*${comment.text}*/`
: `//${comment.text}`;
}
function emitNodeWithNestedComments(hint: EmitHint, node: Node, emitFlags: EmitFlags, emitCallback: (hint: EmitHint, node: Node) => void) {
if (emitFlags & EmitFlags.NoNestedComments) {
disabled = true;
emitCallback(hint, node);
disabled = false;
}
else {
emitCallback(hint, node);
}
}
function emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void) {
if (extendedDiagnostics) {
performance.mark("preEmitBodyWithDetachedComments");
}
const { pos, end } = detachedRange;
const emitFlags = getEmitFlags(node);
const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0;
const skipTrailingComments = disabled || end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0;
if (!skipLeadingComments) {
emitDetachedCommentsAndUpdateCommentsInfo(detachedRange);
}
if (extendedDiagnostics) {
performance.measure("commentTime", "preEmitBodyWithDetachedComments");
}
if (emitFlags & EmitFlags.NoNestedComments && !disabled) {
disabled = true;
emitCallback(node);
disabled = false;
}
else {
emitCallback(node);
}
if (extendedDiagnostics) {
performance.mark("beginEmitBodyWithDetachedCommetns");
}
if (!skipTrailingComments) {
emitLeadingComments(detachedRange.end, /*isEmittedNode*/ true);
if (hasWrittenComment && !writer.isAtStartOfLine()) {
writer.writeLine();
}
}
if (extendedDiagnostics) {
performance.measure("commentTime", "beginEmitBodyWithDetachedCommetns");
}
}
function emitLeadingComments(pos: number, isEmittedNode: boolean) {
hasWrittenComment = false;
if (isEmittedNode) {
forEachLeadingCommentToEmit(pos, emitLeadingComment);
}
else if (pos === 0) {
// If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node,
// unless it is a triple slash comment at the top of the file.
// For Example:
// /// <reference-path ...>
// declare var x;
// /// <reference-path ...>
// interface F {}
// The first /// will NOT be removed while the second one will be removed even though both node will not be emitted
forEachLeadingCommentToEmit(pos, emitTripleSlashLeadingComment);
}
}
function emitTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
if (isTripleSlashComment(commentPos, commentEnd)) {
emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos);
}
}
function shouldWriteComment(text: string, pos: number) {
if (printerOptions.onlyPrintJsDocStyle) {
return (isJSDocLikeText(text, pos) || isPinnedComment(text, pos));
}
return true;
}
function emitLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
if (!shouldWriteComment(currentText, commentPos)) return;
if (!hasWrittenComment) {
emitNewLineBeforeLeadingCommentOfPosition(currentLineMap, writer, rangePos, commentPos);
hasWrittenComment = true;
}
// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
if (emitPos) emitPos(commentPos);
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
if (emitPos) emitPos(commentEnd);
if (hasTrailingNewLine) {
writer.writeLine();
}
else if (kind === SyntaxKind.MultiLineCommentTrivia) {
writer.write(" ");
}
}
function emitLeadingCommentsOfPosition(pos: number) {
if (disabled || pos === -1) {
return;
}
emitLeadingComments(pos, /*isEmittedNode*/ true);
}
function emitTrailingComments(pos: number) {
forEachTrailingCommentToEmit(pos, emitTrailingComment);
}
function emitTrailingComment(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) {
if (!shouldWriteComment(currentText, commentPos)) return;
// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment2*/
if (!writer.isAtStartOfLine()) {
writer.write(" ");
}
if (emitPos) emitPos(commentPos);
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
if (emitPos) emitPos(commentEnd);
if (hasTrailingNewLine) {
writer.writeLine();
}
}
function emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean) {
if (disabled) {
return;
}
if (extendedDiagnostics) {
performance.mark("beforeEmitTrailingCommentsOfPosition");
}
forEachTrailingCommentToEmit(pos, prefixSpace ? emitTrailingComment : emitTrailingCommentOfPosition);
if (extendedDiagnostics) {
performance.measure("commentTime", "beforeEmitTrailingCommentsOfPosition");
}
}
function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) {
// trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space
if (emitPos) emitPos(commentPos);
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
if (emitPos) emitPos(commentEnd);
if (hasTrailingNewLine) {
writer.writeLine();
}
else {
writer.write(" ");
}
}
function forEachLeadingCommentToEmit(pos: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
// Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments
if (containerPos === -1 || pos !== containerPos) {
if (hasDetachedComments(pos)) {
forEachLeadingCommentWithoutDetachedComments(cb);
}
else {
forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
}
}
}
function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) {
// Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments
if (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd)) {
forEachTrailingCommentRange(currentText, end, cb);
}
}
function reset() {
currentSourceFile = undefined!;
currentText = undefined!;
currentLineMap = undefined!;
detachedCommentsInfo = undefined;
}
function setWriter(output: EmitTextWriter): void {
writer = output;
}
function setSourceFile(sourceFile: SourceFile) {
currentSourceFile = sourceFile;
currentText = currentSourceFile.text;
currentLineMap = getLineStarts(currentSourceFile);
detachedCommentsInfo = undefined;
}
function hasDetachedComments(pos: number) {
return detachedCommentsInfo !== undefined && last(detachedCommentsInfo).nodePos === pos;
}
function forEachLeadingCommentWithoutDetachedComments(cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
// get the leading comments from detachedPos
const pos = last(detachedCommentsInfo!).detachedCommentEndPos;
if (detachedCommentsInfo!.length - 1) {
detachedCommentsInfo!.pop();
}
else {
detachedCommentsInfo = undefined;
}
forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
}
function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) {
const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, range, newLine, disabled);
if (currentDetachedCommentInfo) {
if (detachedCommentsInfo) {
detachedCommentsInfo.push(currentDetachedCommentInfo);
}
else {
detachedCommentsInfo = [currentDetachedCommentInfo];
}
}
}
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) {
if (!shouldWriteComment(currentText, commentPos)) return;
if (emitPos) emitPos(commentPos);
writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine);
if (emitPos) emitPos(commentEnd);
}
/**
* Determine if the given comment is a triple-slash
*
* @return true if the comment is a triple-slash comment else false
*/
function isTripleSlashComment(commentPos: number, commentEnd: number) {
return isRecognizedTripleSlashComment(currentText, commentPos, commentEnd);
}
}
}
+86 -27
View File
@@ -1,7 +1,7 @@
namespace ts {
// WARNING: The script `configureNightly.ts` uses a regexp to parse out these values.
// If changing the text in this section, be sure to test `configureNightly` too.
export const versionMajorMinor = "3.1";
export const versionMajorMinor = "3.3";
/** The version of the TypeScript compiler release */
export const version = `${versionMajorMinor}.0-dev`;
}
@@ -16,6 +16,10 @@ namespace ts {
[index: string]: T;
}
export interface SortedReadonlyArray<T> extends ReadonlyArray<T> {
" __sortedArrayBrand": any;
}
export interface SortedArray<T> extends Array<T> {
" __sortedArrayBrand": any;
}
@@ -65,6 +69,7 @@ namespace ts {
/* @internal */
namespace ts {
export const emptyArray: never[] = [] as never[];
/** Create a MapLike with good performance. */
function createDictionaryObject<T>(): MapLike<T> {
@@ -107,13 +112,13 @@ namespace ts {
}
// The global Map object. This may not be available, so we must test for it.
declare const Map: { new <T>(): Map<T> } | undefined;
declare const Map: (new <T>() => Map<T>) | undefined;
// Internet Explorer's Map doesn't support iteration, so don't use it.
// tslint:disable-next-line no-in-operator variable-name
export const MapCtr = typeof Map !== "undefined" && "entries" in Map.prototype ? Map : shimMap();
// Keep the class inside a function so it doesn't get compiled if it's not used.
function shimMap(): { new <T>(): Map<T> } {
function shimMap(): new <T>() => Map<T> {
class MapIterator<T, U extends (string | T | [string, T])> {
private data: MapLike<T>;
@@ -510,12 +515,27 @@ namespace ts {
* @param array The array to map.
* @param mapfn The callback used to map the result into one or more values.
*/
export function flatMap<T, U>(array: ReadonlyArray<T>, mapfn: (x: T, i: number) => U | ReadonlyArray<U> | undefined): U[];
export function flatMap<T, U>(array: ReadonlyArray<T> | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray<U> | undefined): U[] | undefined;
export function flatMap<T, U>(array: ReadonlyArray<T> | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray<U> | undefined): U[] | undefined {
export function flatMap<T, U>(array: ReadonlyArray<T> | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray<U> | undefined): ReadonlyArray<U> {
let result: U[] | undefined;
if (array) {
result = [];
for (let i = 0; i < array.length; i++) {
const v = mapfn(array[i], i);
if (v) {
if (isArray(v)) {
result = addRange(result, v);
}
else {
result = append(result, v);
}
}
}
}
return result || emptyArray;
}
export function flatMapToMutable<T, U>(array: ReadonlyArray<T> | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray<U> | undefined): U[] {
const result: U[] = [];
if (array) {
for (let i = 0; i < array.length; i++) {
const v = mapfn(array[i], i);
if (v) {
@@ -799,8 +819,8 @@ namespace ts {
/**
* Deduplicates an array that has already been sorted.
*/
function deduplicateSorted<T>(array: ReadonlyArray<T>, comparer: EqualityComparer<T> | Comparer<T>): T[] {
if (array.length === 0) return [];
function deduplicateSorted<T>(array: SortedReadonlyArray<T>, comparer: EqualityComparer<T> | Comparer<T>): SortedReadonlyArray<T> {
if (array.length === 0) return emptyArray as any as SortedReadonlyArray<T>;
let last = array[0];
const deduplicated: T[] = [last];
@@ -822,7 +842,7 @@ namespace ts {
deduplicated.push(last = next);
}
return deduplicated;
return deduplicated as any as SortedReadonlyArray<T>;
}
export function insertSorted<T>(array: SortedArray<T>, insert: T, compare: Comparer<T>): void {
@@ -837,11 +857,13 @@ namespace ts {
}
}
export function sortAndDeduplicate<T>(array: ReadonlyArray<T>, comparer: Comparer<T>, equalityComparer?: EqualityComparer<T>) {
return deduplicateSorted(sort(array, comparer), equalityComparer || comparer);
export function sortAndDeduplicate<T>(array: ReadonlyArray<string>): SortedReadonlyArray<string>;
export function sortAndDeduplicate<T>(array: ReadonlyArray<T>, comparer: Comparer<T>, equalityComparer?: EqualityComparer<T>): SortedReadonlyArray<T>;
export function sortAndDeduplicate<T>(array: ReadonlyArray<T>, comparer?: Comparer<T>, equalityComparer?: EqualityComparer<T>): SortedReadonlyArray<T> {
return deduplicateSorted(sort(array, comparer), equalityComparer || comparer || compareStringsCaseSensitive as any as Comparer<T>);
}
export function arrayIsEqualTo<T>(array1: ReadonlyArray<T> | undefined, array2: ReadonlyArray<T> | undefined, equalityComparer: (a: T, b: T) => boolean = equateValues): boolean {
export function arrayIsEqualTo<T>(array1: ReadonlyArray<T> | undefined, array2: ReadonlyArray<T> | undefined, equalityComparer: (a: T, b: T, index: number) => boolean = equateValues): boolean {
if (!array1 || !array2) {
return array1 === array2;
}
@@ -851,7 +873,7 @@ namespace ts {
}
for (let i = 0; i < array1.length; i++) {
if (!equalityComparer(array1[i], array2[i])) {
if (!equalityComparer(array1[i], array2[i], i)) {
return false;
}
}
@@ -1019,8 +1041,8 @@ namespace ts {
/**
* Returns a new sorted array.
*/
export function sort<T>(array: ReadonlyArray<T>, comparer: Comparer<T>): T[] {
return array.slice().sort(comparer);
export function sort<T>(array: ReadonlyArray<T>, comparer?: Comparer<T>): SortedReadonlyArray<T> {
return (array.length === 0 ? array : array.slice().sort(comparer)) as SortedReadonlyArray<T>;
}
export function arrayIterator<T>(array: ReadonlyArray<T>): Iterator<T> {
@@ -1039,10 +1061,10 @@ namespace ts {
/**
* Stable sort of an array. Elements equal to each other maintain their relative position in the array.
*/
export function stableSort<T>(array: ReadonlyArray<T>, comparer: Comparer<T>) {
export function stableSort<T>(array: ReadonlyArray<T>, comparer: Comparer<T>): SortedReadonlyArray<T> {
const indices = array.map((_, i) => i);
stableSortIndices(array, indices, comparer);
return indices.map(i => array[i]);
return indices.map(i => array[i]) as SortedArray<T> as SortedReadonlyArray<T>;
}
export function rangeEquals<T>(array1: ReadonlyArray<T>, array2: ReadonlyArray<T>, pos: number, end: number) {
@@ -1134,13 +1156,26 @@ namespace ts {
* @param offset An offset into `array` at which to start the search.
*/
export function binarySearch<T, U>(array: ReadonlyArray<T>, value: T, keySelector: (v: T) => U, keyComparer: Comparer<U>, offset?: number): number {
if (!array || array.length === 0) {
return binarySearchKey(array, keySelector(value), keySelector, keyComparer, offset);
}
/**
* Performs a binary search, finding the index at which an object with `key` occurs in `array`.
* If no such index is found, returns the 2's-complement of first index at which
* `array[index]` exceeds `key`.
* @param array A sorted array whose first element must be no larger than number
* @param key The key to be searched for in the array.
* @param keySelector A callback used to select the search key from each element of `array`.
* @param keyComparer A callback used to compare two keys in a sorted array.
* @param offset An offset into `array` at which to start the search.
*/
export function binarySearchKey<T, U>(array: ReadonlyArray<T>, key: U, keySelector: (v: T) => U, keyComparer: Comparer<U>, offset?: number): number {
if (!some(array)) {
return -1;
}
let low = offset || 0;
let high = array.length - 1;
const key = keySelector(value);
while (low <= high) {
const middle = low + ((high - low) >> 1);
const midKey = keySelector(array[middle]);
@@ -1233,9 +1268,9 @@ namespace ts {
}
/** Shims `Array.from`. */
export function arrayFrom<T, U>(iterator: Iterator<T>, map: (t: T) => U): U[];
export function arrayFrom<T>(iterator: Iterator<T>): T[];
export function arrayFrom(iterator: Iterator<any>, map?: (t: any) => any): any[] {
export function arrayFrom<T, U>(iterator: Iterator<T> | IterableIterator<T>, map: (t: T) => U): U[];
export function arrayFrom<T>(iterator: Iterator<T> | IterableIterator<T>): T[];
export function arrayFrom(iterator: Iterator<any> | IterableIterator<any>, map?: (t: any) => any): any[] {
const result: any[] = [];
for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) {
result.push(map ? map(value) : value);
@@ -1352,6 +1387,18 @@ namespace ts {
return result;
}
export function copyProperties<T1 extends T2, T2>(first: T1, second: T2) {
for (const id in second) {
if (hasOwnProperty.call(second, id)) {
(first as any)[id] = second[id];
}
}
}
export function maybeBind<T, A extends any[], R>(obj: T, fn: ((this: T, ...args: A) => R) | undefined): ((...args: A) => R) | undefined {
return fn ? fn.bind(obj) : undefined;
}
export interface MultiMap<T> extends Map<T[]> {
/**
* Adds the value to an array of values associated with the key, and returns the array.
@@ -1408,9 +1455,12 @@ namespace ts {
/**
* Tests whether a value is string
*/
export function isString(text: any): text is string {
export function isString(text: unknown): text is string {
return typeof text === "string";
}
export function isNumber(x: unknown): x is number {
return typeof x === "number";
}
export function tryCast<TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut | undefined;
export function tryCast<T>(value: T, test: (value: T) => boolean): T | undefined;
@@ -1533,6 +1583,7 @@ namespace ts {
* Every function should be assignable to this, but this should not be assignable to every function.
*/
export type AnyFunction = (...args: never[]) => void;
export type AnyConstructor = new (...args: unknown[]) => unknown;
export namespace Debug {
export let currentAssertionLevel = AssertionLevel.None;
@@ -1597,8 +1648,9 @@ namespace ts {
return value;
}
export function assertNever(member: never, message?: string, stackCrawlMark?: AnyFunction): never {
return fail(message || `Illegal value: ${member}`, stackCrawlMark || assertNever);
export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never {
const detail = "kind" in member && "pos" in member ? "SyntaxKind: " + showSyntaxKind(member as Node) : JSON.stringify(member);
return fail(`${message} ${detail}`, stackCrawlMark || assertNever);
}
export function getFunctionName(func: AnyFunction) {
@@ -2025,7 +2077,6 @@ namespace ts {
}
/** Represents a "prefix*suffix" pattern. */
/* @internal */
export interface Pattern {
prefix: string;
suffix: string;
@@ -2124,4 +2175,12 @@ namespace ts {
deleted(oldItems[oldIndex++]);
}
}
export function fill<T>(length: number, cb: (index: number) => T): T[] {
const result = Array<T>(length);
for (let i = 0; i < length; i++) {
result[i] = cb(i);
}
return result;
}
}
+265 -34
View File
@@ -835,7 +835,7 @@
"category": "Error",
"code": 1253
},
"A 'const' initializer in an ambient context must be a string or numeric literal.": {
"A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.": {
"category": "Error",
"code": 1254
},
@@ -1007,6 +1007,22 @@
"category": "Error",
"code": 1349
},
"Print the final configuration instead of building.": {
"category": "Message",
"code": 1350
},
"An identifier or keyword cannot immediately follow a numeric literal.": {
"category": "Error",
"code": 1351
},
"A bigint literal cannot use exponential notation.": {
"category": "Error",
"code": 1352
},
"A bigint literal must be an integer.": {
"category": "Error",
"code": 1353
},
"Duplicate identifier '{0}'.": {
"category": "Error",
@@ -1056,7 +1072,7 @@
"category": "Error",
"code": 2311
},
"An interface may only extend a class or another interface.": {
"An interface can only extend an object type or intersection of object types with statically known members.": {
"category": "Error",
"code": 2312
},
@@ -1232,7 +1248,7 @@
"category": "Error",
"code": 2355
},
"An arithmetic operand must be of type 'any', 'number' or an enum type.": {
"An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type.": {
"category": "Error",
"code": 2356
},
@@ -1256,11 +1272,11 @@
"category": "Error",
"code": 2361
},
"The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type.": {
"The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.": {
"category": "Error",
"code": 2362
},
"The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type.": {
"The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.": {
"category": "Error",
"code": 2363
},
@@ -1484,7 +1500,7 @@
"category": "Error",
"code": 2420
},
"A class may only implement another class or interface.": {
"A class can only implement an object type or intersection of object types with statically known members.": {
"category": "Error",
"code": 2422
},
@@ -1628,14 +1644,6 @@
"category": "Error",
"code": 2458
},
"Type '{0}' has no property '{1}' and no string index signature.": {
"category": "Error",
"code": 2459
},
"Type '{0}' has no property '{1}'.": {
"category": "Error",
"code": 2460
},
"Type '{0}' is not an array type.": {
"category": "Error",
"code": 2461
@@ -1688,7 +1696,7 @@
"category": "Error",
"code": 2473
},
"In 'const' enum declarations member initializer must be constant expression.": {
"const enum member initializers can only contain literal values and other computed enum values.": {
"category": "Error",
"code": 2474
},
@@ -1752,7 +1760,7 @@
"category": "Error",
"code": 2492
},
"Tuple type '{0}' with length '{1}' cannot be assigned to tuple with length '{2}'.": {
"Tuple type '{0}' of length '{1}' has no element at index '{2}'.": {
"category": "Error",
"code": 2493
},
@@ -1768,7 +1776,7 @@
"category": "Error",
"code": 2496
},
"Module '{0}' resolves to a non-module entity and cannot be imported using this construct.": {
"This module can only be referenced with ECMAScript imports/exports by turning on the '{0}' flag and referencing its default export.": {
"category": "Error",
"code": 2497
},
@@ -1816,7 +1824,7 @@
"category": "Error",
"code": 2508
},
"Base constructor return type '{0}' is not a class or interface type.": {
"Base constructor return type '{0}' is not an object type or intersection of object types with statically known members.": {
"category": "Error",
"code": 2509
},
@@ -1940,7 +1948,7 @@
"category": "Error",
"code": 2539
},
"Cannot assign to '{0}' because it is a constant or a read-only property.": {
"Cannot assign to '{0}' because it is a read-only property.": {
"category": "Error",
"code": 2540
},
@@ -2048,10 +2056,6 @@
"category": "Error",
"code": 2567
},
"Type '{0}' is not an array type. Use compiler option '--downlevelIteration' to allow iterating of iterators.": {
"category": "Error",
"code": 2568
},
"Type '{0}' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.": {
"category": "Error",
"code": 2569
@@ -2088,6 +2092,42 @@
"category": "Error",
"code": 2577
},
"Cannot find name '{0}'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.": {
"category": "Error",
"code": 2580
},
"Cannot find name '{0}'. Do you need to install type definitions for jQuery? Try `npm i @types/jquery` and then add `jquery` to the types field in your tsconfig.": {
"category": "Error",
"code": 2581
},
"Cannot find name '{0}'. Do you need to install type definitions for a test runner? Try `npm i @types/jest` or `npm i @types/mocha` and then add `jest` or `mocha` to the types field in your tsconfig.": {
"category": "Error",
"code": 2582
},
"Cannot find name '{0}'. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later.": {
"category": "Error",
"code": 2583
},
"Cannot find name '{0}'. Do you need to change your target library? Try changing the `lib` compiler option to include 'dom'.": {
"category": "Error",
"code": 2584
},
"'{0}' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later.": {
"category": "Error",
"code": 2585
},
"Enum type '{0}' circularly references itself.": {
"category": "Error",
"code": 2586
},
"JSDoc type '{0}' circularly references itself.": {
"category": "Error",
"code": 2587
},
"Cannot assign to '{0}' because it is a constant.": {
"category": "Error",
"code": 2588
},
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600
@@ -2453,9 +2493,61 @@
"category": "Error",
"code": 2732
},
"Index '{0}' is out-of-bounds in tuple of length {1}.": {
"It is highly likely that you are missing a semicolon.": {
"category": "Error",
"code": 2733
"code": 2734
},
"Did you mean for '{0}' to be constrained to type 'new (...args: any[]) => {1}'?": {
"category": "Error",
"code": 2735
},
"Operator '{0}' cannot be applied to type '{1}'.": {
"category": "Error",
"code": 2736
},
"BigInt literals are not available when targeting lower than ESNext.": {
"category": "Error",
"code": 2737
},
"An outer value of 'this' is shadowed by this container.": {
"category": "Message",
"code": 2738
},
"Type '{0}' is missing the following properties from type '{1}': {2}": {
"category": "Error",
"code": 2739
},
"Type '{0}' is missing the following properties from type '{1}': {2}, and {3} more.": {
"category": "Error",
"code": 2740
},
"Property '{0}' is missing in type '{1}' but required in type '{2}'.": {
"category": "Error",
"code": 2741
},
"The inferred type of '{0}' cannot be named without a reference to '{1}'. This is likely not portable. A type annotation is necessary.": {
"category": "Error",
"code": 2742
},
"No overload expects {0} type arguments, but overloads do exist that expect either {1} or {2} type arguments.": {
"category": "Error",
"code": 2743
},
"Type parameter defaults can only reference previously declared type parameters.": {
"category": "Error",
"code": 2744
},
"This JSX tag's '{0}' prop expects type '{1}' which requires multiple children, but only a single child was provided.": {
"category": "Error",
"code": 2745
},
"This JSX tag's '{0}' prop expects a single child of type '{1}', but multiple children were provided.": {
"category": "Error",
"code": 2746
},
"'{0}' components don't accept text as child elements. Text in JSX has the type 'string', but the expected type of '{1}' is '{2}'.": {
"category": "Error",
"code": 2747
},
"Import declaration '{0}' is using private name '{1}'.": {
@@ -2920,7 +3012,10 @@
"category": "Error",
"code": 5072
},
"Build option '{0}' requires a value of type {1}.": {
"category": "Error",
"code": 5073
},
"Generates a sourcemap for each corresponding '.d.ts' file.": {
"category": "Message",
@@ -3294,7 +3389,7 @@
"category": "Message",
"code": 6104
},
"Expected type of '{0}' field in 'package.json' to be 'string', got '{1}'.": {
"Expected type of '{0}' field in 'package.json' to be '{1}', got '{2}'.": {
"category": "Message",
"code": 6105
},
@@ -3692,6 +3787,54 @@
"category": "Error",
"code": 6205
},
"'package.json' has a 'typesVersions' field with version-specific path mappings.": {
"category": "Message",
"code": 6206
},
"'package.json' does not have a 'typesVersions' entry that matches version '{0}'.": {
"category": "Message",
"code": 6207
},
"'package.json' has a 'typesVersions' entry '{0}' that matches compiler version '{1}', looking for a pattern to match module name '{2}'.": {
"category": "Message",
"code": 6208
},
"'package.json' has a 'typesVersions' entry '{0}' that is not a valid semver range.": {
"category": "Message",
"code": 6209
},
"An argument for '{0}' was not provided.": {
"category": "Message",
"code": 6210
},
"An argument matching this binding pattern was not provided.": {
"category": "Message",
"code": 6211
},
"Did you mean to call this expression?": {
"category": "Message",
"code": 6212
},
"Did you mean to use 'new' with this expression?": {
"category": "Message",
"code": 6213
},
"Enable strict 'bind', 'call', and 'apply' methods on functions.": {
"category": "Message",
"code": 6214
},
"Using compiler options of project reference redirect '{0}'.": {
"category": "Message",
"code": 6215
},
"Found 1 error.": {
"category": "Message",
"code": 6216
},
"Found {0} errors.": {
"category": "Message",
"code": 6217
},
"Projects to reference": {
"category": "Message",
@@ -3814,8 +3957,8 @@
"category": "Error",
"code": 6370
},
"Skipping clean because not all projects could be located": {
"category": "Error",
"Updating unchanged output timestamps of project '{0}'...": {
"category": "Message",
"code": 6371
},
@@ -3827,6 +3970,10 @@
"category": "Message",
"code": 6501
},
"The expected type comes from the return type of this signature.": {
"category": "Message",
"code": 6502
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
@@ -3856,6 +4003,10 @@
"category": "Error",
"code": 7013
},
"Function type, which lacks return-type annotation, implicitly has an '{0}' return type.": {
"category": "Error",
"code": 7014
},
"Element implicitly has an 'any' type because index expression is not of type 'number'.": {
"category": "Error",
"code": 7015
@@ -3966,6 +4117,43 @@
"category": "Error",
"code": 7042
},
"Variable '{0}' implicitly has an '{1}' type, but a better type may be inferred from usage.": {
"category": "Suggestion",
"code": 7043
},
"Parameter '{0}' implicitly has an '{1}' type, but a better type may be inferred from usage.": {
"category": "Suggestion",
"code": 7044
},
"Member '{0}' implicitly has an '{1}' type, but a better type may be inferred from usage.": {
"category": "Suggestion",
"code": 7045
},
"Variable '{0}' implicitly has type '{1}' in some locations, but a better type may be inferred from usage.": {
"category": "Suggestion",
"code": 7046
},
"Rest parameter '{0}' implicitly has an 'any[]' type, but a better type may be inferred from usage.": {
"category": "Suggestion",
"code": 7047
},
"Property '{0}' implicitly has type 'any', but a better type for its get accessor may be inferred from usage.": {
"category": "Suggestion",
"code": 7048
},
"Property '{0}' implicitly has type 'any', but a better type for its set accessor may be inferred from usage.": {
"category": "Suggestion",
"code": 7049
},
"'{0}' implicitly has an '{1}' return type, but a better type may be inferred from usage.": {
"category": "Suggestion",
"code": 7050
},
"Parameter has a name but no type. Did you mean '{0}: {1}'?": {
"category": "Error",
"code": 7051
},
"You cannot rename this element.": {
"category": "Error",
"code": 8000
@@ -4086,6 +4274,14 @@
"category": "Error",
"code": 8030
},
"You cannot rename a module via a global import.": {
"category": "Error",
"code": 8031
},
"Qualified name '{0}' is not allowed without a leading '@param {object} {1}'.": {
"category": "Error",
"code": 8032
},
"Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": {
"category": "Error",
"code": 9002
@@ -4344,6 +4540,10 @@
"category": "Message",
"code": 90033
},
"Add parameter name": {
"category": "Message",
"code": 90034
},
"Convert function to an ES2015 class": {
"category": "Message",
"code": 95001
@@ -4584,7 +4784,6 @@
"category": "Message",
"code": 95062
},
"Add missing enum member '{0}'": {
"category": "Message",
"code": 95063
@@ -4593,12 +4792,44 @@
"category": "Message",
"code": 95064
},
"Convert to async function":{
"Convert to async function": {
"category": "Message",
"code": 95065
"code": 95065
},
"Convert all to async functions": {
"category": "Message",
"code": 95066
"category": "Message",
"code": 95066
},
"Generate types for '{0}'": {
"category": "Message",
"code": 95067
},
"Generate types for all packages without types": {
"category": "Message",
"code": 95068
},
"Add 'unknown' conversion for non-overlapping types": {
"category": "Message",
"code": 95069
},
"Add 'unknown' to all conversions of non-overlapping types": {
"category": "Message",
"code": 95070
},
"Add missing 'new' operator to call": {
"category": "Message",
"code": 95071
},
"Add missing 'new' operator to all calls": {
"category": "Message",
"code": 95072
},
"Add names to all parameters without names": {
"category": "Message",
"code": 95073
},
"Enable the 'experimentalDecorators' option in your configuration file": {
"category": "Message",
"code": 95074
}
}
+1032 -275
View File
File diff suppressed because it is too large Load Diff
+64 -4
View File
@@ -68,13 +68,16 @@ namespace ts {
/* @internal */ export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier, isSingleQuote: boolean): StringLiteral; // tslint:disable-line unified-signatures
/** If a node is passed, creates a string literal whose source text is read from a source node during emit. */
export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier): StringLiteral;
export function createLiteral(value: number): NumericLiteral;
export function createLiteral(value: number | PseudoBigInt): NumericLiteral;
export function createLiteral(value: boolean): BooleanLiteral;
export function createLiteral(value: string | number | boolean): PrimaryExpression;
export function createLiteral(value: string | number | boolean | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier, isSingleQuote?: boolean): PrimaryExpression {
export function createLiteral(value: string | number | PseudoBigInt | boolean): PrimaryExpression;
export function createLiteral(value: string | number | PseudoBigInt | boolean | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier, isSingleQuote?: boolean): PrimaryExpression {
if (typeof value === "number") {
return createNumericLiteral(value + "");
}
if (typeof value === "object" && "base10Value" in value) { // PseudoBigInt
return createBigIntLiteral(pseudoBigIntToString(value) + "n");
}
if (typeof value === "boolean") {
return value ? createTrue() : createFalse();
}
@@ -93,6 +96,12 @@ namespace ts {
return node;
}
export function createBigIntLiteral(value: string): BigIntLiteral {
const node = <BigIntLiteral>createSynthesizedNode(SyntaxKind.BigIntLiteral);
node.text = value;
return node;
}
export function createStringLiteral(text: string): StringLiteral {
const node = <StringLiteral>createSynthesizedNode(SyntaxKind.StringLiteral);
node.text = text;
@@ -235,7 +244,7 @@ namespace ts {
// Modifiers
export function createModifier<T extends Modifier["kind"]>(kind: T) {
export function createModifier<T extends Modifier["kind"]>(kind: T): Token<T> {
return createToken(kind);
}
@@ -2170,6 +2179,56 @@ namespace ts {
: node;
}
// JSDoc
/* @internal */
export function createJSDocTypeExpression(type: TypeNode): JSDocTypeExpression {
const node = createSynthesizedNode(SyntaxKind.JSDocTypeExpression) as JSDocTypeExpression;
node.type = type;
return node;
}
/* @internal */
export function createJSDocTypeTag(typeExpression?: JSDocTypeExpression, comment?: string): JSDocTypeTag {
const tag = createJSDocTag<JSDocTypeTag>(SyntaxKind.JSDocTypeTag, "type");
tag.typeExpression = typeExpression;
tag.comment = comment;
return tag;
}
/* @internal */
export function createJSDocReturnTag(typeExpression?: JSDocTypeExpression, comment?: string): JSDocReturnTag {
const tag = createJSDocTag<JSDocReturnTag>(SyntaxKind.JSDocReturnTag, "returns");
tag.typeExpression = typeExpression;
tag.comment = comment;
return tag;
}
/* @internal */
export function createJSDocParamTag(name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, comment?: string): JSDocParameterTag {
const tag = createJSDocTag<JSDocParameterTag>(SyntaxKind.JSDocParameterTag, "param");
tag.typeExpression = typeExpression;
tag.name = name;
tag.isBracketed = isBracketed;
tag.comment = comment;
return tag;
}
/* @internal */
export function createJSDocComment(comment?: string | undefined, tags?: NodeArray<JSDocTag> | undefined) {
const node = createSynthesizedNode(SyntaxKind.JSDocComment) as JSDoc;
node.comment = comment;
node.tags = tags;
return node;
}
/* @internal */
function createJSDocTag<T extends JSDocTag>(kind: T["kind"], tagName: string): T {
const node = createSynthesizedNode(kind) as T;
node.tagName = createIdentifier(tagName);
return node;
}
// JSX
export function createJsxElement(openingElement: JsxOpeningElement, children: ReadonlyArray<JsxChild>, closingElement: JsxClosingElement) {
@@ -3416,6 +3475,7 @@ namespace ts {
return cacheIdentifiers;
case SyntaxKind.ThisKeyword:
case SyntaxKind.NumericLiteral:
case SyntaxKind.BigIntLiteral:
case SyntaxKind.StringLiteral:
return false;
case SyntaxKind.ArrayLiteralExpression:
+162
View File
@@ -0,0 +1,162 @@
/* @internal */
namespace ts {
export interface InspectValueOptions {
readonly fileNameToRequire: string;
}
export const enum ValueKind { Const, Array, FunctionOrClass, Object }
export interface ValueInfoBase {
readonly name: string;
}
export type ValueInfo = ValueInfoSimple | ValueInfoArray | ValueInfoFunctionOrClass | ValueInfoObject;
export interface ValueInfoSimple extends ValueInfoBase {
readonly kind: ValueKind.Const;
readonly typeName: string;
readonly comment?: string | undefined;
}
export interface ValueInfoFunctionOrClass extends ValueInfoBase {
readonly kind: ValueKind.FunctionOrClass;
readonly source: string | number; // For a native function, this is the length.
readonly prototypeMembers: ReadonlyArray<ValueInfo>;
readonly namespaceMembers: ReadonlyArray<ValueInfo>;
}
export interface ValueInfoArray extends ValueInfoBase {
readonly kind: ValueKind.Array;
readonly inner: ValueInfo;
}
export interface ValueInfoObject extends ValueInfoBase {
readonly kind: ValueKind.Object;
readonly hasNontrivialPrototype: boolean;
readonly members: ReadonlyArray<ValueInfo>;
}
export function inspectModule(fileNameToRequire: string): ValueInfo {
return inspectValue(removeFileExtension(getBaseFileName(fileNameToRequire)), tryRequire(fileNameToRequire));
}
export function inspectValue(name: string, value: unknown): ValueInfo {
return getValueInfo(name, value, getRecurser());
}
type Recurser = <T>(obj: unknown, name: string, cbOk: () => T, cbFail: (isCircularReference: boolean, keyStack: ReadonlyArray<string>) => T) => T;
function getRecurser(): Recurser {
const seen: unknown[] = [];
const nameStack: string[] = [];
return (obj, name, cbOk, cbFail) => {
if (seen.indexOf(obj) !== -1 || nameStack.length > 4) {
return cbFail(seen.indexOf(obj) !== -1, nameStack);
}
seen.push(obj);
nameStack.push(name);
const res = cbOk();
nameStack.pop();
seen.pop();
return res;
};
}
function getValueInfo(name: string, value: unknown, recurser: Recurser): ValueInfo {
return recurser(value, name,
(): ValueInfo => {
if (typeof value === "function") return getFunctionOrClassInfo(value as AnyFunction, name, recurser);
if (typeof value === "object") {
const builtin = getBuiltinType(name, value as object, recurser);
if (builtin !== undefined) return builtin;
const entries = getEntriesOfObject(value as object);
const hasNontrivialPrototype = Object.getPrototypeOf(value) !== Object.prototype;
const members = flatMap(entries, ({ key, value }) => getValueInfo(key, value, recurser));
return { kind: ValueKind.Object, name, hasNontrivialPrototype, members };
}
return { kind: ValueKind.Const, name, typeName: isNullOrUndefined(value) ? "any" : typeof value };
},
(isCircularReference, keyStack) => anyValue(name, ` ${isCircularReference ? "Circular reference" : "Too-deep object hierarchy"} from ${keyStack.join(".")}`));
}
function getFunctionOrClassInfo(fn: AnyFunction, name: string, recurser: Recurser): ValueInfoFunctionOrClass {
const prototypeMembers = getPrototypeMembers(fn, recurser);
const namespaceMembers = flatMap(getEntriesOfObject(fn), ({ key, value }) => getValueInfo(key, value, recurser));
const toString = cast(Function.prototype.toString.call(fn), isString);
const source = stringContains(toString, "{ [native code] }") ? getFunctionLength(fn) : toString;
return { kind: ValueKind.FunctionOrClass, name, source, namespaceMembers, prototypeMembers };
}
const builtins: () => ReadonlyMap<AnyConstructor> = memoize(() => {
const map = createMap<AnyConstructor>();
for (const { key, value } of getEntriesOfObject(global)) {
if (typeof value === "function" && typeof value.prototype === "object" && value !== Object) {
map.set(key, value as AnyConstructor);
}
}
return map;
});
function getBuiltinType(name: string, value: object, recurser: Recurser): ValueInfo | undefined {
return isArray(value)
? { name, kind: ValueKind.Array, inner: value.length && getValueInfo("element", first(value), recurser) || anyValue(name) }
: forEachEntry(builtins(), (builtin, builtinName): ValueInfo | undefined =>
value instanceof builtin ? { kind: ValueKind.Const, name, typeName: builtinName } : undefined);
}
function getPrototypeMembers(fn: AnyFunction, recurser: Recurser): ReadonlyArray<ValueInfo> {
const prototype = fn.prototype as unknown;
// tslint:disable-next-line no-unnecessary-type-assertion (TODO: update LKG and it will really be unnecessary)
return typeof prototype !== "object" || prototype === null ? emptyArray : mapDefined(getEntriesOfObject(prototype as object), ({ key, value }) =>
key === "constructor" ? undefined : getValueInfo(key, value, recurser));
}
const ignoredProperties: ReadonlyArray<string> = ["arguments", "caller", "constructor", "eval", "super_"];
const reservedFunctionProperties: ReadonlyArray<string> = Object.getOwnPropertyNames(noop);
interface ObjectEntry { readonly key: string; readonly value: unknown; }
function getEntriesOfObject(obj: object): ReadonlyArray<ObjectEntry> {
const seen = createMap<true>();
const entries: ObjectEntry[] = [];
let chain = obj;
while (!isNullOrUndefined(chain) && chain !== Object.prototype && chain !== Function.prototype) {
for (const key of Object.getOwnPropertyNames(chain)) {
if (!isJsPrivate(key) &&
ignoredProperties.indexOf(key) === -1 &&
(typeof obj !== "function" || reservedFunctionProperties.indexOf(key) === -1) &&
// Don't add property from a higher prototype if it already exists in a lower one
addToSeen(seen, key)) {
const value = safeGetPropertyOfObject(chain, key);
// Don't repeat "toString" that matches signature from Object.prototype
if (!(key === "toString" && typeof value === "function" && value.length === 0)) {
entries.push({ key, value });
}
}
}
chain = Object.getPrototypeOf(chain);
}
return entries.sort((e1, e2) => compareStringsCaseSensitive(e1.key, e2.key));
}
function getFunctionLength(fn: AnyFunction): number {
return tryCast(safeGetPropertyOfObject(fn, "length"), isNumber) || 0;
}
function safeGetPropertyOfObject(obj: object, key: string): unknown {
const desc = Object.getOwnPropertyDescriptor(obj, key);
return desc && desc.value;
}
function isNullOrUndefined(value: unknown): value is null | undefined {
return value == null; // tslint:disable-line
}
function anyValue(name: string, comment?: string): ValueInfo {
return { kind: ValueKind.Const, name, typeName: "any", comment };
}
export function isJsPrivate(name: string): boolean {
return startsWith(name, "_");
}
function tryRequire(fileNameToRequire: string): unknown {
try {
return require(fileNameToRequire);
}
catch {
return undefined;
}
}
}
File diff suppressed because it is too large Load Diff
+40 -20
View File
@@ -30,7 +30,7 @@ namespace ts.moduleSpecifiers {
function getPreferencesForUpdate(compilerOptions: CompilerOptions, oldImportSpecifier: string): Preferences {
return {
relativePreference: isExternalModuleNameRelative(oldImportSpecifier) ? RelativePreference.Relative : RelativePreference.NonRelative,
ending: hasJavaScriptOrJsonFileExtension(oldImportSpecifier) ? Ending.JsExtension
ending: hasJSOrJsonFileExtension(oldImportSpecifier) ? Ending.JsExtension
: getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeJs || endsWith(oldImportSpecifier, "index") ? Ending.Index : Ending.Minimal,
};
}
@@ -139,7 +139,7 @@ namespace ts.moduleSpecifiers {
return isPathRelativeToParent(nonRelative) || countPathComponents(relativePath) < countPathComponents(nonRelative) ? relativePath : nonRelative;
}
function countPathComponents(path: string): number {
export function countPathComponents(path: string): number {
let count = 0;
for (let i = startsWith(path, "./") ? 2 : 0; i < path.length; i++) {
if (path.charCodeAt(i) === CharacterCodes.slash) count++;
@@ -148,7 +148,7 @@ namespace ts.moduleSpecifiers {
}
function usesJsExtensionOnImports({ imports }: SourceFile): boolean {
return firstDefined(imports, ({ text }) => pathIsRelative(text) ? hasJavaScriptOrJsonFileExtension(text) : undefined) || false;
return firstDefined(imports, ({ text }) => pathIsRelative(text) ? hasJSOrJsonFileExtension(text) : undefined) || false;
}
function stringsEqual(a: string, b: string, getCanonicalFileName: GetCanonicalFileName): boolean {
@@ -203,7 +203,7 @@ namespace ts.moduleSpecifiers {
return; // Don't want to a package to globally import from itself
}
const target = targets.find(t => compareStrings(t.slice(0, resolved.length + 1), resolved + "/") === Comparison.EqualTo);
const target = find(targets, t => compareStrings(t.slice(0, resolved.length + 1), resolved + "/") === Comparison.EqualTo);
if (target === undefined) return;
const relative = getRelativePathFromDirectory(resolved, target, getCanonicalFileName);
@@ -235,7 +235,8 @@ namespace ts.moduleSpecifiers {
const suffix = pattern.substr(indexOfStar + 1);
if (relativeToBaseUrl.length >= prefix.length + suffix.length &&
startsWith(relativeToBaseUrl, prefix) &&
endsWith(relativeToBaseUrl, suffix)) {
endsWith(relativeToBaseUrl, suffix) ||
!suffix && relativeToBaseUrl === removeTrailingDirectorySeparator(prefix)) {
const matchedStar = relativeToBaseUrl.substr(prefix.length, relativeToBaseUrl.length - suffix.length);
return key.replace("*", matchedStar);
}
@@ -259,11 +260,34 @@ namespace ts.moduleSpecifiers {
}
function tryGetModuleNameAsNodeModule(moduleFileName: string, { getCanonicalFileName, sourceDirectory }: Info, host: ModuleSpecifierResolutionHost, options: CompilerOptions): string | undefined {
if (!host.fileExists || !host.readFile) {
return undefined;
}
const parts: NodeModulePathParts = getNodeModulePathParts(moduleFileName)!;
if (!parts) {
return undefined;
}
const packageRootPath = moduleFileName.substring(0, parts.packageRootIndex);
const packageJsonPath = combinePaths(packageRootPath, "package.json");
const packageJsonContent = host.fileExists(packageJsonPath)
? JSON.parse(host.readFile(packageJsonPath)!)
: undefined;
const versionPaths = packageJsonContent && packageJsonContent.typesVersions
? getPackageJsonTypesVersionsPaths(packageJsonContent.typesVersions)
: undefined;
if (versionPaths) {
const subModuleName = moduleFileName.slice(parts.packageRootIndex + 1);
const fromPaths = tryGetModuleNameFromPaths(
removeFileExtension(subModuleName),
removeExtensionAndIndexPostFix(subModuleName, Ending.Minimal, options),
versionPaths.paths
);
if (fromPaths !== undefined) {
moduleFileName = combinePaths(moduleFileName.slice(0, parts.packageRootIndex), fromPaths);
}
}
// Simplify the full file path to something that can be resolved by Node.
// If the module could be imported by a directory name, use that directory's name
@@ -274,23 +298,18 @@ namespace ts.moduleSpecifiers {
// If the module was found in @types, get the actual Node package name
const nodeModulesDirectoryName = moduleSpecifier.substring(parts.topLevelPackageNameIndex + 1);
const packageName = getPackageNameFromAtTypesDirectory(nodeModulesDirectoryName);
const packageName = getPackageNameFromTypesPackageName(nodeModulesDirectoryName);
// For classic resolution, only allow importing from node_modules/@types, not other node_modules
return getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs && packageName === nodeModulesDirectoryName ? undefined : packageName;
function getDirectoryOrExtensionlessFileName(path: string): string {
// If the file is the main module, it can be imported by the package name
const packageRootPath = path.substring(0, parts.packageRootIndex);
const packageJsonPath = combinePaths(packageRootPath, "package.json");
if (host.fileExists!(packageJsonPath)) { // TODO: GH#18217
const packageJsonContent = JSON.parse(host.readFile!(packageJsonPath)!);
if (packageJsonContent) {
const mainFileRelative = packageJsonContent.typings || packageJsonContent.types || packageJsonContent.main;
if (mainFileRelative) {
const mainExportFile = toPath(mainFileRelative, packageRootPath, getCanonicalFileName);
if (removeFileExtension(mainExportFile) === removeFileExtension(getCanonicalFileName(path))) {
return packageRootPath;
}
if (packageJsonContent) {
const mainFileRelative = packageJsonContent.typings || packageJsonContent.types || packageJsonContent.main;
if (mainFileRelative) {
const mainExportFile = toPath(mainFileRelative, packageRootPath, getCanonicalFileName);
if (removeFileExtension(mainExportFile) === removeFileExtension(getCanonicalFileName(path))) {
return packageRootPath;
}
}
}
@@ -309,11 +328,12 @@ namespace ts.moduleSpecifiers {
}
function tryGetAnyFileFromPath(host: ModuleSpecifierResolutionHost, path: string) {
if (!host.fileExists) return;
// We check all js, `node` and `json` extensions in addition to TS, since node module resolution would also choose those over the directory
const extensions = getSupportedExtensions({ allowJs: true }, [{ extension: "node", isMixedContent: false }, { extension: "json", isMixedContent: false, scriptKind: ScriptKind.JSON }]);
for (const e of extensions) {
const fullPath = path + e;
if (host.fileExists!(fullPath)) { // TODO: GH#18217
if (host.fileExists(fullPath)) {
return fullPath;
}
}
@@ -399,13 +419,13 @@ namespace ts.moduleSpecifiers {
case Ending.Index:
return noExtension;
case Ending.JsExtension:
return noExtension + getJavaScriptExtensionForFile(fileName, options);
return noExtension + getJSExtensionForFile(fileName, options);
default:
return Debug.assertNever(ending);
}
}
function getJavaScriptExtensionForFile(fileName: string, options: CompilerOptions): Extension {
function getJSExtensionForFile(fileName: string, options: CompilerOptions): Extension {
const ext = extensionFromPath(fileName);
switch (ext) {
case Extension.Ts:
+160 -169
View File
@@ -462,52 +462,46 @@ namespace ts {
return visitNodes(cbNode, cbNodes, (<JSDoc>node).tags);
case SyntaxKind.JSDocParameterTag:
case SyntaxKind.JSDocPropertyTag:
if ((node as JSDocPropertyLikeTag).isNameFirst) {
return visitNode(cbNode, (<JSDocPropertyLikeTag>node).name) ||
visitNode(cbNode, (<JSDocPropertyLikeTag>node).typeExpression);
}
else {
return visitNode(cbNode, (<JSDocPropertyLikeTag>node).typeExpression) ||
visitNode(cbNode, (<JSDocPropertyLikeTag>node).name);
}
case SyntaxKind.JSDocReturnTag:
return visitNode(cbNode, (<JSDocReturnTag>node).typeExpression);
case SyntaxKind.JSDocTypeTag:
return visitNode(cbNode, (<JSDocTypeTag>node).typeExpression);
return visitNode(cbNode, (node as JSDocTag).tagName) ||
((node as JSDocPropertyLikeTag).isNameFirst
? visitNode(cbNode, (<JSDocPropertyLikeTag>node).name) ||
visitNode(cbNode, (<JSDocPropertyLikeTag>node).typeExpression)
: visitNode(cbNode, (<JSDocPropertyLikeTag>node).typeExpression) ||
visitNode(cbNode, (<JSDocPropertyLikeTag>node).name));
case SyntaxKind.JSDocAugmentsTag:
return visitNode(cbNode, (<JSDocAugmentsTag>node).class);
return visitNode(cbNode, (node as JSDocTag).tagName) ||
visitNode(cbNode, (<JSDocAugmentsTag>node).class);
case SyntaxKind.JSDocTemplateTag:
return visitNode(cbNode, (<JSDocTemplateTag>node).constraint) || visitNodes(cbNode, cbNodes, (<JSDocTemplateTag>node).typeParameters);
return visitNode(cbNode, (node as JSDocTag).tagName) ||
visitNode(cbNode, (<JSDocTemplateTag>node).constraint) ||
visitNodes(cbNode, cbNodes, (<JSDocTemplateTag>node).typeParameters);
case SyntaxKind.JSDocTypedefTag:
if ((node as JSDocTypedefTag).typeExpression &&
(node as JSDocTypedefTag).typeExpression!.kind === SyntaxKind.JSDocTypeExpression) {
return visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression) ||
visitNode(cbNode, (<JSDocTypedefTag>node).fullName);
}
else {
return visitNode(cbNode, (<JSDocTypedefTag>node).fullName) ||
visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression);
}
return visitNode(cbNode, (node as JSDocTag).tagName) ||
((node as JSDocTypedefTag).typeExpression &&
(node as JSDocTypedefTag).typeExpression!.kind === SyntaxKind.JSDocTypeExpression
? visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression) ||
visitNode(cbNode, (<JSDocTypedefTag>node).fullName)
: visitNode(cbNode, (<JSDocTypedefTag>node).fullName) ||
visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression));
case SyntaxKind.JSDocCallbackTag:
return visitNode(cbNode, (node as JSDocCallbackTag).fullName) ||
return visitNode(cbNode, (node as JSDocTag).tagName) ||
visitNode(cbNode, (node as JSDocCallbackTag).fullName) ||
visitNode(cbNode, (node as JSDocCallbackTag).typeExpression);
case SyntaxKind.JSDocReturnTag:
case SyntaxKind.JSDocTypeTag:
case SyntaxKind.JSDocThisTag:
return visitNode(cbNode, (node as JSDocThisTag).typeExpression);
case SyntaxKind.JSDocEnumTag:
return visitNode(cbNode, (node as JSDocEnumTag).typeExpression);
return visitNode(cbNode, (node as JSDocTag).tagName) ||
visitNode(cbNode, (node as JSDocReturnTag | JSDocTypeTag | JSDocThisTag | JSDocEnumTag).typeExpression);
case SyntaxKind.JSDocSignature:
return visitNodes(cbNode, cbNodes, node.decorators) ||
visitNodes(cbNode, cbNodes, node.modifiers) ||
forEach((<JSDocSignature>node).typeParameters, cbNode) ||
return forEach((<JSDocSignature>node).typeParameters, cbNode) ||
forEach((<JSDocSignature>node).parameters, cbNode) ||
visitNode(cbNode, (<JSDocSignature>node).type);
case SyntaxKind.JSDocTypeLiteral:
if ((node as JSDocTypeLiteral).jsDocPropertyTags) {
for (const tag of (node as JSDocTypeLiteral).jsDocPropertyTags!) {
visitNode(cbNode, tag);
}
}
return;
return forEach((node as JSDocTypeLiteral).jsDocPropertyTags, cbNode);
case SyntaxKind.JSDocTag:
case SyntaxKind.JSDocClassTag:
return visitNode(cbNode, (node as JSDocTag).tagName);
case SyntaxKind.PartiallyEmittedExpression:
return visitNode(cbNode, (<PartiallyEmittedExpression>node).expression);
}
@@ -1486,7 +1480,15 @@ namespace ts {
// which would be a candidate for improved error reporting.
return token() === SyntaxKind.OpenBracketToken || isLiteralPropertyName();
case ParsingContext.ObjectLiteralMembers:
return token() === SyntaxKind.OpenBracketToken || token() === SyntaxKind.AsteriskToken || token() === SyntaxKind.DotDotDotToken || isLiteralPropertyName();
switch (token()) {
case SyntaxKind.OpenBracketToken:
case SyntaxKind.AsteriskToken:
case SyntaxKind.DotDotDotToken:
case SyntaxKind.DotToken: // Not an object literal member, but don't want to close the object (see `tests/cases/fourslash/completionsDotInObjectLiteral.ts`)
return true;
default:
return isLiteralPropertyName();
}
case ParsingContext.RestProperties:
return isLiteralPropertyName();
case ParsingContext.ObjectBindingElements:
@@ -1514,8 +1516,10 @@ namespace ts {
case ParsingContext.TypeParameters:
return isIdentifier();
case ParsingContext.ArrayLiteralMembers:
if (token() === SyntaxKind.CommaToken) {
return true;
switch (token()) {
case SyntaxKind.CommaToken:
case SyntaxKind.DotToken: // Not an array literal member, but don't want to close the array (see `tests/cases/fourslash/completionsDotInArrayLiteralInObjectLiteral.ts`)
return true;
}
// falls through
case ParsingContext.ArgumentExpressions:
@@ -1717,6 +1721,8 @@ namespace ts {
}
function currentNode(parsingContext: ParsingContext): Node | undefined {
// If we don't have a cursor or the parsing context isn't reusable, there's nothing to reuse.
//
// If there is an outstanding parse error that we've encountered, but not attached to
// some node, then we cannot get a node from the old source tree. This is because we
// want to mark the next node we encounter as being unusable.
@@ -1724,30 +1730,17 @@ namespace ts {
// Note: This may be too conservative. Perhaps we could reuse the node and set the bit
// on it (or its leftmost child) as having the error. For now though, being conservative
// is nice and likely won't ever affect perf.
if (parseErrorBeforeNextFinishedNode) {
return undefined;
}
if (!syntaxCursor) {
// if we don't have a cursor, we could never return a node from the old tree.
if (!syntaxCursor || !isReusableParsingContext(parsingContext) || parseErrorBeforeNextFinishedNode) {
return undefined;
}
const node = syntaxCursor.currentNode(scanner.getStartPos());
// Can't reuse a missing node.
if (nodeIsMissing(node)) {
return undefined;
}
// Can't reuse a node that intersected the change range.
if (node.intersectsChange) {
return undefined;
}
// Can't reuse a node that contains a parse error. This is necessary so that we
// produce the same set of errors again.
if (containsParseError(node)) {
if (nodeIsMissing(node) || node.intersectsChange || containsParseError(node)) {
return undefined;
}
@@ -1788,6 +1781,23 @@ namespace ts {
return node;
}
function isReusableParsingContext(parsingContext: ParsingContext): boolean {
switch (parsingContext) {
case ParsingContext.ClassMembers:
case ParsingContext.SwitchClauses:
case ParsingContext.SourceElements:
case ParsingContext.BlockStatements:
case ParsingContext.SwitchClauseStatements:
case ParsingContext.EnumMembers:
case ParsingContext.TypeMembers:
case ParsingContext.VariableDeclarations:
case ParsingContext.JSDocParameters:
case ParsingContext.Parameters:
return true;
}
return false;
}
function canReuseNode(node: Node, parsingContext: ParsingContext): boolean {
switch (parsingContext) {
case ParsingContext.ClassMembers:
@@ -1814,25 +1824,23 @@ namespace ts {
case ParsingContext.Parameters:
return isReusableParameter(node);
case ParsingContext.RestProperties:
return false;
// Any other lists we do not care about reusing nodes in. But feel free to add if
// you can do so safely. Danger areas involve nodes that may involve speculative
// parsing. If speculative parsing is involved with the node, then the range the
// parser reached while looking ahead might be in the edited range (see the example
// in canReuseVariableDeclaratorNode for a good case of this).
case ParsingContext.HeritageClauses:
// case ParsingContext.HeritageClauses:
// This would probably be safe to reuse. There is no speculative parsing with
// heritage clauses.
case ParsingContext.TypeParameters:
// case ParsingContext.TypeParameters:
// This would probably be safe to reuse. There is no speculative parsing with
// type parameters. Note that that's because type *parameters* only occur in
// unambiguous *type* contexts. While type *arguments* occur in very ambiguous
// *expression* contexts.
case ParsingContext.TupleElementTypes:
// case ParsingContext.TupleElementTypes:
// This would probably be safe to reuse. There is no speculative parsing with
// tuple types.
@@ -1841,28 +1849,28 @@ namespace ts {
// produced from speculative parsing a < as a type argument list), we only have
// the types because speculative parsing succeeded. Thus, the lookahead never
// went past the end of the list and rewound.
case ParsingContext.TypeArguments:
// case ParsingContext.TypeArguments:
// Note: these are almost certainly not safe to ever reuse. Expressions commonly
// need a large amount of lookahead, and we should not reuse them as they may
// have actually intersected the edit.
case ParsingContext.ArgumentExpressions:
// case ParsingContext.ArgumentExpressions:
// This is not safe to reuse for the same reason as the 'AssignmentExpression'
// cases. i.e. a property assignment may end with an expression, and thus might
// have lookahead far beyond it's old node.
case ParsingContext.ObjectLiteralMembers:
// case ParsingContext.ObjectLiteralMembers:
// This is probably not safe to reuse. There can be speculative parsing with
// type names in a heritage clause. There can be generic names in the type
// name list, and there can be left hand side expressions (which can have type
// arguments.)
case ParsingContext.HeritageClauseElement:
// case ParsingContext.HeritageClauseElement:
// Perhaps safe to reuse, but it's unlikely we'd see more than a dozen attributes
// on any given element. Same for children.
case ParsingContext.JsxAttributes:
case ParsingContext.JsxChildren:
// case ParsingContext.JsxAttributes:
// case ParsingContext.JsxChildren:
}
@@ -2241,8 +2249,7 @@ namespace ts {
function parseLiteralLikeNode(kind: SyntaxKind): LiteralExpression | LiteralLikeNode {
const node = <LiteralExpression>createNode(kind);
const text = scanner.getTokenValue();
node.text = text;
node.text = scanner.getTokenValue();
if (scanner.hasExtendedUnicodeEscape()) {
node.hasExtendedUnicodeEscape = true;
@@ -2886,8 +2893,9 @@ namespace ts {
return finishNode(node);
}
function nextTokenIsNumericLiteral() {
return nextToken() === SyntaxKind.NumericLiteral;
function nextTokenIsNumericOrBigIntLiteral() {
nextToken();
return token() === SyntaxKind.NumericLiteral || token() === SyntaxKind.BigIntLiteral;
}
function parseNonArrayType(): TypeNode {
@@ -2896,6 +2904,7 @@ namespace ts {
case SyntaxKind.UnknownKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.BigIntKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.UndefinedKeyword:
@@ -2916,11 +2925,12 @@ namespace ts {
case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.BigIntLiteral:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return parseLiteralTypeNode();
case SyntaxKind.MinusToken:
return lookAhead(nextTokenIsNumericLiteral) ? parseLiteralTypeNode(/*negative*/ true) : parseTypeReference();
return lookAhead(nextTokenIsNumericOrBigIntLiteral) ? parseLiteralTypeNode(/*negative*/ true) : parseTypeReference();
case SyntaxKind.VoidKeyword:
case SyntaxKind.NullKeyword:
return parseTokenNode<TypeNode>();
@@ -2954,6 +2964,7 @@ namespace ts {
case SyntaxKind.UnknownKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.BigIntKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.UniqueKeyword:
@@ -2971,6 +2982,7 @@ namespace ts {
case SyntaxKind.NewKeyword:
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.BigIntLiteral:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.ObjectKeyword:
@@ -2984,7 +2996,7 @@ namespace ts {
case SyntaxKind.FunctionKeyword:
return !inStartOfParameter;
case SyntaxKind.MinusToken:
return !inStartOfParameter && lookAhead(nextTokenIsNumericLiteral);
return !inStartOfParameter && lookAhead(nextTokenIsNumericOrBigIntLiteral);
case SyntaxKind.OpenParenToken:
// Only consider '(' the start of a type if followed by ')', '...', an identifier, a modifier,
// or something that starts a type. We don't want to consider things like '(1)' a type.
@@ -3209,6 +3221,7 @@ namespace ts {
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.NumericLiteral:
case SyntaxKind.BigIntLiteral:
case SyntaxKind.StringLiteral:
case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.TemplateHead:
@@ -4620,6 +4633,7 @@ namespace ts {
function parsePrimaryExpression(): PrimaryExpression {
switch (token()) {
case SyntaxKind.NumericLiteral:
case SyntaxKind.BigIntLiteral:
case SyntaxKind.StringLiteral:
case SyntaxKind.NoSubstitutionTemplateLiteral:
return parseLiteralNode();
@@ -4734,8 +4748,7 @@ namespace ts {
// CoverInitializedName[Yield] :
// IdentifierReference[?Yield] Initializer[In, ?Yield]
// this is necessary because ObjectLiteral productions are also used to cover grammar for ObjectAssignmentPattern
const isShorthandPropertyAssignment =
tokenIsIdentifier && (token() === SyntaxKind.CommaToken || token() === SyntaxKind.CloseBraceToken || token() === SyntaxKind.EqualsToken);
const isShorthandPropertyAssignment = tokenIsIdentifier && (token() !== SyntaxKind.ColonToken);
if (isShorthandPropertyAssignment) {
node.kind = SyntaxKind.ShorthandPropertyAssignment;
const equalsToken = parseOptionalToken(SyntaxKind.EqualsToken);
@@ -5136,7 +5149,7 @@ namespace ts {
function nextTokenIsIdentifierOrKeywordOrLiteralOnSameLine() {
nextToken();
return (tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.NumericLiteral || token() === SyntaxKind.StringLiteral) && !scanner.hasPrecedingLineBreak();
return (tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.NumericLiteral || token() === SyntaxKind.BigIntLiteral || token() === SyntaxKind.StringLiteral) && !scanner.hasPrecedingLineBreak();
}
function isDeclaration(): boolean {
@@ -6314,7 +6327,7 @@ namespace ts {
// Parses out a JSDoc type expression.
export function parseJSDocTypeExpression(mayOmitBraces?: boolean): JSDocTypeExpression {
const result = <JSDocTypeExpression>createNode(SyntaxKind.JSDocTypeExpression, scanner.getTokenPos());
const result = <JSDocTypeExpression>createNode(SyntaxKind.JSDocTypeExpression);
const hasBrace = (mayOmitBraces ? parseOptional : parseExpected)(SyntaxKind.OpenBraceToken);
result.type = doInsideOfContext(NodeFlags.JSDoc, parseJSDocType);
@@ -6447,13 +6460,6 @@ namespace ts {
indent += asterisk.length;
}
break;
case SyntaxKind.Identifier:
// Anything else is doc comment text. We just save it. Because it
// wasn't a tag, we can no longer parse a tag on this line until we hit the next
// line break.
pushComment(scanner.getTokenText());
state = JSDocState.SavingComments;
break;
case SyntaxKind.WhitespaceTrivia:
// only collect whitespace if we're already saving comments or have just crossed the comment indent margin
const whitespace = scanner.getTokenText();
@@ -6468,7 +6474,9 @@ namespace ts {
case SyntaxKind.EndOfFileToken:
break loop;
default:
// anything other than whitespace or asterisk at the beginning of the line starts the comment text
// Anything else is doc comment text. We just save it. Because it
// wasn't a tag, we can no longer parse a tag on this line until we hit the next
// line break.
state = JSDocState.SavingComments;
pushComment(scanner.getTokenText());
break;
@@ -6544,51 +6552,50 @@ namespace ts {
function parseTag(indent: number) {
Debug.assert(token() === SyntaxKind.AtToken);
const atToken = <AtToken>createNode(SyntaxKind.AtToken, scanner.getTokenPos());
atToken.end = scanner.getTextPos();
const start = scanner.getTokenPos();
nextJSDocToken();
const tagName = parseJSDocIdentifierName();
const tagName = parseJSDocIdentifierName(/*message*/ undefined);
skipWhitespaceOrAsterisk();
let tag: JSDocTag | undefined;
switch (tagName.escapedText) {
case "augments":
case "extends":
tag = parseAugmentsTag(atToken, tagName);
tag = parseAugmentsTag(start, tagName);
break;
case "class":
case "constructor":
tag = parseClassTag(atToken, tagName);
tag = parseClassTag(start, tagName);
break;
case "this":
tag = parseThisTag(atToken, tagName);
tag = parseThisTag(start, tagName);
break;
case "enum":
tag = parseEnumTag(atToken, tagName);
tag = parseEnumTag(start, tagName);
break;
case "arg":
case "argument":
case "param":
return parseParameterOrPropertyTag(atToken, tagName, PropertyLikeParse.Parameter, indent);
return parseParameterOrPropertyTag(start, tagName, PropertyLikeParse.Parameter, indent);
case "return":
case "returns":
tag = parseReturnTag(atToken, tagName);
tag = parseReturnTag(start, tagName);
break;
case "template":
tag = parseTemplateTag(atToken, tagName);
tag = parseTemplateTag(start, tagName);
break;
case "type":
tag = parseTypeTag(atToken, tagName);
tag = parseTypeTag(start, tagName);
break;
case "typedef":
tag = parseTypedefTag(atToken, tagName, indent);
tag = parseTypedefTag(start, tagName, indent);
break;
case "callback":
tag = parseCallbackTag(atToken, tagName, indent);
tag = parseCallbackTag(start, tagName, indent);
break;
default:
tag = parseUnknownTag(atToken, tagName);
tag = parseUnknownTag(start, tagName);
break;
}
@@ -6671,9 +6678,8 @@ namespace ts {
return comments.length === 0 ? undefined : comments.join("");
}
function parseUnknownTag(atToken: AtToken, tagName: Identifier) {
const result = <JSDocTag>createNode(SyntaxKind.JSDocTag, atToken.pos);
result.atToken = atToken;
function parseUnknownTag(start: number, tagName: Identifier) {
const result = <JSDocTag>createNode(SyntaxKind.JSDocTag, start);
result.tagName = tagName;
return finishNode(result);
}
@@ -6730,7 +6736,7 @@ namespace ts {
}
}
function parseParameterOrPropertyTag(atToken: AtToken, tagName: Identifier, target: PropertyLikeParse, indent: number | undefined): JSDocParameterTag | JSDocPropertyTag {
function parseParameterOrPropertyTag(start: number, tagName: Identifier, target: PropertyLikeParse, indent: number): JSDocParameterTag | JSDocPropertyTag {
let typeExpression = tryParseTypeExpression();
let isNameFirst = !typeExpression;
skipWhitespaceOrAsterisk();
@@ -6743,16 +6749,14 @@ namespace ts {
}
const result = target === PropertyLikeParse.Property ?
<JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos) :
<JSDocParameterTag>createNode(SyntaxKind.JSDocParameterTag, atToken.pos);
let comment: string | undefined;
if (indent !== undefined) comment = parseTagComments(indent + scanner.getStartPos() - atToken.pos);
const nestedTypeLiteral = target !== PropertyLikeParse.CallbackParameter && parseNestedTypeLiteral(typeExpression, name, target);
<JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, start) :
<JSDocParameterTag>createNode(SyntaxKind.JSDocParameterTag, start);
const comment = parseTagComments(indent + scanner.getStartPos() - start);
const nestedTypeLiteral = target !== PropertyLikeParse.CallbackParameter && parseNestedTypeLiteral(typeExpression, name, target, indent);
if (nestedTypeLiteral) {
typeExpression = nestedTypeLiteral;
isNameFirst = true;
}
result.atToken = atToken;
result.tagName = tagName;
result.typeExpression = typeExpression;
result.name = name;
@@ -6762,14 +6766,14 @@ namespace ts {
return finishNode(result);
}
function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression | undefined, name: EntityName, target: PropertyLikeParse) {
function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression | undefined, name: EntityName, target: PropertyLikeParse, indent: number) {
if (typeExpression && isObjectOrObjectArrayTypeReference(typeExpression.type)) {
const typeLiteralExpression = <JSDocTypeExpression>createNode(SyntaxKind.JSDocTypeExpression, scanner.getTokenPos());
let child: JSDocPropertyLikeTag | JSDocTypeTag | false;
let jsdocTypeLiteral: JSDocTypeLiteral;
const start = scanner.getStartPos();
let children: JSDocPropertyLikeTag[] | undefined;
while (child = tryParse(() => parseChildParameterOrPropertyTag(target, name))) {
while (child = tryParse(() => parseChildParameterOrPropertyTag(target, indent, name))) {
if (child.kind === SyntaxKind.JSDocParameterTag || child.kind === SyntaxKind.JSDocPropertyTag) {
children = append(children, child);
}
@@ -6786,33 +6790,30 @@ namespace ts {
}
}
function parseReturnTag(atToken: AtToken, tagName: Identifier): JSDocReturnTag {
function parseReturnTag(start: number, tagName: Identifier): JSDocReturnTag {
if (forEach(tags, t => t.kind === SyntaxKind.JSDocReturnTag)) {
parseErrorAt(tagName.pos, scanner.getTokenPos(), Diagnostics._0_tag_already_specified, tagName.escapedText);
}
const result = <JSDocReturnTag>createNode(SyntaxKind.JSDocReturnTag, atToken.pos);
result.atToken = atToken;
const result = <JSDocReturnTag>createNode(SyntaxKind.JSDocReturnTag, start);
result.tagName = tagName;
result.typeExpression = tryParseTypeExpression();
return finishNode(result);
}
function parseTypeTag(atToken: AtToken, tagName: Identifier): JSDocTypeTag {
function parseTypeTag(start: number, tagName: Identifier): JSDocTypeTag {
if (forEach(tags, t => t.kind === SyntaxKind.JSDocTypeTag)) {
parseErrorAt(tagName.pos, scanner.getTokenPos(), Diagnostics._0_tag_already_specified, tagName.escapedText);
}
const result = <JSDocTypeTag>createNode(SyntaxKind.JSDocTypeTag, atToken.pos);
result.atToken = atToken;
const result = <JSDocTypeTag>createNode(SyntaxKind.JSDocTypeTag, start);
result.tagName = tagName;
result.typeExpression = parseJSDocTypeExpression(/*mayOmitBraces*/ true);
return finishNode(result);
}
function parseAugmentsTag(atToken: AtToken, tagName: Identifier): JSDocAugmentsTag {
const result = <JSDocAugmentsTag>createNode(SyntaxKind.JSDocAugmentsTag, atToken.pos);
result.atToken = atToken;
function parseAugmentsTag(start: number, tagName: Identifier): JSDocAugmentsTag {
const result = <JSDocAugmentsTag>createNode(SyntaxKind.JSDocAugmentsTag, start);
result.tagName = tagName;
result.class = parseExpressionWithTypeArgumentsForAugments();
return finishNode(result);
@@ -6841,37 +6842,33 @@ namespace ts {
return node;
}
function parseClassTag(atToken: AtToken, tagName: Identifier): JSDocClassTag {
const tag = <JSDocClassTag>createNode(SyntaxKind.JSDocClassTag, atToken.pos);
tag.atToken = atToken;
function parseClassTag(start: number, tagName: Identifier): JSDocClassTag {
const tag = <JSDocClassTag>createNode(SyntaxKind.JSDocClassTag, start);
tag.tagName = tagName;
return finishNode(tag);
}
function parseThisTag(atToken: AtToken, tagName: Identifier): JSDocThisTag {
const tag = <JSDocThisTag>createNode(SyntaxKind.JSDocThisTag, atToken.pos);
tag.atToken = atToken;
function parseThisTag(start: number, tagName: Identifier): JSDocThisTag {
const tag = <JSDocThisTag>createNode(SyntaxKind.JSDocThisTag, start);
tag.tagName = tagName;
tag.typeExpression = parseJSDocTypeExpression(/*mayOmitBraces*/ true);
skipWhitespace();
return finishNode(tag);
}
function parseEnumTag(atToken: AtToken, tagName: Identifier): JSDocEnumTag {
const tag = <JSDocEnumTag>createNode(SyntaxKind.JSDocEnumTag, atToken.pos);
tag.atToken = atToken;
function parseEnumTag(start: number, tagName: Identifier): JSDocEnumTag {
const tag = <JSDocEnumTag>createNode(SyntaxKind.JSDocEnumTag, start);
tag.tagName = tagName;
tag.typeExpression = parseJSDocTypeExpression(/*mayOmitBraces*/ true);
skipWhitespace();
return finishNode(tag);
}
function parseTypedefTag(atToken: AtToken, tagName: Identifier, indent: number): JSDocTypedefTag {
function parseTypedefTag(start: number, tagName: Identifier, indent: number): JSDocTypedefTag {
const typeExpression = tryParseTypeExpression();
skipWhitespaceOrAsterisk();
const typedefTag = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag, atToken.pos);
typedefTag.atToken = atToken;
const typedefTag = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag, start);
typedefTag.tagName = tagName;
typedefTag.fullName = parseJSDocTypeNameWithNamespace();
typedefTag.name = getJSDocTypeAliasName(typedefTag.fullName);
@@ -6884,8 +6881,7 @@ namespace ts {
let child: JSDocTypeTag | JSDocPropertyTag | false;
let jsdocTypeLiteral: JSDocTypeLiteral | undefined;
let childTypeTag: JSDocTypeTag | undefined;
const start = atToken.pos;
while (child = tryParse(() => parseChildPropertyTag())) {
while (child = tryParse(() => parseChildPropertyTag(indent))) {
if (!jsdocTypeLiteral) {
jsdocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, start);
}
@@ -6938,9 +6934,8 @@ namespace ts {
return typeNameOrNamespaceName;
}
function parseCallbackTag(atToken: AtToken, tagName: Identifier, indent: number): JSDocCallbackTag {
const callbackTag = createNode(SyntaxKind.JSDocCallbackTag, atToken.pos) as JSDocCallbackTag;
callbackTag.atToken = atToken;
function parseCallbackTag(start: number, tagName: Identifier, indent: number): JSDocCallbackTag {
const callbackTag = createNode(SyntaxKind.JSDocCallbackTag, start) as JSDocCallbackTag;
callbackTag.tagName = tagName;
callbackTag.fullName = parseJSDocTypeNameWithNamespace();
callbackTag.name = getJSDocTypeAliasName(callbackTag.fullName);
@@ -6948,10 +6943,9 @@ namespace ts {
callbackTag.comment = parseTagComments(indent);
let child: JSDocParameterTag | false;
const start = scanner.getStartPos();
const jsdocSignature = createNode(SyntaxKind.JSDocSignature, start) as JSDocSignature;
jsdocSignature.parameters = [];
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.CallbackParameter) as JSDocParameterTag)) {
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.CallbackParameter, indent) as JSDocParameterTag)) {
jsdocSignature.parameters = append(jsdocSignature.parameters as MutableNodeArray<JSDocParameterTag>, child);
}
const returnTag = tryParse(() => {
@@ -6994,18 +6988,18 @@ namespace ts {
return a.escapedText === b.escapedText;
}
function parseChildPropertyTag() {
return parseChildParameterOrPropertyTag(PropertyLikeParse.Property) as JSDocTypeTag | JSDocPropertyTag | false;
function parseChildPropertyTag(indent: number) {
return parseChildParameterOrPropertyTag(PropertyLikeParse.Property, indent) as JSDocTypeTag | JSDocPropertyTag | false;
}
function parseChildParameterOrPropertyTag(target: PropertyLikeParse, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
function parseChildParameterOrPropertyTag(target: PropertyLikeParse, indent: number, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
let canParseTag = true;
let seenAsterisk = false;
while (true) {
switch (nextJSDocToken()) {
case SyntaxKind.AtToken:
if (canParseTag) {
const child = tryParseChildTag(target);
const child = tryParseChildTag(target, indent);
if (child && (child.kind === SyntaxKind.JSDocParameterTag || child.kind === SyntaxKind.JSDocPropertyTag) &&
target !== PropertyLikeParse.CallbackParameter &&
name && (ts.isIdentifier(child.name) || !escapedTextsEqual(name, child.name.left))) {
@@ -7034,10 +7028,9 @@ namespace ts {
}
}
function tryParseChildTag(target: PropertyLikeParse): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
function tryParseChildTag(target: PropertyLikeParse, indent: number): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
Debug.assert(token() === SyntaxKind.AtToken);
const atToken = <AtToken>createNode(SyntaxKind.AtToken);
atToken.end = scanner.getTextPos();
const start = scanner.getStartPos();
nextJSDocToken();
const tagName = parseJSDocIdentifierName();
@@ -7045,7 +7038,7 @@ namespace ts {
let t: PropertyLikeParse;
switch (tagName.escapedText) {
case "type":
return target === PropertyLikeParse.Property && parseTypeTag(atToken, tagName);
return target === PropertyLikeParse.Property && parseTypeTag(start, tagName);
case "prop":
case "property":
t = PropertyLikeParse.Property;
@@ -7061,12 +7054,10 @@ namespace ts {
if (!(target & t)) {
return false;
}
const tag = parseParameterOrPropertyTag(atToken, tagName, target, /*indent*/ undefined);
tag.comment = parseTagComments(tag.end - tag.pos);
return tag;
return parseParameterOrPropertyTag(start, tagName, target, indent);
}
function parseTemplateTag(atToken: AtToken, tagName: Identifier): JSDocTemplateTag {
function parseTemplateTag(start: number, tagName: Identifier): JSDocTemplateTag {
// the template tag looks like '@template {Constraint} T,U,V'
let constraint: JSDocTypeExpression | undefined;
if (token() === SyntaxKind.OpenBraceToken) {
@@ -7084,8 +7075,7 @@ namespace ts {
typeParameters.push(typeParameter);
} while (parseOptionalJsdoc(SyntaxKind.CommaToken));
const result = <JSDocTemplateTag>createNode(SyntaxKind.JSDocTemplateTag, atToken.pos);
result.atToken = atToken;
const result = <JSDocTemplateTag>createNode(SyntaxKind.JSDocTemplateTag, start);
result.tagName = tagName;
result.constraint = constraint;
result.typeParameters = createNodeArray(typeParameters, typeParametersPos);
@@ -7733,7 +7723,7 @@ namespace ts {
/*@internal*/
export function processCommentPragmas(context: PragmaContext, sourceText: string): void {
const triviaScanner = createScanner(context.languageVersion, /*skipTrivia*/ false, LanguageVariant.Standard, sourceText);
const pragmas: PragmaPsuedoMapEntry[] = [];
const pragmas: PragmaPseudoMapEntry[] = [];
// Keep scanning all the leading trivia in the file until we get to something that
// isn't trivia. Any single line comment will be analyzed to see if it is a
@@ -7788,19 +7778,20 @@ namespace ts {
const referencedFiles = context.referencedFiles;
const typeReferenceDirectives = context.typeReferenceDirectives;
const libReferenceDirectives = context.libReferenceDirectives;
forEach(toArray(entryOrList), (arg: PragmaPsuedoMap["reference"]) => {
forEach(toArray(entryOrList), (arg: PragmaPseudoMap["reference"]) => {
// TODO: GH#18217
const { types, lib, path } = arg!.arguments;
if (arg!.arguments["no-default-lib"]) {
context.hasNoDefaultLib = true;
}
else if (arg!.arguments.types) {
typeReferenceDirectives.push({ pos: arg!.arguments.types!.pos, end: arg!.arguments.types!.end, fileName: arg!.arguments.types!.value });
else if (types) {
typeReferenceDirectives.push({ pos: types.pos, end: types.end, fileName: types.value });
}
else if (arg!.arguments.lib) {
libReferenceDirectives.push({ pos: arg!.arguments.lib!.pos, end: arg!.arguments.lib!.end, fileName: arg!.arguments.lib!.value });
else if (lib) {
libReferenceDirectives.push({ pos: lib.pos, end: lib.end, fileName: lib.value });
}
else if (arg!.arguments.path) {
referencedFiles.push({ pos: arg!.arguments.path!.pos, end: arg!.arguments.path!.end, fileName: arg!.arguments.path!.value });
else if (path) {
referencedFiles.push({ pos: path.pos, end: path.end, fileName: path.value });
}
else {
reportDiagnostic(arg!.range.pos, arg!.range.end - arg!.range.pos, Diagnostics.Invalid_reference_directive_syntax);
@@ -7811,7 +7802,7 @@ namespace ts {
case "amd-dependency": {
context.amdDependencies = map(
toArray(entryOrList),
(x: PragmaPsuedoMap["amd-dependency"]) => ({ name: x!.arguments.name!, path: x!.arguments.path })); // TODO: GH#18217
(x: PragmaPseudoMap["amd-dependency"]) => ({ name: x!.arguments.name!, path: x!.arguments.path })); // TODO: GH#18217
break;
}
case "amd-module": {
@@ -7821,11 +7812,11 @@ namespace ts {
// TODO: It's probably fine to issue this diagnostic on all instances of the pragma
reportDiagnostic(entry!.range.pos, entry!.range.end - entry!.range.pos, Diagnostics.An_AMD_module_cannot_have_multiple_name_assignments);
}
context.moduleName = (entry as PragmaPsuedoMap["amd-module"])!.arguments.name;
context.moduleName = (entry as PragmaPseudoMap["amd-module"])!.arguments.name;
}
}
else {
context.moduleName = (entryOrList as PragmaPsuedoMap["amd-module"])!.arguments.name;
context.moduleName = (entryOrList as PragmaPseudoMap["amd-module"])!.arguments.name;
}
break;
}
@@ -7861,10 +7852,10 @@ namespace ts {
const tripleSlashXMLCommentStartRegEx = /^\/\/\/\s*<(\S+)\s.*?\/>/im;
const singleLinePragmaRegEx = /^\/\/\/?\s*@(\S+)\s*(.*)\s*$/im;
function extractPragmas(pragmas: PragmaPsuedoMapEntry[], range: CommentRange, text: string) {
function extractPragmas(pragmas: PragmaPseudoMapEntry[], range: CommentRange, text: string) {
const tripleSlash = range.kind === SyntaxKind.SingleLineCommentTrivia && tripleSlashXMLCommentStartRegEx.exec(text);
if (tripleSlash) {
const name = tripleSlash[1].toLowerCase() as keyof PragmaPsuedoMap; // Technically unsafe cast, but we do it so the below check to make it safe typechecks
const name = tripleSlash[1].toLowerCase() as keyof PragmaPseudoMap; // Technically unsafe cast, but we do it so the below check to make it safe typechecks
const pragma = commentPragmas[name] as PragmaDefinition;
if (!pragma || !(pragma.kind! & PragmaKindFlags.TripleSlashXML)) {
return;
@@ -7891,10 +7882,10 @@ namespace ts {
}
}
}
pragmas.push({ name, args: { arguments: argument, range } } as PragmaPsuedoMapEntry);
pragmas.push({ name, args: { arguments: argument, range } } as PragmaPseudoMapEntry);
}
else {
pragmas.push({ name, args: { arguments: {}, range } } as PragmaPsuedoMapEntry);
pragmas.push({ name, args: { arguments: {}, range } } as PragmaPseudoMapEntry);
}
return;
}
@@ -7913,9 +7904,9 @@ namespace ts {
}
}
function addPragmaForMatch(pragmas: PragmaPsuedoMapEntry[], range: CommentRange, kind: PragmaKindFlags, match: RegExpExecArray) {
function addPragmaForMatch(pragmas: PragmaPseudoMapEntry[], range: CommentRange, kind: PragmaKindFlags, match: RegExpExecArray) {
if (!match) return;
const name = match[1].toLowerCase() as keyof PragmaPsuedoMap; // Technically unsafe cast, but we do it so they below check to make it safe typechecks
const name = match[1].toLowerCase() as keyof PragmaPseudoMap; // Technically unsafe cast, but we do it so they below check to make it safe typechecks
const pragma = commentPragmas[name] as PragmaDefinition;
if (!pragma || !(pragma.kind! & kind)) {
return;
@@ -7923,7 +7914,7 @@ namespace ts {
const args = match[2]; // Split on spaces and match up positionally with definition
const argument = getNamedPragmaArguments(pragma, args);
if (argument === "fail") return; // Missing required argument, fail to parse it
pragmas.push({ name, args: { arguments: argument, range } } as PragmaPsuedoMapEntry);
pragmas.push({ name, args: { arguments: argument, range } } as PragmaPseudoMapEntry);
return;
}
+35
View File
@@ -19,6 +19,41 @@ namespace ts.performance {
let marks: Map<number>;
let measures: Map<number>;
export interface Timer {
enter(): void;
exit(): void;
}
export function createTimerIf(condition: boolean, measureName: string, startMarkName: string, endMarkName: string) {
return condition ? createTimer(measureName, startMarkName, endMarkName) : nullTimer;
}
export function createTimer(measureName: string, startMarkName: string, endMarkName: string): Timer {
let enterCount = 0;
return {
enter,
exit
};
function enter() {
if (++enterCount === 1) {
mark(startMarkName);
}
}
function exit() {
if (--enterCount === 0) {
mark(endMarkName);
measure(measureName, startMarkName, endMarkName);
}
else if (enterCount < 0) {
Debug.fail("enter/exit count does not match.");
}
}
}
export const nullTimer: Timer = { enter: noop, exit: noop };
/**
* Marks a performance event.
*
+595 -273
View File
File diff suppressed because it is too large Load Diff
+77 -46
View File
@@ -5,12 +5,13 @@ namespace ts {
startRecordingFilesWithChangedResolutions(): void;
finishRecordingFilesWithChangedResolutions(): Path[] | undefined;
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined): ResolvedModuleFull[];
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference): (ResolvedModuleFull | undefined)[];
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): CachedResolvedModuleWithFailedLookupLocations | undefined;
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): (ResolvedTypeReferenceDirective | undefined)[];
invalidateResolutionOfFile(filePath: Path): void;
removeResolutionsOfFile(filePath: Path): void;
removeResolutionsFromProjectReferenceRedirects(filePath: Path): void;
setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: Map<ReadonlyArray<string>>): void;
createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution;
@@ -68,7 +69,10 @@ namespace ts {
dir: string;
dirPath: Path;
nonRecursive?: boolean;
ignore?: true;
}
export function isPathInNodeModulesStartingWithDot(path: Path) {
return stringContains(path, "/node_modules/.");
}
export const maxNumberOfFilesToIterateForInvalidation = 256;
@@ -79,7 +83,7 @@ namespace ts {
export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string | undefined, logChangesWhenResolvingModule: boolean): ResolutionCache {
let filesWithChangedSetOfUnresolvedImports: Path[] | undefined;
let filesWithInvalidatedResolutions: Map<true> | undefined;
let filesWithInvalidatedNonRelativeUnresolvedImports: Map<ReadonlyArray<string>> | undefined;
let filesWithInvalidatedNonRelativeUnresolvedImports: ReadonlyMap<ReadonlyArray<string>> | undefined;
let allFilesHaveInvalidatedResolution = false;
const nonRelativeExternalModuleResolutions = createMultiMap<ResolutionWithFailedLookupLocations>();
@@ -90,17 +94,17 @@ namespace ts {
// The key in the map is source file's path.
// The values are Map of resolutions with key being name lookedup.
const resolvedModuleNames = createMap<Map<CachedResolvedModuleWithFailedLookupLocations>>();
const perDirectoryResolvedModuleNames = createMap<Map<CachedResolvedModuleWithFailedLookupLocations>>();
const nonRelaticeModuleNameCache = createMap<PerModuleNameCache>();
const perDirectoryResolvedModuleNames: CacheWithRedirects<Map<CachedResolvedModuleWithFailedLookupLocations>> = createCacheWithRedirects();
const nonRelativeModuleNameCache: CacheWithRedirects<PerModuleNameCache> = createCacheWithRedirects();
const moduleResolutionCache = createModuleResolutionCacheWithMaps(
perDirectoryResolvedModuleNames,
nonRelaticeModuleNameCache,
nonRelativeModuleNameCache,
getCurrentDirectory(),
resolutionHost.getCanonicalFileName
);
const resolvedTypeReferenceDirectives = createMap<Map<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
const perDirectoryResolvedTypeReferenceDirectives = createMap<Map<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects<Map<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>> = createCacheWithRedirects();
/**
* These are the extensions that failed lookup files will have by default,
@@ -128,6 +132,7 @@ namespace ts {
resolveModuleNames,
getResolvedModuleWithFailedLookupLocationsFromCache,
resolveTypeReferenceDirectives,
removeResolutionsFromProjectReferenceRedirects,
removeResolutionsOfFile,
invalidateResolutionOfFile,
setFilesWithInvalidatedNonRelativeUnresolvedImports,
@@ -199,7 +204,7 @@ namespace ts {
function clearPerDirectoryResolutions() {
perDirectoryResolvedModuleNames.clear();
nonRelaticeModuleNameCache.clear();
nonRelativeModuleNameCache.clear();
perDirectoryResolvedTypeReferenceDirectives.clear();
nonRelativeExternalModuleResolutions.forEach(watchFailedLookupLocationOfNonRelativeModuleResolutions);
nonRelativeExternalModuleResolutions.clear();
@@ -217,8 +222,8 @@ namespace ts {
});
}
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): CachedResolvedModuleWithFailedLookupLocations {
const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host, moduleResolutionCache);
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference): CachedResolvedModuleWithFailedLookupLocations {
const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host, moduleResolutionCache, redirectedReference);
// return result immediately only if global cache support is not enabled or if it is .ts, .tsx or .d.ts
if (!resolutionHost.getGlobalCache) {
return primaryResult;
@@ -226,7 +231,7 @@ namespace ts {
// otherwise try to load typings from @types
const globalCache = resolutionHost.getGlobalCache();
if (globalCache !== undefined && !isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTypeScript(primaryResult.resolvedModule.extension))) {
if (globalCache !== undefined && !isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTS(primaryResult.resolvedModule.extension))) {
// create different collection of failed lookup locations for second pass
// if it will fail and we've already found something during the first pass - we don't want to pollute its results
const { resolvedModule, failedLookupLocations } = loadModuleFromGlobalCache(moduleName, resolutionHost.projectName, compilerOptions, host, globalCache);
@@ -240,41 +245,52 @@ namespace ts {
}
function resolveNamesWithLocalCache<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>(
names: string[],
names: ReadonlyArray<string>,
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
cache: Map<Map<T>>,
perDirectoryCache: Map<Map<T>>,
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => T,
perDirectoryCacheWithRedirects: CacheWithRedirects<Map<T>>,
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference) => T,
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>,
reusedNames: string[] | undefined,
logChanges: boolean): R[] {
shouldRetryResolution: (t: T) => boolean,
reusedNames: ReadonlyArray<string> | undefined,
logChanges: boolean): (R | undefined)[] {
const path = resolutionHost.toPath(containingFile);
const resolutionsInFile = cache.get(path) || cache.set(path, createMap()).get(path)!;
const dirPath = getDirectoryPath(path);
const perDirectoryCache = perDirectoryCacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference);
let perDirectoryResolution = perDirectoryCache.get(dirPath);
if (!perDirectoryResolution) {
perDirectoryResolution = createMap();
perDirectoryCache.set(dirPath, perDirectoryResolution);
}
const resolvedModules: R[] = [];
const resolvedModules: (R | undefined)[] = [];
const compilerOptions = resolutionHost.getCompilationSettings();
const hasInvalidatedNonRelativeUnresolvedImport = logChanges && isFileWithInvalidatedNonRelativeUnresolvedImports(path);
// All the resolutions in this file are invalidated if this file wasnt resolved using same redirect
const program = resolutionHost.getCurrentProgram();
const oldRedirect = program && program.getResolvedProjectReferenceToRedirect(containingFile);
const unmatchedRedirects = oldRedirect ?
!redirectedReference || redirectedReference.sourceFile.path !== oldRedirect.sourceFile.path :
!!redirectedReference;
const seenNamesInFile = createMap<true>();
for (const name of names) {
let resolution = resolutionsInFile.get(name);
// Resolution is valid if it is present and not invalidated
if (!seenNamesInFile.has(name) &&
allFilesHaveInvalidatedResolution || !resolution || resolution.isInvalidated ||
allFilesHaveInvalidatedResolution || unmatchedRedirects || !resolution || resolution.isInvalidated ||
// If the name is unresolved import that was invalidated, recalculate
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && !getResolutionWithResolvedFileName(resolution))) {
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && shouldRetryResolution(resolution))) {
const existingResolution = resolution;
const resolutionInDirectory = perDirectoryResolution.get(name);
if (resolutionInDirectory) {
resolution = resolutionInDirectory;
}
else {
resolution = loader(name, containingFile, compilerOptions, resolutionHost);
resolution = loader(name, containingFile, compilerOptions, resolutionHost, redirectedReference);
perDirectoryResolution.set(name, resolution);
}
resolutionsInFile.set(name, resolution);
@@ -291,7 +307,7 @@ namespace ts {
}
Debug.assert(resolution !== undefined && !resolution.isInvalidated);
seenNamesInFile.set(name, true);
resolvedModules.push(getResolutionWithResolvedFileName(resolution)!); // TODO: GH#18217
resolvedModules.push(getResolutionWithResolvedFileName(resolution));
}
// Stop watching and remove the unused name
@@ -323,20 +339,22 @@ namespace ts {
}
}
function resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] {
function resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): (ResolvedTypeReferenceDirective | undefined)[] {
return resolveNamesWithLocalCache<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations, ResolvedTypeReferenceDirective>(
typeDirectiveNames, containingFile,
typeDirectiveNames, containingFile, redirectedReference,
resolvedTypeReferenceDirectives, perDirectoryResolvedTypeReferenceDirectives,
resolveTypeReferenceDirective, getResolvedTypeReferenceDirective,
/*shouldRetryResolution*/ resolution => resolution.resolvedTypeReferenceDirective === undefined,
/*reusedNames*/ undefined, /*logChanges*/ false
);
}
function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined): ResolvedModuleFull[] {
function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference): (ResolvedModuleFull | undefined)[] {
return resolveNamesWithLocalCache<CachedResolvedModuleWithFailedLookupLocations, ResolvedModuleFull>(
moduleNames, containingFile,
moduleNames, containingFile, redirectedReference,
resolvedModuleNames, perDirectoryResolvedModuleNames,
resolveModuleName, getResolvedModule,
/*shouldRetryResolution*/ resolution => !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension),
reusedNames, logChangesWhenResolvingModule
);
}
@@ -389,16 +407,10 @@ namespace ts {
return true;
}
function filterFSRootDirectoriesToWatch(watchPath: DirectoryOfFailedLookupWatch, dirPath: Path): DirectoryOfFailedLookupWatch {
if (!canWatchDirectory(dirPath)) {
watchPath.ignore = true;
}
return watchPath;
}
function getDirectoryToWatchFailedLookupLocation(failedLookupLocation: string, failedLookupLocationPath: Path): DirectoryOfFailedLookupWatch {
function getDirectoryToWatchFailedLookupLocation(failedLookupLocation: string, failedLookupLocationPath: Path): DirectoryOfFailedLookupWatch | undefined {
if (isInDirectoryPath(rootPath, failedLookupLocationPath)) {
failedLookupLocation = isRootedDiskPath(failedLookupLocation) ? failedLookupLocation : getNormalizedAbsolutePath(failedLookupLocation, getCurrentDirectory());
// Ensure failed look up is normalized path
failedLookupLocation = isRootedDiskPath(failedLookupLocation) ? normalizePath(failedLookupLocation) : getNormalizedAbsolutePath(failedLookupLocation, getCurrentDirectory());
Debug.assert(failedLookupLocation.length === failedLookupLocationPath.length, `FailedLookup: ${failedLookupLocation} failedLookupLocationPath: ${failedLookupLocationPath}`); // tslint:disable-line
const subDirectoryInRoot = failedLookupLocationPath.indexOf(directorySeparator, rootPath.length + 1);
if (subDirectoryInRoot !== -1) {
@@ -417,16 +429,16 @@ namespace ts {
);
}
function getDirectoryToWatchFromFailedLookupLocationDirectory(dir: string, dirPath: Path) {
function getDirectoryToWatchFromFailedLookupLocationDirectory(dir: string, dirPath: Path): DirectoryOfFailedLookupWatch | undefined {
// If directory path contains node module, get the most parent node_modules directory for watching
while (stringContains(dirPath, nodeModulesPathPart)) {
while (pathContainsNodeModules(dirPath)) {
dir = getDirectoryPath(dir);
dirPath = getDirectoryPath(dirPath);
}
// If the directory is node_modules use it to watch, always watch it recursively
if (isNodeModulesDirectory(dirPath)) {
return filterFSRootDirectoriesToWatch({ dir, dirPath }, getDirectoryPath(dirPath));
return canWatchDirectory(getDirectoryPath(dirPath)) ? { dir, dirPath } : undefined;
}
let nonRecursive = true;
@@ -446,7 +458,7 @@ namespace ts {
}
}
return filterFSRootDirectoriesToWatch({ dir: subDirectory || dir, dirPath: subDirectoryPath || dirPath, nonRecursive }, dirPath);
return canWatchDirectory(dirPath) ? { dir: subDirectory || dir, dirPath: subDirectoryPath || dirPath, nonRecursive } : undefined;
}
function isPathWithDefaultFailedLookupExtension(path: Path) {
@@ -478,8 +490,9 @@ namespace ts {
let setAtRoot = false;
for (const failedLookupLocation of failedLookupLocations) {
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
const { dir, dirPath, nonRecursive, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
if (!ignore) {
const toWatch = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
if (toWatch) {
const { dir, dirPath, nonRecursive } = toWatch;
// If the failed lookup location path is not one of the supported extensions,
// store it in the custom path
if (!isPathWithDefaultFailedLookupExtension(failedLookupLocationPath)) {
@@ -538,8 +551,9 @@ namespace ts {
let removeAtRoot = false;
for (const failedLookupLocation of failedLookupLocations) {
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
const { dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
if (!ignore) {
const toWatch = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
if (toWatch) {
const { dirPath } = toWatch;
const refCount = customFailedLookupPaths.get(failedLookupLocationPath);
if (refCount) {
if (refCount === 1) {
@@ -593,6 +607,20 @@ namespace ts {
}
}
function removeResolutionsFromProjectReferenceRedirects(filePath: Path) {
if (!fileExtensionIs(filePath, Extension.Json)) { return; }
const program = resolutionHost.getCurrentProgram();
if (!program) { return; }
// If this file is input file for the referenced project, get it
const resolvedProjectReference = program.getResolvedProjectReferenceByPath(filePath);
if (!resolvedProjectReference) { return; }
// filePath is for the projectReference and the containing file is from this project reference, invalidate the resolution
resolvedProjectReference.commandLine.fileNames.forEach(f => removeResolutionsOfFile(resolutionHost.toPath(f)));
}
function removeResolutionsOfFile(filePath: Path) {
removeResolutionsOfFileFromCache(resolvedModuleNames, filePath);
removeResolutionsOfFileFromCache(resolvedTypeReferenceDirectives, filePath);
@@ -654,7 +682,7 @@ namespace ts {
);
}
function setFilesWithInvalidatedNonRelativeUnresolvedImports(filesMap: Map<ReadonlyArray<string>>) {
function setFilesWithInvalidatedNonRelativeUnresolvedImports(filesMap: ReadonlyMap<ReadonlyArray<string>>) {
Debug.assert(filesWithInvalidatedNonRelativeUnresolvedImports === filesMap || filesWithInvalidatedNonRelativeUnresolvedImports === undefined);
filesWithInvalidatedNonRelativeUnresolvedImports = filesMap;
}
@@ -667,6 +695,9 @@ namespace ts {
isChangedFailedLookupLocation = location => isInDirectoryPath(fileOrDirectoryPath, resolutionHost.toPath(location));
}
else {
// If something to do with folder/file starting with "." in node_modules folder, skip it
if (isPathInNodeModulesStartingWithDot(fileOrDirectoryPath)) return false;
// Some file or directory in the watching directory is created
// Return early if it does not have any of the watching extension or not the custom failed lookup path
const dirOfFileOrDirectory = getDirectoryPath(fileOrDirectoryPath);
@@ -711,8 +742,8 @@ namespace ts {
if (isInDirectoryPath(rootPath, typeRootPath)) {
return rootPath;
}
const { dirPath, ignore } = getDirectoryToWatchFromFailedLookupLocationDirectory(typeRoot, typeRootPath);
return !ignore && directoryWatchesOfFailedLookups.has(dirPath) ? dirPath : undefined;
const toWatch = getDirectoryToWatchFromFailedLookupLocationDirectory(typeRoot, typeRootPath);
return toWatch && directoryWatchesOfFailedLookups.has(toWatch.dirPath) ? toWatch.dirPath : undefined;
}
function createTypeRootsWatch(typeRootPath: Path, typeRoot: string): FileWatcher {
+212 -142
View File
@@ -61,81 +61,88 @@ namespace ts {
tryScan<T>(callback: () => T): T;
}
const textToToken = createMapFromTemplate({
"abstract": SyntaxKind.AbstractKeyword,
"any": SyntaxKind.AnyKeyword,
"as": SyntaxKind.AsKeyword,
"boolean": SyntaxKind.BooleanKeyword,
"break": SyntaxKind.BreakKeyword,
"case": SyntaxKind.CaseKeyword,
"catch": SyntaxKind.CatchKeyword,
"class": SyntaxKind.ClassKeyword,
"continue": SyntaxKind.ContinueKeyword,
"const": SyntaxKind.ConstKeyword,
"constructor": SyntaxKind.ConstructorKeyword,
"debugger": SyntaxKind.DebuggerKeyword,
"declare": SyntaxKind.DeclareKeyword,
"default": SyntaxKind.DefaultKeyword,
"delete": SyntaxKind.DeleteKeyword,
"do": SyntaxKind.DoKeyword,
"else": SyntaxKind.ElseKeyword,
"enum": SyntaxKind.EnumKeyword,
"export": SyntaxKind.ExportKeyword,
"extends": SyntaxKind.ExtendsKeyword,
"false": SyntaxKind.FalseKeyword,
"finally": SyntaxKind.FinallyKeyword,
"for": SyntaxKind.ForKeyword,
"from": SyntaxKind.FromKeyword,
"function": SyntaxKind.FunctionKeyword,
"get": SyntaxKind.GetKeyword,
"if": SyntaxKind.IfKeyword,
"implements": SyntaxKind.ImplementsKeyword,
"import": SyntaxKind.ImportKeyword,
"in": SyntaxKind.InKeyword,
"infer": SyntaxKind.InferKeyword,
"instanceof": SyntaxKind.InstanceOfKeyword,
"interface": SyntaxKind.InterfaceKeyword,
"is": SyntaxKind.IsKeyword,
"keyof": SyntaxKind.KeyOfKeyword,
"let": SyntaxKind.LetKeyword,
"module": SyntaxKind.ModuleKeyword,
"namespace": SyntaxKind.NamespaceKeyword,
"never": SyntaxKind.NeverKeyword,
"new": SyntaxKind.NewKeyword,
"null": SyntaxKind.NullKeyword,
"number": SyntaxKind.NumberKeyword,
"object": SyntaxKind.ObjectKeyword,
"package": SyntaxKind.PackageKeyword,
"private": SyntaxKind.PrivateKeyword,
"protected": SyntaxKind.ProtectedKeyword,
"public": SyntaxKind.PublicKeyword,
"readonly": SyntaxKind.ReadonlyKeyword,
"require": SyntaxKind.RequireKeyword,
"global": SyntaxKind.GlobalKeyword,
"return": SyntaxKind.ReturnKeyword,
"set": SyntaxKind.SetKeyword,
"static": SyntaxKind.StaticKeyword,
"string": SyntaxKind.StringKeyword,
"super": SyntaxKind.SuperKeyword,
"switch": SyntaxKind.SwitchKeyword,
"symbol": SyntaxKind.SymbolKeyword,
"this": SyntaxKind.ThisKeyword,
"throw": SyntaxKind.ThrowKeyword,
"true": SyntaxKind.TrueKeyword,
"try": SyntaxKind.TryKeyword,
"type": SyntaxKind.TypeKeyword,
"typeof": SyntaxKind.TypeOfKeyword,
"undefined": SyntaxKind.UndefinedKeyword,
"unique": SyntaxKind.UniqueKeyword,
"unknown": SyntaxKind.UnknownKeyword,
"var": SyntaxKind.VarKeyword,
"void": SyntaxKind.VoidKeyword,
"while": SyntaxKind.WhileKeyword,
"with": SyntaxKind.WithKeyword,
"yield": SyntaxKind.YieldKeyword,
"async": SyntaxKind.AsyncKeyword,
"await": SyntaxKind.AwaitKeyword,
"of": SyntaxKind.OfKeyword,
const textToKeywordObj: MapLike<KeywordSyntaxKind> = {
abstract: SyntaxKind.AbstractKeyword,
any: SyntaxKind.AnyKeyword,
as: SyntaxKind.AsKeyword,
bigint: SyntaxKind.BigIntKeyword,
boolean: SyntaxKind.BooleanKeyword,
break: SyntaxKind.BreakKeyword,
case: SyntaxKind.CaseKeyword,
catch: SyntaxKind.CatchKeyword,
class: SyntaxKind.ClassKeyword,
continue: SyntaxKind.ContinueKeyword,
const: SyntaxKind.ConstKeyword,
["" + "constructor"]: SyntaxKind.ConstructorKeyword,
debugger: SyntaxKind.DebuggerKeyword,
declare: SyntaxKind.DeclareKeyword,
default: SyntaxKind.DefaultKeyword,
delete: SyntaxKind.DeleteKeyword,
do: SyntaxKind.DoKeyword,
else: SyntaxKind.ElseKeyword,
enum: SyntaxKind.EnumKeyword,
export: SyntaxKind.ExportKeyword,
extends: SyntaxKind.ExtendsKeyword,
false: SyntaxKind.FalseKeyword,
finally: SyntaxKind.FinallyKeyword,
for: SyntaxKind.ForKeyword,
from: SyntaxKind.FromKeyword,
function: SyntaxKind.FunctionKeyword,
get: SyntaxKind.GetKeyword,
if: SyntaxKind.IfKeyword,
implements: SyntaxKind.ImplementsKeyword,
import: SyntaxKind.ImportKeyword,
in: SyntaxKind.InKeyword,
infer: SyntaxKind.InferKeyword,
instanceof: SyntaxKind.InstanceOfKeyword,
interface: SyntaxKind.InterfaceKeyword,
is: SyntaxKind.IsKeyword,
keyof: SyntaxKind.KeyOfKeyword,
let: SyntaxKind.LetKeyword,
module: SyntaxKind.ModuleKeyword,
namespace: SyntaxKind.NamespaceKeyword,
never: SyntaxKind.NeverKeyword,
new: SyntaxKind.NewKeyword,
null: SyntaxKind.NullKeyword,
number: SyntaxKind.NumberKeyword,
object: SyntaxKind.ObjectKeyword,
package: SyntaxKind.PackageKeyword,
private: SyntaxKind.PrivateKeyword,
protected: SyntaxKind.ProtectedKeyword,
public: SyntaxKind.PublicKeyword,
readonly: SyntaxKind.ReadonlyKeyword,
require: SyntaxKind.RequireKeyword,
global: SyntaxKind.GlobalKeyword,
return: SyntaxKind.ReturnKeyword,
set: SyntaxKind.SetKeyword,
static: SyntaxKind.StaticKeyword,
string: SyntaxKind.StringKeyword,
super: SyntaxKind.SuperKeyword,
switch: SyntaxKind.SwitchKeyword,
symbol: SyntaxKind.SymbolKeyword,
this: SyntaxKind.ThisKeyword,
throw: SyntaxKind.ThrowKeyword,
true: SyntaxKind.TrueKeyword,
try: SyntaxKind.TryKeyword,
type: SyntaxKind.TypeKeyword,
typeof: SyntaxKind.TypeOfKeyword,
undefined: SyntaxKind.UndefinedKeyword,
unique: SyntaxKind.UniqueKeyword,
unknown: SyntaxKind.UnknownKeyword,
var: SyntaxKind.VarKeyword,
void: SyntaxKind.VoidKeyword,
while: SyntaxKind.WhileKeyword,
with: SyntaxKind.WithKeyword,
yield: SyntaxKind.YieldKeyword,
async: SyntaxKind.AsyncKeyword,
await: SyntaxKind.AwaitKeyword,
of: SyntaxKind.OfKeyword,
};
const textToKeyword = createMapFromTemplate(textToKeywordObj);
const textToToken = createMapFromTemplate<SyntaxKind>({
...textToKeywordObj,
"{": SyntaxKind.OpenBraceToken,
"}": SyntaxKind.CloseBraceToken,
"(": SyntaxKind.OpenParenToken,
@@ -331,17 +338,35 @@ namespace ts {
return result;
}
export function getPositionOfLineAndCharacter(sourceFile: SourceFileLike, line: number, character: number): number {
return computePositionOfLineAndCharacter(getLineStarts(sourceFile), line, character, sourceFile.text);
export function getPositionOfLineAndCharacter(sourceFile: SourceFileLike, line: number, character: number): number;
/* @internal */
// tslint:disable-next-line:unified-signatures
export function getPositionOfLineAndCharacter(sourceFile: SourceFileLike, line: number, character: number, allowEdits?: true): number;
export function getPositionOfLineAndCharacter(sourceFile: SourceFileLike, line: number, character: number, allowEdits?: true): number {
return sourceFile.getPositionOfLineAndCharacter ?
sourceFile.getPositionOfLineAndCharacter(line, character, allowEdits) :
computePositionOfLineAndCharacter(getLineStarts(sourceFile), line, character, sourceFile.text, allowEdits);
}
/* @internal */
export function computePositionOfLineAndCharacter(lineStarts: ReadonlyArray<number>, line: number, character: number, debugText?: string): number {
export function computePositionOfLineAndCharacter(lineStarts: ReadonlyArray<number>, line: number, character: number, debugText?: string, allowEdits?: true): number {
if (line < 0 || line >= lineStarts.length) {
Debug.fail(`Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length} , line map is correct? ${debugText !== undefined ? arraysEqual(lineStarts, computeLineStarts(debugText)) : "unknown"}`);
if (allowEdits) {
// Clamp line to nearest allowable value
line = line < 0 ? 0 : line >= lineStarts.length ? lineStarts.length - 1 : line;
}
else {
Debug.fail(`Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length} , line map is correct? ${debugText !== undefined ? arraysEqual(lineStarts, computeLineStarts(debugText)) : "unknown"}`);
}
}
const res = lineStarts[line] + character;
if (allowEdits) {
// Clamp to nearest allowable values to allow the underlying to be edited without crashing (accuracy is lost, instead)
// TODO: Somehow track edits between file as it was during the creation of sourcemap we have and the current file and
// apply them to the computed position to improve accuracy
return res > lineStarts[line + 1] ? lineStarts[line + 1] : typeof debugText === "string" && res > debugText.length ? debugText.length : res;
}
if (line < lineStarts.length - 1) {
Debug.assert(res < lineStarts[line + 1]);
}
@@ -915,7 +940,7 @@ namespace ts {
return result + text.substring(start, pos);
}
function scanNumber(): string {
function scanNumber(): {type: SyntaxKind, value: string} {
const start = pos;
const mainFragment = scanNumberFragment();
let decimalFragment: string | undefined;
@@ -939,18 +964,54 @@ namespace ts {
end = pos;
}
}
let result: string;
if (tokenFlags & TokenFlags.ContainsSeparator) {
let result = mainFragment;
result = mainFragment;
if (decimalFragment) {
result += "." + decimalFragment;
}
if (scientificFragment) {
result += scientificFragment;
}
return "" + +result;
}
else {
return "" + +(text.substring(start, end)); // No need to use all the fragments; no _ removal needed
result = text.substring(start, end); // No need to use all the fragments; no _ removal needed
}
if (decimalFragment !== undefined || tokenFlags & TokenFlags.Scientific) {
checkForIdentifierStartAfterNumericLiteral(start, decimalFragment === undefined && !!(tokenFlags & TokenFlags.Scientific));
return {
type: SyntaxKind.NumericLiteral,
value: "" + +result // if value is not an integer, it can be safely coerced to a number
};
}
else {
tokenValue = result;
const type = checkBigIntSuffix(); // if value is an integer, check whether it is a bigint
checkForIdentifierStartAfterNumericLiteral(start);
return { type, value: tokenValue };
}
}
function checkForIdentifierStartAfterNumericLiteral(numericStart: number, isScientific?: boolean) {
if (!isIdentifierStart(text.charCodeAt(pos), languageVersion)) {
return;
}
const identifierStart = pos;
const { length } = scanIdentifierParts();
if (length === 1 && text[identifierStart] === "n") {
if (isScientific) {
error(Diagnostics.A_bigint_literal_cannot_use_exponential_notation, numericStart, identifierStart - numericStart + 1);
}
else {
error(Diagnostics.A_bigint_literal_must_be_an_integer, numericStart, identifierStart - numericStart + 1);
}
}
else {
error(Diagnostics.An_identifier_or_keyword_cannot_immediately_follow_a_numeric_literal, identifierStart, length);
pos = identifierStart;
}
}
@@ -967,24 +1028,24 @@ namespace ts {
* returning -1 if the given number is unavailable.
*/
function scanExactNumberOfHexDigits(count: number, canHaveSeparators: boolean): number {
return scanHexDigits(/*minCount*/ count, /*scanAsManyAsPossible*/ false, canHaveSeparators);
const valueString = scanHexDigits(/*minCount*/ count, /*scanAsManyAsPossible*/ false, canHaveSeparators);
return valueString ? parseInt(valueString, 16) : -1;
}
/**
* Scans as many hexadecimal digits as are available in the text,
* returning -1 if the given number of digits was unavailable.
* returning "" if the given number of digits was unavailable.
*/
function scanMinimumNumberOfHexDigits(count: number, canHaveSeparators: boolean): number {
function scanMinimumNumberOfHexDigits(count: number, canHaveSeparators: boolean): string {
return scanHexDigits(/*minCount*/ count, /*scanAsManyAsPossible*/ true, canHaveSeparators);
}
function scanHexDigits(minCount: number, scanAsManyAsPossible: boolean, canHaveSeparators: boolean): number {
let digits = 0;
let value = 0;
function scanHexDigits(minCount: number, scanAsManyAsPossible: boolean, canHaveSeparators: boolean): string {
let valueChars: number[] = [];
let allowSeparator = false;
let isPreviousTokenSeparator = false;
while (digits < minCount || scanAsManyAsPossible) {
const ch = text.charCodeAt(pos);
while (valueChars.length < minCount || scanAsManyAsPossible) {
let ch = text.charCodeAt(pos);
if (canHaveSeparators && ch === CharacterCodes._) {
tokenFlags |= TokenFlags.ContainsSeparator;
if (allowSeparator) {
@@ -1001,29 +1062,25 @@ namespace ts {
continue;
}
allowSeparator = canHaveSeparators;
if (ch >= CharacterCodes._0 && ch <= CharacterCodes._9) {
value = value * 16 + ch - CharacterCodes._0;
if (ch >= CharacterCodes.A && ch <= CharacterCodes.F) {
ch += CharacterCodes.a - CharacterCodes.A; // standardize hex literals to lowercase
}
else if (ch >= CharacterCodes.A && ch <= CharacterCodes.F) {
value = value * 16 + ch - CharacterCodes.A + 10;
}
else if (ch >= CharacterCodes.a && ch <= CharacterCodes.f) {
value = value * 16 + ch - CharacterCodes.a + 10;
}
else {
else if (!((ch >= CharacterCodes._0 && ch <= CharacterCodes._9) ||
(ch >= CharacterCodes.a && ch <= CharacterCodes.f)
)) {
break;
}
valueChars.push(ch);
pos++;
digits++;
isPreviousTokenSeparator = false;
}
if (digits < minCount) {
value = -1;
if (valueChars.length < minCount) {
valueChars = [];
}
if (text.charCodeAt(pos - 1) === CharacterCodes._) {
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos - 1, 1);
}
return value;
return String.fromCharCode(...valueChars);
}
function scanString(jsxAttributeString = false): string {
@@ -1203,7 +1260,8 @@ namespace ts {
}
function scanExtendedUnicodeEscape(): string {
const escapedValue = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ false);
const escapedValueString = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ false);
const escapedValue = escapedValueString ? parseInt(escapedValueString, 16) : -1;
let isInvalidExtendedEscape = false;
// Validate the value of the digit
@@ -1290,28 +1348,25 @@ namespace ts {
return result;
}
function getIdentifierToken(): SyntaxKind {
function getIdentifierToken(): SyntaxKind.Identifier | KeywordSyntaxKind {
// Reserved words are between 2 and 11 characters long and start with a lowercase letter
const len = tokenValue.length;
if (len >= 2 && len <= 11) {
const ch = tokenValue.charCodeAt(0);
if (ch >= CharacterCodes.a && ch <= CharacterCodes.z) {
token = textToToken.get(tokenValue)!;
if (token !== undefined) {
return token;
const keyword = textToKeyword.get(tokenValue);
if (keyword !== undefined) {
return token = keyword;
}
}
}
return token = SyntaxKind.Identifier;
}
function scanBinaryOrOctalDigits(base: number): number {
Debug.assert(base === 2 || base === 8, "Expected either base 2 or base 8");
let value = 0;
function scanBinaryOrOctalDigits(base: 2 | 8): string {
let value = "";
// For counting number of digits; Valid binaryIntegerLiteral must have at least one binary digit following B or b.
// Similarly valid octalIntegerLiteral must have at least one octal digit following o or O.
let numberOfDigits = 0;
let separatorAllowed = false;
let isPreviousTokenSeparator = false;
while (true) {
@@ -1333,27 +1388,42 @@ namespace ts {
continue;
}
separatorAllowed = true;
const valueOfCh = ch - CharacterCodes._0;
if (!isDigit(ch) || valueOfCh >= base) {
if (!isDigit(ch) || ch - CharacterCodes._0 >= base) {
break;
}
value = value * base + valueOfCh;
value += text[pos];
pos++;
numberOfDigits++;
isPreviousTokenSeparator = false;
}
// Invalid binaryIntegerLiteral or octalIntegerLiteral
if (numberOfDigits === 0) {
return -1;
}
if (text.charCodeAt(pos - 1) === CharacterCodes._) {
// Literal ends with underscore - not allowed
error(Diagnostics.Numeric_separators_are_not_allowed_here, pos - 1, 1);
return value;
}
return value;
}
function checkBigIntSuffix(): SyntaxKind {
if (text.charCodeAt(pos) === CharacterCodes.n) {
tokenValue += "n";
// Use base 10 instead of base 2 or base 8 for shorter literals
if (tokenFlags & TokenFlags.BinaryOrOctalSpecifier) {
tokenValue = parsePseudoBigInt(tokenValue) + "n";
}
pos++;
return SyntaxKind.BigIntLiteral;
}
else { // not a bigint, so can convert to number in simplified form
// Number() may not support 0b or 0o, so use parseInt() instead
const numericValue = tokenFlags & TokenFlags.BinarySpecifier
? parseInt(tokenValue.slice(2), 2) // skip "0b"
: tokenFlags & TokenFlags.OctalSpecifier
? parseInt(tokenValue.slice(2), 8) // skip "0o"
: +tokenValue;
tokenValue = "" + numericValue;
return SyntaxKind.NumericLiteral;
}
}
function scan(): SyntaxKind {
startPos = pos;
tokenFlags = 0;
@@ -1502,7 +1572,7 @@ namespace ts {
return token = SyntaxKind.MinusToken;
case CharacterCodes.dot:
if (isDigit(text.charCodeAt(pos + 1))) {
tokenValue = scanNumber();
tokenValue = scanNumber().value;
return token = SyntaxKind.NumericLiteral;
}
if (text.charCodeAt(pos + 1) === CharacterCodes.dot && text.charCodeAt(pos + 2) === CharacterCodes.dot) {
@@ -1578,36 +1648,36 @@ namespace ts {
case CharacterCodes._0:
if (pos + 2 < end && (text.charCodeAt(pos + 1) === CharacterCodes.X || text.charCodeAt(pos + 1) === CharacterCodes.x)) {
pos += 2;
let value = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ true);
if (value < 0) {
tokenValue = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ true);
if (!tokenValue) {
error(Diagnostics.Hexadecimal_digit_expected);
value = 0;
tokenValue = "0";
}
tokenValue = "" + value;
tokenValue = "0x" + tokenValue;
tokenFlags |= TokenFlags.HexSpecifier;
return token = SyntaxKind.NumericLiteral;
return token = checkBigIntSuffix();
}
else if (pos + 2 < end && (text.charCodeAt(pos + 1) === CharacterCodes.B || text.charCodeAt(pos + 1) === CharacterCodes.b)) {
pos += 2;
let value = scanBinaryOrOctalDigits(/* base */ 2);
if (value < 0) {
tokenValue = scanBinaryOrOctalDigits(/* base */ 2);
if (!tokenValue) {
error(Diagnostics.Binary_digit_expected);
value = 0;
tokenValue = "0";
}
tokenValue = "" + value;
tokenValue = "0b" + tokenValue;
tokenFlags |= TokenFlags.BinarySpecifier;
return token = SyntaxKind.NumericLiteral;
return token = checkBigIntSuffix();
}
else if (pos + 2 < end && (text.charCodeAt(pos + 1) === CharacterCodes.O || text.charCodeAt(pos + 1) === CharacterCodes.o)) {
pos += 2;
let value = scanBinaryOrOctalDigits(/* base */ 8);
if (value < 0) {
tokenValue = scanBinaryOrOctalDigits(/* base */ 8);
if (!tokenValue) {
error(Diagnostics.Octal_digit_expected);
value = 0;
tokenValue = "0";
}
tokenValue = "" + value;
tokenValue = "0o" + tokenValue;
tokenFlags |= TokenFlags.OctalSpecifier;
return token = SyntaxKind.NumericLiteral;
return token = checkBigIntSuffix();
}
// Try to parse as an octal
if (pos + 1 < end && isOctalDigit(text.charCodeAt(pos + 1))) {
@@ -1628,8 +1698,8 @@ namespace ts {
case CharacterCodes._7:
case CharacterCodes._8:
case CharacterCodes._9:
tokenValue = scanNumber();
return token = SyntaxKind.NumericLiteral;
({ type: token, value: tokenValue } = scanNumber());
return token;
case CharacterCodes.colon:
pos++;
return token = SyntaxKind.ColonToken;
@@ -2026,7 +2096,7 @@ namespace ts {
pos++;
}
tokenValue = text.substring(tokenPos, pos);
return token = SyntaxKind.Identifier;
return token = getIdentifierToken();
}
else {
return token = SyntaxKind.Unknown;
+391
View File
@@ -0,0 +1,391 @@
/* @internal */
namespace ts {
// https://semver.org/#spec-item-2
// > A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative
// > integers, and MUST NOT contain leading zeroes. X is the major version, Y is the minor
// > version, and Z is the patch version. Each element MUST increase numerically.
//
// NOTE: We differ here in that we allow X and X.Y, with missing parts having the default
// value of `0`.
const versionRegExp = /^(0|[1-9]\d*)(?:\.(0|[1-9]\d*)(?:\.(0|[1-9]\d*)(?:\-([a-z0-9-.]+))?(?:\+([a-z0-9-.]+))?)?)?$/i;
// https://semver.org/#spec-item-9
// > A pre-release version MAY be denoted by appending a hyphen and a series of dot separated
// > identifiers immediately following the patch version. Identifiers MUST comprise only ASCII
// > alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty. Numeric identifiers
// > MUST NOT include leading zeroes.
const prereleaseRegExp = /^(?:0|[1-9]\d*|[a-z-][a-z0-9-]*)(?:\.(?:0|[1-9]\d*|[a-z-][a-z0-9-]*))*$/i;
// https://semver.org/#spec-item-10
// > Build metadata MAY be denoted by appending a plus sign and a series of dot separated
// > identifiers immediately following the patch or pre-release version. Identifiers MUST
// > comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty.
const buildRegExp = /^[a-z0-9-]+(?:\.[a-z0-9-]+)*$/i;
// https://semver.org/#spec-item-9
// > Numeric identifiers MUST NOT include leading zeroes.
const numericIdentifierRegExp = /^(0|[1-9]\d*)$/;
/**
* Describes a precise semantic version number, https://semver.org
*/
export class Version {
static readonly zero = new Version(0, 0, 0);
readonly major: number;
readonly minor: number;
readonly patch: number;
readonly prerelease: ReadonlyArray<string>;
readonly build: ReadonlyArray<string>;
constructor(text: string);
constructor(major: number, minor?: number, patch?: number, prerelease?: string, build?: string);
constructor(major: number | string, minor = 0, patch = 0, prerelease = "", build = "") {
if (typeof major === "string") {
const result = Debug.assertDefined(tryParseComponents(major), "Invalid version");
({ major, minor, patch, prerelease, build } = result);
}
Debug.assert(major >= 0, "Invalid argument: major");
Debug.assert(minor >= 0, "Invalid argument: minor");
Debug.assert(patch >= 0, "Invalid argument: patch");
Debug.assert(!prerelease || prereleaseRegExp.test(prerelease), "Invalid argument: prerelease");
Debug.assert(!build || buildRegExp.test(build), "Invalid argument: build");
this.major = major;
this.minor = minor;
this.patch = patch;
this.prerelease = prerelease ? prerelease.split(".") : emptyArray;
this.build = build ? build.split(".") : emptyArray;
}
static tryParse(text: string) {
const result = tryParseComponents(text);
if (!result) return undefined;
const { major, minor, patch, prerelease, build } = result;
return new Version(major, minor, patch, prerelease, build);
}
compareTo(other: Version | undefined) {
// https://semver.org/#spec-item-11
// > Precedence is determined by the first difference when comparing each of these
// > identifiers from left to right as follows: Major, minor, and patch versions are
// > always compared numerically.
//
// https://semver.org/#spec-item-11
// > Precedence for two pre-release versions with the same major, minor, and patch version
// > MUST be determined by comparing each dot separated identifier from left to right until
// > a difference is found [...]
//
// https://semver.org/#spec-item-11
// > Build metadata does not figure into precedence
if (this === other) return Comparison.EqualTo;
if (other === undefined) return Comparison.GreaterThan;
return compareValues(this.major, other.major)
|| compareValues(this.minor, other.minor)
|| compareValues(this.patch, other.patch)
|| comparePrerelaseIdentifiers(this.prerelease, other.prerelease);
}
increment(field: "major" | "minor" | "patch") {
switch (field) {
case "major": return new Version(this.major + 1, 0, 0);
case "minor": return new Version(this.major, this.minor + 1, 0);
case "patch": return new Version(this.major, this.minor, this.patch + 1);
default: return Debug.assertNever(field);
}
}
toString() {
let result = `${this.major}.${this.minor}.${this.patch}`;
if (some(this.prerelease)) result += `-${this.prerelease.join(".")}`;
if (some(this.build)) result += `+${this.build.join(".")}`;
return result;
}
}
function tryParseComponents(text: string) {
const match = versionRegExp.exec(text);
if (!match) return undefined;
const [, major, minor = "0", patch = "0", prerelease = "", build = ""] = match;
if (prerelease && !prereleaseRegExp.test(prerelease)) return undefined;
if (build && !buildRegExp.test(build)) return undefined;
return {
major: parseInt(major, 10),
minor: parseInt(minor, 10),
patch: parseInt(patch, 10),
prerelease,
build
};
}
function comparePrerelaseIdentifiers(left: ReadonlyArray<string>, right: ReadonlyArray<string>) {
// https://semver.org/#spec-item-11
// > When major, minor, and patch are equal, a pre-release version has lower precedence
// > than a normal version.
if (left === right) return Comparison.EqualTo;
if (left.length === 0) return right.length === 0 ? Comparison.EqualTo : Comparison.GreaterThan;
if (right.length === 0) return Comparison.LessThan;
// https://semver.org/#spec-item-11
// > Precedence for two pre-release versions with the same major, minor, and patch version
// > MUST be determined by comparing each dot separated identifier from left to right until
// > a difference is found [...]
const length = Math.min(left.length, right.length);
for (let i = 0; i < length; i++) {
const leftIdentifier = left[i];
const rightIdentifier = right[i];
if (leftIdentifier === rightIdentifier) continue;
const leftIsNumeric = numericIdentifierRegExp.test(leftIdentifier);
const rightIsNumeric = numericIdentifierRegExp.test(rightIdentifier);
if (leftIsNumeric || rightIsNumeric) {
// https://semver.org/#spec-item-11
// > Numeric identifiers always have lower precedence than non-numeric identifiers.
if (leftIsNumeric !== rightIsNumeric) return leftIsNumeric ? Comparison.LessThan : Comparison.GreaterThan;
// https://semver.org/#spec-item-11
// > identifiers consisting of only digits are compared numerically
const result = compareValues(+leftIdentifier, +rightIdentifier);
if (result) return result;
}
else {
// https://semver.org/#spec-item-11
// > identifiers with letters or hyphens are compared lexically in ASCII sort order.
const result = compareStringsCaseSensitive(leftIdentifier, rightIdentifier);
if (result) return result;
}
}
// https://semver.org/#spec-item-11
// > A larger set of pre-release fields has a higher precedence than a smaller set, if all
// > of the preceding identifiers are equal.
return compareValues(left.length, right.length);
}
/**
* Describes a semantic version range, per https://github.com/npm/node-semver#ranges
*/
export class VersionRange {
private _alternatives: ReadonlyArray<ReadonlyArray<Comparator>>;
constructor(spec: string) {
this._alternatives = spec ? Debug.assertDefined(parseRange(spec), "Invalid range spec.") : emptyArray;
}
static tryParse(text: string) {
const sets = parseRange(text);
if (sets) {
const range = new VersionRange("");
range._alternatives = sets;
return range;
}
return undefined;
}
test(version: Version | string) {
if (typeof version === "string") version = new Version(version);
return testDisjunction(version, this._alternatives);
}
toString() {
return formatDisjunction(this._alternatives);
}
}
interface Comparator {
readonly operator: "<" | "<=" | ">" | ">=" | "=";
readonly operand: Version;
}
// https://github.com/npm/node-semver#range-grammar
//
// range-set ::= range ( logical-or range ) *
// range ::= hyphen | simple ( ' ' simple ) * | ''
// logical-or ::= ( ' ' ) * '||' ( ' ' ) *
const logicalOrRegExp = /\s*\|\|\s*/g;
const whitespaceRegExp = /\s+/g;
// https://github.com/npm/node-semver#range-grammar
//
// partial ::= xr ( '.' xr ( '.' xr qualifier ? )? )?
// xr ::= 'x' | 'X' | '*' | nr
// nr ::= '0' | ['1'-'9'] ( ['0'-'9'] ) *
// qualifier ::= ( '-' pre )? ( '+' build )?
// pre ::= parts
// build ::= parts
// parts ::= part ( '.' part ) *
// part ::= nr | [-0-9A-Za-z]+
const partialRegExp = /^([xX*0]|[1-9]\d*)(?:\.([xX*0]|[1-9]\d*)(?:\.([xX*0]|[1-9]\d*)(?:-([a-z0-9-.]+))?(?:\+([a-z0-9-.]+))?)?)?$/i;
// https://github.com/npm/node-semver#range-grammar
//
// hyphen ::= partial ' - ' partial
const hyphenRegExp = /^\s*([a-z0-9-+.*]+)\s+-\s+([a-z0-9-+.*]+)\s*$/i;
// https://github.com/npm/node-semver#range-grammar
//
// simple ::= primitive | partial | tilde | caret
// primitive ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial
// tilde ::= '~' partial
// caret ::= '^' partial
const rangeRegExp = /^\s*(~|\^|<|<=|>|>=|=)?\s*([a-z0-9-+.*]+)$/i;
function parseRange(text: string) {
const alternatives: Comparator[][] = [];
for (const range of text.trim().split(logicalOrRegExp)) {
if (!range) continue;
const comparators: Comparator[] = [];
const match = hyphenRegExp.exec(range);
if (match) {
if (!parseHyphen(match[1], match[2], comparators)) return undefined;
}
else {
for (const simple of range.split(whitespaceRegExp)) {
const match = rangeRegExp.exec(simple);
if (!match || !parseComparator(match[1], match[2], comparators)) return undefined;
}
}
alternatives.push(comparators);
}
return alternatives;
}
function parsePartial(text: string) {
const match = partialRegExp.exec(text);
if (!match) return undefined;
const [, major, minor = "*", patch = "*", prerelease, build] = match;
const version = new Version(
isWildcard(major) ? 0 : parseInt(major, 10),
isWildcard(major) || isWildcard(minor) ? 0 : parseInt(minor, 10),
isWildcard(major) || isWildcard(minor) || isWildcard(patch) ? 0 : parseInt(patch, 10),
prerelease,
build);
return { version, major, minor, patch };
}
function parseHyphen(left: string, right: string, comparators: Comparator[]) {
const leftResult = parsePartial(left);
if (!leftResult) return false;
const rightResult = parsePartial(right);
if (!rightResult) return false;
if (!isWildcard(leftResult.major)) {
comparators.push(createComparator(">=", leftResult.version));
}
if (!isWildcard(rightResult.major)) {
comparators.push(
isWildcard(rightResult.minor) ? createComparator("<", rightResult.version.increment("major")) :
isWildcard(rightResult.patch) ? createComparator("<", rightResult.version.increment("minor")) :
createComparator("<=", rightResult.version));
}
return true;
}
function parseComparator(operator: string, text: string, comparators: Comparator[]) {
const result = parsePartial(text);
if (!result) return false;
const { version, major, minor, patch } = result;
if (!isWildcard(major)) {
switch (operator) {
case "~":
comparators.push(createComparator(">=", version));
comparators.push(createComparator("<", version.increment(
isWildcard(minor) ? "major" :
"minor")));
break;
case "^":
comparators.push(createComparator(">=", version));
comparators.push(createComparator("<", version.increment(
version.major > 0 || isWildcard(minor) ? "major" :
version.minor > 0 || isWildcard(patch) ? "minor" :
"patch")));
break;
case "<":
case ">=":
comparators.push(createComparator(operator, version));
break;
case "<=":
case ">":
comparators.push(
isWildcard(minor) ? createComparator(operator === "<=" ? "<" : ">=", version.increment("major")) :
isWildcard(patch) ? createComparator(operator === "<=" ? "<" : ">=", version.increment("minor")) :
createComparator(operator, version));
break;
case "=":
case undefined:
if (isWildcard(minor) || isWildcard(patch)) {
comparators.push(createComparator(">=", version));
comparators.push(createComparator("<", version.increment(isWildcard(minor) ? "major" : "minor")));
}
else {
comparators.push(createComparator("=", version));
}
break;
default:
// unrecognized
return false;
}
}
else if (operator === "<" || operator === ">") {
comparators.push(createComparator("<", Version.zero));
}
return true;
}
function isWildcard(part: string) {
return part === "*" || part === "x" || part === "X";
}
function createComparator(operator: Comparator["operator"], operand: Version) {
return { operator, operand };
}
function testDisjunction(version: Version, alternatives: ReadonlyArray<ReadonlyArray<Comparator>>) {
// an empty disjunction is treated as "*" (all versions)
if (alternatives.length === 0) return true;
for (const alternative of alternatives) {
if (testAlternative(version, alternative)) return true;
}
return false;
}
function testAlternative(version: Version, comparators: ReadonlyArray<Comparator>) {
for (const comparator of comparators) {
if (!testComparator(version, comparator.operator, comparator.operand)) return false;
}
return true;
}
function testComparator(version: Version, operator: Comparator["operator"], operand: Version) {
const cmp = version.compareTo(operand);
switch (operator) {
case "<": return cmp < 0;
case "<=": return cmp <= 0;
case ">": return cmp > 0;
case ">=": return cmp >= 0;
case "=": return cmp === 0;
default: return Debug.assertNever(operator);
}
}
function formatDisjunction(alternatives: ReadonlyArray<ReadonlyArray<Comparator>>) {
return map(alternatives, formatAlternative).join(" || ") || "*";
}
function formatAlternative(comparators: ReadonlyArray<Comparator>) {
return map(comparators, formatComparator).join(" ");
}
function formatComparator(comparator: Comparator) {
return `${comparator.operator}${comparator.operand}`;
}
}
+652 -546
View File
File diff suppressed because it is too large Load Diff
-377
View File
@@ -1,377 +0,0 @@
/* @internal */
namespace ts {
export interface SourceFileLikeCache {
get(path: Path): SourceFileLike | undefined;
}
export function createSourceFileLikeCache(host: { readFile?: (path: string) => string | undefined, fileExists?: (path: string) => boolean }): SourceFileLikeCache {
const cached = createMap<SourceFileLike>();
return {
get(path: Path) {
if (cached.has(path)) {
return cached.get(path);
}
if (!host.fileExists || !host.readFile || !host.fileExists(path)) return;
// And failing that, check the disk
const text = host.readFile(path)!; // TODO: GH#18217
const file = {
text,
lineMap: undefined,
getLineAndCharacterOfPosition(pos: number) {
return computeLineAndCharacterOfPosition(getLineStarts(this), pos);
}
} as SourceFileLike;
cached.set(path, file);
return file;
}
};
}
}
/* @internal */
namespace ts.sourcemaps {
export interface SourceMapData {
version?: number;
file?: string;
sourceRoot?: string;
sources: string[];
sourcesContent?: (string | null)[];
names?: string[];
mappings: string;
}
export interface SourceMappableLocation {
fileName: string;
position: number;
}
export interface SourceMapper {
getOriginalPosition(input: SourceMappableLocation): SourceMappableLocation;
getGeneratedPosition(input: SourceMappableLocation): SourceMappableLocation;
}
export const identitySourceMapper = { getOriginalPosition: identity, getGeneratedPosition: identity };
export interface SourceMapDecodeHost {
readFile(path: string): string | undefined;
fileExists(path: string): boolean;
getCanonicalFileName(path: string): string;
log(text: string): void;
}
export function decode(host: SourceMapDecodeHost, mapPath: string, map: SourceMapData, program?: Program, fallbackCache = createSourceFileLikeCache(host)): SourceMapper {
const currentDirectory = getDirectoryPath(mapPath);
const sourceRoot = map.sourceRoot ? getNormalizedAbsolutePath(map.sourceRoot, currentDirectory) : currentDirectory;
let decodedMappings: ProcessedSourceMapPosition[];
let generatedOrderedMappings: ProcessedSourceMapPosition[];
let sourceOrderedMappings: ProcessedSourceMapPosition[];
return {
getOriginalPosition,
getGeneratedPosition
};
function getGeneratedPosition(loc: SourceMappableLocation): SourceMappableLocation {
const maps = getSourceOrderedMappings();
if (!length(maps)) return loc;
let targetIndex = binarySearch(maps, { sourcePath: loc.fileName, sourcePosition: loc.position }, identity, compareProcessedPositionSourcePositions);
if (targetIndex < 0 && maps.length > 0) {
// if no exact match, closest is 2's compliment of result
targetIndex = ~targetIndex;
}
if (!maps[targetIndex] || comparePaths(loc.fileName, maps[targetIndex].sourcePath, sourceRoot) !== 0) {
return loc;
}
return { fileName: toPath(map.file!, sourceRoot, host.getCanonicalFileName), position: maps[targetIndex].emittedPosition }; // Closest pos
}
function getOriginalPosition(loc: SourceMappableLocation): SourceMappableLocation {
const maps = getGeneratedOrderedMappings();
if (!length(maps)) return loc;
let targetIndex = binarySearch(maps, { emittedPosition: loc.position }, identity, compareProcessedPositionEmittedPositions);
if (targetIndex < 0 && maps.length > 0) {
// if no exact match, closest is 2's compliment of result
targetIndex = ~targetIndex;
}
return { fileName: toPath(maps[targetIndex].sourcePath, sourceRoot, host.getCanonicalFileName), position: maps[targetIndex].sourcePosition }; // Closest pos
}
function getSourceFileLike(fileName: string, location: string): SourceFileLike | undefined {
// Lookup file in program, if provided
const path = toPath(fileName, location, host.getCanonicalFileName);
const file = program && program.getSourceFile(path);
// file returned here could be .d.ts when asked for .ts file if projectReferences and module resolution created this source file
if (!file || file.resolvedPath !== path) {
// Otherwise check the cache (which may hit disk)
return fallbackCache.get(path);
}
return file;
}
function getPositionOfLineAndCharacterUsingName(fileName: string, directory: string, line: number, character: number) {
const file = getSourceFileLike(fileName, directory);
if (!file) {
return -1;
}
return getPositionOfLineAndCharacter(file, line, character);
}
function getDecodedMappings() {
return decodedMappings || (decodedMappings = calculateDecodedMappings(map, processPosition, host));
}
function getSourceOrderedMappings() {
return sourceOrderedMappings || (sourceOrderedMappings = getDecodedMappings().slice().sort(compareProcessedPositionSourcePositions));
}
function getGeneratedOrderedMappings() {
return generatedOrderedMappings || (generatedOrderedMappings = getDecodedMappings().slice().sort(compareProcessedPositionEmittedPositions));
}
function compareProcessedPositionSourcePositions(a: ProcessedSourceMapPosition, b: ProcessedSourceMapPosition) {
return comparePaths(a.sourcePath, b.sourcePath, sourceRoot) ||
compareValues(a.sourcePosition, b.sourcePosition);
}
function compareProcessedPositionEmittedPositions(a: ProcessedSourceMapPosition, b: ProcessedSourceMapPosition) {
return compareValues(a.emittedPosition, b.emittedPosition);
}
function processPosition(position: RawSourceMapPosition): ProcessedSourceMapPosition {
const sourcePath = map.sources[position.sourceIndex];
return {
emittedPosition: getPositionOfLineAndCharacterUsingName(map.file!, currentDirectory, position.emittedLine, position.emittedColumn),
sourcePosition: getPositionOfLineAndCharacterUsingName(sourcePath, sourceRoot, position.sourceLine, position.sourceColumn),
sourcePath,
// TODO: Consider using `name` field to remap the expected identifier to scan for renames to handle another tool renaming oout output
// name: position.nameIndex ? map.names[position.nameIndex] : undefined
};
}
}
/*@internal*/
export interface MappingsDecoder extends Iterator<SourceMapSpan> {
readonly decodingIndex: number;
readonly error: string | undefined;
readonly lastSpan: SourceMapSpan;
}
/*@internal*/
export function decodeMappings(map: SourceMapData): MappingsDecoder {
const state: DecoderState = {
encodedText: map.mappings,
currentNameIndex: undefined,
sourceMapNamesLength: map.names ? map.names.length : undefined,
currentEmittedColumn: 0,
currentEmittedLine: 0,
currentSourceColumn: 0,
currentSourceLine: 0,
currentSourceIndex: 0,
decodingIndex: 0
};
function captureSpan(): SourceMapSpan {
return {
emittedColumn: state.currentEmittedColumn,
emittedLine: state.currentEmittedLine,
sourceColumn: state.currentSourceColumn,
sourceIndex: state.currentSourceIndex,
sourceLine: state.currentSourceLine,
nameIndex: state.currentNameIndex
};
}
return {
get decodingIndex() { return state.decodingIndex; },
get error() { return state.error; },
get lastSpan() { return captureSpan(); },
next() {
if (hasCompletedDecoding(state) || state.error) return { done: true, value: undefined as never };
if (!decodeSinglePosition(state)) return { done: true, value: undefined as never };
return { done: false, value: captureSpan() };
}
};
}
function calculateDecodedMappings<T>(map: SourceMapData, processPosition: (position: RawSourceMapPosition) => T, host?: { log?(s: string): void }): T[] {
const decoder = decodeMappings(map);
const positions = arrayFrom(decoder, processPosition);
if (decoder.error) {
if (host && host.log) {
host.log(`Encountered error while decoding sourcemap: ${decoder.error}`);
}
return [];
}
return positions;
}
interface ProcessedSourceMapPosition {
emittedPosition: number;
sourcePosition: number;
sourcePath: string;
}
interface RawSourceMapPosition {
emittedLine: number;
emittedColumn: number;
sourceLine: number;
sourceColumn: number;
sourceIndex: number;
nameIndex?: number;
}
interface DecoderState {
decodingIndex: number;
currentEmittedLine: number;
currentEmittedColumn: number;
currentSourceLine: number;
currentSourceColumn: number;
currentSourceIndex: number;
currentNameIndex: number | undefined;
encodedText: string;
sourceMapNamesLength?: number;
error?: string;
}
function hasCompletedDecoding(state: DecoderState) {
return state.decodingIndex === state.encodedText.length;
}
function decodeSinglePosition(state: DecoderState): boolean {
while (state.decodingIndex < state.encodedText.length) {
const char = state.encodedText.charCodeAt(state.decodingIndex);
if (char === CharacterCodes.semicolon) {
// New line
state.currentEmittedLine++;
state.currentEmittedColumn = 0;
state.decodingIndex++;
continue;
}
if (char === CharacterCodes.comma) {
// Next entry is on same line - no action needed
state.decodingIndex++;
continue;
}
// Read the current position
// 1. Column offset from prev read jsColumn
state.currentEmittedColumn += base64VLQFormatDecode();
// Incorrect emittedColumn dont support this map
if (createErrorIfCondition(state.currentEmittedColumn < 0, "Invalid emittedColumn found")) {
return false;
}
// Dont support reading mappings that dont have information about original source and its line numbers
if (createErrorIfCondition(isSourceMappingSegmentEnd(state.encodedText, state.decodingIndex), "Unsupported Error Format: No entries after emitted column")) {
return false;
}
// 2. Relative sourceIndex
state.currentSourceIndex += base64VLQFormatDecode();
// Incorrect sourceIndex dont support this map
if (createErrorIfCondition(state.currentSourceIndex < 0, "Invalid sourceIndex found")) {
return false;
}
// Dont support reading mappings that dont have information about original source position
if (createErrorIfCondition(isSourceMappingSegmentEnd(state.encodedText, state.decodingIndex), "Unsupported Error Format: No entries after sourceIndex")) {
return false;
}
// 3. Relative sourceLine 0 based
state.currentSourceLine += base64VLQFormatDecode();
// Incorrect sourceLine dont support this map
if (createErrorIfCondition(state.currentSourceLine < 0, "Invalid sourceLine found")) {
return false;
}
// Dont support reading mappings that dont have information about original source and its line numbers
if (createErrorIfCondition(isSourceMappingSegmentEnd(state.encodedText, state.decodingIndex), "Unsupported Error Format: No entries after emitted Line")) {
return false;
}
// 4. Relative sourceColumn 0 based
state.currentSourceColumn += base64VLQFormatDecode();
// Incorrect sourceColumn dont support this map
if (createErrorIfCondition(state.currentSourceColumn < 0, "Invalid sourceLine found")) {
return false;
}
// 5. Check if there is name:
if (!isSourceMappingSegmentEnd(state.encodedText, state.decodingIndex)) {
if (state.currentNameIndex === undefined) {
state.currentNameIndex = 0;
}
state.currentNameIndex += base64VLQFormatDecode();
// Incorrect nameIndex dont support this map
// TODO: If we start using `name`s, issue errors when they aren't correct in the sourcemap
// if (createErrorIfCondition(state.currentNameIndex < 0 || state.currentNameIndex >= state.sourceMapNamesLength, "Invalid name index for the source map entry")) {
// return;
// }
}
// Dont support reading mappings that dont have information about original source and its line numbers
if (createErrorIfCondition(!isSourceMappingSegmentEnd(state.encodedText, state.decodingIndex), "Unsupported Error Format: There are more entries after " + (state.currentNameIndex === undefined ? "sourceColumn" : "nameIndex"))) {
return false;
}
// Entry should be complete
return true;
}
createErrorIfCondition(/*condition*/ true, "No encoded entry found");
return false;
function createErrorIfCondition(condition: boolean, errormsg: string) {
if (state.error) {
// An error was already reported
return true;
}
if (condition) {
state.error = errormsg;
}
return condition;
}
function base64VLQFormatDecode(): number {
let moreDigits = true;
let shiftCount = 0;
let value = 0;
for (; moreDigits; state.decodingIndex++) {
if (createErrorIfCondition(state.decodingIndex >= state.encodedText.length, "Error in decoding base64VLQFormatDecode, past the mapping string")) {
return undefined!; // TODO: GH#18217
}
// 6 digit number
const currentByte = base64FormatDecode(state.encodedText.charAt(state.decodingIndex));
// If msb is set, we still have more bits to continue
moreDigits = (currentByte & 32) !== 0;
// least significant 5 bits are the next msbs in the final value.
value = value | ((currentByte & 31) << shiftCount);
shiftCount += 5;
}
// Least significant bit if 1 represents negative and rest of the msb is actual absolute value
if ((value & 1) === 0) {
// + number
value = value >> 1;
}
else {
// - number
value = value >> 1;
value = -value;
}
return value;
}
}
function base64FormatDecode(char: string) {
return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(char);
}
function isSourceMappingSegmentEnd(encodedText: string, pos: number) {
return (pos === encodedText.length ||
encodedText.charCodeAt(pos) === CharacterCodes.comma ||
encodedText.charCodeAt(pos) === CharacterCodes.semicolon);
}
}
+24 -37
View File
@@ -2,6 +2,16 @@ declare function setTimeout(handler: (...args: any[]) => void, timeout: number):
declare function clearTimeout(handle: any): void;
namespace ts {
/**
* djb2 hashing algorithm
* http://www.cse.yorku.ca/~oz/hash.html
*/
/* @internal */
export function generateDjb2Hash(data: string): string {
const chars = data.split("").map(str => str.charCodeAt(0));
return `${chars.reduce((prev, curr) => ((prev << 5) + prev) + curr, 5381)}`;
}
/**
* Set a high stack trace limit to provide more information in case of an error.
* Called for command-line and server use cases.
@@ -36,23 +46,6 @@ namespace ts {
Low = 250
}
function getPriorityValues(highPriorityValue: number): [number, number, number] {
const mediumPriorityValue = highPriorityValue * 2;
const lowPriorityValue = mediumPriorityValue * 4;
return [highPriorityValue, mediumPriorityValue, lowPriorityValue];
}
function pollingInterval(watchPriority: PollingInterval): number {
return pollingIntervalsForPriority[watchPriority];
}
const pollingIntervalsForPriority = getPriorityValues(250);
/* @internal */
export function watchFileUsingPriorityPollingInterval(host: System, fileName: string, callback: FileWatcherCallback, watchPriority: PollingInterval): FileWatcher {
return host.watchFile!(fileName, callback, pollingInterval(watchPriority));
}
/* @internal */
export type HostWatchFile = (fileName: string, callback: FileWatcherCallback, pollingInterval: PollingInterval | undefined) => FileWatcher;
/* @internal */
@@ -317,18 +310,22 @@ namespace ts {
const newTime = modifiedTime.getTime();
if (oldTime !== newTime) {
watchedFile.mtime = modifiedTime;
const eventKind = oldTime === 0
? FileWatcherEventKind.Created
: newTime === 0
? FileWatcherEventKind.Deleted
: FileWatcherEventKind.Changed;
watchedFile.callback(watchedFile.fileName, eventKind);
watchedFile.callback(watchedFile.fileName, getFileWatcherEventKind(oldTime, newTime));
return true;
}
return false;
}
/*@internal*/
export function getFileWatcherEventKind(oldTime: number, newTime: number) {
return oldTime === 0
? FileWatcherEventKind.Created
: newTime === 0
? FileWatcherEventKind.Deleted
: FileWatcherEventKind.Changed;
}
/*@internal*/
export interface RecursiveDirectoryWatcherHost {
watchDirectory: HostWatchDirectory;
@@ -792,11 +789,10 @@ namespace ts {
dirName,
(_eventName: string, relativeFileName) => {
// When files are deleted from disk, the triggered "rename" event would have a relativefileName of "undefined"
const fileName = !isString(relativeFileName)
? undefined! // TODO: GH#18217
: getNormalizedAbsolutePath(relativeFileName, dirName);
if (!isString(relativeFileName)) { return; }
const fileName = getNormalizedAbsolutePath(relativeFileName, dirName);
// Some applications save a working file via rename operations
const callbacks = fileWatcherCallbacks.get(toCanonicalName(fileName));
const callbacks = fileName && fileWatcherCallbacks.get(toCanonicalName(fileName));
if (callbacks) {
for (const fileCallback of callbacks) {
fileCallback(fileName, FileWatcherEventKind.Changed);
@@ -843,7 +839,7 @@ namespace ts {
}
}
type FsWatchCallback = (eventName: "rename" | "change", relativeFileName: string) => void;
type FsWatchCallback = (eventName: "rename" | "change", relativeFileName: string | undefined) => void;
function createFileWatcherCallback(callback: FsWatchCallback): FileWatcherCallback {
return (_fileName, eventKind) => callback(eventKind === FileWatcherEventKind.Changed ? "change" : "rename", "");
@@ -1129,15 +1125,6 @@ namespace ts {
}
}
/**
* djb2 hashing algorithm
* http://www.cse.yorku.ca/~oz/hash.html
*/
function generateDjb2Hash(data: string): string {
const chars = data.split("").map(str => str.charCodeAt(0));
return `${chars.reduce((prev, curr) => ((prev << 5) + prev) + curr, 5381)}`;
}
function createMD5HashUsingNativeCrypto(data: string): string {
const hash = _crypto!.createHash("md5");
hash.update(data);
+10 -2
View File
@@ -68,6 +68,14 @@ namespace ts {
return transformers;
}
export function noEmitSubstitution(_hint: EmitHint, node: Node) {
return node;
}
export function noEmitNotification(hint: EmitHint, node: Node, callback: (hint: EmitHint, node: Node) => void) {
callback(hint, node);
}
/**
* Transforms an array of SourceFiles by passing them through each transformer.
*
@@ -87,8 +95,8 @@ namespace ts {
let lexicalEnvironmentStackOffset = 0;
let lexicalEnvironmentSuspended = false;
let emitHelpers: EmitHelper[] | undefined;
let onSubstituteNode: TransformationContext["onSubstituteNode"] = (_, node) => node;
let onEmitNode: TransformationContext["onEmitNode"] = (hint, node, callback) => callback(hint, node);
let onSubstituteNode: TransformationContext["onSubstituteNode"] = noEmitSubstitution;
let onEmitNode: TransformationContext["onEmitNode"] = noEmitNotification;
let state = TransformationState.Uninitialized;
const diagnostics: DiagnosticWithLocation[] = [];
+30 -10
View File
@@ -1,11 +1,11 @@
/*@internal*/
namespace ts {
export function getDeclarationDiagnostics(host: EmitHost, resolver: EmitResolver, file: SourceFile | undefined): DiagnosticWithLocation[] | undefined {
if (file && isSourceFileJavaScript(file)) {
if (file && isSourceFileJS(file)) {
return []; // No declaration diagnostics for js for now
}
const compilerOptions = host.getCompilerOptions();
const result = transformNodes(resolver, host, compilerOptions, file ? [file] : filter(host.getSourceFiles(), isSourceFileNotJavaScript), [transformDeclarations], /*allowDtsFiles*/ false);
const result = transformNodes(resolver, host, compilerOptions, file ? [file] : filter(host.getSourceFiles(), isSourceFileNotJS), [transformDeclarations], /*allowDtsFiles*/ false);
return result.diagnostics;
}
@@ -45,6 +45,7 @@ namespace ts {
reportInaccessibleThisError,
reportInaccessibleUniqueSymbolError,
reportPrivateInBaseOfClassExpression,
reportLikelyUnsafeImportRequiredError,
moduleResolverHost: host,
trackReferencedAmbientModule,
trackExternalModuleSymbolOfImportTypeNode
@@ -153,11 +154,19 @@ namespace ts {
}
}
function reportLikelyUnsafeImportRequiredError(specifier: string) {
if (errorNameNode) {
context.addDiagnostic(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_cannot_be_named_without_a_reference_to_1_This_is_likely_not_portable_A_type_annotation_is_necessary,
declarationNameToString(errorNameNode),
specifier));
}
}
function transformRoot(node: Bundle): Bundle;
function transformRoot(node: SourceFile): SourceFile;
function transformRoot(node: SourceFile | Bundle): SourceFile | Bundle;
function transformRoot(node: SourceFile | Bundle) {
if (node.kind === SyntaxKind.SourceFile && (node.isDeclarationFile || isSourceFileJavaScript(node))) {
if (node.kind === SyntaxKind.SourceFile && (node.isDeclarationFile || isSourceFileJS(node))) {
return node;
}
@@ -168,7 +177,7 @@ namespace ts {
let hasNoDefaultLib = false;
const bundle = createBundle(map(node.sourceFiles,
sourceFile => {
if (sourceFile.isDeclarationFile || isSourceFileJavaScript(sourceFile)) return undefined!; // Omit declaration files from bundle results, too // TODO: GH#18217
if (sourceFile.isDeclarationFile || isSourceFileJS(sourceFile)) return undefined!; // Omit declaration files from bundle results, too // TODO: GH#18217
hasNoDefaultLib = hasNoDefaultLib || sourceFile.hasNoDefaultLib;
currentSourceFile = sourceFile;
enclosingDeclaration = sourceFile;
@@ -275,7 +284,7 @@ namespace ts {
else {
if (isBundledEmit && contains((node as Bundle).sourceFiles, file)) return; // Omit references to files which are being merged
const paths = getOutputPathsFor(file, host, /*forceDtsPaths*/ true);
declFileName = paths.declarationFilePath || paths.jsFilePath;
declFileName = paths.declarationFilePath || paths.jsFilePath || file.fileName;
}
if (declFileName) {
@@ -289,6 +298,13 @@ namespace ts {
if (startsWith(fileName, "./") && hasExtension(fileName)) {
fileName = fileName.substring(2);
}
// omit references to files from node_modules (npm may disambiguate module
// references when installing this package, making the path is unreliable).
if (startsWith(fileName, "node_modules/") || fileName.indexOf("/node_modules/") !== -1) {
return;
}
references.push({ pos: -1, end: -1, fileName });
}
};
@@ -296,7 +312,7 @@ namespace ts {
}
function collectReferences(sourceFile: SourceFile, ret: Map<SourceFile>) {
if (noResolve || isSourceFileJavaScript(sourceFile)) return ret;
if (noResolve || isSourceFileJS(sourceFile)) return ret;
forEach(sourceFile.referencedFiles, f => {
const elem = tryResolveScriptReference(host, sourceFile, f);
if (elem) {
@@ -366,7 +382,7 @@ namespace ts {
function ensureNoInitializer(node: CanHaveLiteralInitializer) {
if (shouldPrintWithInitializer(node)) {
return resolver.createLiteralConstValue(getParseTreeNode(node) as CanHaveLiteralInitializer); // TODO: Make safe
return resolver.createLiteralConstValue(getParseTreeNode(node) as CanHaveLiteralInitializer, symbolTracker); // TODO: Make safe
}
return undefined;
}
@@ -618,7 +634,10 @@ namespace ts {
if (!isLateVisibilityPaintedStatement(i)) {
return Debug.fail(`Late replaced statement was found which is not handled by the declaration transformer!: ${(ts as any).SyntaxKind ? (ts as any).SyntaxKind[(i as any).kind] : (i as any).kind}`);
}
const priorNeedsDeclare = needsDeclare;
needsDeclare = i.parent && isSourceFile(i.parent) && !(isExternalModule(i.parent) && isBundledEmit);
const result = transformTopLevelDeclaration(i, /*privateDeclaration*/ true);
needsDeclare = priorNeedsDeclare;
lateStatementReplacementMap.set("" + getOriginalNodeId(i), result);
}
@@ -982,7 +1001,7 @@ namespace ts {
ensureType(input, input.type),
/*body*/ undefined
));
if (clean && resolver.isJSContainerFunctionDeclaration(input)) {
if (clean && resolver.isExpandoFunctionDeclaration(input)) {
const declarations = mapDefined(resolver.getPropertiesOfContainerFunction(input), p => {
if (!isPropertyAccessExpression(p.valueDeclaration)) {
return undefined;
@@ -1036,7 +1055,7 @@ namespace ts {
const modifiers = createNodeArray(ensureModifiers(input, isPrivate));
const typeParameters = ensureTypeParams(input, input.typeParameters);
const ctor = getFirstConstructorWithBody(input);
let parameterProperties: PropertyDeclaration[] | undefined;
let parameterProperties: ReadonlyArray<PropertyDeclaration> | undefined;
if (ctor) {
const oldDiag = getSymbolAccessibilityDiagnostic;
parameterProperties = compact(flatMap(ctor.parameters, param => {
@@ -1084,7 +1103,8 @@ namespace ts {
if (extendsClause && !isEntityNameExpression(extendsClause.expression) && extendsClause.expression.kind !== SyntaxKind.NullKeyword) {
// We must add a temporary declaration for the extends clause expression
const newId = createOptimisticUniqueName(`${unescapeLeadingUnderscores(input.name!.escapedText)}_base`); // TODO: GH#18217
const oldId = input.name ? unescapeLeadingUnderscores(input.name.escapedText) : "default";
const newId = createOptimisticUniqueName(`${oldId}_base`);
getSymbolAccessibilityDiagnostic = () => ({
diagnosticMessage: Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1,
errorNode: extendsClause,
@@ -389,6 +389,7 @@ namespace ts {
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1;
break;
case SyntaxKind.ConstructorType:
case SyntaxKind.ConstructSignature:
diagnosticMessage = Diagnostics.Type_parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1;
break;
@@ -410,6 +411,7 @@ namespace ts {
}
break;
case SyntaxKind.FunctionType:
case SyntaxKind.FunctionDeclaration:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_function_has_or_is_using_private_name_1;
break;
@@ -434,7 +436,7 @@ namespace ts {
// Heritage clause is written by user so it can always be named
if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) {
// Class or Interface implemented/extended is inaccessible
diagnosticMessage = (node as ExpressionWithTypeArguments).parent.token === SyntaxKind.ImplementsKeyword ?
diagnosticMessage = isHeritageClause(node.parent) && node.parent.token === SyntaxKind.ImplementsKeyword ?
Diagnostics.Implements_clause_of_exported_class_0_has_or_is_using_private_name_1 :
Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1;
}
@@ -446,7 +448,7 @@ namespace ts {
return {
diagnosticMessage,
errorNode: node,
typeName: getNameOfDeclaration((node as ExpressionWithTypeArguments).parent.parent)
typeName: getNameOfDeclaration(node.parent.parent as Declaration)
};
}
@@ -466,4 +468,4 @@ namespace ts {
};
}
}
}
}
+3 -3
View File
@@ -307,8 +307,8 @@ namespace ts {
if (!getRestIndicatorOfBindingOrAssignmentElement(element)) {
const propertyName = getPropertyNameOfBindingOrAssignmentElement(element)!;
if (flattenContext.level >= FlattenLevel.ObjectRest
&& !(element.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest))
&& !(getTargetOfBindingOrAssignmentElement(element)!.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest))
&& !(element.transformFlags & (TransformFlags.ContainsRestOrSpread | TransformFlags.ContainsObjectRestOrSpread))
&& !(getTargetOfBindingOrAssignmentElement(element)!.transformFlags & (TransformFlags.ContainsRestOrSpread | TransformFlags.ContainsObjectRestOrSpread))
&& !isComputedPropertyName(propertyName)) {
bindingElements = append(bindingElements, element);
}
@@ -384,7 +384,7 @@ namespace ts {
if (flattenContext.level >= FlattenLevel.ObjectRest) {
// If an array pattern contains an ObjectRest, we must cache the result so that we
// can perform the ObjectRest destructuring in a different declaration
if (element.transformFlags & TransformFlags.ContainsObjectRest) {
if (element.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
const temp = createTempVariable(/*recordTempVariable*/ undefined);
if (flattenContext.hoistTempVariables) {
flattenContext.context.hoistVariableDeclaration(temp);
+481 -166
View File
@@ -46,10 +46,16 @@ namespace ts {
* so nobody can observe this new value.
*/
interface LoopOutParameter {
flags: LoopOutParameterFlags;
originalName: Identifier;
outParamName: Identifier;
}
const enum LoopOutParameterFlags {
Body = 1 << 0, // Modified in the body of the iteration statement
Initializer = 1 << 1, // Set in the initializer of a ForStatement
}
const enum CopyDirection {
ToOriginal,
ToOutParameter
@@ -129,10 +135,14 @@ namespace ts {
*/
hoistedLocalVariables?: Identifier[];
conditionVariable?: Identifier;
loopParameters: ParameterDeclaration[];
/**
* List of loop out parameters - detailed descripion can be found in the comment to LoopOutParameter
*/
loopOutParameters?: LoopOutParameter[];
loopOutParameters: LoopOutParameter[];
}
const enum SuperCaptureResult {
@@ -347,7 +357,7 @@ namespace ts {
return (node.transformFlags & TransformFlags.ContainsES2015) !== 0
|| convertedLoopState !== undefined
|| (hierarchyFacts & HierarchyFacts.ConstructorWithCapturedSuper && (isStatement(node) || (node.kind === SyntaxKind.Block)))
|| (isIterationStatement(node, /*lookInLabeledStatements*/ false) && shouldConvertIterationStatementBody(node))
|| (isIterationStatement(node, /*lookInLabeledStatements*/ false) && shouldConvertIterationStatement(node))
|| (getEmitFlags(node) & EmitFlags.TypeScriptClassWrapper) !== 0;
}
@@ -646,8 +656,8 @@ namespace ts {
}
}
let returnExpression: Expression = createLiteral(labelMarker);
if (convertedLoopState.loopOutParameters!.length) {
const outParams = convertedLoopState.loopOutParameters!;
if (convertedLoopState.loopOutParameters.length) {
const outParams = convertedLoopState.loopOutParameters;
let expr: Expression | undefined;
for (let i = 0; i < outParams.length; i++) {
const copyExpr = copyOutParameter(outParams[i], CopyDirection.ToOutParameter);
@@ -770,7 +780,7 @@ namespace ts {
enableSubstitutionsForBlockScopedBindings();
}
const extendsClauseElement = getEffectiveBaseTypeNode(node);
const extendsClauseElement = getClassExtendsHeritageElement(node);
const classFunction = createFunctionExpression(
/*modifiers*/ undefined,
/*asteriskToken*/ undefined,
@@ -1340,8 +1350,8 @@ namespace ts {
* part of a constructor declaration with a
* synthesized call to `super`
*/
function shouldAddRestParameter(node: ParameterDeclaration | undefined, inConstructorWithSynthesizedSuper: boolean) {
return node && node.dotDotDotToken && node.name.kind === SyntaxKind.Identifier && !inConstructorWithSynthesizedSuper;
function shouldAddRestParameter(node: ParameterDeclaration | undefined, inConstructorWithSynthesizedSuper: boolean): node is ParameterDeclaration {
return !!(node && node.dotDotDotToken && !inConstructorWithSynthesizedSuper);
}
/**
@@ -1360,11 +1370,11 @@ namespace ts {
}
// `declarationName` is the name of the local declaration for the parameter.
const declarationName = getMutableClone(<Identifier>parameter!.name);
const declarationName = parameter.name.kind === SyntaxKind.Identifier ? getMutableClone(parameter.name) : createTempVariable(/*recordTempVariable*/ undefined);
setEmitFlags(declarationName, EmitFlags.NoSourceMap);
// `expressionName` is the name of the parameter used in expressions.
const expressionName = getSynthesizedClone(<Identifier>parameter!.name);
const expressionName = parameter.name.kind === SyntaxKind.Identifier ? getSynthesizedClone(parameter.name) : declarationName;
const restIndex = node.parameters.length - 1;
const temp = createLoopVariable();
@@ -1429,6 +1439,24 @@ namespace ts {
setEmitFlags(forStatement, EmitFlags.CustomPrologue);
startOnNewLine(forStatement);
statements.push(forStatement);
if (parameter.name.kind !== SyntaxKind.Identifier) {
// do the actual destructuring of the rest parameter if necessary
statements.push(
setEmitFlags(
setTextRange(
createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(
flattenDestructuringBinding(parameter, visitor, context, FlattenLevel.All, expressionName),
)
),
parameter
),
EmitFlags.CustomPrologue
)
);
}
}
/**
@@ -2610,7 +2638,40 @@ namespace ts {
return visitEachChild(node, visitor, context);
}
function shouldConvertIterationStatementBody(node: IterationStatement): boolean {
interface ForStatementWithConvertibleInitializer extends ForStatement {
initializer: VariableDeclarationList;
}
interface ForStatementWithConvertibleCondition extends ForStatement {
condition: Expression;
}
interface ForStatementWithConvertibleIncrementor extends ForStatement {
incrementor: Expression;
}
function shouldConvertPartOfIterationStatement(node: Node) {
return (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ContainsCapturedBlockScopeBinding) !== 0;
}
function shouldConvertInitializerOfForStatement(node: IterationStatement): node is ForStatementWithConvertibleInitializer {
return isForStatement(node) && !!node.initializer && shouldConvertPartOfIterationStatement(node.initializer);
}
function shouldConvertConditionOfForStatement(node: IterationStatement): node is ForStatementWithConvertibleCondition {
return isForStatement(node) && !!node.condition && shouldConvertPartOfIterationStatement(node.condition);
}
function shouldConvertIncrementorOfForStatement(node: IterationStatement): node is ForStatementWithConvertibleIncrementor {
return isForStatement(node) && !!node.incrementor && shouldConvertPartOfIterationStatement(node.incrementor);
}
function shouldConvertIterationStatement(node: IterationStatement) {
return shouldConvertBodyOfIterationStatement(node)
|| shouldConvertInitializerOfForStatement(node);
}
function shouldConvertBodyOfIterationStatement(node: IterationStatement): boolean {
return (resolver.getNodeCheckFlags(node) & NodeCheckFlags.LoopWithCapturedBlockScopedBinding) !== 0;
}
@@ -2639,7 +2700,7 @@ namespace ts {
}
function convertIterationStatementBodyIfNecessary(node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convert?: LoopConverter): VisitResult<Statement> {
if (!shouldConvertIterationStatementBody(node)) {
if (!shouldConvertIterationStatement(node)) {
let saveAllowedNonLabeledJumps: Jump | undefined;
if (convertedLoopState) {
// we get here if we are trying to emit normal loop loop inside converted loop
@@ -2658,7 +2719,102 @@ namespace ts {
return result;
}
const functionName = createUniqueName("_loop");
const currentState = createConvertedLoopState(node);
const statements: Statement[] = [];
const outerConvertedLoopState = convertedLoopState;
convertedLoopState = currentState;
const initializerFunction = shouldConvertInitializerOfForStatement(node) ? createFunctionForInitializerOfForStatement(node, currentState) : undefined;
const bodyFunction = shouldConvertBodyOfIterationStatement(node) ? createFunctionForBodyOfIterationStatement(node, currentState, outerConvertedLoopState) : undefined;
convertedLoopState = outerConvertedLoopState;
if (initializerFunction) statements.push(initializerFunction.functionDeclaration);
if (bodyFunction) statements.push(bodyFunction.functionDeclaration);
addExtraDeclarationsForConvertedLoop(statements, currentState, outerConvertedLoopState);
if (initializerFunction) {
statements.push(generateCallToConvertedLoopInitializer(initializerFunction.functionName, initializerFunction.containsYield));
}
let loop: Statement;
if (bodyFunction) {
if (convert) {
loop = convert(node, outermostLabeledStatement, bodyFunction.part);
}
else {
const clone = convertIterationStatementCore(node, initializerFunction, createBlock(bodyFunction.part, /*multiLine*/ true));
aggregateTransformFlags(clone);
loop = restoreEnclosingLabel(clone, outermostLabeledStatement, convertedLoopState && resetLabel);
}
}
else {
const clone = convertIterationStatementCore(node, initializerFunction, visitNode(node.statement, visitor, isStatement, liftToBlock));
aggregateTransformFlags(clone);
loop = restoreEnclosingLabel(clone, outermostLabeledStatement, convertedLoopState && resetLabel);
}
statements.push(loop);
return statements;
}
function convertIterationStatementCore(node: IterationStatement, initializerFunction: IterationStatementPartFunction<VariableDeclarationList> | undefined, convertedLoopBody: Statement) {
switch (node.kind) {
case SyntaxKind.ForStatement: return convertForStatement(node as ForStatement, initializerFunction, convertedLoopBody);
case SyntaxKind.ForInStatement: return convertForInStatement(node as ForInStatement, convertedLoopBody);
case SyntaxKind.ForOfStatement: return convertForOfStatement(node as ForOfStatement, convertedLoopBody);
case SyntaxKind.DoStatement: return convertDoStatement(node as DoStatement, convertedLoopBody);
case SyntaxKind.WhileStatement: return convertWhileStatement(node as WhileStatement, convertedLoopBody);
default: return Debug.failBadSyntaxKind(node, "IterationStatement expected");
}
}
function convertForStatement(node: ForStatement, initializerFunction: IterationStatementPartFunction<VariableDeclarationList> | undefined, convertedLoopBody: Statement) {
const shouldConvertCondition = node.condition && shouldConvertPartOfIterationStatement(node.condition);
const shouldConvertIncrementor = shouldConvertCondition || node.incrementor && shouldConvertPartOfIterationStatement(node.incrementor);
return updateFor(
node,
visitNode(initializerFunction ? initializerFunction.part : node.initializer, visitor, isForInitializer),
visitNode(shouldConvertCondition ? undefined : node.condition, visitor, isExpression),
visitNode(shouldConvertIncrementor ? undefined : node.incrementor, visitor, isExpression),
convertedLoopBody
);
}
function convertForOfStatement(node: ForOfStatement, convertedLoopBody: Statement) {
return updateForOf(
node,
/*awaitModifier*/ undefined,
visitNode(node.initializer, visitor, isForInitializer),
visitNode(node.expression, visitor, isExpression),
convertedLoopBody);
}
function convertForInStatement(node: ForInStatement, convertedLoopBody: Statement) {
return updateForIn(
node,
visitNode(node.initializer, visitor, isForInitializer),
visitNode(node.expression, visitor, isExpression),
convertedLoopBody);
}
function convertDoStatement(node: DoStatement, convertedLoopBody: Statement) {
return updateDo(
node,
convertedLoopBody,
visitNode(node.expression, visitor, isExpression));
}
function convertWhileStatement(node: WhileStatement, convertedLoopBody: Statement) {
return updateWhile(
node,
visitNode(node.expression, visitor, isExpression),
convertedLoopBody);
}
function createConvertedLoopState(node: IterationStatement) {
let loopInitializer: VariableDeclarationList | undefined;
switch (node.kind) {
case SyntaxKind.ForStatement:
@@ -2670,74 +2826,311 @@ namespace ts {
}
break;
}
// variables that will be passed to the loop as parameters
const loopParameters: ParameterDeclaration[] = [];
// variables declared in the loop initializer that will be changed inside the loop
const loopOutParameters: LoopOutParameter[] = [];
if (loopInitializer && (getCombinedNodeFlags(loopInitializer) & NodeFlags.BlockScoped)) {
const hasCapturedBindingsInForInitializer = shouldConvertInitializerOfForStatement(node);
for (const decl of loopInitializer.declarations) {
processLoopVariableDeclaration(decl, loopParameters, loopOutParameters);
processLoopVariableDeclaration(node, decl, loopParameters, loopOutParameters, hasCapturedBindingsInForInitializer);
}
}
const outerConvertedLoopState = convertedLoopState;
convertedLoopState = { loopOutParameters };
if (outerConvertedLoopState) {
const currentState: ConvertedLoopState = { loopParameters, loopOutParameters };
if (convertedLoopState) {
// convertedOuterLoopState !== undefined means that this converted loop is nested in another converted loop.
// if outer converted loop has already accumulated some state - pass it through
if (outerConvertedLoopState.argumentsName) {
if (convertedLoopState.argumentsName) {
// outer loop has already used 'arguments' so we've already have some name to alias it
// use the same name in all nested loops
convertedLoopState.argumentsName = outerConvertedLoopState.argumentsName;
currentState.argumentsName = convertedLoopState.argumentsName;
}
if (outerConvertedLoopState.thisName) {
if (convertedLoopState.thisName) {
// outer loop has already used 'this' so we've already have some name to alias it
// use the same name in all nested loops
convertedLoopState.thisName = outerConvertedLoopState.thisName;
currentState.thisName = convertedLoopState.thisName;
}
if (outerConvertedLoopState.hoistedLocalVariables) {
if (convertedLoopState.hoistedLocalVariables) {
// we've already collected some non-block scoped variable declarations in enclosing loop
// use the same storage in nested loop
convertedLoopState.hoistedLocalVariables = outerConvertedLoopState.hoistedLocalVariables;
currentState.hoistedLocalVariables = convertedLoopState.hoistedLocalVariables;
}
}
return currentState;
}
function addExtraDeclarationsForConvertedLoop(statements: Statement[], state: ConvertedLoopState, outerState: ConvertedLoopState | undefined) {
let extraVariableDeclarations: VariableDeclaration[] | undefined;
// propagate state from the inner loop to the outer loop if necessary
if (state.argumentsName) {
// if alias for arguments is set
if (outerState) {
// pass it to outer converted loop
outerState.argumentsName = state.argumentsName;
}
else {
// this is top level converted loop and we need to create an alias for 'arguments' object
(extraVariableDeclarations || (extraVariableDeclarations = [])).push(
createVariableDeclaration(
state.argumentsName,
/*type*/ undefined,
createIdentifier("arguments")
)
);
}
}
if (state.thisName) {
// if alias for this is set
if (outerState) {
// pass it to outer converted loop
outerState.thisName = state.thisName;
}
else {
// this is top level converted loop so we need to create an alias for 'this' here
// NOTE:
// if converted loops were all nested in arrow function then we'll always emit '_this' so convertedLoopState.thisName will not be set.
// If it is set this means that all nested loops are not nested in arrow function and it is safe to capture 'this'.
(extraVariableDeclarations || (extraVariableDeclarations = [])).push(
createVariableDeclaration(
state.thisName,
/*type*/ undefined,
createIdentifier("this")
)
);
}
}
if (state.hoistedLocalVariables) {
// if hoistedLocalVariables !== undefined this means that we've possibly collected some variable declarations to be hoisted later
if (outerState) {
// pass them to outer converted loop
outerState.hoistedLocalVariables = state.hoistedLocalVariables;
}
else {
if (!extraVariableDeclarations) {
extraVariableDeclarations = [];
}
// hoist collected variable declarations
for (const identifier of state.hoistedLocalVariables) {
extraVariableDeclarations.push(createVariableDeclaration(identifier));
}
}
}
// add extra variables to hold out parameters if necessary
if (state.loopOutParameters.length) {
if (!extraVariableDeclarations) {
extraVariableDeclarations = [];
}
for (const outParam of state.loopOutParameters) {
extraVariableDeclarations.push(createVariableDeclaration(outParam.outParamName));
}
}
if (state.conditionVariable) {
if (!extraVariableDeclarations) {
extraVariableDeclarations = [];
}
extraVariableDeclarations.push(createVariableDeclaration(state.conditionVariable, /*type*/ undefined, createFalse()));
}
// create variable statement to hold all introduced variable declarations
if (extraVariableDeclarations) {
statements.push(createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(extraVariableDeclarations)
));
}
}
interface IterationStatementPartFunction<T> {
functionName: Identifier;
functionDeclaration: Statement;
containsYield: boolean;
part: T;
}
function createOutVariable(p: LoopOutParameter) {
return createVariableDeclaration(p.originalName, /*type*/ undefined, p.outParamName);
}
/**
* Creates a `_loop_init` function for a `ForStatement` with a block-scoped initializer
* that is captured in a closure inside of the initializer. The `_loop_init` function is
* used to preserve the per-iteration environment semantics of
* [13.7.4.8 RS: ForBodyEvaluation](https://tc39.github.io/ecma262/#sec-forbodyevaluation).
*/
function createFunctionForInitializerOfForStatement(node: ForStatementWithConvertibleInitializer, currentState: ConvertedLoopState): IterationStatementPartFunction<VariableDeclarationList> {
const functionName = createUniqueName("_loop_init");
const containsYield = (node.initializer.transformFlags & TransformFlags.ContainsYield) !== 0;
let emitFlags = EmitFlags.None;
if (currentState.containsLexicalThis) emitFlags |= EmitFlags.CapturesThis;
if (containsYield && hierarchyFacts & HierarchyFacts.AsyncFunctionBody) emitFlags |= EmitFlags.AsyncFunctionBody;
const statements: Statement[] = [];
statements.push(createVariableStatement(/*modifiers*/ undefined, node.initializer));
copyOutParameters(currentState.loopOutParameters, LoopOutParameterFlags.Initializer, CopyDirection.ToOutParameter, statements);
// This transforms the following ES2015 syntax:
//
// for (let i = (setImmediate(() => console.log(i)), 0); i < 2; i++) {
// // loop body
// }
//
// Into the following ES5 syntax:
//
// var _loop_init_1 = function () {
// var i = (setImmediate(() => console.log(i)), 0);
// out_i_1 = i;
// };
// var out_i_1;
// _loop_init_1();
// for (var i = out_i_1; i < 2; i++) {
// // loop body
// }
//
// Which prevents mutations to `i` in the per-iteration environment of the body
// from affecting the initial value for `i` outside of the per-iteration environment.
const functionDeclaration = createVariableStatement(
/*modifiers*/ undefined,
setEmitFlags(
createVariableDeclarationList([
createVariableDeclaration(
functionName,
/*type*/ undefined,
setEmitFlags(
createFunctionExpression(
/*modifiers*/ undefined,
containsYield ? createToken(SyntaxKind.AsteriskToken) : undefined,
/*name*/ undefined,
/*typeParameters*/ undefined,
/*parameters*/ undefined,
/*type*/ undefined,
visitNode(
createBlock(statements, /*multiLine*/ true),
visitor,
isBlock
)
),
emitFlags
)
)
]),
EmitFlags.NoHoisting
)
);
const part = createVariableDeclarationList(map(currentState.loopOutParameters, createOutVariable));
return { functionName, containsYield, functionDeclaration, part };
}
/**
* Creates a `_loop` function for an `IterationStatement` with a block-scoped initializer
* that is captured in a closure inside of the loop body. The `_loop` function is used to
* preserve the per-iteration environment semantics of
* [13.7.4.8 RS: ForBodyEvaluation](https://tc39.github.io/ecma262/#sec-forbodyevaluation).
*/
function createFunctionForBodyOfIterationStatement(node: IterationStatement, currentState: ConvertedLoopState, outerState: ConvertedLoopState | undefined): IterationStatementPartFunction<Statement[]> {
const functionName = createUniqueName("_loop");
startLexicalEnvironment();
let loopBody = visitNode(node.statement, visitor, isStatement, liftToBlock);
const statement = visitNode(node.statement, visitor, isStatement, liftToBlock);
const lexicalEnvironment = endLexicalEnvironment();
const currentState = convertedLoopState;
convertedLoopState = outerConvertedLoopState;
const statements: Statement[] = [];
if (shouldConvertConditionOfForStatement(node) || shouldConvertIncrementorOfForStatement(node)) {
// If a block-scoped variable declared in the initializer of `node` is captured in
// the condition or incrementor, we must move the condition and incrementor into
// the body of the for loop.
//
// This transforms the following ES2015 syntax:
//
// for (let i = 0; setImmediate(() => console.log(i)), i < 2; setImmediate(() => console.log(i)), i++) {
// // loop body
// }
//
// Into the following ES5 syntax:
//
// var _loop_1 = function (i) {
// if (inc_1)
// setImmediate(() => console.log(i)), i++;
// else
// inc_1 = true;
// if (!(setImmediate(() => console.log(i)), i < 2))
// return out_i_1 = i, "break";
// // loop body
// out_i_1 = i;
// }
// var out_i_1, inc_1 = false;
// for (var i = 0;;) {
// var state_1 = _loop_1(i);
// i = out_i_1;
// if (state_1 === "break")
// break;
// }
//
// Which prevents mutations to `i` in the per-iteration environment of the body
// from affecting the value of `i` in the previous per-iteration environment.
//
// Note that the incrementor of a `for` loop is evaluated in a *new* per-iteration
// environment that is carried over to the next iteration of the loop. As a result,
// we must indicate whether this is the first evaluation of the loop body so that
// we only evaluate the incrementor on subsequent evaluations.
if (loopOutParameters.length || lexicalEnvironment) {
const statements = isBlock(loopBody) ? loopBody.statements.slice() : [loopBody];
if (loopOutParameters.length) {
copyOutParameters(loopOutParameters, CopyDirection.ToOutParameter, statements);
currentState.conditionVariable = createUniqueName("inc");
statements.push(createIf(
currentState.conditionVariable,
createStatement(visitNode(node.incrementor, visitor, isExpression)),
createStatement(createAssignment(currentState.conditionVariable, createTrue()))
));
if (shouldConvertConditionOfForStatement(node)) {
statements.push(createIf(
createPrefix(SyntaxKind.ExclamationToken, visitNode(node.condition, visitor, isExpression)),
visitNode(createBreak(), visitor, isStatement)
));
}
addStatementsAfterPrologue(statements, lexicalEnvironment);
loopBody = createBlock(statements, /*multiline*/ true);
}
if (isBlock(loopBody)) {
loopBody.multiLine = true;
if (isBlock(statement)) {
addRange(statements, statement.statements);
}
else {
loopBody = createBlock([loopBody], /*multiline*/ true);
statements.push(statement);
}
copyOutParameters(currentState.loopOutParameters, LoopOutParameterFlags.Body, CopyDirection.ToOutParameter, statements);
addStatementsAfterPrologue(statements, lexicalEnvironment);
const loopBody = createBlock(statements, /*multiLine*/ true);
if (isBlock(statement)) setOriginalNode(loopBody, statement);
const containsYield = (node.statement.transformFlags & TransformFlags.ContainsYield) !== 0;
const isAsyncBlockContainingAwait = containsYield && (hierarchyFacts & HierarchyFacts.AsyncFunctionBody) !== 0;
let loopBodyFlags: EmitFlags = 0;
if (currentState.containsLexicalThis) {
loopBodyFlags |= EmitFlags.CapturesThis;
}
let emitFlags: EmitFlags = 0;
if (currentState.containsLexicalThis) emitFlags |= EmitFlags.CapturesThis;
if (containsYield && (hierarchyFacts & HierarchyFacts.AsyncFunctionBody) !== 0) emitFlags |= EmitFlags.AsyncFunctionBody;
if (isAsyncBlockContainingAwait) {
loopBodyFlags |= EmitFlags.AsyncFunctionBody;
}
// This transforms the following ES2015 syntax (in addition to other variations):
//
// for (let i = 0; i < 2; i++) {
// setImmediate(() => console.log(i));
// }
//
// Into the following ES5 syntax:
//
// var _loop_1 = function (i) {
// setImmediate(() => console.log(i));
// };
// for (var i = 0; i < 2; i++) {
// _loop_1(i);
// }
const convertedLoopVariable =
const functionDeclaration =
createVariableStatement(
/*modifiers*/ undefined,
setEmitFlags(
@@ -2752,11 +3145,11 @@ namespace ts {
containsYield ? createToken(SyntaxKind.AsteriskToken) : undefined,
/*name*/ undefined,
/*typeParameters*/ undefined,
loopParameters,
currentState.loopParameters,
/*type*/ undefined,
<Block>loopBody
loopBody
),
loopBodyFlags
emitFlags
)
)
]
@@ -2765,106 +3158,8 @@ namespace ts {
)
);
const statements: Statement[] = [convertedLoopVariable];
let extraVariableDeclarations: VariableDeclaration[] | undefined;
// propagate state from the inner loop to the outer loop if necessary
if (currentState.argumentsName) {
// if alias for arguments is set
if (outerConvertedLoopState) {
// pass it to outer converted loop
outerConvertedLoopState.argumentsName = currentState.argumentsName;
}
else {
// this is top level converted loop and we need to create an alias for 'arguments' object
(extraVariableDeclarations || (extraVariableDeclarations = [])).push(
createVariableDeclaration(
currentState.argumentsName,
/*type*/ undefined,
createIdentifier("arguments")
)
);
}
}
if (currentState.thisName) {
// if alias for this is set
if (outerConvertedLoopState) {
// pass it to outer converted loop
outerConvertedLoopState.thisName = currentState.thisName;
}
else {
// this is top level converted loop so we need to create an alias for 'this' here
// NOTE:
// if converted loops were all nested in arrow function then we'll always emit '_this' so convertedLoopState.thisName will not be set.
// If it is set this means that all nested loops are not nested in arrow function and it is safe to capture 'this'.
(extraVariableDeclarations || (extraVariableDeclarations = [])).push(
createVariableDeclaration(
currentState.thisName,
/*type*/ undefined,
createIdentifier("this")
)
);
}
}
if (currentState.hoistedLocalVariables) {
// if hoistedLocalVariables !== undefined this means that we've possibly collected some variable declarations to be hoisted later
if (outerConvertedLoopState) {
// pass them to outer converted loop
outerConvertedLoopState.hoistedLocalVariables = currentState.hoistedLocalVariables;
}
else {
if (!extraVariableDeclarations) {
extraVariableDeclarations = [];
}
// hoist collected variable declarations
for (const identifier of currentState.hoistedLocalVariables) {
extraVariableDeclarations.push(createVariableDeclaration(identifier));
}
}
}
// add extra variables to hold out parameters if necessary
if (loopOutParameters.length) {
if (!extraVariableDeclarations) {
extraVariableDeclarations = [];
}
for (const outParam of loopOutParameters) {
extraVariableDeclarations.push(createVariableDeclaration(outParam.outParamName));
}
}
// create variable statement to hold all introduced variable declarations
if (extraVariableDeclarations) {
statements.push(createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(extraVariableDeclarations)
));
}
const convertedLoopBodyStatements = generateCallToConvertedLoop(functionName, loopParameters, currentState, containsYield);
let loop: Statement;
if (convert) {
loop = convert(node, outermostLabeledStatement, convertedLoopBodyStatements);
}
else {
let clone = getMutableClone(node);
// clean statement part
clone.statement = undefined!;
// visit childnodes to transform initializer/condition/incrementor parts
clone = visitEachChild(clone, visitor, context);
// set loop statement
clone.statement = createBlock(convertedLoopBodyStatements, /*multiline*/ true);
// reset and re-aggregate the transform flags
clone.transformFlags = 0;
aggregateTransformFlags(clone);
loop = restoreEnclosingLabel(clone, outermostLabeledStatement, convertedLoopState && resetLabel);
}
statements.push(loop);
return statements;
const part = generateCallToConvertedLoop(functionName, currentState, outerState, containsYield);
return { functionName, containsYield, functionDeclaration, part };
}
function copyOutParameter(outParam: LoopOutParameter, copyDirection: CopyDirection): BinaryExpression {
@@ -2873,14 +3168,26 @@ namespace ts {
return createBinary(target, SyntaxKind.EqualsToken, source);
}
function copyOutParameters(outParams: LoopOutParameter[], copyDirection: CopyDirection, statements: Statement[]): void {
function copyOutParameters(outParams: LoopOutParameter[], partFlags: LoopOutParameterFlags, copyDirection: CopyDirection, statements: Statement[]): void {
for (const outParam of outParams) {
statements.push(createExpressionStatement(copyOutParameter(outParam, copyDirection)));
if (outParam.flags & partFlags) {
statements.push(createExpressionStatement(copyOutParameter(outParam, copyDirection)));
}
}
}
function generateCallToConvertedLoop(loopFunctionExpressionName: Identifier, parameters: ParameterDeclaration[], state: ConvertedLoopState, isAsyncBlockContainingAwait: boolean): Statement[] {
const outerConvertedLoopState = convertedLoopState;
function generateCallToConvertedLoopInitializer(initFunctionExpressionName: Identifier, containsYield: boolean): Statement {
const call = createCall(initFunctionExpressionName, /*typeArguments*/ undefined, []);
const callResult = containsYield
? createYield(
createToken(SyntaxKind.AsteriskToken),
setEmitFlags(call, EmitFlags.Iterator)
)
: call;
return createStatement(callResult);
}
function generateCallToConvertedLoop(loopFunctionExpressionName: Identifier, state: ConvertedLoopState, outerState: ConvertedLoopState | undefined, containsYield: boolean): Statement[] {
const statements: Statement[] = [];
// loop is considered simple if it does not have any return statements or break\continue that transfer control outside of the loop
@@ -2891,8 +3198,8 @@ namespace ts {
!state.labeledNonLocalBreaks &&
!state.labeledNonLocalContinues;
const call = createCall(loopFunctionExpressionName, /*typeArguments*/ undefined, map(parameters, p => <Identifier>p.name));
const callResult = isAsyncBlockContainingAwait
const call = createCall(loopFunctionExpressionName, /*typeArguments*/ undefined, map(state.loopParameters, p => <Identifier>p.name));
const callResult = containsYield
? createYield(
createToken(SyntaxKind.AsteriskToken),
setEmitFlags(call, EmitFlags.Iterator)
@@ -2900,7 +3207,7 @@ namespace ts {
: call;
if (isSimpleLoop) {
statements.push(createExpressionStatement(callResult));
copyOutParameters(state.loopOutParameters!, CopyDirection.ToOriginal, statements);
copyOutParameters(state.loopOutParameters, LoopOutParameterFlags.Body, CopyDirection.ToOriginal, statements);
}
else {
const loopResultName = createUniqueName("state");
@@ -2911,12 +3218,12 @@ namespace ts {
)
);
statements.push(stateVariable);
copyOutParameters(state.loopOutParameters!, CopyDirection.ToOriginal, statements);
copyOutParameters(state.loopOutParameters, LoopOutParameterFlags.Body, CopyDirection.ToOriginal, statements);
if (state.nonLocalJumps! & Jump.Return) {
let returnStatement: ReturnStatement;
if (outerConvertedLoopState) {
outerConvertedLoopState.nonLocalJumps! |= Jump.Return;
if (outerState) {
outerState.nonLocalJumps! |= Jump.Return;
returnStatement = createReturn(loopResultName);
}
else {
@@ -2949,8 +3256,8 @@ namespace ts {
if (state.labeledNonLocalBreaks || state.labeledNonLocalContinues) {
const caseClauses: CaseClause[] = [];
processLabeledJumps(state.labeledNonLocalBreaks!, /*isBreak*/ true, loopResultName, outerConvertedLoopState, caseClauses);
processLabeledJumps(state.labeledNonLocalContinues!, /*isBreak*/ false, loopResultName, outerConvertedLoopState, caseClauses);
processLabeledJumps(state.labeledNonLocalBreaks!, /*isBreak*/ true, loopResultName, outerState, caseClauses);
processLabeledJumps(state.labeledNonLocalContinues!, /*isBreak*/ false, loopResultName, outerState, caseClauses);
statements.push(
createSwitch(
loopResultName,
@@ -2998,20 +3305,28 @@ namespace ts {
});
}
function processLoopVariableDeclaration(decl: VariableDeclaration | BindingElement, loopParameters: ParameterDeclaration[], loopOutParameters: LoopOutParameter[]) {
function processLoopVariableDeclaration(container: IterationStatement, decl: VariableDeclaration | BindingElement, loopParameters: ParameterDeclaration[], loopOutParameters: LoopOutParameter[], hasCapturedBindingsInForInitializer: boolean) {
const name = decl.name;
if (isBindingPattern(name)) {
for (const element of name.elements) {
if (!isOmittedExpression(element)) {
processLoopVariableDeclaration(element, loopParameters, loopOutParameters);
processLoopVariableDeclaration(container, element, loopParameters, loopOutParameters, hasCapturedBindingsInForInitializer);
}
}
}
else {
loopParameters.push(createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, name));
if (resolver.getNodeCheckFlags(decl) & NodeCheckFlags.NeedsLoopOutParameter) {
const checkFlags = resolver.getNodeCheckFlags(decl);
if (checkFlags & NodeCheckFlags.NeedsLoopOutParameter || hasCapturedBindingsInForInitializer) {
const outParamName = createUniqueName("out_" + idText(name));
loopOutParameters.push({ originalName: name, outParamName });
let flags: LoopOutParameterFlags = 0;
if (checkFlags & NodeCheckFlags.NeedsLoopOutParameter) {
flags |= LoopOutParameterFlags.Body;
}
if (isForStatement(container) && container.initializer && resolver.isBindingCapturedByNode(container.initializer, decl)) {
flags |= LoopOutParameterFlags.Initializer;
}
loopOutParameters.push({ flags, originalName: name, outParamName });
}
}
}
@@ -3432,7 +3747,7 @@ namespace ts {
function visitCallExpressionWithPotentialCapturedThisAssignment(node: CallExpression, assignToCapturedThis: boolean): CallExpression | BinaryExpression {
// We are here either because SuperKeyword was used somewhere in the expression, or
// because we contain a SpreadElementExpression.
if (node.transformFlags & TransformFlags.ContainsSpread ||
if (node.transformFlags & TransformFlags.ContainsRestOrSpread ||
node.expression.kind === SyntaxKind.SuperKeyword ||
isSuperProperty(skipOuterExpressions(node.expression))) {
@@ -3442,7 +3757,7 @@ namespace ts {
}
let resultingCall: CallExpression | BinaryExpression;
if (node.transformFlags & TransformFlags.ContainsSpread) {
if (node.transformFlags & TransformFlags.ContainsRestOrSpread) {
// [source]
// f(...a, b)
// x.m(...a, b)
@@ -3505,7 +3820,7 @@ namespace ts {
* @param node A NewExpression node.
*/
function visitNewExpression(node: NewExpression): LeftHandSideExpression {
if (node.transformFlags & TransformFlags.ContainsSpread) {
if (node.transformFlags & TransformFlags.ContainsRestOrSpread) {
// We are here because we contain a SpreadElementExpression.
// [source]
// new C(...a)
@@ -4079,7 +4394,7 @@ namespace ts {
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
};
return function (d, b) {
extendStatics(d, b);
+144 -14
View File
@@ -32,6 +32,15 @@ namespace ts {
let enclosingFunctionParameterNames: UnderscoreEscapedMap<true>;
/**
* Keeps track of property names accessed on super (`super.x`) within async functions.
*/
let capturedSuperProperties: UnderscoreEscapedMap<true>;
/** Whether the async function contains an element access on super (`super[x]`). */
let hasSuperElementAccess: boolean;
/** A set of node IDs for generated super accessors (variable statements). */
const substitutedSuperAccessors: boolean[] = [];
// Save the previous transformation hooks.
const previousOnEmitNode = context.onEmitNode;
const previousOnSubstituteNode = context.onSubstituteNode;
@@ -56,7 +65,6 @@ namespace ts {
if ((node.transformFlags & TransformFlags.ContainsES2017) === 0) {
return node;
}
switch (node.kind) {
case SyntaxKind.AsyncKeyword:
// ES2017 async modifier should be elided for targets < ES2017
@@ -77,6 +85,18 @@ namespace ts {
case SyntaxKind.ArrowFunction:
return visitArrowFunction(<ArrowFunction>node);
case SyntaxKind.PropertyAccessExpression:
if (capturedSuperProperties && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.SuperKeyword) {
capturedSuperProperties.set(node.name.escapedText, true);
}
return visitEachChild(node, visitor, context);
case SyntaxKind.ElementAccessExpression:
if (capturedSuperProperties && (<ElementAccessExpression>node).expression.kind === SyntaxKind.SuperKeyword) {
hasSuperElementAccess = true;
}
return visitEachChild(node, visitor, context);
default:
return visitEachChild(node, visitor, context);
}
@@ -398,6 +418,11 @@ namespace ts {
recordDeclarationName(parameter, enclosingFunctionParameterNames);
}
const savedCapturedSuperProperties = capturedSuperProperties;
const savedHasSuperElementAccess = hasSuperElementAccess;
capturedSuperProperties = createUnderscoreEscapedMap<true>();
hasSuperElementAccess = false;
let result: ConciseBody;
if (!isArrowFunction) {
const statements: Statement[] = [];
@@ -415,18 +440,26 @@ namespace ts {
addStatementsAfterPrologue(statements, endLexicalEnvironment());
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
// This step isn't needed if we eventually transform this to ES5.
const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 && resolver.getNodeCheckFlags(node) & (NodeCheckFlags.AsyncMethodWithSuperBinding | NodeCheckFlags.AsyncMethodWithSuper);
if (emitSuperHelpers) {
enableSubstitutionForAsyncMethodsWithSuper();
const variableStatement = createSuperAccessVariableStatement(resolver, node, capturedSuperProperties);
substitutedSuperAccessors[getNodeId(variableStatement)] = true;
addStatementsAfterPrologue(statements, [variableStatement]);
}
const block = createBlock(statements, /*multiLine*/ true);
setTextRange(block, node.body);
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
// This step isn't needed if we eventually transform this to ES5.
if (languageVersion >= ScriptTarget.ES2015) {
if (emitSuperHelpers && hasSuperElementAccess) {
// Emit helpers for super element access expressions (`super[x]`).
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) {
enableSubstitutionForAsyncMethodsWithSuper();
addEmitHelper(block, advancedAsyncSuperHelper);
}
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) {
enableSubstitutionForAsyncMethodsWithSuper();
addEmitHelper(block, asyncSuperHelper);
}
}
@@ -452,6 +485,8 @@ namespace ts {
}
enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames;
capturedSuperProperties = savedCapturedSuperProperties;
hasSuperElementAccess = savedHasSuperElementAccess;
return result;
}
@@ -493,6 +528,8 @@ namespace ts {
context.enableEmitNotification(SyntaxKind.GetAccessor);
context.enableEmitNotification(SyntaxKind.SetAccessor);
context.enableEmitNotification(SyntaxKind.Constructor);
// We need to be notified when entering the generated accessor arrow functions.
context.enableEmitNotification(SyntaxKind.VariableStatement);
}
}
@@ -516,6 +553,14 @@ namespace ts {
return;
}
}
// Disable substitution in the generated super accessor itself.
else if (enabledSubstitutions && substitutedSuperAccessors[getNodeId(node)]) {
const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags;
enclosingSuperContainerFlags = 0;
previousOnEmitNode(hint, node, emitCallback);
enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags;
return;
}
previousOnEmitNode(hint, node, emitCallback);
}
@@ -548,8 +593,10 @@ namespace ts {
function substitutePropertyAccessExpression(node: PropertyAccessExpression) {
if (node.expression.kind === SyntaxKind.SuperKeyword) {
return createSuperAccessInAsyncMethod(
createLiteral(idText(node.name)),
return setTextRange(
createPropertyAccess(
createFileLevelUniqueName("_super"),
node.name),
node
);
}
@@ -558,7 +605,7 @@ namespace ts {
function substituteElementAccessExpression(node: ElementAccessExpression) {
if (node.expression.kind === SyntaxKind.SuperKeyword) {
return createSuperAccessInAsyncMethod(
return createSuperElementAccessInAsyncMethod(
node.argumentExpression,
node
);
@@ -593,12 +640,12 @@ namespace ts {
|| kind === SyntaxKind.SetAccessor;
}
function createSuperAccessInAsyncMethod(argumentExpression: Expression, location: TextRange): LeftHandSideExpression {
function createSuperElementAccessInAsyncMethod(argumentExpression: Expression, location: TextRange): LeftHandSideExpression {
if (enclosingSuperContainerFlags & NodeCheckFlags.AsyncMethodWithSuperBinding) {
return setTextRange(
createPropertyAccess(
createCall(
createFileLevelUniqueName("_super"),
createFileLevelUniqueName("_superIndex"),
/*typeArguments*/ undefined,
[argumentExpression]
),
@@ -610,7 +657,7 @@ namespace ts {
else {
return setTextRange(
createCall(
createFileLevelUniqueName("_super"),
createFileLevelUniqueName("_superIndex"),
/*typeArguments*/ undefined,
[argumentExpression]
),
@@ -620,6 +667,89 @@ namespace ts {
}
}
/** Creates a variable named `_super` with accessor properties for the given property names. */
export function createSuperAccessVariableStatement(resolver: EmitResolver, node: FunctionLikeDeclaration, names: UnderscoreEscapedMap<true>) {
// Create a variable declaration with a getter/setter (if binding) definition for each name:
// const _super = Object.create(null, { x: { get: () => super.x, set: (v) => super.x = v }, ... });
const hasBinding = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) !== 0;
const accessors: PropertyAssignment[] = [];
names.forEach((_, key) => {
const name = unescapeLeadingUnderscores(key);
const getterAndSetter: PropertyAssignment[] = [];
getterAndSetter.push(createPropertyAssignment(
"get",
createArrowFunction(
/* modifiers */ undefined,
/* typeParameters */ undefined,
/* parameters */ [],
/* type */ undefined,
/* equalsGreaterThanToken */ undefined,
createPropertyAccess(
createSuper(),
name
)
)
));
if (hasBinding) {
getterAndSetter.push(
createPropertyAssignment(
"set",
createArrowFunction(
/* modifiers */ undefined,
/* typeParameters */ undefined,
/* parameters */ [
createParameter(
/* decorators */ undefined,
/* modifiers */ undefined,
/* dotDotDotToken */ undefined,
"v",
/* questionToken */ undefined,
/* type */ undefined,
/* initializer */ undefined
)
],
/* type */ undefined,
/* equalsGreaterThanToken */ undefined,
createAssignment(
createPropertyAccess(
createSuper(),
name),
createIdentifier("v")
)
)
)
);
}
accessors.push(
createPropertyAssignment(
name,
createObjectLiteral(getterAndSetter),
)
);
});
return createVariableStatement(
/* modifiers */ undefined,
createVariableDeclarationList(
[
createVariableDeclaration(
createFileLevelUniqueName("_super"),
/* type */ undefined,
createCall(
createPropertyAccess(
createIdentifier("Object"),
"create"
),
/* typeArguments */ undefined,
[
createNull(),
createObjectLiteral(accessors, /* multiline */ true)
]
)
)
],
NodeFlags.Const));
}
const awaiterHelper: EmitHelper = {
name: "typescript:awaiter",
scoped: false,
@@ -667,14 +797,14 @@ namespace ts {
name: "typescript:async-super",
scoped: true,
text: helperString`
const ${"_super"} = name => super[name];`
const ${"_superIndex"} = name => super[name];`
};
export const advancedAsyncSuperHelper: EmitHelper = {
name: "typescript:advanced-async-super",
scoped: true,
text: helperString`
const ${"_super"} = (function (geti, seti) {
const ${"_superIndex"} = (function (geti, seti) {
const cache = Object.create(null);
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
})(name => super[name], (name, value) => super[name] = value);`
+77 -33
View File
@@ -26,6 +26,13 @@ namespace ts {
let enclosingFunctionFlags: FunctionFlags;
let enclosingSuperContainerFlags: NodeCheckFlags = 0;
/** Keeps track of property names accessed on super (`super.x`) within async functions. */
let capturedSuperProperties: UnderscoreEscapedMap<true>;
/** Whether the async function contains an element access on super (`super[x]`). */
let hasSuperElementAccess: boolean;
/** A set of node IDs for generated super accessors. */
const substitutedSuperAccessors: boolean[] = [];
return chainBundle(transformSourceFile);
function transformSourceFile(node: SourceFile) {
@@ -57,7 +64,6 @@ namespace ts {
if ((node.transformFlags & TransformFlags.ContainsESNext) === 0) {
return node;
}
switch (node.kind) {
case SyntaxKind.AwaitExpression:
return visitAwaitExpression(node as AwaitExpression);
@@ -101,6 +107,16 @@ namespace ts {
return visitParenthesizedExpression(node as ParenthesizedExpression, noDestructuringValue);
case SyntaxKind.CatchClause:
return visitCatchClause(node as CatchClause);
case SyntaxKind.PropertyAccessExpression:
if (capturedSuperProperties && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.SuperKeyword) {
capturedSuperProperties.set(node.name.escapedText, true);
}
return visitEachChild(node, visitor, context);
case SyntaxKind.ElementAccessExpression:
if (capturedSuperProperties && (<ElementAccessExpression>node).expression.kind === SyntaxKind.SuperKeyword) {
hasSuperElementAccess = true;
}
return visitEachChild(node, visitor, context);
default:
return visitEachChild(node, visitor, context);
}
@@ -210,7 +226,7 @@ namespace ts {
}
function visitObjectLiteralExpression(node: ObjectLiteralExpression): Expression {
if (node.transformFlags & TransformFlags.ContainsObjectSpread) {
if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
// spread elements emit like so:
// non-spread elements are chunked together into object literals, and then all are passed to __assign:
// { a, ...o, b } => __assign({a}, o, {b});
@@ -250,7 +266,7 @@ namespace ts {
* @param node A BinaryExpression node.
*/
function visitBinaryExpression(node: BinaryExpression, noDestructuringValue: boolean): Expression {
if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsObjectRest) {
if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
return flattenDestructuringAssignment(
node,
visitor,
@@ -276,7 +292,7 @@ namespace ts {
*/
function visitVariableDeclaration(node: VariableDeclaration): VisitResult<VariableDeclaration> {
// If we are here it is because the name contains a binding pattern with a rest somewhere in it.
if (isBindingPattern(node.name) && node.name.transformFlags & TransformFlags.ContainsObjectRest) {
if (isBindingPattern(node.name) && node.name.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
return flattenDestructuringBinding(
node,
visitor,
@@ -307,7 +323,7 @@ namespace ts {
* @param node A ForOfStatement.
*/
function visitForOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement | undefined): VisitResult<Statement> {
if (node.initializer.transformFlags & TransformFlags.ContainsObjectRest) {
if (node.initializer.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
node = transformForOfStatementWithObjectRest(node);
}
if (node.awaitModifier) {
@@ -499,7 +515,7 @@ namespace ts {
}
function visitParameter(node: ParameterDeclaration): ParameterDeclaration {
if (node.transformFlags & TransformFlags.ContainsObjectRest) {
if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
// Binding patterns are converted into a generated name and are
// evaluated inside the function body.
return updateParameter(
@@ -655,41 +671,57 @@ namespace ts {
const statementOffset = addPrologue(statements, node.body!.statements, /*ensureUseStrict*/ false, visitor);
appendObjectRestAssignmentsIfNeeded(statements, node);
statements.push(
createReturn(
createAsyncGeneratorHelper(
context,
createFunctionExpression(
/*modifiers*/ undefined,
createToken(SyntaxKind.AsteriskToken),
node.name && getGeneratedNameForNode(node.name),
/*typeParameters*/ undefined,
/*parameters*/ [],
/*type*/ undefined,
updateBlock(
node.body!,
visitLexicalEnvironment(node.body!.statements, visitor, context, statementOffset)
)
const savedCapturedSuperProperties = capturedSuperProperties;
const savedHasSuperElementAccess = hasSuperElementAccess;
capturedSuperProperties = createUnderscoreEscapedMap<true>();
hasSuperElementAccess = false;
const returnStatement = createReturn(
createAsyncGeneratorHelper(
context,
createFunctionExpression(
/*modifiers*/ undefined,
createToken(SyntaxKind.AsteriskToken),
node.name && getGeneratedNameForNode(node.name),
/*typeParameters*/ undefined,
/*parameters*/ [],
/*type*/ undefined,
updateBlock(
node.body!,
visitLexicalEnvironment(node.body!.statements, visitor, context, statementOffset)
)
)
)
);
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
// This step isn't needed if we eventually transform this to ES5.
const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 && resolver.getNodeCheckFlags(node) & (NodeCheckFlags.AsyncMethodWithSuperBinding | NodeCheckFlags.AsyncMethodWithSuper);
if (emitSuperHelpers) {
enableSubstitutionForAsyncMethodsWithSuper();
const variableStatement = createSuperAccessVariableStatement(resolver, node, capturedSuperProperties);
substitutedSuperAccessors[getNodeId(variableStatement)] = true;
addStatementsAfterPrologue(statements, [variableStatement]);
}
statements.push(returnStatement);
addStatementsAfterPrologue(statements, endLexicalEnvironment());
const block = updateBlock(node.body!, statements);
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
// This step isn't needed if we eventually transform this to ES5.
if (languageVersion >= ScriptTarget.ES2015) {
if (emitSuperHelpers && hasSuperElementAccess) {
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) {
enableSubstitutionForAsyncMethodsWithSuper();
addEmitHelper(block, advancedAsyncSuperHelper);
}
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) {
enableSubstitutionForAsyncMethodsWithSuper();
addEmitHelper(block, asyncSuperHelper);
}
}
capturedSuperProperties = savedCapturedSuperProperties;
hasSuperElementAccess = savedHasSuperElementAccess;
return block;
}
@@ -716,7 +748,7 @@ namespace ts {
function appendObjectRestAssignmentsIfNeeded(statements: Statement[] | undefined, node: FunctionLikeDeclaration): Statement[] | undefined {
for (const parameter of node.parameters) {
if (parameter.transformFlags & TransformFlags.ContainsObjectRest) {
if (parameter.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
const temp = getGeneratedNameForNode(parameter);
const declarations = flattenDestructuringBinding(
parameter,
@@ -758,6 +790,8 @@ namespace ts {
context.enableEmitNotification(SyntaxKind.GetAccessor);
context.enableEmitNotification(SyntaxKind.SetAccessor);
context.enableEmitNotification(SyntaxKind.Constructor);
// We need to be notified when entering the generated accessor arrow functions.
context.enableEmitNotification(SyntaxKind.VariableStatement);
}
}
@@ -781,6 +815,14 @@ namespace ts {
return;
}
}
// Disable substitution in the generated super accessor itself.
else if (enabledSubstitutions && substitutedSuperAccessors[getNodeId(node)]) {
const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags;
enclosingSuperContainerFlags = 0 as NodeCheckFlags;
previousOnEmitNode(hint, node, emitCallback);
enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags;
return;
}
previousOnEmitNode(hint, node, emitCallback);
}
@@ -813,8 +855,10 @@ namespace ts {
function substitutePropertyAccessExpression(node: PropertyAccessExpression) {
if (node.expression.kind === SyntaxKind.SuperKeyword) {
return createSuperAccessInAsyncMethod(
createLiteral(idText(node.name)),
return setTextRange(
createPropertyAccess(
createFileLevelUniqueName("_super"),
node.name),
node
);
}
@@ -823,7 +867,7 @@ namespace ts {
function substituteElementAccessExpression(node: ElementAccessExpression) {
if (node.expression.kind === SyntaxKind.SuperKeyword) {
return createSuperAccessInAsyncMethod(
return createSuperElementAccessInAsyncMethod(
node.argumentExpression,
node
);
@@ -858,12 +902,12 @@ namespace ts {
|| kind === SyntaxKind.SetAccessor;
}
function createSuperAccessInAsyncMethod(argumentExpression: Expression, location: TextRange): LeftHandSideExpression {
function createSuperElementAccessInAsyncMethod(argumentExpression: Expression, location: TextRange): LeftHandSideExpression {
if (enclosingSuperContainerFlags & NodeCheckFlags.AsyncMethodWithSuperBinding) {
return setTextRange(
createPropertyAccess(
createCall(
createIdentifier("_super"),
createIdentifier("_superIndex"),
/*typeArguments*/ undefined,
[argumentExpression]
),
@@ -875,7 +919,7 @@ namespace ts {
else {
return setTextRange(
createCall(
createIdentifier("_super"),
createIdentifier("_superIndex"),
/*typeArguments*/ undefined,
[argumentExpression]
),
+40 -5
View File
@@ -973,9 +973,11 @@ namespace ts {
// Check if we have property assignment inside class declaration.
// If there is a property assignment, we need to emit constructor whether users define it or not
// If there is no property assignment, we can omit constructor if users do not define it
const hasInstancePropertyWithInitializer = forEach(node.members, isInstanceInitializedProperty);
const hasParameterPropertyAssignments = node.transformFlags & TransformFlags.ContainsParameterPropertyAssignments;
const constructor = getFirstConstructorWithBody(node);
const hasInstancePropertyWithInitializer = forEach(node.members, isInstanceInitializedProperty);
const hasParameterPropertyAssignments = constructor &&
constructor.transformFlags & TransformFlags.ContainsTypeScriptClassSyntax &&
forEach(constructor.parameters, isParameterWithPropertyAssignment);
// If the class does not contain nodes that require a synthesized constructor,
// accept the current constructor if it exists.
@@ -1896,9 +1898,13 @@ namespace ts {
case SyntaxKind.StringLiteral:
return createIdentifier("String");
case SyntaxKind.PrefixUnaryExpression:
case SyntaxKind.NumericLiteral:
return createIdentifier("Number");
case SyntaxKind.BigIntLiteral:
return getGlobalBigIntNameWithFallback();
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return createIdentifier("Boolean");
@@ -1910,6 +1916,9 @@ namespace ts {
case SyntaxKind.NumberKeyword:
return createIdentifier("Number");
case SyntaxKind.BigIntKeyword:
return getGlobalBigIntNameWithFallback();
case SyntaxKind.SymbolKeyword:
return languageVersion < ScriptTarget.ES2015
? getGlobalSymbolNameWithFallback()
@@ -1920,7 +1929,10 @@ namespace ts {
case SyntaxKind.IntersectionType:
case SyntaxKind.UnionType:
return serializeUnionOrIntersectionType(<UnionOrIntersectionTypeNode>node);
return serializeTypeList((<UnionOrIntersectionTypeNode>node).types);
case SyntaxKind.ConditionalType:
return serializeTypeList([(<ConditionalTypeNode>node).trueType, (<ConditionalTypeNode>node).falseType]);
case SyntaxKind.TypeQuery:
case SyntaxKind.TypeOperator:
@@ -1933,6 +1945,7 @@ namespace ts {
case SyntaxKind.ImportType:
break;
default:
return Debug.failBadSyntaxKind(node);
}
@@ -1940,11 +1953,11 @@ namespace ts {
return createIdentifier("Object");
}
function serializeUnionOrIntersectionType(node: UnionOrIntersectionTypeNode): SerializedTypeNode {
function serializeTypeList(types: ReadonlyArray<TypeNode>): SerializedTypeNode {
// Note when updating logic here also update getEntityNameForDecoratorMetadata
// so that aliases can be marked as referenced
let serializedUnion: SerializedTypeNode | undefined;
for (let typeNode of node.types) {
for (let typeNode of types) {
while (typeNode.kind === SyntaxKind.ParenthesizedType) {
typeNode = (typeNode as ParenthesizedTypeNode).type; // Skip parens if need be
}
@@ -1990,6 +2003,11 @@ namespace ts {
const kind = resolver.getTypeReferenceSerializationKind(node.typeName, currentNameScope || currentLexicalScope);
switch (kind) {
case TypeReferenceSerializationKind.Unknown:
// From conditional type type reference that cannot be resolved is Similar to any or unknown
if (findAncestor(node, n => n.parent && isConditionalTypeNode(n.parent) && (n.parent.trueType === n || n.parent.falseType === n))) {
return createIdentifier("Object");
}
const serialized = serializeEntityNameAsExpressionFallback(node.typeName);
const temp = createTempVariable(hoistVariableDeclaration);
return createConditional(
@@ -2004,6 +2022,9 @@ namespace ts {
case TypeReferenceSerializationKind.VoidNullableOrNeverType:
return createVoidZero();
case TypeReferenceSerializationKind.BigIntLikeType:
return getGlobalBigIntNameWithFallback();
case TypeReferenceSerializationKind.BooleanType:
return createIdentifier("Boolean");
@@ -2113,6 +2134,20 @@ namespace ts {
);
}
/**
* Gets an expression that points to the global "BigInt" constructor at runtime if it is
* available.
*/
function getGlobalBigIntNameWithFallback(): SerializedTypeNode {
return languageVersion < ScriptTarget.ESNext
? createConditional(
createTypeCheck(createIdentifier("BigInt"), "function"),
createIdentifier("BigInt"),
createIdentifier("Object")
)
: createIdentifier("BigInt");
}
/**
* A simple inlinable expression is an expression which can be copied into multiple locations
* without risk of repeating any sideeffects and whose value could not possibly change between
+698 -503
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -9,6 +9,7 @@
"files": [
"core.ts",
"performance.ts",
"semver.ts",
"types.ts",
"sys.ts",
@@ -24,7 +25,7 @@
"checker.ts",
"factory.ts",
"visitor.ts",
"sourcemapDecoder.ts",
"sourcemap.ts",
"transformers/utilities.ts",
"transformers/destructuring.ts",
"transformers/ts.ts",
@@ -41,8 +42,6 @@
"transformers/declarations/diagnostics.ts",
"transformers/declarations.ts",
"transformer.ts",
"sourcemap.ts",
"comments.ts",
"emitter.ts",
"watchUtilities.ts",
"program.ts",
@@ -51,6 +50,7 @@
"resolutionCache.ts",
"moduleSpecifiers.ts",
"watch.ts",
"tsbuild.ts"
"tsbuild.ts",
"inspectValue.ts",
]
}
+415 -162
View File
File diff suppressed because it is too large Load Diff
+469 -204
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -1111,6 +1111,7 @@ namespace ts {
case SyntaxKind.TaggedTemplateExpression:
result = reduceNode((<TaggedTemplateExpression>node).tag, cbNode, result);
result = reduceNodes((<TaggedTemplateExpression>node).typeArguments, cbNodes, result);
result = reduceNode((<TaggedTemplateExpression>node).template, cbNode, result);
break;
@@ -1377,6 +1378,7 @@ namespace ts {
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.JsxOpeningElement:
result = reduceNode((<JsxSelfClosingElement | JsxOpeningElement>node).tagName, cbNode, result);
result = reduceNodes((<JsxSelfClosingElement | JsxOpeningElement>node).typeArguments, cbNode, result);
result = reduceNode((<JsxSelfClosingElement | JsxOpeningElement>node).attributes, cbNode, result);
break;
+222 -189
View File
@@ -43,7 +43,6 @@ namespace ts {
return false;
}
/** @internal */
export const screenStartingMessageCodes: number[] = [
Diagnostics.Starting_compilation_in_watch_mode.code,
Diagnostics.File_change_detected_Starting_incremental_compilation.code,
@@ -89,6 +88,24 @@ namespace ts {
return result;
}
export type ReportEmitErrorSummary = (errorCount: number) => void;
export function getErrorCountForSummary(diagnostics: ReadonlyArray<Diagnostic>) {
return countWhere(diagnostics, diagnostic => diagnostic.category === DiagnosticCategory.Error);
}
export function getWatchErrorSummaryDiagnosticMessage(errorCount: number) {
return errorCount === 1 ?
Diagnostics.Found_1_error_Watching_for_file_changes :
Diagnostics.Found_0_errors_Watching_for_file_changes;
}
export function getErrorSummaryText(errorCount: number, newLine: string) {
if (errorCount === 0) return "";
const d = createCompilerDiagnostic(errorCount === 1 ? Diagnostics.Found_1_error : Diagnostics.Found_0_errors, errorCount);
return `${newLine}${flattenDiagnosticMessageText(d.messageText, newLine)}${newLine}${newLine}`;
}
/**
* Program structure needed to emit the files and report diagnostics
*/
@@ -101,15 +118,13 @@ namespace ts {
getGlobalDiagnostics(): ReadonlyArray<Diagnostic>;
getSemanticDiagnostics(): ReadonlyArray<Diagnostic>;
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;
emit(): EmitResult;
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback): EmitResult;
}
export type ReportEmitErrorSummary = (errorCount: number) => void;
/**
* Helper that emit files, report diagnostics and lists emitted and/or source files depending on compiler options
*/
export function emitFilesAndReportErrors(program: ProgramToEmitFilesAndReportErrors, reportDiagnostic: DiagnosticReporter, writeFileName?: (s: string) => void, reportSummary?: ReportEmitErrorSummary) {
export function emitFilesAndReportErrors(program: ProgramToEmitFilesAndReportErrors, reportDiagnostic: DiagnosticReporter, writeFileName?: (s: string) => void, reportSummary?: ReportEmitErrorSummary, writeFile?: WriteFileCallback) {
// First get and report any syntactic errors.
const diagnostics = program.getConfigFileParsingDiagnostics().slice();
const configFileParsingDiagnosticsLength = diagnostics.length;
@@ -128,7 +143,7 @@ namespace ts {
}
// Emit and report any errors we ran into.
const { emittedFiles, emitSkipped, diagnostics: emitDiagnostics } = program.emit();
const { emittedFiles, emitSkipped, diagnostics: emitDiagnostics } = program.emit(/*targetSourceFile*/ undefined, writeFile);
addRange(diagnostics, emitDiagnostics);
if (reportSemanticDiagnostics) {
@@ -151,7 +166,7 @@ namespace ts {
}
if (reportSummary) {
reportSummary(diagnostics.filter(diagnostic => diagnostic.category === DiagnosticCategory.Error).length);
reportSummary(getErrorCountForSummary(diagnostics));
}
if (emitSkipped && diagnostics.length > 0) {
@@ -172,30 +187,110 @@ namespace ts {
const onWatchStatusChange = reportWatchStatus || createWatchStatusReporter(system);
return {
onWatchStatusChange,
watchFile: system.watchFile ? ((path, callback, pollingInterval) => system.watchFile!(path, callback, pollingInterval)) : () => noopFileWatcher,
watchDirectory: system.watchDirectory ? ((path, callback, recursive) => system.watchDirectory!(path, callback, recursive)) : () => noopFileWatcher,
setTimeout: system.setTimeout ? ((callback, ms, ...args: any[]) => system.setTimeout!.call(system, callback, ms, ...args)) : noop,
clearTimeout: system.clearTimeout ? (timeoutId => system.clearTimeout!(timeoutId)) : noop
watchFile: maybeBind(system, system.watchFile) || (() => noopFileWatcher),
watchDirectory: maybeBind(system, system.watchDirectory) || (() => noopFileWatcher),
setTimeout: maybeBind(system, system.setTimeout) || noop,
clearTimeout: maybeBind(system, system.clearTimeout) || noop
};
}
export const enum WatchType {
ConfigFile = "Config file",
SourceFile = "Source file",
MissingFile = "Missing file",
WildcardDirectory = "Wild card directory",
FailedLookupLocations = "Failed Lookup Locations",
TypeRoots = "Type roots"
}
interface WatchFactory<X, Y = undefined> extends ts.WatchFactory<X, Y> {
writeLog: (s: string) => void;
}
export function createWatchFactory<Y = undefined>(host: { trace?(s: string): void; }, options: { extendedDiagnostics?: boolean; diagnostics?: boolean; }) {
const watchLogLevel = host.trace ? options.extendedDiagnostics ? WatchLogLevel.Verbose : options.diagnostics ? WatchLogLevel.TriggerOnly : WatchLogLevel.None : WatchLogLevel.None;
const writeLog: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => host.trace!(s)) : noop;
const result = getWatchFactory<WatchType, Y>(watchLogLevel, writeLog) as WatchFactory<WatchType, Y>;
result.writeLog = writeLog;
return result;
}
export function createCompilerHostFromProgramHost(host: ProgramHost<any>, getCompilerOptions: () => CompilerOptions, directoryStructureHost: DirectoryStructureHost = host): CompilerHost {
const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames();
const hostGetNewLine = memoize(() => host.getNewLine());
return {
getSourceFile: (fileName, languageVersion, onError) => {
let text: string | undefined;
try {
performance.mark("beforeIORead");
text = host.readFile(fileName, getCompilerOptions().charset);
performance.mark("afterIORead");
performance.measure("I/O Read", "beforeIORead", "afterIORead");
}
catch (e) {
if (onError) {
onError(e.message);
}
text = "";
}
return text !== undefined ? createSourceFile(fileName, text, languageVersion) : undefined;
},
getDefaultLibLocation: maybeBind(host, host.getDefaultLibLocation),
getDefaultLibFileName: options => host.getDefaultLibFileName(options),
writeFile,
getCurrentDirectory: memoize(() => host.getCurrentDirectory()),
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
getCanonicalFileName: createGetCanonicalFileName(useCaseSensitiveFileNames),
getNewLine: () => getNewLineCharacter(getCompilerOptions(), hostGetNewLine),
fileExists: f => host.fileExists(f),
readFile: f => host.readFile(f),
trace: maybeBind(host, host.trace),
directoryExists: maybeBind(directoryStructureHost, directoryStructureHost.directoryExists),
getDirectories: maybeBind(directoryStructureHost, directoryStructureHost.getDirectories),
realpath: maybeBind(host, host.realpath),
getEnvironmentVariable: maybeBind(host, host.getEnvironmentVariable) || (() => ""),
createHash: maybeBind(host, host.createHash),
readDirectory: maybeBind(host, host.readDirectory),
};
function ensureDirectoriesExist(directoryPath: string) {
if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists!(directoryPath)) {
const parentDirectory = getDirectoryPath(directoryPath);
ensureDirectoriesExist(parentDirectory);
if (host.createDirectory) host.createDirectory(directoryPath);
}
}
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) {
try {
performance.mark("beforeIOWrite");
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
host.writeFile!(fileName, text, writeByteOrderMark);
performance.mark("afterIOWrite");
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
}
catch (e) {
if (onError) {
onError(e.message);
}
}
}
}
/**
* Creates the watch compiler host that can be extended with config file or root file names and options host
*/
function createWatchCompilerHost<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(system = sys, createProgram: CreateProgram<T> | undefined, reportDiagnostic: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHost<T> {
if (!createProgram) {
createProgram = createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>;
}
export function createProgramHost<T extends BuilderProgram>(system: System, createProgram: CreateProgram<T>): ProgramHost<T> {
const getDefaultLibLocation = memoize(() => getDirectoryPath(normalizePath(system.getExecutingFilePath())));
let host: DirectoryStructureHost = system;
host; // tslint:disable-line no-unused-expression (TODO: `host` is unused!)
const useCaseSensitiveFileNames = () => system.useCaseSensitiveFileNames;
const writeFileName = (s: string) => system.write(s + system.newLine);
const { onWatchStatusChange, watchFile, watchDirectory, setTimeout, clearTimeout } = createWatchHost(system, reportWatchStatus);
return {
useCaseSensitiveFileNames,
useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames,
getNewLine: () => system.newLine,
getCurrentDirectory: () => system.getCurrentDirectory(),
getCurrentDirectory: memoize(() => system.getCurrentDirectory()),
getDefaultLibLocation,
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
fileExists: path => system.fileExists(path),
@@ -203,41 +298,40 @@ namespace ts {
directoryExists: path => system.directoryExists(path),
getDirectories: path => system.getDirectories(path),
readDirectory: (path, extensions, exclude, include, depth) => system.readDirectory(path, extensions, exclude, include, depth),
realpath: system.realpath && (path => system.realpath!(path)),
getEnvironmentVariable: system.getEnvironmentVariable && (name => system.getEnvironmentVariable(name)),
watchFile,
watchDirectory,
setTimeout,
clearTimeout,
trace: s => system.write(s),
onWatchStatusChange,
realpath: maybeBind(system, system.realpath),
getEnvironmentVariable: maybeBind(system, system.getEnvironmentVariable),
trace: s => system.write(s + system.newLine),
createDirectory: path => system.createDirectory(path),
writeFile: (path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark),
onCachedDirectoryStructureHostCreate: cacheHost => host = cacheHost || system,
createHash: system.createHash && (s => system.createHash!(s)),
createProgram,
afterProgramCreate: emitFilesAndReportErrorUsingBuilder
createHash: maybeBind(system, system.createHash),
createProgram
};
}
function getDefaultLibLocation() {
return getDirectoryPath(normalizePath(system.getExecutingFilePath()));
}
function emitFilesAndReportErrorUsingBuilder(builderProgram: BuilderProgram) {
/**
* Creates the watch compiler host that can be extended with config file or root file names and options host
*/
function createWatchCompilerHost<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(system = sys, createProgram: CreateProgram<T> | undefined, reportDiagnostic: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHost<T> {
const writeFileName = (s: string) => system.write(s + system.newLine);
const result = createProgramHost(system, createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>) as WatchCompilerHost<T>;
copyProperties(result, createWatchHost(system, reportWatchStatus));
result.afterProgramCreate = builderProgram => {
const compilerOptions = builderProgram.getCompilerOptions();
const newLine = getNewLineCharacter(compilerOptions, () => system.newLine);
const reportSummary = (errorCount: number) => {
if (errorCount === 1) {
onWatchStatusChange!(createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes, errorCount), newLine, compilerOptions);
}
else {
onWatchStatusChange!(createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errorCount, errorCount), newLine, compilerOptions);
}
};
emitFilesAndReportErrors(builderProgram, reportDiagnostic, writeFileName, reportSummary);
}
emitFilesAndReportErrors(
builderProgram,
reportDiagnostic,
writeFileName,
errorCount => result.onWatchStatusChange!(
createCompilerDiagnostic(getWatchErrorSummaryDiagnosticMessage(errorCount), errorCount),
newLine,
compilerOptions
)
);
};
return result;
}
/**
@@ -263,10 +357,11 @@ namespace ts {
/**
* Creates the watch compiler host from system for compiling root files and options in watch mode
*/
export function createWatchCompilerHostOfFilesAndCompilerOptions<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(rootFiles: string[], options: CompilerOptions, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions<T> {
export function createWatchCompilerHostOfFilesAndCompilerOptions<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(rootFiles: string[], options: CompilerOptions, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, projectReferences?: ReadonlyArray<ProjectReference>): WatchCompilerHostOfFilesAndCompilerOptions<T> {
const host = createWatchCompilerHost(system, createProgram, reportDiagnostic || createDiagnosticReporter(system), reportWatchStatus) as WatchCompilerHostOfFilesAndCompilerOptions<T>;
host.rootFiles = rootFiles;
host.options = options;
host.projectReferences = projectReferences;
return host;
}
}
@@ -274,7 +369,8 @@ namespace ts {
namespace ts {
export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
export type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) => T;
export type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference> | undefined) => T;
/** Host that has watch functionality used in --watch mode */
export interface WatchHost {
/** If provided, called with Diagnostic message that informs about change in watch status */
@@ -289,19 +385,11 @@ namespace ts {
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
export interface WatchCompilerHost<T extends BuilderProgram> extends WatchHost {
// TODO: GH#18217 Optional methods are frequently asserted
export interface ProgramHost<T extends BuilderProgram> {
/**
* Used to create the program when need for program creation or recreation detected
*/
createProgram: CreateProgram<T>;
/** If provided, callback to invoke after every new program creation */
afterProgramCreate?(program: T): void;
// Only for testing
/*@internal*/
maxNumberOfFilesToIterateForInvalidation?: number;
// Sub set of compiler host methods to read and generate new program
useCaseSensitiveFileNames(): boolean;
@@ -337,20 +425,29 @@ namespace ts {
getEnvironmentVariable?(name: string): string | undefined;
/** If provided, used to resolve the module names, otherwise typescript's default module resolution */
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference): (ResolvedModule | undefined)[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): (ResolvedTypeReferenceDirective | undefined)[];
}
/** Internal interface used to wire emit through same host */
/*@internal*/
export interface WatchCompilerHost<T extends BuilderProgram> {
export interface ProgramHost<T extends BuilderProgram> {
// TODO: GH#18217 Optional methods are frequently asserted
createDirectory?(path: string): void;
writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void;
onCachedDirectoryStructureHostCreate?(host: CachedDirectoryStructureHost): void;
}
export interface WatchCompilerHost<T extends BuilderProgram> extends ProgramHost<T>, WatchHost {
/** If provided, callback to invoke after every new program creation */
afterProgramCreate?(program: T): void;
// Only for testing
/*@internal*/
maxNumberOfFilesToIterateForInvalidation?: number;
}
/**
* Host to create watch with root files and options
*/
@@ -360,6 +457,9 @@ namespace ts {
/** Compiler options */
options: CompilerOptions;
/** Project References */
projectReferences?: ReadonlyArray<ProjectReference>;
}
/**
@@ -413,11 +513,11 @@ namespace ts {
/**
* Create the watch compiler host for either configFile or fileNames and its options
*/
export function createWatchCompilerHost<T extends BuilderProgram>(rootFiles: string[], options: CompilerOptions, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions<T>;
export function createWatchCompilerHost<T extends BuilderProgram>(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile<T>;
export function createWatchCompilerHost<T extends BuilderProgram>(rootFilesOrConfigFileName: string | string[], options: CompilerOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions<T> | WatchCompilerHostOfConfigFile<T> {
export function createWatchCompilerHost<T extends BuilderProgram>(rootFiles: string[], options: CompilerOptions, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, projectReferences?: ReadonlyArray<ProjectReference>): WatchCompilerHostOfFilesAndCompilerOptions<T>;
export function createWatchCompilerHost<T extends BuilderProgram>(rootFilesOrConfigFileName: string | string[], options: CompilerOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, projectReferences?: ReadonlyArray<ProjectReference>): WatchCompilerHostOfFilesAndCompilerOptions<T> | WatchCompilerHostOfConfigFile<T> {
if (isArray(rootFilesOrConfigFileName)) {
return createWatchCompilerHostOfFilesAndCompilerOptions(rootFilesOrConfigFileName, options!, system, createProgram, reportDiagnostic, reportWatchStatus); // TODO: GH#18217
return createWatchCompilerHostOfFilesAndCompilerOptions(rootFilesOrConfigFileName, options!, system, createProgram, reportDiagnostic, reportWatchStatus, projectReferences); // TODO: GH#18217
}
else {
return createWatchCompilerHostOfConfigFile(rootFilesOrConfigFileName, options, system, createProgram, reportDiagnostic, reportWatchStatus);
@@ -460,12 +560,11 @@ namespace ts {
const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames();
const currentDirectory = host.getCurrentDirectory();
const getCurrentDirectory = () => currentDirectory;
const readFile: (path: string, encoding?: string) => string | undefined = (path, encoding) => host.readFile(path, encoding);
const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {}, createProgram } = host;
let { rootFiles: rootFileNames, options: compilerOptions } = host;
let { rootFiles: rootFileNames, options: compilerOptions, projectReferences } = host;
let configFileSpecs: ConfigFileSpecs;
let configFileParsingDiagnostics: ReadonlyArray<Diagnostic> | undefined;
let configFileParsingDiagnostics: Diagnostic[] | undefined;
let canConfigFileJsonReportNoInputFiles = false;
let hasChangedConfigFileParsingErrors = false;
const cachedDirectoryStructureHost = configFileName === undefined ? undefined : createCachedDirectoryStructureHost(host, currentDirectory, useCaseSensitiveFileNames);
@@ -473,14 +572,7 @@ namespace ts {
host.onCachedDirectoryStructureHostCreate(cachedDirectoryStructureHost);
}
const directoryStructureHost: DirectoryStructureHost = cachedDirectoryStructureHost || host;
const parseConfigFileHost: ParseConfigFileHost = {
useCaseSensitiveFileNames,
readDirectory: (path, extensions, exclude, include, depth) => directoryStructureHost.readDirectory!(path, extensions, exclude, include, depth),
fileExists: path => host.fileExists(path),
readFile,
getCurrentDirectory,
onUnRecoverableConfigFileDiagnostic: host.onUnRecoverableConfigFileDiagnostic
};
const parseConfigFileHost = parseConfigHostFromCompilerHostLike(host, directoryStructureHost);
// From tsc we want to get already parsed result and hence check for rootFileNames
let newLine = updateNewLine();
@@ -496,54 +588,37 @@ namespace ts {
newLine = updateNewLine();
}
const trace = host.trace && ((s: string) => { host.trace!(s + newLine); });
const watchLogLevel = trace ? compilerOptions.extendedDiagnostics ? WatchLogLevel.Verbose :
compilerOptions.diagnostics ? WatchLogLevel.TriggerOnly : WatchLogLevel.None : WatchLogLevel.None;
const writeLog: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? trace! : noop; // TODO: GH#18217
const { watchFile, watchFilePath, watchDirectory } = getWatchFactory<string>(watchLogLevel, writeLog);
const { watchFile, watchFilePath, watchDirectory, writeLog } = createWatchFactory<string>(host, compilerOptions);
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
writeLog(`Current directory: ${currentDirectory} CaseSensitiveFileNames: ${useCaseSensitiveFileNames}`);
if (configFileName) {
watchFile(host, configFileName, scheduleProgramReload, PollingInterval.High, "Config file");
watchFile(host, configFileName, scheduleProgramReload, PollingInterval.High, WatchType.ConfigFile);
}
const compilerHost: CompilerHost & ResolutionCacheHost = {
// Members for CompilerHost
getSourceFile: (fileName, languageVersion, onError?, shouldCreateNewSourceFile?) => getVersionedSourceFileByPath(fileName, toPath(fileName), languageVersion, onError, shouldCreateNewSourceFile),
getSourceFileByPath: getVersionedSourceFileByPath,
getDefaultLibLocation: host.getDefaultLibLocation && (() => host.getDefaultLibLocation!()),
getDefaultLibFileName: options => host.getDefaultLibFileName(options),
writeFile,
getCurrentDirectory,
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
getCanonicalFileName,
getNewLine: () => newLine,
fileExists,
readFile,
trace,
directoryExists: directoryStructureHost.directoryExists && (path => directoryStructureHost.directoryExists!(path)),
getDirectories: (directoryStructureHost.getDirectories && ((path: string) => directoryStructureHost.getDirectories!(path)))!, // TODO: GH#18217
realpath: host.realpath && (s => host.realpath!(s)),
getEnvironmentVariable: host.getEnvironmentVariable ? (name => host.getEnvironmentVariable!(name)) : (() => ""),
onReleaseOldSourceFile,
createHash: host.createHash && (data => host.createHash!(data)),
// Members for ResolutionCacheHost
toPath,
getCompilationSettings: () => compilerOptions,
watchDirectoryOfFailedLookupLocation: (dir, cb, flags) => watchDirectory(host, dir, cb, flags, "Failed Lookup Locations"),
watchTypeRootsDirectory: (dir, cb, flags) => watchDirectory(host, dir, cb, flags, "Type roots"),
getCachedDirectoryStructureHost: () => cachedDirectoryStructureHost,
onInvalidatedResolution: scheduleProgramUpdate,
onChangedAutomaticTypeDirectiveNames: () => {
hasChangedAutomaticTypeDirectiveNames = true;
scheduleProgramUpdate();
},
maxNumberOfFilesToIterateForInvalidation: host.maxNumberOfFilesToIterateForInvalidation,
getCurrentProgram,
writeLog
const compilerHost = createCompilerHostFromProgramHost(host, () => compilerOptions, directoryStructureHost) as CompilerHost & ResolutionCacheHost;
// Members for CompilerHost
const getNewSourceFile = compilerHost.getSourceFile;
compilerHost.getSourceFile = (fileName, ...args) => getVersionedSourceFileByPath(fileName, toPath(fileName), ...args);
compilerHost.getSourceFileByPath = getVersionedSourceFileByPath;
compilerHost.getNewLine = () => newLine;
compilerHost.fileExists = fileExists;
compilerHost.onReleaseOldSourceFile = onReleaseOldSourceFile;
// Members for ResolutionCacheHost
compilerHost.toPath = toPath;
compilerHost.getCompilationSettings = () => compilerOptions;
compilerHost.watchDirectoryOfFailedLookupLocation = (dir, cb, flags) => watchDirectory(host, dir, cb, flags, WatchType.FailedLookupLocations);
compilerHost.watchTypeRootsDirectory = (dir, cb, flags) => watchDirectory(host, dir, cb, flags, WatchType.TypeRoots);
compilerHost.getCachedDirectoryStructureHost = () => cachedDirectoryStructureHost;
compilerHost.onInvalidatedResolution = scheduleProgramUpdate;
compilerHost.onChangedAutomaticTypeDirectiveNames = () => {
hasChangedAutomaticTypeDirectiveNames = true;
scheduleProgramUpdate();
};
compilerHost.maxNumberOfFilesToIterateForInvalidation = host.maxNumberOfFilesToIterateForInvalidation;
compilerHost.getCurrentProgram = getCurrentProgram;
compilerHost.writeLog = writeLog;
// Cache for the module resolution
const resolutionCache = createResolutionCache(compilerHost, configFileName ?
getDirectoryPath(getNormalizedAbsolutePath(configFileName, currentDirectory)) :
@@ -552,11 +627,11 @@ namespace ts {
);
// Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names
compilerHost.resolveModuleNames = host.resolveModuleNames ?
((moduleNames, containingFile, reusedNames) => host.resolveModuleNames!(moduleNames, containingFile, reusedNames)) :
((moduleNames, containingFile, reusedNames) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames));
((moduleNames, containingFile, reusedNames, redirectedReference) => host.resolveModuleNames!(moduleNames, containingFile, reusedNames, redirectedReference)) :
((moduleNames, containingFile, reusedNames, redirectedReference) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference));
compilerHost.resolveTypeReferenceDirectives = host.resolveTypeReferenceDirectives ?
((typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives!(typeDirectiveNames, containingFile)) :
((typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile));
((typeDirectiveNames, containingFile, redirectedReference) => host.resolveTypeReferenceDirectives!(typeDirectiveNames, containingFile, redirectedReference)) :
((typeDirectiveNames, containingFile, redirectedReference) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference));
const userProvidedResolution = !!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives;
synchronizeProgram();
@@ -589,9 +664,9 @@ namespace ts {
// All resolutions are invalid if user provided resolutions
const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution(userProvidedResolution);
if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames)) {
if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames, projectReferences)) {
if (hasChangedConfigFileParsingErrors) {
builderProgram = createProgram(/*rootNames*/ undefined, /*options*/ undefined, compilerHost, builderProgram, configFileParsingDiagnostics);
builderProgram = createProgram(/*rootNames*/ undefined, /*options*/ undefined, compilerHost, builderProgram, configFileParsingDiagnostics, projectReferences);
hasChangedConfigFileParsingErrors = false;
}
}
@@ -608,11 +683,9 @@ namespace ts {
function createNewProgram(program: Program, hasInvalidatedResolution: HasInvalidatedResolution) {
// Compile the program
if (watchLogLevel !== WatchLogLevel.None) {
writeLog("CreatingProgramWith::");
writeLog(` roots: ${JSON.stringify(rootFileNames)}`);
writeLog(` options: ${JSON.stringify(compilerOptions)}`);
}
writeLog("CreatingProgramWith::");
writeLog(` roots: ${JSON.stringify(rootFileNames)}`);
writeLog(` options: ${JSON.stringify(compilerOptions)}`);
const needsUpdateInTypeRootWatch = hasChangedCompilerOptions || !program;
hasChangedCompilerOptions = false;
@@ -620,7 +693,7 @@ namespace ts {
resolutionCache.startCachingPerDirectoryResolution();
compilerHost.hasInvalidatedResolution = hasInvalidatedResolution;
compilerHost.hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames;
builderProgram = createProgram(rootFileNames, compilerOptions, compilerHost, builderProgram, configFileParsingDiagnostics);
builderProgram = createProgram(rootFileNames, compilerOptions, compilerHost, builderProgram, configFileParsingDiagnostics, projectReferences);
resolutionCache.finishCachingPerDirectoryResolution();
// Update watches
@@ -686,7 +759,7 @@ namespace ts {
// Create new source file if requested or the versions dont match
if (!hostSourceFile || shouldCreateNewSourceFile || !isFilePresentOnHost(hostSourceFile) || hostSourceFile.version.toString() !== hostSourceFile.sourceFile.version) {
const sourceFile = getNewSourceFile();
const sourceFile = getNewSourceFile(fileName, languageVersion, onError);
if (hostSourceFile) {
if (shouldCreateNewSourceFile) {
hostSourceFile.version++;
@@ -697,7 +770,7 @@ namespace ts {
(hostSourceFile as FilePresentOnHost).sourceFile = sourceFile;
sourceFile.version = hostSourceFile.version.toString();
if (!(hostSourceFile as FilePresentOnHost).fileWatcher) {
(hostSourceFile as FilePresentOnHost).fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, path, "Source file");
(hostSourceFile as FilePresentOnHost).fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, path, WatchType.SourceFile);
}
}
else {
@@ -711,7 +784,7 @@ namespace ts {
else {
if (sourceFile) {
sourceFile.version = initialVersion.toString();
const fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, path, "Source file");
const fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, path, WatchType.SourceFile);
sourceFilesCache.set(path, { sourceFile, version: initialVersion, fileWatcher });
}
else {
@@ -721,23 +794,6 @@ namespace ts {
return sourceFile;
}
return hostSourceFile.sourceFile;
function getNewSourceFile() {
let text: string | undefined;
try {
performance.mark("beforeIORead");
text = host.readFile(fileName, compilerOptions.charset);
performance.mark("afterIORead");
performance.measure("I/O Read", "beforeIORead", "afterIORead");
}
catch (e) {
if (onError) {
onError(e.message);
}
}
return text !== undefined ? createSourceFile(fileName, text, languageVersion) : undefined;
}
}
function nextSourceFileVersion(path: Path) {
@@ -758,8 +814,8 @@ namespace ts {
return !hostSourceFile || isFileMissingOnHost(hostSourceFile) ? undefined : hostSourceFile.version.toString();
}
function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions) {
const hostSourceFileInfo = sourceFilesCache.get(oldSourceFile.path);
function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions, hasSourceFileByPath: boolean) {
const hostSourceFileInfo = sourceFilesCache.get(oldSourceFile.resolvedPath);
// If this is the source file thats in the cache and new program doesnt need it,
// remove the cached entry.
// Note we arent deleting entry if file became missing in new program or
@@ -773,8 +829,10 @@ namespace ts {
if ((hostSourceFileInfo as FilePresentOnHost).fileWatcher) {
(hostSourceFileInfo as FilePresentOnHost).fileWatcher.close();
}
sourceFilesCache.delete(oldSourceFile.path);
resolutionCache.removeResolutionsOfFile(oldSourceFile.path);
sourceFilesCache.delete(oldSourceFile.resolvedPath);
if (!hasSourceFileByPath) {
resolutionCache.removeResolutionsOfFile(oldSourceFile.path);
}
}
}
}
@@ -824,12 +882,7 @@ namespace ts {
function reloadFileNamesFromConfigFile() {
writeLog("Reloading new file names and options");
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, parseConfigFileHost);
if (result.fileNames.length) {
configFileParsingDiagnostics = filter(configFileParsingDiagnostics, error => !isErrorNoInputFiles(error));
hasChangedConfigFileParsingErrors = true;
}
else if (!configFileSpecs.filesSpecs && !some(configFileParsingDiagnostics, isErrorNoInputFiles)) {
configFileParsingDiagnostics = configFileParsingDiagnostics!.concat(getErrorForNoInputFiles(configFileSpecs, configFileName));
if (updateErrorForNoInputFiles(result, configFileName, configFileSpecs, configFileParsingDiagnostics!, canConfigFileJsonReportNoInputFiles)) {
hasChangedConfigFileParsingErrors = true;
}
rootFileNames = result.fileNames;
@@ -861,7 +914,9 @@ namespace ts {
rootFileNames = configFileParseResult.fileNames;
compilerOptions = configFileParseResult.options;
configFileSpecs = configFileParseResult.configFileSpecs!; // TODO: GH#18217
configFileParsingDiagnostics = getConfigFileParsingDiagnostics(configFileParseResult);
projectReferences = configFileParseResult.projectReferences;
configFileParsingDiagnostics = getConfigFileParsingDiagnostics(configFileParseResult).slice();
canConfigFileJsonReportNoInputFiles = canJsonReportNoInutFiles(configFileParseResult.raw);
hasChangedConfigFileParsingErrors = true;
}
@@ -872,6 +927,7 @@ namespace ts {
if (eventKind === FileWatcherEventKind.Deleted && sourceFilesCache.get(path)) {
resolutionCache.invalidateResolutionOfFile(path);
}
resolutionCache.removeResolutionsFromProjectReferenceRedirects(path);
nextSourceFileVersion(path);
// Update the program
@@ -885,7 +941,7 @@ namespace ts {
}
function watchMissingFilePath(missingFilePath: Path) {
return watchFilePath(host, missingFilePath, onMissingFileChange, PollingInterval.Medium, missingFilePath, "Missing file");
return watchFilePath(host, missingFilePath, onMissingFileChange, PollingInterval.Medium, missingFilePath, WatchType.MissingFile);
}
function onMissingFileChange(fileName: string, eventKind: FileWatcherEventKind, missingFilePath: Path) {
@@ -931,6 +987,8 @@ namespace ts {
}
nextSourceFileVersion(fileOrDirectoryPath);
if (isPathInNodeModulesStartingWithDot(fileOrDirectoryPath)) return;
// If the the added or created file or directory is not supported file name, ignore the file
// But when watched directory is added/removed, we need to reload the file list
if (fileOrDirectoryPath !== directory && hasExtension(fileOrDirectoryPath) && !isSupportedSourceFileName(fileOrDirectory, compilerOptions)) {
@@ -947,33 +1005,8 @@ namespace ts {
}
},
flags,
"Wild card directories"
WatchType.WildcardDirectory
);
}
function ensureDirectoriesExist(directoryPath: string) {
if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists!(directoryPath)) {
const parentDirectory = getDirectoryPath(directoryPath);
ensureDirectoriesExist(parentDirectory);
host.createDirectory!(directoryPath);
}
}
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) {
try {
performance.mark("beforeIOWrite");
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
host.writeFile!(fileName, text, writeByteOrderMark);
performance.mark("afterIOWrite");
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
}
catch (e) {
if (onError) {
onError(e.message);
}
}
}
}
}
+4 -4
View File
@@ -343,10 +343,10 @@ namespace ts {
export interface WatchDirectoryHost {
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
}
export type WatchFile<X, Y> = (host: WatchFileHost, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, detailInfo1?: X, detailInfo2?: Y) => FileWatcher;
export type WatchFile<X, Y> = (host: WatchFileHost, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
export type FilePathWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void;
export type WatchFilePath<X, Y> = (host: WatchFileHost, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, path: Path, detailInfo1?: X, detailInfo2?: Y) => FileWatcher;
export type WatchDirectory<X, Y> = (host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, detailInfo1?: X, detailInfo2?: Y) => FileWatcher;
export type WatchFilePath<X, Y> = (host: WatchFileHost, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, path: Path, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
export type WatchDirectory<X, Y> = (host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
export interface WatchFactory<X, Y> {
watchFile: WatchFile<X, Y>;
@@ -444,7 +444,7 @@ namespace ts {
}
function getWatchInfo<T, X, Y>(file: string, flags: T, detailInfo1: X, detailInfo2: Y | undefined, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined) {
return `WatchInfo: ${file} ${flags} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : detailInfo1}`;
return `WatchInfo: ${file} ${flags} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : detailInfo2 === undefined ? detailInfo1 : `${detailInfo1} ${detailInfo2}`}`;
}
export function closeFileWatcherOf<T extends { watcher: FileWatcher; }>(objWithWatcher: T) {