mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into isInMultiLineComment
This commit is contained in:
+142
-153
@@ -10,7 +10,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
interface ActiveLabel {
|
||||
name: string;
|
||||
name: __String;
|
||||
breakTarget: FlowLabel;
|
||||
continueTarget: FlowLabel;
|
||||
referenced: boolean;
|
||||
@@ -132,8 +132,9 @@ namespace ts {
|
||||
let inStrictMode: boolean;
|
||||
|
||||
let symbolCount = 0;
|
||||
let Symbol: { new (flags: SymbolFlags, name: string): Symbol };
|
||||
let classifiableNames: Map<string>;
|
||||
|
||||
let Symbol: { new (flags: SymbolFlags, name: __String): Symbol };
|
||||
let classifiableNames: UnderscoreEscapedMap<true>;
|
||||
|
||||
const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
|
||||
const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
|
||||
@@ -147,7 +148,7 @@ namespace ts {
|
||||
options = opts;
|
||||
languageVersion = getEmitScriptTarget(options);
|
||||
inStrictMode = bindInStrictMode(file, opts);
|
||||
classifiableNames = createMap<string>();
|
||||
classifiableNames = createUnderscoreEscapedMap<true>();
|
||||
symbolCount = 0;
|
||||
skipTransformFlagAggregation = file.isDeclarationFile;
|
||||
|
||||
@@ -191,7 +192,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function createSymbol(flags: SymbolFlags, name: string): Symbol {
|
||||
function createSymbol(flags: SymbolFlags, name: __String): Symbol {
|
||||
symbolCount++;
|
||||
return new Symbol(flags, name);
|
||||
}
|
||||
@@ -207,11 +208,11 @@ namespace ts {
|
||||
symbol.declarations.push(node);
|
||||
|
||||
if (symbolFlags & SymbolFlags.HasExports && !symbol.exports) {
|
||||
symbol.exports = createMap<Symbol>();
|
||||
symbol.exports = createSymbolTable();
|
||||
}
|
||||
|
||||
if (symbolFlags & SymbolFlags.HasMembers && !symbol.members) {
|
||||
symbol.members = createMap<Symbol>();
|
||||
symbol.members = createSymbolTable();
|
||||
}
|
||||
|
||||
if (symbolFlags & SymbolFlags.Value) {
|
||||
@@ -226,67 +227,68 @@ namespace ts {
|
||||
|
||||
// Should not be called on a declaration with a computed property name,
|
||||
// unless it is a well known Symbol.
|
||||
function getDeclarationName(node: Declaration): string {
|
||||
function getDeclarationName(node: Declaration): __String {
|
||||
const name = getNameOfDeclaration(node);
|
||||
if (name) {
|
||||
if (isAmbientModule(node)) {
|
||||
return isGlobalScopeAugmentation(<ModuleDeclaration>node) ? "__global" : `"${(<LiteralExpression>name).text}"`;
|
||||
const moduleName = getTextOfIdentifierOrLiteral(<Identifier | LiteralExpression>name);
|
||||
return (isGlobalScopeAugmentation(<ModuleDeclaration>node) ? "__global" : `"${moduleName}"`) as __String;
|
||||
}
|
||||
if (name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
const nameExpression = (<ComputedPropertyName>name).expression;
|
||||
// treat computed property names where expression is string/numeric literal as just string/numeric literal
|
||||
if (isStringOrNumericLiteral(nameExpression)) {
|
||||
return nameExpression.text;
|
||||
return escapeLeadingUnderscores(nameExpression.text);
|
||||
}
|
||||
|
||||
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
|
||||
return getPropertyNameForKnownSymbolName((<PropertyAccessExpression>nameExpression).name.text);
|
||||
return getPropertyNameForKnownSymbolName(unescapeLeadingUnderscores((<PropertyAccessExpression>nameExpression).name.escapedText));
|
||||
}
|
||||
return (<Identifier | LiteralExpression>name).text;
|
||||
return getEscapedTextOfIdentifierOrLiteral(<Identifier | LiteralExpression>name);
|
||||
}
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Constructor:
|
||||
return "__constructor";
|
||||
return InternalSymbolName.Constructor;
|
||||
case SyntaxKind.FunctionType:
|
||||
case SyntaxKind.CallSignature:
|
||||
return "__call";
|
||||
return InternalSymbolName.Call;
|
||||
case SyntaxKind.ConstructorType:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
return "__new";
|
||||
return InternalSymbolName.New;
|
||||
case SyntaxKind.IndexSignature:
|
||||
return "__index";
|
||||
return InternalSymbolName.Index;
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
return "__export";
|
||||
return InternalSymbolName.ExportStar;
|
||||
case SyntaxKind.ExportAssignment:
|
||||
return (<ExportAssignment>node).isExportEquals ? "export=" : "default";
|
||||
return (<ExportAssignment>node).isExportEquals ? InternalSymbolName.ExportEquals : InternalSymbolName.Default;
|
||||
case SyntaxKind.BinaryExpression:
|
||||
if (getSpecialPropertyAssignmentKind(node as BinaryExpression) === SpecialPropertyAssignmentKind.ModuleExports) {
|
||||
// module.exports = ...
|
||||
return "export=";
|
||||
return InternalSymbolName.ExportEquals;
|
||||
}
|
||||
Debug.fail("Unknown binary declaration kind");
|
||||
break;
|
||||
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
return hasModifier(node, ModifierFlags.Default) ? "default" : undefined;
|
||||
return (hasModifier(node, ModifierFlags.Default) ? InternalSymbolName.Default : undefined);
|
||||
case SyntaxKind.JSDocFunctionType:
|
||||
return isJSDocConstructSignature(node) ? "__new" : "__call";
|
||||
return (isJSDocConstructSignature(node) ? InternalSymbolName.New : InternalSymbolName.Call);
|
||||
case SyntaxKind.Parameter:
|
||||
// Parameters with names are handled at the top of this function. Parameters
|
||||
// without names can only come from JSDocFunctionTypes.
|
||||
Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType);
|
||||
const functionType = <JSDocFunctionType>node.parent;
|
||||
const index = indexOf(functionType.parameters, node);
|
||||
return "arg" + index;
|
||||
return "arg" + index as __String;
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
const parentNode = node.parent && node.parent.parent;
|
||||
let nameFromParentNode: string;
|
||||
let nameFromParentNode: __String;
|
||||
if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) {
|
||||
if ((<VariableStatement>parentNode).declarationList.declarations.length > 0) {
|
||||
const nameIdentifier = (<VariableStatement>parentNode).declarationList.declarations[0].name;
|
||||
if (nameIdentifier.kind === SyntaxKind.Identifier) {
|
||||
nameFromParentNode = (<Identifier>nameIdentifier).text;
|
||||
if (isIdentifier(nameIdentifier)) {
|
||||
nameFromParentNode = nameIdentifier.escapedText;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,7 +297,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getDisplayName(node: Declaration): string {
|
||||
return (node as NamedDeclaration).name ? declarationNameToString((node as NamedDeclaration).name) : getDeclarationName(node);
|
||||
return (node as NamedDeclaration).name ? declarationNameToString((node as NamedDeclaration).name) : unescapeLeadingUnderscores(getDeclarationName(node));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,17 +308,17 @@ namespace ts {
|
||||
* @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.)
|
||||
* @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations.
|
||||
*/
|
||||
function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol {
|
||||
function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags, isReplaceableByMethod?: boolean): Symbol {
|
||||
Debug.assert(!hasDynamicName(node));
|
||||
|
||||
const isDefaultExport = hasModifier(node, ModifierFlags.Default);
|
||||
|
||||
// The exported symbol for an export default function/class node is always named "default"
|
||||
const name = isDefaultExport && parent ? "default" : getDeclarationName(node);
|
||||
const name = isDefaultExport && parent ? InternalSymbolName.Default : getDeclarationName(node);
|
||||
|
||||
let symbol: Symbol;
|
||||
if (name === undefined) {
|
||||
symbol = createSymbol(SymbolFlags.None, "__missing");
|
||||
symbol = createSymbol(SymbolFlags.None, InternalSymbolName.Missing);
|
||||
}
|
||||
else {
|
||||
// Check and see if the symbol table already has a symbol with this name. If not,
|
||||
@@ -343,15 +345,20 @@ namespace ts {
|
||||
// you have multiple 'vars' with the same name in the same container). In this case
|
||||
// just add this node into the declarations list of the symbol.
|
||||
symbol = symbolTable.get(name);
|
||||
|
||||
if (includes & SymbolFlags.Classifiable) {
|
||||
classifiableNames.set(name, true);
|
||||
}
|
||||
|
||||
if (!symbol) {
|
||||
symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name));
|
||||
if (isReplaceableByMethod) symbol.isReplaceableByMethod = true;
|
||||
}
|
||||
|
||||
if (name && (includes & SymbolFlags.Classifiable)) {
|
||||
classifiableNames.set(name, name);
|
||||
else if (isReplaceableByMethod && !symbol.isReplaceableByMethod) {
|
||||
// A symbol already exists, so don't add this as a declaration.
|
||||
return symbol;
|
||||
}
|
||||
|
||||
if (symbol.flags & excludes) {
|
||||
else if (symbol.flags & excludes) {
|
||||
if (symbol.isReplaceableByMethod) {
|
||||
// Javascript constructor-declared symbols can be discarded in favor of
|
||||
// prototype symbols like methods.
|
||||
@@ -414,9 +421,8 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue,
|
||||
// ExportType, or ExportContainer flag, and an associated export symbol with all the correct flags set
|
||||
// on it. There are 2 main reasons:
|
||||
// Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue flag,
|
||||
// and an associated export symbol with all the correct flags set on it. There are 2 main reasons:
|
||||
//
|
||||
// 1. We treat locals and exports of the same name as mutually exclusive within a container.
|
||||
// That means the binder will issue a Duplicate Identifier error if you mix locals and exports
|
||||
@@ -436,10 +442,7 @@ namespace ts {
|
||||
(node as JSDocTypedefTag).name.kind === SyntaxKind.Identifier &&
|
||||
((node as JSDocTypedefTag).name as Identifier).isInJSDocNamespace;
|
||||
if ((!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) || isJSDocTypedefInJSDocNamespace) {
|
||||
const exportKind =
|
||||
(symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0) |
|
||||
(symbolFlags & SymbolFlags.Type ? SymbolFlags.ExportType : 0) |
|
||||
(symbolFlags & SymbolFlags.Namespace ? SymbolFlags.ExportNamespace : 0);
|
||||
const exportKind = symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0;
|
||||
const local = declareSymbol(container.locals, /*parent*/ undefined, node, exportKind, symbolExcludes);
|
||||
local.exportSymbol = declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes);
|
||||
node.localSymbol = local;
|
||||
@@ -481,7 +484,7 @@ namespace ts {
|
||||
if (containerFlags & ContainerFlags.IsContainer) {
|
||||
container = blockScopeContainer = node;
|
||||
if (containerFlags & ContainerFlags.HasLocals) {
|
||||
container.locals = createMap<Symbol>();
|
||||
container.locals = createSymbolTable();
|
||||
}
|
||||
addToContainerChain(container);
|
||||
}
|
||||
@@ -595,7 +598,19 @@ namespace ts {
|
||||
// Binding of JsDocComment should be done before the current block scope container changes.
|
||||
// because the scope of JsDocComment should not be affected by whether the current node is a
|
||||
// container or not.
|
||||
forEach(node.jsDoc, bind);
|
||||
if (node.jsDoc) {
|
||||
if (isInJavaScriptFile(node)) {
|
||||
for (const j of node.jsDoc) {
|
||||
bind(j);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const j of node.jsDoc) {
|
||||
setParentPointers(node, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (checkUnreachable(node)) {
|
||||
bindEachChild(node);
|
||||
return;
|
||||
@@ -612,7 +627,7 @@ namespace ts {
|
||||
break;
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
bindForInOrForOfStatement(<ForInStatement | ForOfStatement>node);
|
||||
bindForInOrForOfStatement(<ForInOrOfStatement>node);
|
||||
break;
|
||||
case SyntaxKind.IfStatement:
|
||||
bindIfStatement(<IfStatement>node);
|
||||
@@ -950,7 +965,7 @@ namespace ts {
|
||||
currentFlow = finishFlowLabel(postLoopLabel);
|
||||
}
|
||||
|
||||
function bindForInOrForOfStatement(node: ForInStatement | ForOfStatement): void {
|
||||
function bindForInOrForOfStatement(node: ForInOrOfStatement): void {
|
||||
const preLoopLabel = createLoopLabel();
|
||||
const postLoopLabel = createBranchLabel();
|
||||
addAntecedent(preLoopLabel, currentFlow);
|
||||
@@ -994,7 +1009,7 @@ namespace ts {
|
||||
currentFlow = unreachableFlow;
|
||||
}
|
||||
|
||||
function findActiveLabel(name: string) {
|
||||
function findActiveLabel(name: __String) {
|
||||
if (activeLabels) {
|
||||
for (const label of activeLabels) {
|
||||
if (label.name === name) {
|
||||
@@ -1016,7 +1031,7 @@ namespace ts {
|
||||
function bindBreakOrContinueStatement(node: BreakOrContinueStatement): void {
|
||||
bind(node.label);
|
||||
if (node.label) {
|
||||
const activeLabel = findActiveLabel(node.label.text);
|
||||
const activeLabel = findActiveLabel(node.label.escapedText);
|
||||
if (activeLabel) {
|
||||
activeLabel.referenced = true;
|
||||
bindBreakOrContinueFlow(node, activeLabel.breakTarget, activeLabel.continueTarget);
|
||||
@@ -1157,8 +1172,8 @@ namespace ts {
|
||||
bindEach(node.statements);
|
||||
}
|
||||
|
||||
function pushActiveLabel(name: string, breakTarget: FlowLabel, continueTarget: FlowLabel): ActiveLabel {
|
||||
const activeLabel = {
|
||||
function pushActiveLabel(name: __String, breakTarget: FlowLabel, continueTarget: FlowLabel): ActiveLabel {
|
||||
const activeLabel: ActiveLabel = {
|
||||
name,
|
||||
breakTarget,
|
||||
continueTarget,
|
||||
@@ -1177,7 +1192,7 @@ namespace ts {
|
||||
const postStatementLabel = createBranchLabel();
|
||||
bind(node.label);
|
||||
addAntecedent(preStatementLabel, currentFlow);
|
||||
const activeLabel = pushActiveLabel(node.label.text, postStatementLabel, preStatementLabel);
|
||||
const activeLabel = pushActiveLabel(node.label.escapedText, postStatementLabel, preStatementLabel);
|
||||
bind(node.statement);
|
||||
popActiveLabel();
|
||||
if (!activeLabel.referenced && !options.allowUnusedLabels) {
|
||||
@@ -1317,7 +1332,7 @@ namespace ts {
|
||||
function bindInitializedVariableFlow(node: VariableDeclaration | ArrayBindingElement) {
|
||||
const name = !isOmittedExpression(node) ? node.name : undefined;
|
||||
if (isBindingPattern(name)) {
|
||||
for (const child of <ArrayBindingElement[]>name.elements) {
|
||||
for (const child of name.elements) {
|
||||
bindInitializedVariableFlow(child);
|
||||
}
|
||||
}
|
||||
@@ -1328,7 +1343,7 @@ namespace ts {
|
||||
|
||||
function bindVariableDeclarationFlow(node: VariableDeclaration) {
|
||||
bindEachChild(node);
|
||||
if (node.initializer || node.parent.parent.kind === SyntaxKind.ForInStatement || node.parent.parent.kind === SyntaxKind.ForOfStatement) {
|
||||
if (node.initializer || isForInOrOfStatement(node.parent.parent)) {
|
||||
bindInitializedVariableFlow(node);
|
||||
}
|
||||
}
|
||||
@@ -1385,14 +1400,12 @@ namespace ts {
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.TypeLiteral:
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
case SyntaxKind.JSDocRecordType:
|
||||
case SyntaxKind.JsxAttributes:
|
||||
return ContainerFlags.IsContainer;
|
||||
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
return ContainerFlags.IsContainer | ContainerFlags.IsInterface;
|
||||
|
||||
case SyntaxKind.JSDocFunctionType:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
case SyntaxKind.MappedType:
|
||||
@@ -1412,9 +1425,10 @@ namespace ts {
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.JSDocFunctionType:
|
||||
case SyntaxKind.FunctionType:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
case SyntaxKind.IndexSignature:
|
||||
case SyntaxKind.FunctionType:
|
||||
case SyntaxKind.ConstructorType:
|
||||
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;
|
||||
|
||||
@@ -1490,10 +1504,9 @@ namespace ts {
|
||||
return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes);
|
||||
|
||||
case SyntaxKind.TypeLiteral:
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.JSDocRecordType:
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
case SyntaxKind.JsxAttributes:
|
||||
// Interface/Object-types always have their children added to the 'members' of
|
||||
// their container. They are only accessible through an instance of their
|
||||
@@ -1521,7 +1534,7 @@ namespace ts {
|
||||
// All the children of these container types are never visible through another
|
||||
// symbol (i.e. through another symbol's 'exports' or 'members'). Instead,
|
||||
// they're only accessed 'lexically' (i.e. from code that exists underneath
|
||||
// their container in the tree. To accomplish this, we simply add their declared
|
||||
// their container in the tree). To accomplish this, we simply add their declared
|
||||
// symbol to the 'locals' of the container. These symbols can then be found as
|
||||
// the type checker walks up the containers, checking them for matching names.
|
||||
return declareSymbol(container.locals, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
|
||||
@@ -1633,10 +1646,10 @@ namespace ts {
|
||||
const symbol = createSymbol(SymbolFlags.Signature, getDeclarationName(node));
|
||||
addDeclarationToSymbol(symbol, node, SymbolFlags.Signature);
|
||||
|
||||
const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type");
|
||||
const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type);
|
||||
addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral);
|
||||
typeLiteralSymbol.members = createMap<Symbol>();
|
||||
typeLiteralSymbol.members.set(symbol.name, symbol);
|
||||
typeLiteralSymbol.members = createSymbolTable();
|
||||
typeLiteralSymbol.members.set(symbol.escapedName, symbol);
|
||||
}
|
||||
|
||||
function bindObjectLiteralExpression(node: ObjectLiteralExpression) {
|
||||
@@ -1646,14 +1659,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (inStrictMode) {
|
||||
const seen = createMap<ElementKind>();
|
||||
const seen = createUnderscoreEscapedMap<ElementKind>();
|
||||
|
||||
for (const prop of node.properties) {
|
||||
if (prop.kind === SyntaxKind.SpreadAssignment || prop.name.kind !== SyntaxKind.Identifier) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const identifier = <Identifier>prop.name;
|
||||
const identifier = prop.name;
|
||||
|
||||
// ECMA-262 11.1.5 Object Initializer
|
||||
// If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true
|
||||
@@ -1667,9 +1680,9 @@ namespace ts {
|
||||
? ElementKind.Property
|
||||
: ElementKind.Accessor;
|
||||
|
||||
const existingKind = seen.get(identifier.text);
|
||||
const existingKind = seen.get(identifier.escapedText);
|
||||
if (!existingKind) {
|
||||
seen.set(identifier.text, currentKind);
|
||||
seen.set(identifier.escapedText, currentKind);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1681,18 +1694,18 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__object");
|
||||
return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.Object);
|
||||
}
|
||||
|
||||
function bindJsxAttributes(node: JsxAttributes) {
|
||||
return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__jsxAttributes");
|
||||
return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.JSXAttributes);
|
||||
}
|
||||
|
||||
function bindJsxAttribute(node: JsxAttribute, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
|
||||
return declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
|
||||
}
|
||||
|
||||
function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string) {
|
||||
function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: __String) {
|
||||
const symbol = createSymbol(symbolFlags, name);
|
||||
addDeclarationToSymbol(symbol, node, symbolFlags);
|
||||
}
|
||||
@@ -1710,7 +1723,7 @@ namespace ts {
|
||||
// falls through
|
||||
default:
|
||||
if (!blockScopeContainer.locals) {
|
||||
blockScopeContainer.locals = createMap<Symbol>();
|
||||
blockScopeContainer.locals = createSymbolTable();
|
||||
addToContainerChain(blockScopeContainer);
|
||||
}
|
||||
declareSymbol(blockScopeContainer.locals, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
|
||||
@@ -1779,8 +1792,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isEvalOrArgumentsIdentifier(node: Node): boolean {
|
||||
return node.kind === SyntaxKind.Identifier &&
|
||||
((<Identifier>node).text === "eval" || (<Identifier>node).text === "arguments");
|
||||
return isIdentifier(node) && (node.escapedText === "eval" || node.escapedText === "arguments");
|
||||
}
|
||||
|
||||
function checkStrictModeEvalOrArguments(contextNode: Node, name: Node) {
|
||||
@@ -1791,7 +1803,7 @@ namespace ts {
|
||||
// otherwise report generic error message.
|
||||
const span = getErrorSpanForNode(file, name);
|
||||
file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length,
|
||||
getStrictModeEvalOrArgumentsMessage(contextNode), identifier.text));
|
||||
getStrictModeEvalOrArgumentsMessage(contextNode), unescapeLeadingUnderscores(identifier.escapedText)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1883,10 +1895,6 @@ namespace ts {
|
||||
file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2));
|
||||
}
|
||||
|
||||
function getDestructuringParameterName(node: Declaration) {
|
||||
return "__" + indexOf((<SignatureDeclaration>node.parent).parameters, node);
|
||||
}
|
||||
|
||||
function bind(node: Node): void {
|
||||
if (!node) {
|
||||
return;
|
||||
@@ -1903,7 +1911,7 @@ namespace ts {
|
||||
// Here the current node is "foo", which is a container, but the scope of "MyType" should
|
||||
// not be inside "foo". Therefore we always bind @typedef before bind the parent node,
|
||||
// and skip binding this tag later when binding all the other jsdoc tags.
|
||||
bindJSDocTypedefTagIfAny(node);
|
||||
if (isInJavaScriptFile(node)) bindJSDocTypedefTagIfAny(node);
|
||||
|
||||
// First we bind declaration nodes to a symbol if possible. We'll both create a symbol
|
||||
// and then potentially add the symbol to an appropriate symbol table. Possible
|
||||
@@ -1991,7 +1999,7 @@ namespace ts {
|
||||
// for typedef type names with namespaces, bind the new jsdoc type symbol here
|
||||
// because it requires all containing namespaces to be in effect, namely the
|
||||
// current "blockScopeContainer" needs to be set to its immediate namespace parent.
|
||||
if (isInJavaScriptFile(node) && (<Identifier>node).isInJSDocNamespace) {
|
||||
if ((<Identifier>node).isInJSDocNamespace) {
|
||||
let parentNode = node.parent;
|
||||
while (parentNode && parentNode.kind !== SyntaxKind.JSDocTypedefTag) {
|
||||
parentNode = parentNode.parent;
|
||||
@@ -2057,8 +2065,10 @@ namespace ts {
|
||||
case SyntaxKind.Parameter:
|
||||
return bindParameter(<ParameterDeclaration>node);
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
return bindVariableDeclarationOrBindingElement(<VariableDeclaration>node);
|
||||
case SyntaxKind.BindingElement:
|
||||
return bindVariableDeclarationOrBindingElement(<VariableDeclaration | BindingElement>node);
|
||||
node.flowNode = currentFlow;
|
||||
return bindVariableDeclarationOrBindingElement(<BindingElement>node);
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
return bindPropertyWorker(node as PropertyDeclaration | PropertySignature);
|
||||
@@ -2068,22 +2078,6 @@ namespace ts {
|
||||
case SyntaxKind.EnumMember:
|
||||
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes);
|
||||
|
||||
case SyntaxKind.SpreadAssignment:
|
||||
case SyntaxKind.JsxSpreadAttribute:
|
||||
let root = container;
|
||||
let hasRest = false;
|
||||
while (root.parent) {
|
||||
if (root.kind === SyntaxKind.ObjectLiteralExpression &&
|
||||
root.parent.kind === SyntaxKind.BinaryExpression &&
|
||||
(root.parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken &&
|
||||
(root.parent as BinaryExpression).left === root) {
|
||||
hasRest = true;
|
||||
break;
|
||||
}
|
||||
root = root.parent;
|
||||
}
|
||||
return;
|
||||
|
||||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
case SyntaxKind.IndexSignature:
|
||||
@@ -2105,11 +2099,13 @@ namespace ts {
|
||||
case SyntaxKind.SetAccessor:
|
||||
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes);
|
||||
case SyntaxKind.FunctionType:
|
||||
case SyntaxKind.JSDocFunctionType:
|
||||
case SyntaxKind.ConstructorType:
|
||||
return bindFunctionOrConstructorType(<SignatureDeclaration>node);
|
||||
case SyntaxKind.TypeLiteral:
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
case SyntaxKind.MappedType:
|
||||
return bindAnonymousTypeWorker(node as TypeLiteralNode | MappedTypeNode);
|
||||
return bindAnonymousTypeWorker(node as TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral);
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
return bindObjectLiteralExpression(<ObjectLiteralExpression>node);
|
||||
case SyntaxKind.FunctionExpression:
|
||||
@@ -2136,7 +2132,6 @@ namespace ts {
|
||||
return bindEnumDeclaration(<EnumDeclaration>node);
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return bindModuleDeclaration(<ModuleDeclaration>node);
|
||||
|
||||
// Jsx-attributes
|
||||
case SyntaxKind.JsxAttributes:
|
||||
return bindJsxAttributes(<JsxAttributes>node);
|
||||
@@ -2168,25 +2163,17 @@ namespace ts {
|
||||
case SyntaxKind.ModuleBlock:
|
||||
return updateStrictModeStatementList((<Block | ModuleBlock>node).statements);
|
||||
|
||||
default:
|
||||
if (isInJavaScriptFile(node)) return bindJSDocWorker(node);
|
||||
}
|
||||
}
|
||||
|
||||
function bindJSDocWorker(node: Node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.JSDocRecordMember:
|
||||
return bindPropertyWorker(node as JSDocRecordMember);
|
||||
case SyntaxKind.JSDocParameterTag:
|
||||
if (node.parent.kind !== SyntaxKind.JSDocTypeLiteral) {
|
||||
break;
|
||||
}
|
||||
// falls through
|
||||
case SyntaxKind.JSDocPropertyTag:
|
||||
return declareSymbolAndAddToSymbolTable(node as JSDocPropertyTag,
|
||||
(node as JSDocPropertyTag).isBracketed || ((node as JSDocPropertyTag).typeExpression && (node as JSDocPropertyTag).typeExpression.type.kind === SyntaxKind.JSDocOptionalType) ?
|
||||
SymbolFlags.Property | SymbolFlags.Optional : SymbolFlags.Property,
|
||||
SymbolFlags.PropertyExcludes);
|
||||
case SyntaxKind.JSDocFunctionType:
|
||||
return bindFunctionOrConstructorType(<SignatureDeclaration>node);
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
case SyntaxKind.JSDocRecordType:
|
||||
return bindAnonymousTypeWorker(node as JSDocTypeLiteral | JSDocRecordType);
|
||||
const propTag = node as JSDocPropertyLikeTag;
|
||||
const flags = propTag.isBracketed || propTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType ?
|
||||
SymbolFlags.Property | SymbolFlags.Optional :
|
||||
SymbolFlags.Property;
|
||||
return declareSymbolAndAddToSymbolTable(propTag, flags, SymbolFlags.PropertyExcludes);
|
||||
case SyntaxKind.JSDocTypedefTag: {
|
||||
const { fullName } = node as JSDocTypedefTag;
|
||||
if (!fullName || fullName.kind === SyntaxKind.Identifier) {
|
||||
@@ -2201,8 +2188,8 @@ namespace ts {
|
||||
return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
|
||||
}
|
||||
|
||||
function bindAnonymousTypeWorker(node: TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral | JSDocRecordType) {
|
||||
return bindAnonymousDeclaration(<Declaration>node, SymbolFlags.TypeLiteral, "__type");
|
||||
function bindAnonymousTypeWorker(node: TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral) {
|
||||
return bindAnonymousDeclaration(<Declaration>node, SymbolFlags.TypeLiteral, InternalSymbolName.Type);
|
||||
}
|
||||
|
||||
function checkTypePredicate(node: TypePredicateNode) {
|
||||
@@ -2224,7 +2211,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function bindSourceFileAsExternalModule() {
|
||||
bindAnonymousDeclaration(file, SymbolFlags.ValueModule, `"${removeFileExtension(file.fileName)}"`);
|
||||
bindAnonymousDeclaration(file, SymbolFlags.ValueModule, `"${removeFileExtension(file.fileName)}"` as __String);
|
||||
}
|
||||
|
||||
function bindExportAssignment(node: ExportAssignment | BinaryExpression) {
|
||||
@@ -2268,7 +2255,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
file.symbol.globalExports = file.symbol.globalExports || createMap<Symbol>();
|
||||
file.symbol.globalExports = file.symbol.globalExports || createSymbolTable();
|
||||
declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
|
||||
}
|
||||
|
||||
@@ -2302,7 +2289,7 @@ namespace ts {
|
||||
// When we create a property via 'exports.foo = bar', the 'exports.foo' property access
|
||||
// expression is the declaration
|
||||
setCommonJsModuleIndicator(node);
|
||||
declareSymbol(file.symbol.exports, file.symbol, <PropertyAccessExpression>node.left, SymbolFlags.Property | SymbolFlags.Export, SymbolFlags.None);
|
||||
declareSymbol(file.symbol.exports, file.symbol, <PropertyAccessExpression>node.left, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None);
|
||||
}
|
||||
|
||||
function isExportsOrModuleExportsOrAlias(node: Node): boolean {
|
||||
@@ -2312,21 +2299,17 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isNameOfExportsOrModuleExportsAliasDeclaration(node: Node) {
|
||||
if (node.kind === SyntaxKind.Identifier) {
|
||||
const symbol = lookupSymbolForName((<Identifier>node).text);
|
||||
if (symbol && symbol.valueDeclaration && symbol.valueDeclaration.kind === SyntaxKind.VariableDeclaration) {
|
||||
const declaration = symbol.valueDeclaration as VariableDeclaration;
|
||||
if (declaration.initializer) {
|
||||
return isExportsOrModuleExportsOrAliasOrAssignemnt(declaration.initializer);
|
||||
}
|
||||
}
|
||||
if (isIdentifier(node)) {
|
||||
const symbol = lookupSymbolForName(node.escapedText);
|
||||
return symbol && symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) &&
|
||||
symbol.valueDeclaration.initializer && isExportsOrModuleExportsOrAliasOrAssignment(symbol.valueDeclaration.initializer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isExportsOrModuleExportsOrAliasOrAssignemnt(node: Node): boolean {
|
||||
function isExportsOrModuleExportsOrAliasOrAssignment(node: Node): boolean {
|
||||
return isExportsOrModuleExportsOrAlias(node) ||
|
||||
(isAssignmentExpression(node, /*excludeCompoundAssignements*/ true) && (isExportsOrModuleExportsOrAliasOrAssignemnt(node.left) || isExportsOrModuleExportsOrAliasOrAssignemnt(node.right)));
|
||||
(isAssignmentExpression(node, /*excludeCompoundAssignements*/ true) && (isExportsOrModuleExportsOrAliasOrAssignment(node.left) || isExportsOrModuleExportsOrAliasOrAssignment(node.right)));
|
||||
}
|
||||
|
||||
function bindModuleExportsAssignment(node: BinaryExpression) {
|
||||
@@ -2343,7 +2326,7 @@ namespace ts {
|
||||
|
||||
// 'module.exports = expr' assignment
|
||||
setCommonJsModuleIndicator(node);
|
||||
declareSymbol(file.symbol.exports, file.symbol, node, SymbolFlags.Property | SymbolFlags.Export | SymbolFlags.ValueModule, SymbolFlags.None);
|
||||
declareSymbol(file.symbol.exports, file.symbol, node, SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.ValueModule, SymbolFlags.None);
|
||||
}
|
||||
|
||||
function bindThisPropertyAssignment(node: BinaryExpression) {
|
||||
@@ -2353,7 +2336,7 @@ namespace ts {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
// Declare a 'member' if the container is an ES5 class or ES6 constructor
|
||||
container.symbol.members = container.symbol.members || createMap<Symbol>();
|
||||
container.symbol.members = container.symbol.members || createSymbolTable();
|
||||
// It's acceptable for multiple 'this' assignments of the same identifier to occur
|
||||
declareSymbol(container.symbol.members, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
|
||||
break;
|
||||
@@ -2366,11 +2349,8 @@ namespace ts {
|
||||
// this.foo assignment in a JavaScript class
|
||||
// Bind this property to the containing class
|
||||
const containingClass = container.parent;
|
||||
const symbol = declareSymbol(hasModifier(container, ModifierFlags.Static) ? containingClass.symbol.exports : containingClass.symbol.members, containingClass.symbol, node, SymbolFlags.Property, SymbolFlags.None);
|
||||
if (symbol) {
|
||||
// symbols declared through 'this' property assignements can be overwritten by subsequent method declarations
|
||||
(symbol as Symbol).isReplaceableByMethod = true;
|
||||
}
|
||||
const symbolTable = hasModifier(container, ModifierFlags.Static) ? containingClass.symbol.exports : containingClass.symbol.members;
|
||||
declareSymbol(symbolTable, containingClass.symbol, node, SymbolFlags.Property, SymbolFlags.None, /*isReplaceableByMethod*/ true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2389,7 +2369,7 @@ namespace ts {
|
||||
constructorFunction.parent = classPrototype;
|
||||
classPrototype.parent = leftSideOfAssignment;
|
||||
|
||||
bindPropertyAssignment(constructorFunction.text, leftSideOfAssignment, /*isPrototypeProperty*/ true);
|
||||
bindPropertyAssignment(constructorFunction.escapedText, leftSideOfAssignment, /*isPrototypeProperty*/ true);
|
||||
}
|
||||
|
||||
function bindStaticPropertyAssignment(node: BinaryExpression) {
|
||||
@@ -2411,15 +2391,15 @@ namespace ts {
|
||||
bindExportsPropertyAssignment(node);
|
||||
}
|
||||
else {
|
||||
bindPropertyAssignment(target.text, leftSideOfAssignment, /*isPrototypeProperty*/ false);
|
||||
bindPropertyAssignment(target.escapedText, leftSideOfAssignment, /*isPrototypeProperty*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
function lookupSymbolForName(name: string) {
|
||||
function lookupSymbolForName(name: __String) {
|
||||
return (container.symbol && container.symbol.exports && container.symbol.exports.get(name)) || (container.locals && container.locals.get(name));
|
||||
}
|
||||
|
||||
function bindPropertyAssignment(functionName: string, propertyAccessExpression: PropertyAccessExpression, isPrototypeProperty: boolean) {
|
||||
function bindPropertyAssignment(functionName: __String, propertyAccessExpression: PropertyAccessExpression, isPrototypeProperty: boolean) {
|
||||
let targetSymbol = lookupSymbolForName(functionName);
|
||||
|
||||
if (targetSymbol && isDeclarationOfFunctionOrClassExpression(targetSymbol)) {
|
||||
@@ -2432,8 +2412,8 @@ namespace ts {
|
||||
|
||||
// Set up the members collection if it doesn't exist already
|
||||
const symbolTable = isPrototypeProperty ?
|
||||
(targetSymbol.members || (targetSymbol.members = createMap<Symbol>())) :
|
||||
(targetSymbol.exports || (targetSymbol.exports = createMap<Symbol>()));
|
||||
(targetSymbol.members || (targetSymbol.members = createSymbolTable())) :
|
||||
(targetSymbol.exports || (targetSymbol.exports = createSymbolTable()));
|
||||
|
||||
// Declare the method/property
|
||||
declareSymbol(symbolTable, targetSymbol, propertyAccessExpression, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
|
||||
@@ -2452,11 +2432,11 @@ namespace ts {
|
||||
bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes);
|
||||
}
|
||||
else {
|
||||
const bindingName = node.name ? node.name.text : "__class";
|
||||
const bindingName = node.name ? node.name.escapedText : InternalSymbolName.Class;
|
||||
bindAnonymousDeclaration(node, SymbolFlags.Class, bindingName);
|
||||
// Add name of class expression into the map for semantic classifier
|
||||
if (node.name) {
|
||||
classifiableNames.set(node.name.text, node.name.text);
|
||||
classifiableNames.set(node.name.escapedText, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2471,15 +2451,15 @@ namespace ts {
|
||||
// Note: we check for this here because this class may be merging into a module. The
|
||||
// module might have an exported variable called 'prototype'. We can't allow that as
|
||||
// that would clash with the built-in 'prototype' for the class.
|
||||
const prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype");
|
||||
const symbolExport = symbol.exports.get(prototypeSymbol.name);
|
||||
const prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype" as __String);
|
||||
const symbolExport = symbol.exports.get(prototypeSymbol.escapedName);
|
||||
if (symbolExport) {
|
||||
if (node.name) {
|
||||
node.name.parent = node;
|
||||
}
|
||||
file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations[0], Diagnostics.Duplicate_identifier_0, prototypeSymbol.name));
|
||||
file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations[0], Diagnostics.Duplicate_identifier_0, unescapeLeadingUnderscores(prototypeSymbol.escapedName)));
|
||||
}
|
||||
symbol.exports.set(prototypeSymbol.name, prototypeSymbol);
|
||||
symbol.exports.set(prototypeSymbol.escapedName, prototypeSymbol);
|
||||
prototypeSymbol.parent = symbol;
|
||||
}
|
||||
|
||||
@@ -2524,7 +2504,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (isBindingPattern(node.name)) {
|
||||
bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node));
|
||||
bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, "__" + indexOf(node.parent.parameters, node) as __String);
|
||||
}
|
||||
else {
|
||||
declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes);
|
||||
@@ -2565,7 +2545,7 @@ namespace ts {
|
||||
node.flowNode = currentFlow;
|
||||
}
|
||||
checkStrictModeFunctionName(node);
|
||||
const bindingName = node.name ? node.name.text : "__function";
|
||||
const bindingName = node.name ? node.name.escapedText : InternalSymbolName.Function;
|
||||
return bindAnonymousDeclaration(node, SymbolFlags.Function, bindingName);
|
||||
}
|
||||
|
||||
@@ -2579,7 +2559,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
return hasDynamicName(node)
|
||||
? bindAnonymousDeclaration(node, symbolFlags, "__computed")
|
||||
? bindAnonymousDeclaration(node, symbolFlags, InternalSymbolName.Computed)
|
||||
: declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
|
||||
}
|
||||
|
||||
@@ -3600,4 +3580,13 @@ namespace ts {
|
||||
return TransformFlags.NodeExcludes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "Binds" JSDoc nodes in TypeScript code.
|
||||
* Since we will never create symbols for JSDoc, we just set parent pointers instead.
|
||||
*/
|
||||
function setParentPointers(parent: Node, child: Node): void {
|
||||
child.parent = parent;
|
||||
forEachChild(child, (childsChild) => setParentPointers(child, childsChild));
|
||||
}
|
||||
}
|
||||
|
||||
+1431
-1195
File diff suppressed because it is too large
Load Diff
+706
-238
File diff suppressed because it is too large
Load Diff
+232
-186
@@ -2,8 +2,11 @@
|
||||
/// <reference path="performance.ts" />
|
||||
|
||||
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 = "2.5";
|
||||
/** The version of the TypeScript compiler release */
|
||||
export const version = "2.4.0";
|
||||
export const version = `${versionMajorMinor}.0`;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -46,13 +49,31 @@ namespace ts {
|
||||
return new MapCtr<T>();
|
||||
}
|
||||
|
||||
/** Create a new escaped identifier map. */
|
||||
export function createUnderscoreEscapedMap<T>(): UnderscoreEscapedMap<T> {
|
||||
return new MapCtr<T>() as UnderscoreEscapedMap<T>;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createSymbolTable(symbols?: ReadonlyArray<Symbol>): SymbolTable {
|
||||
const result = createMap<Symbol>() as SymbolTable;
|
||||
if (symbols) {
|
||||
for (const symbol of symbols) {
|
||||
result.set(symbol.escapedName, symbol);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function createMapFromTemplate<T>(template?: MapLike<T>): Map<T> {
|
||||
const map: Map<T> = new MapCtr<T>();
|
||||
|
||||
// Copies keys/values from template. Note that for..in will not throw if
|
||||
// template is undefined, and instead will just exit the loop.
|
||||
for (const key in template) if (hasOwnProperty.call(template, key)) {
|
||||
map.set(key, template[key]);
|
||||
for (const key in template) {
|
||||
if (hasOwnProperty.call(template, key)) {
|
||||
map.set(key, template[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
@@ -69,7 +90,7 @@ namespace ts {
|
||||
|
||||
class MapIterator<T, U extends (string | T | [string, T])> {
|
||||
private data: MapLike<T>;
|
||||
private keys: string[];
|
||||
private keys: ReadonlyArray<string>;
|
||||
private index = 0;
|
||||
private selector: (data: MapLike<T>, key: string) => U;
|
||||
constructor(data: MapLike<T>, selector: (data: MapLike<T>, key: string) => U) {
|
||||
@@ -143,54 +164,6 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
export function createFileMap<T>(keyMapper?: (key: string) => string): FileMap<T> {
|
||||
const files = createMap<T>();
|
||||
return {
|
||||
get,
|
||||
set,
|
||||
contains,
|
||||
remove,
|
||||
forEachValue: forEachValueInMap,
|
||||
getKeys,
|
||||
clear,
|
||||
};
|
||||
|
||||
function forEachValueInMap(f: (key: Path, value: T) => void) {
|
||||
files.forEach((file, key) => {
|
||||
f(<Path>key, file);
|
||||
});
|
||||
}
|
||||
|
||||
function getKeys() {
|
||||
return arrayFrom(files.keys()) as Path[];
|
||||
}
|
||||
|
||||
// path should already be well-formed so it does not need to be normalized
|
||||
function get(path: Path): T {
|
||||
return files.get(toKey(path));
|
||||
}
|
||||
|
||||
function set(path: Path, value: T) {
|
||||
files.set(toKey(path), value);
|
||||
}
|
||||
|
||||
function contains(path: Path) {
|
||||
return files.has(toKey(path));
|
||||
}
|
||||
|
||||
function remove(path: Path) {
|
||||
files.delete(toKey(path));
|
||||
}
|
||||
|
||||
function clear() {
|
||||
files.clear();
|
||||
}
|
||||
|
||||
function toKey(path: Path): string {
|
||||
return keyMapper ? keyMapper(path) : path;
|
||||
}
|
||||
}
|
||||
|
||||
export function toPath(fileName: string, basePath: string, getCanonicalFileName: (path: string) => string): Path {
|
||||
const nonCanonicalizedPath = isRootedDiskPath(fileName)
|
||||
? normalizePath(fileName)
|
||||
@@ -204,7 +177,7 @@ namespace ts {
|
||||
GreaterThan = 1
|
||||
}
|
||||
|
||||
export function length(array: any[]) {
|
||||
export function length(array: ReadonlyArray<any>) {
|
||||
return array ? array.length : 0;
|
||||
}
|
||||
|
||||
@@ -213,7 +186,7 @@ namespace ts {
|
||||
* returns a truthy value, then returns that value.
|
||||
* If no such value is found, the callback is applied to each element of array and undefined is returned.
|
||||
*/
|
||||
export function forEach<T, U>(array: T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined {
|
||||
export function forEach<T, U>(array: ReadonlyArray<T> | undefined, callback: (element: T, index: number) => U | undefined): U | undefined {
|
||||
if (array) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const result = callback(array[i], i);
|
||||
@@ -246,14 +219,14 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function zipWith<T, U>(arrayA: T[], arrayB: U[], callback: (a: T, b: U, index: number) => void): void {
|
||||
export function zipWith<T, U>(arrayA: ReadonlyArray<T>, arrayB: ReadonlyArray<U>, callback: (a: T, b: U, index: number) => void): void {
|
||||
Debug.assert(arrayA.length === arrayB.length);
|
||||
for (let i = 0; i < arrayA.length; i++) {
|
||||
callback(arrayA[i], arrayB[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
export function zipToMap<T>(keys: string[], values: T[]): Map<T> {
|
||||
export function zipToMap<T>(keys: ReadonlyArray<string>, values: ReadonlyArray<T>): Map<T> {
|
||||
Debug.assert(keys.length === values.length);
|
||||
const map = createMap<T>();
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
@@ -267,7 +240,7 @@ namespace ts {
|
||||
* returns a falsey value, then returns false.
|
||||
* If no such value is found, the callback is applied to each element of array and `true` is returned.
|
||||
*/
|
||||
export function every<T>(array: T[], callback: (element: T, index: number) => boolean): boolean {
|
||||
export function every<T>(array: ReadonlyArray<T>, callback: (element: T, index: number) => boolean): boolean {
|
||||
if (array) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (!callback(array[i], i)) {
|
||||
@@ -280,7 +253,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
/** Works like Array.prototype.find, returning `undefined` if no element satisfying the predicate is found. */
|
||||
export function find<T>(array: T[], predicate: (element: T, index: number) => boolean): T | undefined {
|
||||
export function find<T>(array: ReadonlyArray<T>, predicate: (element: T, index: number) => boolean): T | undefined {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const value = array[i];
|
||||
if (predicate(value, i)) {
|
||||
@@ -291,7 +264,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
/** Works like Array.prototype.findIndex, returning `-1` if no element satisfying the predicate is found. */
|
||||
export function findIndex<T>(array: T[], predicate: (element: T, index: number) => boolean): number {
|
||||
export function findIndex<T>(array: ReadonlyArray<T>, predicate: (element: T, index: number) => boolean): number {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (predicate(array[i], i)) {
|
||||
return i;
|
||||
@@ -304,7 +277,7 @@ namespace ts {
|
||||
* Returns the first truthy result of `callback`, or else fails.
|
||||
* This is like `forEach`, but never returns undefined.
|
||||
*/
|
||||
export function findMap<T, U>(array: T[], callback: (element: T, index: number) => U | undefined): U {
|
||||
export function findMap<T, U>(array: ReadonlyArray<T>, callback: (element: T, index: number) => U | undefined): U {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const result = callback(array[i], i);
|
||||
if (result) {
|
||||
@@ -314,7 +287,7 @@ namespace ts {
|
||||
Debug.fail();
|
||||
}
|
||||
|
||||
export function contains<T>(array: T[], value: T): boolean {
|
||||
export function contains<T>(array: ReadonlyArray<T>, value: T): boolean {
|
||||
if (array) {
|
||||
for (const v of array) {
|
||||
if (v === value) {
|
||||
@@ -325,7 +298,7 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function indexOf<T>(array: T[], value: T): number {
|
||||
export function indexOf<T>(array: ReadonlyArray<T>, value: T): number {
|
||||
if (array) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (array[i] === value) {
|
||||
@@ -336,7 +309,7 @@ namespace ts {
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function indexOfAnyCharCode(text: string, charCodes: number[], start?: number): number {
|
||||
export function indexOfAnyCharCode(text: string, charCodes: ReadonlyArray<number>, start?: number): number {
|
||||
for (let i = start || 0; i < text.length; i++) {
|
||||
if (contains(charCodes, text.charCodeAt(i))) {
|
||||
return i;
|
||||
@@ -345,7 +318,7 @@ namespace ts {
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function countWhere<T>(array: T[], predicate: (x: T, i: number) => boolean): number {
|
||||
export function countWhere<T>(array: ReadonlyArray<T>, predicate: (x: T, i: number) => boolean): number {
|
||||
let count = 0;
|
||||
if (array) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
@@ -364,6 +337,8 @@ namespace ts {
|
||||
*/
|
||||
export function filter<T, U extends T>(array: T[], f: (x: T) => x is U): U[];
|
||||
export function filter<T>(array: T[], f: (x: T) => boolean): T[];
|
||||
export function filter<T, U extends T>(array: ReadonlyArray<T>, f: (x: T) => x is U): ReadonlyArray<U>;
|
||||
export function filter<T, U extends T>(array: ReadonlyArray<T>, f: (x: T) => boolean): ReadonlyArray<T>;
|
||||
export function filter<T>(array: T[], f: (x: T) => boolean): T[] {
|
||||
if (array) {
|
||||
const len = array.length;
|
||||
@@ -411,7 +386,11 @@ namespace ts {
|
||||
array.length = outIndex;
|
||||
}
|
||||
|
||||
export function map<T, U>(array: T[], f: (x: T, i: number) => U): U[] {
|
||||
export function clear(array: {}[]): void {
|
||||
array.length = 0;
|
||||
}
|
||||
|
||||
export function map<T, U>(array: ReadonlyArray<T>, f: (x: T, i: number) => U): U[] {
|
||||
let result: U[];
|
||||
if (array) {
|
||||
result = [];
|
||||
@@ -423,6 +402,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Maps from T to T and avoids allocation if all elements map to themselves
|
||||
export function sameMap<T>(array: T[], f: (x: T, i: number) => T): T[];
|
||||
export function sameMap<T>(array: ReadonlyArray<T>, f: (x: T, i: number) => T): ReadonlyArray<T>;
|
||||
export function sameMap<T>(array: T[], f: (x: T, i: number) => T): T[] {
|
||||
let result: T[];
|
||||
if (array) {
|
||||
@@ -448,7 +429,7 @@ namespace ts {
|
||||
*
|
||||
* @param array The array to flatten.
|
||||
*/
|
||||
export function flatten<T>(array: (T | T[])[]): T[] {
|
||||
export function flatten<T>(array: ReadonlyArray<T | ReadonlyArray<T>>): T[] {
|
||||
let result: T[];
|
||||
if (array) {
|
||||
result = [];
|
||||
@@ -473,7 +454,7 @@ 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: T[] | undefined, mapfn: (x: T, i: number) => U | U[] | undefined): U[] | undefined {
|
||||
export function flatMap<T, U>(array: ReadonlyArray<T> | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray<U> | undefined): U[] | undefined {
|
||||
let result: U[];
|
||||
if (array) {
|
||||
result = [];
|
||||
@@ -492,6 +473,17 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function flatMapIter<T, U>(iter: Iterator<T>, mapfn: (x: T) => U[] | undefined): U[] {
|
||||
const result: U[] = [];
|
||||
while (true) {
|
||||
const { value, done } = iter.next();
|
||||
if (done) break;
|
||||
const res = mapfn(value);
|
||||
if (res) result.push(...res);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps an array. If the mapped value is an array, it is spread into the result.
|
||||
* Avoids allocation if all elements map to themselves.
|
||||
@@ -499,6 +491,8 @@ namespace ts {
|
||||
* @param array The array to map.
|
||||
* @param mapfn The callback used to map the result into one or more values.
|
||||
*/
|
||||
export function sameFlatMap<T>(array: T[], mapfn: (x: T, i: number) => T | ReadonlyArray<T>): T[];
|
||||
export function sameFlatMap<T>(array: ReadonlyArray<T>, mapfn: (x: T, i: number) => T | ReadonlyArray<T>): ReadonlyArray<T>;
|
||||
export function sameFlatMap<T>(array: T[], mapfn: (x: T, i: number) => T | T[]): T[] {
|
||||
let result: T[];
|
||||
if (array) {
|
||||
@@ -521,8 +515,8 @@ namespace ts {
|
||||
return result || array;
|
||||
}
|
||||
|
||||
export function mapDefined<T>(array: ReadonlyArray<T>, mapFn: (x: T, i: number) => T | undefined): ReadonlyArray<T> {
|
||||
const result: T[] = [];
|
||||
export function mapDefined<T, U>(array: ReadonlyArray<T>, mapFn: (x: T, i: number) => U | undefined): U[] {
|
||||
const result: U[] = [];
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const item = array[i];
|
||||
const mapped = mapFn(item, i);
|
||||
@@ -537,7 +531,7 @@ namespace ts {
|
||||
* Computes the first matching span of elements and returns a tuple of the first span
|
||||
* and the remaining elements.
|
||||
*/
|
||||
export function span<T>(array: T[], f: (x: T, i: number) => boolean): [T[], T[]] {
|
||||
export function span<T>(array: ReadonlyArray<T>, f: (x: T, i: number) => boolean): [T[], T[]] {
|
||||
if (array) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (!f(array[i], i)) {
|
||||
@@ -557,7 +551,7 @@ namespace ts {
|
||||
* @param keyfn A callback used to select the key for an element.
|
||||
* @param mapfn A callback used to map a contiguous chunk of values to a single value.
|
||||
*/
|
||||
export function spanMap<T, K, U>(array: T[], keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] {
|
||||
export function spanMap<T, K, U>(array: ReadonlyArray<T>, keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] {
|
||||
let result: U[];
|
||||
if (array) {
|
||||
result = [];
|
||||
@@ -597,7 +591,7 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function mapEntries<T, U>(map: Map<T>, f: (key: string, value: T) => [string, U]): Map<U> {
|
||||
export function mapEntries<T, U>(map: ReadonlyMap<T>, f: (key: string, value: T) => [string, U]): Map<U> {
|
||||
if (!map) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -610,7 +604,7 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function some<T>(array: T[], predicate?: (value: T) => boolean): boolean {
|
||||
export function some<T>(array: ReadonlyArray<T>, predicate?: (value: T) => boolean): boolean {
|
||||
if (array) {
|
||||
if (predicate) {
|
||||
for (const v of array) {
|
||||
@@ -626,6 +620,8 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function concatenate<T>(array1: T[], array2: T[]): T[];
|
||||
export function concatenate<T>(array1: ReadonlyArray<T>, array2: ReadonlyArray<T>): ReadonlyArray<T>;
|
||||
export function concatenate<T>(array1: T[], array2: T[]): T[] {
|
||||
if (!some(array2)) return array1;
|
||||
if (!some(array1)) return array2;
|
||||
@@ -633,7 +629,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
// TODO: fixme (N^2) - add optional comparer so collection can be sorted before deduplication.
|
||||
export function deduplicate<T>(array: T[], areEqual?: (a: T, b: T) => boolean): T[] {
|
||||
export function deduplicate<T>(array: ReadonlyArray<T>, areEqual?: (a: T, b: T) => boolean): T[] {
|
||||
let result: T[];
|
||||
if (array) {
|
||||
result = [];
|
||||
@@ -690,6 +686,8 @@ namespace ts {
|
||||
/**
|
||||
* Compacts an array, removing any falsey elements.
|
||||
*/
|
||||
export function compact<T>(array: T[]): T[];
|
||||
export function compact<T>(array: ReadonlyArray<T>): ReadonlyArray<T>;
|
||||
export function compact<T>(array: T[]): T[] {
|
||||
let result: T[];
|
||||
if (array) {
|
||||
@@ -713,7 +711,7 @@ namespace ts {
|
||||
* are not present in `arrayA` but are present in `arrayB`. Assumes both arrays are sorted
|
||||
* based on the provided comparer.
|
||||
*/
|
||||
export function relativeComplement<T>(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: (x: T, y: T) => Comparison = compareValues, offsetA = 0, offsetB = 0): T[] | undefined {
|
||||
export function relativeComplement<T>(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: Comparer<T> = compareValues, offsetA = 0, offsetB = 0): T[] | undefined {
|
||||
if (!arrayB || !arrayA || arrayB.length === 0 || arrayA.length === 0) return arrayB;
|
||||
const result: T[] = [];
|
||||
outer: for (; offsetB < arrayB.length; offsetB++) {
|
||||
@@ -729,10 +727,11 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function sum(array: any[], prop: string): number {
|
||||
export function sum<T extends Record<K, number>, K extends string>(array: T[], prop: K): number {
|
||||
let result = 0;
|
||||
for (const v of array) {
|
||||
result += v[prop];
|
||||
// Note: we need the following type assertion because of GH #17069
|
||||
result += v[prop] as number;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -756,7 +755,7 @@ namespace ts {
|
||||
* Gets the actual offset into an array for a relative offset. Negative offsets indicate a
|
||||
* position offset from the end of the array.
|
||||
*/
|
||||
function toOffset(array: any[], offset: number) {
|
||||
function toOffset(array: ReadonlyArray<any>, offset: number) {
|
||||
return offset < 0 ? array.length + offset : offset;
|
||||
}
|
||||
|
||||
@@ -770,7 +769,7 @@ namespace ts {
|
||||
* @param start The offset in `from` at which to start copying values.
|
||||
* @param end The offset in `from` at which to stop copying values (non-inclusive).
|
||||
*/
|
||||
export function addRange<T>(to: T[] | undefined, from: T[] | undefined, start?: number, end?: number): T[] | undefined {
|
||||
export function addRange<T>(to: T[] | undefined, from: ReadonlyArray<T> | undefined, start?: number, end?: number): T[] | undefined {
|
||||
if (from === undefined) return to;
|
||||
if (to === undefined) return from.slice(start, end);
|
||||
start = start === undefined ? 0 : toOffset(from, start);
|
||||
@@ -787,14 +786,14 @@ namespace ts {
|
||||
/**
|
||||
* Stable sort of an array. Elements equal to each other maintain their relative position in the array.
|
||||
*/
|
||||
export function stableSort<T>(array: T[], comparer: (x: T, y: T) => Comparison = compareValues) {
|
||||
export function stableSort<T>(array: ReadonlyArray<T>, comparer: Comparer<T> = compareValues) {
|
||||
return array
|
||||
.map((_, i) => i) // create array of indices
|
||||
.sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y)) // sort indices by value then position
|
||||
.map(i => array[i]); // get sorted array
|
||||
}
|
||||
|
||||
export function rangeEquals<T>(array1: T[], array2: T[], pos: number, end: number) {
|
||||
export function rangeEquals<T>(array1: ReadonlyArray<T>, array2: ReadonlyArray<T>, pos: number, end: number) {
|
||||
while (pos < end) {
|
||||
if (array1[pos] !== array2[pos]) {
|
||||
return false;
|
||||
@@ -808,7 +807,7 @@ namespace ts {
|
||||
* Returns the element at a specific offset in an array if non-empty, `undefined` otherwise.
|
||||
* A negative offset indicates the element should be retrieved from the end of the array.
|
||||
*/
|
||||
export function elementAt<T>(array: T[] | undefined, offset: number): T | undefined {
|
||||
export function elementAt<T>(array: ReadonlyArray<T> | undefined, offset: number): T | undefined {
|
||||
if (array) {
|
||||
offset = toOffset(array, offset);
|
||||
if (offset < array.length) {
|
||||
@@ -821,21 +820,21 @@ namespace ts {
|
||||
/**
|
||||
* Returns the first element of an array if non-empty, `undefined` otherwise.
|
||||
*/
|
||||
export function firstOrUndefined<T>(array: T[]): T | undefined {
|
||||
export function firstOrUndefined<T>(array: ReadonlyArray<T>): T | undefined {
|
||||
return elementAt(array, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last element of an array if non-empty, `undefined` otherwise.
|
||||
*/
|
||||
export function lastOrUndefined<T>(array: T[]): T | undefined {
|
||||
export function lastOrUndefined<T>(array: ReadonlyArray<T>): T | undefined {
|
||||
return elementAt(array, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the only element of an array if it contains only one element, `undefined` otherwise.
|
||||
*/
|
||||
export function singleOrUndefined<T>(array: T[]): T | undefined {
|
||||
export function singleOrUndefined<T>(array: ReadonlyArray<T>): T | undefined {
|
||||
return array && array.length === 1
|
||||
? array[0]
|
||||
: undefined;
|
||||
@@ -845,18 +844,22 @@ namespace ts {
|
||||
* Returns the only element of an array if it contains only one element; otheriwse, returns the
|
||||
* array.
|
||||
*/
|
||||
export function singleOrMany<T>(array: T[]): T | T[];
|
||||
export function singleOrMany<T>(array: ReadonlyArray<T>): T | ReadonlyArray<T>;
|
||||
export function singleOrMany<T>(array: T[]): T | T[] {
|
||||
return array && array.length === 1
|
||||
? array[0]
|
||||
: array;
|
||||
}
|
||||
|
||||
export function replaceElement<T>(array: T[], index: number, value: T): T[] {
|
||||
export function replaceElement<T>(array: ReadonlyArray<T>, index: number, value: T): T[] {
|
||||
const result = array.slice(0);
|
||||
result[index] = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
export type Comparer<T> = (a: T, b: T) => Comparison;
|
||||
|
||||
/**
|
||||
* Performs a binary search, finding the index at which 'value' occurs in 'array'.
|
||||
* If no such index is found, returns the 2's-complement of first index at which
|
||||
@@ -864,7 +867,7 @@ namespace ts {
|
||||
* @param array A sorted array whose first element must be no larger than number
|
||||
* @param number The value to be searched for in the array.
|
||||
*/
|
||||
export function binarySearch<T>(array: T[], value: T, comparer?: (v1: T, v2: T) => number, offset?: number): number {
|
||||
export function binarySearch<T>(array: ReadonlyArray<T>, value: T, comparer?: Comparer<T>, offset?: number): number {
|
||||
if (!array || array.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -893,8 +896,8 @@ namespace ts {
|
||||
return ~low;
|
||||
}
|
||||
|
||||
export function reduceLeft<T, U>(array: T[], f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U;
|
||||
export function reduceLeft<T>(array: T[], f: (memo: T, value: T, i: number) => T): T;
|
||||
export function reduceLeft<T, U>(array: ReadonlyArray<T>, f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U;
|
||||
export function reduceLeft<T>(array: ReadonlyArray<T>, f: (memo: T, value: T, i: number) => T): T;
|
||||
export function reduceLeft<T>(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T {
|
||||
if (array && array.length > 0) {
|
||||
const size = array.length;
|
||||
@@ -919,8 +922,8 @@ namespace ts {
|
||||
return initial;
|
||||
}
|
||||
|
||||
export function reduceRight<T, U>(array: T[], f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U;
|
||||
export function reduceRight<T>(array: T[], f: (memo: T, value: T, i: number) => T): T;
|
||||
export function reduceRight<T, U>(array: ReadonlyArray<T>, f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U;
|
||||
export function reduceRight<T>(array: ReadonlyArray<T>, f: (memo: T, value: T, i: number) => T): T;
|
||||
export function reduceRight<T>(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T {
|
||||
if (array) {
|
||||
const size = array.length;
|
||||
@@ -953,7 +956,7 @@ namespace ts {
|
||||
* @param map A map-like.
|
||||
* @param key A property key.
|
||||
*/
|
||||
export function hasProperty<T>(map: MapLike<T>, key: string): boolean {
|
||||
export function hasProperty(map: MapLike<any>, key: string): boolean {
|
||||
return hasOwnProperty.call(map, key);
|
||||
}
|
||||
|
||||
@@ -977,9 +980,12 @@ namespace ts {
|
||||
*/
|
||||
export function getOwnKeys<T>(map: MapLike<T>): string[] {
|
||||
const keys: string[] = [];
|
||||
for (const key in map) if (hasOwnProperty.call(map, key)) {
|
||||
keys.push(key);
|
||||
for (const key in map) {
|
||||
if (hasOwnProperty.call(map, key)) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
@@ -994,23 +1000,17 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function convertToArray<T, U>(iterator: Iterator<T>, f: (value: T) => U) {
|
||||
const result: U[] = [];
|
||||
for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) {
|
||||
result.push(f(value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls `callback` for each entry in the map, returning the first truthy result.
|
||||
* Use `map.forEach` instead for normal iteration.
|
||||
*/
|
||||
export function forEachEntry<T, U>(map: Map<T>, callback: (value: T, key: string) => U | undefined): U | undefined {
|
||||
export function forEachEntry<T, U>(map: ReadonlyUnderscoreEscapedMap<T>, callback: (value: T, key: __String) => U | undefined): U | undefined;
|
||||
export function forEachEntry<T, U>(map: ReadonlyMap<T>, callback: (value: T, key: string) => U | undefined): U | undefined;
|
||||
export function forEachEntry<T, U>(map: ReadonlyUnderscoreEscapedMap<T> | ReadonlyMap<T>, callback: (value: T, key: (string & __String)) => U | undefined): U | undefined {
|
||||
const iterator = map.entries();
|
||||
for (let { value: pair, done } = iterator.next(); !done; { value: pair, done } = iterator.next()) {
|
||||
const [key, value] = pair;
|
||||
const result = callback(value, key);
|
||||
const result = callback(value, key as (string & __String));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
@@ -1019,10 +1019,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
/** `forEachEntry` for just keys. */
|
||||
export function forEachKey<T>(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined {
|
||||
export function forEachKey<T>(map: ReadonlyUnderscoreEscapedMap<{}>, callback: (key: __String) => T | undefined): T | undefined;
|
||||
export function forEachKey<T>(map: ReadonlyMap<{}>, callback: (key: string) => T | undefined): T | undefined;
|
||||
export function forEachKey<T>(map: ReadonlyUnderscoreEscapedMap<{}> | ReadonlyMap<{}>, callback: (key: string & __String) => T | undefined): T | undefined {
|
||||
const iterator = map.keys();
|
||||
for (let { value: key, done } = iterator.next(); !done; { value: key, done } = iterator.next()) {
|
||||
const result = callback(key);
|
||||
const result = callback(key as string & __String);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
@@ -1031,9 +1033,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
/** Copy entries from `source` to `target`. */
|
||||
export function copyEntries<T>(source: Map<T>, target: Map<T>): void {
|
||||
source.forEach((value, key) => {
|
||||
target.set(key, value);
|
||||
export function copyEntries<T>(source: ReadonlyUnderscoreEscapedMap<T>, target: UnderscoreEscapedMap<T>): void;
|
||||
export function copyEntries<T>(source: ReadonlyMap<T>, target: Map<T>): void;
|
||||
export function copyEntries<T, U extends UnderscoreEscapedMap<T> | Map<T>>(source: U, target: U): void {
|
||||
(source as Map<T>).forEach((value, key) => {
|
||||
(target as Map<T>).set(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1042,8 +1046,10 @@ namespace ts {
|
||||
export function assign<T1 extends MapLike<{}>>(t: T1, ...args: any[]): any;
|
||||
export function assign<T1 extends MapLike<{}>>(t: T1, ...args: any[]) {
|
||||
for (const arg of args) {
|
||||
for (const p in arg) if (hasProperty(arg, p)) {
|
||||
t[p] = arg[p];
|
||||
for (const p in arg) {
|
||||
if (hasProperty(arg, p)) {
|
||||
t[p] = arg[p];
|
||||
}
|
||||
}
|
||||
}
|
||||
return t;
|
||||
@@ -1058,13 +1064,19 @@ namespace ts {
|
||||
export function equalOwnProperties<T>(left: MapLike<T>, right: MapLike<T>, equalityComparer?: (left: T, right: T) => boolean) {
|
||||
if (left === right) return true;
|
||||
if (!left || !right) return false;
|
||||
for (const key in left) if (hasOwnProperty.call(left, key)) {
|
||||
if (!hasOwnProperty.call(right, key) === undefined) return false;
|
||||
if (equalityComparer ? !equalityComparer(left[key], right[key]) : left[key] !== right[key]) return false;
|
||||
for (const key in left) {
|
||||
if (hasOwnProperty.call(left, key)) {
|
||||
if (!hasOwnProperty.call(right, key) === undefined) return false;
|
||||
if (equalityComparer ? !equalityComparer(left[key], right[key]) : left[key] !== right[key]) return false;
|
||||
}
|
||||
}
|
||||
for (const key in right) if (hasOwnProperty.call(right, key)) {
|
||||
if (!hasOwnProperty.call(left, key)) return false;
|
||||
|
||||
for (const key in right) {
|
||||
if (hasOwnProperty.call(right, key)) {
|
||||
if (!hasOwnProperty.call(left, key)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1078,9 +1090,9 @@ namespace ts {
|
||||
* the same key with the given 'makeKey' function, then the element with the higher
|
||||
* index in the array will be the one associated with the produced key.
|
||||
*/
|
||||
export function arrayToMap<T>(array: T[], makeKey: (value: T) => string): Map<T>;
|
||||
export function arrayToMap<T, U>(array: T[], makeKey: (value: T) => string, makeValue: (value: T) => U): Map<U>;
|
||||
export function arrayToMap<T, U>(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map<T | U> {
|
||||
export function arrayToMap<T>(array: ReadonlyArray<T>, makeKey: (value: T) => string): Map<T>;
|
||||
export function arrayToMap<T, U>(array: ReadonlyArray<T>, makeKey: (value: T) => string, makeValue: (value: T) => U): Map<U>;
|
||||
export function arrayToMap<T, U>(array: ReadonlyArray<T>, makeKey: (value: T) => string, makeValue?: (value: T) => U): Map<T | U> {
|
||||
const result = createMap<T | U>();
|
||||
for (const value of array) {
|
||||
result.set(makeKey(value), makeValue ? makeValue(value) : value);
|
||||
@@ -1088,9 +1100,22 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function cloneMap<T>(map: Map<T>) {
|
||||
/**
|
||||
* Creates a set from the elements of an array.
|
||||
*
|
||||
* @param array the array of input elements.
|
||||
*/
|
||||
export function arrayToSet(array: ReadonlyArray<string>): Map<true>;
|
||||
export function arrayToSet<T>(array: ReadonlyArray<T>, makeKey: (value: T) => string): Map<true>;
|
||||
export function arrayToSet(array: ReadonlyArray<any>, makeKey?: (value: any) => string): Map<true> {
|
||||
return arrayToMap<any, true>(array, makeKey || (s => s), () => true);
|
||||
}
|
||||
|
||||
export function cloneMap(map: SymbolTable): SymbolTable;
|
||||
export function cloneMap<T>(map: ReadonlyMap<T>): Map<T>;
|
||||
export function cloneMap<T>(map: ReadonlyMap<T> | SymbolTable): Map<T> | SymbolTable {
|
||||
const clone = createMap<T>();
|
||||
copyEntries(map, clone);
|
||||
copyEntries(map as Map<T>, clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -1106,12 +1131,18 @@ namespace ts {
|
||||
|
||||
export function extend<T1, T2>(first: T1, second: T2): T1 & T2 {
|
||||
const result: T1 & T2 = <any>{};
|
||||
for (const id in second) if (hasOwnProperty.call(second, id)) {
|
||||
(result as any)[id] = (second as any)[id];
|
||||
for (const id in second) {
|
||||
if (hasOwnProperty.call(second, id)) {
|
||||
(result as any)[id] = (second as any)[id];
|
||||
}
|
||||
}
|
||||
for (const id in first) if (hasOwnProperty.call(first, id)) {
|
||||
(result as any)[id] = (first as any)[id];
|
||||
|
||||
for (const id in first) {
|
||||
if (hasOwnProperty.call(first, id)) {
|
||||
(result as any)[id] = (first as any)[id];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1159,7 +1190,7 @@ namespace ts {
|
||||
/**
|
||||
* Tests whether a value is an array.
|
||||
*/
|
||||
export function isArray(value: any): value is any[] {
|
||||
export function isArray(value: any): value is ReadonlyArray<any> {
|
||||
return Array.isArray ? Array.isArray(value) : value instanceof Array;
|
||||
}
|
||||
|
||||
@@ -1557,10 +1588,21 @@ namespace ts {
|
||||
return path && !isRootedDiskPath(path) && path.indexOf("://") !== -1;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function pathIsRelative(path: string): boolean {
|
||||
return /^\.\.?($|[\\/])/.test(path);
|
||||
}
|
||||
|
||||
export function isExternalModuleNameRelative(moduleName: string): boolean {
|
||||
// TypeScript 1.0 spec (April 2014): 11.2.1
|
||||
// An external module name is "relative" if the first term is "." or "..".
|
||||
return /^\.\.?($|[\\/])/.test(moduleName);
|
||||
// Update: We also consider a path like `C:\foo.ts` "relative" because we do not search for it in `node_modules` or treat it as an ambient module.
|
||||
return pathIsRelative(moduleName) || isRootedDiskPath(moduleName);
|
||||
}
|
||||
|
||||
/** @deprecated Use `!isExternalModuleNameRelative(moduleName)` instead. */
|
||||
export function moduleHasNonRelativeName(moduleName: string): boolean {
|
||||
return !isExternalModuleNameRelative(moduleName);
|
||||
}
|
||||
|
||||
export function getEmitScriptTarget(compilerOptions: CompilerOptions) {
|
||||
@@ -1629,7 +1671,7 @@ namespace ts {
|
||||
return getNormalizedPathFromPathComponents(getNormalizedPathComponents(fileName, currentDirectory));
|
||||
}
|
||||
|
||||
export function getNormalizedPathFromPathComponents(pathComponents: string[]) {
|
||||
export function getNormalizedPathFromPathComponents(pathComponents: ReadonlyArray<string>) {
|
||||
if (pathComponents && pathComponents.length) {
|
||||
return pathComponents[0] + pathComponents.slice(1).join(directorySeparator);
|
||||
}
|
||||
@@ -1692,7 +1734,7 @@ namespace ts {
|
||||
if (directoryComponents.length > 1 && lastOrUndefined(directoryComponents) === "") {
|
||||
// If the directory path given was of type test/cases/ then we really need components of directory to be only till its name
|
||||
// that is ["test", "cases", ""] needs to be actually ["test", "cases"]
|
||||
directoryComponents.length--;
|
||||
directoryComponents.pop();
|
||||
}
|
||||
|
||||
// Find the component that differs
|
||||
@@ -1831,7 +1873,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function fileExtensionIsOneOf(path: string, extensions: string[]): boolean {
|
||||
export function fileExtensionIsOneOf(path: string, extensions: ReadonlyArray<string>): boolean {
|
||||
for (const extension of extensions) {
|
||||
if (fileExtensionIs(path, extension)) {
|
||||
return true;
|
||||
@@ -1856,7 +1898,7 @@ namespace ts {
|
||||
const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*";
|
||||
const singleAsteriskRegexFragmentOther = "[^/]*";
|
||||
|
||||
export function getRegularExpressionForWildcard(specs: string[], basePath: string, usage: "files" | "directories" | "exclude"): string | undefined {
|
||||
export function getRegularExpressionForWildcard(specs: ReadonlyArray<string>, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined {
|
||||
const patterns = getRegularExpressionsForWildcards(specs, basePath, usage);
|
||||
if (!patterns || !patterns.length) {
|
||||
return undefined;
|
||||
@@ -1868,7 +1910,7 @@ namespace ts {
|
||||
return `^(${pattern})${terminator}`;
|
||||
}
|
||||
|
||||
function getRegularExpressionsForWildcards(specs: string[], basePath: string, usage: "files" | "directories" | "exclude"): string[] | undefined {
|
||||
function getRegularExpressionsForWildcards(specs: ReadonlyArray<string>, basePath: string, usage: "files" | "directories" | "exclude"): string[] | undefined {
|
||||
if (specs === undefined || specs.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -1973,21 +2015,21 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface FileSystemEntries {
|
||||
files: string[];
|
||||
directories: string[];
|
||||
files: ReadonlyArray<string>;
|
||||
directories: ReadonlyArray<string>;
|
||||
}
|
||||
|
||||
export interface FileMatcherPatterns {
|
||||
/** One pattern for each "include" spec. */
|
||||
includeFilePatterns: string[];
|
||||
includeFilePatterns: ReadonlyArray<string>;
|
||||
/** One pattern matching one of any of the "include" specs. */
|
||||
includeFilePattern: string;
|
||||
includeDirectoryPattern: string;
|
||||
excludePattern: string;
|
||||
basePaths: string[];
|
||||
basePaths: ReadonlyArray<string>;
|
||||
}
|
||||
|
||||
export function getFileMatcherPatterns(path: string, excludes: string[], includes: string[], useCaseSensitiveFileNames: boolean, currentDirectory: string): FileMatcherPatterns {
|
||||
export function getFileMatcherPatterns(path: string, excludes: ReadonlyArray<string>, includes: ReadonlyArray<string>, useCaseSensitiveFileNames: boolean, currentDirectory: string): FileMatcherPatterns {
|
||||
path = normalizePath(path);
|
||||
currentDirectory = normalizePath(currentDirectory);
|
||||
const absolutePath = combinePaths(currentDirectory, path);
|
||||
@@ -2001,7 +2043,7 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
export function matchFiles(path: string, extensions: string[], excludes: string[], includes: string[], useCaseSensitiveFileNames: boolean, currentDirectory: string, getFileSystemEntries: (path: string) => FileSystemEntries): string[] {
|
||||
export function matchFiles(path: string, extensions: ReadonlyArray<string>, excludes: ReadonlyArray<string>, includes: ReadonlyArray<string>, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries): string[] {
|
||||
path = normalizePath(path);
|
||||
currentDirectory = normalizePath(currentDirectory);
|
||||
|
||||
@@ -2018,15 +2060,14 @@ namespace ts {
|
||||
|
||||
const comparer = useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive;
|
||||
for (const basePath of patterns.basePaths) {
|
||||
visitDirectory(basePath, combinePaths(currentDirectory, basePath));
|
||||
visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth);
|
||||
}
|
||||
|
||||
return flatten(results);
|
||||
return flatten<string>(results);
|
||||
|
||||
function visitDirectory(path: string, absolutePath: string) {
|
||||
function visitDirectory(path: string, absolutePath: string, depth: number | undefined) {
|
||||
let { files, directories } = getFileSystemEntries(path);
|
||||
files = files.slice().sort(comparer);
|
||||
directories = directories.slice().sort(comparer);
|
||||
|
||||
for (const current of files) {
|
||||
const name = combinePaths(path, current);
|
||||
@@ -2044,12 +2085,20 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
if (depth !== undefined) {
|
||||
depth--;
|
||||
if (depth === 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
directories = directories.slice().sort(comparer);
|
||||
for (const current of directories) {
|
||||
const name = combinePaths(path, current);
|
||||
const absoluteName = combinePaths(absolutePath, current);
|
||||
if ((!includeDirectoryRegex || includeDirectoryRegex.test(absoluteName)) &&
|
||||
(!excludeRegex || !excludeRegex.test(absoluteName))) {
|
||||
visitDirectory(name, absoluteName);
|
||||
visitDirectory(name, absoluteName, depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2058,7 +2107,7 @@ namespace ts {
|
||||
/**
|
||||
* Computes the unique non-wildcard base paths amongst the provided include patterns.
|
||||
*/
|
||||
function getBasePaths(path: string, includes: string[], useCaseSensitiveFileNames: boolean) {
|
||||
function getBasePaths(path: string, includes: ReadonlyArray<string>, useCaseSensitiveFileNames: boolean) {
|
||||
// Storage for our results in the form of literal paths (e.g. the paths as written by the user).
|
||||
const basePaths: string[] = [path];
|
||||
|
||||
@@ -2099,27 +2148,29 @@ namespace ts {
|
||||
return absolute.substring(0, absolute.lastIndexOf(directorySeparator, wildcardOffset));
|
||||
}
|
||||
|
||||
export function ensureScriptKind(fileName: string, scriptKind?: ScriptKind): ScriptKind {
|
||||
export function ensureScriptKind(fileName: string, scriptKind: ScriptKind | undefined): ScriptKind {
|
||||
// Using scriptKind as a condition handles both:
|
||||
// - 'scriptKind' is unspecified and thus it is `undefined`
|
||||
// - 'scriptKind' is set and it is `Unknown` (0)
|
||||
// If the 'scriptKind' is 'undefined' or 'Unknown' then we attempt
|
||||
// to get the ScriptKind from the file name. If it cannot be resolved
|
||||
// from the file name then the default 'TS' script kind is returned.
|
||||
return (scriptKind || getScriptKindFromFileName(fileName)) || ScriptKind.TS;
|
||||
return scriptKind || getScriptKindFromFileName(fileName) || ScriptKind.TS;
|
||||
}
|
||||
|
||||
export function getScriptKindFromFileName(fileName: string): ScriptKind {
|
||||
const ext = fileName.substr(fileName.lastIndexOf("."));
|
||||
switch (ext.toLowerCase()) {
|
||||
case ".js":
|
||||
case Extension.Js:
|
||||
return ScriptKind.JS;
|
||||
case ".jsx":
|
||||
case Extension.Jsx:
|
||||
return ScriptKind.JSX;
|
||||
case ".ts":
|
||||
case Extension.Ts:
|
||||
return ScriptKind.TS;
|
||||
case ".tsx":
|
||||
case Extension.Tsx:
|
||||
return ScriptKind.TSX;
|
||||
case ".json":
|
||||
return ScriptKind.JSON;
|
||||
default:
|
||||
return ScriptKind.Unknown;
|
||||
}
|
||||
@@ -2128,18 +2179,18 @@ namespace ts {
|
||||
/**
|
||||
* List of supported extensions in order of file resolution precedence.
|
||||
*/
|
||||
export const supportedTypeScriptExtensions = [".ts", ".tsx", ".d.ts"];
|
||||
export const supportedTypeScriptExtensions: ReadonlyArray<Extension> = [Extension.Ts, Extension.Tsx, Extension.Dts];
|
||||
/** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */
|
||||
export const supportedTypescriptExtensionsForExtractExtension = [".d.ts", ".ts", ".tsx"];
|
||||
export const supportedJavascriptExtensions = [".js", ".jsx"];
|
||||
const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions);
|
||||
export const supportedTypescriptExtensionsForExtractExtension: ReadonlyArray<Extension> = [Extension.Dts, Extension.Ts, Extension.Tsx];
|
||||
export const supportedJavascriptExtensions: ReadonlyArray<Extension> = [Extension.Js, Extension.Jsx];
|
||||
const allSupportedExtensions = [...supportedTypeScriptExtensions, ...supportedJavascriptExtensions];
|
||||
|
||||
export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: JsFileExtensionInfo[]): string[] {
|
||||
export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: ReadonlyArray<JsFileExtensionInfo>): ReadonlyArray<string> {
|
||||
const needAllExtensions = options && options.allowJs;
|
||||
if (!extraFileExtensions || extraFileExtensions.length === 0 || !needAllExtensions) {
|
||||
return needAllExtensions ? allSupportedExtensions : supportedTypeScriptExtensions;
|
||||
}
|
||||
const extensions = allSupportedExtensions.slice(0);
|
||||
const extensions: string[] = allSupportedExtensions.slice(0);
|
||||
for (const extInfo of extraFileExtensions) {
|
||||
if (extensions.indexOf(extInfo.extension) === -1) {
|
||||
extensions.push(extInfo.extension);
|
||||
@@ -2156,7 +2207,7 @@ namespace ts {
|
||||
return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension));
|
||||
}
|
||||
|
||||
export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: JsFileExtensionInfo[]) {
|
||||
export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: ReadonlyArray<JsFileExtensionInfo>) {
|
||||
if (!fileName) { return false; }
|
||||
|
||||
for (const extension of getSupportedExtensions(compilerOptions, extraFileExtensions)) {
|
||||
@@ -2180,7 +2231,7 @@ namespace ts {
|
||||
Lowest = DeclarationAndJavaScriptFiles,
|
||||
}
|
||||
|
||||
export function getExtensionPriority(path: string, supportedExtensions: string[]): ExtensionPriority {
|
||||
export function getExtensionPriority(path: string, supportedExtensions: ReadonlyArray<string>): ExtensionPriority {
|
||||
for (let i = supportedExtensions.length - 1; i >= 0; i--) {
|
||||
if (fileExtensionIs(path, supportedExtensions[i])) {
|
||||
return adjustExtensionPriority(<ExtensionPriority>i, supportedExtensions);
|
||||
@@ -2195,7 +2246,7 @@ namespace ts {
|
||||
/**
|
||||
* Adjusts an extension priority to be the highest priority within the same range.
|
||||
*/
|
||||
export function adjustExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: string[]): ExtensionPriority {
|
||||
export function adjustExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: ReadonlyArray<string>): ExtensionPriority {
|
||||
if (extensionPriority < ExtensionPriority.DeclarationAndJavaScriptFiles) {
|
||||
return ExtensionPriority.TypeScriptFiles;
|
||||
}
|
||||
@@ -2204,12 +2255,13 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
return supportedExtensions.length;
|
||||
} }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next lowest extension priority for a given priority.
|
||||
*/
|
||||
export function getNextLowestExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: string[]): ExtensionPriority {
|
||||
export function getNextLowestExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: ReadonlyArray<string>): ExtensionPriority {
|
||||
if (extensionPriority < ExtensionPriority.DeclarationAndJavaScriptFiles) {
|
||||
return ExtensionPriority.DeclarationAndJavaScriptFiles;
|
||||
}
|
||||
@@ -2218,7 +2270,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"];
|
||||
const extensionsToRemove = [Extension.Dts, Extension.Ts, Extension.Js, Extension.Tsx, Extension.Jsx];
|
||||
export function removeFileExtension(path: string): string {
|
||||
for (const ext of extensionsToRemove) {
|
||||
const extensionless = tryRemoveExtension(path, ext);
|
||||
@@ -2246,14 +2298,15 @@ namespace ts {
|
||||
getTokenConstructor(): new <TKind extends SyntaxKind>(kind: TKind, pos?: number, end?: number) => Token<TKind>;
|
||||
getIdentifierConstructor(): new (kind: SyntaxKind.Identifier, pos?: number, end?: number) => Identifier;
|
||||
getSourceFileConstructor(): new (kind: SyntaxKind.SourceFile, pos?: number, end?: number) => SourceFile;
|
||||
getSymbolConstructor(): new (flags: SymbolFlags, name: string) => Symbol;
|
||||
getSymbolConstructor(): new (flags: SymbolFlags, name: __String) => Symbol;
|
||||
getTypeConstructor(): new (checker: TypeChecker, flags: TypeFlags) => Type;
|
||||
getSignatureConstructor(): new (checker: TypeChecker) => Signature;
|
||||
getSourceMapSourceConstructor(): new (fileName: string, text: string, skipTrivia?: (pos: number) => number) => SourceMapSource;
|
||||
}
|
||||
|
||||
function Symbol(this: Symbol, flags: SymbolFlags, name: string) {
|
||||
function Symbol(this: Symbol, flags: SymbolFlags, name: __String) {
|
||||
this.flags = flags;
|
||||
this.name = name;
|
||||
this.escapedName = name;
|
||||
this.declarations = undefined;
|
||||
}
|
||||
|
||||
@@ -2279,6 +2332,12 @@ namespace ts {
|
||||
this.original = undefined;
|
||||
}
|
||||
|
||||
function SourceMapSource(this: SourceMapSource, fileName: string, text: string, skipTrivia?: (pos: number) => number) {
|
||||
this.fileName = fileName;
|
||||
this.text = text;
|
||||
this.skipTrivia = skipTrivia || (pos => pos);
|
||||
}
|
||||
|
||||
export let objectAllocator: ObjectAllocator = {
|
||||
getNodeConstructor: () => <any>Node,
|
||||
getTokenConstructor: () => <any>Node,
|
||||
@@ -2286,7 +2345,8 @@ namespace ts {
|
||||
getSourceFileConstructor: () => <any>Node,
|
||||
getSymbolConstructor: () => <any>Symbol,
|
||||
getTypeConstructor: () => <any>Type,
|
||||
getSignatureConstructor: () => <any>Signature
|
||||
getSignatureConstructor: () => <any>Signature,
|
||||
getSourceMapSourceConstructor: () => <any>SourceMapSource,
|
||||
};
|
||||
|
||||
export const enum AssertionLevel {
|
||||
@@ -2315,7 +2375,7 @@ namespace ts {
|
||||
|
||||
export function fail(message?: string, stackCrawlMark?: Function): void {
|
||||
debugger;
|
||||
const e = new Error(message ? `Debug Failure. ` : "Debug Failure.");
|
||||
const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");
|
||||
if ((<any>Error).captureStackTrace) {
|
||||
(<any>Error).captureStackTrace(e, stackCrawlMark || fail);
|
||||
}
|
||||
@@ -2390,7 +2450,7 @@ namespace ts {
|
||||
* (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.)
|
||||
*/
|
||||
/* @internal */
|
||||
export function matchPatternOrExact(patternStrings: string[], candidate: string): string | Pattern | undefined {
|
||||
export function matchPatternOrExact(patternStrings: ReadonlyArray<string>, candidate: string): string | Pattern | undefined {
|
||||
const patterns: Pattern[] = [];
|
||||
for (const patternString of patternStrings) {
|
||||
const pattern = tryParsePattern(patternString);
|
||||
@@ -2423,7 +2483,7 @@ namespace ts {
|
||||
|
||||
/** Return the object corresponding to the best pattern to match `candidate`. */
|
||||
/* @internal */
|
||||
export function findBestPatternMatch<T>(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined {
|
||||
export function findBestPatternMatch<T>(values: ReadonlyArray<T>, getPattern: (value: T) => Pattern, candidate: string): T | undefined {
|
||||
let matchedValue: T | undefined = undefined;
|
||||
// use length of prefix as betterness criteria
|
||||
let longestMatchPrefixLength = -1;
|
||||
@@ -2464,7 +2524,7 @@ namespace ts {
|
||||
|
||||
/** True if an extension is one of the supported TypeScript extensions. */
|
||||
export function extensionIsTypeScript(ext: Extension): boolean {
|
||||
return ext <= Extension.LastTypeScriptExtension;
|
||||
return ext === Extension.Ts || ext === Extension.Tsx || ext === Extension.Dts;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2479,24 +2539,10 @@ namespace ts {
|
||||
Debug.fail(`File ${path} has unknown extension.`);
|
||||
}
|
||||
export function tryGetExtensionFromPath(path: string): Extension | undefined {
|
||||
if (fileExtensionIs(path, ".d.ts")) {
|
||||
return Extension.Dts;
|
||||
}
|
||||
if (fileExtensionIs(path, ".ts")) {
|
||||
return Extension.Ts;
|
||||
}
|
||||
if (fileExtensionIs(path, ".tsx")) {
|
||||
return Extension.Tsx;
|
||||
}
|
||||
if (fileExtensionIs(path, ".js")) {
|
||||
return Extension.Js;
|
||||
}
|
||||
if (fileExtensionIs(path, ".jsx")) {
|
||||
return Extension.Jsx;
|
||||
}
|
||||
return find<Extension>(supportedTypescriptExtensionsForExtractExtension, e => fileExtensionIs(path, e)) || find(supportedJavascriptExtensions, e => fileExtensionIs(path, e));
|
||||
}
|
||||
|
||||
export function isCheckJsEnabledForFile(sourceFile: SourceFile, compilerOptions: CompilerOptions) {
|
||||
return sourceFile.checkJsDirective ? sourceFile.checkJsDirective.enabled : compilerOptions.checkJs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ namespace ts {
|
||||
decreaseIndent = newWriter.decreaseIndent;
|
||||
}
|
||||
|
||||
function writeAsynchronousModuleElements(nodes: Node[]) {
|
||||
function writeAsynchronousModuleElements(nodes: ReadonlyArray<Node>) {
|
||||
const oldWriter = writer;
|
||||
forEach(nodes, declaration => {
|
||||
let nodeToCheck: Node;
|
||||
@@ -335,9 +335,12 @@ namespace ts {
|
||||
write(": ");
|
||||
|
||||
// use the checker's type, not the declared type,
|
||||
// for non-optional initialized parameters that aren't a parameter property
|
||||
// for optional parameter properties
|
||||
// and also for non-optional initialized parameters that aren't a parameter property
|
||||
// these types may need to add `undefined`.
|
||||
const shouldUseResolverType = declaration.kind === SyntaxKind.Parameter &&
|
||||
resolver.isRequiredInitializedParameter(declaration as ParameterDeclaration);
|
||||
(resolver.isRequiredInitializedParameter(declaration as ParameterDeclaration) ||
|
||||
resolver.isOptionalUninitializedParameterProperty(declaration as ParameterDeclaration));
|
||||
if (type && !shouldUseResolverType) {
|
||||
// Write the type
|
||||
emitType(type);
|
||||
@@ -371,13 +374,13 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function emitLines(nodes: Node[]) {
|
||||
function emitLines(nodes: ReadonlyArray<Node>) {
|
||||
for (const node of nodes) {
|
||||
emit(node);
|
||||
}
|
||||
}
|
||||
|
||||
function emitSeparatedList(nodes: Node[], separator: string, eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) {
|
||||
function emitSeparatedList(nodes: ReadonlyArray<Node>, separator: string, eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) {
|
||||
let currentWriterPos = writer.getTextPos();
|
||||
for (const node of nodes) {
|
||||
if (!canEmitFn || canEmitFn(node)) {
|
||||
@@ -390,7 +393,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function emitCommaList(nodes: Node[], eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) {
|
||||
function emitCommaList(nodes: ReadonlyArray<Node>, eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) {
|
||||
emitSeparatedList(nodes, ", ", eachNodeEmitFn, canEmitFn);
|
||||
}
|
||||
|
||||
@@ -596,7 +599,7 @@ namespace ts {
|
||||
currentIdentifiers = node.identifiers;
|
||||
isCurrentFileExternalModule = isExternalModule(node);
|
||||
enclosingDeclaration = node;
|
||||
emitDetachedComments(currentText, currentLineMap, writer, writeCommentRange, node, newLine, /*removeComents*/ true);
|
||||
emitDetachedComments(currentText, currentLineMap, writer, writeCommentRange, node, newLine, /*removeComments*/ true);
|
||||
emitLines(node.statements);
|
||||
}
|
||||
|
||||
@@ -1004,7 +1007,7 @@ namespace ts {
|
||||
return node.parent.kind === SyntaxKind.MethodDeclaration && hasModifier(node.parent, ModifierFlags.Private);
|
||||
}
|
||||
|
||||
function emitTypeParameters(typeParameters: TypeParameterDeclaration[]) {
|
||||
function emitTypeParameters(typeParameters: ReadonlyArray<TypeParameterDeclaration>) {
|
||||
function emitTypeParameter(node: TypeParameterDeclaration) {
|
||||
increaseIndent();
|
||||
emitJsDocComments(node);
|
||||
@@ -1106,7 +1109,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function emitHeritageClause(typeReferences: ExpressionWithTypeArguments[], isImplementsList: boolean) {
|
||||
function emitHeritageClause(typeReferences: ReadonlyArray<ExpressionWithTypeArguments>, isImplementsList: boolean) {
|
||||
if (typeReferences) {
|
||||
write(isImplementsList ? " implements " : " extends ");
|
||||
emitCommaList(typeReferences, emitTypeOfTypeReference);
|
||||
@@ -1161,7 +1164,7 @@ namespace ts {
|
||||
if (baseTypeNode && !isEntityNameExpression(baseTypeNode.expression)) {
|
||||
tempVarName = baseTypeNode.expression.kind === SyntaxKind.NullKeyword ?
|
||||
"null" :
|
||||
emitTempVariableDeclaration(baseTypeNode.expression, `${node.name.text}_base`, {
|
||||
emitTempVariableDeclaration(baseTypeNode.expression, `${node.name.escapedText}_base`, {
|
||||
diagnosticMessage: Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1,
|
||||
errorNode: baseTypeNode,
|
||||
typeName: node.name
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
{
|
||||
"Unterminated string literal.": {
|
||||
"category": "Error",
|
||||
"code": 1002
|
||||
@@ -707,7 +707,7 @@
|
||||
"category": "Error",
|
||||
"code": 1223
|
||||
},
|
||||
"Signature '{0}' must have a type predicate.": {
|
||||
"Signature '{0}' must be a type predicate.": {
|
||||
"category": "Error",
|
||||
"code": 1224
|
||||
},
|
||||
@@ -899,6 +899,14 @@
|
||||
"category": "Error",
|
||||
"code": 1326
|
||||
},
|
||||
"String literal with double quotes expected.": {
|
||||
"category": "Error",
|
||||
"code": 1327
|
||||
},
|
||||
"Property value can only be string literal, numeric literal, 'true', 'false', 'null', object literal or array literal.": {
|
||||
"category": "Error",
|
||||
"code": 1328
|
||||
},
|
||||
|
||||
"Duplicate identifier '{0}'.": {
|
||||
"category": "Error",
|
||||
@@ -2056,7 +2064,7 @@
|
||||
"category": "Error",
|
||||
"code": 2679
|
||||
},
|
||||
"A 'this' parameter must be the first parameter.": {
|
||||
"A '{0}' parameter must be the first parameter.": {
|
||||
"category": "Error",
|
||||
"code": 2680
|
||||
},
|
||||
@@ -2184,6 +2192,10 @@
|
||||
"category": "Error",
|
||||
"code": 2712
|
||||
},
|
||||
"Cannot access '{0}.{1}' because '{0}' is a type, but not a namespace. Did you mean to retrieve the type of the property '{1}' in '{0}' with '{0}[\"{1}\"]'?": {
|
||||
"category": "Error",
|
||||
"code": 2713
|
||||
},
|
||||
|
||||
"Import declaration '{0}' is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
@@ -2650,7 +2662,7 @@
|
||||
"category": "Message",
|
||||
"code": 6015
|
||||
},
|
||||
"Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.": {
|
||||
"Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.": {
|
||||
"category": "Message",
|
||||
"code": 6016
|
||||
},
|
||||
@@ -3278,6 +3290,10 @@
|
||||
"category": "Message",
|
||||
"code": 6184
|
||||
},
|
||||
"Disable strict checking of generic signatures in function types.": {
|
||||
"category": "Message",
|
||||
"code": 6185
|
||||
},
|
||||
"Variable '{0}' implicitly has an '{1}' type.": {
|
||||
"category": "Error",
|
||||
"code": 7005
|
||||
@@ -3443,6 +3459,10 @@
|
||||
"category": "Error",
|
||||
"code": 8012
|
||||
},
|
||||
"'non-null assertions' can only be used in a .ts file.": {
|
||||
"category": "Error",
|
||||
"code": 8013
|
||||
},
|
||||
"'enum declarations' can only be used in a .ts file.": {
|
||||
"category": "Error",
|
||||
"code": 8015
|
||||
@@ -3451,6 +3471,22 @@
|
||||
"category": "Error",
|
||||
"code": 8016
|
||||
},
|
||||
"Octal literal types must use ES2015 syntax. Use the syntax '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 8017
|
||||
},
|
||||
"Octal literals are not allowed in enums members initializer. Use the syntax '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 8018
|
||||
},
|
||||
"Report errors in .js files.": {
|
||||
"category": "Message",
|
||||
"code": 8019
|
||||
},
|
||||
"JSDoc types can only be used inside documentation comments.": {
|
||||
"category": "Error",
|
||||
"code": 8020
|
||||
},
|
||||
"Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": {
|
||||
"category": "Error",
|
||||
"code": 9002
|
||||
@@ -3621,7 +3657,15 @@
|
||||
"category": "Message",
|
||||
"code": 90024
|
||||
},
|
||||
|
||||
"Prefix '{0}' with an underscore.": {
|
||||
"category": "Message",
|
||||
"code": 90025
|
||||
},
|
||||
"Rewrite as the indexed access type '{0}'.": {
|
||||
"category": "Message",
|
||||
"code": 90026
|
||||
},
|
||||
|
||||
"Convert function to an ES2015 class": {
|
||||
"category": "Message",
|
||||
"code": 95001
|
||||
@@ -3629,18 +3673,5 @@
|
||||
"Convert function '{0}' to class": {
|
||||
"category": "Message",
|
||||
"code": 95002
|
||||
},
|
||||
|
||||
"Octal literal types must use ES2015 syntax. Use the syntax '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 8017
|
||||
},
|
||||
"Octal literals are not allowed in enums members initializer. Use the syntax '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 8018
|
||||
},
|
||||
"Report errors in .js files.": {
|
||||
"category": "Message",
|
||||
"code": 8019
|
||||
}
|
||||
}
|
||||
|
||||
+97
-19
@@ -8,6 +8,70 @@ namespace ts {
|
||||
const delimiters = createDelimiterMap();
|
||||
const brackets = createBracketsMap();
|
||||
|
||||
/*@internal*/
|
||||
/**
|
||||
* Iterates over the source files that are expected to have an emit output.
|
||||
*
|
||||
* @param host An EmitHost.
|
||||
* @param action The action to execute.
|
||||
* @param sourceFilesOrTargetSourceFile
|
||||
* If an array, the full list of source files to emit.
|
||||
* Else, calls `getSourceFilesToEmit` with the (optional) target source file to determine the list of source files to emit.
|
||||
*/
|
||||
export function forEachEmittedFile(
|
||||
host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean) => void,
|
||||
sourceFilesOrTargetSourceFile?: SourceFile[] | SourceFile,
|
||||
emitOnlyDtsFiles?: boolean) {
|
||||
|
||||
const sourceFiles = isArray(sourceFilesOrTargetSourceFile) ? sourceFilesOrTargetSourceFile : getSourceFilesToEmit(host, sourceFilesOrTargetSourceFile);
|
||||
const options = host.getCompilerOptions();
|
||||
if (options.outFile || options.out) {
|
||||
if (sourceFiles.length) {
|
||||
const jsFilePath = options.outFile || options.out;
|
||||
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
|
||||
const declarationFilePath = options.declaration ? removeFileExtension(jsFilePath) + Extension.Dts : "";
|
||||
action({ jsFilePath, sourceMapFilePath, declarationFilePath }, createBundle(sourceFiles), emitOnlyDtsFiles);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const sourceFile of sourceFiles) {
|
||||
const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options));
|
||||
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
|
||||
const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined;
|
||||
action({ jsFilePath, sourceMapFilePath, declarationFilePath }, sourceFile, emitOnlyDtsFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSourceMapFilePath(jsFilePath: string, options: CompilerOptions) {
|
||||
return options.sourceMap ? jsFilePath + ".map" : undefined;
|
||||
}
|
||||
|
||||
// JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also.
|
||||
// So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve.
|
||||
// For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve
|
||||
function getOutputExtension(sourceFile: SourceFile, options: CompilerOptions): Extension {
|
||||
if (options.jsx === JsxEmit.Preserve) {
|
||||
if (isSourceFileJavaScript(sourceFile)) {
|
||||
if (fileExtensionIs(sourceFile.fileName, Extension.Jsx)) {
|
||||
return Extension.Jsx;
|
||||
}
|
||||
}
|
||||
else if (sourceFile.languageVariant === LanguageVariant.JSX) {
|
||||
// TypeScript source file preserving JSX syntax
|
||||
return Extension.Jsx;
|
||||
}
|
||||
}
|
||||
return Extension.Js;
|
||||
}
|
||||
|
||||
function getOriginalSourceFileOrBundle(sourceFileOrBundle: SourceFile | Bundle) {
|
||||
if (sourceFileOrBundle.kind === SyntaxKind.Bundle) {
|
||||
return updateBundle(sourceFileOrBundle, sameMap(sourceFileOrBundle.sourceFiles, getOriginalSourceFile));
|
||||
}
|
||||
return getOriginalSourceFile(sourceFileOrBundle);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
|
||||
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean, transformers?: TransformerFactory<SourceFile>[]): EmitResult {
|
||||
@@ -217,7 +281,7 @@ namespace ts {
|
||||
let currentSourceFile: SourceFile | undefined;
|
||||
let nodeIdToGeneratedName: string[]; // Map of generated names for specific nodes.
|
||||
let autoGeneratedIdToGeneratedName: string[]; // Map of generated names for temp and loop variables.
|
||||
let generatedNames: Map<string>; // Set of names generated by the NameGenerator.
|
||||
let generatedNames: Map<true>; // Set of names generated by the NameGenerator.
|
||||
let tempFlagsStack: TempFlags[]; // Stack of enclosing name generation scopes.
|
||||
let tempFlags: TempFlags; // TempFlags for the current name generation scope.
|
||||
let writer: EmitTextWriter;
|
||||
@@ -335,7 +399,7 @@ namespace ts {
|
||||
function reset() {
|
||||
nodeIdToGeneratedName = [];
|
||||
autoGeneratedIdToGeneratedName = [];
|
||||
generatedNames = createMap<string>();
|
||||
generatedNames = createMap<true>();
|
||||
tempFlagsStack = [];
|
||||
tempFlags = TempFlags.Auto;
|
||||
comments.reset();
|
||||
@@ -959,7 +1023,7 @@ namespace ts {
|
||||
function emitConstructorType(node: ConstructorTypeNode) {
|
||||
write("new ");
|
||||
emitTypeParameters(node, node.typeParameters);
|
||||
emitParametersForArrow(node, node.parameters);
|
||||
emitParameters(node, node.parameters);
|
||||
write(" => ");
|
||||
emit(node.type);
|
||||
}
|
||||
@@ -2164,7 +2228,7 @@ namespace ts {
|
||||
* Emits any prologue directives at the start of a Statement list, returning the
|
||||
* number of prologue directives written to the output.
|
||||
*/
|
||||
function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean, seenPrologueDirectives?: Map<String>): number {
|
||||
function emitPrologueDirectives(statements: ReadonlyArray<Node>, startWithNewLine?: boolean, seenPrologueDirectives?: Map<true>): number {
|
||||
for (let i = 0; i < statements.length; i++) {
|
||||
const statement = statements[i];
|
||||
if (isPrologueDirective(statement)) {
|
||||
@@ -2175,7 +2239,7 @@ namespace ts {
|
||||
}
|
||||
emit(statement);
|
||||
if (seenPrologueDirectives) {
|
||||
seenPrologueDirectives.set(statement.expression.text, statement.expression.text);
|
||||
seenPrologueDirectives.set(statement.expression.text, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2194,7 +2258,7 @@ namespace ts {
|
||||
emitPrologueDirectives((sourceFileOrBundle as SourceFile).statements);
|
||||
}
|
||||
else {
|
||||
const seenPrologueDirectives = createMap<String>();
|
||||
const seenPrologueDirectives = createMap<true>();
|
||||
for (const sourceFile of (sourceFileOrBundle as Bundle).sourceFiles) {
|
||||
setSourceFile(sourceFile);
|
||||
emitPrologueDirectives(sourceFile.statements, /*startWithNewLine*/ true, seenPrologueDirectives);
|
||||
@@ -2283,11 +2347,25 @@ namespace ts {
|
||||
emitList(parentNode, parameters, ListFormat.Parameters);
|
||||
}
|
||||
|
||||
function emitParametersForArrow(parentNode: Node, parameters: NodeArray<ParameterDeclaration>) {
|
||||
if (parameters &&
|
||||
parameters.length === 1 &&
|
||||
parameters[0].type === undefined &&
|
||||
parameters[0].pos === parentNode.pos) {
|
||||
function canEmitSimpleArrowHead(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray<ParameterDeclaration>) {
|
||||
const parameter = singleOrUndefined(parameters);
|
||||
return parameter
|
||||
&& parameter.pos === parentNode.pos // may not have parsed tokens between parent and parameter
|
||||
&& !(isArrowFunction(parentNode) && parentNode.type) // arrow function may not have return type annotation
|
||||
&& !some(parentNode.decorators) // parent may not have decorators
|
||||
&& !some(parentNode.modifiers) // parent may not have modifiers
|
||||
&& !some(parentNode.typeParameters) // parent may not have type parameters
|
||||
&& !some(parameter.decorators) // parameter may not have decorators
|
||||
&& !some(parameter.modifiers) // parameter may not have modifiers
|
||||
&& !parameter.dotDotDotToken // parameter may not be rest
|
||||
&& !parameter.questionToken // parameter may not be optional
|
||||
&& !parameter.type // parameter may not have a type annotation
|
||||
&& !parameter.initializer // parameter may not have an initializer
|
||||
&& isIdentifier(parameter.name); // parameter name must be identifier
|
||||
}
|
||||
|
||||
function emitParametersForArrow(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray<ParameterDeclaration>) {
|
||||
if (canEmitSimpleArrowHead(parentNode, parameters)) {
|
||||
emit(parameters[0]);
|
||||
}
|
||||
else {
|
||||
@@ -2683,7 +2761,7 @@ namespace ts {
|
||||
return generateName(node);
|
||||
}
|
||||
else if (isIdentifier(node) && (nodeIsSynthesized(node) || !node.parent)) {
|
||||
return unescapeIdentifier(node.text);
|
||||
return unescapeLeadingUnderscores(node.escapedText);
|
||||
}
|
||||
else if (node.kind === SyntaxKind.StringLiteral && (<StringLiteral>node).textSourceNode) {
|
||||
return getTextOfNode((<StringLiteral>node).textSourceNode, includeTrivia);
|
||||
@@ -2740,13 +2818,13 @@ namespace ts {
|
||||
// Auto, Loop, and Unique names are cached based on their unique
|
||||
// autoGenerateId.
|
||||
const autoGenerateId = name.autoGenerateId;
|
||||
return autoGeneratedIdToGeneratedName[autoGenerateId] || (autoGeneratedIdToGeneratedName[autoGenerateId] = unescapeIdentifier(makeName(name)));
|
||||
return autoGeneratedIdToGeneratedName[autoGenerateId] || (autoGeneratedIdToGeneratedName[autoGenerateId] = makeName(name));
|
||||
}
|
||||
}
|
||||
|
||||
function generateNameCached(node: Node) {
|
||||
const nodeId = getNodeId(node);
|
||||
return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = unescapeIdentifier(generateNameForNode(node)));
|
||||
return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = generateNameForNode(node));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2765,7 +2843,7 @@ namespace ts {
|
||||
function isUniqueLocalName(name: string, container: Node): boolean {
|
||||
for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) {
|
||||
if (node.locals) {
|
||||
const local = node.locals.get(name);
|
||||
const local = node.locals.get(escapeLeadingUnderscores(name));
|
||||
// We conservatively include alias symbols to cover cases where they're emitted as locals
|
||||
if (local && local.flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) {
|
||||
return false;
|
||||
@@ -2818,7 +2896,7 @@ namespace ts {
|
||||
while (true) {
|
||||
const generatedName = baseName + i;
|
||||
if (isUniqueName(generatedName)) {
|
||||
generatedNames.set(generatedName, generatedName);
|
||||
generatedNames.set(generatedName, true);
|
||||
return generatedName;
|
||||
}
|
||||
i++;
|
||||
@@ -2839,8 +2917,8 @@ namespace ts {
|
||||
*/
|
||||
function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) {
|
||||
const expr = getExternalModuleName(node);
|
||||
const baseName = expr.kind === SyntaxKind.StringLiteral ?
|
||||
escapeIdentifier(makeIdentifierFromModuleName((<LiteralExpression>expr).text)) : "module";
|
||||
const baseName = isStringLiteral(expr) ?
|
||||
makeIdentifierFromModuleName(expr.text) : "module";
|
||||
return makeUniqueName(baseName);
|
||||
}
|
||||
|
||||
@@ -2903,7 +2981,7 @@ namespace ts {
|
||||
case GeneratedIdentifierKind.Loop:
|
||||
return makeTempVariableName(TempFlags._i);
|
||||
case GeneratedIdentifierKind.Unique:
|
||||
return makeUniqueName(unescapeIdentifier(name.text));
|
||||
return makeUniqueName(unescapeLeadingUnderscores(name.escapedText));
|
||||
}
|
||||
|
||||
Debug.fail("Unsupported GeneratedIdentifierKind.");
|
||||
|
||||
+381
-323
File diff suppressed because it is too large
Load Diff
@@ -13,12 +13,6 @@ namespace ts {
|
||||
return compilerOptions.traceResolution && host.trace !== undefined;
|
||||
}
|
||||
|
||||
/** Array that is only intended to be pushed to, never read. */
|
||||
/* @internal */
|
||||
export interface Push<T> {
|
||||
push(value: T): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of trying to resolve a module.
|
||||
* At least one of `ts` and `js` should be defined, or the whole thing should be `undefined`.
|
||||
@@ -54,10 +48,6 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
export function moduleHasNonRelativeName(moduleName: string): boolean {
|
||||
return !(isRootedDiskPath(moduleName) || isExternalModuleNameRelative(moduleName));
|
||||
}
|
||||
|
||||
interface ModuleResolutionState {
|
||||
host: ModuleResolutionHost;
|
||||
compilerOptions: CompilerOptions;
|
||||
@@ -151,11 +141,7 @@ namespace ts {
|
||||
*/
|
||||
export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string | undefined, options: CompilerOptions, host: ModuleResolutionHost): ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
|
||||
const traceEnabled = isTraceEnabled(options, host);
|
||||
const moduleResolutionState: ModuleResolutionState = {
|
||||
compilerOptions: options,
|
||||
host: host,
|
||||
traceEnabled
|
||||
};
|
||||
const moduleResolutionState: ModuleResolutionState = { compilerOptions: options, host, traceEnabled };
|
||||
|
||||
const typeRoots = getEffectiveTypeRoots(options, host);
|
||||
if (traceEnabled) {
|
||||
@@ -270,6 +256,8 @@ namespace ts {
|
||||
for (const typeDirectivePath of host.getDirectories(root)) {
|
||||
const normalized = normalizePath(typeDirectivePath);
|
||||
const packageJsonPath = pathToPackageJson(combinePaths(root, normalized));
|
||||
// `types-publisher` sometimes creates packages with `"typings": null` for packages that don't provide their own types.
|
||||
// See `createNotNeededPackageJSON` in the types-publisher` repo.
|
||||
// tslint:disable-next-line:no-null-keyword
|
||||
const isNotNeededPackage = host.fileExists(packageJsonPath) && readJson(packageJsonPath, host).typings === null;
|
||||
if (!isNotNeededPackage) {
|
||||
@@ -306,7 +294,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string): ModuleResolutionCache {
|
||||
const directoryToModuleNameMap = createFileMap<Map<ResolvedModuleWithFailedLookupLocations>>();
|
||||
const directoryToModuleNameMap = createMap<Map<ResolvedModuleWithFailedLookupLocations>>();
|
||||
const moduleNameToDirectoryMap = createMap<PerModuleNameCache>();
|
||||
|
||||
return { getOrCreateCacheForDirectory, getOrCreateCacheForModuleName };
|
||||
@@ -322,7 +310,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getOrCreateCacheForModuleName(nonRelativeModuleName: string) {
|
||||
if (!moduleHasNonRelativeName(nonRelativeModuleName)) {
|
||||
if (isExternalModuleNameRelative(nonRelativeModuleName)) {
|
||||
return undefined;
|
||||
}
|
||||
let perModuleNameCache = moduleNameToDirectoryMap.get(nonRelativeModuleName);
|
||||
@@ -334,7 +322,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function createPerModuleNameCache(): PerModuleNameCache {
|
||||
const directoryPathMap = createFileMap<ResolvedModuleWithFailedLookupLocations>();
|
||||
const directoryPathMap = createMap<ResolvedModuleWithFailedLookupLocations>();
|
||||
|
||||
return { get, set };
|
||||
|
||||
@@ -356,7 +344,7 @@ namespace ts {
|
||||
function set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void {
|
||||
const path = toPath(directory, currentDirectory, getCanonicalFileName);
|
||||
// if entry is already in cache do nothing
|
||||
if (directoryPathMap.contains(path)) {
|
||||
if (directoryPathMap.has(path)) {
|
||||
return;
|
||||
}
|
||||
directoryPathMap.set(path, result);
|
||||
@@ -370,7 +358,7 @@ namespace ts {
|
||||
let current = path;
|
||||
while (true) {
|
||||
const parent = getDirectoryPath(current);
|
||||
if (parent === current || directoryPathMap.contains(parent)) {
|
||||
if (parent === current || directoryPathMap.has(parent)) {
|
||||
break;
|
||||
}
|
||||
directoryPathMap.set(parent, result);
|
||||
@@ -539,7 +527,7 @@ namespace ts {
|
||||
function tryLoadModuleUsingOptionalResolutionSettings(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
|
||||
failedLookupLocations: Push<string>, state: ModuleResolutionState): Resolved | undefined {
|
||||
|
||||
if (moduleHasNonRelativeName(moduleName)) {
|
||||
if (!isExternalModuleNameRelative(moduleName)) {
|
||||
return tryLoadModuleUsingBaseUrl(extensions, moduleName, loader, failedLookupLocations, state);
|
||||
}
|
||||
else {
|
||||
@@ -715,7 +703,7 @@ namespace ts {
|
||||
return toSearchResult({ resolved, isExternalLibraryImport: false });
|
||||
}
|
||||
|
||||
if (moduleHasNonRelativeName(moduleName)) {
|
||||
if (!isExternalModuleNameRelative(moduleName)) {
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder_target_file_type_1, moduleName, Extensions[extensions]);
|
||||
}
|
||||
@@ -815,15 +803,15 @@ namespace ts {
|
||||
|
||||
switch (extensions) {
|
||||
case Extensions.DtsOnly:
|
||||
return tryExtension(".d.ts", Extension.Dts);
|
||||
return tryExtension(Extension.Dts);
|
||||
case Extensions.TypeScript:
|
||||
return tryExtension(".ts", Extension.Ts) || tryExtension(".tsx", Extension.Tsx) || tryExtension(".d.ts", Extension.Dts);
|
||||
return tryExtension(Extension.Ts) || tryExtension(Extension.Tsx) || tryExtension(Extension.Dts);
|
||||
case Extensions.JavaScript:
|
||||
return tryExtension(".js", Extension.Js) || tryExtension(".jsx", Extension.Jsx);
|
||||
return tryExtension(Extension.Js) || tryExtension(Extension.Jsx);
|
||||
}
|
||||
|
||||
function tryExtension(ext: string, extension: Extension): Resolved | undefined {
|
||||
const path = tryFile(candidate + ext, failedLookupLocations, onlyRecordFailures, state);
|
||||
function tryExtension(extension: Extension): Resolved | undefined {
|
||||
const path = tryFile(candidate + extension, failedLookupLocations, onlyRecordFailures, state);
|
||||
return path && { path, extension };
|
||||
}
|
||||
}
|
||||
@@ -995,7 +983,7 @@ namespace ts {
|
||||
export function getPackageNameFromAtTypesDirectory(mangledName: string): string {
|
||||
const withoutAtTypePrefix = removePrefix(mangledName, "@types/");
|
||||
if (withoutAtTypePrefix !== mangledName) {
|
||||
return withoutAtTypePrefix.indexOf("__") !== -1 ?
|
||||
return withoutAtTypePrefix.indexOf(mangledScopedPackageSeparator) !== -1 ?
|
||||
"@" + withoutAtTypePrefix.replace(mangledScopedPackageSeparator, ts.directorySeparator) :
|
||||
withoutAtTypePrefix;
|
||||
}
|
||||
@@ -1028,7 +1016,7 @@ namespace ts {
|
||||
}
|
||||
const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName);
|
||||
|
||||
if (moduleHasNonRelativeName(moduleName)) {
|
||||
if (!isExternalModuleNameRelative(moduleName)) {
|
||||
// Climb up parent directories looking for a module.
|
||||
const resolved = forEachAncestorDirectory(containingDirectory, directory => {
|
||||
const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, directory, traceEnabled, host);
|
||||
|
||||
+583
-686
File diff suppressed because it is too large
Load Diff
+202
-74
@@ -3,7 +3,6 @@
|
||||
/// <reference path="core.ts" />
|
||||
|
||||
namespace ts {
|
||||
const emptyArray: any[] = [];
|
||||
const ignoreDiagnosticCommentRegEx = /(^\s*$)|(^\s*\/\/\/?\s*(@ts-ignore)?)/;
|
||||
|
||||
export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "tsconfig.json"): string {
|
||||
@@ -332,6 +331,7 @@ namespace ts {
|
||||
const categoryColor = getCategoryFormat(diagnostic.category);
|
||||
const category = DiagnosticCategory[diagnostic.category].toLowerCase();
|
||||
output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }`;
|
||||
output += sys.newLine;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
@@ -382,7 +382,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
interface DiagnosticCache {
|
||||
perFile?: FileMap<Diagnostic[]>;
|
||||
perFile?: Map<Diagnostic[]>;
|
||||
allDiagnostics?: Diagnostic[];
|
||||
}
|
||||
|
||||
@@ -405,7 +405,7 @@ namespace ts {
|
||||
let commonSourceDirectory: string;
|
||||
let diagnosticsProducingTypeChecker: TypeChecker;
|
||||
let noDiagnosticsTypeChecker: TypeChecker;
|
||||
let classifiableNames: Map<string>;
|
||||
let classifiableNames: UnderscoreEscapedMap<true>;
|
||||
let modifiedFilePaths: Path[] | undefined;
|
||||
|
||||
const cachedSemanticDiagnosticsForFile: DiagnosticCache = {};
|
||||
@@ -441,12 +441,13 @@ namespace ts {
|
||||
const supportedExtensions = getSupportedExtensions(options);
|
||||
|
||||
// Map storing if there is emit blocking diagnostics for given input
|
||||
const hasEmitBlockingDiagnostics = createFileMap<boolean>(getCanonicalFileName);
|
||||
const hasEmitBlockingDiagnostics = createMap<boolean>();
|
||||
let _compilerOptionsObjectLiteralSyntax: ObjectLiteralExpression;
|
||||
|
||||
let moduleResolutionCache: ModuleResolutionCache;
|
||||
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModuleFull[];
|
||||
if (host.resolveModuleNames) {
|
||||
resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile).map(resolved => {
|
||||
resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(checkAllDefined(moduleNames), containingFile).map(resolved => {
|
||||
// An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
|
||||
if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) {
|
||||
return resolved as ResolvedModuleFull;
|
||||
@@ -459,22 +460,22 @@ namespace ts {
|
||||
else {
|
||||
moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x));
|
||||
const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache).resolvedModule;
|
||||
resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache(moduleNames, containingFile, loader);
|
||||
resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache(checkAllDefined(moduleNames), containingFile, loader);
|
||||
}
|
||||
|
||||
let resolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[], containingFile: string) => ResolvedTypeReferenceDirective[];
|
||||
if (host.resolveTypeReferenceDirectives) {
|
||||
resolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile);
|
||||
resolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives(checkAllDefined(typeDirectiveNames), containingFile);
|
||||
}
|
||||
else {
|
||||
const loader = (typesRef: string, containingFile: string) => resolveTypeReferenceDirective(typesRef, containingFile, options, host).resolvedTypeReferenceDirective;
|
||||
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(typeReferenceDirectiveNames, containingFile, loader);
|
||||
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(checkAllDefined(typeReferenceDirectiveNames), containingFile, loader);
|
||||
}
|
||||
|
||||
const filesByName = createFileMap<SourceFile>();
|
||||
const filesByName = createMap<SourceFile | undefined>();
|
||||
// stores 'filename -> file association' ignoring case
|
||||
// used to track cases when two file names differ only in casing
|
||||
const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createFileMap<SourceFile>(fileName => fileName.toLowerCase()) : undefined;
|
||||
const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createMap<SourceFile>() : undefined;
|
||||
|
||||
const structuralIsReused = tryReuseStructureFromOldProgram();
|
||||
if (structuralIsReused !== StructureIsReused.Completely) {
|
||||
@@ -512,6 +513,8 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
const missingFilePaths = arrayFrom(filesByName.keys(), p => <Path>p).filter(p => !filesByName.get(p));
|
||||
|
||||
// unconditionally set moduleResolutionCache to undefined to avoid unnecessary leaks
|
||||
moduleResolutionCache = undefined;
|
||||
|
||||
@@ -523,6 +526,7 @@ namespace ts {
|
||||
getSourceFile,
|
||||
getSourceFileByPath,
|
||||
getSourceFiles: () => files,
|
||||
getMissingFilePaths: () => missingFilePaths,
|
||||
getCompilerOptions: () => options,
|
||||
getSyntacticDiagnostics,
|
||||
getOptionsDiagnostics,
|
||||
@@ -552,6 +556,10 @@ namespace ts {
|
||||
|
||||
return program;
|
||||
|
||||
function toPath(fileName: string): Path {
|
||||
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
}
|
||||
|
||||
function getCommonSourceDirectory() {
|
||||
if (commonSourceDirectory === undefined) {
|
||||
const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary));
|
||||
@@ -576,7 +584,7 @@ namespace ts {
|
||||
if (!classifiableNames) {
|
||||
// Initialize a checker so that all our files are bound.
|
||||
getTypeChecker();
|
||||
classifiableNames = createMap<string>();
|
||||
classifiableNames = createUnderscoreEscapedMap<true>();
|
||||
|
||||
for (const sourceFile of files) {
|
||||
copyEntries(sourceFile.classifiableNames, classifiableNames);
|
||||
@@ -802,6 +810,10 @@ namespace ts {
|
||||
// moduleAugmentations has changed
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
if ((oldSourceFile.flags & NodeFlags.PossiblyContainsDynamicImport) !== (newSourceFile.flags & NodeFlags.PossiblyContainsDynamicImport)) {
|
||||
// dynamicImport has changed
|
||||
oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
|
||||
// 'types' references has changed
|
||||
@@ -857,6 +869,21 @@ namespace ts {
|
||||
return oldProgram.structureIsReused;
|
||||
}
|
||||
|
||||
// If a file has ceased to be missing, then we need to discard some of the old
|
||||
// structure in order to pick it up.
|
||||
// Caution: if the file has created and then deleted between since it was discovered to
|
||||
// be missing, then the corresponding file watcher will have been closed and no new one
|
||||
// will be created until we encounter a change that prevents complete structure reuse.
|
||||
// During this interval, creation of the file will go unnoticed. We expect this to be
|
||||
// both rare and low-impact.
|
||||
if (oldProgram.getMissingFilePaths().some(missingFilePath => host.fileExists(missingFilePath))) {
|
||||
return oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
for (const p of oldProgram.getMissingFilePaths()) {
|
||||
filesByName.set(p, undefined);
|
||||
}
|
||||
|
||||
// update fileName -> file mapping
|
||||
for (let i = 0; i < newSourceFiles.length; i++) {
|
||||
filesByName.set(filePaths[i], newSourceFiles[i]);
|
||||
@@ -911,7 +938,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isEmitBlocked(emitFileName: string): boolean {
|
||||
return hasEmitBlockingDiagnostics.contains(toPath(emitFileName, currentDirectory, getCanonicalFileName));
|
||||
return hasEmitBlockingDiagnostics.has(toPath(emitFileName));
|
||||
}
|
||||
|
||||
function emitWorker(program: Program, sourceFile: SourceFile, writeFileCallback: WriteFileCallback, cancellationToken: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult {
|
||||
@@ -970,7 +997,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getSourceFile(fileName: string): SourceFile {
|
||||
return getSourceFileByPath(toPath(fileName, currentDirectory, getCanonicalFileName));
|
||||
return getSourceFileByPath(toPath(fileName));
|
||||
}
|
||||
|
||||
function getSourceFileByPath(path: Path): SourceFile {
|
||||
@@ -1132,7 +1159,6 @@ namespace ts {
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
// type annotation
|
||||
if ((<FunctionLikeDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration>parent).type === node) {
|
||||
@@ -1170,10 +1196,14 @@ namespace ts {
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics.enum_declarations_can_only_be_used_in_a_ts_file));
|
||||
return;
|
||||
case SyntaxKind.TypeAssertionExpression:
|
||||
const typeAssertionExpression = <TypeAssertion>node;
|
||||
diagnostics.push(createDiagnosticForNode(typeAssertionExpression.type, Diagnostics.type_assertion_expressions_can_only_be_used_in_a_ts_file));
|
||||
case SyntaxKind.NonNullExpression:
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics.non_null_assertions_can_only_be_used_in_a_ts_file));
|
||||
return;
|
||||
case SyntaxKind.AsExpression:
|
||||
diagnostics.push(createDiagnosticForNode((node as AsExpression).type, Diagnostics.type_assertion_expressions_can_only_be_used_in_a_ts_file));
|
||||
return;
|
||||
case SyntaxKind.TypeAssertionExpression:
|
||||
Debug.fail(); // Won't parse these in a JS file anyway, as they are interpreted as JSX.
|
||||
}
|
||||
|
||||
const prevParent = parent;
|
||||
@@ -1197,7 +1227,6 @@ namespace ts {
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
// Check type parameters
|
||||
if (nodes === (<ClassDeclaration | FunctionLikeDeclaration>parent).typeParameters) {
|
||||
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.type_parameter_declarations_can_only_be_used_in_a_ts_file));
|
||||
@@ -1311,7 +1340,7 @@ namespace ts {
|
||||
const result = getDiagnostics(sourceFile, cancellationToken) || emptyArray;
|
||||
if (sourceFile) {
|
||||
if (!cache.perFile) {
|
||||
cache.perFile = createFileMap<Diagnostic[]>();
|
||||
cache.perFile = createMap<Diagnostic[]>();
|
||||
}
|
||||
cache.perFile.set(sourceFile.path, result);
|
||||
}
|
||||
@@ -1328,7 +1357,11 @@ namespace ts {
|
||||
function getOptionsDiagnostics(): Diagnostic[] {
|
||||
return sortAndDeduplicateDiagnostics(concatenate(
|
||||
fileProcessingDiagnostics.getGlobalDiagnostics(),
|
||||
programDiagnostics.getGlobalDiagnostics()));
|
||||
concatenate(
|
||||
programDiagnostics.getGlobalDiagnostics(),
|
||||
options.configFile ? programDiagnostics.getDiagnostics(options.configFile.fileName) : []
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
function getGlobalDiagnostics(): Diagnostic[] {
|
||||
@@ -1360,8 +1393,8 @@ namespace ts {
|
||||
const isExternalModuleFile = isExternalModule(file);
|
||||
|
||||
// file.imports may not be undefined if there exists dynamic import
|
||||
let imports: LiteralExpression[];
|
||||
let moduleAugmentations: LiteralExpression[];
|
||||
let imports: StringLiteral[];
|
||||
let moduleAugmentations: StringLiteral[];
|
||||
let ambientModules: string[];
|
||||
|
||||
// If we are importing helpers, we need to add a synthetic reference to resolve the
|
||||
@@ -1379,7 +1412,7 @@ namespace ts {
|
||||
|
||||
for (const node of file.statements) {
|
||||
collectModuleReferences(node, /*inAmbientModule*/ false);
|
||||
if ((file.flags & NodeFlags.PossiblyContainDynamicImport) || isJavaScriptFile) {
|
||||
if ((file.flags & NodeFlags.PossiblyContainsDynamicImport) || isJavaScriptFile) {
|
||||
collectDynamicImportOrRequireCalls(node);
|
||||
}
|
||||
}
|
||||
@@ -1396,35 +1429,36 @@ namespace ts {
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
const moduleNameExpr = getExternalModuleName(node);
|
||||
if (!moduleNameExpr || moduleNameExpr.kind !== SyntaxKind.StringLiteral) {
|
||||
if (!moduleNameExpr || !isStringLiteral(moduleNameExpr)) {
|
||||
break;
|
||||
}
|
||||
if (!(<LiteralExpression>moduleNameExpr).text) {
|
||||
if (!moduleNameExpr.text) {
|
||||
break;
|
||||
}
|
||||
|
||||
// TypeScript 1.0 spec (April 2014): 12.1.6
|
||||
// An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
|
||||
// only through top - level external module names. Relative external module names are not permitted.
|
||||
if (!inAmbientModule || !isExternalModuleNameRelative((<LiteralExpression>moduleNameExpr).text)) {
|
||||
(imports || (imports = [])).push(<LiteralExpression>moduleNameExpr);
|
||||
if (!inAmbientModule || !isExternalModuleNameRelative(moduleNameExpr.text)) {
|
||||
(imports || (imports = [])).push(moduleNameExpr);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
if (isAmbientModule(<ModuleDeclaration>node) && (inAmbientModule || hasModifier(node, ModifierFlags.Ambient) || file.isDeclarationFile)) {
|
||||
const moduleName = <LiteralExpression>(<ModuleDeclaration>node).name;
|
||||
const moduleName = <StringLiteral>(<ModuleDeclaration>node).name; // TODO: GH#17347
|
||||
const nameText = ts.getTextOfIdentifierOrLiteral(moduleName);
|
||||
// Ambient module declarations can be interpreted as augmentations for some existing external modules.
|
||||
// This will happen in two cases:
|
||||
// - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope
|
||||
// - if current file is not external module then module augmentation is an ambient module declaration with non-relative module name
|
||||
// immediately nested in top level ambient module declaration .
|
||||
if (isExternalModuleFile || (inAmbientModule && !isExternalModuleNameRelative(moduleName.text))) {
|
||||
if (isExternalModuleFile || (inAmbientModule && !isExternalModuleNameRelative(nameText))) {
|
||||
(moduleAugmentations || (moduleAugmentations = [])).push(moduleName);
|
||||
}
|
||||
else if (!inAmbientModule) {
|
||||
if (file.isDeclarationFile) {
|
||||
// for global .d.ts files record name of ambient module
|
||||
(ambientModules || (ambientModules = [])).push(moduleName.text);
|
||||
(ambientModules || (ambientModules = [])).push(nameText);
|
||||
}
|
||||
// An AmbientExternalModuleDeclaration declares an external module.
|
||||
// This type of declaration is permitted only in the global module.
|
||||
@@ -1459,7 +1493,7 @@ namespace ts {
|
||||
|
||||
/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
|
||||
function getSourceFileFromReference(referencingFile: SourceFile, ref: FileReference): SourceFile | undefined {
|
||||
return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), fileName => filesByName.get(toPath(fileName, currentDirectory, getCanonicalFileName)));
|
||||
return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), fileName => filesByName.get(toPath(fileName)));
|
||||
}
|
||||
|
||||
function getSourceFileFromReferenceWorker(
|
||||
@@ -1495,7 +1529,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
const sourceFileWithAddedExtension = forEach(supportedExtensions, extension => getSourceFile(fileName + extension));
|
||||
if (fail && !sourceFileWithAddedExtension) fail(Diagnostics.File_0_not_found, fileName + ".ts");
|
||||
if (fail && !sourceFileWithAddedExtension) fail(Diagnostics.File_0_not_found, fileName + Extension.Ts);
|
||||
return sourceFileWithAddedExtension;
|
||||
}
|
||||
}
|
||||
@@ -1503,7 +1537,7 @@ namespace ts {
|
||||
/** This has side effects through `findSourceFile`. */
|
||||
function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): void {
|
||||
getSourceFileFromReferenceWorker(fileName,
|
||||
fileName => findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, refFile, refPos, refEnd),
|
||||
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd),
|
||||
(diagnostic, ...args) => {
|
||||
fileProcessingDiagnostics.add(refFile !== undefined && refEnd !== undefined && refPos !== undefined
|
||||
? createFileDiagnostic(refFile, refPos, refEnd - refPos, diagnostic, ...args)
|
||||
@@ -1524,7 +1558,7 @@ namespace ts {
|
||||
|
||||
// Get source file from normalized fileName
|
||||
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile {
|
||||
if (filesByName.contains(path)) {
|
||||
if (filesByName.has(path)) {
|
||||
const file = filesByName.get(path);
|
||||
// try to check if we've already seen this file but with a different casing in path
|
||||
// NOTE: this only makes sense for case-insensitive file systems
|
||||
@@ -1572,13 +1606,14 @@ namespace ts {
|
||||
file.path = path;
|
||||
|
||||
if (host.useCaseSensitiveFileNames()) {
|
||||
const pathLowerCase = path.toLowerCase();
|
||||
// for case-sensitive file systems check if we've already seen some file with similar filename ignoring case
|
||||
const existingFile = filesByNameIgnoreCase.get(path);
|
||||
const existingFile = filesByNameIgnoreCase.get(pathLowerCase);
|
||||
if (existingFile) {
|
||||
reportFileNamesDifferOnlyInCasingError(fileName, existingFile.fileName, refFile, refPos, refEnd);
|
||||
}
|
||||
else {
|
||||
filesByNameIgnoreCase.set(path, file);
|
||||
filesByNameIgnoreCase.set(pathLowerCase, file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1725,7 +1760,7 @@ namespace ts {
|
||||
modulesWithElidedImports.set(file.path, true);
|
||||
}
|
||||
else if (shouldAddFile) {
|
||||
const path = toPath(resolvedFileName, currentDirectory, getCanonicalFileName);
|
||||
const path = toPath(resolvedFileName);
|
||||
const pos = skipTrivia(file.text, file.imports[i].pos);
|
||||
findSourceFile(resolvedFileName, path, /*isDefaultLib*/ false, file, pos, file.imports[i].end);
|
||||
}
|
||||
@@ -1773,33 +1808,33 @@ namespace ts {
|
||||
function verifyCompilerOptions() {
|
||||
if (options.isolatedModules) {
|
||||
if (options.declaration) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "declaration", "isolatedModules"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "declaration", "isolatedModules");
|
||||
}
|
||||
|
||||
if (options.noEmitOnError) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noEmitOnError", "isolatedModules"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noEmitOnError", "isolatedModules");
|
||||
}
|
||||
|
||||
if (options.out) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "isolatedModules"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "isolatedModules");
|
||||
}
|
||||
|
||||
if (options.outFile) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "outFile", "isolatedModules"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "outFile", "isolatedModules");
|
||||
}
|
||||
}
|
||||
|
||||
if (options.inlineSourceMap) {
|
||||
if (options.sourceMap) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceMap", "inlineSourceMap"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceMap", "inlineSourceMap");
|
||||
}
|
||||
if (options.mapRoot) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "mapRoot", "inlineSourceMap"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "mapRoot", "inlineSourceMap");
|
||||
}
|
||||
}
|
||||
|
||||
if (options.paths && options.baseUrl === undefined) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_paths_cannot_be_used_without_specifying_baseUrl_option));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_paths_cannot_be_used_without_specifying_baseUrl_option, "paths");
|
||||
}
|
||||
|
||||
if (options.paths) {
|
||||
@@ -1808,63 +1843,65 @@ namespace ts {
|
||||
continue;
|
||||
}
|
||||
if (!hasZeroOrOneAsteriskCharacter(key)) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, key));
|
||||
createDiagnosticForOptionPaths(/*onKey*/ true, key, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, key);
|
||||
}
|
||||
if (isArray(options.paths[key])) {
|
||||
if (options.paths[key].length === 0) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Substitutions_for_pattern_0_shouldn_t_be_an_empty_array, key));
|
||||
const len = options.paths[key].length;
|
||||
if (len === 0) {
|
||||
createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_shouldn_t_be_an_empty_array, key);
|
||||
}
|
||||
for (const subst of options.paths[key]) {
|
||||
for (let i = 0; i < len; i++) {
|
||||
const subst = options.paths[key][i];
|
||||
const typeOfSubst = typeof subst;
|
||||
if (typeOfSubst === "string") {
|
||||
if (!hasZeroOrOneAsteriskCharacter(subst)) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Substitution_0_in_pattern_1_in_can_have_at_most_one_Asterisk_character, subst, key));
|
||||
createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_in_pattern_1_in_can_have_at_most_one_Asterisk_character, subst, key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Substitution_0_for_pattern_1_has_incorrect_type_expected_string_got_2, subst, key, typeOfSubst));
|
||||
createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_for_pattern_1_has_incorrect_type_expected_string_got_2, subst, key, typeOfSubst);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Substitutions_for_pattern_0_should_be_an_array, key));
|
||||
createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_should_be_an_array, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.sourceMap && !options.inlineSourceMap) {
|
||||
if (options.inlineSources) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources");
|
||||
}
|
||||
if (options.sourceRoot) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot");
|
||||
}
|
||||
}
|
||||
|
||||
if (options.out && options.outFile) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "outFile"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "outFile");
|
||||
}
|
||||
|
||||
if (options.mapRoot && !options.sourceMap) {
|
||||
// Error to specify --mapRoot without --sourcemap
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "mapRoot", "sourceMap"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "mapRoot", "sourceMap");
|
||||
}
|
||||
|
||||
if (options.declarationDir) {
|
||||
if (!options.declaration) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "declarationDir", "declaration"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "declarationDir", "declaration");
|
||||
}
|
||||
if (options.out || options.outFile) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "declarationDir", options.out ? "out" : "outFile"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "declarationDir", options.out ? "out" : "outFile");
|
||||
}
|
||||
}
|
||||
|
||||
if (options.lib && options.noLib) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "lib", "noLib"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "lib", "noLib");
|
||||
}
|
||||
|
||||
if (options.noImplicitUseStrict && (options.alwaysStrict === undefined ? options.strict : options.alwaysStrict)) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noImplicitUseStrict", "alwaysStrict"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noImplicitUseStrict", "alwaysStrict");
|
||||
}
|
||||
|
||||
const languageVersion = options.target || ScriptTarget.ES3;
|
||||
@@ -1873,7 +1910,7 @@ namespace ts {
|
||||
const firstNonAmbientExternalModuleSourceFile = forEach(files, f => isExternalModule(f) && !f.isDeclarationFile ? f : undefined);
|
||||
if (options.isolatedModules) {
|
||||
if (options.module === ModuleKind.None && languageVersion < ScriptTarget.ES2015) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher, "isolatedModules", "target");
|
||||
}
|
||||
|
||||
const firstNonExternalModuleSourceFile = forEach(files, f => !isExternalModule(f) && !f.isDeclarationFile ? f : undefined);
|
||||
@@ -1891,7 +1928,7 @@ namespace ts {
|
||||
// Cannot specify module gen that isn't amd or system with --out
|
||||
if (outFile) {
|
||||
if (options.module && !(options.module === ModuleKind.AMD || options.module === ModuleKind.System)) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Only_amd_and_system_modules_are_supported_alongside_0, options.out ? "out" : "outFile"));
|
||||
createDiagnosticForOptionName(Diagnostics.Only_amd_and_system_modules_are_supported_alongside_0, options.out ? "out" : "outFile", "module");
|
||||
}
|
||||
else if (options.module === undefined && firstNonAmbientExternalModuleSourceFile) {
|
||||
const span = getErrorSpanForNode(firstNonAmbientExternalModuleSourceFile, firstNonAmbientExternalModuleSourceFile.externalModuleIndicator);
|
||||
@@ -1910,12 +1947,12 @@ namespace ts {
|
||||
|
||||
// If we failed to find a good common directory, but outDir is specified and at least one of our files is on a windows drive/URL/other resource, add a failure
|
||||
if (options.outDir && dir === "" && forEach(files, file => getRootLength(file.fileName) > 1)) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files));
|
||||
createDiagnosticForOptionName(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files, "outDir");
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.noEmit && options.allowJs && options.declaration) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration");
|
||||
}
|
||||
|
||||
if (options.checkJs && !options.allowJs) {
|
||||
@@ -1924,25 +1961,25 @@ namespace ts {
|
||||
|
||||
if (options.emitDecoratorMetadata &&
|
||||
!options.experimentalDecorators) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators");
|
||||
}
|
||||
|
||||
if (options.jsxFactory) {
|
||||
if (options.reactNamespace) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory"));
|
||||
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory");
|
||||
}
|
||||
if (!parseIsolatedEntityName(options.jsxFactory, languageVersion)) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFactory));
|
||||
createOptionValueDiagnostic("jsxFactory", Diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFactory);
|
||||
}
|
||||
}
|
||||
else if (options.reactNamespace && !isIdentifierText(options.reactNamespace, languageVersion)) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace));
|
||||
createOptionValueDiagnostic("reactNamespace", Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace);
|
||||
}
|
||||
|
||||
// If the emit is enabled make sure that every output file is unique and not overwriting any of the input files
|
||||
if (!options.noEmit && !options.suppressOutputPathCheck) {
|
||||
const emitHost = getEmitHost();
|
||||
const emitFilesSeen = createFileMap<boolean>(!host.useCaseSensitiveFileNames() ? key => key.toLocaleLowerCase() : undefined);
|
||||
const emitFilesSeen = createMap<true>();
|
||||
forEachEmittedFile(emitHost, (emitFileNames) => {
|
||||
verifyEmitFilePath(emitFileNames.jsFilePath, emitFilesSeen);
|
||||
verifyEmitFilePath(emitFileNames.declarationFilePath, emitFilesSeen);
|
||||
@@ -1950,11 +1987,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Verify that all the emit files are unique and don't overwrite input files
|
||||
function verifyEmitFilePath(emitFileName: string, emitFilesSeen: FileMap<boolean>) {
|
||||
function verifyEmitFilePath(emitFileName: string, emitFilesSeen: Map<true>) {
|
||||
if (emitFileName) {
|
||||
const emitFilePath = toPath(emitFileName, currentDirectory, getCanonicalFileName);
|
||||
const emitFilePath = toPath(emitFileName);
|
||||
// Report error if the output overwrites input file
|
||||
if (filesByName.contains(emitFilePath)) {
|
||||
if (filesByName.has(emitFilePath)) {
|
||||
let chain: DiagnosticMessageChain;
|
||||
if (!options.configFilePath) {
|
||||
// The program is from either an inferred project or an external project
|
||||
@@ -1964,20 +2001,106 @@ namespace ts {
|
||||
blockEmittingOfFile(emitFileName, createCompilerDiagnosticFromMessageChain(chain));
|
||||
}
|
||||
|
||||
const emitFileKey = !host.useCaseSensitiveFileNames() ? emitFilePath.toLocaleLowerCase() : emitFilePath;
|
||||
// Report error if multiple files write into same file
|
||||
if (emitFilesSeen.contains(emitFilePath)) {
|
||||
if (emitFilesSeen.has(emitFileKey)) {
|
||||
// Already seen the same emit file - report error
|
||||
blockEmittingOfFile(emitFileName, createCompilerDiagnostic(Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files, emitFileName));
|
||||
}
|
||||
else {
|
||||
emitFilesSeen.set(emitFilePath, true);
|
||||
emitFilesSeen.set(emitFileKey, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createDiagnosticForOptionPathKeyValue(key: string, valueIndex: number, message: DiagnosticMessage, arg0: string | number, arg1: string | number, arg2?: string | number) {
|
||||
let needCompilerDiagnostic = true;
|
||||
const pathsSyntax = getOptionPathsSyntax();
|
||||
for (const pathProp of pathsSyntax) {
|
||||
if (isObjectLiteralExpression(pathProp.initializer)) {
|
||||
for (const keyProps of getPropertyAssignment(pathProp.initializer, key)) {
|
||||
if (isArrayLiteralExpression(keyProps.initializer) &&
|
||||
keyProps.initializer.elements.length > valueIndex) {
|
||||
programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile, keyProps.initializer.elements[valueIndex], message, arg0, arg1, arg2));
|
||||
needCompilerDiagnostic = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needCompilerDiagnostic) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2));
|
||||
}
|
||||
}
|
||||
|
||||
function createDiagnosticForOptionPaths(onKey: boolean, key: string, message: DiagnosticMessage, arg0: string | number) {
|
||||
let needCompilerDiagnostic = true;
|
||||
const pathsSyntax = getOptionPathsSyntax();
|
||||
for (const pathProp of pathsSyntax) {
|
||||
if (isObjectLiteralExpression(pathProp.initializer) &&
|
||||
createOptionDiagnosticInObjectLiteralSyntax(
|
||||
pathProp.initializer, onKey, key, /*key2*/ undefined,
|
||||
message, arg0)) {
|
||||
needCompilerDiagnostic = false;
|
||||
}
|
||||
}
|
||||
if (needCompilerDiagnostic) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(message, arg0));
|
||||
}
|
||||
}
|
||||
|
||||
function getOptionPathsSyntax() {
|
||||
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
|
||||
if (compilerOptionsObjectLiteralSyntax) {
|
||||
return getPropertyAssignment(compilerOptionsObjectLiteralSyntax, "paths");
|
||||
}
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
function createDiagnosticForOptionName(message: DiagnosticMessage, option1: string, option2?: string) {
|
||||
createDiagnosticForOption(/*onKey*/ true, option1, option2, message, option1, option2);
|
||||
}
|
||||
|
||||
function createOptionValueDiagnostic(option1: string, message: DiagnosticMessage, arg0: string) {
|
||||
createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0);
|
||||
}
|
||||
|
||||
function createDiagnosticForOption(onKey: boolean, option1: string, option2: string, message: DiagnosticMessage, arg0: string | number, arg1?: string | number) {
|
||||
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
|
||||
const needCompilerDiagnostic = !compilerOptionsObjectLiteralSyntax ||
|
||||
!createOptionDiagnosticInObjectLiteralSyntax(compilerOptionsObjectLiteralSyntax, onKey, option1, option2, message, arg0, arg1);
|
||||
|
||||
if (needCompilerDiagnostic) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1));
|
||||
}
|
||||
}
|
||||
|
||||
function getCompilerOptionsObjectLiteralSyntax() {
|
||||
if (_compilerOptionsObjectLiteralSyntax === undefined) {
|
||||
_compilerOptionsObjectLiteralSyntax = null; // tslint:disable-line:no-null-keyword
|
||||
if (options.configFile && options.configFile.jsonObject) {
|
||||
for (const prop of getPropertyAssignment(options.configFile.jsonObject, "compilerOptions")) {
|
||||
if (isObjectLiteralExpression(prop.initializer)) {
|
||||
_compilerOptionsObjectLiteralSyntax = prop.initializer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return _compilerOptionsObjectLiteralSyntax;
|
||||
}
|
||||
|
||||
function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string, message: DiagnosticMessage, arg0: string | number, arg1?: string | number): boolean {
|
||||
const props = getPropertyAssignment(objectLiteral, key1, key2);
|
||||
for (const prop of props) {
|
||||
programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile, onKey ? prop.name : prop.initializer, message, arg0, arg1));
|
||||
}
|
||||
return !!props.length;
|
||||
}
|
||||
|
||||
function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) {
|
||||
hasEmitBlockingDiagnostics.set(toPath(emitFileName, currentDirectory, getCanonicalFileName), true);
|
||||
hasEmitBlockingDiagnostics.set(toPath(emitFileName), true);
|
||||
programDiagnostics.add(diag);
|
||||
}
|
||||
}
|
||||
@@ -2009,4 +2132,9 @@ namespace ts {
|
||||
return options.allowJs ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_allowJs_is_not_set;
|
||||
}
|
||||
}
|
||||
|
||||
function checkAllDefined(names: string[]): string[] {
|
||||
Debug.assert(names.every(name => name !== undefined), "A name is undefined.", () => JSON.stringify(names));
|
||||
return names;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-6
File diff suppressed because one or more lines are too long
+36
-17
@@ -22,7 +22,7 @@ namespace ts {
|
||||
*
|
||||
* @param sourceFile The source file.
|
||||
*/
|
||||
setSourceFile(sourceFile: SourceFile): void;
|
||||
setSourceFile(sourceFile: SourceMapSource): void;
|
||||
|
||||
/**
|
||||
* Emits a mapping.
|
||||
@@ -81,7 +81,7 @@ namespace ts {
|
||||
export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
|
||||
const compilerOptions = host.getCompilerOptions();
|
||||
const extendedDiagnostics = compilerOptions.extendedDiagnostics;
|
||||
let currentSourceFile: SourceFile;
|
||||
let currentSource: SourceMapSource;
|
||||
let currentSourceText: string;
|
||||
let sourceMapDir: string; // The directory in which sourcemap will be
|
||||
|
||||
@@ -109,6 +109,13 @@ namespace ts {
|
||||
getSourceMappingURL,
|
||||
};
|
||||
|
||||
/**
|
||||
* Skips trivia such as comments and white-space that can optionally overriden by the source map source
|
||||
*/
|
||||
function skipSourceTrivia(pos: number): number {
|
||||
return currentSource.skipTrivia ? currentSource.skipTrivia(pos) : skipTrivia(currentSourceText, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the SourceMapWriter for a new output file.
|
||||
*
|
||||
@@ -125,7 +132,7 @@ namespace ts {
|
||||
reset();
|
||||
}
|
||||
|
||||
currentSourceFile = undefined;
|
||||
currentSource = undefined;
|
||||
currentSourceText = undefined;
|
||||
|
||||
// Current source map file and its index in the sources list
|
||||
@@ -138,7 +145,7 @@ namespace ts {
|
||||
|
||||
// Initialize source map data
|
||||
sourceMapData = {
|
||||
sourceMapFilePath: sourceMapFilePath,
|
||||
sourceMapFilePath,
|
||||
jsSourceMappingURL: !compilerOptions.inlineSourceMap ? getBaseFileName(normalizeSlashes(sourceMapFilePath)) : undefined,
|
||||
sourceMapFile: getBaseFileName(normalizeSlashes(filePath)),
|
||||
sourceMapSourceRoot: compilerOptions.sourceRoot || "",
|
||||
@@ -192,7 +199,7 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
|
||||
currentSourceFile = undefined;
|
||||
currentSource = undefined;
|
||||
sourceMapDir = undefined;
|
||||
sourceMapSourceIndex = undefined;
|
||||
lastRecordedSourceMapSpan = undefined;
|
||||
@@ -263,7 +270,7 @@ namespace ts {
|
||||
performance.mark("beforeSourcemap");
|
||||
}
|
||||
|
||||
const sourceLinePos = getLineAndCharacterOfPosition(currentSourceFile, pos);
|
||||
const sourceLinePos = getLineAndCharacterOfPosition(currentSource, pos);
|
||||
|
||||
// Convert the location to be one-based.
|
||||
sourceLinePos.line++;
|
||||
@@ -285,8 +292,8 @@ namespace ts {
|
||||
|
||||
// New span
|
||||
lastRecordedSourceMapSpan = {
|
||||
emittedLine: emittedLine,
|
||||
emittedColumn: emittedColumn,
|
||||
emittedLine,
|
||||
emittedColumn,
|
||||
sourceLine: sourceLinePos.line,
|
||||
sourceColumn: sourceLinePos.character,
|
||||
sourceIndex: sourceMapSourceIndex
|
||||
@@ -320,14 +327,22 @@ namespace ts {
|
||||
if (node) {
|
||||
const emitNode = node.emitNode;
|
||||
const emitFlags = emitNode && emitNode.flags;
|
||||
const { pos, end } = emitNode && emitNode.sourceMapRange || node;
|
||||
const range = emitNode && emitNode.sourceMapRange;
|
||||
const { pos, end } = range || node;
|
||||
let source = range && range.source;
|
||||
const oldSource = currentSource;
|
||||
if (source === oldSource) source = undefined;
|
||||
|
||||
if (source) setSourceFile(source);
|
||||
|
||||
if (node.kind !== SyntaxKind.NotEmittedStatement
|
||||
&& (emitFlags & EmitFlags.NoLeadingSourceMap) === 0
|
||||
&& pos >= 0) {
|
||||
emitPos(skipTrivia(currentSourceText, pos));
|
||||
emitPos(skipSourceTrivia(pos));
|
||||
}
|
||||
|
||||
if (source) setSourceFile(oldSource);
|
||||
|
||||
if (emitFlags & EmitFlags.NoNestedSourceMaps) {
|
||||
disabled = true;
|
||||
emitCallback(hint, node);
|
||||
@@ -337,11 +352,15 @@ namespace ts {
|
||||
emitCallback(hint, node);
|
||||
}
|
||||
|
||||
if (source) setSourceFile(source);
|
||||
|
||||
if (node.kind !== SyntaxKind.NotEmittedStatement
|
||||
&& (emitFlags & EmitFlags.NoTrailingSourceMap) === 0
|
||||
&& end >= 0) {
|
||||
emitPos(end);
|
||||
}
|
||||
|
||||
if (source) setSourceFile(oldSource);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,7 +381,7 @@ namespace ts {
|
||||
const emitFlags = emitNode && emitNode.flags;
|
||||
const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges[token];
|
||||
|
||||
tokenPos = skipTrivia(currentSourceText, range ? range.pos : tokenPos);
|
||||
tokenPos = skipSourceTrivia(range ? range.pos : tokenPos);
|
||||
if ((emitFlags & EmitFlags.NoTokenLeadingSourceMaps) === 0 && tokenPos >= 0) {
|
||||
emitPos(tokenPos);
|
||||
}
|
||||
@@ -382,13 +401,13 @@ namespace ts {
|
||||
*
|
||||
* @param sourceFile The source file.
|
||||
*/
|
||||
function setSourceFile(sourceFile: SourceFile) {
|
||||
function setSourceFile(sourceFile: SourceMapSource) {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentSourceFile = sourceFile;
|
||||
currentSourceText = currentSourceFile.text;
|
||||
currentSource = sourceFile;
|
||||
currentSourceText = currentSource.text;
|
||||
|
||||
// Add the file to tsFilePaths
|
||||
// If sourceroot option: Use the relative path corresponding to the common directory path
|
||||
@@ -396,7 +415,7 @@ namespace ts {
|
||||
const sourcesDirectoryPath = compilerOptions.sourceRoot ? host.getCommonSourceDirectory() : sourceMapDir;
|
||||
|
||||
const source = getRelativePathToDirectoryOrUrl(sourcesDirectoryPath,
|
||||
currentSourceFile.fileName,
|
||||
currentSource.fileName,
|
||||
host.getCurrentDirectory(),
|
||||
host.getCanonicalFileName,
|
||||
/*isAbsolutePathAnUrl*/ true);
|
||||
@@ -407,10 +426,10 @@ namespace ts {
|
||||
sourceMapData.sourceMapSources.push(source);
|
||||
|
||||
// The one that can be used from program to get the actual source file
|
||||
sourceMapData.inputSourceFileNames.push(currentSourceFile.fileName);
|
||||
sourceMapData.inputSourceFileNames.push(currentSource.fileName);
|
||||
|
||||
if (compilerOptions.inlineSources) {
|
||||
sourceMapData.sourceMapSourcesContent.push(currentSourceFile.text);
|
||||
sourceMapData.sourceMapSourcesContent.push(currentSource.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+57
-19
@@ -4,7 +4,25 @@ declare function setTimeout(handler: (...args: any[]) => void, timeout: number):
|
||||
declare function clearTimeout(handle: any): void;
|
||||
|
||||
namespace ts {
|
||||
export type FileWatcherCallback = (fileName: string, removed?: boolean) => void;
|
||||
/**
|
||||
* Set a high stack trace limit to provide more information in case of an error.
|
||||
* Called for command-line and server use cases.
|
||||
* Not called if TypeScript is used as a library.
|
||||
*/
|
||||
/* @internal */
|
||||
export function setStackTraceLimit() {
|
||||
if ((Error as any).stackTraceLimit < 100) { // Also tests that we won't set the property if it doesn't exist.
|
||||
(Error as any).stackTraceLimit = 100;
|
||||
}
|
||||
}
|
||||
|
||||
export enum FileWatcherEventKind {
|
||||
Created,
|
||||
Changed,
|
||||
Deleted
|
||||
}
|
||||
|
||||
export type FileWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind) => void;
|
||||
export type DirectoryWatcherCallback = (fileName: string) => void;
|
||||
export interface WatchedFile {
|
||||
fileName: string;
|
||||
@@ -17,7 +35,7 @@ namespace ts {
|
||||
newLine: string;
|
||||
useCaseSensitiveFileNames: boolean;
|
||||
write(s: string): void;
|
||||
readFile(path: string, encoding?: string): string;
|
||||
readFile(path: string, encoding?: string): string | undefined;
|
||||
getFileSize?(path: string): number;
|
||||
writeFile(path: string, data: string, writeByteOrderMark?: boolean): void;
|
||||
/**
|
||||
@@ -33,8 +51,12 @@ namespace ts {
|
||||
getExecutingFilePath(): string;
|
||||
getCurrentDirectory(): string;
|
||||
getDirectories(path: string): string[];
|
||||
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[];
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
getModifiedTime?(path: string): Date;
|
||||
/**
|
||||
* This should be cryptographically secure.
|
||||
* A good implementation is node.js' `crypto.createHash`. (https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm)
|
||||
*/
|
||||
createHash?(data: string): string;
|
||||
getMemoryUsage?(): number;
|
||||
exit(exitCode?: number): void;
|
||||
@@ -87,10 +109,10 @@ namespace ts {
|
||||
directoryExists(path: string): boolean;
|
||||
createDirectory(path: string): void;
|
||||
resolvePath(path: string): string;
|
||||
readFile(path: string): string;
|
||||
readFile(path: string): string | undefined;
|
||||
writeFile(path: string, contents: string): void;
|
||||
getDirectories(path: string): string[];
|
||||
readDirectory(path: string, extensions?: string[], basePaths?: string[], excludeEx?: string, includeFileEx?: string, includeDirEx?: string): string[];
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, basePaths?: ReadonlyArray<string>, excludeEx?: string, includeFileEx?: string, includeDirEx?: string): string[];
|
||||
watchFile?(path: string, callback: FileWatcherCallback): FileWatcher;
|
||||
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
|
||||
realpath(path: string): string;
|
||||
@@ -170,7 +192,7 @@ namespace ts {
|
||||
const callbacks = fileWatcherCallbacks.get(fileName);
|
||||
if (callbacks) {
|
||||
for (const fileCallback of callbacks) {
|
||||
fileCallback(fileName);
|
||||
fileCallback(fileName, FileWatcherEventKind.Changed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,15 +208,22 @@ namespace ts {
|
||||
if (platform === "win32" || platform === "win64") {
|
||||
return false;
|
||||
}
|
||||
// convert current file name to upper case / lower case and check if file exists
|
||||
// (guards against cases when name is already all uppercase or lowercase)
|
||||
return !fileExists(__filename.toUpperCase()) || !fileExists(__filename.toLowerCase());
|
||||
// If this file exists under a different case, we must be case-insensitve.
|
||||
return !fileExists(swapCase(__filename));
|
||||
}
|
||||
|
||||
/** Convert all lowercase chars to uppercase, and vice-versa */
|
||||
function swapCase(s: string): string {
|
||||
return s.replace(/\w/g, (ch) => {
|
||||
const up = ch.toUpperCase();
|
||||
return ch === up ? ch.toLowerCase() : up;
|
||||
});
|
||||
}
|
||||
|
||||
const platform: string = _os.platform();
|
||||
const useCaseSensitiveFileNames = isFileSystemCaseSensitive();
|
||||
|
||||
function readFile(fileName: string, _encoding?: string): string {
|
||||
function readFile(fileName: string, _encoding?: string): string | undefined {
|
||||
if (!fileExists(fileName)) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -277,8 +306,8 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function readDirectory(path: string, extensions?: string[], excludes?: string[], includes?: string[]): string[] {
|
||||
return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), getAccessibleFileSystemEntries);
|
||||
function readDirectory(path: string, extensions?: ReadonlyArray<string>, excludes?: ReadonlyArray<string>, includes?: ReadonlyArray<string>, depth?: number): string[] {
|
||||
return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), depth, getAccessibleFileSystemEntries);
|
||||
}
|
||||
|
||||
const enum FileSystemEntryKind {
|
||||
@@ -315,7 +344,7 @@ namespace ts {
|
||||
const nodeSystem: System = {
|
||||
args: process.argv.slice(2),
|
||||
newLine: _os.EOL,
|
||||
useCaseSensitiveFileNames: useCaseSensitiveFileNames,
|
||||
useCaseSensitiveFileNames,
|
||||
write(s: string): void {
|
||||
process.stdout.write(s);
|
||||
},
|
||||
@@ -336,11 +365,22 @@ namespace ts {
|
||||
}
|
||||
|
||||
function fileChanged(curr: any, prev: any) {
|
||||
if (+curr.mtime <= +prev.mtime) {
|
||||
const isCurrZero = +curr.mtime === 0;
|
||||
const isPrevZero = +prev.mtime === 0;
|
||||
const created = !isCurrZero && isPrevZero;
|
||||
const deleted = isCurrZero && !isPrevZero;
|
||||
|
||||
const eventKind = created
|
||||
? FileWatcherEventKind.Created
|
||||
: deleted
|
||||
? FileWatcherEventKind.Deleted
|
||||
: FileWatcherEventKind.Changed;
|
||||
|
||||
if (eventKind === FileWatcherEventKind.Changed && +curr.mtime <= +prev.mtime) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(fileName);
|
||||
callback(fileName, eventKind);
|
||||
}
|
||||
},
|
||||
watchDirectory: (directoryName, callback, recursive) => {
|
||||
@@ -373,9 +413,7 @@ namespace ts {
|
||||
}
|
||||
);
|
||||
},
|
||||
resolvePath: function(path: string): string {
|
||||
return _path.resolve(path);
|
||||
},
|
||||
resolvePath: path => _path.resolve(path),
|
||||
fileExists,
|
||||
directoryExists,
|
||||
createDirectory(directoryName: string) {
|
||||
@@ -471,7 +509,7 @@ namespace ts {
|
||||
getCurrentDirectory: () => ChakraHost.currentDirectory,
|
||||
getDirectories: ChakraHost.getDirectories,
|
||||
getEnvironmentVariable: ChakraHost.getEnvironmentVariable || (() => ""),
|
||||
readDirectory: (path: string, extensions?: string[], excludes?: string[], includes?: string[]) => {
|
||||
readDirectory(path, extensions, excludes, includes, _depth) {
|
||||
const pattern = getFileMatcherPatterns(path, excludes, includes, !!ChakraHost.useCaseSensitiveFileNames, ChakraHost.currentDirectory);
|
||||
return ChakraHost.readDirectory(path, extensions, pattern.basePaths, pattern.excludePattern, pattern.includeFilePattern, pattern.includeDirectoryPattern);
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/// <reference path="visitor.ts" />
|
||||
/// <reference path="transformers/utilities.ts" />
|
||||
/// <reference path="transformers/ts.ts" />
|
||||
/// <reference path="transformers/jsx.ts" />
|
||||
/// <reference path="transformers/esnext.ts" />
|
||||
@@ -239,7 +240,7 @@ namespace ts {
|
||||
function hoistVariableDeclaration(name: Identifier): void {
|
||||
Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
|
||||
Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
|
||||
const decl = createVariableDeclaration(name);
|
||||
const decl = setEmitFlags(createVariableDeclaration(name), EmitFlags.NoNestedSourceMaps);
|
||||
if (!lexicalEnvironmentVariableDeclarations) {
|
||||
lexicalEnvironmentVariableDeclarations = [decl];
|
||||
}
|
||||
|
||||
@@ -411,11 +411,11 @@ namespace ts {
|
||||
}
|
||||
else if (isStringOrNumericLiteral(propertyName)) {
|
||||
const argumentExpression = getSynthesizedClone(propertyName);
|
||||
argumentExpression.text = unescapeIdentifier(argumentExpression.text);
|
||||
argumentExpression.text = argumentExpression.text;
|
||||
return createElementAccess(value, argumentExpression);
|
||||
}
|
||||
else {
|
||||
const name = createIdentifier(unescapeIdentifier(propertyName.text));
|
||||
const name = createIdentifier(unescapeLeadingUnderscores(propertyName.escapedText));
|
||||
return createPropertyAccess(value, name);
|
||||
}
|
||||
}
|
||||
@@ -492,7 +492,7 @@ namespace ts {
|
||||
/** Given value: o, propName: p, pattern: { a, b, ...p } from the original statement
|
||||
* `{ a, b, ...p } = o`, create `p = __rest(o, ["a", "b"]);`
|
||||
*/
|
||||
function createRestCall(context: TransformationContext, value: Expression, elements: BindingOrAssignmentElement[], computedTempVariables: Expression[], location: TextRange): Expression {
|
||||
function createRestCall(context: TransformationContext, value: Expression, elements: ReadonlyArray<BindingOrAssignmentElement>, computedTempVariables: ReadonlyArray<Expression>, location: TextRange): Expression {
|
||||
context.requestEmitHelper(restHelper);
|
||||
const propertyNames: Expression[] = [];
|
||||
let computedTempVariableOffset = 0;
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace ts {
|
||||
* set of labels that occurred inside the converted loop
|
||||
* used to determine if labeled jump can be emitted as is or it should be dispatched to calling code
|
||||
*/
|
||||
labels?: Map<string>;
|
||||
labels?: Map<boolean>;
|
||||
/*
|
||||
* collection of labeled jumps that transfer control outside the converted loop.
|
||||
* maps store association 'label -> labelMarker' where
|
||||
@@ -647,7 +647,7 @@ namespace ts {
|
||||
if (isGeneratedIdentifier(node)) {
|
||||
return node;
|
||||
}
|
||||
if (node.text !== "arguments" || !resolver.isArgumentsLocalBinding(node)) {
|
||||
if (node.escapedText !== "arguments" || !resolver.isArgumentsLocalBinding(node)) {
|
||||
return node;
|
||||
}
|
||||
return convertedLoopState.argumentsName || (convertedLoopState.argumentsName = createUniqueName("arguments"));
|
||||
@@ -661,7 +661,7 @@ namespace ts {
|
||||
// - break/continue is non-labeled and located in non-converted loop/switch statement
|
||||
const jump = node.kind === SyntaxKind.BreakStatement ? Jump.Break : Jump.Continue;
|
||||
const canUseBreakOrContinue =
|
||||
(node.label && convertedLoopState.labels && convertedLoopState.labels.get(node.label.text)) ||
|
||||
(node.label && convertedLoopState.labels && convertedLoopState.labels.get(unescapeLeadingUnderscores(node.label.escapedText))) ||
|
||||
(!node.label && (convertedLoopState.allowedNonLabeledJumps & jump));
|
||||
|
||||
if (!canUseBreakOrContinue) {
|
||||
@@ -679,12 +679,12 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
if (node.kind === SyntaxKind.BreakStatement) {
|
||||
labelMarker = `break-${node.label.text}`;
|
||||
setLabeledJump(convertedLoopState, /*isBreak*/ true, node.label.text, labelMarker);
|
||||
labelMarker = `break-${node.label.escapedText}`;
|
||||
setLabeledJump(convertedLoopState, /*isBreak*/ true, unescapeLeadingUnderscores(node.label.escapedText), labelMarker);
|
||||
}
|
||||
else {
|
||||
labelMarker = `continue-${node.label.text}`;
|
||||
setLabeledJump(convertedLoopState, /*isBreak*/ false, node.label.text, labelMarker);
|
||||
labelMarker = `continue-${node.label.escapedText}`;
|
||||
setLabeledJump(convertedLoopState, /*isBreak*/ false, unescapeLeadingUnderscores(node.label.escapedText), labelMarker);
|
||||
}
|
||||
}
|
||||
let returnExpression: Expression = createLiteral(labelMarker);
|
||||
@@ -1963,7 +1963,7 @@ namespace ts {
|
||||
updated,
|
||||
setTextRange(
|
||||
createNodeArray(
|
||||
prependCaptureNewTargetIfNeeded(updated.statements, node, /*copyOnWrite*/ true)
|
||||
prependCaptureNewTargetIfNeeded(updated.statements as MutableNodeArray<Statement>, node, /*copyOnWrite*/ true)
|
||||
),
|
||||
/*location*/ updated.statements
|
||||
)
|
||||
@@ -2236,16 +2236,16 @@ namespace ts {
|
||||
}
|
||||
|
||||
function recordLabel(node: LabeledStatement) {
|
||||
convertedLoopState.labels.set(node.label.text, node.label.text);
|
||||
convertedLoopState.labels.set(unescapeLeadingUnderscores(node.label.escapedText), true);
|
||||
}
|
||||
|
||||
function resetLabel(node: LabeledStatement) {
|
||||
convertedLoopState.labels.set(node.label.text, undefined);
|
||||
convertedLoopState.labels.set(unescapeLeadingUnderscores(node.label.escapedText), false);
|
||||
}
|
||||
|
||||
function visitLabeledStatement(node: LabeledStatement): VisitResult<Statement> {
|
||||
if (convertedLoopState && !convertedLoopState.labels) {
|
||||
convertedLoopState.labels = createMap<string>();
|
||||
convertedLoopState.labels = createMap<boolean>();
|
||||
}
|
||||
const statement = unwrapInnermostStatementOfLabel(node, convertedLoopState && recordLabel);
|
||||
return isIterationStatement(statement, /*lookInLabeledStatements*/ false)
|
||||
@@ -3053,7 +3053,7 @@ namespace ts {
|
||||
else {
|
||||
loopParameters.push(createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, name));
|
||||
if (resolver.getNodeCheckFlags(decl) & NodeCheckFlags.NeedsLoopOutParameter) {
|
||||
const outParamName = createUniqueName("out_" + unescapeIdentifier(name.text));
|
||||
const outParamName = createUniqueName("out_" + unescapeLeadingUnderscores(name.escapedText));
|
||||
loopOutParameters.push({ originalName: name, outParamName });
|
||||
}
|
||||
}
|
||||
@@ -3199,7 +3199,7 @@ namespace ts {
|
||||
|
||||
function addStatementToStartOfBlock(block: Block, statement: Statement): Block {
|
||||
const transformedStatements = visitNodes(block.statements, visitor, isStatement);
|
||||
return updateBlock(block, [statement].concat(transformedStatements));
|
||||
return updateBlock(block, [statement, ...transformedStatements]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3401,28 +3401,19 @@ namespace ts {
|
||||
classBodyStart++;
|
||||
}
|
||||
|
||||
// We reuse the comment and source-map positions from the original variable statement
|
||||
// and class alias, while converting the function declaration for the class constructor
|
||||
// into an expression.
|
||||
// The next statement is the function declaration.
|
||||
statements.push(funcStatements[classBodyStart]);
|
||||
classBodyStart++;
|
||||
|
||||
// Add the class alias following the declaration.
|
||||
statements.push(
|
||||
updateVariableStatement(
|
||||
varStatement,
|
||||
/*modifiers*/ undefined,
|
||||
updateVariableDeclarationList(varStatement.declarationList, [
|
||||
updateVariableDeclaration(variable,
|
||||
variable.name,
|
||||
/*type*/ undefined,
|
||||
updateBinary(aliasAssignment,
|
||||
aliasAssignment.left,
|
||||
convertFunctionDeclarationToExpression(
|
||||
cast(funcStatements[classBodyStart], isFunctionDeclaration)
|
||||
)
|
||||
)
|
||||
)
|
||||
])
|
||||
createStatement(
|
||||
createAssignment(
|
||||
aliasAssignment.left,
|
||||
cast(variable.name, isIdentifier)
|
||||
)
|
||||
)
|
||||
);
|
||||
classBodyStart++;
|
||||
}
|
||||
|
||||
// Find the trailing 'return' statement (4)
|
||||
@@ -3588,7 +3579,7 @@ namespace ts {
|
||||
// Map spans of spread expressions into their expressions and spans of other
|
||||
// expressions into an array literal.
|
||||
const numElements = elements.length;
|
||||
const segments = flatten(
|
||||
const segments = flatten<Expression>(
|
||||
spanMap(elements, partitionSpread, (partition, visitPartition, _start, end) =>
|
||||
visitPartition(partition, multiLine, hasTrailingComma && end === numElements)
|
||||
)
|
||||
@@ -3600,7 +3591,7 @@ namespace ts {
|
||||
if (isCallExpression(firstSegment)
|
||||
&& isIdentifier(firstSegment.expression)
|
||||
&& (getEmitFlags(firstSegment.expression) & EmitFlags.HelperName)
|
||||
&& firstSegment.expression.text === "___spread") {
|
||||
&& firstSegment.expression.escapedText === "___spread") {
|
||||
return segments[0];
|
||||
}
|
||||
}
|
||||
@@ -3851,7 +3842,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function visitMetaProperty(node: MetaProperty) {
|
||||
if (node.keywordToken === SyntaxKind.NewKeyword && node.name.text === "target") {
|
||||
if (node.keywordToken === SyntaxKind.NewKeyword && node.name.escapedText === "target") {
|
||||
if (hierarchyFacts & HierarchyFacts.ComputedPropertyName) {
|
||||
hierarchyFacts |= HierarchyFacts.NewTargetInComputedPropertyName;
|
||||
}
|
||||
@@ -4076,7 +4067,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
const expression = (<SpreadElement>callArgument).expression;
|
||||
return isIdentifier(expression) && expression.text === "arguments";
|
||||
return isIdentifier(expression) && expression.escapedText === "arguments";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -369,7 +369,7 @@ namespace ts {
|
||||
function substitutePropertyAccessExpression(node: PropertyAccessExpression) {
|
||||
if (node.expression.kind === SyntaxKind.SuperKeyword) {
|
||||
return createSuperAccessInAsyncMethod(
|
||||
createLiteral(node.name.text),
|
||||
createLiteral(unescapeLeadingUnderscores(node.name.escapedText)),
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace ts {
|
||||
* @param name An Identifier
|
||||
*/
|
||||
function trySubstituteReservedName(name: Identifier) {
|
||||
const token = name.originalKeywordKind || (nodeIsSynthesized(name) ? stringToToken(name.text) : undefined);
|
||||
const token = name.originalKeywordKind || (nodeIsSynthesized(name) ? stringToToken(unescapeLeadingUnderscores(name.escapedText)) : undefined);
|
||||
if (token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord) {
|
||||
return setTextRange(createLiteral(name), name);
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace ts {
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function chunkObjectLiteralElements(elements: ObjectLiteralElement[]): Expression[] {
|
||||
function chunkObjectLiteralElements(elements: ReadonlyArray<ObjectLiteralElement>): Expression[] {
|
||||
let chunkObject: (ShorthandPropertyAssignment | PropertyAssignment)[];
|
||||
const objects: Expression[] = [];
|
||||
for (const e of elements) {
|
||||
@@ -776,7 +776,7 @@ namespace ts {
|
||||
function substitutePropertyAccessExpression(node: PropertyAccessExpression) {
|
||||
if (node.expression.kind === SyntaxKind.SuperKeyword) {
|
||||
return createSuperAccessInAsyncMethod(
|
||||
createLiteral(node.name.text),
|
||||
createLiteral(unescapeLeadingUnderscores(node.name.escapedText)),
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
@@ -640,10 +640,13 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return createStatement(
|
||||
inlineExpressions(
|
||||
map(variables, transformInitializedVariable)
|
||||
)
|
||||
return setSourceMapRange(
|
||||
createStatement(
|
||||
inlineExpressions(
|
||||
map(variables, transformInitializedVariable)
|
||||
)
|
||||
),
|
||||
node
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1173,7 +1176,7 @@ namespace ts {
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function transformAndEmitStatements(statements: Statement[], start = 0) {
|
||||
function transformAndEmitStatements(statements: ReadonlyArray<Statement>, start = 0) {
|
||||
const numStatements = statements.length;
|
||||
for (let i = start; i < numStatements; i++) {
|
||||
transformAndEmitStatement(statements[i]);
|
||||
@@ -1281,9 +1284,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
function transformInitializedVariable(node: VariableDeclaration) {
|
||||
return createAssignment(
|
||||
<Identifier>getSynthesizedClone(node.name),
|
||||
visitNode(node.initializer, visitor, isExpression)
|
||||
return setSourceMapRange(
|
||||
createAssignment(
|
||||
setSourceMapRange(<Identifier>getSynthesizedClone(node.name), node.name),
|
||||
visitNode(node.initializer, visitor, isExpression)
|
||||
),
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1629,14 +1635,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
function transformAndEmitContinueStatement(node: ContinueStatement): void {
|
||||
const label = findContinueTarget(node.label ? node.label.text : undefined);
|
||||
const label = findContinueTarget(node.label ? unescapeLeadingUnderscores(node.label.escapedText) : undefined);
|
||||
Debug.assert(label > 0, "Expected continue statment to point to a valid Label.");
|
||||
emitBreak(label, /*location*/ node);
|
||||
}
|
||||
|
||||
function visitContinueStatement(node: ContinueStatement): Statement {
|
||||
if (inStatementContainingYield) {
|
||||
const label = findContinueTarget(node.label && node.label.text);
|
||||
const label = findContinueTarget(node.label && unescapeLeadingUnderscores(node.label.escapedText));
|
||||
if (label > 0) {
|
||||
return createInlineBreak(label, /*location*/ node);
|
||||
}
|
||||
@@ -1646,14 +1652,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
function transformAndEmitBreakStatement(node: BreakStatement): void {
|
||||
const label = findBreakTarget(node.label ? node.label.text : undefined);
|
||||
const label = findBreakTarget(node.label ? unescapeLeadingUnderscores(node.label.escapedText) : undefined);
|
||||
Debug.assert(label > 0, "Expected break statment to point to a valid Label.");
|
||||
emitBreak(label, /*location*/ node);
|
||||
}
|
||||
|
||||
function visitBreakStatement(node: BreakStatement): Statement {
|
||||
if (inStatementContainingYield) {
|
||||
const label = findBreakTarget(node.label && node.label.text);
|
||||
const label = findBreakTarget(node.label && unescapeLeadingUnderscores(node.label.escapedText));
|
||||
if (label > 0) {
|
||||
return createInlineBreak(label, /*location*/ node);
|
||||
}
|
||||
@@ -1832,7 +1838,7 @@ namespace ts {
|
||||
// /*body*/
|
||||
// .endlabeled
|
||||
// .mark endLabel
|
||||
beginLabeledBlock(node.label.text);
|
||||
beginLabeledBlock(unescapeLeadingUnderscores(node.label.escapedText));
|
||||
transformAndEmitEmbeddedStatement(node.statement);
|
||||
endLabeledBlock();
|
||||
}
|
||||
@@ -1843,7 +1849,7 @@ namespace ts {
|
||||
|
||||
function visitLabeledStatement(node: LabeledStatement) {
|
||||
if (inStatementContainingYield) {
|
||||
beginScriptLabeledBlock(node.label.text);
|
||||
beginScriptLabeledBlock(unescapeLeadingUnderscores(node.label.escapedText));
|
||||
}
|
||||
|
||||
node = visitEachChild(node, visitor, context);
|
||||
@@ -1944,7 +1950,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function substituteExpressionIdentifier(node: Identifier) {
|
||||
if (!isGeneratedIdentifier(node) && renamedCatchVariables && renamedCatchVariables.has(node.text)) {
|
||||
if (!isGeneratedIdentifier(node) && renamedCatchVariables && renamedCatchVariables.has(unescapeLeadingUnderscores(node.escapedText))) {
|
||||
const original = getOriginalNode(node);
|
||||
if (isIdentifier(original) && original.parent) {
|
||||
const declaration = resolver.getReferencedValueDeclaration(original);
|
||||
@@ -2117,7 +2123,7 @@ namespace ts {
|
||||
hoistVariableDeclaration(variable.name);
|
||||
}
|
||||
else {
|
||||
const text = (<Identifier>variable.name).text;
|
||||
const text = unescapeLeadingUnderscores((<Identifier>variable.name).escapedText);
|
||||
name = declareLocal(text);
|
||||
if (!renamedCatchVariables) {
|
||||
renamedCatchVariables = createMap<boolean>();
|
||||
@@ -2214,8 +2220,8 @@ namespace ts {
|
||||
beginBlock(<LoopBlock>{
|
||||
kind: CodeBlockKind.Loop,
|
||||
isScript: false,
|
||||
breakLabel: breakLabel,
|
||||
continueLabel: continueLabel
|
||||
breakLabel,
|
||||
continueLabel,
|
||||
});
|
||||
return breakLabel;
|
||||
}
|
||||
@@ -2256,7 +2262,7 @@ namespace ts {
|
||||
beginBlock(<SwitchBlock>{
|
||||
kind: CodeBlockKind.Switch,
|
||||
isScript: false,
|
||||
breakLabel: breakLabel
|
||||
breakLabel,
|
||||
});
|
||||
return breakLabel;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace ts {
|
||||
return visitJsxOpeningLikeElement(node, /*children*/ undefined, isChild, /*location*/ node);
|
||||
}
|
||||
|
||||
function visitJsxOpeningLikeElement(node: JsxOpeningLikeElement, children: JsxChild[], isChild: boolean, location: TextRange) {
|
||||
function visitJsxOpeningLikeElement(node: JsxOpeningLikeElement, children: ReadonlyArray<JsxChild>, isChild: boolean, location: TextRange) {
|
||||
const tagName = getTagName(node);
|
||||
let objectProperties: Expression;
|
||||
const attrs = node.attributes.properties;
|
||||
@@ -88,7 +88,7 @@ namespace ts {
|
||||
else {
|
||||
// Map spans of JsxAttribute nodes into object literals and spans
|
||||
// of JsxSpreadAttribute nodes into expressions.
|
||||
const segments = flatten(
|
||||
const segments = flatten<Expression | ObjectLiteralExpression>(
|
||||
spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) => isSpread
|
||||
? map(attrs, transformJsxSpreadAttributeToExpression)
|
||||
: createObjectLiteral(map(attrs, transformJsxAttributeToObjectLiteralElement))
|
||||
@@ -252,8 +252,8 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
const name = (<JsxOpeningLikeElement>node).tagName;
|
||||
if (isIdentifier(name) && isIntrinsicJsxName(name.text)) {
|
||||
return createLiteral(name.text);
|
||||
if (isIdentifier(name) && isIntrinsicJsxName(name.escapedText)) {
|
||||
return createLiteral(unescapeLeadingUnderscores(name.escapedText));
|
||||
}
|
||||
else {
|
||||
return createExpressionFromEntityName(name);
|
||||
@@ -268,11 +268,11 @@ namespace ts {
|
||||
*/
|
||||
function getAttributeName(node: JsxAttribute): StringLiteral | Identifier {
|
||||
const name = node.name;
|
||||
if (/^[A-Za-z_]\w*$/.test(name.text)) {
|
||||
if (/^[A-Za-z_]\w*$/.test(unescapeLeadingUnderscores(name.escapedText))) {
|
||||
return name;
|
||||
}
|
||||
else {
|
||||
return createLiteral(name.text);
|
||||
return createLiteral(unescapeLeadingUnderscores(name.escapedText));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace ts {
|
||||
addRange(statements, endLexicalEnvironment());
|
||||
|
||||
const updated = updateSourceFileNode(node, setTextRange(createNodeArray(statements), node.statements));
|
||||
if (currentModuleInfo.hasExportStarsToExportValues) {
|
||||
if (currentModuleInfo.hasExportStarsToExportValues && !compilerOptions.importHelpers) {
|
||||
// If we have any `export * from ...` declarations
|
||||
// we need to inform the emitter to add the __export helper.
|
||||
addEmitHelper(updated, exportStarHelper);
|
||||
@@ -408,7 +408,7 @@ namespace ts {
|
||||
addRange(statements, endLexicalEnvironment());
|
||||
|
||||
const body = createBlock(statements, /*multiLine*/ true);
|
||||
if (currentModuleInfo.hasExportStarsToExportValues) {
|
||||
if (currentModuleInfo.hasExportStarsToExportValues && !compilerOptions.importHelpers) {
|
||||
// If we have any `export * from ...` declarations
|
||||
// we need to inform the emitter to add the __export helper.
|
||||
addEmitHelper(body, exportStarHelper);
|
||||
@@ -514,14 +514,14 @@ namespace ts {
|
||||
|
||||
function visitImportCallExpression(node: ImportCall): Expression {
|
||||
switch (compilerOptions.module) {
|
||||
case ModuleKind.CommonJS:
|
||||
return transformImportCallExpressionCommonJS(node);
|
||||
case ModuleKind.AMD:
|
||||
return transformImportCallExpressionAMD(node);
|
||||
case ModuleKind.UMD:
|
||||
return transformImportCallExpressionUMD(node);
|
||||
case ModuleKind.CommonJS:
|
||||
default:
|
||||
return transformImportCallExpressionCommonJS(node);
|
||||
}
|
||||
Debug.fail("All supported module kind in this transformation step should have been handled");
|
||||
}
|
||||
|
||||
function transformImportCallExpressionUMD(node: ImportCall): Expression {
|
||||
@@ -833,15 +833,7 @@ namespace ts {
|
||||
// export * from "mod";
|
||||
return setTextRange(
|
||||
createStatement(
|
||||
createCall(
|
||||
createIdentifier("__export"),
|
||||
/*typeArguments*/ undefined,
|
||||
[
|
||||
moduleKind !== ModuleKind.AMD
|
||||
? createRequireCall(node)
|
||||
: generatedName
|
||||
]
|
||||
)
|
||||
createExportStarHelper(context, moduleKind !== ModuleKind.AMD ? createRequireCall(node) : generatedName)
|
||||
),
|
||||
node
|
||||
);
|
||||
@@ -932,7 +924,7 @@ namespace ts {
|
||||
getDeclarationName(node, /*allowComments*/ true, /*allowSourceMaps*/ true),
|
||||
/*typeParameters*/ undefined,
|
||||
visitNodes(node.heritageClauses, importCallExpressionVisitor),
|
||||
node.members
|
||||
visitNodes(node.members, importCallExpressionVisitor)
|
||||
),
|
||||
node
|
||||
),
|
||||
@@ -1233,7 +1225,7 @@ namespace ts {
|
||||
*/
|
||||
function appendExportsOfDeclaration(statements: Statement[] | undefined, decl: Declaration): Statement[] | undefined {
|
||||
const name = getDeclarationName(decl);
|
||||
const exportSpecifiers = currentModuleInfo.exportSpecifiers.get(name.text);
|
||||
const exportSpecifiers = currentModuleInfo.exportSpecifiers.get(unescapeLeadingUnderscores(name.escapedText));
|
||||
if (exportSpecifiers) {
|
||||
for (const exportSpecifier of exportSpecifiers) {
|
||||
statements = appendExportStatement(statements, exportSpecifier.name, name, /*location*/ exportSpecifier.name);
|
||||
@@ -1598,9 +1590,17 @@ namespace ts {
|
||||
text: `
|
||||
function __export(m) {
|
||||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
||||
}`
|
||||
}
|
||||
`
|
||||
};
|
||||
|
||||
function createExportStarHelper(context: TransformationContext, module: Expression) {
|
||||
const compilerOptions = context.getCompilerOptions();
|
||||
return compilerOptions.importHelpers
|
||||
? createCall(getHelperName("__exportStar"), /*typeArguments*/ undefined, [module, createIdentifier("exports")])
|
||||
: createCall(createIdentifier("__export"), /*typeArguments*/ undefined, [module]);
|
||||
}
|
||||
|
||||
// emit helper for dynamic import
|
||||
const dynamicImportUMDHelper: EmitHelper = {
|
||||
name: "typescript:dynamicimport-sync-require",
|
||||
|
||||
@@ -324,7 +324,7 @@ namespace ts {
|
||||
const exportedNames: ObjectLiteralElementLike[] = [];
|
||||
if (moduleInfo.exportedNames) {
|
||||
for (const exportedLocalName of moduleInfo.exportedNames) {
|
||||
if (exportedLocalName.text === "default") {
|
||||
if (exportedLocalName.escapedText === "default") {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -353,7 +353,7 @@ namespace ts {
|
||||
// write name of indirectly exported entry, i.e. 'export {x} from ...'
|
||||
exportedNames.push(
|
||||
createPropertyAssignment(
|
||||
createLiteral((element.name || element.propertyName).text),
|
||||
createLiteral(unescapeLeadingUnderscores((element.name || element.propertyName).escapedText)),
|
||||
createTrue()
|
||||
)
|
||||
);
|
||||
@@ -504,10 +504,10 @@ namespace ts {
|
||||
for (const e of (<ExportDeclaration>entry).exportClause.elements) {
|
||||
properties.push(
|
||||
createPropertyAssignment(
|
||||
createLiteral(e.name.text),
|
||||
createLiteral(unescapeLeadingUnderscores(e.name.escapedText)),
|
||||
createElementAccess(
|
||||
parameterName,
|
||||
createLiteral((e.propertyName || e.name).text)
|
||||
createLiteral(unescapeLeadingUnderscores((e.propertyName || e.name).escapedText))
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -1028,7 +1028,7 @@ namespace ts {
|
||||
let excludeName: string;
|
||||
if (exportSelf) {
|
||||
statements = appendExportStatement(statements, decl.name, getLocalName(decl));
|
||||
excludeName = decl.name.text;
|
||||
excludeName = unescapeLeadingUnderscores(decl.name.escapedText);
|
||||
}
|
||||
|
||||
statements = appendExportsOfDeclaration(statements, decl, excludeName);
|
||||
@@ -1055,7 +1055,7 @@ namespace ts {
|
||||
if (hasModifier(decl, ModifierFlags.Export)) {
|
||||
const exportName = hasModifier(decl, ModifierFlags.Default) ? createLiteral("default") : decl.name;
|
||||
statements = appendExportStatement(statements, exportName, getLocalName(decl));
|
||||
excludeName = exportName.text;
|
||||
excludeName = getTextOfIdentifierOrLiteral(exportName);
|
||||
}
|
||||
|
||||
if (decl.name) {
|
||||
@@ -1080,10 +1080,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
const name = getDeclarationName(decl);
|
||||
const exportSpecifiers = moduleInfo.exportSpecifiers.get(name.text);
|
||||
const exportSpecifiers = moduleInfo.exportSpecifiers.get(unescapeLeadingUnderscores(name.escapedText));
|
||||
if (exportSpecifiers) {
|
||||
for (const exportSpecifier of exportSpecifiers) {
|
||||
if (exportSpecifier.name.text !== excludeName) {
|
||||
if (exportSpecifier.name.escapedText !== excludeName) {
|
||||
statements = appendExportStatement(statements, exportSpecifier.name, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace ts {
|
||||
let currentNamespace: ModuleDeclaration;
|
||||
let currentNamespaceContainerName: Identifier;
|
||||
let currentScope: SourceFile | Block | ModuleBlock | CaseBlock;
|
||||
let currentScopeFirstDeclarationsOfName: Map<Node>;
|
||||
let currentScopeFirstDeclarationsOfName: UnderscoreEscapedMap<Node>;
|
||||
|
||||
/**
|
||||
* Keeps track of whether expression substitution has been enabled for specific edge cases.
|
||||
@@ -522,7 +522,7 @@ namespace ts {
|
||||
return parameter.decorators !== undefined && parameter.decorators.length > 0;
|
||||
}
|
||||
|
||||
function getClassFacts(node: ClassDeclaration, staticProperties: PropertyDeclaration[]) {
|
||||
function getClassFacts(node: ClassDeclaration, staticProperties: ReadonlyArray<PropertyDeclaration>) {
|
||||
let facts = ClassFacts.None;
|
||||
if (some(staticProperties)) facts |= ClassFacts.HasStaticInitializedProperties;
|
||||
if (getClassExtendsHeritageClauseElement(node)) facts |= ClassFacts.HasExtendsClause;
|
||||
@@ -1051,7 +1051,7 @@ namespace ts {
|
||||
*
|
||||
* @param node The constructor node.
|
||||
*/
|
||||
function getParametersWithPropertyAssignments(node: ConstructorDeclaration): ParameterDeclaration[] {
|
||||
function getParametersWithPropertyAssignments(node: ConstructorDeclaration): ReadonlyArray<ParameterDeclaration> {
|
||||
return filter(node.parameters, isParameterWithPropertyAssignment);
|
||||
}
|
||||
|
||||
@@ -1104,7 +1104,7 @@ namespace ts {
|
||||
* @param node The class node.
|
||||
* @param isStatic A value indicating whether to get properties from the static or instance side of the class.
|
||||
*/
|
||||
function getInitializedProperties(node: ClassExpression | ClassDeclaration, isStatic: boolean): PropertyDeclaration[] {
|
||||
function getInitializedProperties(node: ClassExpression | ClassDeclaration, isStatic: boolean): ReadonlyArray<PropertyDeclaration> {
|
||||
return filter(node.members, isStatic ? isStaticInitializedProperty : isInstanceInitializedProperty);
|
||||
}
|
||||
|
||||
@@ -1144,7 +1144,7 @@ namespace ts {
|
||||
* @param properties An array of property declarations to transform.
|
||||
* @param receiver The receiver on which each property should be assigned.
|
||||
*/
|
||||
function addInitializedPropertyStatements(statements: Statement[], properties: PropertyDeclaration[], receiver: LeftHandSideExpression) {
|
||||
function addInitializedPropertyStatements(statements: Statement[], properties: ReadonlyArray<PropertyDeclaration>, receiver: LeftHandSideExpression) {
|
||||
for (const property of properties) {
|
||||
const statement = createStatement(transformInitializedProperty(property, receiver));
|
||||
setSourceMapRange(statement, moveRangePastModifiers(property));
|
||||
@@ -1159,7 +1159,7 @@ namespace ts {
|
||||
* @param properties An array of property declarations to transform.
|
||||
* @param receiver The receiver on which each property should be assigned.
|
||||
*/
|
||||
function generateInitializedPropertyExpressions(properties: PropertyDeclaration[], receiver: LeftHandSideExpression) {
|
||||
function generateInitializedPropertyExpressions(properties: ReadonlyArray<PropertyDeclaration>, receiver: LeftHandSideExpression) {
|
||||
const expressions: Expression[] = [];
|
||||
for (const property of properties) {
|
||||
const expression = transformInitializedProperty(property, receiver);
|
||||
@@ -1194,7 +1194,7 @@ namespace ts {
|
||||
* @param isStatic A value indicating whether to retrieve static or instance members of
|
||||
* the class.
|
||||
*/
|
||||
function getDecoratedClassElements(node: ClassExpression | ClassDeclaration, isStatic: boolean): ClassElement[] {
|
||||
function getDecoratedClassElements(node: ClassExpression | ClassDeclaration, isStatic: boolean): ReadonlyArray<ClassElement> {
|
||||
return filter(node.members, isStatic ? isStaticDecoratedClassElement : isInstanceDecoratedClassElement);
|
||||
}
|
||||
|
||||
@@ -1233,8 +1233,8 @@ namespace ts {
|
||||
* A structure describing the decorators for a class element.
|
||||
*/
|
||||
interface AllDecorators {
|
||||
decorators: Decorator[];
|
||||
parameters?: Decorator[][];
|
||||
decorators: ReadonlyArray<Decorator>;
|
||||
parameters?: ReadonlyArray<ReadonlyArray<Decorator>>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1244,7 +1244,7 @@ namespace ts {
|
||||
* @param node The function-like node.
|
||||
*/
|
||||
function getDecoratorsOfParameters(node: FunctionLikeDeclaration) {
|
||||
let decorators: Decorator[][];
|
||||
let decorators: ReadonlyArray<Decorator>[];
|
||||
if (node) {
|
||||
const parameters = node.parameters;
|
||||
for (let i = 0; i < parameters.length; i++) {
|
||||
@@ -1682,7 +1682,7 @@ namespace ts {
|
||||
const valueDeclaration =
|
||||
isClassLike(node)
|
||||
? getFirstConstructorWithBody(node)
|
||||
: isFunctionLike(node) && nodeIsPresent(node.body)
|
||||
: isFunctionLike(node) && nodeIsPresent((node as FunctionLikeDeclaration).body)
|
||||
? node
|
||||
: undefined;
|
||||
|
||||
@@ -1692,7 +1692,7 @@ namespace ts {
|
||||
const numParameters = parameters.length;
|
||||
for (let i = 0; i < numParameters; i++) {
|
||||
const parameter = parameters[i];
|
||||
if (i === 0 && isIdentifier(parameter.name) && parameter.name.text === "this") {
|
||||
if (i === 0 && isIdentifier(parameter.name) && parameter.name.escapedText === "this") {
|
||||
continue;
|
||||
}
|
||||
if (parameter.dotDotDotToken) {
|
||||
@@ -1707,7 +1707,7 @@ namespace ts {
|
||||
return createArrayLiteral(expressions);
|
||||
}
|
||||
|
||||
function getParametersOfDecoratedDeclaration(node: FunctionLikeDeclaration, container: ClassLikeDeclaration) {
|
||||
function getParametersOfDecoratedDeclaration(node: FunctionLike, container: ClassLikeDeclaration) {
|
||||
if (container && node.kind === SyntaxKind.GetAccessor) {
|
||||
const { setAccessor } = getAllAccessorDeclarations(container.members, <AccessorDeclaration>node);
|
||||
if (setAccessor) {
|
||||
@@ -1841,7 +1841,7 @@ namespace ts {
|
||||
for (const typeNode of node.types) {
|
||||
const serializedIndividual = serializeTypeNode(typeNode);
|
||||
|
||||
if (isIdentifier(serializedIndividual) && serializedIndividual.text === "Object") {
|
||||
if (isIdentifier(serializedIndividual) && serializedIndividual.escapedText === "Object") {
|
||||
// One of the individual is global object, return immediately
|
||||
return serializedIndividual;
|
||||
}
|
||||
@@ -1851,7 +1851,7 @@ namespace ts {
|
||||
// Different types
|
||||
if (!isIdentifier(serializedUnion) ||
|
||||
!isIdentifier(serializedIndividual) ||
|
||||
serializedUnion.text !== serializedIndividual.text) {
|
||||
serializedUnion.escapedText !== serializedIndividual.escapedText) {
|
||||
return createIdentifier("Object");
|
||||
}
|
||||
}
|
||||
@@ -2007,7 +2007,7 @@ namespace ts {
|
||||
: (<ComputedPropertyName>name).expression;
|
||||
}
|
||||
else if (isIdentifier(name)) {
|
||||
return createLiteral(unescapeIdentifier(name.text));
|
||||
return createLiteral(unescapeLeadingUnderscores(name.escapedText));
|
||||
}
|
||||
else {
|
||||
return getSynthesizedClone(name);
|
||||
@@ -2644,10 +2644,10 @@ namespace ts {
|
||||
* on symbol names.
|
||||
*/
|
||||
function recordEmittedDeclarationInScope(node: Node) {
|
||||
const name = node.symbol && node.symbol.name;
|
||||
const name = node.symbol && node.symbol.escapedName;
|
||||
if (name) {
|
||||
if (!currentScopeFirstDeclarationsOfName) {
|
||||
currentScopeFirstDeclarationsOfName = createMap<Node>();
|
||||
currentScopeFirstDeclarationsOfName = createUnderscoreEscapedMap<Node>();
|
||||
}
|
||||
|
||||
if (!currentScopeFirstDeclarationsOfName.has(name)) {
|
||||
@@ -2662,7 +2662,7 @@ namespace ts {
|
||||
*/
|
||||
function isFirstEmittedDeclarationInScope(node: Node) {
|
||||
if (currentScopeFirstDeclarationsOfName) {
|
||||
const name = node.symbol && node.symbol.name;
|
||||
const name = node.symbol && node.symbol.escapedName;
|
||||
if (name) {
|
||||
return currentScopeFirstDeclarationsOfName.get(name) === node;
|
||||
}
|
||||
@@ -3158,7 +3158,7 @@ namespace ts {
|
||||
getExternalModuleOrNamespaceExportName(currentNamespaceContainerName, node, /*allowComments*/ false, /*allowSourceMaps*/ true),
|
||||
getLocalName(node)
|
||||
);
|
||||
setSourceMapRange(expression, createRange(node.name.pos, node.end));
|
||||
setSourceMapRange(expression, createRange(node.name ? node.name.pos : node.pos, node.end));
|
||||
|
||||
const statement = createStatement(expression);
|
||||
setSourceMapRange(statement, createRange(-1, node.end));
|
||||
@@ -3210,7 +3210,7 @@ namespace ts {
|
||||
function getClassAliasIfNeeded(node: ClassDeclaration) {
|
||||
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) {
|
||||
enableSubstitutionForClassAliases();
|
||||
const classAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? unescapeIdentifier(node.name.text) : "default");
|
||||
const classAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? unescapeLeadingUnderscores(node.name.escapedText) : "default");
|
||||
classAliases[getOriginalNodeId(node)] = classAlias;
|
||||
hoistVariableDeclaration(classAlias);
|
||||
return classAlias;
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export function getOriginalNodeId(node: Node) {
|
||||
node = getOriginalNode(node);
|
||||
return node ? getNodeId(node) : 0;
|
||||
}
|
||||
|
||||
export interface ExternalModuleInfo {
|
||||
externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules
|
||||
externalHelpersImportDeclaration: ImportDeclaration | undefined; // import of external helpers
|
||||
exportSpecifiers: Map<ExportSpecifier[]>; // export specifiers by name
|
||||
exportedBindings: Identifier[][]; // exported names of local declarations
|
||||
exportedNames: Identifier[]; // all exported names local to module
|
||||
exportEquals: ExportAssignment | undefined; // an export= declaration if one was present
|
||||
hasExportStarsToExportValues: boolean; // whether this module contains export*
|
||||
}
|
||||
|
||||
export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo {
|
||||
const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = [];
|
||||
const exportSpecifiers = createMultiMap<ExportSpecifier>();
|
||||
const exportedBindings: Identifier[][] = [];
|
||||
const uniqueExports = createMap<boolean>();
|
||||
let exportedNames: Identifier[];
|
||||
let hasExportDefault = false;
|
||||
let exportEquals: ExportAssignment = undefined;
|
||||
let hasExportStarsToExportValues = false;
|
||||
|
||||
for (const node of sourceFile.statements) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
// import "mod"
|
||||
// import x from "mod"
|
||||
// import * as x from "mod"
|
||||
// import { x, y } from "mod"
|
||||
externalImports.push(<ImportDeclaration>node);
|
||||
break;
|
||||
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
if ((<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference) {
|
||||
// import x = require("mod")
|
||||
externalImports.push(<ImportEqualsDeclaration>node);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
if ((<ExportDeclaration>node).moduleSpecifier) {
|
||||
if (!(<ExportDeclaration>node).exportClause) {
|
||||
// export * from "mod"
|
||||
externalImports.push(<ExportDeclaration>node);
|
||||
hasExportStarsToExportValues = true;
|
||||
}
|
||||
else {
|
||||
// export { x, y } from "mod"
|
||||
externalImports.push(<ExportDeclaration>node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// export { x, y }
|
||||
for (const specifier of (<ExportDeclaration>node).exportClause.elements) {
|
||||
if (!uniqueExports.get(unescapeLeadingUnderscores(specifier.name.escapedText))) {
|
||||
const name = specifier.propertyName || specifier.name;
|
||||
exportSpecifiers.add(unescapeLeadingUnderscores(name.escapedText), specifier);
|
||||
|
||||
const decl = resolver.getReferencedImportDeclaration(name)
|
||||
|| resolver.getReferencedValueDeclaration(name);
|
||||
|
||||
if (decl) {
|
||||
multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(decl), specifier.name);
|
||||
}
|
||||
|
||||
uniqueExports.set(unescapeLeadingUnderscores(specifier.name.escapedText), true);
|
||||
exportedNames = append(exportedNames, specifier.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.ExportAssignment:
|
||||
if ((<ExportAssignment>node).isExportEquals && !exportEquals) {
|
||||
// export = x
|
||||
exportEquals = <ExportAssignment>node;
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.VariableStatement:
|
||||
if (hasModifier(node, ModifierFlags.Export)) {
|
||||
for (const decl of (<VariableStatement>node).declarationList.declarations) {
|
||||
exportedNames = collectExportedVariableInfo(decl, uniqueExports, exportedNames);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
if (hasModifier(node, ModifierFlags.Export)) {
|
||||
if (hasModifier(node, ModifierFlags.Default)) {
|
||||
// export default function() { }
|
||||
if (!hasExportDefault) {
|
||||
multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(<FunctionDeclaration>node));
|
||||
hasExportDefault = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// export function x() { }
|
||||
const name = (<FunctionDeclaration>node).name;
|
||||
if (!uniqueExports.get(unescapeLeadingUnderscores(name.escapedText))) {
|
||||
multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name);
|
||||
uniqueExports.set(unescapeLeadingUnderscores(name.escapedText), true);
|
||||
exportedNames = append(exportedNames, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
if (hasModifier(node, ModifierFlags.Export)) {
|
||||
if (hasModifier(node, ModifierFlags.Default)) {
|
||||
// export default class { }
|
||||
if (!hasExportDefault) {
|
||||
multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(<ClassDeclaration>node));
|
||||
hasExportDefault = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// export class x { }
|
||||
const name = (<ClassDeclaration>node).name;
|
||||
if (!uniqueExports.get(unescapeLeadingUnderscores(name.escapedText))) {
|
||||
multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name);
|
||||
uniqueExports.set(unescapeLeadingUnderscores(name.escapedText), true);
|
||||
exportedNames = append(exportedNames, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(sourceFile, compilerOptions, hasExportStarsToExportValues);
|
||||
const externalHelpersImportDeclaration = externalHelpersModuleName && createImportDeclaration(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
createImportClause(/*name*/ undefined, createNamespaceImport(externalHelpersModuleName)),
|
||||
createLiteral(externalHelpersModuleNameText));
|
||||
|
||||
if (externalHelpersImportDeclaration) {
|
||||
externalImports.unshift(externalHelpersImportDeclaration);
|
||||
}
|
||||
|
||||
return { externalImports, exportSpecifiers, exportEquals, hasExportStarsToExportValues, exportedBindings, exportedNames, externalHelpersImportDeclaration };
|
||||
}
|
||||
|
||||
function collectExportedVariableInfo(decl: VariableDeclaration | BindingElement, uniqueExports: Map<boolean>, exportedNames: Identifier[]) {
|
||||
if (isBindingPattern(decl.name)) {
|
||||
for (const element of decl.name.elements) {
|
||||
if (!isOmittedExpression(element)) {
|
||||
exportedNames = collectExportedVariableInfo(element, uniqueExports, exportedNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!isGeneratedIdentifier(decl.name)) {
|
||||
if (!uniqueExports.get(unescapeLeadingUnderscores(decl.name.escapedText))) {
|
||||
uniqueExports.set(unescapeLeadingUnderscores(decl.name.escapedText), true);
|
||||
exportedNames = append(exportedNames, decl.name);
|
||||
}
|
||||
}
|
||||
return exportedNames;
|
||||
}
|
||||
|
||||
/** Use a sparse array as a multi-map. */
|
||||
function multiMapSparseArrayAdd<V>(map: V[][], key: number, value: V): V[] {
|
||||
let values = map[key];
|
||||
if (values) {
|
||||
values.push(value);
|
||||
}
|
||||
else {
|
||||
map[key] = values = [value];
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
+26
-17
@@ -61,7 +61,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function reportDiagnosticWithColorAndContext(diagnostic: Diagnostic, host: FormatDiagnosticsHost): void {
|
||||
sys.write(ts.formatDiagnosticsWithColorAndContext([diagnostic], host) + sys.newLine + sys.newLine);
|
||||
sys.write(ts.formatDiagnosticsWithColorAndContext([diagnostic], host) + sys.newLine);
|
||||
}
|
||||
|
||||
function reportWatchDiagnostic(diagnostic: Diagnostic) {
|
||||
@@ -223,20 +223,13 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = parseConfigFileTextToJson(configFileName, cachedConfigFileText);
|
||||
const configObject = result.config;
|
||||
if (!configObject) {
|
||||
reportDiagnostics([result.error], /* compilerHost */ undefined);
|
||||
sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
return;
|
||||
}
|
||||
const result = parseJsonText(configFileName, cachedConfigFileText);
|
||||
reportDiagnostics(result.parseDiagnostics, /* compilerHost */ undefined);
|
||||
|
||||
const cwd = sys.getCurrentDirectory();
|
||||
const configParseResult = parseJsonConfigFileContent(configObject, sys, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), commandLine.options, getNormalizedAbsolutePath(configFileName, cwd));
|
||||
if (configParseResult.errors.length > 0) {
|
||||
reportDiagnostics(configParseResult.errors, /* compilerHost */ undefined);
|
||||
sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
return;
|
||||
}
|
||||
const configParseResult = parseJsonSourceFileConfigFileContent(result, sys, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), commandLine.options, getNormalizedAbsolutePath(configFileName, cwd));
|
||||
reportDiagnostics(configParseResult.errors, /* compilerHost */ undefined);
|
||||
|
||||
if (isWatchSet(configParseResult.options)) {
|
||||
if (!sys.watchFile) {
|
||||
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--watch"), /* host */ undefined);
|
||||
@@ -292,6 +285,16 @@ namespace ts {
|
||||
|
||||
setCachedProgram(compileResult.program);
|
||||
reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes));
|
||||
|
||||
const missingPaths = compileResult.program.getMissingFilePaths();
|
||||
missingPaths.forEach(path => {
|
||||
const fileWatcher = sys.watchFile(path, (_fileName, eventKind) => {
|
||||
if (eventKind === FileWatcherEventKind.Created) {
|
||||
fileWatcher.close();
|
||||
startTimerForRecompilation();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function cachedFileExists(fileName: string): boolean {
|
||||
@@ -315,7 +318,7 @@ namespace ts {
|
||||
const sourceFile = hostGetSourceFile(fileName, languageVersion, onError);
|
||||
if (sourceFile && isWatchSet(compilerOptions) && sys.watchFile) {
|
||||
// Attach a file watcher
|
||||
sourceFile.fileWatcher = sys.watchFile(sourceFile.fileName, (_fileName: string, removed?: boolean) => sourceFileChanged(sourceFile, removed));
|
||||
sourceFile.fileWatcher = sys.watchFile(sourceFile.fileName, (_fileName, eventKind) => sourceFileChanged(sourceFile, eventKind));
|
||||
}
|
||||
return sourceFile;
|
||||
}
|
||||
@@ -337,10 +340,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
// If a source file changes, mark it as unwatched and start the recompilation timer
|
||||
function sourceFileChanged(sourceFile: SourceFile, removed?: boolean) {
|
||||
function sourceFileChanged(sourceFile: SourceFile, eventKind: FileWatcherEventKind) {
|
||||
sourceFile.fileWatcher.close();
|
||||
sourceFile.fileWatcher = undefined;
|
||||
if (removed) {
|
||||
if (eventKind === FileWatcherEventKind.Deleted) {
|
||||
unorderedRemoveItem(rootFileNames, sourceFile.fileName);
|
||||
}
|
||||
startTimerForRecompilation();
|
||||
@@ -662,6 +665,12 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
ts.setStackTraceLimit();
|
||||
|
||||
if (ts.Debug.isDebugging) {
|
||||
ts.Debug.enableDebugInfo();
|
||||
}
|
||||
|
||||
if (ts.sys.tryEnableSourceMapsForHost && /^development$/i.test(ts.sys.getEnvironmentVariable("NODE_ENV"))) {
|
||||
ts.sys.tryEnableSourceMapsForHost();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"checker.ts",
|
||||
"factory.ts",
|
||||
"visitor.ts",
|
||||
"transformers/utilities.ts",
|
||||
"transformers/ts.ts",
|
||||
"transformers/jsx.ts",
|
||||
"transformers/esnext.ts",
|
||||
@@ -25,7 +26,6 @@
|
||||
"transformers/es2015.ts",
|
||||
"transformers/es5.ts",
|
||||
"transformers/generators.ts",
|
||||
"transformers/es5.ts",
|
||||
"transformers/destructuring.ts",
|
||||
"transformers/module/module.ts",
|
||||
"transformers/module/system.ts",
|
||||
|
||||
+243
-195
@@ -8,13 +8,10 @@ namespace ts {
|
||||
[index: string]: T;
|
||||
}
|
||||
|
||||
/** ES6 Map interface. */
|
||||
export interface Map<T> {
|
||||
/** ES6 Map interface, only read methods included. */
|
||||
export interface ReadonlyMap<T> {
|
||||
get(key: string): T | undefined;
|
||||
has(key: string): boolean;
|
||||
set(key: string, value: T): this;
|
||||
delete(key: string): boolean;
|
||||
clear(): void;
|
||||
forEach(action: (value: T, key: string) => void): void;
|
||||
readonly size: number;
|
||||
keys(): Iterator<string>;
|
||||
@@ -22,26 +19,27 @@ namespace ts {
|
||||
entries(): Iterator<[string, T]>;
|
||||
}
|
||||
|
||||
/** ES6 Map interface. */
|
||||
export interface Map<T> extends ReadonlyMap<T> {
|
||||
set(key: string, value: T): this;
|
||||
delete(key: string): boolean;
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
/** ES6 Iterator type. */
|
||||
export interface Iterator<T> {
|
||||
next(): { value: T, done: false } | { value: never, done: true };
|
||||
}
|
||||
|
||||
/** Array that is only intended to be pushed to, never read. */
|
||||
export interface Push<T> {
|
||||
push(...values: T[]): void;
|
||||
}
|
||||
|
||||
// branded string type used to store absolute, normalized and canonicalized paths
|
||||
// arbitrary file name can be converted to Path via toPath function
|
||||
export type Path = string & { __pathBrand: any };
|
||||
|
||||
export interface FileMap<T> {
|
||||
get(fileName: Path): T;
|
||||
set(fileName: Path, value: T): void;
|
||||
contains(fileName: Path): boolean;
|
||||
remove(fileName: Path): void;
|
||||
|
||||
forEachValue(f: (key: Path, v: T) => void): void;
|
||||
getKeys(): Path[];
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
export interface TextRange {
|
||||
pos: number;
|
||||
end: number;
|
||||
@@ -358,22 +356,15 @@ namespace ts {
|
||||
JSDocAllType,
|
||||
// The ? type
|
||||
JSDocUnknownType,
|
||||
JSDocArrayType,
|
||||
JSDocUnionType,
|
||||
JSDocTupleType,
|
||||
JSDocNullableType,
|
||||
JSDocNonNullableType,
|
||||
JSDocRecordType,
|
||||
JSDocRecordMember,
|
||||
JSDocTypeReference,
|
||||
JSDocOptionalType,
|
||||
JSDocFunctionType,
|
||||
JSDocVariadicType,
|
||||
JSDocConstructorType,
|
||||
JSDocThisType,
|
||||
JSDocComment,
|
||||
JSDocTag,
|
||||
JSDocAugmentsTag,
|
||||
JSDocClassTag,
|
||||
JSDocParameterTag,
|
||||
JSDocReturnTag,
|
||||
JSDocTypeTag,
|
||||
@@ -381,7 +372,6 @@ namespace ts {
|
||||
JSDocTypedefTag,
|
||||
JSDocPropertyTag,
|
||||
JSDocTypeLiteral,
|
||||
JSDocLiteralType,
|
||||
|
||||
// Synthesized list
|
||||
SyntaxList,
|
||||
@@ -423,9 +413,9 @@ namespace ts {
|
||||
LastBinaryOperator = CaretEqualsToken,
|
||||
FirstNode = QualifiedName,
|
||||
FirstJSDocNode = JSDocTypeExpression,
|
||||
LastJSDocNode = JSDocLiteralType,
|
||||
FirstJSDocTagNode = JSDocComment,
|
||||
LastJSDocTagNode = JSDocLiteralType
|
||||
LastJSDocNode = JSDocTypeLiteral,
|
||||
FirstJSDocTagNode = JSDocTag,
|
||||
LastJSDocTagNode = JSDocTypeLiteral
|
||||
}
|
||||
|
||||
export const enum NodeFlags {
|
||||
@@ -450,13 +440,17 @@ namespace ts {
|
||||
ThisNodeOrAnySubNodesHasError = 1 << 17, // If this node or any of its children had an error
|
||||
HasAggregatedChildData = 1 << 18, // If we've computed data from children and cached it in this node
|
||||
|
||||
// This flag will be set to true when the parse encounter dynamic import so that post-parsing process of module resolution
|
||||
// will not walk the tree if the flag is not set. However, this flag is just a approximation because once it is set, the flag never get reset.
|
||||
// (hence it is named "possiblyContainDynamicImport").
|
||||
// During editing, if dynamic import is remove, incremental parsing will *NOT* update this flag. This will then causes walking of the tree during module resolution.
|
||||
// However, the removal operation should not occur often and in the case of the removal, it is likely that users will add back the import anyway.
|
||||
// The advantage of this approach is its simplicity. For the case of batch compilation, we garuntee that users won't have to pay the price of walking the tree if dynamic import isn't used.
|
||||
PossiblyContainDynamicImport = 1 << 19,
|
||||
// This flag will be set when the parser encounters a dynamic import expression so that module resolution
|
||||
// will not have to walk the tree if the flag is not set. However, this flag is just a approximation because
|
||||
// once it is set, the flag never gets cleared (hence why it's named "PossiblyContainsDynamicImport").
|
||||
// During editing, if dynamic import is removed, incremental parsing will *NOT* update this flag. This means that the tree will always be traversed
|
||||
// during module resolution. However, the removal operation should not occur often and in the case of the
|
||||
// removal, it is likely that users will add the import anyway.
|
||||
// The advantage of this approach is its simplicity. For the case of batch compilation,
|
||||
// we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used.
|
||||
/* @internal */
|
||||
PossiblyContainsDynamicImport = 1 << 19,
|
||||
JSDoc = 1 << 20, // If node was parsed inside jsdoc
|
||||
|
||||
BlockScoped = Let | Const,
|
||||
|
||||
@@ -516,25 +510,28 @@ namespace ts {
|
||||
flags: NodeFlags;
|
||||
/* @internal */ modifierFlagsCache?: ModifierFlags;
|
||||
/* @internal */ transformFlags?: TransformFlags;
|
||||
decorators?: NodeArray<Decorator>; // Array of decorators (in document order)
|
||||
modifiers?: ModifiersArray; // Array of modifiers
|
||||
/* @internal */ id?: number; // Unique id (used to look up NodeLinks)
|
||||
parent?: Node; // Parent node (initialized by binding)
|
||||
/* @internal */ original?: Node; // The original node if this is an updated node.
|
||||
/* @internal */ startsOnNewLine?: boolean; // Whether a synthesized node should start on a new line (used by transforms).
|
||||
/* @internal */ jsDoc?: JSDoc[]; // JSDoc that directly precedes this node
|
||||
/* @internal */ jsDocCache?: (JSDoc | JSDocTag)[]; // All JSDoc that applies to the node, including parent docs and @param tags
|
||||
/* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding)
|
||||
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
|
||||
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
|
||||
/* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes)
|
||||
/* @internal */ flowNode?: FlowNode; // Associated FlowNode (initialized by binding)
|
||||
/* @internal */ emitNode?: EmitNode; // Associated EmitNode (initialized by transforms)
|
||||
/* @internal */ contextualType?: Type; // Used to temporarily assign a contextual type during overload resolution
|
||||
/* @internal */ contextualMapper?: TypeMapper; // Mapper for contextual type
|
||||
decorators?: NodeArray<Decorator>; // Array of decorators (in document order)
|
||||
modifiers?: ModifiersArray; // Array of modifiers
|
||||
/* @internal */ id?: number; // Unique id (used to look up NodeLinks)
|
||||
parent?: Node; // Parent node (initialized by binding)
|
||||
/* @internal */ original?: Node; // The original node if this is an updated node.
|
||||
/* @internal */ startsOnNewLine?: boolean; // Whether a synthesized node should start on a new line (used by transforms).
|
||||
/* @internal */ jsDoc?: JSDoc[]; // JSDoc that directly precedes this node
|
||||
/* @internal */ jsDocCache?: ReadonlyArray<JSDocTag>; // Cache for getJSDocTags
|
||||
/* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding)
|
||||
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
|
||||
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
|
||||
/* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes)
|
||||
/* @internal */ flowNode?: FlowNode; // Associated FlowNode (initialized by binding)
|
||||
/* @internal */ emitNode?: EmitNode; // Associated EmitNode (initialized by transforms)
|
||||
/* @internal */ contextualType?: Type; // Used to temporarily assign a contextual type during overload resolution
|
||||
/* @internal */ contextualMapper?: TypeMapper; // Mapper for contextual type
|
||||
}
|
||||
|
||||
export interface NodeArray<T extends Node> extends Array<T>, TextRange {
|
||||
/* @internal */
|
||||
export type MutableNodeArray<T extends Node> = NodeArray<T> & T[];
|
||||
|
||||
export interface NodeArray<T extends Node> extends ReadonlyArray<T>, TextRange {
|
||||
hasTrailingComma?: boolean;
|
||||
/* @internal */ transformFlags?: TransformFlags;
|
||||
}
|
||||
@@ -582,15 +579,16 @@ namespace ts {
|
||||
export interface Identifier extends PrimaryExpression {
|
||||
kind: SyntaxKind.Identifier;
|
||||
/**
|
||||
* Text of identifier (with escapes converted to characters).
|
||||
* If the identifier begins with two underscores, this will begin with three.
|
||||
* Prefer to use `id.unescapedText`. (Note: This is available only in services, not internally to the TypeScript compiler.)
|
||||
* Text of identifier, but if the identifier begins with two underscores, this will begin with three.
|
||||
*/
|
||||
text: string;
|
||||
escapedText: __String;
|
||||
originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later
|
||||
/*@internal*/ autoGenerateKind?: GeneratedIdentifierKind; // Specifies whether to auto-generate the text for an identifier.
|
||||
/*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name.
|
||||
isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace
|
||||
/*@internal*/ typeArguments?: NodeArray<TypeNode>; // Only defined on synthesized nodes. Though not syntactically valid, used in emitting diagnostics.
|
||||
/*@internal*/ jsdocDotPos?: number; // Identifier occurs in JSDoc-style generic: Id.<T>
|
||||
}
|
||||
|
||||
// Transient identifier node (marked by id === -1)
|
||||
@@ -610,6 +608,7 @@ namespace ts {
|
||||
kind: SyntaxKind.QualifiedName;
|
||||
left: EntityName;
|
||||
right: Identifier;
|
||||
/*@internal*/ jsdocDotPos?: number; // QualifiedName occurs in JSDoc-style generic: Id1.Id2.<T>
|
||||
}
|
||||
|
||||
export type EntityName = Identifier | QualifiedName;
|
||||
@@ -686,7 +685,7 @@ namespace ts {
|
||||
kind: SyntaxKind.Parameter;
|
||||
parent?: SignatureDeclaration;
|
||||
dotDotDotToken?: DotDotDotToken; // Present on rest parameter
|
||||
name: BindingName; // Declared parameter name
|
||||
name: BindingName; // Declared parameter name.
|
||||
questionToken?: QuestionToken; // Present on optional parameter
|
||||
type?: TypeNode; // Optional type annotation
|
||||
initializer?: Expression; // Optional initializer
|
||||
@@ -702,7 +701,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface PropertySignature extends TypeElement {
|
||||
kind: SyntaxKind.PropertySignature | SyntaxKind.JSDocRecordMember;
|
||||
kind: SyntaxKind.PropertySignature;
|
||||
name: PropertyName; // Declared property name
|
||||
questionToken?: QuestionToken; // Present on optional property
|
||||
type?: TypeNode; // Optional type annotation
|
||||
@@ -761,10 +760,11 @@ namespace ts {
|
||||
// SyntaxKind.ShorthandPropertyAssignment
|
||||
// SyntaxKind.EnumMember
|
||||
// SyntaxKind.JSDocPropertyTag
|
||||
// SyntaxKind.JSDocParameterTag
|
||||
export interface VariableLikeDeclaration extends NamedDeclaration {
|
||||
propertyName?: PropertyName;
|
||||
dotDotDotToken?: DotDotDotToken;
|
||||
name: DeclarationName;
|
||||
name?: DeclarationName; // May be missing for ParameterDeclaration, see comment there
|
||||
questionToken?: QuestionToken;
|
||||
type?: TypeNode;
|
||||
initializer?: Expression;
|
||||
@@ -792,13 +792,13 @@ namespace ts {
|
||||
|
||||
/**
|
||||
* Several node kinds share function-like features such as a signature,
|
||||
* a name, and a body. These nodes should extend FunctionLikeDeclaration.
|
||||
* a name, and a body. These nodes should extend FunctionLikeDeclarationBase.
|
||||
* Examples:
|
||||
* - FunctionDeclaration
|
||||
* - MethodDeclaration
|
||||
* - AccessorDeclaration
|
||||
*/
|
||||
export interface FunctionLikeDeclaration extends SignatureDeclaration {
|
||||
export interface FunctionLikeDeclarationBase extends SignatureDeclaration {
|
||||
_functionLikeDeclarationBrand: any;
|
||||
|
||||
asteriskToken?: AsteriskToken;
|
||||
@@ -806,7 +806,24 @@ namespace ts {
|
||||
body?: Block | Expression;
|
||||
}
|
||||
|
||||
export interface FunctionDeclaration extends FunctionLikeDeclaration, DeclarationStatement {
|
||||
export type FunctionLikeDeclaration =
|
||||
| FunctionDeclaration
|
||||
| MethodDeclaration
|
||||
| ConstructorDeclaration
|
||||
| GetAccessorDeclaration
|
||||
| SetAccessorDeclaration
|
||||
| FunctionExpression
|
||||
| ArrowFunction;
|
||||
export type FunctionLike =
|
||||
| FunctionLikeDeclaration
|
||||
| FunctionTypeNode
|
||||
| ConstructorTypeNode
|
||||
| IndexSignatureDeclaration
|
||||
| MethodSignature
|
||||
| ConstructSignatureDeclaration
|
||||
| CallSignatureDeclaration;
|
||||
|
||||
export interface FunctionDeclaration extends FunctionLikeDeclarationBase, DeclarationStatement {
|
||||
kind: SyntaxKind.FunctionDeclaration;
|
||||
name?: Identifier;
|
||||
body?: FunctionBody;
|
||||
@@ -826,13 +843,13 @@ namespace ts {
|
||||
// Because of this, it may be necessary to determine what sort of MethodDeclaration you have
|
||||
// at later stages of the compiler pipeline. In that case, you can either check the parent kind
|
||||
// of the method, or use helpers like isObjectLiteralMethodDeclaration
|
||||
export interface MethodDeclaration extends FunctionLikeDeclaration, ClassElement, ObjectLiteralElement {
|
||||
export interface MethodDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement {
|
||||
kind: SyntaxKind.MethodDeclaration;
|
||||
name: PropertyName;
|
||||
body?: FunctionBody;
|
||||
}
|
||||
|
||||
export interface ConstructorDeclaration extends FunctionLikeDeclaration, ClassElement {
|
||||
export interface ConstructorDeclaration extends FunctionLikeDeclarationBase, ClassElement {
|
||||
kind: SyntaxKind.Constructor;
|
||||
parent?: ClassDeclaration | ClassExpression;
|
||||
body?: FunctionBody;
|
||||
@@ -846,7 +863,7 @@ namespace ts {
|
||||
|
||||
// See the comment on MethodDeclaration for the intuition behind GetAccessorDeclaration being a
|
||||
// ClassElement and an ObjectLiteralElement.
|
||||
export interface GetAccessorDeclaration extends FunctionLikeDeclaration, ClassElement, ObjectLiteralElement {
|
||||
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement {
|
||||
kind: SyntaxKind.GetAccessor;
|
||||
parent?: ClassDeclaration | ClassExpression | ObjectLiteralExpression;
|
||||
name: PropertyName;
|
||||
@@ -855,7 +872,7 @@ namespace ts {
|
||||
|
||||
// See the comment on MethodDeclaration for the intuition behind SetAccessorDeclaration being a
|
||||
// ClassElement and an ObjectLiteralElement.
|
||||
export interface SetAccessorDeclaration extends FunctionLikeDeclaration, ClassElement, ObjectLiteralElement {
|
||||
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement {
|
||||
kind: SyntaxKind.SetAccessor;
|
||||
parent?: ClassDeclaration | ClassExpression | ObjectLiteralExpression;
|
||||
name: PropertyName;
|
||||
@@ -901,7 +918,7 @@ namespace ts {
|
||||
kind: SyntaxKind.ConstructorType;
|
||||
}
|
||||
|
||||
export type TypeReferenceType = TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference;
|
||||
export type TypeReferenceType = TypeReferenceNode | ExpressionWithTypeArguments;
|
||||
|
||||
export interface TypeReferenceNode extends TypeNode {
|
||||
kind: SyntaxKind.TypeReference;
|
||||
@@ -1319,13 +1336,13 @@ namespace ts {
|
||||
export type FunctionBody = Block;
|
||||
export type ConciseBody = FunctionBody | Expression;
|
||||
|
||||
export interface FunctionExpression extends PrimaryExpression, FunctionLikeDeclaration {
|
||||
export interface FunctionExpression extends PrimaryExpression, FunctionLikeDeclarationBase {
|
||||
kind: SyntaxKind.FunctionExpression;
|
||||
name?: Identifier;
|
||||
body: FunctionBody; // Required, whereas the member inherited from FunctionDeclaration is optional
|
||||
}
|
||||
|
||||
export interface ArrowFunction extends Expression, FunctionLikeDeclaration {
|
||||
export interface ArrowFunction extends Expression, FunctionLikeDeclarationBase {
|
||||
kind: SyntaxKind.ArrowFunction;
|
||||
equalsGreaterThanToken: EqualsGreaterThanToken;
|
||||
body: ConciseBody;
|
||||
@@ -1704,6 +1721,8 @@ namespace ts {
|
||||
incrementor?: Expression;
|
||||
}
|
||||
|
||||
export type ForInOrOfStatement = ForInStatement | ForOfStatement;
|
||||
|
||||
export interface ForInStatement extends IterationStatement {
|
||||
kind: SyntaxKind.ForInStatement;
|
||||
initializer: ForInitializer;
|
||||
@@ -1793,7 +1812,7 @@ namespace ts {
|
||||
block: Block;
|
||||
}
|
||||
|
||||
export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration;
|
||||
export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag;
|
||||
|
||||
export interface ClassLikeDeclaration extends NamedDeclaration {
|
||||
name?: Identifier;
|
||||
@@ -2018,9 +2037,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
// represents a top level: { type } expression in a JSDoc comment.
|
||||
export interface JSDocTypeExpression extends Node {
|
||||
export interface JSDocTypeExpression extends TypeNode {
|
||||
kind: SyntaxKind.JSDocTypeExpression;
|
||||
type: JSDocType;
|
||||
type: TypeNode;
|
||||
}
|
||||
|
||||
export interface JSDocType extends TypeNode {
|
||||
@@ -2035,80 +2054,31 @@ namespace ts {
|
||||
kind: SyntaxKind.JSDocUnknownType;
|
||||
}
|
||||
|
||||
export interface JSDocArrayType extends JSDocType {
|
||||
kind: SyntaxKind.JSDocArrayType;
|
||||
elementType: JSDocType;
|
||||
}
|
||||
|
||||
export interface JSDocUnionType extends JSDocType {
|
||||
kind: SyntaxKind.JSDocUnionType;
|
||||
types: NodeArray<JSDocType>;
|
||||
}
|
||||
|
||||
export interface JSDocTupleType extends JSDocType {
|
||||
kind: SyntaxKind.JSDocTupleType;
|
||||
types: NodeArray<JSDocType>;
|
||||
}
|
||||
|
||||
export interface JSDocNonNullableType extends JSDocType {
|
||||
kind: SyntaxKind.JSDocNonNullableType;
|
||||
type: JSDocType;
|
||||
type: TypeNode;
|
||||
}
|
||||
|
||||
export interface JSDocNullableType extends JSDocType {
|
||||
kind: SyntaxKind.JSDocNullableType;
|
||||
type: JSDocType;
|
||||
}
|
||||
|
||||
export interface JSDocRecordType extends JSDocType {
|
||||
kind: SyntaxKind.JSDocRecordType;
|
||||
literal: TypeLiteralNode;
|
||||
}
|
||||
|
||||
export interface JSDocTypeReference extends JSDocType {
|
||||
kind: SyntaxKind.JSDocTypeReference;
|
||||
name: EntityName;
|
||||
typeArguments: NodeArray<JSDocType>;
|
||||
type: TypeNode;
|
||||
}
|
||||
|
||||
export interface JSDocOptionalType extends JSDocType {
|
||||
kind: SyntaxKind.JSDocOptionalType;
|
||||
type: JSDocType;
|
||||
type: TypeNode;
|
||||
}
|
||||
|
||||
export interface JSDocFunctionType extends JSDocType, SignatureDeclaration {
|
||||
kind: SyntaxKind.JSDocFunctionType;
|
||||
parameters: NodeArray<ParameterDeclaration>;
|
||||
type: JSDocType;
|
||||
}
|
||||
|
||||
export interface JSDocVariadicType extends JSDocType {
|
||||
kind: SyntaxKind.JSDocVariadicType;
|
||||
type: JSDocType;
|
||||
type: TypeNode;
|
||||
}
|
||||
|
||||
export interface JSDocConstructorType extends JSDocType {
|
||||
kind: SyntaxKind.JSDocConstructorType;
|
||||
type: JSDocType;
|
||||
}
|
||||
|
||||
export interface JSDocThisType extends JSDocType {
|
||||
kind: SyntaxKind.JSDocThisType;
|
||||
type: JSDocType;
|
||||
}
|
||||
|
||||
export interface JSDocLiteralType extends JSDocType {
|
||||
kind: SyntaxKind.JSDocLiteralType;
|
||||
literal: LiteralTypeNode;
|
||||
}
|
||||
|
||||
export type JSDocTypeReferencingNode = JSDocThisType | JSDocConstructorType | JSDocVariadicType | JSDocOptionalType | JSDocNullableType | JSDocNonNullableType;
|
||||
|
||||
export interface JSDocRecordMember extends PropertySignature {
|
||||
kind: SyntaxKind.JSDocRecordMember;
|
||||
name: Identifier | StringLiteral | NumericLiteral;
|
||||
type?: JSDocType;
|
||||
}
|
||||
export type JSDocTypeReferencingNode = JSDocVariadicType | JSDocOptionalType | JSDocNullableType | JSDocNonNullableType;
|
||||
|
||||
export interface JSDoc extends Node {
|
||||
kind: SyntaxKind.JSDocComment;
|
||||
@@ -2132,6 +2102,10 @@ namespace ts {
|
||||
typeExpression: JSDocTypeExpression;
|
||||
}
|
||||
|
||||
export interface JSDocClassTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocClassTag;
|
||||
}
|
||||
|
||||
export interface JSDocTemplateTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocTemplateTag;
|
||||
typeParameters: NodeArray<TypeParameterDeclaration>;
|
||||
@@ -2152,38 +2126,32 @@ namespace ts {
|
||||
kind: SyntaxKind.JSDocTypedefTag;
|
||||
fullName?: JSDocNamespaceDeclaration | Identifier;
|
||||
name?: Identifier;
|
||||
typeExpression?: JSDocTypeExpression;
|
||||
jsDocTypeLiteral?: JSDocTypeLiteral;
|
||||
typeExpression?: JSDocTypeExpression | JSDocTypeLiteral;
|
||||
}
|
||||
|
||||
export interface JSDocPropertyTag extends JSDocTag, TypeElement {
|
||||
export interface JSDocPropertyLikeTag extends JSDocTag, Declaration {
|
||||
parent: JSDoc;
|
||||
kind: SyntaxKind.JSDocPropertyTag;
|
||||
name: Identifier;
|
||||
/** the parameter name, if provided *before* the type (TypeScript-style) */
|
||||
preParameterName?: Identifier;
|
||||
/** the parameter name, if provided *after* the type (JSDoc-standard) */
|
||||
postParameterName?: Identifier;
|
||||
name: EntityName;
|
||||
typeExpression: JSDocTypeExpression;
|
||||
/** Whether the property name came before the type -- non-standard for JSDoc, but Typescript-like */
|
||||
isNameFirst: boolean;
|
||||
isBracketed: boolean;
|
||||
}
|
||||
|
||||
export interface JSDocPropertyTag extends JSDocPropertyLikeTag {
|
||||
kind: SyntaxKind.JSDocPropertyTag;
|
||||
}
|
||||
|
||||
export interface JSDocParameterTag extends JSDocPropertyLikeTag {
|
||||
kind: SyntaxKind.JSDocParameterTag;
|
||||
}
|
||||
|
||||
export interface JSDocTypeLiteral extends JSDocType {
|
||||
kind: SyntaxKind.JSDocTypeLiteral;
|
||||
jsDocPropertyTags?: NodeArray<JSDocPropertyTag>;
|
||||
jsDocPropertyTags?: ReadonlyArray<JSDocPropertyLikeTag>;
|
||||
jsDocTypeTag?: JSDocTypeTag;
|
||||
}
|
||||
|
||||
export interface JSDocParameterTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocParameterTag;
|
||||
/** the parameter name, if provided *before* the type (TypeScript-style) */
|
||||
preParameterName?: Identifier;
|
||||
typeExpression?: JSDocTypeExpression;
|
||||
/** the parameter name, if provided *after* the type (JSDoc-standard) */
|
||||
postParameterName?: Identifier;
|
||||
/** the parameter name, regardless of the location it was provided */
|
||||
name: Identifier;
|
||||
isBracketed: boolean;
|
||||
/** If true, then this type literal represents an *array* of its type. */
|
||||
isArrayType?: boolean;
|
||||
}
|
||||
|
||||
export const enum FlowFlags {
|
||||
@@ -2326,7 +2294,7 @@ namespace ts {
|
||||
// The first node that causes this file to be a CommonJS module
|
||||
/* @internal */ commonJsModuleIndicator: Node;
|
||||
|
||||
/* @internal */ identifiers: Map<string>;
|
||||
/* @internal */ identifiers: Map<string>; // Map from a string to an interned string
|
||||
/* @internal */ nodeCount: number;
|
||||
/* @internal */ identifierCount: number;
|
||||
/* @internal */ symbolCount: number;
|
||||
@@ -2347,16 +2315,16 @@ namespace ts {
|
||||
// Stores a line map for the file.
|
||||
// This field should never be used directly to obtain line map, use getLineMap function instead.
|
||||
/* @internal */ lineMap: number[];
|
||||
/* @internal */ classifiableNames?: Map<string>;
|
||||
/* @internal */ classifiableNames?: UnderscoreEscapedMap<true>;
|
||||
// Stores a mapping 'external module reference text' -> 'resolved file name' | undefined
|
||||
// It is used to resolve module names in the checker.
|
||||
// Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead
|
||||
/* @internal */ resolvedModules: Map<ResolvedModuleFull>;
|
||||
/* @internal */ resolvedTypeReferenceDirectiveNames: Map<ResolvedTypeReferenceDirective>;
|
||||
/* @internal */ imports: StringLiteral[];
|
||||
/* @internal */ moduleAugmentations: StringLiteral[];
|
||||
/* @internal */ imports: ReadonlyArray<StringLiteral>;
|
||||
/* @internal */ moduleAugmentations: ReadonlyArray<StringLiteral>;
|
||||
/* @internal */ patternAmbientModules?: PatternAmbientModule[];
|
||||
/* @internal */ ambientModuleNames: string[];
|
||||
/* @internal */ ambientModuleNames: ReadonlyArray<string>;
|
||||
/* @internal */ checkJsDirective: CheckJsDirective | undefined;
|
||||
}
|
||||
|
||||
@@ -2365,6 +2333,11 @@ namespace ts {
|
||||
sourceFiles: SourceFile[];
|
||||
}
|
||||
|
||||
export interface JsonSourceFile extends SourceFile {
|
||||
jsonObject?: ObjectLiteralExpression;
|
||||
extendedSourceFiles?: string[];
|
||||
}
|
||||
|
||||
export interface ScriptReferenceHost {
|
||||
getCompilerOptions(): CompilerOptions;
|
||||
getSourceFile(fileName: string): SourceFile;
|
||||
@@ -2375,7 +2348,7 @@ namespace ts {
|
||||
export interface ParseConfigHost {
|
||||
useCaseSensitiveFileNames: boolean;
|
||||
|
||||
readDirectory(rootDir: string, extensions: string[], excludes: string[], includes: string[]): string[];
|
||||
readDirectory(rootDir: string, extensions: ReadonlyArray<string>, excludes: ReadonlyArray<string>, includes: ReadonlyArray<string>, depth: number): string[];
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether the specified path exists and is a file.
|
||||
@@ -2383,11 +2356,11 @@ namespace ts {
|
||||
*/
|
||||
fileExists(path: string): boolean;
|
||||
|
||||
readFile(path: string): string;
|
||||
readFile(path: string): string | undefined;
|
||||
}
|
||||
|
||||
export interface WriteFileCallback {
|
||||
(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void, sourceFiles?: SourceFile[]): void;
|
||||
(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void, sourceFiles?: ReadonlyArray<SourceFile>): void;
|
||||
}
|
||||
|
||||
export class OperationCanceledException { }
|
||||
@@ -2411,6 +2384,13 @@ namespace ts {
|
||||
*/
|
||||
getSourceFiles(): SourceFile[];
|
||||
|
||||
/**
|
||||
* Get a list of file names that were passed to 'createProgram' or referenced in a
|
||||
* program source file but could not be located.
|
||||
*/
|
||||
/* @internal */
|
||||
getMissingFilePaths(): Path[];
|
||||
|
||||
/**
|
||||
* Emits the JavaScript and declaration files. If targetSourceFile is not specified, then
|
||||
* the JavaScript and declaration files will be produced for all the files in this program.
|
||||
@@ -2441,7 +2421,7 @@ namespace ts {
|
||||
/* @internal */ getDiagnosticsProducingTypeChecker(): TypeChecker;
|
||||
/* @internal */ dropDiagnosticsProducingTypeChecker(): void;
|
||||
|
||||
/* @internal */ getClassifiableNames(): Map<string>;
|
||||
/* @internal */ getClassifiableNames(): UnderscoreEscapedMap<true>;
|
||||
|
||||
/* @internal */ getNodeCount(): number;
|
||||
/* @internal */ getIdentifierCount(): number;
|
||||
@@ -2547,6 +2527,7 @@ namespace ts {
|
||||
* Returns `any` if the index is not valid.
|
||||
*/
|
||||
/* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type;
|
||||
getNullableType(type: Type, flags: TypeFlags): Type;
|
||||
getNonNullableType(type: Type): Type;
|
||||
|
||||
/** Note that the resulting nodes cannot be checked. */
|
||||
@@ -2572,9 +2553,13 @@ namespace ts {
|
||||
getAugmentedPropertiesOfType(type: Type): Symbol[];
|
||||
getRootSymbols(symbol: Symbol): Symbol[];
|
||||
getContextualType(node: Expression): Type | undefined;
|
||||
getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature | undefined;
|
||||
/**
|
||||
* returns unknownSignature in the case of an error. Don't know when it returns undefined.
|
||||
* @param argumentCount Apparent number of arguments, passed in case of a possibly incomplete call. This should come from an ArgumentListInfo. See `signatureHelp.ts`.
|
||||
*/
|
||||
getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[], argumentCount?: number): Signature | undefined;
|
||||
getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature | undefined;
|
||||
isImplementationOfOverload(node: FunctionLikeDeclaration): boolean | undefined;
|
||||
isImplementationOfOverload(node: FunctionLike): boolean | undefined;
|
||||
isUndefinedSymbol(symbol: Symbol): boolean;
|
||||
isArgumentsSymbol(symbol: Symbol): boolean;
|
||||
isUnknownSymbol(symbol: Symbol): boolean;
|
||||
@@ -2596,6 +2581,8 @@ namespace ts {
|
||||
getAmbientModules(): Symbol[];
|
||||
|
||||
tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
|
||||
/** Unlike `tryGetMemberInModuleExports`, this includes properties of an `export =` value. */
|
||||
/* @internal */ tryGetMemberInModuleExportsAndProperties(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
|
||||
getApparentType(type: Type): Type;
|
||||
getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined;
|
||||
getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
|
||||
@@ -2700,6 +2687,7 @@ namespace ts {
|
||||
SuppressAnyReturnType = 1 << 12, // If the return type is any-like, don't offer a return type.
|
||||
AddUndefined = 1 << 13, // Add undefined to types of initialized, non-optional parameters
|
||||
WriteClassExpressionAsTypeLiteral = 1 << 14, // Write a type literal instead of (Anonymous class)
|
||||
InArrayType = 1 << 15, // Writing an array element type
|
||||
}
|
||||
|
||||
export const enum SymbolFormatFlags {
|
||||
@@ -2804,6 +2792,7 @@ namespace ts {
|
||||
collectLinkedAliases(node: Identifier): Node[];
|
||||
isImplementationOfOverload(node: FunctionLikeDeclaration): boolean | undefined;
|
||||
isRequiredInitializedParameter(node: ParameterDeclaration): boolean;
|
||||
isOptionalUninitializedParameterProperty(node: ParameterDeclaration): boolean;
|
||||
writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
|
||||
writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
|
||||
writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
|
||||
@@ -2847,13 +2836,11 @@ namespace ts {
|
||||
TypeParameter = 1 << 18, // Type parameter
|
||||
TypeAlias = 1 << 19, // Type alias
|
||||
ExportValue = 1 << 20, // Exported value marker (see comment in declareModuleMember in binder)
|
||||
ExportType = 1 << 21, // Exported type marker (see comment in declareModuleMember in binder)
|
||||
ExportNamespace = 1 << 22, // Exported namespace marker (see comment in declareModuleMember in binder)
|
||||
Alias = 1 << 23, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker)
|
||||
Prototype = 1 << 24, // Prototype property (no source representation)
|
||||
ExportStar = 1 << 25, // Export * declaration
|
||||
Optional = 1 << 26, // Optional property
|
||||
Transient = 1 << 27, // Transient symbol (created during type check)
|
||||
Alias = 1 << 21, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker)
|
||||
Prototype = 1 << 22, // Prototype property (no source representation)
|
||||
ExportStar = 1 << 23, // Export * declaration
|
||||
Optional = 1 << 24, // Optional property
|
||||
Transient = 1 << 25, // Transient symbol (created during type check)
|
||||
|
||||
Enum = RegularEnum | ConstEnum,
|
||||
Variable = FunctionScopedVariable | BlockScopedVariable,
|
||||
@@ -2898,7 +2885,6 @@ namespace ts {
|
||||
BlockScoped = BlockScopedVariable | Class | Enum,
|
||||
|
||||
PropertyOrAccessor = Property | Accessor,
|
||||
Export = ExportNamespace | ExportType | ExportValue,
|
||||
|
||||
ClassMember = Method | Accessor | Property,
|
||||
|
||||
@@ -2910,7 +2896,7 @@ namespace ts {
|
||||
|
||||
export interface Symbol {
|
||||
flags: SymbolFlags; // Symbol flags
|
||||
name: string; // Name of symbol
|
||||
escapedName: __String; // Name of symbol
|
||||
declarations?: Declaration[]; // Declarations associated with this symbol
|
||||
valueDeclaration?: Declaration; // First value declaration of the symbol
|
||||
members?: SymbolTable; // Class, interface or literal instance members
|
||||
@@ -2978,7 +2964,55 @@ namespace ts {
|
||||
isRestParameter?: boolean;
|
||||
}
|
||||
|
||||
export type SymbolTable = Map<Symbol>;
|
||||
export const enum InternalSymbolName {
|
||||
Call = "__call", // Call signatures
|
||||
Constructor = "__constructor", // Constructor implementations
|
||||
New = "__new", // Constructor signatures
|
||||
Index = "__index", // Index signatures
|
||||
ExportStar = "__export", // Module export * declarations
|
||||
Global = "__global", // Global self-reference
|
||||
Missing = "__missing", // Indicates missing symbol
|
||||
Type = "__type", // Anonymous type literal symbol
|
||||
Object = "__object", // Anonymous object literal declaration
|
||||
JSXAttributes = "__jsxAttributes", // Anonymous JSX attributes object literal declaration
|
||||
Class = "__class", // Unnamed class expression
|
||||
Function = "__function", // Unnamed function expression
|
||||
Computed = "__computed", // Computed property name declaration with dynamic name
|
||||
Resolving = "__resolving__", // Indicator symbol used to mark partially resolved type aliases
|
||||
ExportEquals = "export=", // Export assignment symbol
|
||||
Default = "default", // Default export symbol (technically not wholly internal, but included here for usability)
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a string whose leading underscore have been escaped by adding extra leading underscores.
|
||||
* The shape of this brand is rather unique compared to others we've used.
|
||||
* Instead of just an intersection of a string and an object, it is that union-ed
|
||||
* with an intersection of void and an object. This makes it wholly incompatible
|
||||
* with a normal string (which is good, it cannot be misused on assignment or on usage),
|
||||
* while still being comparable with a normal string via === (also good) and castable from a string.
|
||||
*/
|
||||
export type __String = (string & { __escapedIdentifier: void }) | (void & { __escapedIdentifier: void }) | InternalSymbolName;
|
||||
|
||||
/** ReadonlyMap where keys are `__String`s. */
|
||||
export interface ReadonlyUnderscoreEscapedMap<T> {
|
||||
get(key: __String): T | undefined;
|
||||
has(key: __String): boolean;
|
||||
forEach(action: (value: T, key: __String) => void): void;
|
||||
readonly size: number;
|
||||
keys(): Iterator<__String>;
|
||||
values(): Iterator<T>;
|
||||
entries(): Iterator<[__String, T]>;
|
||||
}
|
||||
|
||||
/** Map where keys are `__String`s. */
|
||||
export interface UnderscoreEscapedMap<T> extends ReadonlyUnderscoreEscapedMap<T> {
|
||||
set(key: __String, value: T): this;
|
||||
delete(key: __String): boolean;
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
/** SymbolTable based on ES6 Map interface. */
|
||||
export type SymbolTable = UnderscoreEscapedMap<Symbol>;
|
||||
|
||||
/** Represents a "prefix*suffix" pattern. */
|
||||
/* @internal */
|
||||
@@ -3159,7 +3193,7 @@ namespace ts {
|
||||
objectFlags: ObjectFlags;
|
||||
}
|
||||
|
||||
/** Class and interface types (TypeFlags.Class and TypeFlags.Interface). */
|
||||
/** Class and interface types (ObjectFlags.Class and ObjectFlags.Interface). */
|
||||
export interface InterfaceType extends ObjectType {
|
||||
typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic)
|
||||
outerTypeParameters: TypeParameter[]; // Outer type parameters (undefined if none)
|
||||
@@ -3183,7 +3217,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
/**
|
||||
* Type references (TypeFlags.Reference). When a class or interface has type parameters or
|
||||
* Type references (ObjectFlags.Reference). When a class or interface has type parameters or
|
||||
* a "this" type, references to the class or interface are made using type references. The
|
||||
* typeArguments property specifies the types to substitute for the type parameters of the
|
||||
* class or interface and optionally includes an extra element that specifies the type to
|
||||
@@ -3366,8 +3400,8 @@ namespace ts {
|
||||
/* @internal */
|
||||
export interface TypeMapper {
|
||||
(t: TypeParameter): Type;
|
||||
mappedTypes?: Type[]; // Types mapped by this mapper
|
||||
instantiations?: Type[]; // Cache of instantiations created using this type mapper.
|
||||
mappedTypes?: TypeParameter[]; // Types mapped by this mapper
|
||||
instantiations?: Type[]; // Cache of instantiations created using this type mapper.
|
||||
}
|
||||
|
||||
export const enum InferencePriority {
|
||||
@@ -3396,8 +3430,6 @@ namespace ts {
|
||||
signature: Signature; // Generic signature for which inferences are made
|
||||
inferences: InferenceInfo[]; // Inferences made for each type parameter
|
||||
flags: InferenceFlags; // Inference flags
|
||||
failedTypeParameterIndex?: number; // Index of type parameter for which inference failed
|
||||
// It is optional because in contextual signature instantiation, nothing fails
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -3479,6 +3511,7 @@ namespace ts {
|
||||
charset?: string;
|
||||
checkJs?: boolean;
|
||||
/* @internal */ configFilePath?: string;
|
||||
/* @internal */ readonly configFile?: JsonSourceFile;
|
||||
declaration?: boolean;
|
||||
declarationDir?: string;
|
||||
/* @internal */ diagnostics?: boolean;
|
||||
@@ -3514,6 +3547,7 @@ namespace ts {
|
||||
noImplicitAny?: boolean; // Always combine with strict property
|
||||
noImplicitReturns?: boolean;
|
||||
noImplicitThis?: boolean; // Always combine with strict property
|
||||
noStrictGenericChecks?: boolean;
|
||||
noUnusedLocals?: boolean;
|
||||
noUnusedParameters?: boolean;
|
||||
noImplicitUseStrict?: boolean;
|
||||
@@ -3550,7 +3584,7 @@ namespace ts {
|
||||
/*@internal*/ version?: boolean;
|
||||
/*@internal*/ watch?: boolean;
|
||||
|
||||
[option: string]: CompilerOptionsValue | undefined;
|
||||
[option: string]: CompilerOptionsValue | JsonSourceFile | undefined;
|
||||
}
|
||||
|
||||
export interface TypeAcquisition {
|
||||
@@ -3610,7 +3644,8 @@ namespace ts {
|
||||
JSX = 2,
|
||||
TS = 3,
|
||||
TSX = 4,
|
||||
External = 5
|
||||
External = 5,
|
||||
JSON = 6
|
||||
}
|
||||
|
||||
export const enum ScriptTarget {
|
||||
@@ -3682,6 +3717,8 @@ namespace ts {
|
||||
/* @internal */
|
||||
export interface TsConfigOnlyOption extends CommandLineOptionBase {
|
||||
type: "object";
|
||||
elementOptions?: Map<CommandLineOption>;
|
||||
extraKeyDiagnosticMessage?: DiagnosticMessage;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -3834,7 +3871,7 @@ namespace ts {
|
||||
fileExists(fileName: string): boolean;
|
||||
// readFile function is used to read arbitrary text files on disk, i.e. when resolution procedure needs the content of 'package.json'
|
||||
// to determine location of bundled typings for node module
|
||||
readFile(fileName: string): string;
|
||||
readFile(fileName: string): string | undefined;
|
||||
trace?(s: string): void;
|
||||
directoryExists?(directoryName: string): boolean;
|
||||
realpath?(path: string): string;
|
||||
@@ -3873,13 +3910,12 @@ namespace ts {
|
||||
extension: Extension;
|
||||
}
|
||||
|
||||
export enum Extension {
|
||||
Ts,
|
||||
Tsx,
|
||||
Dts,
|
||||
Js,
|
||||
Jsx,
|
||||
LastTypeScriptExtension = Dts
|
||||
export const enum Extension {
|
||||
Ts = ".ts",
|
||||
Tsx = ".tsx",
|
||||
Dts = ".d.ts",
|
||||
Js = ".js",
|
||||
Jsx = ".jsx"
|
||||
}
|
||||
|
||||
export interface ResolvedModuleWithFailedLookupLocations {
|
||||
@@ -4008,18 +4044,29 @@ namespace ts {
|
||||
ES2015FunctionSyntaxMask = ContainsCapturedLexicalThis | ContainsDefaultValueAssignments,
|
||||
}
|
||||
|
||||
export interface SourceMapRange extends TextRange {
|
||||
source?: SourceMapSource;
|
||||
}
|
||||
|
||||
export interface SourceMapSource {
|
||||
fileName: string;
|
||||
text: string;
|
||||
/* @internal */ lineMap: number[];
|
||||
skipTrivia?: (pos: number) => number;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface EmitNode {
|
||||
annotatedNodes?: Node[]; // Tracks Parse-tree nodes with EmitNodes for eventual cleanup.
|
||||
flags?: EmitFlags; // Flags that customize emit
|
||||
leadingComments?: SynthesizedComment[]; // Synthesized leading comments
|
||||
annotatedNodes?: Node[]; // Tracks Parse-tree nodes with EmitNodes for eventual cleanup.
|
||||
flags?: EmitFlags; // Flags that customize emit
|
||||
leadingComments?: SynthesizedComment[]; // Synthesized leading comments
|
||||
trailingComments?: SynthesizedComment[]; // Synthesized trailing comments
|
||||
commentRange?: TextRange; // The text range to use when emitting leading or trailing comments
|
||||
sourceMapRange?: TextRange; // The text range to use when emitting leading or trailing source mappings
|
||||
tokenSourceMapRanges?: TextRange[]; // The text range to use when emitting source mappings for tokens
|
||||
constantValue?: string | number; // The constant value of an expression
|
||||
externalHelpersModuleName?: Identifier; // The local name for an imported helpers module
|
||||
helpers?: EmitHelper[]; // Emit helpers for the node
|
||||
commentRange?: TextRange; // The text range to use when emitting leading or trailing comments
|
||||
sourceMapRange?: SourceMapRange; // The text range to use when emitting leading or trailing source mappings
|
||||
tokenSourceMapRanges?: SourceMapRange[]; // The text range to use when emitting source mappings for tokens
|
||||
constantValue?: string | number; // The constant value of an expression
|
||||
externalHelpersModuleName?: Identifier; // The local name for an imported helpers module
|
||||
helpers?: EmitHelper[]; // Emit helpers for the node
|
||||
}
|
||||
|
||||
export const enum EmitFlags {
|
||||
@@ -4081,6 +4128,7 @@ namespace ts {
|
||||
AsyncGenerator = 1 << 12, // __asyncGenerator (used by ES2017 async generator transformation)
|
||||
AsyncDelegator = 1 << 13, // __asyncDelegator (used by ES2017 async generator yield* transformation)
|
||||
AsyncValues = 1 << 14, // __asyncValues (used by ES2017 for..await..of transformation)
|
||||
ExportStar = 1 << 15, // __exportStar (used by CommonJS/AMD/UMD module transformation)
|
||||
|
||||
// Helpers included by ES2015 for..of
|
||||
ForOfIncludes = Values,
|
||||
@@ -4098,7 +4146,7 @@ namespace ts {
|
||||
SpreadIncludes = Read | Spread,
|
||||
|
||||
FirstEmitHelper = Extends,
|
||||
LastEmitHelper = AsyncValues
|
||||
LastEmitHelper = ExportStar
|
||||
}
|
||||
|
||||
export const enum EmitHint {
|
||||
@@ -4238,7 +4286,7 @@ namespace ts {
|
||||
*/
|
||||
export type Visitor = (node: Node) => VisitResult<Node>;
|
||||
|
||||
export type VisitResult<T extends Node> = T | T[];
|
||||
export type VisitResult<T extends Node> = T | T[] | undefined;
|
||||
|
||||
export interface Printer {
|
||||
/**
|
||||
|
||||
+387
-281
File diff suppressed because it is too large
Load Diff
+71
-56
@@ -86,7 +86,7 @@ namespace ts {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
let updated: NodeArray<T>;
|
||||
let updated: MutableNodeArray<T>;
|
||||
|
||||
// Ensure start and count have valid values
|
||||
const length = nodes.length;
|
||||
@@ -270,12 +270,13 @@ namespace ts {
|
||||
nodesVisitor((<PropertyDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<PropertyDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<PropertyDeclaration>node).name, visitor, isPropertyName),
|
||||
visitNode((<PropertyDeclaration>node).questionToken, tokenVisitor, isToken),
|
||||
visitNode((<PropertyDeclaration>node).type, visitor, isTypeNode),
|
||||
visitNode((<PropertyDeclaration>node).initializer, visitor, isExpression));
|
||||
|
||||
case SyntaxKind.MethodSignature:
|
||||
return updateMethodSignature(<MethodSignature>node,
|
||||
nodesVisitor((<MethodSignature>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<MethodSignature>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
nodesVisitor((<MethodSignature>node).parameters, visitor, isParameterDeclaration),
|
||||
visitNode((<MethodSignature>node).type, visitor, isTypeNode),
|
||||
visitNode((<MethodSignature>node).name, visitor, isPropertyName),
|
||||
@@ -288,7 +289,7 @@ namespace ts {
|
||||
visitNode((<MethodDeclaration>node).asteriskToken, tokenVisitor, isToken),
|
||||
visitNode((<MethodDeclaration>node).name, visitor, isPropertyName),
|
||||
visitNode((<MethodDeclaration>node).questionToken, tokenVisitor, isToken),
|
||||
nodesVisitor((<MethodDeclaration>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<MethodDeclaration>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
visitParameterList((<MethodDeclaration>node).parameters, visitor, context, nodesVisitor),
|
||||
visitNode((<MethodDeclaration>node).type, visitor, isTypeNode),
|
||||
visitFunctionBody((<MethodDeclaration>node).body, visitor, context));
|
||||
@@ -319,13 +320,13 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.CallSignature:
|
||||
return updateCallSignature(<CallSignatureDeclaration>node,
|
||||
nodesVisitor((<CallSignatureDeclaration>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<CallSignatureDeclaration>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
nodesVisitor((<CallSignatureDeclaration>node).parameters, visitor, isParameterDeclaration),
|
||||
visitNode((<CallSignatureDeclaration>node).type, visitor, isTypeNode));
|
||||
|
||||
case SyntaxKind.ConstructSignature:
|
||||
return updateConstructSignature(<ConstructSignatureDeclaration>node,
|
||||
nodesVisitor((<ConstructSignatureDeclaration>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<ConstructSignatureDeclaration>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
nodesVisitor((<ConstructSignatureDeclaration>node).parameters, visitor, isParameterDeclaration),
|
||||
visitNode((<ConstructSignatureDeclaration>node).type, visitor, isTypeNode));
|
||||
|
||||
@@ -350,13 +351,13 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.FunctionType:
|
||||
return updateFunctionTypeNode(<FunctionTypeNode>node,
|
||||
nodesVisitor((<FunctionTypeNode>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<FunctionTypeNode>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
nodesVisitor((<FunctionTypeNode>node).parameters, visitor, isParameterDeclaration),
|
||||
visitNode((<FunctionTypeNode>node).type, visitor, isTypeNode));
|
||||
|
||||
case SyntaxKind.ConstructorType:
|
||||
return updateConstructorTypeNode(<ConstructorTypeNode>node,
|
||||
nodesVisitor((<ConstructorTypeNode>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<ConstructorTypeNode>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
nodesVisitor((<ConstructorTypeNode>node).parameters, visitor, isParameterDeclaration),
|
||||
visitNode((<ConstructorTypeNode>node).type, visitor, isTypeNode));
|
||||
|
||||
@@ -400,7 +401,7 @@ namespace ts {
|
||||
case SyntaxKind.MappedType:
|
||||
return updateMappedTypeNode((<MappedTypeNode>node),
|
||||
visitNode((<MappedTypeNode>node).readonlyToken, tokenVisitor, isToken),
|
||||
visitNode((<MappedTypeNode>node).typeParameter, visitor, isTypeParameter),
|
||||
visitNode((<MappedTypeNode>node).typeParameter, visitor, isTypeParameterDeclaration),
|
||||
visitNode((<MappedTypeNode>node).questionToken, tokenVisitor, isToken),
|
||||
visitNode((<MappedTypeNode>node).type, visitor, isTypeNode));
|
||||
|
||||
@@ -476,7 +477,7 @@ namespace ts {
|
||||
nodesVisitor((<FunctionExpression>node).modifiers, visitor, isModifier),
|
||||
visitNode((<FunctionExpression>node).asteriskToken, tokenVisitor, isToken),
|
||||
visitNode((<FunctionExpression>node).name, visitor, isIdentifier),
|
||||
nodesVisitor((<FunctionExpression>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<FunctionExpression>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
visitParameterList((<FunctionExpression>node).parameters, visitor, context, nodesVisitor),
|
||||
visitNode((<FunctionExpression>node).type, visitor, isTypeNode),
|
||||
visitFunctionBody((<FunctionExpression>node).body, visitor, context));
|
||||
@@ -484,7 +485,7 @@ namespace ts {
|
||||
case SyntaxKind.ArrowFunction:
|
||||
return updateArrowFunction(<ArrowFunction>node,
|
||||
nodesVisitor((<ArrowFunction>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<ArrowFunction>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<ArrowFunction>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
visitParameterList((<ArrowFunction>node).parameters, visitor, context, nodesVisitor),
|
||||
visitNode((<ArrowFunction>node).type, visitor, isTypeNode),
|
||||
visitFunctionBody((<ArrowFunction>node).body, visitor, context));
|
||||
@@ -543,7 +544,7 @@ namespace ts {
|
||||
return updateClassExpression(<ClassExpression>node,
|
||||
nodesVisitor((<ClassExpression>node).modifiers, visitor, isModifier),
|
||||
visitNode((<ClassExpression>node).name, visitor, isIdentifier),
|
||||
nodesVisitor((<ClassExpression>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<ClassExpression>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
nodesVisitor((<ClassExpression>node).heritageClauses, visitor, isHeritageClause),
|
||||
nodesVisitor((<ClassExpression>node).members, visitor, isClassElement));
|
||||
|
||||
@@ -676,7 +677,7 @@ namespace ts {
|
||||
nodesVisitor((<FunctionDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<FunctionDeclaration>node).asteriskToken, tokenVisitor, isToken),
|
||||
visitNode((<FunctionDeclaration>node).name, visitor, isIdentifier),
|
||||
nodesVisitor((<FunctionDeclaration>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<FunctionDeclaration>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
visitParameterList((<FunctionDeclaration>node).parameters, visitor, context, nodesVisitor),
|
||||
visitNode((<FunctionDeclaration>node).type, visitor, isTypeNode),
|
||||
visitFunctionBody((<FunctionExpression>node).body, visitor, context));
|
||||
@@ -686,7 +687,7 @@ namespace ts {
|
||||
nodesVisitor((<ClassDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<ClassDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<ClassDeclaration>node).name, visitor, isIdentifier),
|
||||
nodesVisitor((<ClassDeclaration>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<ClassDeclaration>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
nodesVisitor((<ClassDeclaration>node).heritageClauses, visitor, isHeritageClause),
|
||||
nodesVisitor((<ClassDeclaration>node).members, visitor, isClassElement));
|
||||
|
||||
@@ -695,7 +696,7 @@ namespace ts {
|
||||
nodesVisitor((<InterfaceDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<InterfaceDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<InterfaceDeclaration>node).name, visitor, isIdentifier),
|
||||
nodesVisitor((<InterfaceDeclaration>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<InterfaceDeclaration>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
nodesVisitor((<InterfaceDeclaration>node).heritageClauses, visitor, isHeritageClause),
|
||||
nodesVisitor((<InterfaceDeclaration>node).members, visitor, isTypeElement));
|
||||
|
||||
@@ -704,7 +705,7 @@ namespace ts {
|
||||
nodesVisitor((<TypeAliasDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<TypeAliasDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<TypeAliasDeclaration>node).name, visitor, isIdentifier),
|
||||
nodesVisitor((<TypeAliasDeclaration>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<TypeAliasDeclaration>node).typeParameters, visitor, isTypeParameterDeclaration),
|
||||
visitNode((<TypeAliasDeclaration>node).type, visitor, isTypeNode));
|
||||
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
@@ -900,7 +901,7 @@ namespace ts {
|
||||
*
|
||||
* @param nodes The NodeArray.
|
||||
*/
|
||||
function extractSingleNode(nodes: Node[]): Node {
|
||||
function extractSingleNode(nodes: ReadonlyArray<Node>): Node {
|
||||
Debug.assert(nodes.length <= 1, "Too many nodes written to output.");
|
||||
return singleOrUndefined(nodes);
|
||||
}
|
||||
@@ -1148,10 +1149,6 @@ namespace ts {
|
||||
result = reduceNode((<AsExpression>node).type, cbNode, result);
|
||||
break;
|
||||
|
||||
case SyntaxKind.NonNullExpression:
|
||||
result = reduceNode((<NonNullExpression>node).expression, cbNode, result);
|
||||
break;
|
||||
|
||||
// Misc
|
||||
case SyntaxKind.TemplateSpan:
|
||||
result = reduceNode((<TemplateSpan>node).expression, cbNode, result);
|
||||
@@ -1198,9 +1195,9 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
result = reduceNode((<ForInStatement | ForOfStatement>node).initializer, cbNode, result);
|
||||
result = reduceNode((<ForInStatement | ForOfStatement>node).expression, cbNode, result);
|
||||
result = reduceNode((<ForInStatement | ForOfStatement>node).statement, cbNode, result);
|
||||
result = reduceNode((<ForInOrOfStatement>node).initializer, cbNode, result);
|
||||
result = reduceNode((<ForInOrOfStatement>node).expression, cbNode, result);
|
||||
result = reduceNode((<ForInOrOfStatement>node).statement, cbNode, result);
|
||||
break;
|
||||
|
||||
case SyntaxKind.ReturnStatement:
|
||||
@@ -1424,13 +1421,13 @@ namespace ts {
|
||||
/**
|
||||
* Merges generated lexical declarations into a new statement list.
|
||||
*/
|
||||
export function mergeLexicalEnvironment(statements: NodeArray<Statement>, declarations: Statement[]): NodeArray<Statement>;
|
||||
export function mergeLexicalEnvironment(statements: NodeArray<Statement>, declarations: ReadonlyArray<Statement>): NodeArray<Statement>;
|
||||
|
||||
/**
|
||||
* Appends generated lexical declarations to an array of statements.
|
||||
*/
|
||||
export function mergeLexicalEnvironment(statements: Statement[], declarations: Statement[]): Statement[];
|
||||
export function mergeLexicalEnvironment(statements: Statement[], declarations: Statement[]) {
|
||||
export function mergeLexicalEnvironment(statements: Statement[], declarations: ReadonlyArray<Statement>): Statement[];
|
||||
export function mergeLexicalEnvironment(statements: Statement[] | NodeArray<Statement>, declarations: ReadonlyArray<Statement>) {
|
||||
if (!some(declarations)) {
|
||||
return statements;
|
||||
}
|
||||
@@ -1445,7 +1442,7 @@ namespace ts {
|
||||
*
|
||||
* @param nodes The NodeArray.
|
||||
*/
|
||||
export function liftToBlock(nodes: Node[]): Statement {
|
||||
export function liftToBlock(nodes: ReadonlyArray<Node>): Statement {
|
||||
Debug.assert(every(nodes, isStatement), "Cannot lift nodes to a Block.");
|
||||
return <Statement>singleOrUndefined(nodes) || createBlock(<NodeArray<Statement>>nodes);
|
||||
}
|
||||
@@ -1517,35 +1514,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export namespace Debug {
|
||||
if (isDebugging) {
|
||||
// Add additional properties in debug mode to assist with debugging.
|
||||
Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, {
|
||||
"__debugFlags": { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
|
||||
});
|
||||
|
||||
Object.defineProperties(objectAllocator.getTypeConstructor().prototype, {
|
||||
"__debugFlags": { get(this: Type) { return formatTypeFlags(this.flags); } },
|
||||
"__debugObjectFlags": { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((<ObjectType>this).objectFlags) : ""; } },
|
||||
"__debugTypeToString": { value(this: Type) { return this.checker.typeToString(this); } },
|
||||
});
|
||||
|
||||
for (const ctor of [objectAllocator.getNodeConstructor(), objectAllocator.getIdentifierConstructor(), objectAllocator.getTokenConstructor(), objectAllocator.getSourceFileConstructor()]) {
|
||||
if (!ctor.prototype.hasOwnProperty("__debugKind")) {
|
||||
Object.defineProperties(ctor.prototype, {
|
||||
"__debugKind": { get(this: Node) { return formatSyntaxKind(this.kind); } },
|
||||
"__debugModifierFlags": { get(this: Node) { return formatModifierFlags(getModifierFlagsNoCache(this)); } },
|
||||
"__debugTransformFlags": { get(this: Node) { return formatTransformFlags(this.transformFlags); } },
|
||||
"__debugEmitFlags": { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } },
|
||||
"__debugGetText": { value(this: Node, includeTrivia?: boolean) {
|
||||
if (nodeIsSynthesized(this)) return "";
|
||||
const parseNode = getParseTreeNode(this);
|
||||
const sourceFile = parseNode && getSourceFileOfNode(parseNode);
|
||||
return sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : "";
|
||||
} }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
let isDebugInfoEnabled = false;
|
||||
|
||||
export const failBadSyntaxKind = shouldAssert(AssertionLevel.Normal)
|
||||
? (node: Node, message?: string): void => fail(
|
||||
@@ -1592,5 +1561,51 @@ namespace ts {
|
||||
() => `Node ${formatSyntaxKind(node.kind)} was unexpected'.`,
|
||||
assertMissingNode)
|
||||
: noop;
|
||||
|
||||
/**
|
||||
* Injects debug information into frequently used types.
|
||||
*/
|
||||
export function enableDebugInfo() {
|
||||
if (isDebugInfoEnabled) return;
|
||||
|
||||
// Add additional properties in debug mode to assist with debugging.
|
||||
Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, {
|
||||
"__debugFlags": { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
|
||||
});
|
||||
|
||||
Object.defineProperties(objectAllocator.getTypeConstructor().prototype, {
|
||||
"__debugFlags": { get(this: Type) { return formatTypeFlags(this.flags); } },
|
||||
"__debugObjectFlags": { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((<ObjectType>this).objectFlags) : ""; } },
|
||||
"__debugTypeToString": { value(this: Type) { return this.checker.typeToString(this); } },
|
||||
});
|
||||
|
||||
const nodeConstructors = [
|
||||
objectAllocator.getNodeConstructor(),
|
||||
objectAllocator.getIdentifierConstructor(),
|
||||
objectAllocator.getTokenConstructor(),
|
||||
objectAllocator.getSourceFileConstructor()
|
||||
];
|
||||
|
||||
for (const ctor of nodeConstructors) {
|
||||
if (!ctor.prototype.hasOwnProperty("__debugKind")) {
|
||||
Object.defineProperties(ctor.prototype, {
|
||||
"__debugKind": { get(this: Node) { return formatSyntaxKind(this.kind); } },
|
||||
"__debugModifierFlags": { get(this: Node) { return formatModifierFlags(getModifierFlagsNoCache(this)); } },
|
||||
"__debugTransformFlags": { get(this: Node) { return formatTransformFlags(this.transformFlags); } },
|
||||
"__debugEmitFlags": { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } },
|
||||
"__debugGetText": {
|
||||
value(this: Node, includeTrivia?: boolean) {
|
||||
if (nodeIsSynthesized(this)) return "";
|
||||
const parseNode = getParseTreeNode(this);
|
||||
const sourceFile = parseNode && getSourceFileOfNode(parseNode);
|
||||
return sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : "";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isDebugInfoEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
|
||||
let result: Harness.Compiler.CompilerResult;
|
||||
let options: ts.CompilerOptions;
|
||||
let tsConfigFiles: Harness.Compiler.TestFile[];
|
||||
// equivalent to the files that will be passed on the command line
|
||||
let toBeCompiled: Harness.Compiler.TestFile[];
|
||||
// equivalent to other files on the file system not directly passed to the compiler (ie things that are referenced by other files)
|
||||
@@ -77,10 +78,12 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
const units = testCaseContent.testUnitData;
|
||||
harnessSettings = testCaseContent.settings;
|
||||
let tsConfigOptions: ts.CompilerOptions;
|
||||
tsConfigFiles = [];
|
||||
if (testCaseContent.tsConfig) {
|
||||
assert.equal(testCaseContent.tsConfig.fileNames.length, 0, `list of files in tsconfig is not currently supported`);
|
||||
|
||||
tsConfigOptions = ts.clone(testCaseContent.tsConfig.options);
|
||||
tsConfigOptions = ts.cloneCompilerOptions(testCaseContent.tsConfig.options);
|
||||
tsConfigFiles.push(this.createHarnessTestFile(testCaseContent.tsConfigFileUnitData, rootDir, ts.combinePaths(rootDir, tsConfigOptions.configFilePath)));
|
||||
}
|
||||
else {
|
||||
const baseUrl = harnessSettings["baseUrl"];
|
||||
@@ -90,7 +93,7 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
}
|
||||
|
||||
lastUnit = units[units.length - 1];
|
||||
hasNonDtsFiles = ts.forEach(units, unit => !ts.fileExtensionIs(unit.name, ".d.ts"));
|
||||
hasNonDtsFiles = ts.forEach(units, unit => !ts.fileExtensionIs(unit.name, ts.Extension.Dts));
|
||||
// We need to assemble the list of input files for the compiler and other related files on the 'filesystem' (ie in a multi-file test)
|
||||
// If the last file in a test uses require or a triple slash reference we'll assume all other files will be brought in via references,
|
||||
// otherwise, assume all files are just meant to be in the same compilation session without explicit references to one another.
|
||||
@@ -98,21 +101,22 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
otherFiles = [];
|
||||
|
||||
if (testCaseContent.settings["noImplicitReferences"] || /require\(/.test(lastUnit.content) || /reference\spath/.test(lastUnit.content)) {
|
||||
toBeCompiled.push({ unitName: this.makeUnitName(lastUnit.name, rootDir), content: lastUnit.content, fileOptions: lastUnit.fileOptions });
|
||||
toBeCompiled.push(this.createHarnessTestFile(lastUnit, rootDir));
|
||||
units.forEach(unit => {
|
||||
if (unit.name !== lastUnit.name) {
|
||||
otherFiles.push({ unitName: this.makeUnitName(unit.name, rootDir), content: unit.content, fileOptions: unit.fileOptions });
|
||||
otherFiles.push(this.createHarnessTestFile(unit, rootDir));
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
toBeCompiled = units.map(unit => {
|
||||
return { unitName: this.makeUnitName(unit.name, rootDir), content: unit.content, fileOptions: unit.fileOptions };
|
||||
return this.createHarnessTestFile(unit, rootDir);
|
||||
});
|
||||
}
|
||||
|
||||
if (tsConfigOptions && tsConfigOptions.configFilePath !== undefined) {
|
||||
tsConfigOptions.configFilePath = ts.combinePaths(rootDir, tsConfigOptions.configFilePath);
|
||||
tsConfigOptions.configFile.fileName = tsConfigOptions.configFilePath;
|
||||
}
|
||||
|
||||
const output = Harness.Compiler.compileFiles(
|
||||
@@ -132,11 +136,12 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
options = undefined;
|
||||
toBeCompiled = undefined;
|
||||
otherFiles = undefined;
|
||||
tsConfigFiles = undefined;
|
||||
});
|
||||
|
||||
// check errors
|
||||
it("Correct errors for " + fileName, () => {
|
||||
Harness.Compiler.doErrorBaseline(justName, toBeCompiled.concat(otherFiles), result.errors);
|
||||
Harness.Compiler.doErrorBaseline(justName, tsConfigFiles.concat(toBeCompiled, otherFiles), result.errors);
|
||||
});
|
||||
|
||||
it (`Correct module resolution tracing for ${fileName}`, () => {
|
||||
@@ -165,7 +170,7 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
|
||||
it("Correct JS output for " + fileName, () => {
|
||||
if (hasNonDtsFiles && this.emit) {
|
||||
Harness.Compiler.doJsEmitBaseline(justName, fileName, options, result, toBeCompiled, otherFiles, harnessSettings);
|
||||
Harness.Compiler.doJsEmitBaseline(justName, fileName, options, result, tsConfigFiles, toBeCompiled, otherFiles, harnessSettings);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -183,6 +188,10 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
});
|
||||
}
|
||||
|
||||
private createHarnessTestFile(lastUnit: Harness.TestCaseParser.TestUnitData, rootDir: string, unitName?: string): Harness.Compiler.TestFile {
|
||||
return { unitName: unitName || this.makeUnitName(lastUnit.name, rootDir), content: lastUnit.content, fileOptions: lastUnit.fileOptions };
|
||||
}
|
||||
|
||||
public initializeTests() {
|
||||
describe(this.testSuiteName + " tests", () => {
|
||||
describe("Setup compiler for compiler baselines", () => {
|
||||
|
||||
+186
-148
@@ -130,7 +130,7 @@ namespace FourSlash {
|
||||
// 0 - cancelled
|
||||
// >0 - not cancelled
|
||||
// <0 - not cancelled and value denotes number of isCancellationRequested after which token become cancelled
|
||||
private static NotCanceled: number = -1;
|
||||
private static readonly NotCanceled: number = -1;
|
||||
private numberOfCallsBeforeCancellation: number = TestCancellationToken.NotCanceled;
|
||||
|
||||
public isCancellationRequested(): boolean {
|
||||
@@ -219,7 +219,7 @@ namespace FourSlash {
|
||||
|
||||
// Add input file which has matched file name with the given reference-file path.
|
||||
// This is necessary when resolveReference flag is specified
|
||||
private addMatchedInputFile(referenceFilePath: string, extensions: string[]) {
|
||||
private addMatchedInputFile(referenceFilePath: string, extensions: ReadonlyArray<string>) {
|
||||
const inputFiles = this.inputFiles;
|
||||
const languageServiceAdapterHost = this.languageServiceAdapterHost;
|
||||
if (!extensions) {
|
||||
@@ -536,11 +536,16 @@ namespace FourSlash {
|
||||
Harness.IO.log("Unexpected error(s) found. Error list is:");
|
||||
}
|
||||
|
||||
for (const { start, length, messageText } of errors) {
|
||||
Harness.IO.log(" minChar: " + start +
|
||||
", limChar: " + (start + length) +
|
||||
for (const { start, length, messageText, file } of errors) {
|
||||
Harness.IO.log(" from: " + showPosition(file, start) +
|
||||
", to: " + showPosition(file, start + length) +
|
||||
", message: " + ts.flattenDiagnosticMessageText(messageText, Harness.IO.newLine()) + "\n");
|
||||
}
|
||||
|
||||
function showPosition(file: ts.SourceFile, pos: number) {
|
||||
const { line, character } = ts.getLineAndCharacterOfPosition(file, pos);
|
||||
return `${line}:${character}`;
|
||||
}
|
||||
}
|
||||
|
||||
public verifyNoErrors() {
|
||||
@@ -600,7 +605,7 @@ namespace FourSlash {
|
||||
this.verifyGoToXPlain(arg0, endMarkerNames, getDefs);
|
||||
}
|
||||
else if (ts.isArray(arg0)) {
|
||||
const pairs: [string | string[], string | string[]][] = arg0;
|
||||
const pairs: ReadonlyArray<[string | string[], string | string[]]> = arg0;
|
||||
for (const [start, end] of pairs) {
|
||||
this.verifyGoToXPlain(start, end, getDefs);
|
||||
}
|
||||
@@ -689,7 +694,7 @@ namespace FourSlash {
|
||||
|
||||
public verifyCompletionListItemsCountIsGreaterThan(count: number, negative: boolean) {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
const itemsCount = completions.entries.length;
|
||||
const itemsCount = completions ? completions.entries.length : 0;
|
||||
|
||||
if (negative) {
|
||||
if (itemsCount > count) {
|
||||
@@ -809,8 +814,8 @@ namespace FourSlash {
|
||||
|
||||
function filterByTextOrDocumentation(entry: ts.CompletionEntry) {
|
||||
const details = that.getCompletionEntryDetails(entry.name);
|
||||
const documentation = ts.displayPartsToString(details.documentation);
|
||||
const text = ts.displayPartsToString(details.displayParts);
|
||||
const documentation = details && ts.displayPartsToString(details.documentation);
|
||||
const text = details && ts.displayPartsToString(details.displayParts);
|
||||
|
||||
// If any of the expected values are undefined, assume that users don't
|
||||
// care about them.
|
||||
@@ -847,6 +852,9 @@ namespace FourSlash {
|
||||
if (expectedKind) {
|
||||
error += "Expected kind: " + expectedKind + " to equal: " + filterCompletions[0].kind + ".";
|
||||
}
|
||||
else {
|
||||
error += "kind: " + filterCompletions[0].kind + ".";
|
||||
}
|
||||
if (replacementSpan) {
|
||||
const spanText = filterCompletions[0].replacementSpan ? stringify(filterCompletions[0].replacementSpan) : undefined;
|
||||
error += "Expected replacement span: " + stringify(replacementSpan) + " to equal: " + spanText + ".";
|
||||
@@ -941,6 +949,22 @@ namespace FourSlash {
|
||||
this.verifySymbol(symbol, declarationRanges);
|
||||
}
|
||||
|
||||
public symbolsInScope(range: Range): ts.Symbol[] {
|
||||
const node = this.goToAndGetNode(range);
|
||||
return this.getChecker().getSymbolsInScope(node, ts.SymbolFlags.Value | ts.SymbolFlags.Type | ts.SymbolFlags.Namespace);
|
||||
}
|
||||
|
||||
public verifyTypeOfSymbolAtLocation(range: Range, symbol: ts.Symbol, expected: string): void {
|
||||
const node = this.goToAndGetNode(range);
|
||||
const checker = this.getChecker();
|
||||
const type = checker.getTypeOfSymbolAtLocation(symbol, node);
|
||||
|
||||
const actual = checker.typeToString(type);
|
||||
if (actual !== expected) {
|
||||
this.raiseError(`Expected: '${expected}', actual: '${actual}'`);
|
||||
}
|
||||
}
|
||||
|
||||
private verifyReferencesAre(expectedReferences: Range[]) {
|
||||
const actualReferences = this.getReferencesAtCaret() || [];
|
||||
|
||||
@@ -1036,21 +1060,27 @@ namespace FourSlash {
|
||||
fail(`Expected ${expected}, got ${actual}`);
|
||||
}
|
||||
|
||||
for (const key in actual) if (ts.hasProperty(actual as any, key)) {
|
||||
const ak = actual[key], ek = expected[key];
|
||||
if (typeof ak === "object" && typeof ek === "object") {
|
||||
recur(ak, ek, path ? path + "." + key : key);
|
||||
}
|
||||
else if (ak !== ek) {
|
||||
fail(`Expected '${key}' to be '${ek}', got '${ak}'`);
|
||||
for (const key in actual) {
|
||||
if (ts.hasProperty(actual as any, key)) {
|
||||
const ak = actual[key], ek = expected[key];
|
||||
if (typeof ak === "object" && typeof ek === "object") {
|
||||
recur(ak, ek, path ? path + "." + key : key);
|
||||
}
|
||||
else if (ak !== ek) {
|
||||
fail(`Expected '${key}' to be '${ek}', got '${ak}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const key in expected) if (ts.hasProperty(expected as any, key)) {
|
||||
if (!ts.hasProperty(actual as any, key)) {
|
||||
fail(`${msgPrefix}Missing property '${key}'`);
|
||||
|
||||
for (const key in expected) {
|
||||
if (ts.hasProperty(expected as any, key)) {
|
||||
if (!ts.hasProperty(actual as any, key)) {
|
||||
fail(`${msgPrefix}Missing property '${key}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (fullActual === undefined || fullExpected === undefined) {
|
||||
if (fullActual === fullExpected) {
|
||||
return;
|
||||
@@ -1132,15 +1162,17 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
public verifyQuickInfos(namesAndTexts: { [name: string]: string | [string, string] }) {
|
||||
for (const name in namesAndTexts) if (ts.hasProperty(namesAndTexts, name)) {
|
||||
const text = namesAndTexts[name];
|
||||
if (ts.isArray(text)) {
|
||||
assert(text.length === 2);
|
||||
const [expectedText, expectedDocumentation] = text;
|
||||
this.verifyQuickInfoAt(name, expectedText, expectedDocumentation);
|
||||
}
|
||||
else {
|
||||
this.verifyQuickInfoAt(name, text);
|
||||
for (const name in namesAndTexts) {
|
||||
if (ts.hasProperty(namesAndTexts, name)) {
|
||||
const text = namesAndTexts[name];
|
||||
if (ts.isArray(text)) {
|
||||
assert(text.length === 2);
|
||||
const [expectedText, expectedDocumentation] = text;
|
||||
this.verifyQuickInfoAt(name, expectedText, expectedDocumentation);
|
||||
}
|
||||
else {
|
||||
this.verifyQuickInfoAt(name, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1149,7 +1181,6 @@ namespace FourSlash {
|
||||
if (expectedDocumentation === "") {
|
||||
throw new Error("Use 'undefined' instead");
|
||||
}
|
||||
|
||||
const actualQuickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
const actualQuickInfoText = actualQuickInfo ? ts.displayPartsToString(actualQuickInfo.displayParts) : "";
|
||||
const actualQuickInfoDocumentation = actualQuickInfo ? ts.displayPartsToString(actualQuickInfo.documentation) : "";
|
||||
@@ -1452,7 +1483,7 @@ namespace FourSlash {
|
||||
let baselineFile = this.testData.globalOptions[metadataOptionNames.baselineFile];
|
||||
if (!baselineFile) {
|
||||
baselineFile = this.activeFile.fileName.replace(this.basePath + "/breakpointValidation", "bpSpan");
|
||||
baselineFile = baselineFile.replace(".ts", ".baseline");
|
||||
baselineFile = baselineFile.replace(ts.Extension.Ts, ".baseline");
|
||||
|
||||
}
|
||||
Harness.Baseline.runBaseline(
|
||||
@@ -1522,7 +1553,7 @@ namespace FourSlash {
|
||||
public baselineQuickInfo() {
|
||||
let baselineFile = this.testData.globalOptions[metadataOptionNames.baselineFile];
|
||||
if (!baselineFile) {
|
||||
baselineFile = ts.getBaseFileName(this.activeFile.fileName).replace(".ts", ".baseline");
|
||||
baselineFile = ts.getBaseFileName(this.activeFile.fileName).replace(ts.Extension.Ts, ".baseline");
|
||||
}
|
||||
|
||||
Harness.Baseline.runBaseline(
|
||||
@@ -1569,7 +1600,7 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public printCurrentFileState(makeWhitespaceVisible = false, makeCaretVisible = true) {
|
||||
public printCurrentFileState(makeWhitespaceVisible: boolean, makeCaretVisible: boolean) {
|
||||
for (const file of this.testData.files) {
|
||||
const active = (this.activeFile === file);
|
||||
Harness.IO.log(`=== Script (${file.fileName}) ${(active ? "(active, cursor at |)" : "")} ===`);
|
||||
@@ -1595,16 +1626,19 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
private printMembersOrCompletions(info: ts.CompletionInfo) {
|
||||
if (info === undefined) { return "No completion info."; }
|
||||
const { entries } = info;
|
||||
|
||||
function pad(s: string, length: number) {
|
||||
return s + new Array(length - s.length + 1).join(" ");
|
||||
}
|
||||
function max<T>(arr: T[], selector: (x: T) => number): number {
|
||||
return arr.reduce((prev, x) => Math.max(prev, selector(x)), 0);
|
||||
}
|
||||
const longestNameLength = max(info.entries, m => m.name.length);
|
||||
const longestKindLength = max(info.entries, m => m.kind.length);
|
||||
info.entries.sort((m, n) => m.sortText > n.sortText ? 1 : m.sortText < n.sortText ? -1 : m.name > n.name ? 1 : m.name < n.name ? -1 : 0);
|
||||
const membersString = info.entries.map(m => `${pad(m.name, longestNameLength)} ${pad(m.kind, longestKindLength)} ${m.kindModifiers}`).join("\n");
|
||||
const longestNameLength = max(entries, m => m.name.length);
|
||||
const longestKindLength = max(entries, m => m.kind.length);
|
||||
entries.sort((m, n) => m.sortText > n.sortText ? 1 : m.sortText < n.sortText ? -1 : m.name > n.name ? 1 : m.name < n.name ? -1 : 0);
|
||||
const membersString = entries.map(m => `${pad(m.name, longestNameLength)} ${pad(m.kind, longestKindLength)} ${m.kindModifiers}`).join("\n");
|
||||
Harness.IO.log(membersString);
|
||||
}
|
||||
|
||||
@@ -1619,9 +1653,7 @@ namespace FourSlash {
|
||||
const checkCadence = (count >> 2) + 1;
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
// Make the edit
|
||||
this.languageServiceAdapterHost.editScript(this.activeFile.fileName, offset, offset + 1, ch);
|
||||
this.updateMarkersForEdit(this.activeFile.fileName, offset, offset + 1, ch);
|
||||
this.editScriptAndUpdateMarkers(this.activeFile.fileName, offset, offset + 1, ch);
|
||||
|
||||
if (i % checkCadence === 0) {
|
||||
this.checkPostEditInvariants();
|
||||
@@ -1632,21 +1664,15 @@ namespace FourSlash {
|
||||
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings);
|
||||
if (edits.length) {
|
||||
offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
// this.checkPostEditInvariants();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move the caret to wherever we ended up
|
||||
this.currentCaretPosition = offset;
|
||||
|
||||
this.fixCaretPosition();
|
||||
this.checkPostEditInvariants();
|
||||
}
|
||||
|
||||
public replace(start: number, length: number, text: string) {
|
||||
this.languageServiceAdapterHost.editScript(this.activeFile.fileName, start, start + length, text);
|
||||
this.updateMarkersForEdit(this.activeFile.fileName, start, start + length, text);
|
||||
this.editScriptAndUpdateMarkers(this.activeFile.fileName, start, start + length, text);
|
||||
this.checkPostEditInvariants();
|
||||
}
|
||||
|
||||
@@ -1656,28 +1682,17 @@ namespace FourSlash {
|
||||
const checkCadence = (count >> 2) + 1;
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
this.currentCaretPosition--;
|
||||
offset--;
|
||||
// Make the edit
|
||||
this.languageServiceAdapterHost.editScript(this.activeFile.fileName, offset, offset + 1, ch);
|
||||
this.updateMarkersForEdit(this.activeFile.fileName, offset, offset + 1, ch);
|
||||
this.editScriptAndUpdateMarkers(this.activeFile.fileName, offset, offset + 1, ch);
|
||||
|
||||
if (i % checkCadence === 0) {
|
||||
this.checkPostEditInvariants();
|
||||
}
|
||||
|
||||
// Handle post-keystroke formatting
|
||||
if (this.enableFormatting) {
|
||||
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings);
|
||||
if (edits.length) {
|
||||
offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
}
|
||||
}
|
||||
// Don't need to examine formatting because there are no formatting changes on backspace.
|
||||
}
|
||||
|
||||
// Move the caret to wherever we ended up
|
||||
this.currentCaretPosition = offset;
|
||||
|
||||
this.fixCaretPosition();
|
||||
this.checkPostEditInvariants();
|
||||
}
|
||||
|
||||
@@ -1688,14 +1703,13 @@ namespace FourSlash {
|
||||
const checkCadence = (text.length >> 2) + 1;
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
// Make the edit
|
||||
const ch = text.charAt(i);
|
||||
this.languageServiceAdapterHost.editScript(this.activeFile.fileName, offset, offset, ch);
|
||||
this.editScriptAndUpdateMarkers(this.activeFile.fileName, offset, offset, ch);
|
||||
if (highFidelity) {
|
||||
this.languageService.getBraceMatchingAtPosition(this.activeFile.fileName, offset);
|
||||
}
|
||||
|
||||
this.updateMarkersForEdit(this.activeFile.fileName, offset, offset, ch);
|
||||
this.currentCaretPosition++;
|
||||
offset++;
|
||||
|
||||
if (highFidelity) {
|
||||
@@ -1722,32 +1736,24 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
// Move the caret to wherever we ended up
|
||||
this.currentCaretPosition = offset;
|
||||
this.fixCaretPosition();
|
||||
this.checkPostEditInvariants();
|
||||
}
|
||||
|
||||
// Enters text as if the user had pasted it
|
||||
public paste(text: string) {
|
||||
const start = this.currentCaretPosition;
|
||||
let offset = this.currentCaretPosition;
|
||||
this.languageServiceAdapterHost.editScript(this.activeFile.fileName, offset, offset, text);
|
||||
this.updateMarkersForEdit(this.activeFile.fileName, offset, offset, text);
|
||||
this.editScriptAndUpdateMarkers(this.activeFile.fileName, this.currentCaretPosition, this.currentCaretPosition, text);
|
||||
this.checkPostEditInvariants();
|
||||
offset += text.length;
|
||||
const offset = this.currentCaretPosition += text.length;
|
||||
|
||||
// Handle formatting
|
||||
if (this.enableFormatting) {
|
||||
const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, offset, this.formatCodeSettings);
|
||||
if (edits.length) {
|
||||
offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
// Move the caret to wherever we ended up
|
||||
this.currentCaretPosition = offset;
|
||||
this.fixCaretPosition();
|
||||
|
||||
this.checkPostEditInvariants();
|
||||
}
|
||||
@@ -1775,30 +1781,41 @@ namespace FourSlash {
|
||||
Utils.assertStructuralEquals(incrementalSourceFile, referenceSourceFile);
|
||||
}
|
||||
|
||||
private fixCaretPosition() {
|
||||
// The caret can potentially end up between the \r and \n, which is confusing. If
|
||||
// that happens, move it back one character
|
||||
if (this.currentCaretPosition > 0) {
|
||||
const ch = this.getFileContent(this.activeFile.fileName).substring(this.currentCaretPosition - 1, this.currentCaretPosition);
|
||||
if (ch === "\r") {
|
||||
this.currentCaretPosition--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private applyEdits(fileName: string, edits: ts.TextChange[], isFormattingEdit = false): number {
|
||||
/**
|
||||
* @returns The number of characters added to the file as a result of the edits.
|
||||
* May be negative.
|
||||
*/
|
||||
private applyEdits(fileName: string, edits: ts.TextChange[], isFormattingEdit: boolean): number {
|
||||
// We get back a set of edits, but langSvc.editScript only accepts one at a time. Use this to keep track
|
||||
// of the incremental offset from each edit to the next. Assumption is that these edit ranges don't overlap
|
||||
let runningOffset = 0;
|
||||
// of the incremental offset from each edit to the next. We assume these edit ranges don't overlap
|
||||
|
||||
edits = edits.sort((a, b) => a.span.start - b.span.start);
|
||||
for (let i = 0; i < edits.length - 1; i++) {
|
||||
const firstEditSpan = edits[i].span;
|
||||
const firstEditEnd = firstEditSpan.start + firstEditSpan.length;
|
||||
assert.isTrue(firstEditEnd <= edits[i + 1].span.start);
|
||||
}
|
||||
|
||||
// Get a snapshot of the content of the file so we can make sure any formatting edits didn't destroy non-whitespace characters
|
||||
const oldContent = this.getFileContent(fileName);
|
||||
let runningOffset = 0;
|
||||
|
||||
for (const edit of edits) {
|
||||
this.languageServiceAdapterHost.editScript(fileName, edit.span.start + runningOffset, ts.textSpanEnd(edit.span) + runningOffset, edit.newText);
|
||||
this.updateMarkersForEdit(fileName, edit.span.start + runningOffset, ts.textSpanEnd(edit.span) + runningOffset, edit.newText);
|
||||
const change = (edit.span.start - ts.textSpanEnd(edit.span)) + edit.newText.length;
|
||||
runningOffset += change;
|
||||
const offsetStart = edit.span.start + runningOffset;
|
||||
const offsetEnd = offsetStart + edit.span.length;
|
||||
this.editScriptAndUpdateMarkers(fileName, offsetStart, offsetEnd, edit.newText);
|
||||
const editDelta = edit.newText.length - edit.span.length;
|
||||
if (offsetStart <= this.currentCaretPosition) {
|
||||
if (offsetEnd <= this.currentCaretPosition) {
|
||||
// The entirety of the edit span falls before the caret position, shift the caret accordingly
|
||||
this.currentCaretPosition += editDelta;
|
||||
}
|
||||
else {
|
||||
// The span being replaced includes the caret position, place the caret at the beginning of the span
|
||||
this.currentCaretPosition = offsetStart;
|
||||
}
|
||||
}
|
||||
runningOffset += editDelta;
|
||||
// TODO: Consider doing this at least some of the time for higher fidelity. Currently causes a failure (bug 707150)
|
||||
// this.languageService.getScriptLexicalStructure(fileName);
|
||||
}
|
||||
@@ -1810,6 +1827,7 @@ namespace FourSlash {
|
||||
this.raiseError("Formatting operation destroyed non-whitespace content");
|
||||
}
|
||||
}
|
||||
|
||||
return runningOffset;
|
||||
}
|
||||
|
||||
@@ -1825,23 +1843,21 @@ namespace FourSlash {
|
||||
|
||||
public formatDocument() {
|
||||
const edits = this.languageService.getFormattingEditsForDocument(this.activeFile.fileName, this.formatCodeSettings);
|
||||
this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
this.fixCaretPosition();
|
||||
this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
}
|
||||
|
||||
public formatSelection(start: number, end: number) {
|
||||
const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, end, this.formatCodeSettings);
|
||||
this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
this.fixCaretPosition();
|
||||
this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
}
|
||||
|
||||
public formatOnType(pos: number, key: string) {
|
||||
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, pos, key, this.formatCodeSettings);
|
||||
this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
this.fixCaretPosition();
|
||||
this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
}
|
||||
|
||||
private updateMarkersForEdit(fileName: string, minChar: number, limChar: number, text: string) {
|
||||
private editScriptAndUpdateMarkers(fileName: string, editStart: number, editEnd: number, newText: string) {
|
||||
this.languageServiceAdapterHost.editScript(fileName, editStart, editEnd, newText);
|
||||
for (const marker of this.testData.markers) {
|
||||
if (marker.fileName === fileName) {
|
||||
marker.position = updatePosition(marker.position);
|
||||
@@ -1856,14 +1872,14 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
function updatePosition(position: number) {
|
||||
if (position > minChar) {
|
||||
if (position < limChar) {
|
||||
if (position > editStart) {
|
||||
if (position < editEnd) {
|
||||
// Inside the edit - mark it as invalidated (?)
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
// Move marker back/forward by the appropriate amount
|
||||
return position + (minChar - limChar) + text.length;
|
||||
return position + (editStart - editEnd) + newText.length;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -2109,8 +2125,8 @@ namespace FourSlash {
|
||||
const actual = this.getFileContent(this.activeFile.fileName);
|
||||
if (normalizeNewLines(actual) !== normalizeNewLines(text)) {
|
||||
throw new Error("verifyCurrentFileContent\n" +
|
||||
"\tExpected: \"" + text + "\"\n" +
|
||||
"\t Actual: \"" + actual + "\"");
|
||||
"\tExpected: \"" + TestState.makeWhitespaceVisible(text) + "\"\n" +
|
||||
"\t Actual: \"" + TestState.makeWhitespaceVisible(actual) + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2156,7 +2172,7 @@ namespace FourSlash {
|
||||
Harness.IO.log(this.spanInfoToString(this.getNameOrDottedNameSpan(pos), "**"));
|
||||
}
|
||||
|
||||
private verifyClassifications(expected: { classificationType: string; text: string; textSpan?: TextSpan }[], actual: ts.ClassifiedSpan[]) {
|
||||
private verifyClassifications(expected: { classificationType: string; text: string; textSpan?: TextSpan }[], actual: ts.ClassifiedSpan[], sourceFileText: string) {
|
||||
if (actual.length !== expected.length) {
|
||||
this.raiseError("verifyClassifications failed - expected total classifications to be " + expected.length +
|
||||
", but was " + actual.length +
|
||||
@@ -2196,9 +2212,11 @@ namespace FourSlash {
|
||||
});
|
||||
|
||||
function jsonMismatchString() {
|
||||
const showActual = actual.map(({ classificationType, textSpan }) =>
|
||||
({ classificationType, text: sourceFileText.slice(textSpan.start, textSpan.start + textSpan.length) }));
|
||||
return Harness.IO.newLine() +
|
||||
"expected: '" + Harness.IO.newLine() + stringify(expected) + "'" + Harness.IO.newLine() +
|
||||
"actual: '" + Harness.IO.newLine() + stringify(actual) + "'";
|
||||
"actual: '" + Harness.IO.newLine() + stringify(showActual) + "'";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2221,14 +2239,14 @@ namespace FourSlash {
|
||||
const actual = this.languageService.getSemanticClassifications(this.activeFile.fileName,
|
||||
ts.createTextSpan(0, this.activeFile.content.length));
|
||||
|
||||
this.verifyClassifications(expected, actual);
|
||||
this.verifyClassifications(expected, actual, this.activeFile.content);
|
||||
}
|
||||
|
||||
public verifySyntacticClassifications(expected: { classificationType: string; text: string }[]) {
|
||||
const actual = this.languageService.getSyntacticClassifications(this.activeFile.fileName,
|
||||
ts.createTextSpan(0, this.activeFile.content.length));
|
||||
|
||||
this.verifyClassifications(expected, actual);
|
||||
this.verifyClassifications(expected, actual, this.activeFile.content);
|
||||
}
|
||||
|
||||
public verifyOutliningSpans(spans: TextSpan[]) {
|
||||
@@ -2325,35 +2343,25 @@ namespace FourSlash {
|
||||
* @param fileName Path to file where error should be retrieved from.
|
||||
*/
|
||||
private getCodeFixActions(fileName: string, errorCode?: number): ts.CodeAction[] {
|
||||
const diagnosticsForCodeFix = this.getDiagnostics(fileName).map(diagnostic => {
|
||||
return {
|
||||
start: diagnostic.start,
|
||||
length: diagnostic.length,
|
||||
code: diagnostic.code
|
||||
};
|
||||
});
|
||||
const dedupedDiagnositcs = ts.deduplicate(diagnosticsForCodeFix, ts.equalOwnProperties);
|
||||
|
||||
let actions: ts.CodeAction[] = undefined;
|
||||
|
||||
for (const diagnostic of dedupedDiagnositcs) {
|
||||
const diagnosticsForCodeFix = this.getDiagnostics(fileName).map(diagnostic => ({
|
||||
start: diagnostic.start,
|
||||
length: diagnostic.length,
|
||||
code: diagnostic.code
|
||||
}));
|
||||
|
||||
return ts.flatMap(ts.deduplicate(diagnosticsForCodeFix, ts.equalOwnProperties), diagnostic => {
|
||||
if (errorCode && errorCode !== diagnostic.code) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
const newActions = this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.length, [diagnostic.code], this.formatCodeSettings);
|
||||
if (newActions && newActions.length) {
|
||||
actions = actions ? actions.concat(newActions) : newActions;
|
||||
}
|
||||
}
|
||||
return actions;
|
||||
return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings);
|
||||
});
|
||||
}
|
||||
|
||||
private applyCodeActions(actions: ts.CodeAction[], index?: number): void {
|
||||
if (index === undefined) {
|
||||
if (!(actions && actions.length === 1)) {
|
||||
this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found.`);
|
||||
this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `${Harness.IO.newLine()} "${a.description}"`) : "" }`);
|
||||
}
|
||||
index = 0;
|
||||
}
|
||||
@@ -2378,7 +2386,7 @@ namespace FourSlash {
|
||||
|
||||
const codeFixes = this.getCodeFixActions(this.activeFile.fileName, errorCode);
|
||||
|
||||
if (!codeFixes || codeFixes.length === 0) {
|
||||
if (codeFixes.length === 0) {
|
||||
if (expectedTextArray.length !== 0) {
|
||||
this.raiseError("No codefixes returned.");
|
||||
}
|
||||
@@ -2397,7 +2405,7 @@ namespace FourSlash {
|
||||
const sortedActualArray = actualTextArray.sort();
|
||||
if (!ts.arrayIsEqualTo(sortedExpectedArray, sortedActualArray)) {
|
||||
this.raiseError(
|
||||
`Actual text array doesn't match expected text array. \nActual: \n"${sortedActualArray.join("\n\n")}"\n---\nExpected: \n'${sortedExpectedArray.join("\n\n")}'`);
|
||||
`Actual text array doesn't match expected text array. \nActual: \n'${sortedActualArray.join("\n\n")}'\n---\nExpected: \n'${sortedExpectedArray.join("\n\n")}'`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2563,7 +2571,7 @@ namespace FourSlash {
|
||||
|
||||
// if there was an explicit match kind specified, then it should be validated.
|
||||
if (matchKind !== undefined) {
|
||||
const missingItem = { name: name, kind: kind, searchValue: searchValue, matchKind: matchKind, fileName: fileName, parentName: parentName };
|
||||
const missingItem = { name, kind, searchValue, matchKind, fileName, parentName };
|
||||
this.raiseError(`verifyNavigationItemsListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(items)})`);
|
||||
}
|
||||
}
|
||||
@@ -2637,7 +2645,7 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
const missingItem = { fileName: fileName, start: start, end: end, isWriteAccess: isWriteAccess };
|
||||
const missingItem = { fileName, start, end, isWriteAccess };
|
||||
this.raiseError(`verifyOccurrencesAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(occurrences)})`);
|
||||
}
|
||||
|
||||
@@ -2673,6 +2681,13 @@ namespace FourSlash {
|
||||
this.rangesByText().forEach(ranges => this.verifyRangesAreDocumentHighlights(ranges));
|
||||
}
|
||||
|
||||
public verifyDocumentHighlightsOf(startRange: Range, ranges: Range[]) {
|
||||
ts.Debug.assert(ts.contains(ranges, startRange));
|
||||
const fileNames = unique(ranges, range => range.fileName);
|
||||
this.goToRangeStart(startRange);
|
||||
this.verifyDocumentHighlights(ranges, fileNames);
|
||||
}
|
||||
|
||||
public verifyRangesAreDocumentHighlights(ranges?: Range[]) {
|
||||
ranges = ranges || this.getRanges();
|
||||
const fileNames = unique(ranges, range => range.fileName);
|
||||
@@ -2714,11 +2729,11 @@ namespace FourSlash {
|
||||
public verifyCodeFixAvailable(negative: boolean) {
|
||||
const codeFix = this.getCodeFixActions(this.activeFile.fileName);
|
||||
|
||||
if (negative && codeFix) {
|
||||
if (negative && codeFix.length) {
|
||||
this.raiseError(`verifyCodeFixAvailable failed - expected no fixes but found one.`);
|
||||
}
|
||||
|
||||
if (!(negative || codeFix)) {
|
||||
if (!(negative || codeFix.length)) {
|
||||
this.raiseError(`verifyCodeFixAvailable failed - expected code fixes but none found.`);
|
||||
}
|
||||
}
|
||||
@@ -2755,6 +2770,7 @@ namespace FourSlash {
|
||||
markerName: string,
|
||||
expectedContent: string,
|
||||
refactorNameToApply: string,
|
||||
actionName: string,
|
||||
formattingOptions?: ts.FormatCodeSettings) {
|
||||
|
||||
formattingOptions = formattingOptions || this.formatCodeSettings;
|
||||
@@ -2767,9 +2783,11 @@ namespace FourSlash {
|
||||
this.raiseError(`The expected refactor: ${refactorNameToApply} is not available at the marker location.`);
|
||||
}
|
||||
|
||||
const codeActions = this.languageService.getRefactorCodeActions(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply);
|
||||
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply, actionName);
|
||||
|
||||
this.applyCodeActions(codeActions);
|
||||
for (const edit of editInfo.edits) {
|
||||
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
|
||||
}
|
||||
const actualContent = this.getFileContent(this.activeFile.fileName);
|
||||
|
||||
if (this.normalizeNewlines(actualContent) !== this.normalizeNewlines(expectedContent)) {
|
||||
@@ -2963,7 +2981,6 @@ ${code}
|
||||
f(test, goTo, verify, edit, debug, format, cancellation, FourSlashInterface.Classification, FourSlash.verifyOperationIsCancelled);
|
||||
}
|
||||
catch (err) {
|
||||
// Debugging: FourSlash.currentTestState.printCurrentFileState();
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -3249,7 +3266,7 @@ ${code}
|
||||
}
|
||||
|
||||
const range: Range = {
|
||||
fileName: fileName,
|
||||
fileName,
|
||||
start: rangeStart.position,
|
||||
end: (i - 1) - difference,
|
||||
marker: rangeStart.marker
|
||||
@@ -3377,7 +3394,7 @@ ${code}
|
||||
content: output,
|
||||
fileOptions: {},
|
||||
version: 0,
|
||||
fileName: fileName
|
||||
fileName,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3436,6 +3453,10 @@ namespace FourSlashInterface {
|
||||
public markerByName(s: string): FourSlash.Marker {
|
||||
return this.state.getMarkerByName(s);
|
||||
}
|
||||
|
||||
public symbolsInScope(range: FourSlash.Range): ts.Symbol[] {
|
||||
return this.state.symbolsInScope(range);
|
||||
}
|
||||
}
|
||||
|
||||
export class GoTo {
|
||||
@@ -3505,6 +3526,12 @@ namespace FourSlashInterface {
|
||||
"constructor",
|
||||
"async"
|
||||
];
|
||||
public allowedConstructorParameterKeywords = [
|
||||
"public",
|
||||
"private",
|
||||
"protected",
|
||||
"readonly",
|
||||
];
|
||||
|
||||
constructor(protected state: FourSlash.TestState, private negative = false) {
|
||||
if (!negative) {
|
||||
@@ -3547,6 +3574,12 @@ namespace FourSlashInterface {
|
||||
}
|
||||
}
|
||||
|
||||
public completionListContainsConstructorParameterKeywords() {
|
||||
for (const keyword of this.allowedConstructorParameterKeywords) {
|
||||
this.completionListContains(keyword, keyword, /*documentation*/ undefined, "keyword");
|
||||
}
|
||||
}
|
||||
|
||||
public completionListIsGlobal(expected: boolean) {
|
||||
this.state.verifyCompletionListIsGlobal(expected);
|
||||
}
|
||||
@@ -3696,6 +3729,10 @@ namespace FourSlashInterface {
|
||||
this.state.verifySymbolAtLocation(startRange, declarationRanges);
|
||||
}
|
||||
|
||||
public typeOfSymbolAtLocation(range: FourSlash.Range, symbol: ts.Symbol, expected: string) {
|
||||
this.state.verifyTypeOfSymbolAtLocation(range, symbol, expected);
|
||||
}
|
||||
|
||||
public referencesOf(start: FourSlash.Range, references: FourSlash.Range[]) {
|
||||
this.state.verifyReferencesOf(start, references);
|
||||
}
|
||||
@@ -3816,8 +3853,8 @@ namespace FourSlashInterface {
|
||||
this.state.verifyRangeAfterCodeFix(expectedText, includeWhiteSpace, errorCode, index);
|
||||
}
|
||||
|
||||
public fileAfterApplyingRefactorAtMarker(markerName: string, expectedContent: string, refactorNameToApply: string, formattingOptions?: ts.FormatCodeSettings): void {
|
||||
this.state.verifyFileAfterApplyingRefactorAtMarker(markerName, expectedContent, refactorNameToApply, formattingOptions);
|
||||
public fileAfterApplyingRefactorAtMarker(markerName: string, expectedContent: string, refactorNameToApply: string, actionName: string, formattingOptions?: ts.FormatCodeSettings): void {
|
||||
this.state.verifyFileAfterApplyingRefactorAtMarker(markerName, expectedContent, refactorNameToApply, actionName, formattingOptions);
|
||||
}
|
||||
|
||||
public rangeIs(expectedText: string, includeWhiteSpace?: boolean): void {
|
||||
@@ -3888,6 +3925,10 @@ namespace FourSlashInterface {
|
||||
this.state.verifyRangesWithSameTextAreDocumentHighlights();
|
||||
}
|
||||
|
||||
public documentHighlightsOf(startRange: FourSlash.Range, ranges: FourSlash.Range[]) {
|
||||
this.state.verifyDocumentHighlightsOf(startRange, ranges);
|
||||
}
|
||||
|
||||
public completionEntryDetailIs(entryName: string, text: string, documentation?: string, kind?: string, tags?: ts.JSDocTagInfo[]) {
|
||||
this.state.verifyCompletionEntryDetails(entryName, text, documentation, kind, tags);
|
||||
}
|
||||
@@ -4000,11 +4041,11 @@ namespace FourSlashInterface {
|
||||
}
|
||||
|
||||
public printCurrentFileState() {
|
||||
this.state.printCurrentFileState();
|
||||
this.state.printCurrentFileState(/*makeWhitespaceVisible*/ false, /*makeCaretVisible*/ true);
|
||||
}
|
||||
|
||||
public printCurrentFileStateWithWhitespace() {
|
||||
this.state.printCurrentFileState(/*makeWhitespaceVisible*/ true);
|
||||
this.state.printCurrentFileState(/*makeWhitespaceVisible*/ true, /*makeCaretVisible*/ true);
|
||||
}
|
||||
|
||||
public printCurrentFileStateWithoutCaret() {
|
||||
@@ -4196,11 +4237,8 @@ namespace FourSlashInterface {
|
||||
}
|
||||
|
||||
function getClassification(classificationType: ts.ClassificationTypeNames, text: string, position?: number): Classification {
|
||||
return {
|
||||
classificationType,
|
||||
text: text,
|
||||
textSpan: position === undefined ? undefined : { start: position, end: position + text.length }
|
||||
};
|
||||
const textSpan = position === undefined ? undefined : { start: position, end: position + text.length };
|
||||
return { classificationType, text, textSpan };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+40
-32
@@ -259,8 +259,9 @@ namespace Utils {
|
||||
return true;
|
||||
}
|
||||
else if ((f & v) > 0) {
|
||||
if (result.length)
|
||||
if (result.length) {
|
||||
result += " | ";
|
||||
}
|
||||
result += flags[v];
|
||||
return false;
|
||||
}
|
||||
@@ -478,7 +479,7 @@ namespace Harness {
|
||||
getCurrentDirectory(): string;
|
||||
useCaseSensitiveFileNames(): boolean;
|
||||
resolvePath(path: string): string;
|
||||
readFile(path: string): string;
|
||||
readFile(path: string): string | undefined;
|
||||
writeFile(path: string, contents: string): void;
|
||||
directoryName(path: string): string;
|
||||
getDirectories(path: string): string[];
|
||||
@@ -492,7 +493,7 @@ namespace Harness {
|
||||
args(): string[];
|
||||
getExecutingFilePath(): string;
|
||||
exit(exitCode?: number): void;
|
||||
readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]): string[];
|
||||
readDirectory(path: string, extension?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
tryEnableSourceMapsForHost?(): void;
|
||||
getEnvironmentVariable?(name: string): string;
|
||||
}
|
||||
@@ -536,7 +537,7 @@ namespace Harness {
|
||||
ts.sys.tryEnableSourceMapsForHost();
|
||||
}
|
||||
}
|
||||
export const readDirectory: typeof IO.readDirectory = (path, extension, exclude, include) => ts.sys.readDirectory(path, extension, exclude, include);
|
||||
export const readDirectory: typeof IO.readDirectory = (path, extension, exclude, include, depth) => ts.sys.readDirectory(path, extension, exclude, include, depth);
|
||||
|
||||
export function createDirectory(path: string) {
|
||||
if (!directoryExists(path)) {
|
||||
@@ -718,7 +719,7 @@ namespace Harness {
|
||||
}
|
||||
});
|
||||
|
||||
export function readFile(file: string) {
|
||||
export function readFile(file: string): string | undefined {
|
||||
const response = Http.getFileFromServerSync(serverRoot + file);
|
||||
if (response.status === 200) {
|
||||
return response.responseText;
|
||||
@@ -732,12 +733,12 @@ namespace Harness {
|
||||
Http.writeToServerSync(serverRoot + path, "WRITE", contents);
|
||||
}
|
||||
|
||||
export function readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]) {
|
||||
export function readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[], depth?: number) {
|
||||
const fs = new Utils.VirtualFileSystem(path, useCaseSensitiveFileNames());
|
||||
for (const file of listFiles(path)) {
|
||||
fs.addFile(file);
|
||||
}
|
||||
return ts.matchFiles(path, extension, exclude, include, useCaseSensitiveFileNames(), getCurrentDirectory(), path => {
|
||||
return ts.matchFiles(path, extension, exclude, include, useCaseSensitiveFileNames(), getCurrentDirectory(), depth, path => {
|
||||
const entry = fs.traversePath(path);
|
||||
if (entry && entry.isDirectory()) {
|
||||
const directory = <Utils.VirtualDirectory>entry;
|
||||
@@ -892,13 +893,13 @@ namespace Harness {
|
||||
const getCanonicalFileName = ts.createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
|
||||
/** Maps a symlink name to a realpath. Used only for exposing `realpath`. */
|
||||
const realPathMap = ts.createFileMap<string>();
|
||||
const realPathMap = ts.createMap<string>();
|
||||
/**
|
||||
* Maps a file name to a source file.
|
||||
* This will have a different SourceFile for every symlink pointing to that file;
|
||||
* if the program resolves realpaths then symlink entries will be ignored.
|
||||
*/
|
||||
const fileMap = ts.createFileMap<ts.SourceFile>();
|
||||
const fileMap = ts.createMap<ts.SourceFile>();
|
||||
for (const file of inputFiles) {
|
||||
if (file.content !== undefined) {
|
||||
const fileName = ts.normalizePath(file.unitName);
|
||||
@@ -974,8 +975,8 @@ namespace Harness {
|
||||
getCanonicalFileName,
|
||||
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
|
||||
getNewLine: () => newLine,
|
||||
fileExists: fileName => fileMap.contains(toPath(fileName)),
|
||||
readFile: (fileName: string): string => {
|
||||
fileExists: fileName => fileMap.has(toPath(fileName)),
|
||||
readFile(fileName: string): string | undefined {
|
||||
const file = fileMap.get(toPath(fileName));
|
||||
if (ts.endsWith(fileName, "json")) {
|
||||
// strip comments
|
||||
@@ -998,7 +999,7 @@ namespace Harness {
|
||||
getDirectories: d => {
|
||||
const path = ts.toPath(d, currentDirectory, getCanonicalFileName);
|
||||
const result: string[] = [];
|
||||
fileMap.forEachValue(key => {
|
||||
ts.forEachKey(fileMap, key => {
|
||||
if (key.indexOf(path) === 0 && key.lastIndexOf("/") > path.length) {
|
||||
let dirName = key.substr(path.length, key.indexOf("/", path.length + 1) - path.length);
|
||||
if (dirName[0] === "/") {
|
||||
@@ -1014,12 +1015,12 @@ namespace Harness {
|
||||
};
|
||||
}
|
||||
|
||||
function mapHasFileInDirectory(directoryPath: ts.Path, map: ts.FileMap<any>): boolean {
|
||||
function mapHasFileInDirectory(directoryPath: ts.Path, map: ts.Map<{}>): boolean {
|
||||
if (!map) {
|
||||
return false;
|
||||
}
|
||||
let exists = false;
|
||||
map.forEachValue(fileName => {
|
||||
ts.forEachKey(map, fileName => {
|
||||
if (!exists && ts.startsWith(fileName, directoryPath) && fileName[directoryPath.length] === "/") {
|
||||
exists = true;
|
||||
}
|
||||
@@ -1052,7 +1053,7 @@ namespace Harness {
|
||||
];
|
||||
|
||||
let optionsIndex: ts.Map<ts.CommandLineOption>;
|
||||
function getCommandLineOption(name: string): ts.CommandLineOption {
|
||||
function getCommandLineOption(name: string): ts.CommandLineOption | undefined {
|
||||
if (!optionsIndex) {
|
||||
optionsIndex = ts.createMap<ts.CommandLineOption>();
|
||||
const optionDeclarations = harnessOptionDeclarations.concat(ts.optionDeclarations);
|
||||
@@ -1124,7 +1125,7 @@ namespace Harness {
|
||||
compilerOptions: ts.CompilerOptions,
|
||||
// Current directory is needed for rwcRunner to be able to use currentDirectory defined in json file
|
||||
currentDirectory: string): CompilationOutput {
|
||||
const options: ts.CompilerOptions & HarnessOptions = compilerOptions ? ts.clone(compilerOptions) : { noResolve: false };
|
||||
const options: ts.CompilerOptions & HarnessOptions = compilerOptions ? ts.cloneCompilerOptions(compilerOptions) : { noResolve: false };
|
||||
options.target = options.target || ts.ScriptTarget.ES3;
|
||||
options.newLine = options.newLine || ts.NewLineKind.CarriageReturnLineFeed;
|
||||
options.noErrorTruncation = true;
|
||||
@@ -1248,7 +1249,7 @@ namespace Harness {
|
||||
sourceFileName = outFile;
|
||||
}
|
||||
|
||||
const dTsFileName = ts.removeFileExtension(sourceFileName) + ".d.ts";
|
||||
const dTsFileName = ts.removeFileExtension(sourceFileName) + ts.Extension.Dts;
|
||||
|
||||
return ts.forEach(result.declFilesCode, declFile => declFile.fileName === dTsFileName ? declFile : undefined);
|
||||
}
|
||||
@@ -1464,7 +1465,7 @@ namespace Harness {
|
||||
// When calling this function from rwc-runner, the baselinePath will have no extension.
|
||||
// As rwc test- file is stored in json which ".json" will get stripped off.
|
||||
// When calling this function from compiler-runner, the baselinePath will then has either ".ts" or ".tsx" extension
|
||||
const outputFileName = ts.endsWith(baselinePath, ".ts") || ts.endsWith(baselinePath, ".tsx") ?
|
||||
const outputFileName = ts.endsWith(baselinePath, ts.Extension.Ts) || ts.endsWith(baselinePath, ts.Extension.Tsx) ?
|
||||
baselinePath.replace(/\.tsx?/, fullExtension) : baselinePath.concat(fullExtension);
|
||||
Harness.Baseline.runBaseline(outputFileName, () => fullBaseLine, opts);
|
||||
}
|
||||
@@ -1556,13 +1557,13 @@ namespace Harness {
|
||||
}
|
||||
}
|
||||
|
||||
export function doJsEmitBaseline(baselinePath: string, header: string, options: ts.CompilerOptions, result: CompilerResult, toBeCompiled: Harness.Compiler.TestFile[], otherFiles: Harness.Compiler.TestFile[], harnessSettings: Harness.TestCaseParser.CompilerSettings) {
|
||||
export function doJsEmitBaseline(baselinePath: string, header: string, options: ts.CompilerOptions, result: CompilerResult, tsConfigFiles: Harness.Compiler.TestFile[], toBeCompiled: Harness.Compiler.TestFile[], otherFiles: Harness.Compiler.TestFile[], harnessSettings: Harness.TestCaseParser.CompilerSettings) {
|
||||
if (!options.noEmit && result.files.length === 0 && result.errors.length === 0) {
|
||||
throw new Error("Expected at least one js file to be emitted or at least one error to be created.");
|
||||
}
|
||||
|
||||
// check js output
|
||||
Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?/, ".js"), () => {
|
||||
Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?/, ts.Extension.Js), () => {
|
||||
let tsCode = "";
|
||||
const tsSources = otherFiles.concat(toBeCompiled);
|
||||
if (tsSources.length > 1) {
|
||||
@@ -1592,7 +1593,7 @@ namespace Harness {
|
||||
if (declFileCompilationResult && declFileCompilationResult.declResult.errors.length) {
|
||||
jsCode += "\r\n\r\n//// [DtsFileErrors]\r\n";
|
||||
jsCode += "\r\n\r\n";
|
||||
jsCode += Harness.Compiler.getErrorBaseline(declFileCompilationResult.declInputFiles.concat(declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.errors);
|
||||
jsCode += Harness.Compiler.getErrorBaseline(tsConfigFiles.concat(declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.errors);
|
||||
}
|
||||
|
||||
if (jsCode.length > 0) {
|
||||
@@ -1650,22 +1651,22 @@ namespace Harness {
|
||||
}
|
||||
|
||||
export function isTS(fileName: string) {
|
||||
return ts.endsWith(fileName, ".ts");
|
||||
return ts.endsWith(fileName, ts.Extension.Ts);
|
||||
}
|
||||
|
||||
export function isTSX(fileName: string) {
|
||||
return ts.endsWith(fileName, ".tsx");
|
||||
return ts.endsWith(fileName, ts.Extension.Tsx);
|
||||
}
|
||||
|
||||
export function isDTS(fileName: string) {
|
||||
return ts.endsWith(fileName, ".d.ts");
|
||||
return ts.endsWith(fileName, ts.Extension.Dts);
|
||||
}
|
||||
|
||||
export function isJS(fileName: string) {
|
||||
return ts.endsWith(fileName, ".js");
|
||||
return ts.endsWith(fileName, ts.Extension.Js);
|
||||
}
|
||||
export function isJSX(fileName: string) {
|
||||
return ts.endsWith(fileName, ".jsx");
|
||||
return ts.endsWith(fileName, ts.Extension.Jsx);
|
||||
}
|
||||
|
||||
export function isJSMap(fileName: string) {
|
||||
@@ -1743,7 +1744,12 @@ namespace Harness {
|
||||
}
|
||||
|
||||
/** Given a test file containing // @FileName directives, return an array of named units of code to be added to an existing compiler instance */
|
||||
export function makeUnitsFromTest(code: string, fileName: string, rootDir?: string): { settings: CompilerSettings; testUnitData: TestUnitData[]; tsConfig: ts.ParsedCommandLine } {
|
||||
export function makeUnitsFromTest(code: string, fileName: string, rootDir?: string): {
|
||||
settings: CompilerSettings;
|
||||
testUnitData: TestUnitData[];
|
||||
tsConfig: ts.ParsedCommandLine;
|
||||
tsConfigFileUnitData: TestUnitData;
|
||||
} {
|
||||
const settings = extractCompilerSettings(code);
|
||||
|
||||
// List of all the subfiles we've parsed out
|
||||
@@ -1829,17 +1835,19 @@ namespace Harness {
|
||||
|
||||
// check if project has tsconfig.json in the list of files
|
||||
let tsConfig: ts.ParsedCommandLine;
|
||||
let tsConfigFileUnitData: TestUnitData;
|
||||
for (let i = 0; i < testUnitData.length; i++) {
|
||||
const data = testUnitData[i];
|
||||
if (ts.getBaseFileName(data.name).toLowerCase() === "tsconfig.json") {
|
||||
const configJson = ts.parseConfigFileTextToJson(data.name, data.content);
|
||||
assert.isTrue(configJson.config !== undefined);
|
||||
const configJson = ts.parseJsonText(data.name, data.content);
|
||||
assert.isTrue(configJson.endOfFileToken !== undefined);
|
||||
let baseDir = ts.normalizePath(ts.getDirectoryPath(data.name));
|
||||
if (rootDir) {
|
||||
baseDir = ts.getNormalizedAbsolutePath(baseDir, rootDir);
|
||||
}
|
||||
tsConfig = ts.parseJsonConfigFileContent(configJson.config, parseConfigHost, baseDir);
|
||||
tsConfig = ts.parseJsonSourceFileConfigFileContent(configJson, parseConfigHost, baseDir);
|
||||
tsConfig.options.configFilePath = data.name;
|
||||
tsConfigFileUnitData = data;
|
||||
|
||||
// delete entry from the list
|
||||
ts.orderedRemoveItemAt(testUnitData, i);
|
||||
@@ -1847,7 +1855,7 @@ namespace Harness {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { settings, testUnitData, tsConfig };
|
||||
return { settings, testUnitData, tsConfig, tsConfigFileUnitData };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1972,7 +1980,7 @@ namespace Harness {
|
||||
export function isDefaultLibraryFile(filePath: string): boolean {
|
||||
// We need to make sure that the filePath is prefixed with "lib." not just containing "lib." and end with ".d.ts"
|
||||
const fileName = ts.getBaseFileName(ts.normalizeSlashes(filePath));
|
||||
return ts.startsWith(fileName, "lib.") && ts.endsWith(fileName, ".d.ts");
|
||||
return ts.startsWith(fileName, "lib.") && ts.endsWith(fileName, ts.Extension.Dts);
|
||||
}
|
||||
|
||||
export function isBuiltFile(filePath: string): boolean {
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace Harness.LanguageService {
|
||||
}
|
||||
|
||||
class DefaultHostCancellationToken implements ts.HostCancellationToken {
|
||||
public static Instance = new DefaultHostCancellationToken();
|
||||
public static readonly Instance = new DefaultHostCancellationToken();
|
||||
|
||||
public isCancellationRequested() {
|
||||
return false;
|
||||
@@ -208,13 +208,14 @@ namespace Harness.LanguageService {
|
||||
const script = this.getScriptSnapshot(fileName);
|
||||
return script !== undefined;
|
||||
}
|
||||
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
|
||||
return ts.matchFiles(path, extensions, exclude, include,
|
||||
/*useCaseSensitiveFileNames*/ false,
|
||||
this.getCurrentDirectory(),
|
||||
depth,
|
||||
(p) => this.virtualFileSystem.getAccessibleFileSystemEntries(p));
|
||||
}
|
||||
readFile(path: string): string {
|
||||
readFile(path: string): string | undefined {
|
||||
const snapshot = this.getScriptSnapshot(path);
|
||||
return snapshot.getText(0, snapshot.getLength());
|
||||
}
|
||||
@@ -312,9 +313,7 @@ namespace Harness.LanguageService {
|
||||
getScriptVersion(fileName: string): string { return this.nativeHost.getScriptVersion(fileName); }
|
||||
getLocalizedDiagnosticMessages(): string { return JSON.stringify({}); }
|
||||
|
||||
readDirectory(_rootDir: string, _extension: string): string {
|
||||
return ts.notImplemented();
|
||||
}
|
||||
readDirectory = ts.notImplemented;
|
||||
readDirectoryNames = ts.notImplemented;
|
||||
readFileNames = ts.notImplemented;
|
||||
fileExists(fileName: string) { return this.getScriptInfo(fileName) !== undefined; }
|
||||
@@ -495,7 +494,7 @@ namespace Harness.LanguageService {
|
||||
getCodeFixDiagnostics(): ts.Diagnostic[] {
|
||||
throw new Error("Not supported on the shim.");
|
||||
}
|
||||
getRefactorCodeActions(): ts.CodeAction[] {
|
||||
getEditsForRefactor(): ts.RefactorEditInfo {
|
||||
throw new Error("Not supported on the shim.");
|
||||
}
|
||||
getApplicableRefactors(): ts.ApplicableRefactorInfo[] {
|
||||
@@ -623,7 +622,7 @@ namespace Harness.LanguageService {
|
||||
this.writeMessage(message);
|
||||
}
|
||||
|
||||
readFile(fileName: string): string {
|
||||
readFile(fileName: string): string | undefined {
|
||||
if (fileName.indexOf(Harness.Compiler.defaultLibFileName) >= 0) {
|
||||
fileName = Harness.Compiler.defaultLibFileName;
|
||||
}
|
||||
@@ -671,9 +670,7 @@ namespace Harness.LanguageService {
|
||||
return ts.sys.getEnvironmentVariable(name);
|
||||
}
|
||||
|
||||
readDirectory(_path: string, _extension?: string[], _exclude?: string[], _include?: string[]): string[] {
|
||||
return ts.notImplemented();
|
||||
}
|
||||
readDirectory() { return ts.notImplemented(); }
|
||||
|
||||
watchFile(): ts.FileWatcher {
|
||||
return { close: ts.noop };
|
||||
@@ -734,7 +731,7 @@ namespace Harness.LanguageService {
|
||||
}
|
||||
|
||||
createHash(s: string) {
|
||||
return s;
|
||||
return mockHash(s);
|
||||
}
|
||||
|
||||
require(_initialDir: string, _moduleName: string): ts.server.RequireResult {
|
||||
@@ -859,4 +856,8 @@ namespace Harness.LanguageService {
|
||||
getClassifier(): ts.Classifier { throw new Error("getClassifier is not available using the server interface."); }
|
||||
getPreProcessedFileInfo(): ts.PreProcessedFileInfo { throw new Error("getPreProcessedFileInfo is not available using the server interface."); }
|
||||
}
|
||||
|
||||
export function mockHash(s: string): string {
|
||||
return `hash-${s}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,10 +64,11 @@ interface IOLog {
|
||||
}[];
|
||||
directoriesRead: {
|
||||
path: string,
|
||||
extensions: string[],
|
||||
exclude: string[],
|
||||
include: string[],
|
||||
result: string[]
|
||||
extensions: ReadonlyArray<string>,
|
||||
exclude: ReadonlyArray<string>,
|
||||
include: ReadonlyArray<string>,
|
||||
depth: number,
|
||||
result: ReadonlyArray<string>,
|
||||
}[];
|
||||
}
|
||||
|
||||
@@ -220,10 +221,9 @@ namespace Playback {
|
||||
memoize(path => findFileByPath(replayLog.filesRead, path, /*throwFileNotFoundError*/ true).contents));
|
||||
|
||||
wrapper.readDirectory = recordReplay(wrapper.readDirectory, underlying)(
|
||||
(path, extensions, exclude, include) => {
|
||||
const result = (<ts.System>underlying).readDirectory(path, extensions, exclude, include);
|
||||
const logEntry = { path, extensions, exclude, include, result };
|
||||
recordLog.directoriesRead.push(logEntry);
|
||||
(path, extensions, exclude, include, depth) => {
|
||||
const result = (<ts.System>underlying).readDirectory(path, extensions, exclude, include, depth);
|
||||
recordLog.directoriesRead.push({ path, extensions, exclude, include, depth, result });
|
||||
return result;
|
||||
},
|
||||
path => {
|
||||
|
||||
@@ -24,6 +24,7 @@ interface BatchCompileProjectTestCaseEmittedFile extends Harness.Compiler.Genera
|
||||
}
|
||||
|
||||
interface CompileProjectFilesResult {
|
||||
configFileSourceFiles: ts.SourceFile[];
|
||||
moduleKind: ts.ModuleKind;
|
||||
program?: ts.Program;
|
||||
compilerOptions?: ts.CompilerOptions;
|
||||
@@ -124,7 +125,8 @@ class ProjectRunner extends RunnerBase {
|
||||
return Harness.IO.resolvePath(testCase.projectRoot);
|
||||
}
|
||||
|
||||
function compileProjectFiles(moduleKind: ts.ModuleKind, getInputFiles: () => string[],
|
||||
function compileProjectFiles(moduleKind: ts.ModuleKind, configFileSourceFiles: ts.SourceFile[],
|
||||
getInputFiles: () => string[],
|
||||
getSourceFileTextImpl: (fileName: string) => string,
|
||||
writeFile: (fileName: string, data: string, writeByteOrderMark: boolean) => void,
|
||||
compilerOptions: ts.CompilerOptions): CompileProjectFilesResult {
|
||||
@@ -148,6 +150,7 @@ class ProjectRunner extends RunnerBase {
|
||||
}
|
||||
|
||||
return {
|
||||
configFileSourceFiles,
|
||||
moduleKind,
|
||||
program,
|
||||
errors,
|
||||
@@ -196,6 +199,7 @@ class ProjectRunner extends RunnerBase {
|
||||
const outputFiles: BatchCompileProjectTestCaseEmittedFile[] = [];
|
||||
let inputFiles = testCase.inputFiles;
|
||||
let compilerOptions = createCompilerOptions();
|
||||
const configFileSourceFiles: ts.SourceFile[] = [];
|
||||
|
||||
let configFileName: string;
|
||||
if (compilerOptions.project) {
|
||||
@@ -207,41 +211,31 @@ class ProjectRunner extends RunnerBase {
|
||||
configFileName = ts.findConfigFile("", fileExists);
|
||||
}
|
||||
|
||||
let errors: ts.Diagnostic[];
|
||||
if (configFileName) {
|
||||
const result = ts.readConfigFile(configFileName, getSourceFileText);
|
||||
if (result.error) {
|
||||
return {
|
||||
moduleKind,
|
||||
errors: [result.error]
|
||||
};
|
||||
}
|
||||
|
||||
const configObject = result.config;
|
||||
const result = ts.readJsonConfigFile(configFileName, getSourceFileText);
|
||||
configFileSourceFiles.push(result);
|
||||
const configParseHost: ts.ParseConfigHost = {
|
||||
useCaseSensitiveFileNames: Harness.IO.useCaseSensitiveFileNames(),
|
||||
fileExists,
|
||||
readDirectory,
|
||||
readFile
|
||||
};
|
||||
const configParseResult = ts.parseJsonConfigFileContent(configObject, configParseHost, ts.getDirectoryPath(configFileName), compilerOptions);
|
||||
if (configParseResult.errors.length > 0) {
|
||||
return {
|
||||
moduleKind,
|
||||
errors: configParseResult.errors
|
||||
};
|
||||
}
|
||||
const configParseResult = ts.parseJsonSourceFileConfigFileContent(result, configParseHost, ts.getDirectoryPath(configFileName), compilerOptions);
|
||||
inputFiles = configParseResult.fileNames;
|
||||
compilerOptions = configParseResult.options;
|
||||
errors = result.parseDiagnostics.concat(configParseResult.errors);
|
||||
}
|
||||
|
||||
const projectCompilerResult = compileProjectFiles(moduleKind, () => inputFiles, getSourceFileText, writeFile, compilerOptions);
|
||||
const projectCompilerResult = compileProjectFiles(moduleKind, configFileSourceFiles, () => inputFiles, getSourceFileText, writeFile, compilerOptions);
|
||||
return {
|
||||
configFileSourceFiles,
|
||||
moduleKind,
|
||||
program: projectCompilerResult.program,
|
||||
compilerOptions,
|
||||
sourceMapData: projectCompilerResult.sourceMapData,
|
||||
outputFiles,
|
||||
errors: projectCompilerResult.errors,
|
||||
errors: errors ? errors.concat(projectCompilerResult.errors) : projectCompilerResult.errors,
|
||||
};
|
||||
|
||||
function createCompilerOptions() {
|
||||
@@ -281,8 +275,8 @@ class ProjectRunner extends RunnerBase {
|
||||
: ts.normalizeSlashes(testCase.projectRoot) + "/" + ts.normalizeSlashes(fileName);
|
||||
}
|
||||
|
||||
function readDirectory(rootDir: string, extension: string[], exclude: string[], include: string[]): string[] {
|
||||
const harnessReadDirectoryResult = Harness.IO.readDirectory(getFileNameInTheProjectTest(rootDir), extension, exclude, include);
|
||||
function readDirectory(rootDir: string, extension: string[], exclude: string[], include: string[], depth: number): string[] {
|
||||
const harnessReadDirectoryResult = Harness.IO.readDirectory(getFileNameInTheProjectTest(rootDir), extension, exclude, include, depth);
|
||||
const result: string[] = [];
|
||||
for (let i = 0; i < harnessReadDirectoryResult.length; i++) {
|
||||
result[i] = ts.getRelativePathToDirectoryOrUrl(testCase.projectRoot, harnessReadDirectoryResult[i],
|
||||
@@ -295,7 +289,7 @@ class ProjectRunner extends RunnerBase {
|
||||
return Harness.IO.fileExists(getFileNameInTheProjectTest(fileName));
|
||||
}
|
||||
|
||||
function readFile(fileName: string): string {
|
||||
function readFile(fileName: string): string | undefined {
|
||||
return Harness.IO.readFile(getFileNameInTheProjectTest(fileName));
|
||||
}
|
||||
|
||||
@@ -325,8 +319,8 @@ class ProjectRunner extends RunnerBase {
|
||||
// we need to instead create files that can live in the project reference folder
|
||||
// but make sure extension of these files matches with the fileName the compiler asked to write
|
||||
diskRelativeName = "diskFile" + nonSubfolderDiskFiles +
|
||||
(Harness.Compiler.isDTS(fileName) ? ".d.ts" :
|
||||
Harness.Compiler.isJS(fileName) ? ".js" : ".js.map");
|
||||
(Harness.Compiler.isDTS(fileName) ? ts.Extension.Dts :
|
||||
Harness.Compiler.isJS(fileName) ? ts.Extension.Js : ".js.map");
|
||||
nonSubfolderDiskFiles++;
|
||||
}
|
||||
|
||||
@@ -360,7 +354,7 @@ class ProjectRunner extends RunnerBase {
|
||||
ensureDirectoryStructure(ts.getDirectoryPath(ts.normalizePath(outputFilePath)));
|
||||
Harness.IO.writeFile(outputFilePath, data);
|
||||
|
||||
outputFiles.push({ emittedFileName: fileName, code: data, fileName: diskRelativeName, writeByteOrderMark: writeByteOrderMark });
|
||||
outputFiles.push({ emittedFileName: fileName, code: data, fileName: diskRelativeName, writeByteOrderMark });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,14 +380,14 @@ class ProjectRunner extends RunnerBase {
|
||||
emitOutputFilePathWithoutExtension = ts.removeFileExtension(sourceFile.fileName);
|
||||
}
|
||||
|
||||
const outputDtsFileName = emitOutputFilePathWithoutExtension + ".d.ts";
|
||||
const outputDtsFileName = emitOutputFilePathWithoutExtension + ts.Extension.Dts;
|
||||
const file = findOutputDtsFile(outputDtsFileName);
|
||||
if (file) {
|
||||
allInputFiles.unshift(file);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const outputDtsFileName = ts.removeFileExtension(compilerOptions.outFile || compilerOptions.out) + ".d.ts";
|
||||
const outputDtsFileName = ts.removeFileExtension(compilerOptions.outFile || compilerOptions.out) + ts.Extension.Dts;
|
||||
const outputDtsFile = findOutputDtsFile(outputDtsFileName);
|
||||
if (!ts.contains(allInputFiles, outputDtsFile)) {
|
||||
allInputFiles.unshift(outputDtsFile);
|
||||
@@ -402,7 +396,7 @@ class ProjectRunner extends RunnerBase {
|
||||
});
|
||||
|
||||
// Dont allow config files since we are compiling existing source options
|
||||
return compileProjectFiles(compilerResult.moduleKind, getInputFiles, getSourceFileText, writeFile, compilerResult.compilerOptions);
|
||||
return compileProjectFiles(compilerResult.moduleKind, compilerResult.configFileSourceFiles, getInputFiles, getSourceFileText, writeFile, compilerResult.compilerOptions);
|
||||
|
||||
function findOutputDtsFile(fileName: string) {
|
||||
return ts.forEach(compilerResult.outputFiles, outputFile => outputFile.emittedFileName === fileName ? outputFile : undefined);
|
||||
@@ -428,16 +422,16 @@ class ProjectRunner extends RunnerBase {
|
||||
}
|
||||
|
||||
function getErrorsBaseline(compilerResult: CompileProjectFilesResult) {
|
||||
const inputFiles = compilerResult.program ? ts.map(ts.filter(compilerResult.program.getSourceFiles(),
|
||||
sourceFile => !Harness.isDefaultLibraryFile(sourceFile.fileName)),
|
||||
sourceFile => {
|
||||
return {
|
||||
unitName: ts.isRootedDiskPath(sourceFile.fileName) ?
|
||||
RunnerBase.removeFullPaths(sourceFile.fileName) :
|
||||
sourceFile.fileName,
|
||||
content: sourceFile.text
|
||||
};
|
||||
}) : [];
|
||||
const inputFiles = ts.map(compilerResult.configFileSourceFiles.concat(
|
||||
compilerResult.program ?
|
||||
ts.filter(compilerResult.program.getSourceFiles(), sourceFile => !Harness.isDefaultLibraryFile(sourceFile.fileName)) :
|
||||
[]),
|
||||
sourceFile => <Harness.Compiler.TestFile>{
|
||||
unitName: ts.isRootedDiskPath(sourceFile.fileName) ?
|
||||
RunnerBase.removeFullPaths(sourceFile.fileName) :
|
||||
sourceFile.fileName,
|
||||
content: sourceFile.text
|
||||
});
|
||||
|
||||
return Harness.Compiler.getErrorBaseline(inputFiles, compilerResult.errors);
|
||||
}
|
||||
|
||||
@@ -222,6 +222,10 @@ if (taskConfigsFolder) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ts.Debug.isDebugging) {
|
||||
ts.Debug.enableDebugInfo();
|
||||
}
|
||||
|
||||
runTests(runners);
|
||||
}
|
||||
if (!runUnitTests) {
|
||||
|
||||
+18
-16
@@ -22,14 +22,14 @@ namespace RWC {
|
||||
}
|
||||
|
||||
function isTsConfigFile(file: { path: string }): boolean {
|
||||
const tsConfigFileName = "tsconfig.json";
|
||||
return file.path.substr(file.path.length - tsConfigFileName.length).toLowerCase() === tsConfigFileName;
|
||||
return file.path.indexOf("tsconfig") !== -1 && file.path.indexOf("json") !== -1;
|
||||
}
|
||||
|
||||
export function runRWCTest(jsonPath: string) {
|
||||
describe("Testing a RWC project: " + jsonPath, () => {
|
||||
let inputFiles: Harness.Compiler.TestFile[] = [];
|
||||
let otherFiles: Harness.Compiler.TestFile[] = [];
|
||||
let tsconfigFiles: Harness.Compiler.TestFile[] = [];
|
||||
let compilerResult: Harness.Compiler.CompilerResult;
|
||||
let compilerOptions: ts.CompilerOptions;
|
||||
const baselineOpts: Harness.Baseline.BaselineOptions = {
|
||||
@@ -44,6 +44,7 @@ namespace RWC {
|
||||
// Therefore we have to clean out large objects after the test is done.
|
||||
inputFiles = [];
|
||||
otherFiles = [];
|
||||
tsconfigFiles = [];
|
||||
compilerResult = undefined;
|
||||
compilerOptions = undefined;
|
||||
currentDirectory = undefined;
|
||||
@@ -53,7 +54,8 @@ namespace RWC {
|
||||
useCustomLibraryFile = undefined;
|
||||
});
|
||||
|
||||
it("can compile", () => {
|
||||
it("can compile", function(this: Mocha.ITestCallbackContext) {
|
||||
this.timeout(800000); // Allow long timeouts for RWC compilations
|
||||
let opts: ts.ParsedCommandLine;
|
||||
|
||||
const ioLog: IOLog = JSON.parse(Harness.IO.readFile(jsonPath));
|
||||
@@ -74,16 +76,18 @@ namespace RWC {
|
||||
const tsconfigFile = ts.forEach(ioLog.filesRead, f => isTsConfigFile(f) ? f : undefined);
|
||||
if (tsconfigFile) {
|
||||
const tsconfigFileContents = getHarnessCompilerInputUnit(tsconfigFile.path);
|
||||
const parsedTsconfigFileContents = ts.parseConfigFileTextToJson(tsconfigFile.path, tsconfigFileContents.content);
|
||||
tsconfigFiles.push({ unitName: tsconfigFile.path, content: tsconfigFileContents.content });
|
||||
const parsedTsconfigFileContents = ts.parseJsonText(tsconfigFile.path, tsconfigFileContents.content);
|
||||
const configParseHost: ts.ParseConfigHost = {
|
||||
useCaseSensitiveFileNames: Harness.IO.useCaseSensitiveFileNames(),
|
||||
fileExists: Harness.IO.fileExists,
|
||||
readDirectory: Harness.IO.readDirectory,
|
||||
readFile: Harness.IO.readFile
|
||||
};
|
||||
const configParseResult = ts.parseJsonConfigFileContent(parsedTsconfigFileContents.config, configParseHost, ts.getDirectoryPath(tsconfigFile.path));
|
||||
const configParseResult = ts.parseJsonSourceFileConfigFileContent(parsedTsconfigFileContents, configParseHost, ts.getDirectoryPath(tsconfigFile.path));
|
||||
fileNames = configParseResult.fileNames;
|
||||
opts.options = ts.extend(opts.options, configParseResult.options);
|
||||
ts.setConfigFileInOptions(opts.options, configParseResult.options.configFile);
|
||||
}
|
||||
|
||||
// Load the files
|
||||
@@ -198,8 +202,8 @@ namespace RWC {
|
||||
return null;
|
||||
}
|
||||
// Do not include the library in the baselines to avoid noise
|
||||
const baselineFiles = inputFiles.concat(otherFiles).filter(f => !Harness.isDefaultLibraryFile(f.unitName));
|
||||
const errors = compilerResult.errors.filter(e => e.file && !Harness.isDefaultLibraryFile(e.file.fileName));
|
||||
const baselineFiles = tsconfigFiles.concat(inputFiles, otherFiles).filter(f => !Harness.isDefaultLibraryFile(f.unitName));
|
||||
const errors = compilerResult.errors.filter(e => !e.file || !Harness.isDefaultLibraryFile(e.file.fileName));
|
||||
return Harness.Compiler.getErrorBaseline(baselineFiles, errors);
|
||||
}, baselineOpts);
|
||||
});
|
||||
@@ -209,16 +213,16 @@ namespace RWC {
|
||||
it("has the expected errors in generated declaration files", () => {
|
||||
if (compilerOptions.declaration && !compilerResult.errors.length) {
|
||||
Harness.Baseline.runBaseline(`${baseName}.dts.errors.txt`, () => {
|
||||
const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(
|
||||
inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined, compilerOptions, currentDirectory);
|
||||
|
||||
if (declFileCompilationResult.declResult.errors.length === 0) {
|
||||
if (compilerResult.errors.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(
|
||||
inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined, compilerOptions, currentDirectory);
|
||||
|
||||
return Harness.Compiler.minimalDiagnosticsToString(declFileCompilationResult.declResult.errors) +
|
||||
Harness.IO.newLine() + Harness.IO.newLine() +
|
||||
Harness.Compiler.getErrorBaseline(declFileCompilationResult.declInputFiles.concat(declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.errors);
|
||||
Harness.Compiler.getErrorBaseline(tsconfigFiles.concat(declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.errors);
|
||||
}, baselineOpts);
|
||||
}
|
||||
});
|
||||
@@ -235,10 +239,8 @@ namespace RWC {
|
||||
}
|
||||
|
||||
class RWCRunner extends RunnerBase {
|
||||
private static sourcePath = "internal/cases/rwc/";
|
||||
|
||||
public enumerateTestFiles() {
|
||||
return Harness.IO.listFiles(RWCRunner.sourcePath, /.+\.json$/);
|
||||
return Harness.IO.listFiles("internal/cases/rwc/", /.+\.json$/);
|
||||
}
|
||||
|
||||
public kind(): TestRunnerKind {
|
||||
@@ -259,4 +261,4 @@ class RWCRunner extends RunnerBase {
|
||||
private runTest(jsonFileName: string) {
|
||||
RWC.runRWCTest(jsonFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ namespace Harness.SourceMapRecorder {
|
||||
export function recordSourceMapSpan(sourceMapSpan: ts.SourceMapSpan) {
|
||||
// verify the decoded span is same as the new span
|
||||
const decodeResult = SourceMapDecoder.decodeNextEncodedSourceMapSpan();
|
||||
let decodedErrors: string[];
|
||||
let decodeErrors: string[];
|
||||
if (decodeResult.error
|
||||
|| decodeResult.sourceMapSpan.emittedLine !== sourceMapSpan.emittedLine
|
||||
|| decodeResult.sourceMapSpan.emittedColumn !== sourceMapSpan.emittedColumn
|
||||
@@ -268,22 +268,20 @@ namespace Harness.SourceMapRecorder {
|
||||
|| decodeResult.sourceMapSpan.sourceIndex !== sourceMapSpan.sourceIndex
|
||||
|| decodeResult.sourceMapSpan.nameIndex !== sourceMapSpan.nameIndex) {
|
||||
if (decodeResult.error) {
|
||||
decodedErrors = ["!!^^ !!^^ There was decoding error in the sourcemap at this location: " + decodeResult.error];
|
||||
decodeErrors = ["!!^^ !!^^ There was decoding error in the sourcemap at this location: " + decodeResult.error];
|
||||
}
|
||||
else {
|
||||
decodedErrors = ["!!^^ !!^^ The decoded span from sourcemap's mapping entry does not match what was encoded for this span:"];
|
||||
decodeErrors = ["!!^^ !!^^ The decoded span from sourcemap's mapping entry does not match what was encoded for this span:"];
|
||||
}
|
||||
decodedErrors.push("!!^^ !!^^ Decoded span from sourcemap's mappings entry: " + getSourceMapSpanString(decodeResult.sourceMapSpan, /*getAbsentNameIndex*/ true) + " Span encoded by the emitter:" + getSourceMapSpanString(sourceMapSpan, /*getAbsentNameIndex*/ true));
|
||||
decodeErrors.push("!!^^ !!^^ Decoded span from sourcemap's mappings entry: " + getSourceMapSpanString(decodeResult.sourceMapSpan, /*getAbsentNameIndex*/ true) + " Span encoded by the emitter:" + getSourceMapSpanString(sourceMapSpan, /*getAbsentNameIndex*/ true));
|
||||
}
|
||||
|
||||
if (spansOnSingleLine.length && spansOnSingleLine[0].sourceMapSpan.emittedLine !== sourceMapSpan.emittedLine) {
|
||||
// On different line from the one that we have been recording till now,
|
||||
writeRecordedSpans();
|
||||
spansOnSingleLine = [{ sourceMapSpan: sourceMapSpan, decodeErrors: decodedErrors }];
|
||||
}
|
||||
else {
|
||||
spansOnSingleLine.push({ sourceMapSpan: sourceMapSpan, decodeErrors: decodedErrors });
|
||||
spansOnSingleLine = [];
|
||||
}
|
||||
spansOnSingleLine.push({ sourceMapSpan, decodeErrors });
|
||||
}
|
||||
|
||||
export function recordNewSourceFileSpan(sourceMapSpan: ts.SourceMapSpan, newSourceFileCode: string) {
|
||||
|
||||
@@ -4,19 +4,19 @@
|
||||
/* tslint:disable:no-null-keyword */
|
||||
|
||||
class Test262BaselineRunner extends RunnerBase {
|
||||
private static basePath = "internal/cases/test262";
|
||||
private static helpersFilePath = "tests/cases/test262-harness/helpers.d.ts";
|
||||
private static helperFile: Harness.Compiler.TestFile = {
|
||||
private static readonly basePath = "internal/cases/test262";
|
||||
private static readonly helpersFilePath = "tests/cases/test262-harness/helpers.d.ts";
|
||||
private static readonly helperFile: Harness.Compiler.TestFile = {
|
||||
unitName: Test262BaselineRunner.helpersFilePath,
|
||||
content: Harness.IO.readFile(Test262BaselineRunner.helpersFilePath),
|
||||
};
|
||||
private static testFileExtensionRegex = /\.js$/;
|
||||
private static options: ts.CompilerOptions = {
|
||||
private static readonly testFileExtensionRegex = /\.js$/;
|
||||
private static readonly options: ts.CompilerOptions = {
|
||||
allowNonTsExtensions: true,
|
||||
target: ts.ScriptTarget.Latest,
|
||||
module: ts.ModuleKind.CommonJS
|
||||
};
|
||||
private static baselineOptions: Harness.Baseline.BaselineOptions = {
|
||||
private static readonly baselineOptions: Harness.Baseline.BaselineOptions = {
|
||||
Subfolder: "test262",
|
||||
Baselinefolder: "internal/baselines"
|
||||
};
|
||||
@@ -48,7 +48,7 @@ class Test262BaselineRunner extends RunnerBase {
|
||||
// Emit the results
|
||||
testState = {
|
||||
filename: testFilename,
|
||||
inputFiles: inputFiles,
|
||||
inputFiles,
|
||||
compilerResult: undefined,
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"../compiler/checker.ts",
|
||||
"../compiler/factory.ts",
|
||||
"../compiler/visitor.ts",
|
||||
"../compiler/transformers/utilities.ts",
|
||||
"../compiler/transformers/ts.ts",
|
||||
"../compiler/transformers/jsx.ts",
|
||||
"../compiler/transformers/esnext.ts",
|
||||
@@ -73,14 +74,9 @@
|
||||
"../services/formatting/tokenRange.ts",
|
||||
"../services/codeFixProvider.ts",
|
||||
"../services/codefixes/fixes.ts",
|
||||
"../services/codefixes/fixExtendsInterfaceBecomesImplements.ts",
|
||||
"../services/codefixes/fixClassIncorrectlyImplementsInterface.ts",
|
||||
"../services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts",
|
||||
"../services/codefixes/fixClassSuperMustPrecedeThisAccess.ts",
|
||||
"../services/codefixes/fixConstructorForDerivedNeedSuperCall.ts",
|
||||
"../services/codefixes/helpers.ts",
|
||||
"../services/codefixes/importFixes.ts",
|
||||
"../services/codefixes/unusedIdentifierFixes.ts",
|
||||
"../services/codefixes/fixUnusedIdentifier.ts",
|
||||
"../services/codefixes/disableJsDiagnostics.ts",
|
||||
|
||||
"harness.ts",
|
||||
@@ -128,6 +124,7 @@
|
||||
"./unittests/transform.ts",
|
||||
"./unittests/customTransforms.ts",
|
||||
"./unittests/textChanges.ts",
|
||||
"./unittests/telemetry.ts"
|
||||
"./unittests/telemetry.ts",
|
||||
"./unittests/programMissingFiles.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ class TypeWriterWalker {
|
||||
this.results.push({
|
||||
line: lineAndCharacter.line,
|
||||
syntaxKind: node.kind,
|
||||
sourceText: sourceText,
|
||||
sourceText,
|
||||
type: typeString,
|
||||
symbol: symbolString
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace ts {
|
||||
clearTimeout,
|
||||
setImmediate: typeof setImmediate !== "undefined" ? setImmediate : action => setTimeout(action, 0),
|
||||
clearImmediate: typeof clearImmediate !== "undefined" ? clearImmediate : clearTimeout,
|
||||
createHash: s => s
|
||||
createHash: Harness.LanguageService.mockHash,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace ts {
|
||||
const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */ true, /*containingProject*/ undefined);
|
||||
|
||||
const project = projectService.createInferredProjectWithRootFileIfNecessary(rootScriptInfo);
|
||||
project.setCompilerOptions({ module: ts.ModuleKind.AMD } );
|
||||
project.setCompilerOptions({ module: ts.ModuleKind.AMD, noLib: true } );
|
||||
return {
|
||||
project,
|
||||
rootScriptInfo
|
||||
@@ -158,7 +158,7 @@ namespace ts {
|
||||
// setting compiler options discards module resolution cache
|
||||
fileExistsCalled = false;
|
||||
|
||||
const compilerOptions = ts.clone(project.getCompilerOptions());
|
||||
const compilerOptions = ts.cloneCompilerOptions(project.getCompilerOptions());
|
||||
compilerOptions.target = ts.ScriptTarget.ES5;
|
||||
project.setCompilerOptions(compilerOptions);
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ namespace ts.projectSystem {
|
||||
|
||||
file1Consumer1.content = `let y = 10;`;
|
||||
host.reloadFS([moduleFile1, file1Consumer1, file1Consumer2, configFile, libFile]);
|
||||
host.triggerFileWatcherCallback(file1Consumer1.path, /*removed*/ false);
|
||||
host.triggerFileWatcherCallback(file1Consumer1.path, FileWatcherEventKind.Changed);
|
||||
|
||||
session.executeCommand(changeModuleFile1ShapeRequest1);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer2] }]);
|
||||
@@ -225,7 +225,7 @@ namespace ts.projectSystem {
|
||||
session.executeCommand(changeModuleFile1ShapeRequest1);
|
||||
// Delete file1Consumer2
|
||||
host.reloadFS([moduleFile1, file1Consumer1, configFile, libFile]);
|
||||
host.triggerFileWatcherCallback(file1Consumer2.path, /*removed*/ true);
|
||||
host.triggerFileWatcherCallback(file1Consumer2.path, FileWatcherEventKind.Deleted);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1] }]);
|
||||
});
|
||||
|
||||
@@ -475,7 +475,7 @@ namespace ts.projectSystem {
|
||||
|
||||
openFilesForSession([referenceFile1], session);
|
||||
host.reloadFS([referenceFile1, configFile]);
|
||||
host.triggerFileWatcherCallback(moduleFile1.path, /*removed*/ true);
|
||||
host.triggerFileWatcherCallback(moduleFile1.path, FileWatcherEventKind.Deleted);
|
||||
|
||||
const request = makeSessionRequest<server.protocol.FileRequestArgs>(CommandNames.CompileOnSaveAffectedFileList, { file: referenceFile1.path });
|
||||
sendAffectedFileRequestAndCheckResult(session, request, [
|
||||
@@ -513,7 +513,7 @@ namespace ts.projectSystem {
|
||||
const lines = ["var x = 1;", "var y = 2;"];
|
||||
const path = "/a/app";
|
||||
const f = {
|
||||
path: path + ".ts",
|
||||
path: path + ts.Extension.Ts,
|
||||
content: lines.join(newLine)
|
||||
};
|
||||
const host = createServerHost([f], { newLine });
|
||||
@@ -530,7 +530,7 @@ namespace ts.projectSystem {
|
||||
command: "compileOnSaveEmitFile",
|
||||
arguments: { file: f.path }
|
||||
});
|
||||
const emitOutput = host.readFile(path + ".js");
|
||||
const emitOutput = host.readFile(path + ts.Extension.Js);
|
||||
assert.equal(emitOutput, f.content + newLine, "content of emit output should be identical with the input + newline");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -111,23 +111,47 @@ namespace ts {
|
||||
["under a case insensitive host", caseInsensitiveBasePath, caseInsensitiveHost],
|
||||
["under a case sensitive host", caseSensitiveBasePath, caseSensitiveHost]
|
||||
], ([testName, basePath, host]) => {
|
||||
function getParseCommandLine(entry: string) {
|
||||
const {config, error} = ts.readConfigFile(entry, name => host.readFile(name));
|
||||
assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n"));
|
||||
return ts.parseJsonConfigFileContent(config, host, basePath, {}, entry);
|
||||
}
|
||||
|
||||
function getParseCommandLineJsonSourceFile(entry: string) {
|
||||
const jsonSourceFile = ts.readJsonConfigFile(entry, name => host.readFile(name));
|
||||
assert(jsonSourceFile.endOfFileToken && !jsonSourceFile.parseDiagnostics.length, flattenDiagnosticMessageText(jsonSourceFile.parseDiagnostics[0] && jsonSourceFile.parseDiagnostics[0].messageText, "\n"));
|
||||
return {
|
||||
jsonSourceFile,
|
||||
parsed: ts.parseJsonSourceFileConfigFileContent(jsonSourceFile, host, basePath, {}, entry)
|
||||
};
|
||||
}
|
||||
|
||||
function testSuccess(name: string, entry: string, expected: CompilerOptions, expectedFiles: string[]) {
|
||||
expected.configFilePath = entry;
|
||||
it(name, () => {
|
||||
const {config, error} = ts.readConfigFile(entry, name => host.readFile(name));
|
||||
assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n"));
|
||||
const parsed = ts.parseJsonConfigFileContent(config, host, basePath, {}, entry);
|
||||
const parsed = getParseCommandLine(entry);
|
||||
assert(!parsed.errors.length, flattenDiagnosticMessageText(parsed.errors[0] && parsed.errors[0].messageText, "\n"));
|
||||
expected.configFilePath = entry;
|
||||
assert.deepEqual(parsed.options, expected);
|
||||
assert.deepEqual(parsed.fileNames, expectedFiles);
|
||||
});
|
||||
|
||||
it(name + " with jsonSourceFile", () => {
|
||||
const { parsed, jsonSourceFile } = getParseCommandLineJsonSourceFile(entry);
|
||||
assert(!parsed.errors.length, flattenDiagnosticMessageText(parsed.errors[0] && parsed.errors[0].messageText, "\n"));
|
||||
assert.deepEqual(parsed.options, expected);
|
||||
assert.equal(parsed.options.configFile, jsonSourceFile);
|
||||
assert.deepEqual(parsed.fileNames, expectedFiles);
|
||||
});
|
||||
}
|
||||
|
||||
function testFailure(name: string, entry: string, expectedDiagnostics: {code: number, category: DiagnosticCategory, messageText: string}[]) {
|
||||
function testFailure(name: string, entry: string, expectedDiagnostics: { code: number, category: DiagnosticCategory, messageText: string }[]) {
|
||||
it(name, () => {
|
||||
const {config, error} = ts.readConfigFile(entry, name => host.readFile(name));
|
||||
assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n"));
|
||||
const parsed = ts.parseJsonConfigFileContent(config, host, basePath, {}, entry);
|
||||
const parsed = getParseCommandLine(entry);
|
||||
verifyDiagnostics(parsed.errors, expectedDiagnostics);
|
||||
});
|
||||
|
||||
it(name + " with jsonSourceFile", () => {
|
||||
const { parsed } = getParseCommandLineJsonSourceFile(entry);
|
||||
verifyDiagnostics(parsed.errors, expectedDiagnostics);
|
||||
});
|
||||
}
|
||||
@@ -185,4 +209,4 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
namespace ts {
|
||||
describe("convertCompilerOptionsFromJson", () => {
|
||||
function assertCompilerOptions(json: any, configFileName: string, expectedResult: { compilerOptions: CompilerOptions, errors: Diagnostic[] }) {
|
||||
assertCompilerOptionsWithJson(json, configFileName, expectedResult);
|
||||
assertCompilerOptionsWithJsonNode(json, configFileName, expectedResult);
|
||||
}
|
||||
|
||||
function assertCompilerOptionsWithJson(json: any, configFileName: string, expectedResult: { compilerOptions: CompilerOptions, errors: Diagnostic[] }) {
|
||||
const { options: actualCompilerOptions, errors: actualErrors} = convertCompilerOptionsFromJson(json["compilerOptions"], "/apath/", configFileName);
|
||||
|
||||
const parsedCompilerOptions = JSON.stringify(actualCompilerOptions);
|
||||
@@ -21,6 +26,34 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function assertCompilerOptionsWithJsonNode(json: any, configFileName: string, expectedResult: { compilerOptions: CompilerOptions, errors: Diagnostic[] }) {
|
||||
const fileText = JSON.stringify(json);
|
||||
const result = parseJsonText(configFileName, fileText);
|
||||
assert(!result.parseDiagnostics.length);
|
||||
assert(!!result.endOfFileToken);
|
||||
const host: ParseConfigHost = new Utils.MockParseConfigHost("/apath/", true, []);
|
||||
const { options: actualCompilerOptions, errors: actualParseErrors } = parseJsonSourceFileConfigFileContent(result, host, "/apath/", /*existingOptions*/ undefined, configFileName);
|
||||
expectedResult.compilerOptions["configFilePath"] = configFileName;
|
||||
|
||||
const parsedCompilerOptions = JSON.stringify(actualCompilerOptions);
|
||||
const expectedCompilerOptions = JSON.stringify(expectedResult.compilerOptions);
|
||||
assert.equal(parsedCompilerOptions, expectedCompilerOptions);
|
||||
assert.equal(actualCompilerOptions.configFile, result);
|
||||
|
||||
const actualErrors = filter(actualParseErrors, error => error.code !== Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
|
||||
const expectedErrors = expectedResult.errors;
|
||||
assert.isTrue(expectedResult.errors.length === actualErrors.length, `Expected error: ${JSON.stringify(expectedResult.errors)}. Actual error: ${JSON.stringify(actualErrors)}.`);
|
||||
for (let i = 0; i < actualErrors.length; i++) {
|
||||
const actualError = actualErrors[i];
|
||||
const expectedError = expectedErrors[i];
|
||||
assert.equal(actualError.code, expectedError.code, `Expected error-code: ${JSON.stringify(expectedError.code)}. Actual error-code: ${JSON.stringify(actualError.code)}.`);
|
||||
assert.equal(actualError.category, expectedError.category, `Expected error-category: ${JSON.stringify(expectedError.category)}. Actual error-category: ${JSON.stringify(actualError.category)}.`);
|
||||
assert(actualError.file);
|
||||
assert(actualError.start);
|
||||
assert(actualError.length);
|
||||
}
|
||||
}
|
||||
|
||||
// tsconfig.json tests
|
||||
it("Convert correctly format tsconfig.json to compiler-options ", () => {
|
||||
assertCompilerOptions(
|
||||
|
||||
@@ -2,14 +2,20 @@
|
||||
/// <reference path="..\..\compiler\commandLineParser.ts" />
|
||||
|
||||
namespace ts {
|
||||
type ExpectedResult = { typeAcquisition: TypeAcquisition, errors: Diagnostic[] };
|
||||
describe("convertTypeAcquisitionFromJson", () => {
|
||||
function assertTypeAcquisition(json: any, configFileName: string, expectedResult: { typeAcquisition: TypeAcquisition, errors: Diagnostic[] }) {
|
||||
const jsonOptions = json["typeAcquisition"] || json["typingOptions"];
|
||||
const { options: actualTypeAcquisition, errors: actualErrors } = convertTypeAcquisitionFromJson(jsonOptions, "/apath/", configFileName);
|
||||
function assertTypeAcquisition(json: any, configFileName: string, expectedResult: ExpectedResult) {
|
||||
assertTypeAcquisitionWithJson(json, configFileName, expectedResult);
|
||||
assertTypeAcquisitionWithJsonNode(json, configFileName, expectedResult);
|
||||
}
|
||||
|
||||
function verifyAcquisition(actualTypeAcquisition: TypeAcquisition, expectedResult: ExpectedResult) {
|
||||
const parsedTypeAcquisition = JSON.stringify(actualTypeAcquisition);
|
||||
const expectedTypeAcquisition = JSON.stringify(expectedResult.typeAcquisition);
|
||||
assert.equal(parsedTypeAcquisition, expectedTypeAcquisition);
|
||||
}
|
||||
|
||||
function verifyErrors(actualErrors: Diagnostic[], expectedResult: ExpectedResult, hasLocation?: boolean) {
|
||||
const expectedErrors = expectedResult.errors;
|
||||
assert.isTrue(expectedResult.errors.length === actualErrors.length, `Expected error: ${JSON.stringify(expectedResult.errors)}. Actual error: ${JSON.stringify(actualErrors)}.`);
|
||||
for (let i = 0; i < actualErrors.length; i++) {
|
||||
@@ -17,9 +23,34 @@ namespace ts {
|
||||
const expectedError = expectedErrors[i];
|
||||
assert.equal(actualError.code, expectedError.code, `Expected error-code: ${JSON.stringify(expectedError.code)}. Actual error-code: ${JSON.stringify(actualError.code)}.`);
|
||||
assert.equal(actualError.category, expectedError.category, `Expected error-category: ${JSON.stringify(expectedError.category)}. Actual error-category: ${JSON.stringify(actualError.category)}.`);
|
||||
if (hasLocation) {
|
||||
assert(actualError.file);
|
||||
assert(actualError.start);
|
||||
assert(actualError.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assertTypeAcquisitionWithJson(json: any, configFileName: string, expectedResult: ExpectedResult) {
|
||||
const jsonOptions = json["typeAcquisition"] || json["typingOptions"];
|
||||
const { options: actualTypeAcquisition, errors: actualErrors } = convertTypeAcquisitionFromJson(jsonOptions, "/apath/", configFileName);
|
||||
verifyAcquisition(actualTypeAcquisition, expectedResult);
|
||||
verifyErrors(actualErrors, expectedResult);
|
||||
}
|
||||
|
||||
function assertTypeAcquisitionWithJsonNode(json: any, configFileName: string, expectedResult: ExpectedResult) {
|
||||
const fileText = JSON.stringify(json);
|
||||
const result = parseJsonText(configFileName, fileText);
|
||||
assert(!result.parseDiagnostics.length);
|
||||
assert(!!result.endOfFileToken);
|
||||
const host: ParseConfigHost = new Utils.MockParseConfigHost("/apath/", true, []);
|
||||
const { typeAcquisition: actualTypeAcquisition, errors: actualParseErrors } = parseJsonSourceFileConfigFileContent(result, host, "/apath/", /*existingOptions*/ undefined, configFileName);
|
||||
verifyAcquisition(actualTypeAcquisition, expectedResult);
|
||||
|
||||
const actualErrors = filter(actualParseErrors, error => error.code !== Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
|
||||
verifyErrors(actualErrors, expectedResult, /*hasLocation*/ true);
|
||||
}
|
||||
|
||||
// tsconfig.json
|
||||
it("Convert deprecated typingOptions.enableAutoDiscovery format tsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
@@ -177,7 +208,7 @@ namespace ts {
|
||||
},
|
||||
errors: [
|
||||
{
|
||||
category: Diagnostics.Unknown_compiler_option_0.category,
|
||||
category: Diagnostics.Unknown_type_acquisition_option_0.category,
|
||||
code: Diagnostics.Unknown_type_acquisition_option_0.code,
|
||||
file: undefined,
|
||||
start: 0,
|
||||
|
||||
@@ -44,8 +44,8 @@ namespace ts {
|
||||
parsesCorrectly("functionType1", "{function()}");
|
||||
parsesCorrectly("functionType2", "{function(string, boolean)}");
|
||||
parsesCorrectly("functionReturnType1", "{function(string, boolean)}");
|
||||
parsesCorrectly("thisType1", "{this:a.b}");
|
||||
parsesCorrectly("newType1", "{new:a.b}");
|
||||
parsesCorrectly("thisType1", "{function(this:a.b)}");
|
||||
parsesCorrectly("newType1", "{function(new:a.b)}");
|
||||
parsesCorrectly("variadicType", "{...number}");
|
||||
parsesCorrectly("optionalType", "{number=}");
|
||||
parsesCorrectly("optionalNullable", "{?=}");
|
||||
@@ -54,7 +54,7 @@ namespace ts {
|
||||
parsesCorrectly("typeReference3", "{a.function}");
|
||||
parsesCorrectly("arrayType1", "{a[]}");
|
||||
parsesCorrectly("arrayType2", "{a[][]}");
|
||||
parsesCorrectly("arrayType3", "{a[][]=}");
|
||||
parsesCorrectly("arrayType3", "{(a[][])=}");
|
||||
parsesCorrectly("keyword1", "{var}");
|
||||
parsesCorrectly("keyword2", "{null}");
|
||||
parsesCorrectly("keyword3", "{undefined}");
|
||||
@@ -62,6 +62,12 @@ namespace ts {
|
||||
parsesCorrectly("tupleType1", "{[number]}");
|
||||
parsesCorrectly("tupleType2", "{[number,string]}");
|
||||
parsesCorrectly("tupleType3", "{[number,string,boolean]}");
|
||||
parsesCorrectly("tupleTypeWithTrailingComma", "{[number,]}");
|
||||
parsesCorrectly("typeOfType", "{typeof M}");
|
||||
parsesCorrectly("tsConstructorType", "{new () => string}");
|
||||
parsesCorrectly("tsFunctionType", "{() => string}");
|
||||
parsesCorrectly("typeArgumentsNotFollowingDot", "{a<>}");
|
||||
parsesCorrectly("functionTypeWithTrailingComma", "{function(a,)}");
|
||||
});
|
||||
|
||||
describe("parsesIncorrectly", () => {
|
||||
@@ -69,21 +75,13 @@ namespace ts {
|
||||
parsesIncorrectly("unionTypeWithTrailingBar", "{(a|)}");
|
||||
parsesIncorrectly("unionTypeWithoutTypes", "{()}");
|
||||
parsesIncorrectly("nullableTypeWithoutType", "{!}");
|
||||
parsesIncorrectly("functionTypeWithTrailingComma", "{function(a,)}");
|
||||
parsesIncorrectly("thisWithoutType", "{this:}");
|
||||
parsesIncorrectly("newWithoutType", "{new:}");
|
||||
parsesIncorrectly("variadicWithoutType", "{...}");
|
||||
parsesIncorrectly("optionalWithoutType", "{=}");
|
||||
parsesIncorrectly("allWithType", "{*foo}");
|
||||
parsesIncorrectly("typeArgumentsNotFollowingDot", "{a<>}");
|
||||
parsesIncorrectly("emptyTypeArguments", "{a.<>}");
|
||||
parsesIncorrectly("typeArgumentsWithTrailingComma", "{a.<a,>}");
|
||||
parsesIncorrectly("tsFunctionType", "{() => string}");
|
||||
parsesIncorrectly("tsConstructoType", "{new () => string}");
|
||||
parsesIncorrectly("typeOfType", "{typeof M}");
|
||||
parsesIncorrectly("namedParameter", "{function(a: number)}");
|
||||
parsesIncorrectly("tupleTypeWithComma", "{[,]}");
|
||||
parsesIncorrectly("tupleTypeWithTrailingComma", "{[number,]}");
|
||||
parsesIncorrectly("tupleTypeWithLeadingComma", "{[,number]}");
|
||||
});
|
||||
});
|
||||
|
||||
+101
-119
@@ -95,6 +95,44 @@ namespace ts {
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
}
|
||||
|
||||
function validateMatches(expected: ts.ParsedCommandLine, json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[]) {
|
||||
{
|
||||
const jsonText = JSON.stringify(json);
|
||||
const result = parseJsonText(caseInsensitiveTsconfigPath, jsonText);
|
||||
const actual = ts.parseJsonSourceFileConfigFileContent(result, host, basePath, existingOptions, configFileName, resolutionStack);
|
||||
for (const error of expected.errors) {
|
||||
if (error.file) {
|
||||
error.file = result;
|
||||
}
|
||||
}
|
||||
assertParsed(actual, expected);
|
||||
}
|
||||
{
|
||||
const actual = ts.parseJsonConfigFileContent(json, host, basePath, existingOptions, configFileName, resolutionStack);
|
||||
expected.errors = map(expected.errors, error => {
|
||||
return <Diagnostic>{
|
||||
category: error.category,
|
||||
code: error.code,
|
||||
file: undefined,
|
||||
length: undefined,
|
||||
messageText: error.messageText,
|
||||
start: undefined,
|
||||
};
|
||||
});
|
||||
assertParsed(actual, expected);
|
||||
}
|
||||
}
|
||||
|
||||
function createDiagnosticForConfigFile(json: any, start: number, length: number, diagnosticMessage: DiagnosticMessage, arg0: string) {
|
||||
const text = JSON.stringify(json);
|
||||
const file = <SourceFile>{
|
||||
fileName: caseInsensitiveTsconfigPath,
|
||||
kind: SyntaxKind.SourceFile,
|
||||
text
|
||||
};
|
||||
return ts.createFileDiagnostic(file, start, length, diagnosticMessage, arg0);
|
||||
}
|
||||
|
||||
describe("matchFiles", () => {
|
||||
it("with defaults", () => {
|
||||
const json = {};
|
||||
@@ -109,8 +147,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
|
||||
describe("with literal file list", () => {
|
||||
@@ -130,8 +167,7 @@ namespace ts {
|
||||
],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("missing files are still present", () => {
|
||||
const json = {
|
||||
@@ -149,8 +185,7 @@ namespace ts {
|
||||
],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("are not removed due to excludes", () => {
|
||||
const json = {
|
||||
@@ -171,8 +206,7 @@ namespace ts {
|
||||
],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -193,8 +227,7 @@ namespace ts {
|
||||
],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with non .ts file extensions are excluded", () => {
|
||||
const json = {
|
||||
@@ -212,8 +245,7 @@ namespace ts {
|
||||
fileNames: [],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
});
|
||||
it("with missing files are excluded", () => {
|
||||
const json = {
|
||||
@@ -231,8 +263,7 @@ namespace ts {
|
||||
fileNames: [],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
});
|
||||
it("with literal excludes", () => {
|
||||
const json = {
|
||||
@@ -252,8 +283,7 @@ namespace ts {
|
||||
],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with wildcard excludes", () => {
|
||||
const json = {
|
||||
@@ -280,8 +310,7 @@ namespace ts {
|
||||
],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with recursive excludes", () => {
|
||||
const json = {
|
||||
@@ -307,8 +336,7 @@ namespace ts {
|
||||
],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with case sensitive exclude", () => {
|
||||
const json = {
|
||||
@@ -327,8 +355,7 @@ namespace ts {
|
||||
],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseSensitiveHost, caseSensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseSensitiveHost, caseSensitiveBasePath);
|
||||
});
|
||||
it("with common package folders and no exclusions", () => {
|
||||
const json = {
|
||||
@@ -352,8 +379,7 @@ namespace ts {
|
||||
],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with common package folders and exclusions", () => {
|
||||
const json = {
|
||||
@@ -379,8 +405,7 @@ namespace ts {
|
||||
],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with common package folders and empty exclude", () => {
|
||||
const json = {
|
||||
@@ -404,8 +429,7 @@ namespace ts {
|
||||
],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -436,8 +460,7 @@ namespace ts {
|
||||
"c:/dev/x": ts.WatchDirectoryFlags.None
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
|
||||
it("same named declarations are excluded", () => {
|
||||
@@ -458,8 +481,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.None
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("`*` matches only ts files", () => {
|
||||
const json = {
|
||||
@@ -479,8 +501,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.None
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("`?` matches only a single character", () => {
|
||||
const json = {
|
||||
@@ -499,8 +520,7 @@ namespace ts {
|
||||
"c:/dev/x": ts.WatchDirectoryFlags.None
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with recursive directory", () => {
|
||||
const json = {
|
||||
@@ -521,8 +541,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with multiple recursive directories", () => {
|
||||
const json = {
|
||||
@@ -545,8 +564,7 @@ namespace ts {
|
||||
"c:/dev/z": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("case sensitive", () => {
|
||||
const json = {
|
||||
@@ -564,8 +582,7 @@ namespace ts {
|
||||
"/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseSensitiveHost, caseSensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseSensitiveHost, caseSensitiveBasePath);
|
||||
});
|
||||
it("with missing files are excluded", () => {
|
||||
const json = {
|
||||
@@ -584,8 +601,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
});
|
||||
it("always include literal files", () => {
|
||||
const json = {
|
||||
@@ -609,8 +625,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("exclude folders", () => {
|
||||
const json = {
|
||||
@@ -634,8 +649,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with common package folders and no exclusions", () => {
|
||||
const json = {
|
||||
@@ -656,8 +670,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with common package folders and exclusions", () => {
|
||||
const json = {
|
||||
@@ -680,8 +693,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with common package folders and empty exclude", () => {
|
||||
const json = {
|
||||
@@ -703,8 +715,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("exclude .js files when allowJs=false", () => {
|
||||
const json = {
|
||||
@@ -728,8 +739,7 @@ namespace ts {
|
||||
"c:/dev/js": ts.WatchDirectoryFlags.None
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
});
|
||||
it("include .js files when allowJs=true", () => {
|
||||
const json = {
|
||||
@@ -753,8 +763,7 @@ namespace ts {
|
||||
"c:/dev/js": ts.WatchDirectoryFlags.None
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("include explicitly listed .min.js files when allowJs=true", () => {
|
||||
const json = {
|
||||
@@ -778,8 +787,7 @@ namespace ts {
|
||||
"c:/dev/js": ts.WatchDirectoryFlags.None
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("include paths outside of the project", () => {
|
||||
const json = {
|
||||
@@ -802,8 +810,7 @@ namespace ts {
|
||||
"c:/ext": ts.WatchDirectoryFlags.None
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("include paths outside of the project using relative paths", () => {
|
||||
const json = {
|
||||
@@ -825,8 +832,7 @@ namespace ts {
|
||||
"c:/ext": ts.WatchDirectoryFlags.None
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("exclude paths outside of the project using relative paths", () => {
|
||||
const json = {
|
||||
@@ -846,8 +852,7 @@ namespace ts {
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
});
|
||||
it("include files with .. in their name", () => {
|
||||
const json = {
|
||||
@@ -866,8 +871,7 @@ namespace ts {
|
||||
],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("exclude files with .. in their name", () => {
|
||||
const json = {
|
||||
@@ -888,8 +892,7 @@ namespace ts {
|
||||
"c:/ext": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with jsx=none, allowJs=false", () => {
|
||||
const json = {
|
||||
@@ -911,8 +914,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with jsx=preserve, allowJs=false", () => {
|
||||
const json = {
|
||||
@@ -936,8 +938,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with jsx=react-native, allowJs=false", () => {
|
||||
const json = {
|
||||
@@ -961,8 +962,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with jsx=none, allowJs=true", () => {
|
||||
const json = {
|
||||
@@ -986,8 +986,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with jsx=preserve, allowJs=true", () => {
|
||||
const json = {
|
||||
@@ -1013,8 +1012,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with jsx=react-native, allowJs=true", () => {
|
||||
const json = {
|
||||
@@ -1040,8 +1038,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("exclude .min.js files using wildcards", () => {
|
||||
const json = {
|
||||
@@ -1067,8 +1064,7 @@ namespace ts {
|
||||
"c:/dev/js": ts.WatchDirectoryFlags.None
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
describe("with trailing recursive directory", () => {
|
||||
it("in includes", () => {
|
||||
@@ -1080,15 +1076,14 @@ namespace ts {
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**"),
|
||||
createDiagnosticForConfigFile(json, 12, 4, ts.Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**"),
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
});
|
||||
it("in excludes", () => {
|
||||
const json = {
|
||||
@@ -1108,8 +1103,7 @@ namespace ts {
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
});
|
||||
});
|
||||
describe("with multiple recursive directory patterns", () => {
|
||||
@@ -1122,15 +1116,14 @@ namespace ts {
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, "**/x/**/*"),
|
||||
createDiagnosticForConfigFile(json, 12, 11, ts.Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, "**/x/**/*"),
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
});
|
||||
it("in excludes", () => {
|
||||
const json = {
|
||||
@@ -1144,7 +1137,7 @@ namespace ts {
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, "**/x/**")
|
||||
createDiagnosticForConfigFile(json, 34, 9, ts.Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, "**/x/**")
|
||||
],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
@@ -1156,8 +1149,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1171,15 +1163,14 @@ namespace ts {
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/../*"),
|
||||
createDiagnosticForConfigFile(json, 12, 9, ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/../*"),
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
});
|
||||
|
||||
it("in includes after a subdirectory", () => {
|
||||
@@ -1191,15 +1182,14 @@ namespace ts {
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/../*"),
|
||||
createDiagnosticForConfigFile(json, 12, 11, ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/../*"),
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
});
|
||||
|
||||
it("in excludes immediately after", () => {
|
||||
@@ -1214,7 +1204,7 @@ namespace ts {
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/..")
|
||||
createDiagnosticForConfigFile(json, 34, 7, ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/..")
|
||||
],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
@@ -1226,8 +1216,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
|
||||
it("in excludes after a subdirectory", () => {
|
||||
@@ -1242,7 +1231,7 @@ namespace ts {
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/..")
|
||||
createDiagnosticForConfigFile(json, 34, 9, ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/..")
|
||||
],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
@@ -1254,8 +1243,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1272,8 +1260,7 @@ namespace ts {
|
||||
"c:/dev/z": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1297,8 +1284,7 @@ namespace ts {
|
||||
"c:/dev/w": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
describe("that are explicitly included", () => {
|
||||
it("without wildcards", () => {
|
||||
@@ -1317,8 +1303,7 @@ namespace ts {
|
||||
],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with recursive wildcards that match directories", () => {
|
||||
const json = {
|
||||
@@ -1339,8 +1324,7 @@ namespace ts {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with recursive wildcards that match nothing", () => {
|
||||
const json = {
|
||||
@@ -1361,8 +1345,7 @@ namespace ts {
|
||||
"c:/dev/x": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with wildcard excludes that implicitly exclude dotted files", () => {
|
||||
const json = {
|
||||
@@ -1382,8 +1365,7 @@ namespace ts {
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
validateMatches(expected, json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath, /*existingOptions*/ undefined, caseInsensitiveTsconfigPath);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace ts {
|
||||
if (!expected === !actual) {
|
||||
if (expected) {
|
||||
assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`);
|
||||
assert.isTrue(expected.extension === actual.extension, `'ext': expected '${Extension[expected.extension]}' to be equal to '${Extension[actual.extension]}'`);
|
||||
assert.isTrue(expected.extension === actual.extension, `'ext': expected '${expected.extension}' to be equal to '${actual.extension}'`);
|
||||
assert.isTrue(expected.isExternalLibraryImport === actual.isExternalLibraryImport, `'isExternalLibraryImport': expected '${expected.isExternalLibraryImport}' to be equal to '${actual.isExternalLibraryImport}'`);
|
||||
}
|
||||
return true;
|
||||
@@ -56,7 +56,7 @@ namespace ts {
|
||||
else {
|
||||
return { readFile, fileExists: path => map.has(path) };
|
||||
}
|
||||
function readFile(path: string): string {
|
||||
function readFile(path: string): string | undefined {
|
||||
const file = map.get(path);
|
||||
return file && file.content;
|
||||
}
|
||||
@@ -950,8 +950,8 @@ import b = require("./moduleB");
|
||||
{
|
||||
const f1 = { name: "/root/src/app.ts" };
|
||||
const f2 = { name: "/root/src/types/lib/typings/lib.d.ts" };
|
||||
const package = { name: "/root/src/types/lib/package.json", content: JSON.stringify({ types: "typings/lib.d.ts" }) };
|
||||
test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ true, f1, f2, package);
|
||||
const packageFile = { name: "/root/src/types/lib/package.json", content: JSON.stringify({ types: "typings/lib.d.ts" }) };
|
||||
test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ true, f1, f2, packageFile);
|
||||
}
|
||||
{
|
||||
const f1 = { name: "/root/src/app.ts" };
|
||||
@@ -961,8 +961,8 @@ import b = require("./moduleB");
|
||||
{
|
||||
const f1 = { name: "/root/src/app.ts" };
|
||||
const f2 = { name: "/root/src/node_modules/lib/typings/lib.d.ts" };
|
||||
const package = { name: "/root/src/node_modules/lib/package.json", content: JSON.stringify({ types: "typings/lib.d.ts" }) };
|
||||
test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2, package);
|
||||
const packageFile = { name: "/root/src/node_modules/lib/package.json", content: JSON.stringify({ types: "typings/lib.d.ts" }) };
|
||||
test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2, packageFile);
|
||||
}
|
||||
{
|
||||
const f1 = { name: "/root/src/app.ts" };
|
||||
@@ -972,8 +972,8 @@ import b = require("./moduleB");
|
||||
{
|
||||
const f1 = { name: "/root/src/app.ts" };
|
||||
const f2 = { name: "/root/src/node_modules/@types/lib/typings/lib.d.ts" };
|
||||
const package = { name: "/root/src/node_modules/@types/lib/package.json", content: JSON.stringify({ types: "typings/lib.d.ts" }) };
|
||||
test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2, package);
|
||||
const packageFile = { name: "/root/src/node_modules/@types/lib/package.json", content: JSON.stringify({ types: "typings/lib.d.ts" }) };
|
||||
test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2, packageFile);
|
||||
}
|
||||
});
|
||||
it("Can be resolved from secondary location", () => {
|
||||
@@ -990,8 +990,8 @@ import b = require("./moduleB");
|
||||
{
|
||||
const f1 = { name: "/root/src/app.ts" };
|
||||
const f2 = { name: "/root/node_modules/lib/typings/lib.d.ts" };
|
||||
const package = { name: "/root/node_modules/lib/package.json", content: JSON.stringify({ typings: "typings/lib.d.ts" }) };
|
||||
test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2, package);
|
||||
const packageFile = { name: "/root/node_modules/lib/package.json", content: JSON.stringify({ typings: "typings/lib.d.ts" }) };
|
||||
test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2, packageFile);
|
||||
}
|
||||
{
|
||||
const f1 = { name: "/root/src/app.ts" };
|
||||
@@ -1001,8 +1001,8 @@ import b = require("./moduleB");
|
||||
{
|
||||
const f1 = { name: "/root/src/app.ts" };
|
||||
const f2 = { name: "/root/node_modules/@types/lib/typings/lib.d.ts" };
|
||||
const package = { name: "/root/node_modules/@types/lib/package.json", content: JSON.stringify({ typings: "typings/lib.d.ts" }) };
|
||||
test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2, package);
|
||||
const packageFile = { name: "/root/node_modules/@types/lib/package.json", content: JSON.stringify({ typings: "typings/lib.d.ts" }) };
|
||||
test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2, packageFile);
|
||||
}
|
||||
});
|
||||
it("Primary resolution overrides secondary resolutions", () => {
|
||||
|
||||
@@ -81,63 +81,136 @@ namespace ts {
|
||||
|
||||
describe("printNode", () => {
|
||||
const printsCorrectly = makePrintsCorrectly("printsNodeCorrectly");
|
||||
let sourceFile: SourceFile;
|
||||
before(() => sourceFile = createSourceFile("source.ts", "", ScriptTarget.ES2015));
|
||||
// tslint:disable boolean-trivia
|
||||
const syntheticNode = createClassDeclaration(
|
||||
undefined,
|
||||
undefined,
|
||||
/*name*/ createIdentifier("C"),
|
||||
undefined,
|
||||
undefined,
|
||||
createNodeArray([
|
||||
createProperty(
|
||||
undefined,
|
||||
printsCorrectly("class", {}, printer => printer.printNode(
|
||||
EmitHint.Unspecified,
|
||||
createClassDeclaration(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*name*/ createIdentifier("C"),
|
||||
/*typeParameters*/ undefined,
|
||||
/*heritageClauses*/ undefined,
|
||||
[createProperty(
|
||||
/*decorators*/ undefined,
|
||||
createNodeArray([createToken(SyntaxKind.PublicKeyword)]),
|
||||
createIdentifier("prop"),
|
||||
undefined,
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
])
|
||||
);
|
||||
/*questionToken*/ undefined,
|
||||
/*type*/ undefined,
|
||||
/*initializer*/ undefined
|
||||
)]
|
||||
),
|
||||
createSourceFile("source.ts", "", ScriptTarget.ES2015)
|
||||
));
|
||||
|
||||
printsCorrectly("namespaceExportDeclaration", {}, printer => printer.printNode(
|
||||
EmitHint.Unspecified,
|
||||
createNamespaceExportDeclaration("B"),
|
||||
createSourceFile("source.ts", "", ScriptTarget.ES2015)
|
||||
));
|
||||
|
||||
// https://github.com/Microsoft/TypeScript/issues/15971
|
||||
const classWithOptionalMethodAndProperty = createClassDeclaration(
|
||||
undefined,
|
||||
/* modifiers */ createNodeArray([createToken(SyntaxKind.DeclareKeyword)]),
|
||||
/* name */ createIdentifier("X"),
|
||||
undefined,
|
||||
undefined,
|
||||
createNodeArray([
|
||||
createMethod(
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
/* name */ createIdentifier("method"),
|
||||
/* questionToken */ createToken(SyntaxKind.QuestionToken),
|
||||
undefined,
|
||||
undefined,
|
||||
/* type */ createKeywordTypeNode(SyntaxKind.VoidKeyword),
|
||||
undefined
|
||||
printsCorrectly("classWithOptionalMethodAndProperty", {}, printer => printer.printNode(
|
||||
EmitHint.Unspecified,
|
||||
createClassDeclaration(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ [createToken(SyntaxKind.DeclareKeyword)],
|
||||
/*name*/ createIdentifier("X"),
|
||||
/*typeParameters*/ undefined,
|
||||
/*heritageClauses*/ undefined,
|
||||
[
|
||||
createMethod(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ createIdentifier("method"),
|
||||
/*questionToken*/ createToken(SyntaxKind.QuestionToken),
|
||||
/*typeParameters*/ undefined,
|
||||
[],
|
||||
/*type*/ createKeywordTypeNode(SyntaxKind.VoidKeyword),
|
||||
/*body*/ undefined
|
||||
),
|
||||
createProperty(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*name*/ createIdentifier("property"),
|
||||
/*questionToken*/ createToken(SyntaxKind.QuestionToken),
|
||||
/*type*/ createKeywordTypeNode(SyntaxKind.StringKeyword),
|
||||
/*initializer*/ undefined
|
||||
),
|
||||
]
|
||||
),
|
||||
createSourceFile("source.ts", "", ScriptTarget.ES2015)
|
||||
));
|
||||
|
||||
// https://github.com/Microsoft/TypeScript/issues/15651
|
||||
printsCorrectly("functionTypes", {}, printer => printer.printNode(
|
||||
EmitHint.Unspecified,
|
||||
createTupleTypeNode([
|
||||
createFunctionTypeNode(
|
||||
/*typeArguments*/ undefined,
|
||||
[createParameter(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*dotDotDotToken*/ undefined,
|
||||
createIdentifier("args")
|
||||
)],
|
||||
createKeywordTypeNode(SyntaxKind.AnyKeyword)
|
||||
),
|
||||
createProperty(
|
||||
undefined,
|
||||
undefined,
|
||||
/* name */ createIdentifier("property"),
|
||||
/* questionToken */ createToken(SyntaxKind.QuestionToken),
|
||||
/* type */ createKeywordTypeNode(SyntaxKind.StringKeyword),
|
||||
undefined
|
||||
createFunctionTypeNode(
|
||||
[createTypeParameterDeclaration("T")],
|
||||
[createParameter(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*dotDotDotToken*/ undefined,
|
||||
createIdentifier("args")
|
||||
)],
|
||||
createKeywordTypeNode(SyntaxKind.AnyKeyword)
|
||||
),
|
||||
])
|
||||
);
|
||||
|
||||
// tslint:enable boolean-trivia
|
||||
printsCorrectly("class", {}, printer => printer.printNode(EmitHint.Unspecified, syntheticNode, sourceFile));
|
||||
|
||||
printsCorrectly("namespaceExportDeclaration", {}, printer => printer.printNode(EmitHint.Unspecified, createNamespaceExportDeclaration("B"), sourceFile));
|
||||
|
||||
printsCorrectly("classWithOptionalMethodAndProperty", {}, printer => printer.printNode(EmitHint.Unspecified, classWithOptionalMethodAndProperty, sourceFile));
|
||||
createFunctionTypeNode(
|
||||
/*typeArguments*/ undefined,
|
||||
[createParameter(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
createToken(SyntaxKind.DotDotDotToken),
|
||||
createIdentifier("args")
|
||||
)],
|
||||
createKeywordTypeNode(SyntaxKind.AnyKeyword)
|
||||
),
|
||||
createFunctionTypeNode(
|
||||
/*typeArguments*/ undefined,
|
||||
[createParameter(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*dotDotDotToken*/ undefined,
|
||||
createIdentifier("args"),
|
||||
createToken(SyntaxKind.QuestionToken)
|
||||
)],
|
||||
createKeywordTypeNode(SyntaxKind.AnyKeyword)
|
||||
),
|
||||
createFunctionTypeNode(
|
||||
/*typeArguments*/ undefined,
|
||||
[createParameter(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*dotDotDotToken*/ undefined,
|
||||
createIdentifier("args"),
|
||||
/*questionToken*/ undefined,
|
||||
createKeywordTypeNode(SyntaxKind.AnyKeyword)
|
||||
)],
|
||||
createKeywordTypeNode(SyntaxKind.AnyKeyword)
|
||||
),
|
||||
createFunctionTypeNode(
|
||||
/*typeArguments*/ undefined,
|
||||
[createParameter(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*dotDotDotToken*/ undefined,
|
||||
createObjectBindingPattern([])
|
||||
)],
|
||||
createKeywordTypeNode(SyntaxKind.AnyKeyword)
|
||||
),
|
||||
]),
|
||||
createSourceFile("source.ts", "", ScriptTarget.ES2015)
|
||||
));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/// <reference path="..\harness.ts" />
|
||||
|
||||
namespace ts {
|
||||
describe("Program.getMissingFilePaths", () => {
|
||||
|
||||
const options: CompilerOptions = {
|
||||
noLib: true,
|
||||
};
|
||||
|
||||
const emptyFileName = "empty.ts";
|
||||
const emptyFileRelativePath = "./" + emptyFileName;
|
||||
|
||||
const emptyFile: Harness.Compiler.TestFile = {
|
||||
unitName: emptyFileName,
|
||||
content: ""
|
||||
};
|
||||
|
||||
const referenceFileName = "reference.ts";
|
||||
const referenceFileRelativePath = "./" + referenceFileName;
|
||||
|
||||
const referenceFile: Harness.Compiler.TestFile = {
|
||||
unitName: referenceFileName,
|
||||
content:
|
||||
"/// <reference path=\"d:/imaginary/nonexistent1.ts\"/>\n" + // Absolute
|
||||
"/// <reference path=\"./nonexistent2.ts\"/>\n" + // Relative
|
||||
"/// <reference path=\"nonexistent3.ts\"/>\n" + // Unqualified
|
||||
"/// <reference path=\"nonexistent4\"/>\n" // No extension
|
||||
};
|
||||
|
||||
const testCompilerHost = Harness.Compiler.createCompilerHost(
|
||||
/*inputFiles*/ [emptyFile, referenceFile],
|
||||
/*writeFile*/ undefined,
|
||||
/*scriptTarget*/ undefined,
|
||||
/*useCaseSensitiveFileNames*/ false,
|
||||
/*currentDirectory*/ "d:\\pretend\\",
|
||||
/*newLineKind*/ NewLineKind.LineFeed,
|
||||
/*libFiles*/ undefined
|
||||
);
|
||||
|
||||
it("handles no missing root files", () => {
|
||||
const program = createProgram([emptyFileRelativePath], options, testCompilerHost);
|
||||
const missing = program.getMissingFilePaths();
|
||||
assert.isDefined(missing);
|
||||
assert.deepEqual(missing, []);
|
||||
});
|
||||
|
||||
it("handles missing root file", () => {
|
||||
const program = createProgram(["./nonexistent.ts"], options, testCompilerHost);
|
||||
const missing = program.getMissingFilePaths();
|
||||
assert.isDefined(missing);
|
||||
assert.deepEqual(missing, ["d:/pretend/nonexistent.ts"]); // Absolute path
|
||||
});
|
||||
|
||||
it("handles multiple missing root files", () => {
|
||||
const program = createProgram(["./nonexistent0.ts", "./nonexistent1.ts"], options, testCompilerHost);
|
||||
const missing = program.getMissingFilePaths().sort();
|
||||
assert.deepEqual(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]);
|
||||
});
|
||||
|
||||
it("handles a mix of present and missing root files", () => {
|
||||
const program = createProgram(["./nonexistent0.ts", emptyFileRelativePath, "./nonexistent1.ts"], options, testCompilerHost);
|
||||
const missing = program.getMissingFilePaths().sort();
|
||||
assert.deepEqual(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]);
|
||||
});
|
||||
|
||||
it("handles repeatedly specified root files", () => {
|
||||
const program = createProgram(["./nonexistent.ts", "./nonexistent.ts"], options, testCompilerHost);
|
||||
const missing = program.getMissingFilePaths();
|
||||
assert.isDefined(missing);
|
||||
assert.deepEqual(missing, ["d:/pretend/nonexistent.ts"]);
|
||||
});
|
||||
|
||||
it("normalizes file paths", () => {
|
||||
const program0 = createProgram(["./nonexistent.ts", "./NONEXISTENT.ts"], options, testCompilerHost);
|
||||
const program1 = createProgram(["./NONEXISTENT.ts", "./nonexistent.ts"], options, testCompilerHost);
|
||||
const missing0 = program0.getMissingFilePaths();
|
||||
const missing1 = program1.getMissingFilePaths();
|
||||
assert.equal(missing0.length, 1);
|
||||
assert.deepEqual(missing0, missing1);
|
||||
});
|
||||
|
||||
it("handles missing triple slash references", () => {
|
||||
const program = createProgram([referenceFileRelativePath], options, testCompilerHost);
|
||||
const missing = program.getMissingFilePaths().sort();
|
||||
assert.isDefined(missing);
|
||||
assert.deepEqual(missing, [
|
||||
// From absolute reference
|
||||
"d:/imaginary/nonexistent1.ts",
|
||||
|
||||
// From relative reference
|
||||
"d:/pretend/nonexistent2.ts",
|
||||
|
||||
// From unqualified reference
|
||||
"d:/pretend/nonexistent3.ts",
|
||||
|
||||
// From no-extension reference
|
||||
"d:/pretend/nonexistent4.d.ts",
|
||||
"d:/pretend/nonexistent4.ts",
|
||||
"d:/pretend/nonexistent4.tsx"
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -6,7 +6,10 @@ namespace ts.projectSystem {
|
||||
describe("Project errors", () => {
|
||||
function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: string[]) {
|
||||
assert.isTrue(projectFiles !== undefined, "missing project files");
|
||||
const errors = projectFiles.projectErrors;
|
||||
checkProjectErrorsWorker(projectFiles.projectErrors, expectedErrors);
|
||||
}
|
||||
|
||||
function checkProjectErrorsWorker(errors: Diagnostic[], expectedErrors: string[]) {
|
||||
assert.equal(errors ? errors.length : 0, expectedErrors.length, `expected ${expectedErrors.length} error in the list`);
|
||||
if (expectedErrors.length) {
|
||||
for (let i = 0; i < errors.length; i++) {
|
||||
@@ -122,21 +125,24 @@ namespace ts.projectSystem {
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
assert.isTrue(configuredProject !== undefined, "should find configured project");
|
||||
checkProjectErrors(configuredProject, [
|
||||
"')' expected.",
|
||||
"Declaration or statement expected.",
|
||||
"Declaration or statement expected.",
|
||||
"Failed to parse file '/a/b/tsconfig.json'"
|
||||
checkProjectErrors(configuredProject, []);
|
||||
const projectErrors = projectService.configuredProjects[0].getAllProjectErrors();
|
||||
checkProjectErrorsWorker(projectErrors, [
|
||||
"'{' expected."
|
||||
]);
|
||||
assert.isNotNull(projectErrors[0].file);
|
||||
assert.equal(projectErrors[0].file.fileName, corruptedConfig.path);
|
||||
}
|
||||
// fix config and trigger watcher
|
||||
host.reloadFS([file1, file2, correctConfig]);
|
||||
host.triggerFileWatcherCallback(correctConfig.path, /*false*/);
|
||||
host.triggerFileWatcherCallback(correctConfig.path, FileWatcherEventKind.Changed);
|
||||
{
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
assert.isTrue(configuredProject !== undefined, "should find configured project");
|
||||
checkProjectErrors(configuredProject, []);
|
||||
const projectErrors = projectService.configuredProjects[0].getAllProjectErrors();
|
||||
checkProjectErrorsWorker(projectErrors, []);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -166,21 +172,24 @@ namespace ts.projectSystem {
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
assert.isTrue(configuredProject !== undefined, "should find configured project");
|
||||
checkProjectErrors(configuredProject, []);
|
||||
const projectErrors = projectService.configuredProjects[0].getAllProjectErrors();
|
||||
checkProjectErrorsWorker(projectErrors, []);
|
||||
}
|
||||
// break config and trigger watcher
|
||||
host.reloadFS([file1, file2, corruptedConfig]);
|
||||
host.triggerFileWatcherCallback(corruptedConfig.path, /*false*/);
|
||||
host.triggerFileWatcherCallback(corruptedConfig.path, FileWatcherEventKind.Changed);
|
||||
{
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
assert.isTrue(configuredProject !== undefined, "should find configured project");
|
||||
checkProjectErrors(configuredProject, [
|
||||
"')' expected.",
|
||||
"Declaration or statement expected.",
|
||||
"Declaration or statement expected.",
|
||||
"Failed to parse file '/a/b/tsconfig.json'"
|
||||
checkProjectErrors(configuredProject, []);
|
||||
const projectErrors = projectService.configuredProjects[0].getAllProjectErrors();
|
||||
checkProjectErrorsWorker(projectErrors, [
|
||||
"'{' expected."
|
||||
]);
|
||||
assert.isNotNull(projectErrors[0].file);
|
||||
assert.equal(projectErrors[0].file.fileName, corruptedConfig.path);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,6 +316,31 @@ namespace ts {
|
||||
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
|
||||
});
|
||||
|
||||
it("succeeds if missing files remain missing", () => {
|
||||
const options: CompilerOptions = { target, noLib: true };
|
||||
|
||||
const program_1 = newProgram(files, ["a.ts"], options);
|
||||
assert.notDeepEqual(emptyArray, program_1.getMissingFilePaths());
|
||||
|
||||
const program_2 = updateProgram(program_1, ["a.ts"], options, noop);
|
||||
assert.deepEqual(program_1.getMissingFilePaths(), program_2.getMissingFilePaths());
|
||||
|
||||
assert.equal(StructureIsReused.Completely, program_1.structureIsReused);
|
||||
});
|
||||
|
||||
it("fails if missing file is created", () => {
|
||||
const options: CompilerOptions = { target, noLib: true };
|
||||
|
||||
const program_1 = newProgram(files, ["a.ts"], options);
|
||||
assert.notDeepEqual(emptyArray, program_1.getMissingFilePaths());
|
||||
|
||||
const newTexts: NamedSourceText[] = files.concat([{ name: "non-existing-file.ts", text: SourceText.New("", "", `var x = 1`) }]);
|
||||
const program_2 = updateProgram(program_1, ["a.ts"], options, noop, newTexts);
|
||||
assert.deepEqual(emptyArray, program_2.getMissingFilePaths());
|
||||
|
||||
assert.equal(StructureIsReused.SafeModules, program_1.structureIsReused);
|
||||
});
|
||||
|
||||
it("resolution cache follows imports", () => {
|
||||
(<any>Error).stackTraceLimit = Infinity;
|
||||
|
||||
|
||||
@@ -30,13 +30,9 @@ describe("Colorization", function () {
|
||||
function identifier(text: string, position?: number) { return createClassification(text, ts.TokenClass.Identifier, position); }
|
||||
function numberLiteral(text: string, position?: number) { return createClassification(text, ts.TokenClass.NumberLiteral, position); }
|
||||
function stringLiteral(text: string, position?: number) { return createClassification(text, ts.TokenClass.StringLiteral, position); }
|
||||
function finalEndOfLineState(value: number): ClassificationEntry { return { value: value, classification: undefined, position: 0 }; }
|
||||
function createClassification(text: string, tokenClass: ts.TokenClass, position?: number): ClassificationEntry {
|
||||
return {
|
||||
value: text,
|
||||
classification: tokenClass,
|
||||
position: position,
|
||||
};
|
||||
function finalEndOfLineState(value: number): ClassificationEntry { return { value, classification: undefined, position: 0 }; }
|
||||
function createClassification(value: string, classification: ts.TokenClass, position?: number): ClassificationEntry {
|
||||
return { value, classification, position };
|
||||
}
|
||||
|
||||
function testLexicalClassification(text: string, initialEndOfLineState: ts.EndOfLineState, ...expectedEntries: ClassificationEntry[]): void {
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace ts.server {
|
||||
newLine: "\n",
|
||||
useCaseSensitiveFileNames: true,
|
||||
write(s): void { lastWrittenToHost = s; },
|
||||
readFile(): string { return void 0; },
|
||||
readFile: () => undefined,
|
||||
writeFile: noop,
|
||||
resolvePath(): string { return void 0; },
|
||||
fileExists: () => false,
|
||||
@@ -19,13 +19,13 @@ namespace ts.server {
|
||||
getExecutingFilePath(): string { return void 0; },
|
||||
getCurrentDirectory(): string { return void 0; },
|
||||
getEnvironmentVariable(): string { return ""; },
|
||||
readDirectory(): string[] { return []; },
|
||||
readDirectory() { return []; },
|
||||
exit: noop,
|
||||
setTimeout() { return 0; },
|
||||
clearTimeout: noop,
|
||||
setImmediate: () => 0,
|
||||
clearImmediate: noop,
|
||||
createHash: s => s
|
||||
createHash: Harness.LanguageService.mockHash,
|
||||
};
|
||||
|
||||
const mockLogger: Logger = {
|
||||
@@ -240,8 +240,8 @@ namespace ts.server {
|
||||
CommandNames.GetCodeFixesFull,
|
||||
CommandNames.GetSupportedCodeFixes,
|
||||
CommandNames.GetApplicableRefactors,
|
||||
CommandNames.GetRefactorCodeActions,
|
||||
CommandNames.GetRefactorCodeActionsFull,
|
||||
CommandNames.GetEditsForRefactor,
|
||||
CommandNames.GetEditsForRefactorFull,
|
||||
];
|
||||
|
||||
it("should not throw when commands are executed with invalid arguments", () => {
|
||||
@@ -389,7 +389,7 @@ namespace ts.server {
|
||||
request_seq: 0,
|
||||
type: "response",
|
||||
command,
|
||||
body: body,
|
||||
body,
|
||||
success: true
|
||||
});
|
||||
});
|
||||
@@ -436,7 +436,7 @@ namespace ts.server {
|
||||
request_seq: 0,
|
||||
type: "response",
|
||||
command,
|
||||
body: body,
|
||||
body,
|
||||
success: true
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace ts.projectSystem {
|
||||
et.service.openClientFile(file.path);
|
||||
assert.equal(et.getEvents().length, 0);
|
||||
});
|
||||
|
||||
it("only sends an event once", () => {
|
||||
const file = makeFile("/a.ts");
|
||||
const tsconfig = makeFile("/tsconfig.json", {});
|
||||
@@ -44,14 +45,15 @@ namespace ts.projectSystem {
|
||||
it("works with external project", () => {
|
||||
const file1 = makeFile("/a.ts");
|
||||
const et = new EventTracker([file1]);
|
||||
const compilerOptions: ts.CompilerOptions = { strict: true };
|
||||
const compilerOptions: ts.server.protocol.CompilerOptions = { strict: true };
|
||||
|
||||
const projectFileName = "foo.csproj";
|
||||
const projectFileName = "/hunter2/foo.csproj";
|
||||
|
||||
open();
|
||||
|
||||
// TODO: Apparently compilerOptions is mutated, so have to repeat it here!
|
||||
et.assertProjectInfoTelemetryEvent({
|
||||
projectId: Harness.LanguageService.mockHash("/hunter2/foo.csproj"),
|
||||
compilerOptions: { strict: true },
|
||||
compileOnSave: true,
|
||||
// These properties can't be present for an external project, so they are undefined instead of false.
|
||||
@@ -75,7 +77,7 @@ namespace ts.projectSystem {
|
||||
et.service.openExternalProject({
|
||||
rootFiles: toExternalFiles([file1.path]),
|
||||
options: compilerOptions,
|
||||
projectFileName: projectFileName,
|
||||
projectFileName,
|
||||
});
|
||||
checkNumberOfProjects(et.service, { externalProjects: 1 });
|
||||
}
|
||||
@@ -136,8 +138,6 @@ namespace ts.projectSystem {
|
||||
declaration: true,
|
||||
|
||||
lib: ["es6", "dom"],
|
||||
|
||||
checkJs: "" as any as boolean,
|
||||
};
|
||||
(compilerOptions as any).unknownCompilerOption = "hunter2"; // These are always ignored.
|
||||
const tsconfig = makeFile("/tsconfig.json", { compilerOptions, files: ["/a.ts"] });
|
||||
@@ -195,6 +195,7 @@ namespace ts.projectSystem {
|
||||
const et = new EventTracker([jsconfig, file]);
|
||||
et.service.openClientFile(file.path);
|
||||
et.assertProjectInfoTelemetryEvent({
|
||||
projectId: Harness.LanguageService.mockHash("/jsconfig.json"),
|
||||
fileStats: fileStats({ js: 1 }),
|
||||
compilerOptions: autoJsCompilerOptions,
|
||||
typeAcquisition: {
|
||||
@@ -214,6 +215,7 @@ namespace ts.projectSystem {
|
||||
et.service.openClientFile(file.path);
|
||||
et.getEvent<server.ProjectLanguageServiceStateEvent>(server.ProjectLanguageServiceStateEvent, /*mayBeMore*/ true);
|
||||
et.assertProjectInfoTelemetryEvent({
|
||||
projectId: Harness.LanguageService.mockHash("/jsconfig.json"),
|
||||
fileStats: fileStats({ js: 1 }),
|
||||
compilerOptions: autoJsCompilerOptions,
|
||||
configFileName: "jsconfig.json",
|
||||
@@ -248,7 +250,26 @@ namespace ts.projectSystem {
|
||||
}
|
||||
|
||||
assertProjectInfoTelemetryEvent(partial: Partial<server.ProjectInfoTelemetryEventData>): void {
|
||||
assert.deepEqual(this.getEvent<server.ProjectInfoTelemetryEvent>(ts.server.ProjectInfoTelemetryEvent), makePayload(partial));
|
||||
assert.deepEqual(this.getEvent<server.ProjectInfoTelemetryEvent>(ts.server.ProjectInfoTelemetryEvent), {
|
||||
projectId: Harness.LanguageService.mockHash("/tsconfig.json"),
|
||||
fileStats: fileStats({ ts: 1 }),
|
||||
compilerOptions: {},
|
||||
extends: false,
|
||||
files: false,
|
||||
include: false,
|
||||
exclude: false,
|
||||
compileOnSave: false,
|
||||
typeAcquisition: {
|
||||
enable: false,
|
||||
exclude: false,
|
||||
include: false,
|
||||
},
|
||||
configFileName: "tsconfig.json",
|
||||
projectType: "configured",
|
||||
languageServiceEnabled: true,
|
||||
version: ts.version,
|
||||
...partial,
|
||||
});
|
||||
}
|
||||
|
||||
getEvent<T extends server.ProjectServiceEvent>(eventName: T["eventName"], mayBeMore = false): T["data"] {
|
||||
@@ -260,28 +281,6 @@ namespace ts.projectSystem {
|
||||
}
|
||||
}
|
||||
|
||||
function makePayload(partial: Partial<server.ProjectInfoTelemetryEventData>): server.ProjectInfoTelemetryEventData {
|
||||
return {
|
||||
fileStats: fileStats({ ts: 1 }),
|
||||
compilerOptions: {},
|
||||
extends: false,
|
||||
files: false,
|
||||
include: false,
|
||||
exclude: false,
|
||||
compileOnSave: false,
|
||||
typeAcquisition: {
|
||||
enable: false,
|
||||
exclude: false,
|
||||
include: false,
|
||||
},
|
||||
configFileName: "tsconfig.json",
|
||||
projectType: "configured",
|
||||
languageServiceEnabled: true,
|
||||
version: ts.version,
|
||||
...partial
|
||||
};
|
||||
}
|
||||
|
||||
function makeFile(path: string, content: {} = ""): projectSystem.FileOrFolder {
|
||||
return { path, content: typeof content === "string" ? "" : JSON.stringify(content) };
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace ts {
|
||||
return find(n);
|
||||
|
||||
function find(node: Node): Node {
|
||||
if (isDeclaration(node) && node.name && isIdentifier(node.name) && node.name.text === name) {
|
||||
if (isDeclaration(node) && node.name && isIdentifier(node.name) && node.name.escapedText === name) {
|
||||
return node;
|
||||
}
|
||||
else {
|
||||
@@ -67,12 +67,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
function flattenNodes(n: Node) {
|
||||
const data: (Node | NodeArray<any>)[] = [];
|
||||
const data: (Node | NodeArray<Node>)[] = [];
|
||||
walk(n);
|
||||
return data;
|
||||
|
||||
function walk(n: Node | Node[]): void {
|
||||
data.push(<any>n);
|
||||
function walk(n: Node | NodeArray<Node>): void {
|
||||
data.push(n);
|
||||
return isArray(n) ? forEach(n, walk) : forEachChild(n, walk, walk);
|
||||
}
|
||||
}
|
||||
@@ -367,20 +367,22 @@ namespace M {
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("M", sourceFile), createTestClass(), { prefix: newLineCharacter });
|
||||
});
|
||||
}
|
||||
{
|
||||
function findOpenBraceForConstructor(sourceFile: SourceFile) {
|
||||
const classDecl = <ClassDeclaration>sourceFile.statements[0];
|
||||
const constructorDecl = forEach(classDecl.members, m => m.kind === SyntaxKind.Constructor && (<ConstructorDeclaration>m).body && <ConstructorDeclaration>m);
|
||||
return constructorDecl.body.getFirstToken();
|
||||
}
|
||||
function createTestSuperCall() {
|
||||
const superCall = createCall(
|
||||
createSuper(),
|
||||
|
||||
function findOpenBraceForConstructor(sourceFile: SourceFile) {
|
||||
const classDecl = <ClassDeclaration>sourceFile.statements[0];
|
||||
const constructorDecl = forEach(classDecl.members, m => m.kind === SyntaxKind.Constructor && (<ConstructorDeclaration>m).body && <ConstructorDeclaration>m);
|
||||
return constructorDecl.body.getFirstToken();
|
||||
}
|
||||
function createTestSuperCall() {
|
||||
const superCall = createCall(
|
||||
createSuper(),
|
||||
/*typeArguments*/ undefined,
|
||||
/*argumentsArray*/ emptyArray
|
||||
);
|
||||
return createStatement(superCall);
|
||||
}
|
||||
);
|
||||
return createStatement(superCall);
|
||||
}
|
||||
|
||||
{
|
||||
const text1 = `
|
||||
class A {
|
||||
constructor() {
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace ts {
|
||||
context.enableSubstitution(SyntaxKind.Identifier);
|
||||
context.onSubstituteNode = (hint, node) => {
|
||||
node = previousOnSubstituteNode(hint, node);
|
||||
if (hint === EmitHint.Expression && node.kind === SyntaxKind.Identifier && (<Identifier>node).text === "undefined") {
|
||||
if (hint === EmitHint.Expression && isIdentifier(node) && node.escapedText === "undefined") {
|
||||
node = createPartiallyEmittedExpression(
|
||||
addSyntheticTrailingComment(
|
||||
setTextRange(
|
||||
@@ -26,7 +26,7 @@ namespace ts {
|
||||
context.enableSubstitution(SyntaxKind.Identifier);
|
||||
context.onSubstituteNode = (hint, node) => {
|
||||
node = previousOnSubstituteNode(hint, node);
|
||||
if (node.kind === SyntaxKind.Identifier && (<Identifier>node).text === "oldName") {
|
||||
if (isIdentifier(node) && node.escapedText === "oldName") {
|
||||
node = setTextRange(createIdentifier("newName"), node);
|
||||
}
|
||||
return node;
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace ts {
|
||||
|
||||
transpileOptions.reportDiagnostics = true;
|
||||
|
||||
justName = "transpile/" + name.replace(/[^a-z0-9\-. ]/ig, "") + (transpileOptions.compilerOptions.jsx ? ".tsx" : ".ts");
|
||||
justName = "transpile/" + name.replace(/[^a-z0-9\-. ]/ig, "") + (transpileOptions.compilerOptions.jsx ? Extension.Tsx : Extension.Ts);
|
||||
toBeCompiled = [{
|
||||
unitName: transpileOptions.fileName,
|
||||
content: input
|
||||
@@ -88,7 +88,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
it("Correct output for " + justName, () => {
|
||||
Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".js"), () => {
|
||||
Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ts.Extension.Js), () => {
|
||||
if (transpileResult.outputText) {
|
||||
return transpileResult.outputText;
|
||||
}
|
||||
|
||||
@@ -3,37 +3,69 @@
|
||||
|
||||
namespace ts {
|
||||
describe("parseConfigFileTextToJson", () => {
|
||||
function assertParseResult(jsonText: string, expectedConfigObject: { config?: any; error?: Diagnostic }) {
|
||||
function assertParseResult(jsonText: string, expectedConfigObject: { config?: any; error?: Diagnostic[] }) {
|
||||
const parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText);
|
||||
assert.equal(JSON.stringify(parsed), JSON.stringify(expectedConfigObject));
|
||||
}
|
||||
|
||||
function assertParseError(jsonText: string) {
|
||||
const parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText);
|
||||
assert.isTrue(undefined === parsed.config);
|
||||
assert.deepEqual(parsed.config, {});
|
||||
assert.isTrue(undefined !== parsed.error);
|
||||
}
|
||||
|
||||
function assertParseErrorWithExcludesKeyword(jsonText: string) {
|
||||
const parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText);
|
||||
const parsedCommand = ts.parseJsonConfigFileContent(parsed.config, ts.sys, "tests/cases/unittests");
|
||||
assert.isTrue(parsedCommand.errors && parsedCommand.errors.length === 1 &&
|
||||
parsedCommand.errors[0].code === ts.Diagnostics.Unknown_option_excludes_Did_you_mean_exclude.code);
|
||||
{
|
||||
const parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText);
|
||||
const parsedCommand = ts.parseJsonConfigFileContent(parsed.config, ts.sys, "tests/cases/unittests");
|
||||
assert.isTrue(parsedCommand.errors && parsedCommand.errors.length === 1 &&
|
||||
parsedCommand.errors[0].code === ts.Diagnostics.Unknown_option_excludes_Did_you_mean_exclude.code);
|
||||
}
|
||||
{
|
||||
const parsed = ts.parseJsonText("/apath/tsconfig.json", jsonText);
|
||||
const parsedCommand = ts.parseJsonSourceFileConfigFileContent(parsed, ts.sys, "tests/cases/unittests");
|
||||
assert.isTrue(parsedCommand.errors && parsedCommand.errors.length === 1 &&
|
||||
parsedCommand.errors[0].code === ts.Diagnostics.Unknown_option_excludes_Did_you_mean_exclude.code);
|
||||
}
|
||||
}
|
||||
|
||||
function getParsedCommandJson(jsonText: string, configFileName: string, basePath: string, allFileList: string[]) {
|
||||
const parsed = ts.parseConfigFileTextToJson(configFileName, jsonText);
|
||||
const host: ParseConfigHost = new Utils.MockParseConfigHost(basePath, true, allFileList);
|
||||
return ts.parseJsonConfigFileContent(parsed.config, host, basePath, /*existingOptions*/ undefined, configFileName);
|
||||
}
|
||||
|
||||
function getParsedCommandJsonNode(jsonText: string, configFileName: string, basePath: string, allFileList: string[]) {
|
||||
const parsed = ts.parseJsonText(configFileName, jsonText);
|
||||
const host: ParseConfigHost = new Utils.MockParseConfigHost(basePath, true, allFileList);
|
||||
return ts.parseJsonSourceFileConfigFileContent(parsed, host, basePath, /*existingOptions*/ undefined, configFileName);
|
||||
}
|
||||
|
||||
function assertParseFileList(jsonText: string, configFileName: string, basePath: string, allFileList: string[], expectedFileList: string[]) {
|
||||
const json = JSON.parse(jsonText);
|
||||
const host: ParseConfigHost = new Utils.MockParseConfigHost(basePath, true, allFileList);
|
||||
const parsed = ts.parseJsonConfigFileContent(json, host, basePath, /*existingOptions*/ undefined, configFileName);
|
||||
assert.isTrue(arrayIsEqualTo(parsed.fileNames.sort(), expectedFileList.sort()));
|
||||
{
|
||||
const parsed = getParsedCommandJson(jsonText, configFileName, basePath, allFileList);
|
||||
assert.isTrue(arrayIsEqualTo(parsed.fileNames.sort(), expectedFileList.sort()));
|
||||
}
|
||||
{
|
||||
const parsed = getParsedCommandJsonNode(jsonText, configFileName, basePath, allFileList);
|
||||
assert.isTrue(arrayIsEqualTo(parsed.fileNames.sort(), expectedFileList.sort()));
|
||||
}
|
||||
}
|
||||
|
||||
function assertParseFileDiagnostics(jsonText: string, configFileName: string, basePath: string, allFileList: string[], expectedDiagnosticCode: number) {
|
||||
const json = JSON.parse(jsonText);
|
||||
const host: ParseConfigHost = new Utils.MockParseConfigHost(basePath, true, allFileList);
|
||||
const parsed = ts.parseJsonConfigFileContent(json, host, basePath, /*existingOptions*/ undefined, configFileName);
|
||||
assert.isTrue(parsed.errors.length >= 0);
|
||||
assert.isTrue(parsed.errors.filter(e => e.code === expectedDiagnosticCode).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)}`);
|
||||
function assertParseFileDiagnostics(jsonText: string, configFileName: string, basePath: string, allFileList: string[], expectedDiagnosticCode: number, noLocation?: boolean) {
|
||||
{
|
||||
const parsed = getParsedCommandJson(jsonText, configFileName, basePath, allFileList);
|
||||
assert.isTrue(parsed.errors.length >= 0);
|
||||
assert.isTrue(parsed.errors.filter(e => e.code === expectedDiagnosticCode).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)}`);
|
||||
}
|
||||
{
|
||||
const parsed = getParsedCommandJsonNode(jsonText, configFileName, basePath, allFileList);
|
||||
assert.isTrue(parsed.errors.length >= 0);
|
||||
assert.isTrue(parsed.errors.filter(e => e.code === expectedDiagnosticCode).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)}`);
|
||||
if (!noLocation) {
|
||||
assert.isTrue(parsed.errors.filter(e => e.code === expectedDiagnosticCode && e.file && e.start && e.length).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)} with location information`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("returns empty config for file with only whitespaces", () => {
|
||||
@@ -199,7 +231,9 @@ namespace ts {
|
||||
}
|
||||
"files": ["file1.ts"]
|
||||
}`;
|
||||
const { configJsonObject, diagnostics } = sanitizeConfigFile("config.json", content);
|
||||
const result = parseJsonText("config.json", content);
|
||||
const diagnostics = result.parseDiagnostics;
|
||||
const configJsonObject = convertToObject(result, diagnostics);
|
||||
const expectedResult = {
|
||||
compilerOptions: {
|
||||
allowJs: true,
|
||||
@@ -229,7 +263,8 @@ namespace ts {
|
||||
"/apath/tsconfig.json",
|
||||
"tests/cases/unittests",
|
||||
["/apath/a.js"],
|
||||
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
|
||||
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code,
|
||||
/*noLocation*/ true);
|
||||
});
|
||||
|
||||
it("generates errors for empty directory", () => {
|
||||
@@ -242,7 +277,8 @@ namespace ts {
|
||||
"/apath/tsconfig.json",
|
||||
"tests/cases/unittests",
|
||||
[],
|
||||
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
|
||||
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code,
|
||||
/*noLocation*/ true);
|
||||
});
|
||||
|
||||
it("generates errors for empty include", () => {
|
||||
@@ -253,7 +289,8 @@ namespace ts {
|
||||
"/apath/tsconfig.json",
|
||||
"tests/cases/unittests",
|
||||
["/apath/a.ts"],
|
||||
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
|
||||
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code,
|
||||
/*noLocation*/ true);
|
||||
});
|
||||
|
||||
it("generates errors for includes with outDir", () => {
|
||||
@@ -267,7 +304,8 @@ namespace ts {
|
||||
"/apath/tsconfig.json",
|
||||
"tests/cases/unittests",
|
||||
["/apath/a.ts"],
|
||||
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
|
||||
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code,
|
||||
/*noLocation*/ true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace ts.projectSystem {
|
||||
for (const typing of installedTypings) {
|
||||
dependencies[typing] = "1.0.0";
|
||||
}
|
||||
return JSON.stringify({ dependencies: dependencies });
|
||||
return JSON.stringify({ dependencies });
|
||||
}
|
||||
|
||||
export function getExecutingFilePathFromLibFile(): string {
|
||||
@@ -261,9 +261,9 @@ namespace ts.projectSystem {
|
||||
return typeof (<File>s).content === "string";
|
||||
}
|
||||
|
||||
export function addFolder(fullPath: string, toPath: (s: string) => Path, fs: FileMap<FSEntry>): Folder {
|
||||
export function addFolder(fullPath: string, toPath: (s: string) => Path, fs: Map<FSEntry>): Folder {
|
||||
const path = toPath(fullPath);
|
||||
if (fs.contains(path)) {
|
||||
if (fs.has(path)) {
|
||||
Debug.assert(isFolder(fs.get(path)));
|
||||
return (<Folder>fs.get(path));
|
||||
}
|
||||
@@ -370,7 +370,7 @@ namespace ts.projectSystem {
|
||||
|
||||
private readonly output: string[] = [];
|
||||
|
||||
private fs: ts.FileMap<FSEntry>;
|
||||
private fs: Map<FSEntry>;
|
||||
private getCanonicalFileName: (s: string) => string;
|
||||
private toPath: (f: string) => Path;
|
||||
private timeoutCallbacks = new Callbacks();
|
||||
@@ -390,7 +390,7 @@ namespace ts.projectSystem {
|
||||
|
||||
reloadFS(filesOrFolders: FileOrFolder[]) {
|
||||
this.filesOrFolders = filesOrFolders;
|
||||
this.fs = createFileMap<FSEntry>();
|
||||
this.fs = createMap<FSEntry>();
|
||||
// always inject safelist file in the list of files
|
||||
for (const fileOrFolder of filesOrFolders.concat(safeList)) {
|
||||
const path = this.toPath(fileOrFolder.path);
|
||||
@@ -408,12 +408,12 @@ namespace ts.projectSystem {
|
||||
|
||||
fileExists(s: string) {
|
||||
const path = this.toPath(s);
|
||||
return this.fs.contains(path) && isFile(this.fs.get(path));
|
||||
return this.fs.has(path) && isFile(this.fs.get(path));
|
||||
}
|
||||
|
||||
getFileSize(s: string) {
|
||||
const path = this.toPath(s);
|
||||
if (this.fs.contains(path)) {
|
||||
if (this.fs.has(path)) {
|
||||
const entry = this.fs.get(path);
|
||||
if (isFile(entry)) {
|
||||
return entry.fileSize ? entry.fileSize : entry.content.length;
|
||||
@@ -424,12 +424,12 @@ namespace ts.projectSystem {
|
||||
|
||||
directoryExists(s: string) {
|
||||
const path = this.toPath(s);
|
||||
return this.fs.contains(path) && isFolder(this.fs.get(path));
|
||||
return this.fs.has(path) && isFolder(this.fs.get(path));
|
||||
}
|
||||
|
||||
getDirectories(s: string) {
|
||||
const path = this.toPath(s);
|
||||
if (!this.fs.contains(path)) {
|
||||
if (!this.fs.has(path)) {
|
||||
return [];
|
||||
}
|
||||
else {
|
||||
@@ -438,25 +438,22 @@ namespace ts.projectSystem {
|
||||
}
|
||||
}
|
||||
|
||||
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
|
||||
const that = this;
|
||||
return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), (dir) => {
|
||||
const result: FileSystemEntries = {
|
||||
directories: [],
|
||||
files: []
|
||||
};
|
||||
const dirEntry = that.fs.get(that.toPath(dir));
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
|
||||
return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, (dir) => {
|
||||
const directories: string[] = [];
|
||||
const files: string[] = [];
|
||||
const dirEntry = this.fs.get(this.toPath(dir));
|
||||
if (isFolder(dirEntry)) {
|
||||
dirEntry.entries.forEach((entry) => {
|
||||
if (isFolder(entry)) {
|
||||
result.directories.push(entry.fullPath);
|
||||
directories.push(entry.fullPath);
|
||||
}
|
||||
else if (isFile(entry)) {
|
||||
result.files.push(entry.fullPath);
|
||||
files.push(entry.fullPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
return { directories, files };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -472,7 +469,7 @@ namespace ts.projectSystem {
|
||||
}
|
||||
|
||||
createHash(s: string): string {
|
||||
return s;
|
||||
return Harness.LanguageService.mockHash(s);
|
||||
}
|
||||
|
||||
triggerDirectoryWatcherCallback(directoryName: string, fileName: string): void {
|
||||
@@ -485,12 +482,12 @@ namespace ts.projectSystem {
|
||||
}
|
||||
}
|
||||
|
||||
triggerFileWatcherCallback(fileName: string, removed?: boolean): void {
|
||||
triggerFileWatcherCallback(fileName: string, eventKind: FileWatcherEventKind): void {
|
||||
const path = this.toPath(fileName);
|
||||
const callbacks = this.watchedFiles.get(path);
|
||||
if (callbacks) {
|
||||
for (const callback of callbacks) {
|
||||
callback(path, removed);
|
||||
callback(path, eventKind);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -564,7 +561,7 @@ namespace ts.projectSystem {
|
||||
}
|
||||
|
||||
clearOutput() {
|
||||
this.output.length = 0;
|
||||
clear(this.output);
|
||||
}
|
||||
|
||||
readonly readFile = (s: string) => (<File>this.fs.get(this.toPath(s))).content;
|
||||
@@ -735,7 +732,7 @@ namespace ts.projectSystem {
|
||||
checkNumberOfConfiguredProjects(projectService, 1);
|
||||
|
||||
const project = projectService.configuredProjects[0];
|
||||
checkProjectActualFiles(project, [file1.path, libFile.path, file2.path]);
|
||||
checkProjectActualFiles(project, [file1.path, libFile.path, file2.path, configFile.path]);
|
||||
checkProjectRootFiles(project, [file1.path, file2.path]);
|
||||
// watching all files except one that was open
|
||||
checkWatchedFiles(host, [configFile.path, file2.path, libFile.path]);
|
||||
@@ -771,7 +768,7 @@ namespace ts.projectSystem {
|
||||
|
||||
// remove the tsconfig file
|
||||
host.reloadFS(filesWithoutConfig);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
|
||||
checkNumberOfInferredProjects(projectService, 2);
|
||||
checkNumberOfConfiguredProjects(projectService, 0);
|
||||
@@ -928,7 +925,7 @@ namespace ts.projectSystem {
|
||||
"files": ["${commonFile1.path}"]
|
||||
}`;
|
||||
host.reloadFS(files);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
|
||||
checkNumberOfConfiguredProjects(projectService, 1);
|
||||
checkProjectRootFiles(project, [commonFile1.path]);
|
||||
@@ -992,7 +989,7 @@ namespace ts.projectSystem {
|
||||
|
||||
checkNumberOfConfiguredProjects(projectService, 1);
|
||||
const project = projectService.configuredProjects[0];
|
||||
checkProjectActualFiles(project, [file1.path, nodeModuleFile.path]);
|
||||
checkProjectActualFiles(project, [file1.path, nodeModuleFile.path, configFile.path]);
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
|
||||
configFile.content = `{
|
||||
@@ -1002,8 +999,8 @@ namespace ts.projectSystem {
|
||||
"files": ["${file1.path}"]
|
||||
}`;
|
||||
host.reloadFS(files);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
checkProjectActualFiles(project, [file1.path, classicModuleFile.path]);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
checkProjectActualFiles(project, [file1.path, classicModuleFile.path, configFile.path]);
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
});
|
||||
|
||||
@@ -1433,7 +1430,7 @@ namespace ts.projectSystem {
|
||||
};
|
||||
|
||||
host.reloadFS([file1, modifiedFile2, file3]);
|
||||
host.triggerFileWatcherCallback(modifiedFile2.path, /*removed*/ false);
|
||||
host.triggerFileWatcherCallback(modifiedFile2.path, FileWatcherEventKind.Changed);
|
||||
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file1.path, modifiedFile2.path, file3.path]);
|
||||
@@ -1465,7 +1462,7 @@ namespace ts.projectSystem {
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
|
||||
host.reloadFS([file1, file3]);
|
||||
host.triggerFileWatcherCallback(file2.path, /*removed*/ true);
|
||||
host.triggerFileWatcherCallback(file2.path, FileWatcherEventKind.Deleted);
|
||||
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 2 });
|
||||
|
||||
@@ -1566,7 +1563,7 @@ namespace ts.projectSystem {
|
||||
host.reloadFS([file1, file2, file3, configFile]);
|
||||
host.triggerDirectoryWatcherCallback(getDirectoryPath(configFile.path), configFile.path);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path, file3.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path, file3.path, configFile.path]);
|
||||
});
|
||||
|
||||
it("correctly migrate files between projects", () => {
|
||||
@@ -1624,7 +1621,7 @@ namespace ts.projectSystem {
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, configFile.path]);
|
||||
|
||||
host.reloadFS([file1, file2, configFile]);
|
||||
|
||||
@@ -1655,7 +1652,7 @@ namespace ts.projectSystem {
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, configFile.path]);
|
||||
|
||||
const modifiedConfigFile = {
|
||||
path: configFile.path,
|
||||
@@ -1663,7 +1660,7 @@ namespace ts.projectSystem {
|
||||
};
|
||||
|
||||
host.reloadFS([file1, file2, modifiedConfigFile]);
|
||||
host.triggerFileWatcherCallback(configFile.path, /*removed*/ false);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectRootFiles(projectService.configuredProjects[0], [file1.path, file2.path]);
|
||||
@@ -1688,7 +1685,7 @@ namespace ts.projectSystem {
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path, configFile.path]);
|
||||
|
||||
const modifiedConfigFile = {
|
||||
path: configFile.path,
|
||||
@@ -1696,7 +1693,7 @@ namespace ts.projectSystem {
|
||||
};
|
||||
|
||||
host.reloadFS([file1, file2, modifiedConfigFile]);
|
||||
host.triggerFileWatcherCallback(configFile.path, /*removed*/ false);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectRootFiles(projectService.configuredProjects[0], [file1.path, file2.path]);
|
||||
@@ -1769,14 +1766,14 @@ namespace ts.projectSystem {
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path, config.path]);
|
||||
|
||||
projectService.openClientFile(file2.path);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path, config.path]);
|
||||
|
||||
host.reloadFS([file1, file2]);
|
||||
host.triggerFileWatcherCallback(config.path, /*removed*/ true);
|
||||
host.triggerFileWatcherCallback(config.path, FileWatcherEventKind.Deleted);
|
||||
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 2 });
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file1.path]);
|
||||
@@ -1808,13 +1805,13 @@ namespace ts.projectSystem {
|
||||
});
|
||||
projectService.openClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path, config.path]);
|
||||
|
||||
projectService.closeClientFile(f1.path);
|
||||
|
||||
projectService.openClientFile(f2.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1, inferredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path, config.path]);
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [f2.path]);
|
||||
});
|
||||
|
||||
@@ -1838,7 +1835,7 @@ namespace ts.projectSystem {
|
||||
|
||||
// HTML file will not be included in any projects yet
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, config.path]);
|
||||
|
||||
// Specify .html extension as mixed content
|
||||
const extraFileExtensions = [{ extension: ".html", scriptKind: ScriptKind.JS, isMixedContent: true }];
|
||||
@@ -1847,7 +1844,7 @@ namespace ts.projectSystem {
|
||||
|
||||
// HTML file still not included in the project as it is closed
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, config.path]);
|
||||
|
||||
// Open HTML file
|
||||
projectService.applyChangesInOpenFiles(
|
||||
@@ -1857,7 +1854,7 @@ namespace ts.projectSystem {
|
||||
|
||||
// Now HTML file is included in the project
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path, config.path]);
|
||||
|
||||
// Check identifiers defined in HTML content are available in .ts file
|
||||
const project = projectService.configuredProjects[0];
|
||||
@@ -1872,7 +1869,7 @@ namespace ts.projectSystem {
|
||||
|
||||
// HTML file is still included in project
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path, config.path]);
|
||||
|
||||
// Check identifiers defined in HTML content are not available in .ts file
|
||||
completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 5);
|
||||
@@ -2028,7 +2025,7 @@ namespace ts.projectSystem {
|
||||
projectService.openExternalProject({ projectFileName, options: {}, rootFiles: [{ fileName: file1.path, scriptKind: ScriptKind.JS, hasMixedContent: true }] });
|
||||
|
||||
checkNumberOfProjects(projectService, { externalProjects: 1 });
|
||||
checkWatchedFiles(host, []);
|
||||
checkWatchedFiles(host, [libFile.path]); // watching the "missing" lib file
|
||||
|
||||
const project = projectService.externalProjects[0];
|
||||
|
||||
@@ -2204,7 +2201,8 @@ namespace ts.projectSystem {
|
||||
projectService.closeClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({});
|
||||
|
||||
for (const f of [f2, f3]) {
|
||||
for (const f of [f1, f2, f3]) {
|
||||
// There shouldnt be any script info as we closed the file that resulted in creation of it
|
||||
const scriptInfo = projectService.getScriptInfoForNormalizedPath(server.toNormalizedPath(f.path));
|
||||
assert.equal(scriptInfo.containingProjects.length, 0, `expect 0 containing projects for '${f.path}'`);
|
||||
}
|
||||
@@ -2257,7 +2255,7 @@ namespace ts.projectSystem {
|
||||
assert.isFalse(lastEvent.data.languageServiceEnabled, "Language service state");
|
||||
|
||||
host.reloadFS([f1, f2, configWithExclude]);
|
||||
host.triggerFileWatcherCallback(config.path, /*removed*/ false);
|
||||
host.triggerFileWatcherCallback(config.path, FileWatcherEventKind.Changed);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
assert.isTrue(project.languageServiceEnabled, "Language service enabled");
|
||||
@@ -2334,6 +2332,50 @@ namespace ts.projectSystem {
|
||||
const navbar = projectService.externalProjects[0].getLanguageService(/*ensureSynchronized*/ false).getNavigationBarItems(f1.path);
|
||||
assert.equal(navbar[0].spans[0].length, f1.content.length);
|
||||
});
|
||||
|
||||
it("deleting config file opened from the external project works", () => {
|
||||
const site = {
|
||||
path: "/user/someuser/project/js/site.js",
|
||||
content: ""
|
||||
};
|
||||
const configFile = {
|
||||
path: "/user/someuser/project/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const projectFileName = "/user/someuser/project/WebApplication6.csproj";
|
||||
const host = createServerHost([libFile, site, configFile]);
|
||||
const projectService = createProjectService(host);
|
||||
|
||||
const externalProject: protocol.ExternalProject = {
|
||||
projectFileName,
|
||||
rootFiles: [toExternalFile(site.path), toExternalFile(configFile.path)],
|
||||
options: { allowJs: false },
|
||||
typeAcquisition: { "include": [] }
|
||||
};
|
||||
|
||||
projectService.openExternalProjects([externalProject]);
|
||||
|
||||
let knownProjects = projectService.synchronizeProjectList([]);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1, externalProjects: 0, inferredProjects: 0 });
|
||||
|
||||
const configProject = projectService.configuredProjects[0];
|
||||
checkProjectActualFiles(configProject, [libFile.path]);
|
||||
|
||||
const diagnostics = configProject.getAllProjectErrors();
|
||||
assert.equal(diagnostics[0].code, Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
|
||||
|
||||
host.reloadFS([libFile, site]);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Deleted);
|
||||
|
||||
knownProjects = projectService.synchronizeProjectList(map(knownProjects, proj => proj.info));
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 0, inferredProjects: 0 });
|
||||
|
||||
externalProject.rootFiles.length = 1;
|
||||
projectService.openExternalProjects([externalProject]);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 1, inferredProjects: 0 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [site.path, libFile.path]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Proper errors", () => {
|
||||
@@ -2487,11 +2529,11 @@ namespace ts.projectSystem {
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path, tsconfig.path]);
|
||||
|
||||
// rename tsconfig.json back to lib.ts
|
||||
host.reloadFS([f1, f2]);
|
||||
host.triggerFileWatcherCallback(tsconfig.path, /*removed*/ true);
|
||||
host.triggerFileWatcherCallback(tsconfig.path, FileWatcherEventKind.Deleted);
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, f2.path]),
|
||||
@@ -2545,8 +2587,8 @@ namespace ts.projectSystem {
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 2 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [cLib.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[1], [dLib.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [cLib.path, cTsconfig.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[1], [dLib.path, dTsconfig.path]);
|
||||
|
||||
// remove one config file
|
||||
projectService.openExternalProject({
|
||||
@@ -2556,7 +2598,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [dLib.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [dLib.path, dTsconfig.path]);
|
||||
|
||||
// remove second config file
|
||||
projectService.openExternalProject({
|
||||
@@ -2576,8 +2618,8 @@ namespace ts.projectSystem {
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 2 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [cLib.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[1], [dLib.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [cLib.path, cTsconfig.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[1], [dLib.path, dTsconfig.path]);
|
||||
|
||||
// close all projects - no projects should be opened
|
||||
projectService.closeExternalProject(projectName);
|
||||
@@ -2633,13 +2675,13 @@ namespace ts.projectSystem {
|
||||
projectService.openClientFile(app.path);
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [libES5.path, app.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [libES5.path, app.path, config1.path]);
|
||||
|
||||
host.reloadFS([libES5, libES2015Promise, app, config2]);
|
||||
host.triggerFileWatcherCallback(config1.path);
|
||||
host.triggerFileWatcherCallback(config1.path, FileWatcherEventKind.Changed);
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [libES5.path, libES2015Promise.path, app.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [libES5.path, libES2015Promise.path, app.path, config2.path]);
|
||||
});
|
||||
|
||||
it("should handle non-existing directories in config file", () => {
|
||||
@@ -2694,7 +2736,7 @@ namespace ts.projectSystem {
|
||||
|
||||
projectService.openClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path, barTypings.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path, barTypings.path, config.path]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2765,7 +2807,7 @@ namespace ts.projectSystem {
|
||||
|
||||
projectService.openClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path, t1.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path, t1.path, tsconfig.path]);
|
||||
|
||||
// delete t1
|
||||
host.reloadFS([f1, tsconfig]);
|
||||
@@ -2774,7 +2816,7 @@ namespace ts.projectSystem {
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path, tsconfig.path]);
|
||||
|
||||
// create t2
|
||||
host.reloadFS([f1, tsconfig, t2]);
|
||||
@@ -2783,7 +2825,7 @@ namespace ts.projectSystem {
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path, t2.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path, t2.path, tsconfig.path]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2859,7 +2901,7 @@ namespace ts.projectSystem {
|
||||
const moduleFileNewPath = "/a/b/moduleFile1.ts";
|
||||
moduleFile.path = moduleFileNewPath;
|
||||
host.reloadFS([moduleFile, file1]);
|
||||
host.triggerFileWatcherCallback(moduleFileOldPath);
|
||||
host.triggerFileWatcherCallback(moduleFileOldPath, FileWatcherEventKind.Changed);
|
||||
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
diags = <server.protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
@@ -2867,7 +2909,7 @@ namespace ts.projectSystem {
|
||||
|
||||
moduleFile.path = moduleFileOldPath;
|
||||
host.reloadFS([moduleFile, file1]);
|
||||
host.triggerFileWatcherCallback(moduleFileNewPath);
|
||||
host.triggerFileWatcherCallback(moduleFileNewPath, FileWatcherEventKind.Changed);
|
||||
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
@@ -2911,7 +2953,7 @@ namespace ts.projectSystem {
|
||||
const moduleFileNewPath = "/a/b/moduleFile1.ts";
|
||||
moduleFile.path = moduleFileNewPath;
|
||||
host.reloadFS([moduleFile, file1, configFile]);
|
||||
host.triggerFileWatcherCallback(moduleFileOldPath);
|
||||
host.triggerFileWatcherCallback(moduleFileOldPath, FileWatcherEventKind.Changed);
|
||||
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
diags = <server.protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
@@ -2919,7 +2961,7 @@ namespace ts.projectSystem {
|
||||
|
||||
moduleFile.path = moduleFileOldPath;
|
||||
host.reloadFS([moduleFile, file1, configFile]);
|
||||
host.triggerFileWatcherCallback(moduleFileNewPath);
|
||||
host.triggerFileWatcherCallback(moduleFileNewPath, FileWatcherEventKind.Changed);
|
||||
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
diags = <server.protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
@@ -2968,7 +3010,7 @@ namespace ts.projectSystem {
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path, node.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [f1.path, node.path, config.path]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3084,7 +3126,7 @@ namespace ts.projectSystem {
|
||||
}
|
||||
}`;
|
||||
host.reloadFS([file, configFile]);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
serverEventManager.checkEventCountOfType("configFileDiag", 2);
|
||||
|
||||
@@ -3092,7 +3134,7 @@ namespace ts.projectSystem {
|
||||
"compilerOptions": {}
|
||||
}`;
|
||||
host.reloadFS([file, configFile]);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
serverEventManager.checkEventCountOfType("configFileDiag", 3);
|
||||
});
|
||||
@@ -3979,4 +4021,69 @@ namespace ts.projectSystem {
|
||||
assert.isUndefined(project.getCompilerOptions().maxNodeModuleJsDepth);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Options Diagnostic locations reported correctly with changes in configFile contents", () => {
|
||||
it("when options change", () => {
|
||||
const file = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 10"
|
||||
};
|
||||
const configFileContentBeforeComment = `{`;
|
||||
const configFileContentComment = `
|
||||
// comment`;
|
||||
const configFileContentAfterComment = `
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"declaration": true
|
||||
}
|
||||
}`;
|
||||
const configFileContentWithComment = configFileContentBeforeComment + configFileContentComment + configFileContentAfterComment;
|
||||
const configFileContentWithoutCommentLine = configFileContentBeforeComment + configFileContentAfterComment;
|
||||
|
||||
const configFile = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: configFileContentWithComment
|
||||
};
|
||||
const host = createServerHost([file, libFile, configFile]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([file], session);
|
||||
|
||||
const projectService = session.getProjectService();
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
const projectName = projectService.configuredProjects[0].getProjectName();
|
||||
|
||||
const diags = session.executeCommand(<server.protocol.SemanticDiagnosticsSyncRequest>{
|
||||
type: "request",
|
||||
command: server.CommandNames.SemanticDiagnosticsSync,
|
||||
seq: 2,
|
||||
arguments: { file: configFile.path, projectFileName: projectName, includeLinePosition: true }
|
||||
}).response;
|
||||
assert.isTrue(diags.length === 2);
|
||||
|
||||
configFile.content = configFileContentWithoutCommentLine;
|
||||
host.reloadFS([file, configFile]);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
|
||||
const diagsAfterEdit = session.executeCommand(<server.protocol.SemanticDiagnosticsSyncRequest>{
|
||||
type: "request",
|
||||
command: server.CommandNames.SemanticDiagnosticsSync,
|
||||
seq: 2,
|
||||
arguments: { file: configFile.path, projectFileName: projectName, includeLinePosition: true }
|
||||
}).response;
|
||||
assert.isTrue(diagsAfterEdit.length === 2);
|
||||
|
||||
verifyDiagnostic(diags[0], diagsAfterEdit[0]);
|
||||
verifyDiagnostic(diags[1], diagsAfterEdit[1]);
|
||||
|
||||
function verifyDiagnostic(beforeEditDiag: server.protocol.DiagnosticWithLinePosition, afterEditDiag: server.protocol.DiagnosticWithLinePosition) {
|
||||
assert.equal(beforeEditDiag.message, afterEditDiag.message);
|
||||
assert.equal(beforeEditDiag.code, afterEditDiag.code);
|
||||
assert.equal(beforeEditDiag.category, afterEditDiag.category);
|
||||
assert.equal(beforeEditDiag.startLocation.line, afterEditDiag.startLocation.line + 1);
|
||||
assert.equal(beforeEditDiag.startLocation.offset, afterEditDiag.startLocation.offset);
|
||||
assert.equal(beforeEditDiag.endLocation.line, afterEditDiag.endLocation.line + 1);
|
||||
assert.equal(beforeEditDiag.endLocation.offset, afterEditDiag.endLocation.offset);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,6 +44,18 @@ namespace ts.projectSystem {
|
||||
});
|
||||
}
|
||||
|
||||
function trackingLogger(): { log(message: string): void, finish(): string[] } {
|
||||
const logs: string[] = [];
|
||||
return {
|
||||
log(message) {
|
||||
logs.push(message);
|
||||
},
|
||||
finish() {
|
||||
return logs;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
import typingsName = TI.typingsName;
|
||||
|
||||
describe("local module", () => {
|
||||
@@ -80,7 +92,7 @@ namespace ts.projectSystem {
|
||||
const service = createProjectService(host, { typingsInstaller: installer });
|
||||
service.openClientFile(f1.path);
|
||||
service.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(service.configuredProjects[0], [f1.path, f2.path]);
|
||||
checkProjectActualFiles(service.configuredProjects[0], [f1.path, f2.path, config.path]);
|
||||
installer.installAll(0);
|
||||
});
|
||||
});
|
||||
@@ -133,12 +145,12 @@ namespace ts.projectSystem {
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
const p = projectService.configuredProjects[0];
|
||||
checkProjectActualFiles(p, [file1.path]);
|
||||
checkProjectActualFiles(p, [file1.path, tsconfig.path]);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, jquery.path]);
|
||||
checkProjectActualFiles(p, [file1.path, jquery.path, tsconfig.path]);
|
||||
});
|
||||
|
||||
it("inferred project (typings installed)", () => {
|
||||
@@ -663,16 +675,22 @@ namespace ts.projectSystem {
|
||||
path: "/node_modules/jquery/package.json",
|
||||
content: JSON.stringify({ name: "jquery" })
|
||||
};
|
||||
// Should not search deeply in node_modules.
|
||||
const nestedPackage = {
|
||||
path: "/node_modules/jquery/nested/package.json",
|
||||
content: JSON.stringify({ name: "nested" }),
|
||||
};
|
||||
const jqueryDTS = {
|
||||
path: "/tmp/node_modules/@types/jquery/index.d.ts",
|
||||
content: ""
|
||||
};
|
||||
const host = createServerHost([app, jsconfig, jquery, jqueryPackage]);
|
||||
const host = createServerHost([app, jsconfig, jquery, jqueryPackage, nestedPackage]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery") });
|
||||
super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery", "nested") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
installWorker(_requestId: number, args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
assert.deepEqual(args, [`@types/jquery@ts${versionMajorMinor}`]);
|
||||
const installedTypings = ["@types/jquery"];
|
||||
const typingFiles = [jqueryDTS];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
@@ -684,12 +702,12 @@ namespace ts.projectSystem {
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
const p = projectService.configuredProjects[0];
|
||||
checkProjectActualFiles(p, [app.path]);
|
||||
checkProjectActualFiles(p, [app.path, jsconfig.path]);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(p, [app.path, jqueryDTS.path]);
|
||||
checkProjectActualFiles(p, [app.path, jqueryDTS.path, jsconfig.path]);
|
||||
});
|
||||
|
||||
it("configured projects discover from bower_components", () => {
|
||||
@@ -730,13 +748,13 @@ namespace ts.projectSystem {
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
const p = projectService.configuredProjects[0];
|
||||
checkProjectActualFiles(p, [app.path]);
|
||||
checkWatchedFiles(host, [jsconfig.path, "/bower_components", "/node_modules"]);
|
||||
checkProjectActualFiles(p, [app.path, jsconfig.path]);
|
||||
checkWatchedFiles(host, [jsconfig.path, "/bower_components", "/node_modules", libFile.path]);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(p, [app.path, jqueryDTS.path]);
|
||||
checkProjectActualFiles(p, [app.path, jqueryDTS.path, jsconfig.path]);
|
||||
});
|
||||
|
||||
it("configured projects discover from bower.json", () => {
|
||||
@@ -777,12 +795,12 @@ namespace ts.projectSystem {
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
const p = projectService.configuredProjects[0];
|
||||
checkProjectActualFiles(p, [app.path]);
|
||||
checkProjectActualFiles(p, [app.path, jsconfig.path]);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(p, [app.path, jqueryDTS.path]);
|
||||
checkProjectActualFiles(p, [app.path, jqueryDTS.path, jsconfig.path]);
|
||||
});
|
||||
|
||||
it("Malformed package.json should be watched", () => {
|
||||
@@ -820,7 +838,7 @@ namespace ts.projectSystem {
|
||||
installer.checkPendingCommands(/*expectedCount*/ 0);
|
||||
|
||||
host.reloadFS([f, fixedPackageJson]);
|
||||
host.triggerFileWatcherCallback(fixedPackageJson.path, /*removed*/ false);
|
||||
host.triggerFileWatcherCallback(fixedPackageJson.path, FileWatcherEventKind.Changed);
|
||||
// expected install request
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
@@ -1009,6 +1027,8 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
describe("discover typings", () => {
|
||||
const emptySafeList = emptyMap;
|
||||
|
||||
it("should use mappings from safe list", () => {
|
||||
const app = {
|
||||
path: "/a/b/app.js",
|
||||
@@ -1022,10 +1042,17 @@ namespace ts.projectSystem {
|
||||
path: "/a/b/chroma.min.js",
|
||||
content: ""
|
||||
};
|
||||
const cache = createMap<string>();
|
||||
|
||||
const safeList = createMapFromTemplate({ jquery: "jquery", chroma: "chroma-js" });
|
||||
|
||||
const host = createServerHost([app, jquery, chroma]);
|
||||
const result = JsTyping.discoverTypings(host, [app.path, jquery.path, chroma.path], getDirectoryPath(<Path>app.path), /*safeListPath*/ undefined, cache, { enable: true }, []);
|
||||
const logger = trackingLogger();
|
||||
const result = JsTyping.discoverTypings(host, logger.log, [app.path, jquery.path, chroma.path], getDirectoryPath(<Path>app.path), safeList, emptyMap, { enable: true }, emptyArray);
|
||||
assert.deepEqual(logger.finish(), [
|
||||
'Inferred typings from file names: ["jquery","chroma-js"]',
|
||||
"Inferred typings from unresolved imports: []",
|
||||
'Result: {"cachedTypingPaths":[],"newTypingNames":["jquery","chroma-js"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}',
|
||||
]);
|
||||
assert.deepEqual(result.newTypingNames, ["jquery", "chroma-js"]);
|
||||
});
|
||||
|
||||
@@ -1038,7 +1065,12 @@ namespace ts.projectSystem {
|
||||
const cache = createMap<string>();
|
||||
|
||||
for (const name of JsTyping.nodeCoreModuleList) {
|
||||
const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(<Path>f.path), /*safeListPath*/ undefined, cache, { enable: true }, [name, "somename"]);
|
||||
const logger = trackingLogger();
|
||||
const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(<Path>f.path), emptySafeList, cache, { enable: true }, [name, "somename"]);
|
||||
assert.deepEqual(logger.finish(), [
|
||||
'Inferred typings from unresolved imports: ["node","somename"]',
|
||||
'Result: {"cachedTypingPaths":[],"newTypingNames":["node","somename"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}',
|
||||
]);
|
||||
assert.deepEqual(result.newTypingNames.sort(), ["node", "somename"]);
|
||||
}
|
||||
});
|
||||
@@ -1054,10 +1086,45 @@ namespace ts.projectSystem {
|
||||
};
|
||||
const host = createServerHost([f, node]);
|
||||
const cache = createMapFromTemplate<string>({ "node": node.path });
|
||||
const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(<Path>f.path), /*safeListPath*/ undefined, cache, { enable: true }, ["fs", "bar"]);
|
||||
const logger = trackingLogger();
|
||||
const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(<Path>f.path), emptySafeList, cache, { enable: true }, ["fs", "bar"]);
|
||||
assert.deepEqual(logger.finish(), [
|
||||
'Inferred typings from unresolved imports: ["node","bar"]',
|
||||
'Result: {"cachedTypingPaths":["/a/b/node.d.ts"],"newTypingNames":["bar"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}',
|
||||
]);
|
||||
assert.deepEqual(result.cachedTypingPaths, [node.path]);
|
||||
assert.deepEqual(result.newTypingNames, ["bar"]);
|
||||
});
|
||||
|
||||
it("should search only 2 levels deep", () => {
|
||||
const app = {
|
||||
path: "/app.js",
|
||||
content: "",
|
||||
};
|
||||
const a = {
|
||||
path: "/node_modules/a/package.json",
|
||||
content: JSON.stringify({ name: "a" }),
|
||||
};
|
||||
const b = {
|
||||
path: "/node_modules/a/b/package.json",
|
||||
content: JSON.stringify({ name: "b" }),
|
||||
};
|
||||
const host = createServerHost([app, a, b]);
|
||||
const cache = createMap<string>();
|
||||
const logger = trackingLogger();
|
||||
const result = JsTyping.discoverTypings(host, logger.log, [app.path], getDirectoryPath(<Path>app.path), emptySafeList, cache, { enable: true }, /*unresolvedImports*/ []);
|
||||
assert.deepEqual(logger.finish(), [
|
||||
'Searching for typing names in /node_modules; all files: ["/node_modules/a/package.json"]',
|
||||
' Found package names: ["a"]',
|
||||
"Inferred typings from unresolved imports: []",
|
||||
'Result: {"cachedTypingPaths":[],"newTypingNames":["a"],"filesToWatch":["/bower_components","/node_modules"]}',
|
||||
]);
|
||||
assert.deepEqual(result, {
|
||||
cachedTypingPaths: [],
|
||||
newTypingNames: ["a"], // But not "b"
|
||||
filesToWatch: ["/bower_components", "/node_modules"],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("telemetry events", () => {
|
||||
@@ -1066,7 +1133,7 @@ namespace ts.projectSystem {
|
||||
path: "/a/app.js",
|
||||
content: ""
|
||||
};
|
||||
const package = {
|
||||
const packageFile = {
|
||||
path: "/a/package.json",
|
||||
content: JSON.stringify({ dependencies: { "commander": "1.0.0" } })
|
||||
};
|
||||
@@ -1075,7 +1142,7 @@ namespace ts.projectSystem {
|
||||
path: cachePath + "node_modules/@types/commander/index.d.ts",
|
||||
content: "export let x: number"
|
||||
};
|
||||
const host = createServerHost([f1, package]);
|
||||
const host = createServerHost([f1, packageFile]);
|
||||
let seenTelemetryEvent = false;
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
@@ -1115,7 +1182,7 @@ namespace ts.projectSystem {
|
||||
path: "/a/app.js",
|
||||
content: ""
|
||||
};
|
||||
const package = {
|
||||
const packageFile = {
|
||||
path: "/a/package.json",
|
||||
content: JSON.stringify({ dependencies: { "commander": "1.0.0" } })
|
||||
};
|
||||
@@ -1124,7 +1191,7 @@ namespace ts.projectSystem {
|
||||
path: cachePath + "node_modules/@types/commander/index.d.ts",
|
||||
content: "export let x: number"
|
||||
};
|
||||
const host = createServerHost([f1, package]);
|
||||
const host = createServerHost([f1, packageFile]);
|
||||
let beginEvent: server.BeginInstallTypes;
|
||||
let endEvent: server.EndInstallTypes;
|
||||
const installer = new (class extends Installer {
|
||||
@@ -1166,12 +1233,12 @@ namespace ts.projectSystem {
|
||||
path: "/a/app.js",
|
||||
content: ""
|
||||
};
|
||||
const package = {
|
||||
const packageFile = {
|
||||
path: "/a/package.json",
|
||||
content: JSON.stringify({ dependencies: { "commander": "1.0.0" } })
|
||||
};
|
||||
const cachePath = "/a/cache/";
|
||||
const host = createServerHost([f1, package]);
|
||||
const host = createServerHost([f1, packageFile]);
|
||||
let beginEvent: server.BeginInstallTypes;
|
||||
let endEvent: server.EndInstallTypes;
|
||||
const installer = new (class extends Installer {
|
||||
@@ -1206,4 +1273,4 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path]);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function lineColToPosition(lineIndex: server.LineIndex, line: number, col: number) {
|
||||
const lineInfo = lineIndex.lineNumberToInfo(line);
|
||||
return (lineInfo.offset + col - 1);
|
||||
return lineIndex.absolutePositionOfStartOfLine(line) + (col - 1);
|
||||
}
|
||||
|
||||
function validateEdit(lineIndex: server.LineIndex, sourceText: string, position: number, deleteLength: number, insertString: string): void {
|
||||
@@ -271,7 +270,7 @@ and grew 1cm per day`;
|
||||
});
|
||||
|
||||
it("Edit ScriptVersionCache ", () => {
|
||||
const svc = server.ScriptVersionCache.fromString(<server.ServerHost>ts.sys, testContent);
|
||||
const svc = server.ScriptVersionCache.fromString(testContent);
|
||||
let checkText = testContent;
|
||||
|
||||
for (let i = 0; i < iterationCount; i++) {
|
||||
@@ -298,20 +297,17 @@ and grew 1cm per day`;
|
||||
|
||||
it("Line/offset from pos", () => {
|
||||
for (let i = 0; i < iterationCount; i++) {
|
||||
const lp = lineIndex.charOffsetToLineNumberAndPos(rsa[i]);
|
||||
const lp = lineIndex.positionToLineOffset(rsa[i]);
|
||||
const lac = ts.computeLineAndCharacterOfPosition(lineMap, rsa[i]);
|
||||
assert.equal(lac.line + 1, lp.line, "Line number mismatch " + (lac.line + 1) + " " + lp.line + " " + i);
|
||||
assert.equal(lac.character, (lp.offset), "Charachter offset mismatch " + lac.character + " " + lp.offset + " " + i);
|
||||
assert.equal(lac.character, lp.offset - 1, "Character offset mismatch " + lac.character + " " + (lp.offset - 1) + " " + i);
|
||||
}
|
||||
});
|
||||
|
||||
it("Start pos from line", () => {
|
||||
for (let i = 0; i < iterationCount; i++) {
|
||||
for (let j = 0; j < lines.length; j++) {
|
||||
const lineInfo = lineIndex.lineNumberToInfo(j + 1);
|
||||
const lineIndexOffset = lineInfo.offset;
|
||||
const lineMapOffset = lineMap[j];
|
||||
assert.equal(lineIndexOffset, lineMapOffset);
|
||||
assert.equal(lineIndex.absolutePositionOfStartOfLine(j + 1), lineMap[j]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -209,15 +209,15 @@ namespace Utils {
|
||||
}
|
||||
}
|
||||
|
||||
readFile(path: string): string {
|
||||
readFile(path: string): string | undefined {
|
||||
const value = this.traversePath(path);
|
||||
if (value && value.isFile()) {
|
||||
return value.content.content;
|
||||
}
|
||||
}
|
||||
|
||||
readDirectory(path: string, extensions: string[], excludes: string[], includes: string[]) {
|
||||
return ts.matchFiles(path, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, (path: string) => this.getAccessibleFileSystemEntries(path));
|
||||
readDirectory(path: string, extensions: ReadonlyArray<string>, excludes: ReadonlyArray<string>, includes: ReadonlyArray<string>, depth: number) {
|
||||
return ts.matchFiles(path, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, depth, (path: string) => this.getAccessibleFileSystemEntries(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+2
-1
@@ -3483,7 +3483,7 @@ interface DragEvent extends MouseEvent {
|
||||
|
||||
declare var DragEvent: {
|
||||
prototype: DragEvent;
|
||||
new(): DragEvent;
|
||||
new(type: "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart" | "drop", dragEventInit?: { dataTransfer?: DataTransfer }): DragEvent;
|
||||
};
|
||||
|
||||
interface DynamicsCompressorNode extends AudioNode {
|
||||
@@ -8224,6 +8224,7 @@ interface Navigator extends Object, NavigatorID, NavigatorOnLine, NavigatorConte
|
||||
readonly serviceWorker: ServiceWorkerContainer;
|
||||
readonly webdriver: boolean;
|
||||
readonly hardwareConcurrency: number;
|
||||
readonly languages: string[];
|
||||
getGamepads(): Gamepad[];
|
||||
javaEnabled(): boolean;
|
||||
msLaunchUri(uri: string, successCallback?: MSLaunchUriCallback, noHandlerCallback?: MSLaunchUriCallback): void;
|
||||
|
||||
Vendored
+1
-1
@@ -1,8 +1,8 @@
|
||||
/// <reference path="lib.es2015.core.d.ts" />
|
||||
/// <reference path="lib.es2015.collection.d.ts" />
|
||||
/// <reference path="lib.es2015.generator.d.ts" />
|
||||
/// <reference path="lib.es2015.iterable.d.ts" />
|
||||
/// <reference path="lib.es2015.promise.d.ts" />
|
||||
/// <reference path="lib.es2015.iterable.d.ts" />
|
||||
/// <reference path="lib.es2015.proxy.d.ts" />
|
||||
/// <reference path="lib.es2015.reflect.d.ts" />
|
||||
/// <reference path="lib.es2015.symbol.d.ts" />
|
||||
|
||||
Vendored
+7
-7
@@ -1635,7 +1635,7 @@ interface Int8Array {
|
||||
* @param thisArg An object to which the this keyword can refer in the callbackfn function.
|
||||
* If thisArg is omitted, undefined is used as the this value.
|
||||
*/
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Int8Array) => number, thisArg: any): Int8Array;
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Int8Array) => number, thisArg?: any): Int8Array;
|
||||
|
||||
/**
|
||||
* Calls the specified callback function for all the elements in an array. The return value of
|
||||
@@ -1902,7 +1902,7 @@ interface Uint8Array {
|
||||
* @param thisArg An object to which the this keyword can refer in the callbackfn function.
|
||||
* If thisArg is omitted, undefined is used as the this value.
|
||||
*/
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Uint8Array) => number, thisArg: any): Uint8Array;
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Uint8Array) => number, thisArg?: any): Uint8Array;
|
||||
|
||||
/**
|
||||
* Calls the specified callback function for all the elements in an array. The return value of
|
||||
@@ -2169,7 +2169,7 @@ interface Uint8ClampedArray {
|
||||
* @param thisArg An object to which the this keyword can refer in the callbackfn function.
|
||||
* If thisArg is omitted, undefined is used as the this value.
|
||||
*/
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Uint8ClampedArray) => number, thisArg: any): Uint8ClampedArray;
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Uint8ClampedArray) => number, thisArg?: any): Uint8ClampedArray;
|
||||
|
||||
/**
|
||||
* Calls the specified callback function for all the elements in an array. The return value of
|
||||
@@ -2434,7 +2434,7 @@ interface Int16Array {
|
||||
* @param thisArg An object to which the this keyword can refer in the callbackfn function.
|
||||
* If thisArg is omitted, undefined is used as the this value.
|
||||
*/
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Int16Array) => number, thisArg: any): Int16Array;
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Int16Array) => number, thisArg?: any): Int16Array;
|
||||
|
||||
/**
|
||||
* Calls the specified callback function for all the elements in an array. The return value of
|
||||
@@ -2702,7 +2702,7 @@ interface Uint16Array {
|
||||
* @param thisArg An object to which the this keyword can refer in the callbackfn function.
|
||||
* If thisArg is omitted, undefined is used as the this value.
|
||||
*/
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Uint16Array) => number, thisArg: any): Uint16Array;
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Uint16Array) => number, thisArg?: any): Uint16Array;
|
||||
|
||||
/**
|
||||
* Calls the specified callback function for all the elements in an array. The return value of
|
||||
@@ -3235,7 +3235,7 @@ interface Uint32Array {
|
||||
* @param thisArg An object to which the this keyword can refer in the callbackfn function.
|
||||
* If thisArg is omitted, undefined is used as the this value.
|
||||
*/
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Uint32Array) => number, thisArg: any): Uint32Array;
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Uint32Array) => number, thisArg?: any): Uint32Array;
|
||||
|
||||
/**
|
||||
* Calls the specified callback function for all the elements in an array. The return value of
|
||||
@@ -3502,7 +3502,7 @@ interface Float32Array {
|
||||
* @param thisArg An object to which the this keyword can refer in the callbackfn function.
|
||||
* If thisArg is omitted, undefined is used as the this value.
|
||||
*/
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Float32Array) => number, thisArg: any): Float32Array;
|
||||
map(callbackfn: (this: void, value: number, index: number, array: Float32Array) => number, thisArg?: any): Float32Array;
|
||||
|
||||
/**
|
||||
* Calls the specified callback function for all the elements in an array. The return value of
|
||||
|
||||
+49
-79
@@ -84,13 +84,17 @@ namespace ts.server {
|
||||
* NOTE: this field is created on demand and should not be accessed directly.
|
||||
* Use 'getFileInfos' instead.
|
||||
*/
|
||||
private fileInfos_doNotAccessDirectly: FileMap<T>;
|
||||
private fileInfos_doNotAccessDirectly: Map<T>;
|
||||
|
||||
constructor(public readonly project: Project, private ctor: { new (scriptInfo: ScriptInfo, project: Project): T }) {
|
||||
}
|
||||
|
||||
private getFileInfos() {
|
||||
return this.fileInfos_doNotAccessDirectly || (this.fileInfos_doNotAccessDirectly = createFileMap<T>());
|
||||
return this.fileInfos_doNotAccessDirectly || (this.fileInfos_doNotAccessDirectly = createMap<T>());
|
||||
}
|
||||
|
||||
protected hasFileInfos() {
|
||||
return !!this.fileInfos_doNotAccessDirectly;
|
||||
}
|
||||
|
||||
public clear() {
|
||||
@@ -113,7 +117,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
protected getFileInfoPaths(): Path[] {
|
||||
return this.getFileInfos().getKeys();
|
||||
return arrayFrom(this.getFileInfos().keys() as Iterator<Path>);
|
||||
}
|
||||
|
||||
protected setFileInfo(path: Path, info: T) {
|
||||
@@ -121,20 +125,22 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
protected removeFileInfo(path: Path) {
|
||||
this.getFileInfos().remove(path);
|
||||
this.getFileInfos().delete(path);
|
||||
}
|
||||
|
||||
protected forEachFileInfo(action: (fileInfo: T) => any) {
|
||||
this.getFileInfos().forEachValue((_path, value) => action(value));
|
||||
this.getFileInfos().forEach(action);
|
||||
}
|
||||
|
||||
abstract getFilesAffectedBy(scriptInfo: ScriptInfo): string[];
|
||||
abstract onProjectUpdateGraph(): void;
|
||||
protected abstract ensureFileInfoIfInProject(scriptInfo: ScriptInfo): void;
|
||||
|
||||
/**
|
||||
* @returns {boolean} whether the emit was conducted or not
|
||||
*/
|
||||
emitFile(scriptInfo: ScriptInfo, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => void): boolean {
|
||||
this.ensureFileInfoIfInProject(scriptInfo);
|
||||
const fileInfo = this.getFileInfo(scriptInfo.path);
|
||||
if (!fileInfo) {
|
||||
return false;
|
||||
@@ -158,7 +164,21 @@ namespace ts.server {
|
||||
super(project, BuilderFileInfo);
|
||||
}
|
||||
|
||||
protected ensureFileInfoIfInProject(scriptInfo: ScriptInfo) {
|
||||
if (this.project.containsScriptInfo(scriptInfo)) {
|
||||
this.getOrCreateFileInfo(scriptInfo.path);
|
||||
}
|
||||
}
|
||||
|
||||
onProjectUpdateGraph() {
|
||||
if (this.hasFileInfos()) {
|
||||
this.forEachFileInfo(fileInfo => {
|
||||
if (!this.project.containsScriptInfo(fileInfo.scriptInfo)) {
|
||||
// This file was deleted from this project
|
||||
this.removeFileInfo(fileInfo.scriptInfo.path);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,57 +203,27 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
class ModuleBuilderFileInfo extends BuilderFileInfo {
|
||||
references: ModuleBuilderFileInfo[] = [];
|
||||
referencedBy: ModuleBuilderFileInfo[] = [];
|
||||
references = createSortedArray<ModuleBuilderFileInfo>();
|
||||
readonly referencedBy = createSortedArray<ModuleBuilderFileInfo>();
|
||||
scriptVersionForReferences: string;
|
||||
|
||||
static compareFileInfos(lf: ModuleBuilderFileInfo, rf: ModuleBuilderFileInfo): number {
|
||||
const l = lf.scriptInfo.fileName;
|
||||
const r = rf.scriptInfo.fileName;
|
||||
return (l < r ? -1 : (l > r ? 1 : 0));
|
||||
}
|
||||
|
||||
static addToReferenceList(array: ModuleBuilderFileInfo[], fileInfo: ModuleBuilderFileInfo) {
|
||||
if (array.length === 0) {
|
||||
array.push(fileInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
const insertIndex = binarySearch(array, fileInfo, ModuleBuilderFileInfo.compareFileInfos);
|
||||
if (insertIndex < 0) {
|
||||
array.splice(~insertIndex, 0, fileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
static removeFromReferenceList(array: ModuleBuilderFileInfo[], fileInfo: ModuleBuilderFileInfo) {
|
||||
if (!array || array.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (array[0] === fileInfo) {
|
||||
array.splice(0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const removeIndex = binarySearch(array, fileInfo, ModuleBuilderFileInfo.compareFileInfos);
|
||||
if (removeIndex >= 0) {
|
||||
array.splice(removeIndex, 1);
|
||||
}
|
||||
static compareFileInfos(lf: ModuleBuilderFileInfo, rf: ModuleBuilderFileInfo): Comparison {
|
||||
return compareStrings(lf.scriptInfo.fileName, rf.scriptInfo.fileName);
|
||||
}
|
||||
|
||||
addReferencedBy(fileInfo: ModuleBuilderFileInfo): void {
|
||||
ModuleBuilderFileInfo.addToReferenceList(this.referencedBy, fileInfo);
|
||||
insertSorted(this.referencedBy, fileInfo, ModuleBuilderFileInfo.compareFileInfos);
|
||||
}
|
||||
|
||||
removeReferencedBy(fileInfo: ModuleBuilderFileInfo): void {
|
||||
ModuleBuilderFileInfo.removeFromReferenceList(this.referencedBy, fileInfo);
|
||||
removeSorted(this.referencedBy, fileInfo, ModuleBuilderFileInfo.compareFileInfos);
|
||||
}
|
||||
|
||||
removeFileReferences() {
|
||||
for (const reference of this.references) {
|
||||
reference.removeReferencedBy(this);
|
||||
}
|
||||
this.references = [];
|
||||
clear(this.references);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,20 +240,24 @@ namespace ts.server {
|
||||
super.clear();
|
||||
}
|
||||
|
||||
private getReferencedFileInfos(fileInfo: ModuleBuilderFileInfo): ModuleBuilderFileInfo[] {
|
||||
private getReferencedFileInfos(fileInfo: ModuleBuilderFileInfo): SortedArray<ModuleBuilderFileInfo> {
|
||||
if (!fileInfo.isExternalModuleOrHasOnlyAmbientExternalModules()) {
|
||||
return [];
|
||||
return createSortedArray();
|
||||
}
|
||||
|
||||
const referencedFilePaths = this.project.getReferencedFiles(fileInfo.scriptInfo.path);
|
||||
if (referencedFilePaths.length > 0) {
|
||||
return map(referencedFilePaths, f => this.getOrCreateFileInfo(f)).sort(ModuleBuilderFileInfo.compareFileInfos);
|
||||
}
|
||||
return [];
|
||||
return toSortedArray(referencedFilePaths.map(f => this.getOrCreateFileInfo(f)), ModuleBuilderFileInfo.compareFileInfos);
|
||||
}
|
||||
|
||||
protected ensureFileInfoIfInProject(_scriptInfo: ScriptInfo) {
|
||||
this.ensureProjectDependencyGraphUpToDate();
|
||||
}
|
||||
|
||||
onProjectUpdateGraph() {
|
||||
this.ensureProjectDependencyGraphUpToDate();
|
||||
// Update the graph only if we have computed graph earlier
|
||||
if (this.hasFileInfos()) {
|
||||
this.ensureProjectDependencyGraphUpToDate();
|
||||
}
|
||||
}
|
||||
|
||||
private ensureProjectDependencyGraphUpToDate() {
|
||||
@@ -292,39 +286,15 @@ namespace ts.server {
|
||||
|
||||
const newReferences = this.getReferencedFileInfos(fileInfo);
|
||||
const oldReferences = fileInfo.references;
|
||||
|
||||
let oldIndex = 0;
|
||||
let newIndex = 0;
|
||||
while (oldIndex < oldReferences.length && newIndex < newReferences.length) {
|
||||
const oldReference = oldReferences[oldIndex];
|
||||
const newReference = newReferences[newIndex];
|
||||
const compare = ModuleBuilderFileInfo.compareFileInfos(oldReference, newReference);
|
||||
if (compare < 0) {
|
||||
enumerateInsertsAndDeletes(newReferences, oldReferences,
|
||||
/*inserted*/ newReference => newReference.addReferencedBy(fileInfo),
|
||||
/*deleted*/ oldReference => {
|
||||
// New reference is greater then current reference. That means
|
||||
// the current reference doesn't exist anymore after parsing. So delete
|
||||
// references.
|
||||
oldReference.removeReferencedBy(fileInfo);
|
||||
oldIndex++;
|
||||
}
|
||||
else if (compare > 0) {
|
||||
// A new reference info. Add it.
|
||||
newReference.addReferencedBy(fileInfo);
|
||||
newIndex++;
|
||||
}
|
||||
else {
|
||||
// Equal. Go to next
|
||||
oldIndex++;
|
||||
newIndex++;
|
||||
}
|
||||
}
|
||||
// Clean old references
|
||||
for (let i = oldIndex; i < oldReferences.length; i++) {
|
||||
oldReferences[i].removeReferencedBy(fileInfo);
|
||||
}
|
||||
// Update new references
|
||||
for (let i = newIndex; i < newReferences.length; i++) {
|
||||
newReferences[i].addReferencedBy(fileInfo);
|
||||
}
|
||||
},
|
||||
/*compare*/ ModuleBuilderFileInfo.compareFileInfos);
|
||||
|
||||
fileInfo.references = newReferences;
|
||||
fileInfo.scriptVersionForReferences = fileInfo.scriptInfo.getLatestVersion();
|
||||
@@ -386,4 +356,4 @@ namespace ts.server {
|
||||
return new ModuleBuilder(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+184
-325
@@ -34,7 +34,7 @@ namespace ts.server {
|
||||
|
||||
export class SessionClient implements LanguageService {
|
||||
private sequence = 0;
|
||||
private lineMaps: ts.Map<number[]> = ts.createMap<number[]>();
|
||||
private lineMaps: Map<number[]> = createMap<number[]>();
|
||||
private messages: string[] = [];
|
||||
private lastRenameEntry: RenameEntry;
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace ts.server {
|
||||
let lineMap = this.lineMaps.get(fileName);
|
||||
if (!lineMap) {
|
||||
const scriptSnapshot = this.host.getScriptSnapshot(fileName);
|
||||
lineMap = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()));
|
||||
lineMap = computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()));
|
||||
this.lineMaps.set(fileName, lineMap);
|
||||
}
|
||||
return lineMap;
|
||||
@@ -61,11 +61,11 @@ namespace ts.server {
|
||||
|
||||
private lineOffsetToPosition(fileName: string, lineOffset: protocol.Location, lineMap?: number[]): number {
|
||||
lineMap = lineMap || this.getLineMap(fileName);
|
||||
return ts.computePositionOfLineAndCharacter(lineMap, lineOffset.line - 1, lineOffset.offset - 1);
|
||||
return computePositionOfLineAndCharacter(lineMap, lineOffset.line - 1, lineOffset.offset - 1);
|
||||
}
|
||||
|
||||
private positionToOneBasedLineOffset(fileName: string, position: number): protocol.Location {
|
||||
const lineOffset = ts.computeLineAndCharacterOfPosition(this.getLineMap(fileName), position);
|
||||
const lineOffset = computeLineAndCharacterOfPosition(this.getLineMap(fileName), position);
|
||||
return {
|
||||
line: lineOffset.line + 1,
|
||||
offset: lineOffset.character + 1
|
||||
@@ -73,13 +73,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private convertCodeEditsToTextChange(fileName: string, codeEdit: protocol.CodeEdit): ts.TextChange {
|
||||
const start = this.lineOffsetToPosition(fileName, codeEdit.start);
|
||||
const end = this.lineOffsetToPosition(fileName, codeEdit.end);
|
||||
|
||||
return {
|
||||
span: ts.createTextSpanFromBounds(start, end),
|
||||
newText: codeEdit.newText
|
||||
};
|
||||
return { span: this.decodeSpan(codeEdit, fileName), newText: codeEdit.newText };
|
||||
}
|
||||
|
||||
private processRequest<T extends protocol.Request>(command: string, args?: any): T {
|
||||
@@ -130,64 +124,42 @@ namespace ts.server {
|
||||
return response;
|
||||
}
|
||||
|
||||
openFile(fileName: string, content?: string, scriptKindName?: "TS" | "JS" | "TSX" | "JSX"): void {
|
||||
const args: protocol.OpenRequestArgs = { file: fileName, fileContent: content, scriptKindName };
|
||||
openFile(file: string, fileContent?: string, scriptKindName?: "TS" | "JS" | "TSX" | "JSX"): void {
|
||||
const args: protocol.OpenRequestArgs = { file, fileContent, scriptKindName };
|
||||
this.processRequest(CommandNames.Open, args);
|
||||
}
|
||||
|
||||
closeFile(fileName: string): void {
|
||||
const args: protocol.FileRequestArgs = { file: fileName };
|
||||
closeFile(file: string): void {
|
||||
const args: protocol.FileRequestArgs = { file };
|
||||
this.processRequest(CommandNames.Close, args);
|
||||
}
|
||||
|
||||
changeFile(fileName: string, start: number, end: number, newText: string): void {
|
||||
changeFile(fileName: string, start: number, end: number, insertString: string): void {
|
||||
// clear the line map after an edit
|
||||
this.lineMaps.set(fileName, undefined);
|
||||
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, start);
|
||||
const endLineOffset = this.positionToOneBasedLineOffset(fileName, end);
|
||||
|
||||
const args: protocol.ChangeRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
endLine: endLineOffset.line,
|
||||
endOffset: endLineOffset.offset,
|
||||
insertString: newText
|
||||
};
|
||||
|
||||
const args: protocol.ChangeRequestArgs = { ...this.createFileLocationRequestArgsWithEndLineAndOffset(fileName, start, end), insertString };
|
||||
this.processRequest(CommandNames.Change, args);
|
||||
}
|
||||
|
||||
getQuickInfoAtPosition(fileName: string, position: number): QuickInfo {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset
|
||||
};
|
||||
const args = this.createFileLocationRequestArgs(fileName, position);
|
||||
|
||||
const request = this.processRequest<protocol.QuickInfoRequest>(CommandNames.Quickinfo, args);
|
||||
const response = this.processResponse<protocol.QuickInfoResponse>(request);
|
||||
|
||||
const start = this.lineOffsetToPosition(fileName, response.body.start);
|
||||
const end = this.lineOffsetToPosition(fileName, response.body.end);
|
||||
|
||||
return {
|
||||
kind: response.body.kind,
|
||||
kindModifiers: response.body.kindModifiers,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
textSpan: this.decodeSpan(response.body, fileName),
|
||||
displayParts: [{ kind: "text", text: response.body.displayString }],
|
||||
documentation: [{ kind: "text", text: response.body.documentation }],
|
||||
tags: response.body.tags
|
||||
};
|
||||
}
|
||||
|
||||
getProjectInfo(fileName: string, needFileNameList: boolean): protocol.ProjectInfo {
|
||||
const args: protocol.ProjectInfoRequestArgs = {
|
||||
file: fileName,
|
||||
needFileNameList: needFileNameList
|
||||
};
|
||||
getProjectInfo(file: string, needFileNameList: boolean): protocol.ProjectInfo {
|
||||
const args: protocol.ProjectInfoRequestArgs = { file, needFileNameList };
|
||||
|
||||
const request = this.processRequest<protocol.ProjectInfoRequest>(CommandNames.ProjectInfo, args);
|
||||
const response = this.processResponse<protocol.ProjectInfoResponse>(request);
|
||||
@@ -199,13 +171,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getCompletionsAtPosition(fileName: string, position: number): CompletionInfo {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.CompletionsRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
prefix: undefined
|
||||
};
|
||||
const args: protocol.CompletionsRequestArgs = this.createFileLocationRequestArgs(fileName, position);
|
||||
|
||||
const request = this.processRequest<protocol.CompletionsRequest>(CommandNames.Completions, args);
|
||||
const response = this.processResponse<protocol.CompletionsResponse>(request);
|
||||
@@ -217,11 +183,8 @@ namespace ts.server {
|
||||
entries: response.body.map(entry => {
|
||||
|
||||
if (entry.replacementSpan !== undefined) {
|
||||
const { name, kind, kindModifiers, sortText, replacementSpan} = entry;
|
||||
|
||||
const convertedSpan = createTextSpanFromBounds(this.lineOffsetToPosition(fileName, replacementSpan.start),
|
||||
this.lineOffsetToPosition(fileName, replacementSpan.end));
|
||||
return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan };
|
||||
const { name, kind, kindModifiers, sortText, replacementSpan } = entry;
|
||||
return { name, kind, kindModifiers, sortText, replacementSpan: this.decodeSpan(replacementSpan, fileName) };
|
||||
}
|
||||
|
||||
return entry as { name: string, kind: ScriptElementKind, kindModifiers: string, sortText: string };
|
||||
@@ -230,13 +193,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.CompletionDetailsRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
entryNames: [entryName]
|
||||
};
|
||||
const args: protocol.CompletionDetailsRequestArgs = { ...this.createFileLocationRequestArgs(fileName, position), entryNames: [entryName] };
|
||||
|
||||
const request = this.processRequest<protocol.CompletionDetailsRequest>(CommandNames.CompletionDetails, args);
|
||||
const response = this.processResponse<protocol.CompletionDetailsResponse>(request);
|
||||
@@ -257,55 +214,36 @@ namespace ts.server {
|
||||
const request = this.processRequest<protocol.NavtoRequest>(CommandNames.Navto, args);
|
||||
const response = this.processResponse<protocol.NavtoResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
const fileName = entry.file;
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
|
||||
return {
|
||||
name: entry.name,
|
||||
containerName: entry.containerName || "",
|
||||
containerKind: entry.containerKind || ScriptElementKind.unknown,
|
||||
kind: entry.kind,
|
||||
kindModifiers: entry.kindModifiers,
|
||||
matchKind: entry.matchKind,
|
||||
isCaseSensitive: entry.isCaseSensitive,
|
||||
fileName: fileName,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end)
|
||||
};
|
||||
});
|
||||
return response.body.map(entry => ({
|
||||
name: entry.name,
|
||||
containerName: entry.containerName || "",
|
||||
containerKind: entry.containerKind || ScriptElementKind.unknown,
|
||||
kind: entry.kind,
|
||||
kindModifiers: entry.kindModifiers,
|
||||
matchKind: entry.matchKind,
|
||||
isCaseSensitive: entry.isCaseSensitive,
|
||||
fileName: entry.file,
|
||||
textSpan: this.decodeSpan(entry),
|
||||
}));
|
||||
}
|
||||
|
||||
getFormattingEditsForRange(fileName: string, start: number, end: number, _options: ts.FormatCodeOptions): ts.TextChange[] {
|
||||
const startLineOffset = this.positionToOneBasedLineOffset(fileName, start);
|
||||
const endLineOffset = this.positionToOneBasedLineOffset(fileName, end);
|
||||
const args: protocol.FormatRequestArgs = {
|
||||
file: fileName,
|
||||
line: startLineOffset.line,
|
||||
offset: startLineOffset.offset,
|
||||
endLine: endLineOffset.line,
|
||||
endOffset: endLineOffset.offset,
|
||||
};
|
||||
getFormattingEditsForRange(file: string, start: number, end: number, _options: FormatCodeOptions): ts.TextChange[] {
|
||||
const args: protocol.FormatRequestArgs = this.createFileLocationRequestArgsWithEndLineAndOffset(file, start, end);
|
||||
|
||||
|
||||
// TODO: handle FormatCodeOptions
|
||||
const request = this.processRequest<protocol.FormatRequest>(CommandNames.Format, args);
|
||||
const response = this.processResponse<protocol.FormatResponse>(request);
|
||||
|
||||
return response.body.map(entry => this.convertCodeEditsToTextChange(fileName, entry));
|
||||
return response.body.map(entry => this.convertCodeEditsToTextChange(file, entry));
|
||||
}
|
||||
|
||||
getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): ts.TextChange[] {
|
||||
getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions): ts.TextChange[] {
|
||||
return this.getFormattingEditsForRange(fileName, 0, this.host.getScriptSnapshot(fileName).getLength(), options);
|
||||
}
|
||||
|
||||
getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, _options: FormatCodeOptions): ts.TextChange[] {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FormatOnKeyRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
key: key
|
||||
};
|
||||
const args: protocol.FormatOnKeyRequestArgs = { ...this.createFileLocationRequestArgs(fileName, position), key };
|
||||
|
||||
// TODO: handle FormatCodeOptions
|
||||
const request = this.processRequest<protocol.FormatOnKeyRequest>(CommandNames.Formatonkey, args);
|
||||
@@ -315,79 +253,49 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
};
|
||||
const args: protocol.FileLocationRequestArgs = this.createFileLocationRequestArgs(fileName, position);
|
||||
|
||||
const request = this.processRequest<protocol.DefinitionRequest>(CommandNames.Definition, args);
|
||||
const response = this.processResponse<protocol.DefinitionResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
const fileName = entry.file;
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
return {
|
||||
containerKind: ScriptElementKind.unknown,
|
||||
containerName: "",
|
||||
fileName: fileName,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
kind: ScriptElementKind.unknown,
|
||||
name: ""
|
||||
};
|
||||
});
|
||||
return response.body.map(entry => ({
|
||||
containerKind: ScriptElementKind.unknown,
|
||||
containerName: "",
|
||||
fileName: entry.file,
|
||||
textSpan: this.decodeSpan(entry),
|
||||
kind: ScriptElementKind.unknown,
|
||||
name: ""
|
||||
}));
|
||||
}
|
||||
|
||||
getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
};
|
||||
const args: protocol.FileLocationRequestArgs = this.createFileLocationRequestArgs(fileName, position);
|
||||
|
||||
const request = this.processRequest<protocol.TypeDefinitionRequest>(CommandNames.TypeDefinition, args);
|
||||
const response = this.processResponse<protocol.TypeDefinitionResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
const fileName = entry.file;
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
return {
|
||||
containerKind: ScriptElementKind.unknown,
|
||||
containerName: "",
|
||||
fileName: fileName,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
kind: ScriptElementKind.unknown,
|
||||
name: ""
|
||||
};
|
||||
});
|
||||
return response.body.map(entry => ({
|
||||
containerKind: ScriptElementKind.unknown,
|
||||
containerName: "",
|
||||
fileName: entry.file,
|
||||
textSpan: this.decodeSpan(entry),
|
||||
kind: ScriptElementKind.unknown,
|
||||
name: ""
|
||||
}));
|
||||
}
|
||||
|
||||
getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[] {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
};
|
||||
const args = this.createFileLocationRequestArgs(fileName, position);
|
||||
|
||||
const request = this.processRequest<protocol.ImplementationRequest>(CommandNames.Implementation, args);
|
||||
const response = this.processResponse<protocol.ImplementationResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
const fileName = entry.file;
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
return {
|
||||
fileName,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
kind: ScriptElementKind.unknown,
|
||||
displayParts: []
|
||||
};
|
||||
});
|
||||
return response.body.map(entry => ({
|
||||
fileName: entry.file,
|
||||
textSpan: this.decodeSpan(entry),
|
||||
kind: ScriptElementKind.unknown,
|
||||
displayParts: []
|
||||
}));
|
||||
}
|
||||
|
||||
findReferences(_fileName: string, _position: number): ReferencedSymbol[] {
|
||||
@@ -396,49 +304,39 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
};
|
||||
const args = this.createFileLocationRequestArgs(fileName, position);
|
||||
|
||||
const request = this.processRequest<protocol.ReferencesRequest>(CommandNames.References, args);
|
||||
const response = this.processResponse<protocol.ReferencesResponse>(request);
|
||||
|
||||
return response.body.refs.map(entry => {
|
||||
const fileName = entry.file;
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
return {
|
||||
fileName: fileName,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
isWriteAccess: entry.isWriteAccess,
|
||||
isDefinition: entry.isDefinition,
|
||||
};
|
||||
});
|
||||
return response.body.refs.map(entry => ({
|
||||
fileName: entry.file,
|
||||
textSpan: this.decodeSpan(entry),
|
||||
isWriteAccess: entry.isWriteAccess,
|
||||
isDefinition: entry.isDefinition,
|
||||
}));
|
||||
}
|
||||
|
||||
getEmitOutput(_fileName: string): EmitOutput {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
getSyntacticDiagnostics(fileName: string): Diagnostic[] {
|
||||
const args: protocol.SyntacticDiagnosticsSyncRequestArgs = { file: fileName, includeLinePosition: true };
|
||||
getSyntacticDiagnostics(file: string): Diagnostic[] {
|
||||
const args: protocol.SyntacticDiagnosticsSyncRequestArgs = { file, includeLinePosition: true };
|
||||
|
||||
const request = this.processRequest<protocol.SyntacticDiagnosticsSyncRequest>(CommandNames.SyntacticDiagnosticsSync, args);
|
||||
const response = this.processResponse<protocol.SyntacticDiagnosticsSyncResponse>(request);
|
||||
|
||||
return (<protocol.DiagnosticWithLinePosition[]>response.body).map(entry => this.convertDiagnostic(entry, fileName));
|
||||
return (<protocol.DiagnosticWithLinePosition[]>response.body).map(entry => this.convertDiagnostic(entry, file));
|
||||
}
|
||||
|
||||
getSemanticDiagnostics(fileName: string): Diagnostic[] {
|
||||
const args: protocol.SemanticDiagnosticsSyncRequestArgs = { file: fileName, includeLinePosition: true };
|
||||
getSemanticDiagnostics(file: string): Diagnostic[] {
|
||||
const args: protocol.SemanticDiagnosticsSyncRequestArgs = { file, includeLinePosition: true };
|
||||
|
||||
const request = this.processRequest<protocol.SemanticDiagnosticsSyncRequest>(CommandNames.SemanticDiagnosticsSync, args);
|
||||
const response = this.processResponse<protocol.SemanticDiagnosticsSyncResponse>(request);
|
||||
|
||||
return (<protocol.DiagnosticWithLinePosition[]>response.body).map(entry => this.convertDiagnostic(entry, fileName));
|
||||
return (<protocol.DiagnosticWithLinePosition[]>response.body).map(entry => this.convertDiagnostic(entry, file));
|
||||
}
|
||||
|
||||
convertDiagnostic(entry: protocol.DiagnosticWithLinePosition, _fileName: string): Diagnostic {
|
||||
@@ -456,7 +354,7 @@ namespace ts.server {
|
||||
start: entry.start,
|
||||
length: entry.length,
|
||||
messageText: entry.message,
|
||||
category: category,
|
||||
category,
|
||||
code: entry.code
|
||||
};
|
||||
}
|
||||
@@ -466,29 +364,18 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getRenameInfo(fileName: string, position: number, findInStrings?: boolean, findInComments?: boolean): RenameInfo {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.RenameRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
findInStrings,
|
||||
findInComments
|
||||
};
|
||||
const args: protocol.RenameRequestArgs = { ...this.createFileLocationRequestArgs(fileName, position), findInStrings, findInComments };
|
||||
|
||||
const request = this.processRequest<protocol.RenameRequest>(CommandNames.Rename, args);
|
||||
const response = this.processResponse<protocol.RenameResponse>(request);
|
||||
const locations: RenameLocation[] = [];
|
||||
response.body.locs.map((entry: protocol.SpanGroup) => {
|
||||
for (const entry of response.body.locs) {
|
||||
const fileName = entry.file;
|
||||
entry.locs.map((loc: protocol.TextSpan) => {
|
||||
const start = this.lineOffsetToPosition(fileName, loc.start);
|
||||
const end = this.lineOffsetToPosition(fileName, loc.end);
|
||||
locations.push({
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
fileName: fileName
|
||||
});
|
||||
});
|
||||
});
|
||||
for (const loc of entry.locs) {
|
||||
locations.push({ textSpan: this.decodeSpan(loc, fileName), fileName });
|
||||
}
|
||||
}
|
||||
|
||||
return this.lastRenameEntry = {
|
||||
canRename: response.body.info.canRename,
|
||||
displayName: response.body.info.displayName,
|
||||
@@ -496,12 +383,12 @@ namespace ts.server {
|
||||
kind: response.body.info.kind,
|
||||
kindModifiers: response.body.info.kindModifiers,
|
||||
localizedErrorMessage: response.body.info.localizedErrorMessage,
|
||||
triggerSpan: ts.createTextSpanFromBounds(position, position),
|
||||
fileName: fileName,
|
||||
position: position,
|
||||
findInStrings: findInStrings,
|
||||
findInComments: findInComments,
|
||||
locations: locations
|
||||
triggerSpan: createTextSpanFromBounds(position, position),
|
||||
fileName,
|
||||
position,
|
||||
findInStrings,
|
||||
findInComments,
|
||||
locations,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -534,12 +421,12 @@ namespace ts.server {
|
||||
}));
|
||||
}
|
||||
|
||||
getNavigationBarItems(fileName: string): NavigationBarItem[] {
|
||||
const request = this.processRequest<protocol.NavBarRequest>(CommandNames.NavBar, { file: fileName });
|
||||
getNavigationBarItems(file: string): NavigationBarItem[] {
|
||||
const request = this.processRequest<protocol.NavBarRequest>(CommandNames.NavBar, { file });
|
||||
const response = this.processResponse<protocol.NavBarResponse>(request);
|
||||
|
||||
const lineMap = this.getLineMap(fileName);
|
||||
return this.decodeNavigationBarItems(response.body, fileName, lineMap);
|
||||
const lineMap = this.getLineMap(file);
|
||||
return this.decodeNavigationBarItems(response.body, file, lineMap);
|
||||
}
|
||||
|
||||
private decodeNavigationTree(tree: protocol.NavigationTree, fileName: string, lineMap: number[]): NavigationTree {
|
||||
@@ -552,15 +439,19 @@ namespace ts.server {
|
||||
};
|
||||
}
|
||||
|
||||
getNavigationTree(fileName: string): NavigationTree {
|
||||
const request = this.processRequest<protocol.NavTreeRequest>(CommandNames.NavTree, { file: fileName });
|
||||
getNavigationTree(file: string): NavigationTree {
|
||||
const request = this.processRequest<protocol.NavTreeRequest>(CommandNames.NavTree, { file });
|
||||
const response = this.processResponse<protocol.NavTreeResponse>(request);
|
||||
|
||||
const lineMap = this.getLineMap(fileName);
|
||||
return this.decodeNavigationTree(response.body, fileName, lineMap);
|
||||
const lineMap = this.getLineMap(file);
|
||||
return this.decodeNavigationTree(response.body, file, lineMap);
|
||||
}
|
||||
|
||||
private decodeSpan(span: protocol.TextSpan, fileName: string, lineMap: number[]) {
|
||||
private decodeSpan(span: protocol.TextSpan & { file: string }): TextSpan;
|
||||
private decodeSpan(span: protocol.TextSpan, fileName: string, lineMap?: number[]): TextSpan;
|
||||
private decodeSpan(span: protocol.TextSpan & { file: string }, fileName?: string, lineMap?: number[]): TextSpan {
|
||||
fileName = fileName || span.file;
|
||||
lineMap = lineMap || this.getLineMap(fileName);
|
||||
return createTextSpanFromBounds(
|
||||
this.lineOffsetToPosition(fileName, span.start, lineMap),
|
||||
this.lineOffsetToPosition(fileName, span.end, lineMap));
|
||||
@@ -575,12 +466,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.SignatureHelpRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset
|
||||
};
|
||||
const args: protocol.SignatureHelpRequestArgs = this.createFileLocationRequestArgs(fileName, position);
|
||||
|
||||
const request = this.processRequest<protocol.SignatureHelpRequest>(CommandNames.SignatureHelp, args);
|
||||
const response = this.processResponse<protocol.SignatureHelpResponse>(request);
|
||||
@@ -589,75 +475,40 @@ namespace ts.server {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const helpItems: protocol.SignatureHelpItems = response.body;
|
||||
const span = helpItems.applicableSpan;
|
||||
const start = this.lineOffsetToPosition(fileName, span.start);
|
||||
const end = this.lineOffsetToPosition(fileName, span.end);
|
||||
const { items, applicableSpan: encodedApplicableSpan, selectedItemIndex, argumentIndex, argumentCount } = response.body;
|
||||
|
||||
const result: SignatureHelpItems = {
|
||||
items: helpItems.items,
|
||||
applicableSpan: {
|
||||
start: start,
|
||||
length: end - start
|
||||
},
|
||||
selectedItemIndex: helpItems.selectedItemIndex,
|
||||
argumentIndex: helpItems.argumentIndex,
|
||||
argumentCount: helpItems.argumentCount,
|
||||
};
|
||||
return result;
|
||||
const applicableSpan = this.decodeSpan(encodedApplicableSpan, fileName);
|
||||
|
||||
return { items, applicableSpan, selectedItemIndex, argumentIndex, argumentCount };
|
||||
}
|
||||
|
||||
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
};
|
||||
const args = this.createFileLocationRequestArgs(fileName, position);
|
||||
|
||||
const request = this.processRequest<protocol.OccurrencesRequest>(CommandNames.Occurrences, args);
|
||||
const response = this.processResponse<protocol.OccurrencesResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
const fileName = entry.file;
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
return {
|
||||
fileName,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
isWriteAccess: entry.isWriteAccess,
|
||||
isDefinition: false
|
||||
};
|
||||
});
|
||||
return response.body.map(entry => ({
|
||||
fileName: entry.file,
|
||||
textSpan: this.decodeSpan(entry),
|
||||
isWriteAccess: entry.isWriteAccess,
|
||||
isDefinition: false
|
||||
}));
|
||||
}
|
||||
|
||||
getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[] {
|
||||
const { line, offset } = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.DocumentHighlightsRequestArgs = { file: fileName, line, offset, filesToSearch };
|
||||
const args: protocol.DocumentHighlightsRequestArgs = { ...this.createFileLocationRequestArgs(fileName, position), filesToSearch };
|
||||
|
||||
const request = this.processRequest<protocol.DocumentHighlightsRequest>(CommandNames.DocumentHighlights, args);
|
||||
const response = this.processResponse<protocol.DocumentHighlightsResponse>(request);
|
||||
|
||||
const self = this;
|
||||
return response.body.map(convertToDocumentHighlights);
|
||||
|
||||
function convertToDocumentHighlights(item: ts.server.protocol.DocumentHighlightsItem): ts.DocumentHighlights {
|
||||
const { file, highlightSpans } = item;
|
||||
|
||||
return {
|
||||
fileName: file,
|
||||
highlightSpans: highlightSpans.map(convertHighlightSpan)
|
||||
};
|
||||
|
||||
function convertHighlightSpan(span: ts.server.protocol.HighlightSpan): ts.HighlightSpan {
|
||||
const start = self.lineOffsetToPosition(file, span.start);
|
||||
const end = self.lineOffsetToPosition(file, span.end);
|
||||
return {
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
kind: span.kind
|
||||
};
|
||||
}
|
||||
}
|
||||
return response.body.map(item => ({
|
||||
fileName: item.file,
|
||||
highlightSpans: item.highlightSpans.map(span => ({
|
||||
textSpan: this.decodeSpan(span, item.file),
|
||||
kind: span.kind
|
||||
})),
|
||||
}));
|
||||
}
|
||||
|
||||
getOutliningSpans(_fileName: string): OutliningSpan[] {
|
||||
@@ -680,39 +531,36 @@ namespace ts.server {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[]): CodeAction[] {
|
||||
const startLineOffset = this.positionToOneBasedLineOffset(fileName, start);
|
||||
const endLineOffset = this.positionToOneBasedLineOffset(fileName, end);
|
||||
|
||||
const args: protocol.CodeFixRequestArgs = {
|
||||
file: fileName,
|
||||
startLine: startLineOffset.line,
|
||||
startOffset: startLineOffset.offset,
|
||||
endLine: endLineOffset.line,
|
||||
endOffset: endLineOffset.offset,
|
||||
errorCodes: errorCodes,
|
||||
};
|
||||
getCodeFixesAtPosition(file: string, start: number, end: number, errorCodes: number[]): CodeAction[] {
|
||||
const args: protocol.CodeFixRequestArgs = { ...this.createFileRangeRequestArgs(file, start, end), errorCodes };
|
||||
|
||||
const request = this.processRequest<protocol.CodeFixRequest>(CommandNames.GetCodeFixes, args);
|
||||
const response = this.processResponse<protocol.CodeFixResponse>(request);
|
||||
|
||||
return response.body.map(entry => this.convertCodeActions(entry, fileName));
|
||||
return response.body.map(entry => this.convertCodeActions(entry, file));
|
||||
}
|
||||
|
||||
private createFileLocationOrRangeRequestArgs(positionOrRange: number | TextRange, fileName: string): protocol.FileLocationOrRangeRequestArgs {
|
||||
if (typeof positionOrRange === "number") {
|
||||
const { line, offset } = this.positionToOneBasedLineOffset(fileName, positionOrRange);
|
||||
return <protocol.FileLocationRequestArgs>{ file: fileName, line, offset };
|
||||
}
|
||||
const { line: startLine, offset: startOffset } = this.positionToOneBasedLineOffset(fileName, positionOrRange.pos);
|
||||
const { line: endLine, offset: endOffset } = this.positionToOneBasedLineOffset(fileName, positionOrRange.end);
|
||||
return <protocol.FileRangeRequestArgs>{
|
||||
file: fileName,
|
||||
startLine,
|
||||
startOffset,
|
||||
endLine,
|
||||
endOffset
|
||||
};
|
||||
return typeof positionOrRange === "number"
|
||||
? this.createFileLocationRequestArgs(fileName, positionOrRange)
|
||||
: this.createFileRangeRequestArgs(fileName, positionOrRange.pos, positionOrRange.end);
|
||||
}
|
||||
|
||||
private createFileLocationRequestArgs(file: string, position: number): protocol.FileLocationRequestArgs {
|
||||
const { line, offset } = this.positionToOneBasedLineOffset(file, position);
|
||||
return { file, line, offset };
|
||||
}
|
||||
|
||||
private createFileRangeRequestArgs(file: string, start: number, end: number): protocol.FileRangeRequestArgs {
|
||||
const { line: startLine, offset: startOffset } = this.positionToOneBasedLineOffset(file, start);
|
||||
const { line: endLine, offset: endOffset } = this.positionToOneBasedLineOffset(file, end);
|
||||
return { file, startLine, startOffset, endLine, endOffset };
|
||||
}
|
||||
|
||||
private createFileLocationRequestArgsWithEndLineAndOffset(file: string, start: number, end: number): protocol.FileLocationRequestArgs & { endLine: number, endOffset: number } {
|
||||
const { line, offset } = this.positionToOneBasedLineOffset(file, start);
|
||||
const { line: endLine, offset: endOffset } = this.positionToOneBasedLineOffset(file, end);
|
||||
return { file, line, offset, endLine, endOffset };
|
||||
}
|
||||
|
||||
getApplicableRefactors(fileName: string, positionOrRange: number | TextRange): ApplicableRefactorInfo[] {
|
||||
@@ -723,20 +571,49 @@ namespace ts.server {
|
||||
return response.body;
|
||||
}
|
||||
|
||||
getRefactorCodeActions(
|
||||
getEditsForRefactor(
|
||||
fileName: string,
|
||||
_formatOptions: FormatCodeSettings,
|
||||
positionOrRange: number | TextRange,
|
||||
refactorName: string) {
|
||||
refactorName: string,
|
||||
actionName: string): RefactorEditInfo {
|
||||
|
||||
const args = this.createFileLocationOrRangeRequestArgs(positionOrRange, fileName) as protocol.GetRefactorCodeActionsRequestArgs;
|
||||
args.refactorName = refactorName;
|
||||
const args = this.createFileLocationOrRangeRequestArgs(positionOrRange, fileName) as protocol.GetEditsForRefactorRequestArgs;
|
||||
args.refactor = refactorName;
|
||||
args.action = actionName;
|
||||
|
||||
const request = this.processRequest<protocol.GetRefactorCodeActionsRequest>(CommandNames.GetRefactorCodeActions, args);
|
||||
const response = this.processResponse<protocol.GetRefactorCodeActionsResponse>(request);
|
||||
const codeActions = response.body.actions;
|
||||
const request = this.processRequest<protocol.GetEditsForRefactorRequest>(CommandNames.GetEditsForRefactor, args);
|
||||
const response = this.processResponse<protocol.GetEditsForRefactorResponse>(request);
|
||||
|
||||
return map(codeActions, codeAction => this.convertCodeActions(codeAction, fileName));
|
||||
if (!response.body) {
|
||||
return {
|
||||
edits: []
|
||||
};
|
||||
}
|
||||
|
||||
const edits: FileTextChanges[] = this.convertCodeEditsToTextChanges(response.body.edits);
|
||||
|
||||
const renameFilename: string | undefined = response.body.renameFilename;
|
||||
let renameLocation: number | undefined = undefined;
|
||||
if (renameFilename !== undefined) {
|
||||
renameLocation = this.lineOffsetToPosition(renameFilename, response.body.renameLocation);
|
||||
}
|
||||
|
||||
return {
|
||||
edits,
|
||||
renameFilename,
|
||||
renameLocation
|
||||
};
|
||||
}
|
||||
|
||||
private convertCodeEditsToTextChanges(edits: protocol.FileCodeEdits[]): FileTextChanges[] {
|
||||
return edits.map(edit => {
|
||||
const fileName = edit.fileName;
|
||||
return {
|
||||
fileName,
|
||||
textChanges: edit.textChanges.map(t => this.convertTextChangeToCodeEdit(t, fileName))
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
convertCodeActions(entry: protocol.CodeAction, fileName: string): CodeAction {
|
||||
@@ -750,37 +627,19 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
convertTextChangeToCodeEdit(change: protocol.CodeEdit, fileName: string): ts.TextChange {
|
||||
const start = this.lineOffsetToPosition(fileName, change.start);
|
||||
const end = this.lineOffsetToPosition(fileName, change.end);
|
||||
|
||||
return {
|
||||
span: {
|
||||
start: start,
|
||||
length: end - start
|
||||
},
|
||||
span: this.decodeSpan(change, fileName),
|
||||
newText: change.newText ? change.newText : ""
|
||||
};
|
||||
}
|
||||
|
||||
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[] {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
};
|
||||
const args = this.createFileLocationRequestArgs(fileName, position);
|
||||
|
||||
const request = this.processRequest<protocol.BraceRequest>(CommandNames.Brace, args);
|
||||
const response = this.processResponse<protocol.BraceResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
return {
|
||||
start: start,
|
||||
length: end - start,
|
||||
};
|
||||
});
|
||||
return response.body.map(entry => this.decodeSpan(entry, fileName));
|
||||
}
|
||||
|
||||
getIndentationAtPosition(_fileName: string, _position: number, _options: EditorOptions): number {
|
||||
|
||||
@@ -37,13 +37,15 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
export interface ProjectInfoTelemetryEventData {
|
||||
/** Cryptographically secure hash of project file location. */
|
||||
readonly projectId: string;
|
||||
/** Count of file extensions seen in the project. */
|
||||
readonly fileStats: FileStats;
|
||||
/**
|
||||
* Any compiler options that might contain paths will be taken out.
|
||||
* Enum compiler options will be converted to strings.
|
||||
*/
|
||||
readonly compilerOptions: ts.CompilerOptions;
|
||||
readonly compilerOptions: CompilerOptions;
|
||||
// "extends", "files", "include", or "exclude" will be undefined if an external config is used.
|
||||
// Otherwise, we will use "true" if the property is present and "false" if it is missing.
|
||||
readonly extends: boolean | undefined;
|
||||
@@ -199,7 +201,7 @@ namespace ts.server {
|
||||
* This helper function processes a list of projects and return the concatenated, sortd and deduplicated output of processing each project.
|
||||
*/
|
||||
export function combineProjectOutput<T>(projects: Project[], action: (project: Project) => T[], comparer?: (a: T, b: T) => number, areEqual?: (a: T, b: T) => boolean) {
|
||||
const result = projects.reduce<T[]>((previous, current) => concatenate(previous, action(current)), []).sort(comparer);
|
||||
const result = flatMap(projects, action).sort(comparer);
|
||||
return projects.length > 1 ? deduplicate(result, areEqual) : result;
|
||||
}
|
||||
|
||||
@@ -237,10 +239,7 @@ namespace ts.server {
|
||||
const fileNamePropertyReader: FilePropertyReader<string> = {
|
||||
getFileName: x => x,
|
||||
getScriptKind: _ => undefined,
|
||||
hasMixedContent: (fileName, extraFileExtensions) => {
|
||||
const mixedContentExtensions = ts.map(ts.filter(extraFileExtensions, item => item.isMixedContent), item => item.extension);
|
||||
return forEach(mixedContentExtensions, extension => fileExtensionIs(fileName, extension));
|
||||
}
|
||||
hasMixedContent: (fileName, extraFileExtensions) => some(extraFileExtensions, ext => ext.isMixedContent && fileExtensionIs(fileName, ext.extension)),
|
||||
};
|
||||
|
||||
const externalFilePropertyReader: FilePropertyReader<protocol.ExternalFile> = {
|
||||
@@ -341,7 +340,7 @@ namespace ts.server {
|
||||
/**
|
||||
* Container of all known scripts
|
||||
*/
|
||||
private readonly filenameToScriptInfo = createFileMap<ScriptInfo>();
|
||||
private readonly filenameToScriptInfo = createMap<ScriptInfo>();
|
||||
/**
|
||||
* maps external project file name to list of config files that were the part of this project
|
||||
*/
|
||||
@@ -371,7 +370,7 @@ namespace ts.server {
|
||||
private readonly throttledOperations: ThrottledOperations;
|
||||
|
||||
private readonly hostConfiguration: HostConfiguration;
|
||||
private static safelist: SafeList = defaultTypeSafeList;
|
||||
private safelist: SafeList = defaultTypeSafeList;
|
||||
|
||||
private changedFiles: ScriptInfo[];
|
||||
|
||||
@@ -563,10 +562,17 @@ namespace ts.server {
|
||||
}
|
||||
else {
|
||||
if (info && (!info.isScriptOpen())) {
|
||||
// file has been changed which might affect the set of referenced files in projects that include
|
||||
// this file and set of inferred projects
|
||||
info.reloadFromFile();
|
||||
this.updateProjectGraphs(info.containingProjects);
|
||||
if (info.containingProjects.length === 0) {
|
||||
// Orphan script info, remove it as we can always reload it on next open
|
||||
info.stopWatcher();
|
||||
this.filenameToScriptInfo.delete(info.path);
|
||||
}
|
||||
else {
|
||||
// file has been changed which might affect the set of referenced files in projects that include
|
||||
// this file and set of inferred projects
|
||||
info.reloadFromFile();
|
||||
this.updateProjectGraphs(info.containingProjects);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -579,7 +585,7 @@ namespace ts.server {
|
||||
// TODO: handle isOpen = true case
|
||||
|
||||
if (!info.isScriptOpen()) {
|
||||
this.filenameToScriptInfo.remove(info.path);
|
||||
this.filenameToScriptInfo.delete(info.path);
|
||||
this.lastDeletedFile = info;
|
||||
|
||||
// capture list of projects since detachAllProjects will wipe out original list
|
||||
@@ -624,7 +630,7 @@ namespace ts.server {
|
||||
// If a change was made inside "folder/file", node will trigger the callback twice:
|
||||
// one with the fileName being "folder/file", and the other one with "folder".
|
||||
// We don't respond to the second one.
|
||||
if (fileName && !ts.isSupportedSourceFileName(fileName, project.getCompilerOptions(), this.hostConfiguration.extraFileExtensions)) {
|
||||
if (fileName && !isSupportedSourceFileName(fileName, project.getCompilerOptions(), this.hostConfiguration.extraFileExtensions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -694,15 +700,15 @@ namespace ts.server {
|
||||
|
||||
switch (project.projectKind) {
|
||||
case ProjectKind.External:
|
||||
removeItemFromSet(this.externalProjects, <ExternalProject>project);
|
||||
unorderedRemoveItem(this.externalProjects, <ExternalProject>project);
|
||||
this.projectToSizeMap.delete((project as ExternalProject).externalProjectName);
|
||||
break;
|
||||
case ProjectKind.Configured:
|
||||
removeItemFromSet(this.configuredProjects, <ConfiguredProject>project);
|
||||
unorderedRemoveItem(this.configuredProjects, <ConfiguredProject>project);
|
||||
this.projectToSizeMap.delete((project as ConfiguredProject).canonicalConfigFilePath);
|
||||
break;
|
||||
case ProjectKind.Inferred:
|
||||
removeItemFromSet(this.inferredProjects, <InferredProject>project);
|
||||
unorderedRemoveItem(this.inferredProjects, <InferredProject>project);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -781,7 +787,7 @@ namespace ts.server {
|
||||
// to the disk, and the server's version of the file can be out of sync.
|
||||
info.close();
|
||||
|
||||
removeItemFromSet(this.openFiles, info);
|
||||
unorderedRemoveItem(this.openFiles, info);
|
||||
|
||||
// collect all projects that should be removed
|
||||
let projectsToRemove: Project[];
|
||||
@@ -827,11 +833,29 @@ namespace ts.server {
|
||||
this.assignScriptInfoToInferredProjectIfNecessary(f, /*addToListOfOpenFiles*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup script infos that arent part of any project is postponed to
|
||||
// next file open so that if file from same project is opened we wont end up creating same script infos
|
||||
}
|
||||
if (info.containingProjects.length === 0) {
|
||||
// if there are not projects that include this script info - delete it
|
||||
this.filenameToScriptInfo.remove(info.path);
|
||||
|
||||
// If the current info is being just closed - add the watcher file to track changes
|
||||
// But if file was deleted, handle that part
|
||||
if (this.host.fileExists(info.fileName)) {
|
||||
this.watchClosedScriptInfo(info);
|
||||
}
|
||||
else {
|
||||
this.handleDeletedFile(info);
|
||||
}
|
||||
}
|
||||
|
||||
private deleteOrphanScriptInfoNotInAnyProject() {
|
||||
this.filenameToScriptInfo.forEach(info => {
|
||||
if (!info.isScriptOpen() && info.containingProjects.length === 0) {
|
||||
// if there are not projects that include this script info - delete it
|
||||
info.stopWatcher();
|
||||
this.filenameToScriptInfo.delete(info.path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -913,7 +937,7 @@ namespace ts.server {
|
||||
|
||||
this.logger.info("Open files: ");
|
||||
for (const rootFile of this.openFiles) {
|
||||
this.logger.info(rootFile.fileName);
|
||||
this.logger.info(`\t${rootFile.fileName}`);
|
||||
}
|
||||
|
||||
this.logger.endGroup();
|
||||
@@ -948,20 +972,14 @@ namespace ts.server {
|
||||
configFilename = normalizePath(configFilename);
|
||||
|
||||
const configFileContent = this.host.readFile(configFilename);
|
||||
let errors: Diagnostic[];
|
||||
|
||||
const result = parseConfigFileTextToJson(configFilename, configFileContent);
|
||||
let config = result.config;
|
||||
|
||||
if (result.error) {
|
||||
// try to reparse config file
|
||||
const { configJsonObject: sanitizedConfig, diagnostics } = sanitizeConfigFile(configFilename, configFileContent);
|
||||
config = sanitizedConfig;
|
||||
errors = diagnostics.length ? diagnostics : [result.error];
|
||||
const result = parseJsonText(configFilename, configFileContent);
|
||||
if (!result.endOfFileToken) {
|
||||
result.endOfFileToken = <EndOfFileToken>{ kind: SyntaxKind.EndOfFileToken };
|
||||
}
|
||||
|
||||
const parsedCommandLine = parseJsonConfigFileContent(
|
||||
config,
|
||||
const errors = result.parseDiagnostics;
|
||||
const parsedCommandLine = parseJsonSourceFileConfigFileContent(
|
||||
result,
|
||||
this.host,
|
||||
getDirectoryPath(configFilename),
|
||||
/*existingOptions*/ {},
|
||||
@@ -970,23 +988,23 @@ namespace ts.server {
|
||||
this.hostConfiguration.extraFileExtensions);
|
||||
|
||||
if (parsedCommandLine.errors.length) {
|
||||
errors = concatenate(errors, parsedCommandLine.errors);
|
||||
errors.push(...parsedCommandLine.errors);
|
||||
}
|
||||
|
||||
Debug.assert(!!parsedCommandLine.fileNames);
|
||||
|
||||
if (parsedCommandLine.fileNames.length === 0) {
|
||||
(errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.The_config_file_0_found_doesn_t_contain_any_source_files, configFilename));
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.The_config_file_0_found_doesn_t_contain_any_source_files, configFilename));
|
||||
return { success: false, configFileErrors: errors };
|
||||
}
|
||||
|
||||
const projectOptions: ProjectOptions = {
|
||||
files: parsedCommandLine.fileNames,
|
||||
compilerOptions: parsedCommandLine.options,
|
||||
configHasExtendsProperty: config.extends !== undefined,
|
||||
configHasFilesProperty: config.files !== undefined,
|
||||
configHasIncludeProperty: config.include !== undefined,
|
||||
configHasExcludeProperty: config.exclude !== undefined,
|
||||
configHasExtendsProperty: parsedCommandLine.raw["extends"] !== undefined,
|
||||
configHasFilesProperty: parsedCommandLine.raw["files"] !== undefined,
|
||||
configHasIncludeProperty: parsedCommandLine.raw["include"] !== undefined,
|
||||
configHasExcludeProperty: parsedCommandLine.raw["exclude"] !== undefined,
|
||||
wildcardDirectories: createMapFromTemplate(parsedCommandLine.wildcardDirectories),
|
||||
typeAcquisition: parsedCommandLine.typeAcquisition,
|
||||
compileOnSave: parsedCommandLine.compileOnSave
|
||||
@@ -1049,6 +1067,7 @@ namespace ts.server {
|
||||
if (!this.eventHandler) return;
|
||||
|
||||
const data: ProjectInfoTelemetryEventData = {
|
||||
projectId: this.host.createHash(projectKey),
|
||||
fileStats: countEachFileTypes(project.getScriptInfos()),
|
||||
compilerOptions: convertCompilerOptionsForTelemetry(project.getCompilerOptions()),
|
||||
typeAcquisition: convertTypeAcquisition(project.getTypeAcquisition()),
|
||||
@@ -1060,7 +1079,7 @@ namespace ts.server {
|
||||
configFileName: configFileName(),
|
||||
projectType: project instanceof server.ExternalProject ? "external" : "configured",
|
||||
languageServiceEnabled: project.languageServiceEnabled,
|
||||
version: ts.version,
|
||||
version,
|
||||
};
|
||||
this.eventHandler({ eventName: ProjectInfoTelemetryEvent, data });
|
||||
|
||||
@@ -1070,7 +1089,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
const configFilePath = project instanceof server.ConfiguredProject && project.getConfigFilePath();
|
||||
const base = ts.getBaseFileName(configFilePath);
|
||||
const base = getBaseFileName(configFilePath);
|
||||
return base === "tsconfig.json" || base === "jsconfig.json" ? base : "other";
|
||||
}
|
||||
|
||||
@@ -1154,7 +1173,7 @@ namespace ts.server {
|
||||
return {
|
||||
success: conversionResult.success,
|
||||
project,
|
||||
errors: project.getProjectErrors()
|
||||
errors: project.getGlobalProjectErrors()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1300,6 +1319,14 @@ namespace ts.server {
|
||||
return this.getScriptInfoForNormalizedPath(toNormalizedPath(uncheckedFileName));
|
||||
}
|
||||
|
||||
watchClosedScriptInfo(info: ScriptInfo) {
|
||||
// do not watch files with mixed content - server doesn't know how to interpret it
|
||||
if (!info.hasMixedContent) {
|
||||
const { fileName } = info;
|
||||
info.setWatcher(this.host.watchFile(fileName, _ => this.onSourceFileChanged(fileName)));
|
||||
}
|
||||
}
|
||||
|
||||
getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean) {
|
||||
let info = this.getScriptInfoForNormalizedPath(fileName);
|
||||
if (!info) {
|
||||
@@ -1315,15 +1342,13 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
else {
|
||||
// do not watch files with mixed content - server doesn't know how to interpret it
|
||||
if (!hasMixedContent) {
|
||||
info.setWatcher(this.host.watchFile(fileName, _ => this.onSourceFileChanged(fileName)));
|
||||
}
|
||||
this.watchClosedScriptInfo(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info) {
|
||||
if (openedByClient && !info.isScriptOpen()) {
|
||||
info.stopWatcher();
|
||||
info.open(fileContent);
|
||||
if (hasMixedContent) {
|
||||
info.registerFileUpdate();
|
||||
@@ -1418,6 +1443,7 @@ namespace ts.server {
|
||||
for (const p of this.inferredProjects) {
|
||||
p.updateGraph();
|
||||
}
|
||||
|
||||
this.printProjects();
|
||||
}
|
||||
|
||||
@@ -1451,6 +1477,11 @@ namespace ts.server {
|
||||
// at this point if file is the part of some configured/external project then this project should be created
|
||||
const info = this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent);
|
||||
this.assignScriptInfoToInferredProjectIfNecessary(info, /*addToListOfOpenFiles*/ true);
|
||||
// Delete the orphan files here because there might be orphan script infos (which are not part of project)
|
||||
// when some file/s were closed which resulted in project removal.
|
||||
// It was then postponed to cleanup these script infos so that they can be reused if
|
||||
// the file from that old project is reopened because of opening file from here.
|
||||
this.deleteOrphanScriptInfoNotInAnyProject();
|
||||
this.printProjects();
|
||||
return { configFileName, configFileErrors };
|
||||
}
|
||||
@@ -1583,13 +1614,13 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
/** Makes a filename safe to insert in a RegExp */
|
||||
private static filenameEscapeRegexp = /[-\/\\^$*+?.()|[\]{}]/g;
|
||||
private static readonly filenameEscapeRegexp = /[-\/\\^$*+?.()|[\]{}]/g;
|
||||
private static escapeFilenameForRegex(filename: string) {
|
||||
return filename.replace(this.filenameEscapeRegexp, "\\$&");
|
||||
}
|
||||
|
||||
resetSafeList(): void {
|
||||
ProjectService.safelist = defaultTypeSafeList;
|
||||
this.safelist = defaultTypeSafeList;
|
||||
}
|
||||
|
||||
loadSafeList(fileName: string): void {
|
||||
@@ -1599,7 +1630,7 @@ namespace ts.server {
|
||||
raw[k].match = new RegExp(raw[k].match as {} as string, "i");
|
||||
}
|
||||
// raw is now fixed and ready
|
||||
ProjectService.safelist = raw;
|
||||
this.safelist = raw;
|
||||
}
|
||||
|
||||
applySafeList(proj: protocol.ExternalProject): void {
|
||||
@@ -1610,8 +1641,8 @@ namespace ts.server {
|
||||
|
||||
const normalizedNames = rootFiles.map(f => normalizeSlashes(f.fileName));
|
||||
|
||||
for (const name of Object.keys(ProjectService.safelist)) {
|
||||
const rule = ProjectService.safelist[name];
|
||||
for (const name of Object.keys(this.safelist)) {
|
||||
const rule = this.safelist[name];
|
||||
for (const root of normalizedNames) {
|
||||
if (rule.match.test(root)) {
|
||||
this.logger.info(`Excluding files based on rule ${name}`);
|
||||
|
||||
+28
-20
@@ -3,21 +3,21 @@
|
||||
/// <reference path="scriptInfo.ts" />
|
||||
|
||||
namespace ts.server {
|
||||
export class LSHost implements ts.LanguageServiceHost, ModuleResolutionHost {
|
||||
private compilationSettings: ts.CompilerOptions;
|
||||
private readonly resolvedModuleNames = createFileMap<Map<ResolvedModuleWithFailedLookupLocations>>();
|
||||
private readonly resolvedTypeReferenceDirectives = createFileMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
|
||||
export class LSHost implements LanguageServiceHost, ModuleResolutionHost {
|
||||
private compilationSettings: CompilerOptions;
|
||||
private readonly resolvedModuleNames = createMap<Map<ResolvedModuleWithFailedLookupLocations>>();
|
||||
private readonly resolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
|
||||
private readonly getCanonicalFileName: (fileName: string) => string;
|
||||
|
||||
private filesWithChangedSetOfUnresolvedImports: Path[];
|
||||
|
||||
private readonly resolveModuleName: typeof resolveModuleName;
|
||||
private resolveModuleName: typeof resolveModuleName;
|
||||
readonly trace: (s: string) => void;
|
||||
readonly realpath?: (path: string) => string;
|
||||
|
||||
constructor(private readonly host: ServerHost, private readonly project: Project, private readonly cancellationToken: HostCancellationToken) {
|
||||
constructor(private readonly host: ServerHost, private project: Project, private readonly cancellationToken: HostCancellationToken) {
|
||||
this.cancellationToken = new ThrottledCancellationToken(cancellationToken, project.projectService.throttleWaitMilliseconds);
|
||||
this.getCanonicalFileName = ts.createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
|
||||
this.getCanonicalFileName = createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
|
||||
|
||||
if (host.trace) {
|
||||
this.trace = s => host.trace(s);
|
||||
@@ -29,7 +29,7 @@ namespace ts.server {
|
||||
: undefined;
|
||||
const primaryResult = resolveModuleName(moduleName, containingFile, compilerOptions, host);
|
||||
// return result immediately only if it is .ts, .tsx or .d.ts
|
||||
if (moduleHasNonRelativeName(moduleName) && !(primaryResult.resolvedModule && extensionIsTypeScript(primaryResult.resolvedModule.extension)) && globalCache !== undefined) {
|
||||
if (!isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTypeScript(primaryResult.resolvedModule.extension)) && globalCache !== undefined) {
|
||||
// otherwise try to load typings from @types
|
||||
|
||||
// create different collection of failed lookup locations for second pass
|
||||
@@ -47,6 +47,11 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.project = undefined;
|
||||
this.resolveModuleName = undefined;
|
||||
}
|
||||
|
||||
public startRecordingFilesWithChangedResolutions() {
|
||||
this.filesWithChangedSetOfUnresolvedImports = [];
|
||||
}
|
||||
@@ -60,7 +65,7 @@ namespace ts.server {
|
||||
private resolveNamesWithLocalCache<T extends { failedLookupLocations: string[] }, R>(
|
||||
names: string[],
|
||||
containingFile: string,
|
||||
cache: ts.FileMap<Map<T>>,
|
||||
cache: Map<Map<T>>,
|
||||
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => T,
|
||||
getResult: (s: T) => R,
|
||||
getResultFileName: (result: R) => string | undefined,
|
||||
@@ -94,7 +99,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
ts.Debug.assert(resolution !== undefined);
|
||||
Debug.assert(resolution !== undefined);
|
||||
|
||||
resolvedModules.push(getResult(resolution));
|
||||
}
|
||||
@@ -172,7 +177,7 @@ namespace ts.server {
|
||||
return combinePaths(nodeModuleBinDir, getDefaultLibFileName(this.compilationSettings));
|
||||
}
|
||||
|
||||
getScriptSnapshot(filename: string): ts.IScriptSnapshot {
|
||||
getScriptSnapshot(filename: string): IScriptSnapshot {
|
||||
const scriptInfo = this.project.getScriptInfoLSHost(filename);
|
||||
if (scriptInfo) {
|
||||
return scriptInfo.getSnapshot();
|
||||
@@ -205,11 +210,14 @@ namespace ts.server {
|
||||
return this.host.resolvePath(path);
|
||||
}
|
||||
|
||||
fileExists(path: string): boolean {
|
||||
return this.host.fileExists(path);
|
||||
fileExists(file: string): boolean {
|
||||
// As an optimization, don't hit the disks for files we already know don't exist
|
||||
// (because we're watching for their creation).
|
||||
const path = toPath(file, this.host.getCurrentDirectory(), this.getCanonicalFileName);
|
||||
return !this.project.isWatchedMissingFile(path) && this.host.fileExists(file);
|
||||
}
|
||||
|
||||
readFile(fileName: string): string {
|
||||
readFile(fileName: string): string | undefined {
|
||||
return this.host.readFile(fileName);
|
||||
}
|
||||
|
||||
@@ -217,8 +225,8 @@ namespace ts.server {
|
||||
return this.host.directoryExists(path);
|
||||
}
|
||||
|
||||
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
|
||||
return this.host.readDirectory(path, extensions, exclude, include);
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
|
||||
return this.host.readDirectory(path, extensions, exclude, include, depth);
|
||||
}
|
||||
|
||||
getDirectories(path: string): string[] {
|
||||
@@ -226,11 +234,11 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
notifyFileRemoved(info: ScriptInfo) {
|
||||
this.resolvedModuleNames.remove(info.path);
|
||||
this.resolvedTypeReferenceDirectives.remove(info.path);
|
||||
this.resolvedModuleNames.delete(info.path);
|
||||
this.resolvedTypeReferenceDirectives.delete(info.path);
|
||||
}
|
||||
|
||||
setCompilationSettings(opt: ts.CompilerOptions) {
|
||||
setCompilationSettings(opt: CompilerOptions) {
|
||||
if (changesAffectModuleResolution(this.compilationSettings, opt)) {
|
||||
this.resolvedModuleNames.clear();
|
||||
this.resolvedTypeReferenceDirectives.clear();
|
||||
@@ -238,4 +246,4 @@ namespace ts.server {
|
||||
this.compilationSettings = opt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+126
-42
@@ -25,7 +25,7 @@ namespace ts.server {
|
||||
result.jsx += 1;
|
||||
break;
|
||||
case ScriptKind.TS:
|
||||
fileExtensionIs(info.fileName, ".d.ts")
|
||||
fileExtensionIs(info.fileName, Extension.Dts)
|
||||
? result.dts += 1
|
||||
: result.ts += 1;
|
||||
break;
|
||||
@@ -58,7 +58,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
export class UnresolvedImportsMap {
|
||||
readonly perFileMap = createFileMap<ReadonlyArray<string>>();
|
||||
readonly perFileMap = createMap<ReadonlyArray<string>>();
|
||||
private version = 0;
|
||||
|
||||
public clear() {
|
||||
@@ -71,7 +71,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
public remove(path: Path) {
|
||||
this.perFileMap.remove(path);
|
||||
this.perFileMap.delete(path);
|
||||
this.version++;
|
||||
}
|
||||
|
||||
@@ -104,9 +104,10 @@ namespace ts.server {
|
||||
|
||||
export abstract class Project {
|
||||
private rootFiles: ScriptInfo[] = [];
|
||||
private rootFilesMap: FileMap<ScriptInfo> = createFileMap<ScriptInfo>();
|
||||
private program: ts.Program;
|
||||
private rootFilesMap: Map<ScriptInfo> = createMap<ScriptInfo>();
|
||||
private program: Program;
|
||||
private externalFiles: SortedReadonlyArray<string>;
|
||||
private missingFilesMap: Map<FileWatcher> = createMap<FileWatcher>();
|
||||
|
||||
private cachedUnresolvedImportsPerFile = new UnresolvedImportsMap();
|
||||
private lastCachedUnresolvedImportsList: SortedReadonlyArray<string>;
|
||||
@@ -116,17 +117,17 @@ namespace ts.server {
|
||||
|
||||
public languageServiceEnabled = true;
|
||||
|
||||
protected readonly lsHost: LSHost;
|
||||
protected lsHost: LSHost;
|
||||
|
||||
builder: Builder;
|
||||
/**
|
||||
* Set of files names that were updated since the last call to getChangesSinceVersion.
|
||||
*/
|
||||
private updatedFileNames: Map<string>;
|
||||
private updatedFileNames: Map<true>;
|
||||
/**
|
||||
* Set of files that was returned from the last call to getChangesSinceVersion.
|
||||
*/
|
||||
private lastReportedFileNames: Map<string>;
|
||||
private lastReportedFileNames: Map<true>;
|
||||
/**
|
||||
* Last version that was reported.
|
||||
*/
|
||||
@@ -179,14 +180,14 @@ namespace ts.server {
|
||||
private readonly projectName: string,
|
||||
readonly projectKind: ProjectKind,
|
||||
readonly projectService: ProjectService,
|
||||
private documentRegistry: ts.DocumentRegistry,
|
||||
private documentRegistry: DocumentRegistry,
|
||||
hasExplicitListOfFiles: boolean,
|
||||
languageServiceEnabled: boolean,
|
||||
private compilerOptions: CompilerOptions,
|
||||
public compileOnSaveEnabled: boolean) {
|
||||
|
||||
if (!this.compilerOptions) {
|
||||
this.compilerOptions = ts.getDefaultCompilerOptions();
|
||||
this.compilerOptions = getDefaultCompilerOptions();
|
||||
this.compilerOptions.allowNonTsExtensions = true;
|
||||
this.compilerOptions.allowJs = true;
|
||||
}
|
||||
@@ -200,7 +201,7 @@ namespace ts.server {
|
||||
this.lsHost = new LSHost(this.projectService.host, this, this.projectService.cancellationToken);
|
||||
this.lsHost.setCompilationSettings(this.compilerOptions);
|
||||
|
||||
this.languageService = ts.createLanguageService(this.lsHost, this.documentRegistry);
|
||||
this.languageService = createLanguageService(this.lsHost, this.documentRegistry);
|
||||
|
||||
if (!languageServiceEnabled) {
|
||||
this.disableLanguageService();
|
||||
@@ -216,7 +217,14 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
getProjectErrors() {
|
||||
/**
|
||||
* Get the errors that dont have any file name associated
|
||||
*/
|
||||
getGlobalProjectErrors() {
|
||||
return filter(this.projectErrors, diagnostic => !diagnostic.file);
|
||||
}
|
||||
|
||||
getAllProjectErrors() {
|
||||
return this.projectErrors;
|
||||
}
|
||||
|
||||
@@ -297,9 +305,19 @@ namespace ts.server {
|
||||
this.rootFiles = undefined;
|
||||
this.rootFilesMap = undefined;
|
||||
this.program = undefined;
|
||||
this.builder = undefined;
|
||||
this.cachedUnresolvedImportsPerFile = undefined;
|
||||
this.projectErrors = undefined;
|
||||
this.lsHost.dispose();
|
||||
this.lsHost = undefined;
|
||||
|
||||
// Clean up file watchers waiting for missing files
|
||||
this.missingFilesMap.forEach(fileWatcher => fileWatcher.close());
|
||||
this.missingFilesMap = undefined;
|
||||
|
||||
// signal language service to release source files acquired from document registry
|
||||
this.languageService.dispose();
|
||||
this.languageService = undefined;
|
||||
}
|
||||
|
||||
getCompilerOptions() {
|
||||
@@ -357,7 +375,7 @@ namespace ts.server {
|
||||
return this.getLanguageService().getEmitOutput(info.fileName, emitOnlyDtsFiles);
|
||||
}
|
||||
|
||||
getFileNames(excludeFilesFromExternalLibraries?: boolean) {
|
||||
getFileNames(excludeFilesFromExternalLibraries?: boolean, excludeConfigFiles?: boolean) {
|
||||
if (!this.program) {
|
||||
return [];
|
||||
}
|
||||
@@ -380,9 +398,39 @@ namespace ts.server {
|
||||
}
|
||||
result.push(asNormalizedPath(f.fileName));
|
||||
}
|
||||
if (!excludeConfigFiles) {
|
||||
const configFile = this.program.getCompilerOptions().configFile;
|
||||
if (configFile) {
|
||||
result.push(asNormalizedPath(configFile.fileName));
|
||||
if (configFile.extendedSourceFiles) {
|
||||
for (const f of configFile.extendedSourceFiles) {
|
||||
result.push(asNormalizedPath(f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
hasConfigFile(configFilePath: NormalizedPath) {
|
||||
if (this.program && this.languageServiceEnabled) {
|
||||
const configFile = this.program.getCompilerOptions().configFile;
|
||||
if (configFile) {
|
||||
if (configFilePath === asNormalizedPath(configFile.fileName)) {
|
||||
return true;
|
||||
}
|
||||
if (configFile.extendedSourceFiles) {
|
||||
for (const f of configFile.extendedSourceFiles) {
|
||||
if (configFilePath === asNormalizedPath(f)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getAllEmittableFiles() {
|
||||
if (!this.languageServiceEnabled) {
|
||||
return [];
|
||||
@@ -410,7 +458,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
isRoot(info: ScriptInfo) {
|
||||
return this.rootFilesMap && this.rootFilesMap.contains(info.path);
|
||||
return this.rootFilesMap && this.rootFilesMap.has(info.path);
|
||||
}
|
||||
|
||||
// add a root file to project
|
||||
@@ -439,7 +487,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
registerFileUpdate(fileName: string) {
|
||||
(this.updatedFileNames || (this.updatedFileNames = createMap<string>())).set(fileName, fileName);
|
||||
(this.updatedFileNames || (this.updatedFileNames = createMap<true>())).set(fileName, true);
|
||||
}
|
||||
|
||||
markAsDirty() {
|
||||
@@ -506,7 +554,7 @@ namespace ts.server {
|
||||
for (const sourceFile of this.program.getSourceFiles()) {
|
||||
this.extractUnresolvedImportsFromSourceFile(sourceFile, result);
|
||||
}
|
||||
this.lastCachedUnresolvedImportsList = toSortedReadonlyArray(result);
|
||||
this.lastCachedUnresolvedImportsList = toSortedArray(result);
|
||||
}
|
||||
unresolvedImports = this.lastCachedUnresolvedImportsList;
|
||||
|
||||
@@ -543,12 +591,12 @@ namespace ts.server {
|
||||
const oldProgram = this.program;
|
||||
this.program = this.languageService.getProgram();
|
||||
|
||||
let hasChanges = false;
|
||||
// bump up the version if
|
||||
// - oldProgram is not set - this is a first time updateGraph is called
|
||||
// - newProgram is different from the old program and structure of the old program was not reused.
|
||||
if (!oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused & StructureIsReused.Completely))) {
|
||||
hasChanges = true;
|
||||
const hasChanges = !oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused & StructureIsReused.Completely));
|
||||
|
||||
if (hasChanges) {
|
||||
if (oldProgram) {
|
||||
for (const f of oldProgram.getSourceFiles()) {
|
||||
if (this.program.getSourceFileByPath(f.path)) {
|
||||
@@ -561,6 +609,35 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const missingFilePaths = this.program.getMissingFilePaths();
|
||||
const missingFilePathsSet = arrayToSet(missingFilePaths);
|
||||
|
||||
// Files that are no longer missing (e.g. because they are no longer required)
|
||||
// should no longer be watched.
|
||||
this.missingFilesMap.forEach((fileWatcher, missingFilePath) => {
|
||||
if (!missingFilePathsSet.has(missingFilePath)) {
|
||||
this.missingFilesMap.delete(missingFilePath);
|
||||
fileWatcher.close();
|
||||
}
|
||||
});
|
||||
|
||||
// Missing files that are not yet watched should be added to the map.
|
||||
for (const missingFilePath of missingFilePaths) {
|
||||
if (!this.missingFilesMap.has(missingFilePath)) {
|
||||
const fileWatcher = this.projectService.host.watchFile(missingFilePath, (_filename: string, eventKind: FileWatcherEventKind) => {
|
||||
if (eventKind === FileWatcherEventKind.Created && this.missingFilesMap.has(missingFilePath)) {
|
||||
fileWatcher.close();
|
||||
this.missingFilesMap.delete(missingFilePath);
|
||||
|
||||
// When a missing file is created, we should update the graph.
|
||||
this.markAsDirty();
|
||||
this.updateGraph();
|
||||
}
|
||||
});
|
||||
this.missingFilesMap.set(missingFilePath, fileWatcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const oldExternalFiles = this.externalFiles || emptyArray as SortedReadonlyArray<string>;
|
||||
@@ -583,6 +660,10 @@ namespace ts.server {
|
||||
return hasChanges;
|
||||
}
|
||||
|
||||
isWatchedMissingFile(path: Path) {
|
||||
return this.missingFilesMap.has(path);
|
||||
}
|
||||
|
||||
getScriptInfoLSHost(fileName: string) {
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfo(fileName, /*openedByClient*/ false);
|
||||
if (scriptInfo) {
|
||||
@@ -609,7 +690,7 @@ namespace ts.server {
|
||||
}
|
||||
let strBuilder = "";
|
||||
for (const file of this.program.getSourceFiles()) {
|
||||
strBuilder += `${file.fileName}\n`;
|
||||
strBuilder += `\t${file.fileName}\n`;
|
||||
}
|
||||
return strBuilder;
|
||||
}
|
||||
@@ -657,11 +738,11 @@ namespace ts.server {
|
||||
if (this.lastReportedFileNames && lastKnownVersion === this.lastReportedVersion) {
|
||||
// if current structure version is the same - return info without any changes
|
||||
if (this.projectStructureVersion === this.lastReportedVersion && !updatedFileNames) {
|
||||
return { info, projectErrors: this.projectErrors };
|
||||
return { info, projectErrors: this.getGlobalProjectErrors() };
|
||||
}
|
||||
// compute and return the difference
|
||||
const lastReportedFileNames = this.lastReportedFileNames;
|
||||
const currentFiles = arrayToMap(this.getFileNames(), x => x);
|
||||
const currentFiles = arrayToSet(this.getFileNames());
|
||||
|
||||
const added: string[] = [];
|
||||
const removed: string[] = [];
|
||||
@@ -679,14 +760,14 @@ namespace ts.server {
|
||||
});
|
||||
this.lastReportedFileNames = currentFiles;
|
||||
this.lastReportedVersion = this.projectStructureVersion;
|
||||
return { info, changes: { added, removed, updated }, projectErrors: this.projectErrors };
|
||||
return { info, changes: { added, removed, updated }, projectErrors: this.getGlobalProjectErrors() };
|
||||
}
|
||||
else {
|
||||
// unknown version - return everything
|
||||
const projectFileNames = this.getFileNames();
|
||||
this.lastReportedFileNames = arrayToMap(projectFileNames, x => x);
|
||||
this.lastReportedFileNames = arrayToSet(projectFileNames);
|
||||
this.lastReportedVersion = this.projectStructureVersion;
|
||||
return { info, files: projectFileNames, projectErrors: this.projectErrors };
|
||||
return { info, files: projectFileNames, projectErrors: this.getGlobalProjectErrors() };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -702,7 +783,7 @@ namespace ts.server {
|
||||
// We need to use a set here since the code can contain the same import twice,
|
||||
// but that will only be one dependency.
|
||||
// To avoid invernal conversion, the key of the referencedFiles map must be of type Path
|
||||
const referencedFiles = createMap<boolean>();
|
||||
const referencedFiles = createMap<true>();
|
||||
if (sourceFile.imports && sourceFile.imports.length > 0) {
|
||||
const checker: TypeChecker = this.program.getTypeChecker();
|
||||
for (const importName of sourceFile.imports) {
|
||||
@@ -746,7 +827,7 @@ namespace ts.server {
|
||||
// remove a root file from project
|
||||
protected removeRoot(info: ScriptInfo): void {
|
||||
orderedRemoveItem(this.rootFiles, info);
|
||||
this.rootFilesMap.remove(info.path);
|
||||
this.rootFilesMap.delete(info.path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -756,7 +837,7 @@ namespace ts.server {
|
||||
*/
|
||||
export class InferredProject extends Project {
|
||||
|
||||
private static newName = (() => {
|
||||
private static readonly newName = (() => {
|
||||
let nextId = 1;
|
||||
return () => {
|
||||
const id = nextId;
|
||||
@@ -776,7 +857,7 @@ namespace ts.server {
|
||||
|
||||
setCompilerOptions(options?: CompilerOptions) {
|
||||
// Avoid manipulating the given options directly
|
||||
const newOptions = options ? clone(options) : this.getCompilerOptions();
|
||||
const newOptions = options ? cloneCompilerOptions(options) : this.getCompilerOptions();
|
||||
if (!newOptions) {
|
||||
return;
|
||||
}
|
||||
@@ -794,7 +875,7 @@ namespace ts.server {
|
||||
// Used to keep track of what directories are watched for this project
|
||||
directoriesWatchedForTsconfig: string[] = [];
|
||||
|
||||
constructor(projectService: ProjectService, documentRegistry: ts.DocumentRegistry, compilerOptions: CompilerOptions) {
|
||||
constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions) {
|
||||
super(InferredProject.newName(),
|
||||
ProjectKind.Inferred,
|
||||
projectService,
|
||||
@@ -855,9 +936,9 @@ namespace ts.server {
|
||||
export class ConfiguredProject extends Project {
|
||||
private typeAcquisition: TypeAcquisition;
|
||||
private projectFileWatcher: FileWatcher;
|
||||
private directoryWatcher: FileWatcher;
|
||||
private directoriesWatchedForWildcards: Map<FileWatcher>;
|
||||
private typeRootsWatchers: FileWatcher[];
|
||||
private directoryWatcher: FileWatcher | undefined;
|
||||
private directoriesWatchedForWildcards: Map<FileWatcher> | undefined;
|
||||
private typeRootsWatchers: FileWatcher[] | undefined;
|
||||
readonly canonicalConfigFilePath: NormalizedPath;
|
||||
|
||||
private plugins: PluginModule[] = [];
|
||||
@@ -867,7 +948,7 @@ namespace ts.server {
|
||||
|
||||
constructor(configFileName: NormalizedPath,
|
||||
projectService: ProjectService,
|
||||
documentRegistry: ts.DocumentRegistry,
|
||||
documentRegistry: DocumentRegistry,
|
||||
hasExplicitListOfFiles: boolean,
|
||||
compilerOptions: CompilerOptions,
|
||||
private wildcardDirectories: Map<WatchDirectoryFlags>,
|
||||
@@ -976,7 +1057,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getExternalFiles(): SortedReadonlyArray<string> {
|
||||
return toSortedReadonlyArray(flatMap(this.plugins, plugin => {
|
||||
return toSortedArray(flatMap(this.plugins, plugin => {
|
||||
if (typeof plugin.getExternalFiles !== "function") return;
|
||||
try {
|
||||
return plugin.getExternalFiles(this);
|
||||
@@ -1043,6 +1124,7 @@ namespace ts.server {
|
||||
|
||||
if (this.projectFileWatcher) {
|
||||
this.projectFileWatcher.close();
|
||||
this.projectFileWatcher = undefined;
|
||||
}
|
||||
|
||||
if (this.typeRootsWatchers) {
|
||||
@@ -1052,10 +1134,12 @@ namespace ts.server {
|
||||
this.typeRootsWatchers = undefined;
|
||||
}
|
||||
|
||||
this.directoriesWatchedForWildcards.forEach(watcher => {
|
||||
watcher.close();
|
||||
});
|
||||
this.directoriesWatchedForWildcards = undefined;
|
||||
if (this.directoriesWatchedForWildcards) {
|
||||
this.directoriesWatchedForWildcards.forEach(watcher => {
|
||||
watcher.close();
|
||||
});
|
||||
this.directoriesWatchedForWildcards = undefined;
|
||||
}
|
||||
|
||||
this.stopWatchingDirectory();
|
||||
}
|
||||
@@ -1070,7 +1154,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getEffectiveTypeRoots() {
|
||||
return ts.getEffectiveTypeRoots(this.getCompilerOptions(), this.projectService.host) || [];
|
||||
return getEffectiveTypeRoots(this.getCompilerOptions(), this.projectService.host) || [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1082,7 +1166,7 @@ namespace ts.server {
|
||||
private typeAcquisition: TypeAcquisition;
|
||||
constructor(public externalProjectName: string,
|
||||
projectService: ProjectService,
|
||||
documentRegistry: ts.DocumentRegistry,
|
||||
documentRegistry: DocumentRegistry,
|
||||
compilerOptions: CompilerOptions,
|
||||
languageServiceEnabled: boolean,
|
||||
public compileOnSaveEnabled: boolean,
|
||||
@@ -1132,4 +1216,4 @@ namespace ts.server {
|
||||
this.typeAcquisition = newTypeAcquisition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+88
-32
@@ -99,8 +99,11 @@ namespace ts.server.protocol {
|
||||
GetSupportedCodeFixes = "getSupportedCodeFixes",
|
||||
|
||||
GetApplicableRefactors = "getApplicableRefactors",
|
||||
GetRefactorCodeActions = "getRefactorCodeActions",
|
||||
GetRefactorCodeActionsFull = "getRefactorCodeActions-full",
|
||||
GetEditsForRefactor = "getEditsForRefactor",
|
||||
/* @internal */
|
||||
GetEditsForRefactorFull = "getEditsForRefactor-full",
|
||||
|
||||
// NOTE: If updating this, be sure to also update `allCommandNames` in `harness/unittests/session.ts`.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -417,52 +420,98 @@ namespace ts.server.protocol {
|
||||
|
||||
export type FileLocationOrRangeRequestArgs = FileLocationRequestArgs | FileRangeRequestArgs;
|
||||
|
||||
/**
|
||||
* Request refactorings at a given position or selection area.
|
||||
*/
|
||||
export interface GetApplicableRefactorsRequest extends Request {
|
||||
command: CommandTypes.GetApplicableRefactors;
|
||||
arguments: GetApplicableRefactorsRequestArgs;
|
||||
}
|
||||
|
||||
export type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs;
|
||||
|
||||
export interface ApplicableRefactorInfo {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response is a list of available refactorings.
|
||||
* Each refactoring exposes one or more "Actions"; a user selects one action to invoke a refactoring
|
||||
*/
|
||||
export interface GetApplicableRefactorsResponse extends Response {
|
||||
body?: ApplicableRefactorInfo[];
|
||||
}
|
||||
|
||||
export interface GetRefactorCodeActionsRequest extends Request {
|
||||
command: CommandTypes.GetRefactorCodeActions;
|
||||
arguments: GetRefactorCodeActionsRequestArgs;
|
||||
/**
|
||||
* A set of one or more available refactoring actions, grouped under a parent refactoring.
|
||||
*/
|
||||
export interface ApplicableRefactorInfo {
|
||||
/**
|
||||
* The programmatic name of the refactoring
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* A description of this refactoring category to show to the user.
|
||||
* If the refactoring gets inlined (see below), this text will not be visible.
|
||||
*/
|
||||
description: string;
|
||||
/**
|
||||
* Inlineable refactorings can have their actions hoisted out to the top level
|
||||
* of a context menu. Non-inlineanable refactorings should always be shown inside
|
||||
* their parent grouping.
|
||||
*
|
||||
* If not specified, this value is assumed to be 'true'
|
||||
*/
|
||||
inlineable?: boolean;
|
||||
|
||||
actions: RefactorActionInfo[];
|
||||
}
|
||||
|
||||
export type GetRefactorCodeActionsRequestArgs = FileLocationOrRangeRequestArgs & {
|
||||
/* The kind of the applicable refactor */
|
||||
refactorName: string;
|
||||
/**
|
||||
* Represents a single refactoring action - for example, the "Extract Method..." refactor might
|
||||
* offer several actions, each corresponding to a surround class or closure to extract into.
|
||||
*/
|
||||
export type RefactorActionInfo = {
|
||||
/**
|
||||
* The programmatic name of the refactoring action
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* A description of this refactoring action to show to the user.
|
||||
* If the parent refactoring is inlined away, this will be the only text shown,
|
||||
* so this description should make sense by itself if the parent is inlineable=true
|
||||
*/
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type RefactorCodeActions = {
|
||||
actions: protocol.CodeAction[];
|
||||
renameLocation?: number
|
||||
};
|
||||
|
||||
/* @internal */
|
||||
export type RefactorCodeActionsFull = {
|
||||
actions: ts.CodeAction[];
|
||||
renameLocation?: number
|
||||
};
|
||||
|
||||
export interface GetRefactorCodeActionsResponse extends Response {
|
||||
body: RefactorCodeActions;
|
||||
export interface GetEditsForRefactorRequest extends Request {
|
||||
command: CommandTypes.GetEditsForRefactor;
|
||||
arguments: GetEditsForRefactorRequestArgs;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface GetRefactorCodeActionsFullResponse extends Response {
|
||||
body: RefactorCodeActionsFull;
|
||||
/**
|
||||
* Request the edits that a particular refactoring action produces.
|
||||
* Callers must specify the name of the refactor and the name of the action.
|
||||
*/
|
||||
export type GetEditsForRefactorRequestArgs = FileLocationOrRangeRequestArgs & {
|
||||
/* The 'name' property from the refactoring that offered this action */
|
||||
refactor: string;
|
||||
/* The 'name' property from the refactoring action */
|
||||
action: string;
|
||||
};
|
||||
|
||||
|
||||
export interface GetEditsForRefactorResponse extends Response {
|
||||
body?: RefactorEditInfo;
|
||||
}
|
||||
|
||||
export type RefactorEditInfo = {
|
||||
edits: FileCodeEdits[];
|
||||
|
||||
/**
|
||||
* An optional location where the editor should start a rename operation once
|
||||
* the refactoring edits have been applied
|
||||
*/
|
||||
renameLocation?: Location;
|
||||
renameFilename?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Request for the available codefixes at a specific position.
|
||||
*/
|
||||
@@ -607,7 +656,7 @@ namespace ts.server.protocol {
|
||||
}
|
||||
|
||||
/**
|
||||
* Location in source code expressed as (one-based) line and character offset.
|
||||
* Location in source code expressed as (one-based) line and (one-based) column offset.
|
||||
*/
|
||||
export interface Location {
|
||||
line: number;
|
||||
@@ -1910,6 +1959,13 @@ namespace ts.server.protocol {
|
||||
source?: string;
|
||||
}
|
||||
|
||||
export interface DiagnosticWithFileName extends Diagnostic {
|
||||
/**
|
||||
* Name of the file the diagnostic is in
|
||||
*/
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
export interface DiagnosticEventBody {
|
||||
/**
|
||||
* The file for which diagnostic information is reported.
|
||||
@@ -1944,7 +2000,7 @@ namespace ts.server.protocol {
|
||||
/**
|
||||
* An arry of diagnostic information items for the found config file.
|
||||
*/
|
||||
diagnostics: Diagnostic[];
|
||||
diagnostics: DiagnosticWithFileName[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+15
-34
@@ -61,7 +61,7 @@ namespace ts.server {
|
||||
: ScriptSnapshot.fromString(this.getOrLoadText());
|
||||
}
|
||||
|
||||
public getLineInfo(line: number) {
|
||||
public getLineInfo(line: number): AbsolutePositionAndLineText {
|
||||
return this.switchToScriptVersionCache().getSnapshot().index.lineNumberToInfo(line);
|
||||
}
|
||||
/**
|
||||
@@ -72,19 +72,12 @@ namespace ts.server {
|
||||
const lineMap = this.getLineMap();
|
||||
const start = lineMap[line]; // -1 since line is 1-based
|
||||
const end = line + 1 < lineMap.length ? lineMap[line + 1] : this.text.length;
|
||||
return ts.createTextSpanFromBounds(start, end);
|
||||
return createTextSpanFromBounds(start, end);
|
||||
}
|
||||
const index = this.svc.getSnapshot().index;
|
||||
const lineInfo = index.lineNumberToInfo(line + 1);
|
||||
let len: number;
|
||||
if (lineInfo.leaf) {
|
||||
len = lineInfo.leaf.text.length;
|
||||
}
|
||||
else {
|
||||
const nextLineInfo = index.lineNumberToInfo(line + 2);
|
||||
len = nextLineInfo.offset - lineInfo.offset;
|
||||
}
|
||||
return ts.createTextSpan(lineInfo.offset, len);
|
||||
const { lineText, absolutePosition } = index.lineNumberToInfo(line + 1);
|
||||
const len = lineText !== undefined ? lineText.length : index.absolutePositionOfStartOfLine(line + 2) - absolutePosition;
|
||||
return createTextSpan(absolutePosition, len);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,27 +86,19 @@ namespace ts.server {
|
||||
*/
|
||||
lineOffsetToPosition(line: number, offset: number): number {
|
||||
if (!this.svc) {
|
||||
return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1);
|
||||
return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1, this.text);
|
||||
}
|
||||
const index = this.svc.getSnapshot().index;
|
||||
|
||||
const lineInfo = index.lineNumberToInfo(line);
|
||||
// TODO: assert this offset is actually on the line
|
||||
return (lineInfo.offset + offset - 1);
|
||||
return this.svc.getSnapshot().index.absolutePositionOfStartOfLine(line) + (offset - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line 1-based index
|
||||
* @param offset 1-based index
|
||||
*/
|
||||
positionToLineOffset(position: number): ILineInfo {
|
||||
positionToLineOffset(position: number): protocol.Location {
|
||||
if (!this.svc) {
|
||||
const { line, character } = computeLineAndCharacterOfPosition(this.getLineMap(), position);
|
||||
return { line: line + 1, offset: character + 1 };
|
||||
}
|
||||
const index = this.svc.getSnapshot().index;
|
||||
const lineOffset = index.charOffsetToLineNumberAndPos(position);
|
||||
return { line: lineOffset.line, offset: lineOffset.offset + 1 };
|
||||
return this.svc.getSnapshot().index.positionToLineOffset(position);
|
||||
}
|
||||
|
||||
private getFileText(tempFileName?: string) {
|
||||
@@ -126,7 +111,7 @@ namespace ts.server {
|
||||
|
||||
private switchToScriptVersionCache(newText?: string): ScriptVersionCache {
|
||||
if (!this.svc) {
|
||||
this.svc = ScriptVersionCache.fromString(this.host, newText !== undefined ? newText : this.getOrLoadText());
|
||||
this.svc = ScriptVersionCache.fromString(newText !== undefined ? newText : this.getOrLoadText());
|
||||
this.svcVersion++;
|
||||
this.text = undefined;
|
||||
}
|
||||
@@ -162,7 +147,7 @@ namespace ts.server {
|
||||
* All projects that include this file
|
||||
*/
|
||||
readonly containingProjects: Project[] = [];
|
||||
private formatCodeSettings: ts.FormatCodeSettings;
|
||||
private formatCodeSettings: FormatCodeSettings;
|
||||
readonly path: Path;
|
||||
|
||||
private fileWatcher: FileWatcher;
|
||||
@@ -247,7 +232,7 @@ namespace ts.server {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
removeItemFromSet(this.containingProjects, project);
|
||||
unorderedRemoveItem(this.containingProjects, project);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -257,7 +242,7 @@ namespace ts.server {
|
||||
// detach is unnecessary since we'll clean the list of containing projects anyways
|
||||
p.removeFile(this, /*detachFromProjects*/ false);
|
||||
}
|
||||
this.containingProjects.length = 0;
|
||||
clear(this.containingProjects);
|
||||
}
|
||||
|
||||
getDefaultProject() {
|
||||
@@ -334,7 +319,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
getLineInfo(line: number) {
|
||||
getLineInfo(line: number): AbsolutePositionAndLineText {
|
||||
return this.textStorage.getLineInfo(line);
|
||||
}
|
||||
|
||||
@@ -364,11 +349,7 @@ namespace ts.server {
|
||||
return this.textStorage.lineOffsetToPosition(line, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line 1-based index
|
||||
* @param offset 1-based index
|
||||
*/
|
||||
positionToLineOffset(position: number): ILineInfo {
|
||||
positionToLineOffset(position: number): protocol.Location {
|
||||
return this.textStorage.positionToLineOffset(position);
|
||||
}
|
||||
|
||||
|
||||
+168
-249
@@ -8,15 +8,13 @@ namespace ts.server {
|
||||
export interface LineCollection {
|
||||
charCount(): number;
|
||||
lineCount(): number;
|
||||
isLeaf(): boolean;
|
||||
isLeaf(): this is LineLeaf;
|
||||
walk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker): void;
|
||||
}
|
||||
|
||||
export interface ILineInfo {
|
||||
line: number;
|
||||
offset: number;
|
||||
text?: string;
|
||||
leaf?: LineLeaf;
|
||||
export interface AbsolutePositionAndLineText {
|
||||
absolutePosition: number;
|
||||
lineText: string | undefined;
|
||||
}
|
||||
|
||||
export enum CharRangeSection {
|
||||
@@ -33,41 +31,35 @@ namespace ts.server {
|
||||
done: boolean;
|
||||
leaf(relativeStart: number, relativeLength: number, lineCollection: LineLeaf): void;
|
||||
pre?(relativeStart: number, relativeLength: number, lineCollection: LineCollection,
|
||||
parent: LineNode, nodeType: CharRangeSection): LineCollection;
|
||||
parent: LineNode, nodeType: CharRangeSection): void;
|
||||
post?(relativeStart: number, relativeLength: number, lineCollection: LineCollection,
|
||||
parent: LineNode, nodeType: CharRangeSection): LineCollection;
|
||||
parent: LineNode, nodeType: CharRangeSection): void;
|
||||
}
|
||||
|
||||
class BaseLineIndexWalker implements ILineIndexWalker {
|
||||
class EditWalker implements ILineIndexWalker {
|
||||
goSubtree = true;
|
||||
done = false;
|
||||
leaf(_rangeStart: number, _rangeLength: number, _ll: LineLeaf) {
|
||||
}
|
||||
}
|
||||
get done() { return false; }
|
||||
|
||||
class EditWalker extends BaseLineIndexWalker {
|
||||
lineIndex = new LineIndex();
|
||||
readonly lineIndex = new LineIndex();
|
||||
// path to start of range
|
||||
startPath: LineCollection[];
|
||||
endBranch: LineCollection[] = [];
|
||||
branchNode: LineNode;
|
||||
private readonly startPath: LineCollection[];
|
||||
private readonly endBranch: LineCollection[] = [];
|
||||
private branchNode: LineNode;
|
||||
// path to current node
|
||||
stack: LineNode[];
|
||||
state = CharRangeSection.Entire;
|
||||
lineCollectionAtBranch: LineCollection;
|
||||
initialText = "";
|
||||
trailingText = "";
|
||||
suppressTrailingText = false;
|
||||
private readonly stack: LineNode[];
|
||||
private state = CharRangeSection.Entire;
|
||||
private lineCollectionAtBranch: LineCollection;
|
||||
private initialText = "";
|
||||
private trailingText = "";
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.lineIndex.root = new LineNode();
|
||||
this.startPath = [this.lineIndex.root];
|
||||
this.stack = [this.lineIndex.root];
|
||||
}
|
||||
|
||||
insertLines(insertedText: string) {
|
||||
if (this.suppressTrailingText) {
|
||||
insertLines(insertedText: string, suppressTrailingText: boolean) {
|
||||
if (suppressTrailingText) {
|
||||
this.trailingText = "";
|
||||
}
|
||||
if (insertedText) {
|
||||
@@ -80,7 +72,7 @@ namespace ts.server {
|
||||
const lines = lm.lines;
|
||||
if (lines.length > 1) {
|
||||
if (lines[lines.length - 1] === "") {
|
||||
lines.length--;
|
||||
lines.pop();
|
||||
}
|
||||
}
|
||||
let branchParent: LineNode;
|
||||
@@ -103,22 +95,20 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
// path at least length two (root and leaf)
|
||||
let insertionNode = <LineNode>this.startPath[this.startPath.length - 2];
|
||||
const leafNode = <LineLeaf>this.startPath[this.startPath.length - 1];
|
||||
const len = lines.length;
|
||||
|
||||
if (len > 0) {
|
||||
if (lines.length > 0) {
|
||||
leafNode.text = lines[0];
|
||||
|
||||
if (len > 1) {
|
||||
let insertedNodes = <LineCollection[]>new Array(len - 1);
|
||||
if (lines.length > 1) {
|
||||
let insertedNodes = <LineCollection[]>new Array(lines.length - 1);
|
||||
let startNode = <LineCollection>leafNode;
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
insertedNodes[i - 1] = new LineLeaf(lines[i]);
|
||||
}
|
||||
let pathIndex = this.startPath.length - 2;
|
||||
while (pathIndex >= 0) {
|
||||
insertionNode = <LineNode>this.startPath[pathIndex];
|
||||
const insertionNode = <LineNode>this.startPath[pathIndex];
|
||||
insertedNodes = insertionNode.insertAt(startNode, insertedNodes);
|
||||
pathIndex--;
|
||||
startNode = insertionNode;
|
||||
@@ -140,6 +130,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
else {
|
||||
const insertionNode = <LineNode>this.startPath[this.startPath.length - 2];
|
||||
// no content for leaf node, so delete it
|
||||
insertionNode.remove(leafNode);
|
||||
for (let j = this.startPath.length - 2; j >= 0; j--) {
|
||||
@@ -150,18 +141,17 @@ namespace ts.server {
|
||||
return this.lineIndex;
|
||||
}
|
||||
|
||||
post(_relativeStart: number, _relativeLength: number, lineCollection: LineCollection): LineCollection {
|
||||
post(_relativeStart: number, _relativeLength: number, lineCollection: LineCollection): void {
|
||||
// have visited the path for start of range, now looking for end
|
||||
// if range is on single line, we will never make this state transition
|
||||
if (lineCollection === this.lineCollectionAtBranch) {
|
||||
this.state = CharRangeSection.End;
|
||||
}
|
||||
// always pop stack because post only called when child has been visited
|
||||
this.stack.length--;
|
||||
return undefined;
|
||||
this.stack.pop();
|
||||
}
|
||||
|
||||
pre(_relativeStart: number, _relativeLength: number, lineCollection: LineCollection, _parent: LineCollection, nodeType: CharRangeSection) {
|
||||
pre(_relativeStart: number, _relativeLength: number, lineCollection: LineCollection, _parent: LineCollection, nodeType: CharRangeSection): void {
|
||||
// currentNode corresponds to parent, but in the new tree
|
||||
const currentNode = this.stack[this.stack.length - 1];
|
||||
|
||||
@@ -193,20 +183,20 @@ namespace ts.server {
|
||||
else {
|
||||
child = fresh(lineCollection);
|
||||
currentNode.add(child);
|
||||
this.startPath[this.startPath.length] = child;
|
||||
this.startPath.push(child);
|
||||
}
|
||||
break;
|
||||
case CharRangeSection.Entire:
|
||||
if (this.state !== CharRangeSection.End) {
|
||||
child = fresh(lineCollection);
|
||||
currentNode.add(child);
|
||||
this.startPath[this.startPath.length] = child;
|
||||
this.startPath.push(child);
|
||||
}
|
||||
else {
|
||||
if (!lineCollection.isLeaf()) {
|
||||
child = fresh(lineCollection);
|
||||
currentNode.add(child);
|
||||
this.endBranch[this.endBranch.length] = child;
|
||||
this.endBranch.push(child);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -221,7 +211,7 @@ namespace ts.server {
|
||||
if (!lineCollection.isLeaf()) {
|
||||
child = fresh(lineCollection);
|
||||
currentNode.add(child);
|
||||
this.endBranch[this.endBranch.length] = child;
|
||||
this.endBranch.push(child);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -233,9 +223,8 @@ namespace ts.server {
|
||||
break;
|
||||
}
|
||||
if (this.goSubtree) {
|
||||
this.stack[this.stack.length] = <LineNode>child;
|
||||
this.stack.push(<LineNode>child);
|
||||
}
|
||||
return lineCollection;
|
||||
}
|
||||
// just gather text from the leaves
|
||||
leaf(relativeStart: number, relativeLength: number, ll: LineLeaf) {
|
||||
@@ -259,22 +248,21 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getTextChangeRange() {
|
||||
return ts.createTextChangeRange(ts.createTextSpan(this.pos, this.deleteLen),
|
||||
return createTextChangeRange(createTextSpan(this.pos, this.deleteLen),
|
||||
this.insertedText ? this.insertedText.length : 0);
|
||||
}
|
||||
}
|
||||
|
||||
export class ScriptVersionCache {
|
||||
changes: TextChange[] = [];
|
||||
versions: LineIndexSnapshot[] = new Array<LineIndexSnapshot>(ScriptVersionCache.maxVersions);
|
||||
minVersion = 0; // no versions earlier than min version will maintain change history
|
||||
private changes: TextChange[] = [];
|
||||
private readonly versions: LineIndexSnapshot[] = new Array<LineIndexSnapshot>(ScriptVersionCache.maxVersions);
|
||||
private minVersion = 0; // no versions earlier than min version will maintain change history
|
||||
|
||||
private host: ServerHost;
|
||||
private currentVersion = 0;
|
||||
|
||||
static changeNumberThreshold = 8;
|
||||
static changeLengthThreshold = 256;
|
||||
static maxVersions = 8;
|
||||
private static readonly changeNumberThreshold = 8;
|
||||
private static readonly changeLengthThreshold = 256;
|
||||
private static readonly maxVersions = 8;
|
||||
|
||||
private versionToIndex(version: number) {
|
||||
if (version < this.minVersion || version > this.currentVersion) {
|
||||
@@ -289,10 +277,10 @@ namespace ts.server {
|
||||
|
||||
// REVIEW: can optimize by coalescing simple edits
|
||||
edit(pos: number, deleteLen: number, insertedText?: string) {
|
||||
this.changes[this.changes.length] = new TextChange(pos, deleteLen, insertedText);
|
||||
if ((this.changes.length > ScriptVersionCache.changeNumberThreshold) ||
|
||||
(deleteLen > ScriptVersionCache.changeLengthThreshold) ||
|
||||
(insertedText && (insertedText.length > ScriptVersionCache.changeLengthThreshold))) {
|
||||
this.changes.push(new TextChange(pos, deleteLen, insertedText));
|
||||
if (this.changes.length > ScriptVersionCache.changeNumberThreshold ||
|
||||
deleteLen > ScriptVersionCache.changeLengthThreshold ||
|
||||
insertedText && insertedText.length > ScriptVersionCache.changeLengthThreshold) {
|
||||
this.getSnapshot();
|
||||
}
|
||||
}
|
||||
@@ -308,21 +296,11 @@ namespace ts.server {
|
||||
return this.currentVersion;
|
||||
}
|
||||
|
||||
reloadFromFile(filename: string) {
|
||||
let content = this.host.readFile(filename);
|
||||
// If the file doesn't exist or cannot be read, we should
|
||||
// wipe out its cached content on the server to avoid side effects.
|
||||
if (!content) {
|
||||
content = "";
|
||||
}
|
||||
this.reload(content);
|
||||
}
|
||||
|
||||
// reload whole script, leaving no change history behind reload
|
||||
reload(script: string) {
|
||||
this.currentVersion++;
|
||||
this.changes = []; // history wiped out by reload
|
||||
const snap = new LineIndexSnapshot(this.currentVersion, this);
|
||||
const snap = new LineIndexSnapshot(this.currentVersion, this, new LineIndex());
|
||||
|
||||
// delete all versions
|
||||
for (let i = 0; i < this.versions.length; i++) {
|
||||
@@ -330,7 +308,6 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
this.versions[this.currentVersionToIndex()] = snap;
|
||||
snap.index = new LineIndex();
|
||||
const lm = LineIndex.linesFromText(script);
|
||||
snap.index.load(lm.lines);
|
||||
|
||||
@@ -344,9 +321,7 @@ namespace ts.server {
|
||||
for (const change of this.changes) {
|
||||
snapIndex = snapIndex.edit(change.pos, change.deleteLen, change.insertedText);
|
||||
}
|
||||
snap = new LineIndexSnapshot(this.currentVersion + 1, this);
|
||||
snap.index = snapIndex;
|
||||
snap.changesSincePreviousVersion = this.changes;
|
||||
snap = new LineIndexSnapshot(this.currentVersion + 1, this, snapIndex, this.changes);
|
||||
|
||||
this.currentVersion = snap.version;
|
||||
this.versions[this.currentVersionToIndex()] = snap;
|
||||
@@ -362,41 +337,36 @@ namespace ts.server {
|
||||
getTextChangesBetweenVersions(oldVersion: number, newVersion: number) {
|
||||
if (oldVersion < newVersion) {
|
||||
if (oldVersion >= this.minVersion) {
|
||||
const textChangeRanges: ts.TextChangeRange[] = [];
|
||||
const textChangeRanges: TextChangeRange[] = [];
|
||||
for (let i = oldVersion + 1; i <= newVersion; i++) {
|
||||
const snap = this.versions[this.versionToIndex(i)];
|
||||
for (const textChange of snap.changesSincePreviousVersion) {
|
||||
textChangeRanges[textChangeRanges.length] = textChange.getTextChangeRange();
|
||||
textChangeRanges.push(textChange.getTextChangeRange());
|
||||
}
|
||||
}
|
||||
return ts.collapseTextChangeRangesAcrossMultipleVersions(textChangeRanges);
|
||||
return collapseTextChangeRangesAcrossMultipleVersions(textChangeRanges);
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ts.unchangedTextChangeRange;
|
||||
return unchangedTextChangeRange;
|
||||
}
|
||||
}
|
||||
|
||||
static fromString(host: ServerHost, script: string) {
|
||||
static fromString(script: string) {
|
||||
const svc = new ScriptVersionCache();
|
||||
const snap = new LineIndexSnapshot(0, svc);
|
||||
const snap = new LineIndexSnapshot(0, svc, new LineIndex());
|
||||
svc.versions[svc.currentVersion] = snap;
|
||||
svc.host = host;
|
||||
snap.index = new LineIndex();
|
||||
const lm = LineIndex.linesFromText(script);
|
||||
snap.index.load(lm.lines);
|
||||
return svc;
|
||||
}
|
||||
}
|
||||
|
||||
export class LineIndexSnapshot implements ts.IScriptSnapshot {
|
||||
index: LineIndex;
|
||||
changesSincePreviousVersion: TextChange[] = [];
|
||||
|
||||
constructor(readonly version: number, readonly cache: ScriptVersionCache) {
|
||||
export class LineIndexSnapshot implements IScriptSnapshot {
|
||||
constructor(readonly version: number, readonly cache: ScriptVersionCache, readonly index: LineIndex, readonly changesSincePreviousVersion: ReadonlyArray<TextChange> = emptyArray) {
|
||||
}
|
||||
|
||||
getText(rangeStart: number, rangeEnd: number) {
|
||||
@@ -407,37 +377,14 @@ namespace ts.server {
|
||||
return this.index.root.charCount();
|
||||
}
|
||||
|
||||
// this requires linear space so don't hold on to these
|
||||
getLineStartPositions(): number[] {
|
||||
const starts: number[] = [-1];
|
||||
let count = 1;
|
||||
let pos = 0;
|
||||
this.index.every(ll => {
|
||||
starts[count] = pos;
|
||||
count++;
|
||||
pos += ll.text.length;
|
||||
return true;
|
||||
}, 0);
|
||||
return starts;
|
||||
}
|
||||
|
||||
getLineMapper() {
|
||||
return (line: number) => {
|
||||
return this.index.lineNumberToInfo(line).offset;
|
||||
};
|
||||
}
|
||||
|
||||
getTextChangeRangeSinceVersion(scriptVersion: number) {
|
||||
if (this.version <= scriptVersion) {
|
||||
return ts.unchangedTextChangeRange;
|
||||
}
|
||||
else {
|
||||
return this.cache.getTextChangesBetweenVersions(scriptVersion, this.version);
|
||||
}
|
||||
}
|
||||
getChangeRange(oldSnapshot: ts.IScriptSnapshot): ts.TextChangeRange {
|
||||
getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange {
|
||||
if (oldSnapshot instanceof LineIndexSnapshot && this.cache === oldSnapshot.cache) {
|
||||
return this.getTextChangeRangeSinceVersion(oldSnapshot.version);
|
||||
if (this.version <= oldSnapshot.version) {
|
||||
return unchangedTextChangeRange;
|
||||
}
|
||||
else {
|
||||
return this.cache.getTextChangesBetweenVersions(oldSnapshot.version, this.version);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -447,22 +394,27 @@ namespace ts.server {
|
||||
// set this to true to check each edit for accuracy
|
||||
checkEdits = false;
|
||||
|
||||
charOffsetToLineNumberAndPos(charOffset: number) {
|
||||
return this.root.charOffsetToLineNumberAndPos(1, charOffset);
|
||||
absolutePositionOfStartOfLine(oneBasedLine: number): number {
|
||||
return this.lineNumberToInfo(oneBasedLine).absolutePosition;
|
||||
}
|
||||
|
||||
lineNumberToInfo(lineNumber: number): ILineInfo {
|
||||
positionToLineOffset(position: number): protocol.Location {
|
||||
const { oneBasedLine, zeroBasedColumn } = this.root.charOffsetToLineInfo(1, position);
|
||||
return { line: oneBasedLine, offset: zeroBasedColumn + 1 };
|
||||
}
|
||||
|
||||
private positionToColumnAndLineText(position: number): { zeroBasedColumn: number, lineText: string } {
|
||||
return this.root.charOffsetToLineInfo(1, position);
|
||||
}
|
||||
|
||||
lineNumberToInfo(oneBasedLine: number): AbsolutePositionAndLineText {
|
||||
const lineCount = this.root.lineCount();
|
||||
if (lineNumber <= lineCount) {
|
||||
const lineInfo = this.root.lineNumberToInfo(lineNumber, 0);
|
||||
lineInfo.line = lineNumber;
|
||||
return lineInfo;
|
||||
if (oneBasedLine <= lineCount) {
|
||||
const { position, leaf } = this.root.lineNumberToInfo(oneBasedLine, 0);
|
||||
return { absolutePosition: position, lineText: leaf && leaf.text };
|
||||
}
|
||||
else {
|
||||
return {
|
||||
line: lineNumber,
|
||||
offset: this.root.charCount()
|
||||
};
|
||||
return { absolutePosition: this.root.charCount(), lineText: undefined };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,7 +460,7 @@ namespace ts.server {
|
||||
const walkFns = {
|
||||
goSubtree: true,
|
||||
done: false,
|
||||
leaf: function (this: ILineIndexWalker, relativeStart: number, relativeLength: number, ll: LineLeaf) {
|
||||
leaf(this: ILineIndexWalker, relativeStart: number, relativeLength: number, ll: LineLeaf) {
|
||||
if (!f(ll, relativeStart, relativeLength)) {
|
||||
this.done = true;
|
||||
}
|
||||
@@ -518,12 +470,9 @@ namespace ts.server {
|
||||
return !walkFns.done;
|
||||
}
|
||||
|
||||
edit(pos: number, deleteLength: number, newText?: string) {
|
||||
function editFlat(source: string, s: number, dl: number, nt = "") {
|
||||
return source.substring(0, s) + nt + source.substring(s + dl, source.length);
|
||||
}
|
||||
edit(pos: number, deleteLength: number, newText?: string): LineIndex {
|
||||
if (this.root.charCount() === 0) {
|
||||
// TODO: assert deleteLength === 0
|
||||
Debug.assert(deleteLength === 0); // Can't delete from empty document
|
||||
if (newText !== undefined) {
|
||||
this.load(LineIndex.linesFromText(newText).lines);
|
||||
return this;
|
||||
@@ -532,9 +481,11 @@ namespace ts.server {
|
||||
else {
|
||||
let checkText: string;
|
||||
if (this.checkEdits) {
|
||||
checkText = editFlat(this.getText(0, this.root.charCount()), pos, deleteLength, newText);
|
||||
const source = this.getText(0, this.root.charCount());
|
||||
checkText = source.slice(0, pos) + newText + source.slice(pos + deleteLength);
|
||||
}
|
||||
const walker = new EditWalker();
|
||||
let suppressTrailingText = false;
|
||||
if (pos >= this.root.charCount()) {
|
||||
// insert at end
|
||||
pos = this.root.charCount() - 1;
|
||||
@@ -546,93 +497,77 @@ namespace ts.server {
|
||||
newText = endString;
|
||||
}
|
||||
deleteLength = 0;
|
||||
walker.suppressTrailingText = true;
|
||||
suppressTrailingText = true;
|
||||
}
|
||||
else if (deleteLength > 0) {
|
||||
// check whether last characters deleted are line break
|
||||
const e = pos + deleteLength;
|
||||
const lineInfo = this.charOffsetToLineNumberAndPos(e);
|
||||
if ((lineInfo && (lineInfo.offset === 0))) {
|
||||
const { zeroBasedColumn, lineText } = this.positionToColumnAndLineText(e);
|
||||
if (zeroBasedColumn === 0) {
|
||||
// move range end just past line that will merge with previous line
|
||||
deleteLength += lineInfo.text.length;
|
||||
deleteLength += lineText.length;
|
||||
// store text by appending to end of insertedText
|
||||
if (newText) {
|
||||
newText = newText + lineInfo.text;
|
||||
}
|
||||
else {
|
||||
newText = lineInfo.text;
|
||||
}
|
||||
newText = newText ? newText + lineText : lineText;
|
||||
}
|
||||
}
|
||||
if (pos < this.root.charCount()) {
|
||||
this.root.walk(pos, deleteLength, walker);
|
||||
walker.insertLines(newText);
|
||||
}
|
||||
|
||||
this.root.walk(pos, deleteLength, walker);
|
||||
walker.insertLines(newText, suppressTrailingText);
|
||||
|
||||
if (this.checkEdits) {
|
||||
const updatedText = this.getText(0, this.root.charCount());
|
||||
const updatedText = walker.lineIndex.getText(0, walker.lineIndex.getLength());
|
||||
Debug.assert(checkText === updatedText, "buffer edit mismatch");
|
||||
}
|
||||
|
||||
return walker.lineIndex;
|
||||
}
|
||||
}
|
||||
|
||||
static buildTreeFromBottom(nodes: LineCollection[]): LineNode {
|
||||
const nodeCount = Math.ceil(nodes.length / lineCollectionCapacity);
|
||||
const interiorNodes: LineNode[] = [];
|
||||
private static buildTreeFromBottom(nodes: LineCollection[]): LineNode {
|
||||
if (nodes.length < lineCollectionCapacity) {
|
||||
return new LineNode(nodes);
|
||||
}
|
||||
|
||||
const interiorNodes = new Array<LineNode>(Math.ceil(nodes.length / lineCollectionCapacity));
|
||||
let nodeIndex = 0;
|
||||
for (let i = 0; i < nodeCount; i++) {
|
||||
interiorNodes[i] = new LineNode();
|
||||
let charCount = 0;
|
||||
let lineCount = 0;
|
||||
for (let j = 0; j < lineCollectionCapacity; j++) {
|
||||
if (nodeIndex < nodes.length) {
|
||||
interiorNodes[i].add(nodes[nodeIndex]);
|
||||
charCount += nodes[nodeIndex].charCount();
|
||||
lineCount += nodes[nodeIndex].lineCount();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
nodeIndex++;
|
||||
}
|
||||
interiorNodes[i].totalChars = charCount;
|
||||
interiorNodes[i].totalLines = lineCount;
|
||||
}
|
||||
if (interiorNodes.length === 1) {
|
||||
return interiorNodes[0];
|
||||
}
|
||||
else {
|
||||
return this.buildTreeFromBottom(interiorNodes);
|
||||
for (let i = 0; i < interiorNodes.length; i++) {
|
||||
const end = Math.min(nodeIndex + lineCollectionCapacity, nodes.length);
|
||||
interiorNodes[i] = new LineNode(nodes.slice(nodeIndex, end));
|
||||
nodeIndex = end;
|
||||
}
|
||||
return this.buildTreeFromBottom(interiorNodes);
|
||||
}
|
||||
|
||||
static linesFromText(text: string) {
|
||||
const lineStarts = ts.computeLineStarts(text);
|
||||
const lineMap = computeLineStarts(text);
|
||||
|
||||
if (lineStarts.length === 0) {
|
||||
return { lines: <string[]>[], lineMap: lineStarts };
|
||||
if (lineMap.length === 0) {
|
||||
return { lines: <string[]>[], lineMap };
|
||||
}
|
||||
const lines = <string[]>new Array(lineStarts.length);
|
||||
const lc = lineStarts.length - 1;
|
||||
const lines = <string[]>new Array(lineMap.length);
|
||||
const lc = lineMap.length - 1;
|
||||
for (let lmi = 0; lmi < lc; lmi++) {
|
||||
lines[lmi] = text.substring(lineStarts[lmi], lineStarts[lmi + 1]);
|
||||
lines[lmi] = text.substring(lineMap[lmi], lineMap[lmi + 1]);
|
||||
}
|
||||
|
||||
const endText = text.substring(lineStarts[lc]);
|
||||
const endText = text.substring(lineMap[lc]);
|
||||
if (endText.length > 0) {
|
||||
lines[lc] = endText;
|
||||
}
|
||||
else {
|
||||
lines.length--;
|
||||
lines.pop();
|
||||
}
|
||||
return { lines: lines, lineMap: lineStarts };
|
||||
return { lines, lineMap };
|
||||
}
|
||||
}
|
||||
|
||||
export class LineNode implements LineCollection {
|
||||
totalChars = 0;
|
||||
totalLines = 0;
|
||||
children: LineCollection[] = [];
|
||||
|
||||
constructor(private readonly children: LineCollection[] = []) {
|
||||
if (children.length) this.updateCounts();
|
||||
}
|
||||
|
||||
isLeaf() {
|
||||
return false;
|
||||
@@ -647,7 +582,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
execWalk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker, childIndex: number, nodeType: CharRangeSection) {
|
||||
private execWalk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker, childIndex: number, nodeType: CharRangeSection) {
|
||||
if (walkFns.pre) {
|
||||
walkFns.pre(rangeStart, rangeLength, this.children[childIndex], this, nodeType);
|
||||
}
|
||||
@@ -663,7 +598,7 @@ namespace ts.server {
|
||||
return walkFns.done;
|
||||
}
|
||||
|
||||
skipChild(relativeStart: number, relativeLength: number, childIndex: number, walkFns: ILineIndexWalker, nodeType: CharRangeSection) {
|
||||
private skipChild(relativeStart: number, relativeLength: number, childIndex: number, walkFns: ILineIndexWalker, nodeType: CharRangeSection) {
|
||||
if (walkFns.pre && (!walkFns.done)) {
|
||||
walkFns.pre(relativeStart, relativeLength, this.children[childIndex], this, nodeType);
|
||||
walkFns.goSubtree = true;
|
||||
@@ -673,16 +608,14 @@ namespace ts.server {
|
||||
walk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker) {
|
||||
// assume (rangeStart < this.totalChars) && (rangeLength <= this.totalChars)
|
||||
let childIndex = 0;
|
||||
let child = this.children[0];
|
||||
let childCharCount = child.charCount();
|
||||
let childCharCount = this.children[childIndex].charCount();
|
||||
// find sub-tree containing start
|
||||
let adjustedStart = rangeStart;
|
||||
while (adjustedStart >= childCharCount) {
|
||||
this.skipChild(adjustedStart, rangeLength, childIndex, walkFns, CharRangeSection.PreStart);
|
||||
adjustedStart -= childCharCount;
|
||||
childIndex++;
|
||||
child = this.children[childIndex];
|
||||
childCharCount = child.charCount();
|
||||
childCharCount = this.children[childIndex].charCount();
|
||||
}
|
||||
// Case I: both start and end of range in same subtree
|
||||
if ((adjustedStart + rangeLength) <= childCharCount) {
|
||||
@@ -697,7 +630,7 @@ namespace ts.server {
|
||||
}
|
||||
let adjustedLength = rangeLength - (childCharCount - adjustedStart);
|
||||
childIndex++;
|
||||
child = this.children[childIndex];
|
||||
const child = this.children[childIndex];
|
||||
childCharCount = child.charCount();
|
||||
while (adjustedLength > childCharCount) {
|
||||
if (this.execWalk(0, childCharCount, walkFns, childIndex, CharRangeSection.Mid)) {
|
||||
@@ -705,8 +638,7 @@ namespace ts.server {
|
||||
}
|
||||
adjustedLength -= childCharCount;
|
||||
childIndex++;
|
||||
child = this.children[childIndex];
|
||||
childCharCount = child.charCount();
|
||||
childCharCount = this.children[childIndex].charCount();
|
||||
}
|
||||
if (adjustedLength > 0) {
|
||||
if (this.execWalk(0, adjustedLength, walkFns, childIndex, CharRangeSection.End)) {
|
||||
@@ -725,103 +657,91 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
charOffsetToLineNumberAndPos(lineNumber: number, charOffset: number): ILineInfo {
|
||||
const childInfo = this.childFromCharOffset(lineNumber, charOffset);
|
||||
// Input position is relative to the start of this node.
|
||||
// Output line number is absolute.
|
||||
charOffsetToLineInfo(lineNumberAccumulator: number, relativePosition: number): { oneBasedLine: number, zeroBasedColumn: number, lineText: string | undefined } {
|
||||
const childInfo = this.childFromCharOffset(lineNumberAccumulator, relativePosition);
|
||||
if (!childInfo.child) {
|
||||
return {
|
||||
line: lineNumber,
|
||||
offset: charOffset,
|
||||
oneBasedLine: lineNumberAccumulator,
|
||||
zeroBasedColumn: relativePosition,
|
||||
lineText: undefined,
|
||||
};
|
||||
}
|
||||
else if (childInfo.childIndex < this.children.length) {
|
||||
if (childInfo.child.isLeaf()) {
|
||||
return {
|
||||
line: childInfo.lineNumber,
|
||||
offset: childInfo.charOffset,
|
||||
text: (<LineLeaf>(childInfo.child)).text,
|
||||
leaf: (<LineLeaf>(childInfo.child))
|
||||
oneBasedLine: childInfo.lineNumberAccumulator,
|
||||
zeroBasedColumn: childInfo.relativePosition,
|
||||
lineText: childInfo.child.text,
|
||||
};
|
||||
}
|
||||
else {
|
||||
const lineNode = <LineNode>(childInfo.child);
|
||||
return lineNode.charOffsetToLineNumberAndPos(childInfo.lineNumber, childInfo.charOffset);
|
||||
return lineNode.charOffsetToLineInfo(childInfo.lineNumberAccumulator, childInfo.relativePosition);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const lineInfo = this.lineNumberToInfo(this.lineCount(), 0);
|
||||
return { line: this.lineCount(), offset: lineInfo.leaf.charCount() };
|
||||
return { oneBasedLine: this.lineCount(), zeroBasedColumn: lineInfo.leaf.charCount(), lineText: undefined };
|
||||
}
|
||||
}
|
||||
|
||||
lineNumberToInfo(lineNumber: number, charOffset: number): ILineInfo {
|
||||
const childInfo = this.childFromLineNumber(lineNumber, charOffset);
|
||||
lineNumberToInfo(relativeOneBasedLine: number, positionAccumulator: number): { position: number, leaf: LineLeaf | undefined } {
|
||||
const childInfo = this.childFromLineNumber(relativeOneBasedLine, positionAccumulator);
|
||||
if (!childInfo.child) {
|
||||
return {
|
||||
line: lineNumber,
|
||||
offset: charOffset
|
||||
};
|
||||
return { position: positionAccumulator, leaf: undefined };
|
||||
}
|
||||
else if (childInfo.child.isLeaf()) {
|
||||
return {
|
||||
line: lineNumber,
|
||||
offset: childInfo.charOffset,
|
||||
text: (<LineLeaf>(childInfo.child)).text,
|
||||
leaf: (<LineLeaf>(childInfo.child))
|
||||
};
|
||||
return { position: childInfo.positionAccumulator, leaf: childInfo.child };
|
||||
}
|
||||
else {
|
||||
const lineNode = <LineNode>(childInfo.child);
|
||||
return lineNode.lineNumberToInfo(childInfo.relativeLineNumber, childInfo.charOffset);
|
||||
return lineNode.lineNumberToInfo(childInfo.relativeOneBasedLine, childInfo.positionAccumulator);
|
||||
}
|
||||
}
|
||||
|
||||
childFromLineNumber(lineNumber: number, charOffset: number) {
|
||||
/**
|
||||
* Input line number is relative to the start of this node.
|
||||
* Output line number is relative to the child.
|
||||
* positionAccumulator will be an absolute position once relativeLineNumber reaches 0.
|
||||
*/
|
||||
private childFromLineNumber(relativeOneBasedLine: number, positionAccumulator: number): { child: LineCollection, relativeOneBasedLine: number, positionAccumulator: number } {
|
||||
let child: LineCollection;
|
||||
let relativeLineNumber = lineNumber;
|
||||
let i: number;
|
||||
let len: number;
|
||||
for (i = 0, len = this.children.length; i < len; i++) {
|
||||
for (i = 0; i < this.children.length; i++) {
|
||||
child = this.children[i];
|
||||
const childLineCount = child.lineCount();
|
||||
if (childLineCount >= relativeLineNumber) {
|
||||
if (childLineCount >= relativeOneBasedLine) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
relativeLineNumber -= childLineCount;
|
||||
charOffset += child.charCount();
|
||||
relativeOneBasedLine -= childLineCount;
|
||||
positionAccumulator += child.charCount();
|
||||
}
|
||||
}
|
||||
return {
|
||||
child: child,
|
||||
childIndex: i,
|
||||
relativeLineNumber: relativeLineNumber,
|
||||
charOffset: charOffset
|
||||
};
|
||||
return { child, relativeOneBasedLine, positionAccumulator };
|
||||
}
|
||||
|
||||
childFromCharOffset(lineNumber: number, charOffset: number) {
|
||||
private childFromCharOffset(lineNumberAccumulator: number, relativePosition: number
|
||||
): { child: LineCollection, childIndex: number, relativePosition: number, lineNumberAccumulator: number } {
|
||||
let child: LineCollection;
|
||||
let i: number;
|
||||
let len: number;
|
||||
for (i = 0, len = this.children.length; i < len; i++) {
|
||||
child = this.children[i];
|
||||
if (child.charCount() > charOffset) {
|
||||
if (child.charCount() > relativePosition) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
charOffset -= child.charCount();
|
||||
lineNumber += child.lineCount();
|
||||
relativePosition -= child.charCount();
|
||||
lineNumberAccumulator += child.lineCount();
|
||||
}
|
||||
}
|
||||
return {
|
||||
child: child,
|
||||
childIndex: i,
|
||||
charOffset: charOffset,
|
||||
lineNumber: lineNumber
|
||||
};
|
||||
return { child, childIndex: i, relativePosition, lineNumberAccumulator };
|
||||
}
|
||||
|
||||
splitAfter(childIndex: number) {
|
||||
private splitAfter(childIndex: number) {
|
||||
let splitNode: LineNode;
|
||||
const clen = this.children.length;
|
||||
childIndex++;
|
||||
@@ -846,13 +766,12 @@ namespace ts.server {
|
||||
this.children[i] = this.children[i + 1];
|
||||
}
|
||||
}
|
||||
this.children.length--;
|
||||
this.children.pop();
|
||||
}
|
||||
|
||||
findChildIndex(child: LineCollection) {
|
||||
let childIndex = 0;
|
||||
const clen = this.children.length;
|
||||
while ((this.children[childIndex] !== child) && (childIndex < clen)) childIndex++;
|
||||
private findChildIndex(child: LineCollection) {
|
||||
const childIndex = this.children.indexOf(child);
|
||||
Debug.assert(childIndex !== -1);
|
||||
return childIndex;
|
||||
}
|
||||
|
||||
@@ -895,12 +814,12 @@ namespace ts.server {
|
||||
}
|
||||
for (let i = splitNodes.length - 1; i >= 0; i--) {
|
||||
if (splitNodes[i].children.length === 0) {
|
||||
splitNodes.length--;
|
||||
splitNodes.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shiftNode) {
|
||||
splitNodes[splitNodes.length] = shiftNode;
|
||||
splitNodes.push(shiftNode);
|
||||
}
|
||||
this.updateCounts();
|
||||
for (let i = 0; i < splitNodeCount; i++) {
|
||||
@@ -911,9 +830,9 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
// assume there is room for the item; return true if more room
|
||||
add(collection: LineCollection) {
|
||||
this.children[this.children.length] = collection;
|
||||
return (this.children.length < lineCollectionCapacity);
|
||||
add(collection: LineCollection): void {
|
||||
this.children.push(collection);
|
||||
Debug.assert(this.children.length <= lineCollectionCapacity);
|
||||
}
|
||||
|
||||
charCount() {
|
||||
|
||||
+29
-17
@@ -43,7 +43,7 @@ namespace ts.server {
|
||||
process.env.USERPROFILE ||
|
||||
(process.env.HOMEDRIVE && process.env.HOMEPATH && normalizeSlashes(process.env.HOMEDRIVE + process.env.HOMEPATH)) ||
|
||||
os.tmpdir();
|
||||
return combinePaths(normalizeSlashes(basePath), "Microsoft/TypeScript");
|
||||
return combinePaths(combinePaths(normalizeSlashes(basePath), "Microsoft/TypeScript"), versionMajorMinor);
|
||||
}
|
||||
case "openbsd":
|
||||
case "freebsd":
|
||||
@@ -51,7 +51,7 @@ namespace ts.server {
|
||||
case "linux":
|
||||
case "android": {
|
||||
const cacheLocation = getNonWindowsCacheLocation(process.platform === "darwin");
|
||||
return combinePaths(cacheLocation, "typescript");
|
||||
return combinePaths(combinePaths(cacheLocation, "typescript"), versionMajorMinor);
|
||||
}
|
||||
default:
|
||||
Debug.fail(`unsupported platform '${process.platform}'`);
|
||||
@@ -138,7 +138,7 @@ namespace ts.server {
|
||||
terminal: false,
|
||||
});
|
||||
|
||||
class Logger implements ts.server.Logger {
|
||||
class Logger implements server.Logger {
|
||||
private fd = -1;
|
||||
private seq = 0;
|
||||
private inGroup = false;
|
||||
@@ -200,7 +200,7 @@ namespace ts.server {
|
||||
|
||||
msg(s: string, type: Msg.Types = Msg.Err) {
|
||||
if (this.fd >= 0 || this.traceToConsole) {
|
||||
s = s + "\n";
|
||||
s = `[${nowString()}] ${s}\n`;
|
||||
const prefix = Logger.padStringRight(type + " " + this.seq.toString(), " ");
|
||||
if (this.firstInGroup) {
|
||||
s = prefix + s;
|
||||
@@ -222,6 +222,12 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
// E.g. "12:34:56.789"
|
||||
function nowString() {
|
||||
const d = new Date();
|
||||
return `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}.${d.getMilliseconds()}`;
|
||||
}
|
||||
|
||||
class NodeTypingsInstaller implements ITypingsInstaller {
|
||||
private installer: NodeChildProcess;
|
||||
private installerPidReported = false;
|
||||
@@ -510,6 +516,7 @@ namespace ts.server {
|
||||
const watchedFiles: WatchedFile[] = [];
|
||||
let nextFileToCheck = 0;
|
||||
let watchTimer: any;
|
||||
return { getModifiedTime, poll, startWatchTimer, addFile, removeFile };
|
||||
|
||||
function getModifiedTime(fileName: string): Date {
|
||||
return fs.statSync(fileName).mtime;
|
||||
@@ -523,11 +530,20 @@ namespace ts.server {
|
||||
|
||||
fs.stat(watchedFile.fileName, (err: any, stats: any) => {
|
||||
if (err) {
|
||||
watchedFile.callback(watchedFile.fileName);
|
||||
watchedFile.callback(watchedFile.fileName, FileWatcherEventKind.Changed);
|
||||
}
|
||||
else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) {
|
||||
watchedFile.mtime = getModifiedTime(watchedFile.fileName);
|
||||
watchedFile.callback(watchedFile.fileName, watchedFile.mtime.getTime() === 0);
|
||||
else {
|
||||
const oldTime = watchedFile.mtime.getTime();
|
||||
const newTime = stats.mtime.getTime();
|
||||
if (oldTime !== newTime) {
|
||||
watchedFile.mtime = stats.mtime;
|
||||
const eventKind = oldTime === 0
|
||||
? FileWatcherEventKind.Created
|
||||
: newTime === 0
|
||||
? FileWatcherEventKind.Deleted
|
||||
: FileWatcherEventKind.Changed;
|
||||
watchedFile.callback(watchedFile.fileName, eventKind);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -559,7 +575,9 @@ namespace ts.server {
|
||||
const file: WatchedFile = {
|
||||
fileName,
|
||||
callback,
|
||||
mtime: getModifiedTime(fileName)
|
||||
mtime: sys.fileExists(fileName)
|
||||
? getModifiedTime(fileName)
|
||||
: new Date(0) // Any subsequent modification will occur after this time
|
||||
};
|
||||
|
||||
watchedFiles.push(file);
|
||||
@@ -572,14 +590,6 @@ namespace ts.server {
|
||||
function removeFile(file: WatchedFile) {
|
||||
unorderedRemoveItem(watchedFiles, file);
|
||||
}
|
||||
|
||||
return {
|
||||
getModifiedTime: getModifiedTime,
|
||||
poll: poll,
|
||||
startWatchTimer: startWatchTimer,
|
||||
addFile: addFile,
|
||||
removeFile: removeFile
|
||||
};
|
||||
}
|
||||
|
||||
// REVIEW: for now this implementation uses polling.
|
||||
@@ -747,6 +757,8 @@ namespace ts.server {
|
||||
validateLocaleAndSetLanguage(localeStr, sys);
|
||||
}
|
||||
|
||||
setStackTraceLimit();
|
||||
|
||||
const typingSafeListLocation = findArgument(Arguments.TypingSafeListLocation);
|
||||
const npmLocation = findArgument(Arguments.NpmLocation);
|
||||
|
||||
|
||||
+201
-128
@@ -49,7 +49,7 @@ namespace ts.server {
|
||||
|
||||
interface FileStart {
|
||||
file: string;
|
||||
start: ILineInfo;
|
||||
start: protocol.Location;
|
||||
}
|
||||
|
||||
function compareNumber(a: number, b: number) {
|
||||
@@ -72,26 +72,32 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
function formatDiag(fileName: NormalizedPath, project: Project, diag: ts.Diagnostic): protocol.Diagnostic {
|
||||
function formatDiag(fileName: NormalizedPath, project: Project, diag: Diagnostic): protocol.Diagnostic {
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(fileName);
|
||||
return {
|
||||
start: scriptInfo.positionToLineOffset(diag.start),
|
||||
end: scriptInfo.positionToLineOffset(diag.start + diag.length),
|
||||
text: ts.flattenDiagnosticMessageText(diag.messageText, "\n"),
|
||||
text: flattenDiagnosticMessageText(diag.messageText, "\n"),
|
||||
code: diag.code,
|
||||
category: DiagnosticCategory[diag.category].toLowerCase(),
|
||||
source: diag.source
|
||||
};
|
||||
}
|
||||
|
||||
function formatConfigFileDiag(diag: ts.Diagnostic): protocol.Diagnostic {
|
||||
return {
|
||||
start: undefined,
|
||||
end: undefined,
|
||||
text: ts.flattenDiagnosticMessageText(diag.messageText, "\n"),
|
||||
category: DiagnosticCategory[diag.category].toLowerCase(),
|
||||
source: diag.source
|
||||
};
|
||||
function convertToLocation(lineAndCharacter: LineAndCharacter): protocol.Location {
|
||||
return { line: lineAndCharacter.line + 1, offset: lineAndCharacter.character + 1 };
|
||||
}
|
||||
|
||||
function formatConfigFileDiag(diag: Diagnostic, includeFileName: true): protocol.DiagnosticWithFileName;
|
||||
function formatConfigFileDiag(diag: Diagnostic, includeFileName: false): protocol.Diagnostic;
|
||||
function formatConfigFileDiag(diag: Diagnostic, includeFileName: boolean): protocol.Diagnostic | protocol.DiagnosticWithFileName {
|
||||
const start = diag.file && convertToLocation(getLineAndCharacterOfPosition(diag.file, diag.start));
|
||||
const end = diag.file && convertToLocation(getLineAndCharacterOfPosition(diag.file, diag.start + diag.length));
|
||||
const text = flattenDiagnosticMessageText(diag.messageText, "\n");
|
||||
const { code, source } = diag;
|
||||
const category = DiagnosticCategory[diag.category].toLowerCase();
|
||||
return includeFileName ? { start, end, text, code, category, source, fileName: diag.file && diag.file.fileName } :
|
||||
{ start, end, text, code, category, source };
|
||||
}
|
||||
|
||||
export interface PendingErrorCheck {
|
||||
@@ -112,7 +118,13 @@ namespace ts.server {
|
||||
return true;
|
||||
}
|
||||
|
||||
export import CommandNames = protocol.CommandTypes;
|
||||
// CommandNames used to be exposed before TS 2.4 as a namespace
|
||||
// In TS 2.4 we switched to an enum, keep this for backward compatibility
|
||||
// The var assignment ensures that even though CommandTypes are a const enum
|
||||
// we want to ensure the value is maintained in the out since the file is
|
||||
// built using --preseveConstEnum.
|
||||
export type CommandNames = protocol.CommandTypes;
|
||||
export const CommandNames = (<any>protocol).CommandTypes;
|
||||
|
||||
export function formatMessage<T extends protocol.Message>(msg: T, logger: server.Logger, byteLength: (s: string, encoding: string) => number, newLine: string): string {
|
||||
const verboseLogging = logger.hasLevel(LogLevel.verbose);
|
||||
@@ -150,19 +162,13 @@ namespace ts.server {
|
||||
* Represents operation that can schedule its next step to be executed later.
|
||||
* Scheduling is done via instance of NextStep. If on current step subsequent step was not scheduled - operation is assumed to be completed.
|
||||
*/
|
||||
class MultistepOperation {
|
||||
class MultistepOperation implements NextStep {
|
||||
private requestId: number;
|
||||
private timerHandle: any;
|
||||
private immediateId: any;
|
||||
private completed = true;
|
||||
private readonly next: NextStep;
|
||||
|
||||
constructor(private readonly operationHost: MultistepOperationHost) {
|
||||
this.next = {
|
||||
immediate: action => this.immediate(action),
|
||||
delay: (ms, action) => this.delay(ms, action)
|
||||
};
|
||||
}
|
||||
constructor(private readonly operationHost: MultistepOperationHost) {}
|
||||
|
||||
public startNew(action: (next: NextStep) => void) {
|
||||
this.complete();
|
||||
@@ -182,7 +188,7 @@ namespace ts.server {
|
||||
this.setImmediateId(undefined);
|
||||
}
|
||||
|
||||
private immediate(action: () => void) {
|
||||
public immediate(action: () => void) {
|
||||
const requestId = this.requestId;
|
||||
Debug.assert(requestId === this.operationHost.getCurrentRequestId(), "immediate: incorrect request id");
|
||||
this.setImmediateId(this.operationHost.getServerHost().setImmediate(() => {
|
||||
@@ -191,7 +197,7 @@ namespace ts.server {
|
||||
}));
|
||||
}
|
||||
|
||||
private delay(ms: number, action: () => void) {
|
||||
public delay(ms: number, action: () => void) {
|
||||
const requestId = this.requestId;
|
||||
Debug.assert(requestId === this.operationHost.getCurrentRequestId(), "delay: incorrect request id");
|
||||
this.setTimerHandle(this.operationHost.getServerHost().setTimeout(() => {
|
||||
@@ -207,7 +213,7 @@ namespace ts.server {
|
||||
stop = true;
|
||||
}
|
||||
else {
|
||||
action(this.next);
|
||||
action(this);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
@@ -377,8 +383,8 @@ namespace ts.server {
|
||||
this.host.write(formatMessage(msg, this.logger, this.byteLength, this.host.newLine));
|
||||
}
|
||||
|
||||
public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: ts.Diagnostic[]) {
|
||||
const bakedDiags = ts.map(diagnostics, formatConfigFileDiag);
|
||||
public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: Diagnostic[]) {
|
||||
const bakedDiags = map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true));
|
||||
const ev: protocol.ConfigFileDiagnosticEvent = {
|
||||
seq: 0,
|
||||
type: "event",
|
||||
@@ -427,7 +433,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
const bakedDiags = diags.map((diag) => formatDiag(file, project, diag));
|
||||
this.event<protocol.DiagnosticEventBody>({ file: file, diagnostics: bakedDiags }, "semanticDiag");
|
||||
this.event<protocol.DiagnosticEventBody>({ file, diagnostics: bakedDiags }, "semanticDiag");
|
||||
}
|
||||
catch (err) {
|
||||
this.logError(err, "semantic check");
|
||||
@@ -439,7 +445,7 @@ namespace ts.server {
|
||||
const diags = project.getLanguageService().getSyntacticDiagnostics(file);
|
||||
if (diags) {
|
||||
const bakedDiags = diags.map((diag) => formatDiag(file, project, diag));
|
||||
this.event<protocol.DiagnosticEventBody>({ file: file, diagnostics: bakedDiags }, "syntaxDiag");
|
||||
this.event<protocol.DiagnosticEventBody>({ file, diagnostics: bakedDiags }, "syntaxDiag");
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
@@ -511,9 +517,55 @@ namespace ts.server {
|
||||
return projectFileName && this.projectService.findProject(projectFileName);
|
||||
}
|
||||
|
||||
private getConfigFileAndProject(args: protocol.FileRequestArgs) {
|
||||
const project = this.getProject(args.projectFileName);
|
||||
const file = toNormalizedPath(args.file);
|
||||
|
||||
return {
|
||||
configFile: project && project.hasConfigFile(file) && file,
|
||||
project
|
||||
};
|
||||
}
|
||||
|
||||
private getConfigFileDiagnostics(configFile: NormalizedPath, project: Project, includeLinePosition: boolean) {
|
||||
const projectErrors = project.getAllProjectErrors();
|
||||
const optionsErrors = project.getLanguageService().getCompilerOptionsDiagnostics();
|
||||
const diagnosticsForConfigFile = filter(
|
||||
concatenate(projectErrors, optionsErrors),
|
||||
diagnostic => diagnostic.file && diagnostic.file.fileName === configFile
|
||||
);
|
||||
return includeLinePosition ?
|
||||
this.convertToDiagnosticsWithLinePositionFromDiagnosticFile(diagnosticsForConfigFile) :
|
||||
map(
|
||||
diagnosticsForConfigFile,
|
||||
diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ false)
|
||||
);
|
||||
}
|
||||
|
||||
private convertToDiagnosticsWithLinePositionFromDiagnosticFile(diagnostics: Diagnostic[]) {
|
||||
return diagnostics.map(d => <protocol.DiagnosticWithLinePosition>{
|
||||
message: flattenDiagnosticMessageText(d.messageText, this.host.newLine),
|
||||
start: d.start,
|
||||
length: d.length,
|
||||
category: DiagnosticCategory[d.category].toLowerCase(),
|
||||
code: d.code,
|
||||
startLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start)),
|
||||
endLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start + d.length))
|
||||
});
|
||||
}
|
||||
|
||||
private getCompilerOptionsDiagnostics(args: protocol.CompilerOptionsDiagnosticsRequestArgs) {
|
||||
const project = this.getProject(args.projectFileName);
|
||||
return this.convertToDiagnosticsWithLinePosition(project.getLanguageService().getCompilerOptionsDiagnostics(), /*scriptInfo*/ undefined);
|
||||
// Get diagnostics that dont have associated file with them
|
||||
// The diagnostics which have file would be in config file and
|
||||
// would be reported as part of configFileDiagnostics
|
||||
return this.convertToDiagnosticsWithLinePosition(
|
||||
filter(
|
||||
project.getLanguageService().getCompilerOptionsDiagnostics(),
|
||||
diagnostic => !diagnostic.file
|
||||
),
|
||||
/*scriptInfo*/ undefined
|
||||
);
|
||||
}
|
||||
|
||||
private convertToDiagnosticsWithLinePosition(diagnostics: Diagnostic[], scriptInfo: ScriptInfo) {
|
||||
@@ -557,7 +609,7 @@ namespace ts.server {
|
||||
return {
|
||||
file: def.fileName,
|
||||
start: defScriptInfo.positionToLineOffset(def.textSpan.start),
|
||||
end: defScriptInfo.positionToLineOffset(ts.textSpanEnd(def.textSpan))
|
||||
end: defScriptInfo.positionToLineOffset(textSpanEnd(def.textSpan))
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -581,7 +633,7 @@ namespace ts.server {
|
||||
return {
|
||||
file: def.fileName,
|
||||
start: defScriptInfo.positionToLineOffset(def.textSpan.start),
|
||||
end: defScriptInfo.positionToLineOffset(ts.textSpanEnd(def.textSpan))
|
||||
end: defScriptInfo.positionToLineOffset(textSpanEnd(def.textSpan))
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -599,7 +651,7 @@ namespace ts.server {
|
||||
return {
|
||||
file: fileName,
|
||||
start: scriptInfo.positionToLineOffset(textSpan.start),
|
||||
end: scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan))
|
||||
end: scriptInfo.positionToLineOffset(textSpanEnd(textSpan))
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -623,7 +675,7 @@ namespace ts.server {
|
||||
const { fileName, isWriteAccess, textSpan, isInString } = occurrence;
|
||||
const scriptInfo = project.getScriptInfo(fileName);
|
||||
const start = scriptInfo.positionToLineOffset(textSpan.start);
|
||||
const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan));
|
||||
const end = scriptInfo.positionToLineOffset(textSpanEnd(textSpan));
|
||||
const result: protocol.OccurrencesResponseItem = {
|
||||
start,
|
||||
end,
|
||||
@@ -639,10 +691,20 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private getSyntacticDiagnosticsSync(args: protocol.SyntacticDiagnosticsSyncRequestArgs): protocol.Diagnostic[] | protocol.DiagnosticWithLinePosition[] {
|
||||
const { configFile } = this.getConfigFileAndProject(args);
|
||||
if (configFile) {
|
||||
// all the config file errors are reported as part of semantic check so nothing to report here
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.getDiagnosticsWorker(args, /*isSemantic*/ false, (project, file) => project.getLanguageService().getSyntacticDiagnostics(file), args.includeLinePosition);
|
||||
}
|
||||
|
||||
private getSemanticDiagnosticsSync(args: protocol.SemanticDiagnosticsSyncRequestArgs): protocol.Diagnostic[] | protocol.DiagnosticWithLinePosition[] {
|
||||
const { configFile, project } = this.getConfigFileAndProject(args);
|
||||
if (configFile) {
|
||||
return this.getConfigFileDiagnostics(configFile, project, args.includeLinePosition);
|
||||
}
|
||||
return this.getDiagnosticsWorker(args, /*isSemantic*/ true, (project, file) => project.getLanguageService().getSemanticDiagnostics(file), args.includeLinePosition);
|
||||
}
|
||||
|
||||
@@ -663,7 +725,7 @@ namespace ts.server {
|
||||
return documentHighlights;
|
||||
}
|
||||
|
||||
function convertToDocumentHighlightsItem(documentHighlights: ts.DocumentHighlights): ts.server.protocol.DocumentHighlightsItem {
|
||||
function convertToDocumentHighlightsItem(documentHighlights: DocumentHighlights): protocol.DocumentHighlightsItem {
|
||||
const { fileName, highlightSpans } = documentHighlights;
|
||||
|
||||
const scriptInfo = project.getScriptInfo(fileName);
|
||||
@@ -672,10 +734,10 @@ namespace ts.server {
|
||||
highlightSpans: highlightSpans.map(convertHighlightSpan)
|
||||
};
|
||||
|
||||
function convertHighlightSpan(highlightSpan: ts.HighlightSpan): ts.server.protocol.HighlightSpan {
|
||||
function convertHighlightSpan(highlightSpan: HighlightSpan): protocol.HighlightSpan {
|
||||
const { textSpan, kind } = highlightSpan;
|
||||
const start = scriptInfo.positionToLineOffset(textSpan.start);
|
||||
const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan));
|
||||
const end = scriptInfo.positionToLineOffset(textSpanEnd(textSpan));
|
||||
return { start, end, kind };
|
||||
}
|
||||
}
|
||||
@@ -686,15 +748,15 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private getProjectInfo(args: protocol.ProjectInfoRequestArgs): protocol.ProjectInfo {
|
||||
return this.getProjectInfoWorker(args.file, args.projectFileName, args.needFileNameList);
|
||||
return this.getProjectInfoWorker(args.file, args.projectFileName, args.needFileNameList, /*excludeConfigFiles*/ false);
|
||||
}
|
||||
|
||||
private getProjectInfoWorker(uncheckedFileName: string, projectFileName: string, needFileNameList: boolean) {
|
||||
private getProjectInfoWorker(uncheckedFileName: string, projectFileName: string, needFileNameList: boolean, excludeConfigFiles: boolean) {
|
||||
const { project } = this.getFileAndProjectWorker(uncheckedFileName, projectFileName, /*refreshInferredProjects*/ true, /*errorOnMissingProject*/ true);
|
||||
const projectInfo = {
|
||||
configFileName: project.getProjectName(),
|
||||
languageServiceDisabled: !project.languageServiceEnabled,
|
||||
fileNames: needFileNameList ? project.getFileNames() : undefined
|
||||
fileNames: needFileNameList ? project.getFileNames(/*excludeFilesFromExternalLibraries*/ false, excludeConfigFiles) : undefined
|
||||
};
|
||||
return projectInfo;
|
||||
}
|
||||
@@ -718,7 +780,7 @@ namespace ts.server {
|
||||
const scriptInfo = this.projectService.getScriptInfo(args.file);
|
||||
projects = scriptInfo.containingProjects;
|
||||
}
|
||||
// ts.filter handles case when 'projects' is undefined
|
||||
// filter handles case when 'projects' is undefined
|
||||
projects = filter(projects, p => p.languageServiceEnabled);
|
||||
if (!projects || !projects.length) {
|
||||
return Errors.ThrowNoProject();
|
||||
@@ -771,28 +833,29 @@ namespace ts.server {
|
||||
return <protocol.FileSpan>{
|
||||
file: location.fileName,
|
||||
start: locationScriptInfo.positionToLineOffset(location.textSpan.start),
|
||||
end: locationScriptInfo.positionToLineOffset(ts.textSpanEnd(location.textSpan)),
|
||||
end: locationScriptInfo.positionToLineOffset(textSpanEnd(location.textSpan)),
|
||||
};
|
||||
});
|
||||
},
|
||||
compareRenameLocation,
|
||||
(a, b) => a.file === b.file && a.start.line === b.start.line && a.start.offset === b.start.offset
|
||||
);
|
||||
const locs = fileSpans.reduce<protocol.SpanGroup[]>((accum, cur) => {
|
||||
|
||||
const locs: protocol.SpanGroup[] = [];
|
||||
for (const cur of fileSpans) {
|
||||
let curFileAccum: protocol.SpanGroup;
|
||||
if (accum.length > 0) {
|
||||
curFileAccum = accum[accum.length - 1];
|
||||
if (locs.length > 0) {
|
||||
curFileAccum = locs[locs.length - 1];
|
||||
if (curFileAccum.file !== cur.file) {
|
||||
curFileAccum = undefined;
|
||||
}
|
||||
}
|
||||
if (!curFileAccum) {
|
||||
curFileAccum = { file: cur.file, locs: [] };
|
||||
accum.push(curFileAccum);
|
||||
locs.push(curFileAccum);
|
||||
}
|
||||
curFileAccum.locs.push({ start: cur.start, end: cur.end });
|
||||
return accum;
|
||||
}, []);
|
||||
}
|
||||
|
||||
return { info: renameInfo, locs };
|
||||
}
|
||||
@@ -852,10 +915,10 @@ namespace ts.server {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const displayString = ts.displayPartsToString(nameInfo.displayParts);
|
||||
const displayString = displayPartsToString(nameInfo.displayParts);
|
||||
const nameSpan = nameInfo.textSpan;
|
||||
const nameColStart = scriptInfo.positionToLineOffset(nameSpan.start).offset;
|
||||
const nameText = scriptInfo.getSnapshot().getText(nameSpan.start, ts.textSpanEnd(nameSpan));
|
||||
const nameText = scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan));
|
||||
const refs = combineProjectOutput<protocol.ReferencesResponseItem>(
|
||||
projects,
|
||||
(project: Project) => {
|
||||
@@ -868,12 +931,12 @@ namespace ts.server {
|
||||
const refScriptInfo = project.getScriptInfo(ref.fileName);
|
||||
const start = refScriptInfo.positionToLineOffset(ref.textSpan.start);
|
||||
const refLineSpan = refScriptInfo.lineToTextSpan(start.line - 1);
|
||||
const lineText = refScriptInfo.getSnapshot().getText(refLineSpan.start, ts.textSpanEnd(refLineSpan)).replace(/\r|\n/g, "");
|
||||
const lineText = refScriptInfo.getSnapshot().getText(refLineSpan.start, textSpanEnd(refLineSpan)).replace(/\r|\n/g, "");
|
||||
return {
|
||||
file: ref.fileName,
|
||||
start: start,
|
||||
lineText: lineText,
|
||||
end: refScriptInfo.positionToLineOffset(ts.textSpanEnd(ref.textSpan)),
|
||||
start,
|
||||
lineText,
|
||||
end: refScriptInfo.positionToLineOffset(textSpanEnd(ref.textSpan)),
|
||||
isWriteAccess: ref.isWriteAccess,
|
||||
isDefinition: ref.isDefinition
|
||||
};
|
||||
@@ -1004,15 +1067,15 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
if (simplifiedResult) {
|
||||
const displayString = ts.displayPartsToString(quickInfo.displayParts);
|
||||
const docString = ts.displayPartsToString(quickInfo.documentation);
|
||||
const displayString = displayPartsToString(quickInfo.displayParts);
|
||||
const docString = displayPartsToString(quickInfo.documentation);
|
||||
|
||||
return {
|
||||
kind: quickInfo.kind,
|
||||
kindModifiers: quickInfo.kindModifiers,
|
||||
start: scriptInfo.positionToLineOffset(quickInfo.textSpan.start),
|
||||
end: scriptInfo.positionToLineOffset(ts.textSpanEnd(quickInfo.textSpan)),
|
||||
displayString: displayString,
|
||||
end: scriptInfo.positionToLineOffset(textSpanEnd(quickInfo.textSpan)),
|
||||
displayString,
|
||||
documentation: docString,
|
||||
tags: quickInfo.tags || []
|
||||
};
|
||||
@@ -1071,32 +1134,29 @@ namespace ts.server {
|
||||
// only to the previous line. If all this is true, then
|
||||
// add edits necessary to properly indent the current line.
|
||||
if ((args.key === "\n") && ((!edits) || (edits.length === 0) || allEditsBeforePos(edits, position))) {
|
||||
const lineInfo = scriptInfo.getLineInfo(args.line);
|
||||
if (lineInfo && (lineInfo.leaf) && (lineInfo.leaf.text)) {
|
||||
const lineText = lineInfo.leaf.text;
|
||||
if (lineText.search("\\S") < 0) {
|
||||
const preferredIndent = project.getLanguageService(/*ensureSynchronized*/ false).getIndentationAtPosition(file, position, formatOptions);
|
||||
let hasIndent = 0;
|
||||
let i: number, len: number;
|
||||
for (i = 0, len = lineText.length; i < len; i++) {
|
||||
if (lineText.charAt(i) === " ") {
|
||||
hasIndent++;
|
||||
}
|
||||
else if (lineText.charAt(i) === "\t") {
|
||||
hasIndent += formatOptions.tabSize;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
const { lineText, absolutePosition } = scriptInfo.getLineInfo(args.line);
|
||||
if (lineText && lineText.search("\\S") < 0) {
|
||||
const preferredIndent = project.getLanguageService(/*ensureSynchronized*/ false).getIndentationAtPosition(file, position, formatOptions);
|
||||
let hasIndent = 0;
|
||||
let i: number, len: number;
|
||||
for (i = 0, len = lineText.length; i < len; i++) {
|
||||
if (lineText.charAt(i) === " ") {
|
||||
hasIndent++;
|
||||
}
|
||||
// i points to the first non whitespace character
|
||||
if (preferredIndent !== hasIndent) {
|
||||
const firstNoWhiteSpacePosition = lineInfo.offset + i;
|
||||
edits.push({
|
||||
span: ts.createTextSpanFromBounds(lineInfo.offset, firstNoWhiteSpacePosition),
|
||||
newText: formatting.getIndentationString(preferredIndent, formatOptions)
|
||||
});
|
||||
else if (lineText.charAt(i) === "\t") {
|
||||
hasIndent += formatOptions.tabSize;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// i points to the first non whitespace character
|
||||
if (preferredIndent !== hasIndent) {
|
||||
const firstNoWhiteSpacePosition = absolutePosition + i;
|
||||
edits.push({
|
||||
span: createTextSpanFromBounds(absolutePosition, firstNoWhiteSpacePosition),
|
||||
newText: formatting.getIndentationString(preferredIndent, formatOptions)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1108,7 +1168,7 @@ namespace ts.server {
|
||||
return edits.map((edit) => {
|
||||
return {
|
||||
start: scriptInfo.positionToLineOffset(edit.span.start),
|
||||
end: scriptInfo.positionToLineOffset(ts.textSpanEnd(edit.span)),
|
||||
end: scriptInfo.positionToLineOffset(textSpanEnd(edit.span)),
|
||||
newText: edit.newText ? edit.newText : ""
|
||||
};
|
||||
});
|
||||
@@ -1126,15 +1186,13 @@ namespace ts.server {
|
||||
return undefined;
|
||||
}
|
||||
if (simplifiedResult) {
|
||||
return completions.entries.reduce((result: protocol.CompletionEntry[], entry: ts.CompletionEntry) => {
|
||||
return mapDefined(completions.entries, entry => {
|
||||
if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) {
|
||||
const { name, kind, kindModifiers, sortText, replacementSpan } = entry;
|
||||
const convertedSpan: protocol.TextSpan =
|
||||
replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined;
|
||||
result.push({ name, kind, kindModifiers, sortText, replacementSpan: convertedSpan });
|
||||
const convertedSpan = replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined;
|
||||
return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan };
|
||||
}
|
||||
return result;
|
||||
}, []).sort((a, b) => ts.compareStrings(a.name, b.name));
|
||||
}).sort((a, b) => compareStrings(a.name, b.name));
|
||||
}
|
||||
else {
|
||||
return completions;
|
||||
@@ -1146,13 +1204,8 @@ namespace ts.server {
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
|
||||
const position = this.getPosition(args, scriptInfo);
|
||||
|
||||
return args.entryNames.reduce((accum: protocol.CompletionEntryDetails[], entryName: string) => {
|
||||
const details = project.getLanguageService().getCompletionEntryDetails(file, position, entryName);
|
||||
if (details) {
|
||||
accum.push(details);
|
||||
}
|
||||
return accum;
|
||||
}, []);
|
||||
return mapDefined(args.entryNames, entryName =>
|
||||
project.getLanguageService().getCompletionEntryDetails(file, position, entryName));
|
||||
}
|
||||
|
||||
private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): protocol.CompileOnSaveAffectedFileListSingleProject[] {
|
||||
@@ -1217,14 +1270,11 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private getDiagnostics(next: NextStep, delay: number, fileNames: string[]): void {
|
||||
const checkList = fileNames.reduce((accum: PendingErrorCheck[], uncheckedFileName: string) => {
|
||||
const checkList = mapDefined(fileNames, uncheckedFileName => {
|
||||
const fileName = toNormalizedPath(uncheckedFileName);
|
||||
const project = this.projectService.getDefaultProjectForFile(fileName, /*refreshInferredProjects*/ true);
|
||||
if (project) {
|
||||
accum.push({ fileName, project });
|
||||
}
|
||||
return accum;
|
||||
}, []);
|
||||
return project && { fileName, project };
|
||||
});
|
||||
|
||||
if (checkList.length > 0) {
|
||||
this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay);
|
||||
@@ -1269,11 +1319,11 @@ namespace ts.server {
|
||||
if (!fileName) {
|
||||
return;
|
||||
}
|
||||
const file = ts.normalizePath(fileName);
|
||||
const file = normalizePath(fileName);
|
||||
this.projectService.closeClientFile(file);
|
||||
}
|
||||
|
||||
private decorateNavigationBarItems(items: ts.NavigationBarItem[], scriptInfo: ScriptInfo): protocol.NavigationBarItem[] {
|
||||
private decorateNavigationBarItems(items: NavigationBarItem[], scriptInfo: ScriptInfo): protocol.NavigationBarItem[] {
|
||||
return map(items, item => ({
|
||||
text: item.text,
|
||||
kind: item.kind,
|
||||
@@ -1294,7 +1344,7 @@ namespace ts.server {
|
||||
: items;
|
||||
}
|
||||
|
||||
private decorateNavigationTree(tree: ts.NavigationTree, scriptInfo: ScriptInfo): protocol.NavigationTree {
|
||||
private decorateNavigationTree(tree: NavigationTree, scriptInfo: ScriptInfo): protocol.NavigationTree {
|
||||
return {
|
||||
text: tree.text,
|
||||
kind: tree.kind,
|
||||
@@ -1307,7 +1357,7 @@ namespace ts.server {
|
||||
private decorateSpan(span: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan {
|
||||
return {
|
||||
start: scriptInfo.positionToLineOffset(span.start),
|
||||
end: scriptInfo.positionToLineOffset(ts.textSpanEnd(span))
|
||||
end: scriptInfo.positionToLineOffset(textSpanEnd(span))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1337,13 +1387,13 @@ namespace ts.server {
|
||||
return navItems.map((navItem) => {
|
||||
const scriptInfo = project.getScriptInfo(navItem.fileName);
|
||||
const start = scriptInfo.positionToLineOffset(navItem.textSpan.start);
|
||||
const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(navItem.textSpan));
|
||||
const end = scriptInfo.positionToLineOffset(textSpanEnd(navItem.textSpan));
|
||||
const bakedItem: protocol.NavtoItem = {
|
||||
name: navItem.name,
|
||||
kind: navItem.kind,
|
||||
file: navItem.fileName,
|
||||
start: start,
|
||||
end: end,
|
||||
start,
|
||||
end,
|
||||
};
|
||||
if (navItem.kindModifiers && (navItem.kindModifiers !== "")) {
|
||||
bakedItem.kindModifiers = navItem.kindModifiers;
|
||||
@@ -1402,7 +1452,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private getSupportedCodeFixes(): string[] {
|
||||
return ts.getSupportedCodeFixes();
|
||||
return getSupportedCodeFixes();
|
||||
}
|
||||
|
||||
private isLocation(locationOrSpan: protocol.FileLocationOrRangeRequestArgs): locationOrSpan is protocol.FileLocationRequestArgs {
|
||||
@@ -1433,29 +1483,40 @@ namespace ts.server {
|
||||
return project.getLanguageService().getApplicableRefactors(file, position || textRange);
|
||||
}
|
||||
|
||||
private getRefactorCodeActions(args: protocol.GetRefactorCodeActionsRequestArgs, simplifiedResult: boolean): protocol.RefactorCodeActions | protocol.RefactorCodeActionsFull {
|
||||
private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): RefactorEditInfo | protocol.RefactorEditInfo {
|
||||
const { file, project } = this.getFileAndProjectWithoutRefreshingInferredProjects(args);
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
|
||||
const { position, textRange } = this.extractPositionAndRange(args, scriptInfo);
|
||||
|
||||
const result: ts.CodeAction[] = project.getLanguageService().getRefactorCodeActions(
|
||||
const result = project.getLanguageService().getEditsForRefactor(
|
||||
file,
|
||||
this.projectService.getFormatCodeOptions(),
|
||||
position || textRange,
|
||||
args.refactorName
|
||||
args.refactor,
|
||||
args.action
|
||||
);
|
||||
|
||||
if (simplifiedResult) {
|
||||
// Not full
|
||||
if (result === undefined) {
|
||||
return {
|
||||
actions: result.map(action => this.mapCodeAction(action, scriptInfo))
|
||||
edits: []
|
||||
};
|
||||
}
|
||||
|
||||
if (simplifiedResult) {
|
||||
const file = result.renameFilename;
|
||||
let location: protocol.Location | undefined;
|
||||
if (file !== undefined && result.renameLocation !== undefined) {
|
||||
const renameScriptInfo = project.getScriptInfoForNormalizedPath(toNormalizedPath(file));
|
||||
location = renameScriptInfo.positionToLineOffset(result.renameLocation);
|
||||
}
|
||||
return {
|
||||
renameLocation: location,
|
||||
renameFilename: file,
|
||||
edits: result.edits.map(change => this.mapTextChangesToCodeEdits(project, change))
|
||||
};
|
||||
}
|
||||
else {
|
||||
// Full
|
||||
return {
|
||||
actions: result
|
||||
};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1513,6 +1574,14 @@ namespace ts.server {
|
||||
};
|
||||
}
|
||||
|
||||
private mapTextChangesToCodeEdits(project: Project, textChanges: FileTextChanges): protocol.FileCodeEdits {
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(toNormalizedPath(textChanges.fileName));
|
||||
return {
|
||||
fileName: textChanges.fileName,
|
||||
textChanges: textChanges.textChanges.map(textChange => this.convertTextChangeToCodeEdit(textChange, scriptInfo))
|
||||
};
|
||||
}
|
||||
|
||||
private convertTextChangeToCodeEdit(change: ts.TextChange, scriptInfo: ScriptInfo): protocol.CodeEdit {
|
||||
return {
|
||||
start: scriptInfo.positionToLineOffset(change.span.start),
|
||||
@@ -1536,7 +1605,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private getDiagnosticsForProject(next: NextStep, delay: number, fileName: string): void {
|
||||
const { fileNames, languageServiceDisabled } = this.getProjectInfoWorker(fileName, /*projectFileName*/ undefined, /*needFileNameList*/ true);
|
||||
const { fileNames, languageServiceDisabled } = this.getProjectInfoWorker(fileName, /*projectFileName*/ undefined, /*needFileNameList*/ true, /*excludeConfigFiles*/ true);
|
||||
if (languageServiceDisabled) {
|
||||
return;
|
||||
}
|
||||
@@ -1552,18 +1621,22 @@ namespace ts.server {
|
||||
const normalizedFileName = toNormalizedPath(fileName);
|
||||
const project = this.projectService.getDefaultProjectForFile(normalizedFileName, /*refreshInferredProjects*/ true);
|
||||
for (const fileNameInProject of fileNamesInProject) {
|
||||
if (this.getCanonicalFileName(fileNameInProject) === this.getCanonicalFileName(fileName))
|
||||
if (this.getCanonicalFileName(fileNameInProject) === this.getCanonicalFileName(fileName)) {
|
||||
highPriorityFiles.push(fileNameInProject);
|
||||
}
|
||||
else {
|
||||
const info = this.projectService.getScriptInfo(fileNameInProject);
|
||||
if (!info.isScriptOpen()) {
|
||||
if (fileNameInProject.indexOf(".d.ts") > 0)
|
||||
if (fileNameInProject.indexOf(Extension.Dts) > 0) {
|
||||
veryLowPriorityFiles.push(fileNameInProject);
|
||||
else
|
||||
}
|
||||
else {
|
||||
lowPriorityFiles.push(fileNameInProject);
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
mediumPriorityFiles.push(fileNameInProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1579,7 +1652,7 @@ namespace ts.server {
|
||||
|
||||
getCanonicalFileName(fileName: string) {
|
||||
const name = this.host.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
|
||||
return ts.normalizePath(name);
|
||||
return normalizePath(name);
|
||||
}
|
||||
|
||||
exit() {
|
||||
@@ -1844,11 +1917,11 @@ namespace ts.server {
|
||||
[CommandNames.GetApplicableRefactors]: (request: protocol.GetApplicableRefactorsRequest) => {
|
||||
return this.requiredResponse(this.getApplicableRefactors(request.arguments));
|
||||
},
|
||||
[CommandNames.GetRefactorCodeActions]: (request: protocol.GetRefactorCodeActionsRequest) => {
|
||||
return this.requiredResponse(this.getRefactorCodeActions(request.arguments, /*simplifiedResult*/ true));
|
||||
[CommandNames.GetEditsForRefactor]: (request: protocol.GetEditsForRefactorRequest) => {
|
||||
return this.requiredResponse(this.getEditsForRefactor(request.arguments, /*simplifiedResult*/ true));
|
||||
},
|
||||
[CommandNames.GetRefactorCodeActionsFull]: (request: protocol.GetRefactorCodeActionsRequest) => {
|
||||
return this.requiredResponse(this.getRefactorCodeActions(request.arguments, /*simplifiedResult*/ false));
|
||||
[CommandNames.GetEditsForRefactorFull]: (request: protocol.GetEditsForRefactorRequest) => {
|
||||
return this.requiredResponse(this.getEditsForRefactor(request.arguments, /*simplifiedResult*/ false));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
"preserveConstEnums": true,
|
||||
"pretty": true,
|
||||
"outFile": "../../built/local/tsserverlibrary.js",
|
||||
|
||||
+10
-6
@@ -20,8 +20,12 @@ declare namespace ts.server {
|
||||
require?(initialPath: string, moduleName: string): RequireResult;
|
||||
}
|
||||
|
||||
export interface SortedArray<T> extends Array<T> {
|
||||
" __sortedArrayBrand": any;
|
||||
}
|
||||
|
||||
export interface SortedReadonlyArray<T> extends ReadonlyArray<T> {
|
||||
" __sortedReadonlyArrayBrand": any;
|
||||
" __sortedArrayBrand": any;
|
||||
}
|
||||
|
||||
export interface TypingInstallerRequest {
|
||||
@@ -31,9 +35,9 @@ declare namespace ts.server {
|
||||
|
||||
export interface DiscoverTypings extends TypingInstallerRequest {
|
||||
readonly fileNames: string[];
|
||||
readonly projectRootPath: ts.Path;
|
||||
readonly compilerOptions: ts.CompilerOptions;
|
||||
readonly typeAcquisition: ts.TypeAcquisition;
|
||||
readonly projectRootPath: Path;
|
||||
readonly compilerOptions: CompilerOptions;
|
||||
readonly typeAcquisition: TypeAcquisition;
|
||||
readonly unresolvedImports: SortedReadonlyArray<string>;
|
||||
readonly cachePath?: string;
|
||||
readonly kind: "discover";
|
||||
@@ -63,8 +67,8 @@ declare namespace ts.server {
|
||||
}
|
||||
|
||||
export interface SetTypings extends ProjectResponse {
|
||||
readonly typeAcquisition: ts.TypeAcquisition;
|
||||
readonly compilerOptions: ts.CompilerOptions;
|
||||
readonly typeAcquisition: TypeAcquisition;
|
||||
readonly compilerOptions: CompilerOptions;
|
||||
readonly typings: string[];
|
||||
readonly unresolvedImports: SortedReadonlyArray<string>;
|
||||
readonly kind: ActionSet;
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace ts.server {
|
||||
this.perProjectCache.set(projectName, {
|
||||
compilerOptions,
|
||||
typeAcquisition,
|
||||
typings: toSortedReadonlyArray(newTypings),
|
||||
typings: toSortedArray(newTypings),
|
||||
unresolvedImports,
|
||||
poisoned: false
|
||||
});
|
||||
|
||||
@@ -85,6 +85,7 @@ namespace ts.server.typingsInstaller {
|
||||
private readonly missingTypingsSet: Map<true> = createMap<true>();
|
||||
private readonly knownCachesSet: Map<true> = createMap<true>();
|
||||
private readonly projectWatchers: Map<FileWatcher[]> = createMap<FileWatcher[]>();
|
||||
private safeList: JsTyping.SafeList | undefined;
|
||||
readonly pendingRunRequests: PendingRequest[] = [];
|
||||
|
||||
private installRunCount = 1;
|
||||
@@ -93,10 +94,10 @@ namespace ts.server.typingsInstaller {
|
||||
abstract readonly typesRegistry: Map<void>;
|
||||
|
||||
constructor(
|
||||
readonly installTypingHost: InstallTypingHost,
|
||||
readonly globalCachePath: string,
|
||||
readonly safeListPath: Path,
|
||||
readonly throttleLimit: number,
|
||||
protected readonly installTypingHost: InstallTypingHost,
|
||||
private readonly globalCachePath: string,
|
||||
private readonly safeListPath: Path,
|
||||
private readonly throttleLimit: number,
|
||||
protected readonly log = nullLog) {
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Global cache location '${globalCachePath}', safe file path '${safeListPath}'`);
|
||||
@@ -143,11 +144,15 @@ namespace ts.server.typingsInstaller {
|
||||
this.processCacheLocation(req.cachePath);
|
||||
}
|
||||
|
||||
if (this.safeList === undefined) {
|
||||
this.safeList = JsTyping.loadSafeList(this.installTypingHost, this.safeListPath);
|
||||
}
|
||||
const discoverTypingsResult = JsTyping.discoverTypings(
|
||||
this.installTypingHost,
|
||||
this.log.isEnabled() ? this.log.writeLine : undefined,
|
||||
req.fileNames,
|
||||
req.projectRootPath,
|
||||
this.safeListPath,
|
||||
this.safeList,
|
||||
this.packageNameToTypingLocation,
|
||||
req.typeAcquisition,
|
||||
req.unresolvedImports);
|
||||
@@ -389,7 +394,7 @@ namespace ts.server.typingsInstaller {
|
||||
this.log.writeLine(`Got FS notification for ${f}, handler is already invoked '${isInvoked}'`);
|
||||
}
|
||||
if (!isInvoked) {
|
||||
this.sendResponse({ projectName: projectName, kind: server.ActionInvalidate });
|
||||
this.sendResponse({ projectName, kind: server.ActionInvalidate });
|
||||
isInvoked = true;
|
||||
}
|
||||
}, /*pollingInterval*/ 2000);
|
||||
@@ -434,5 +439,4 @@ namespace ts.server.typingsInstaller {
|
||||
export function typingsName(packageName: string): string {
|
||||
return `@types/${packageName}@ts${versionMajorMinor}`;
|
||||
}
|
||||
const versionMajorMinor = version.split(".").slice(0, 2).join(".");
|
||||
}
|
||||
+74
-55
@@ -9,7 +9,7 @@ namespace ts.server {
|
||||
verbose
|
||||
}
|
||||
|
||||
export const emptyArray: ReadonlyArray<any> = [];
|
||||
export const emptyArray: SortedReadonlyArray<never> = createSortedArray<never>();
|
||||
|
||||
export interface Logger {
|
||||
close(): void;
|
||||
@@ -49,7 +49,7 @@ namespace ts.server {
|
||||
export function createInstallTypingsRequest(project: Project, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray<string>, cachePath?: string): DiscoverTypings {
|
||||
return {
|
||||
projectName: project.getProjectName(),
|
||||
fileNames: project.getFileNames(/*excludeFilesFromExternalLibraries*/ true),
|
||||
fileNames: project.getFileNames(/*excludeFilesFromExternalLibraries*/ true, /*excludeConfigFiles*/ true),
|
||||
compilerOptions: project.getCompilerOptions(),
|
||||
typeAcquisition,
|
||||
unresolvedImports,
|
||||
@@ -77,7 +77,7 @@ namespace ts.server {
|
||||
tabSize: 4,
|
||||
newLineCharacter: host.newLine || "\n",
|
||||
convertTabsToSpaces: true,
|
||||
indentStyle: ts.IndentStyle.Smart,
|
||||
indentStyle: IndentStyle.Smart,
|
||||
insertSpaceAfterConstructor: false,
|
||||
insertSpaceAfterCommaDelimiter: true,
|
||||
insertSpaceAfterSemicolonInForStatements: true,
|
||||
@@ -103,24 +103,6 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
export function removeItemFromSet<T>(items: T[], itemToRemove: T) {
|
||||
if (items.length === 0) {
|
||||
return;
|
||||
}
|
||||
const index = items.indexOf(itemToRemove);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
if (index === items.length - 1) {
|
||||
// last item - pop it
|
||||
items.pop();
|
||||
}
|
||||
else {
|
||||
// non-last item - replace it with the last one
|
||||
items[index] = items.pop();
|
||||
}
|
||||
}
|
||||
|
||||
export type NormalizedPath = string & { __normalizedPathTag: any };
|
||||
|
||||
export function toNormalizedPath(fileName: string): NormalizedPath {
|
||||
@@ -190,40 +172,8 @@ namespace ts.server {
|
||||
return `/dev/null/inferredProject${counter}*`;
|
||||
}
|
||||
|
||||
export function toSortedReadonlyArray(arr: string[]): SortedReadonlyArray<string> {
|
||||
arr.sort();
|
||||
return <any>arr;
|
||||
}
|
||||
|
||||
export function enumerateInsertsAndDeletes<T>(a: SortedReadonlyArray<T>, b: SortedReadonlyArray<T>, inserted: (item: T) => void, deleted: (item: T) => void, compare?: (a: T, b: T) => Comparison) {
|
||||
compare = compare || ts.compareValues;
|
||||
let aIndex = 0;
|
||||
let bIndex = 0;
|
||||
const aLen = a.length;
|
||||
const bLen = b.length;
|
||||
while (aIndex < aLen && bIndex < bLen) {
|
||||
const aItem = a[aIndex];
|
||||
const bItem = b[bIndex];
|
||||
const compareResult = compare(aItem, bItem);
|
||||
if (compareResult === Comparison.LessThan) {
|
||||
inserted(aItem);
|
||||
aIndex++;
|
||||
}
|
||||
else if (compareResult === Comparison.GreaterThan) {
|
||||
deleted(bItem);
|
||||
bIndex++;
|
||||
}
|
||||
else {
|
||||
aIndex++;
|
||||
bIndex++;
|
||||
}
|
||||
}
|
||||
while (aIndex < aLen) {
|
||||
inserted(a[aIndex++]);
|
||||
}
|
||||
while (bIndex < bLen) {
|
||||
deleted(b[bIndex++]);
|
||||
}
|
||||
export function createSortedArray<T>(): SortedArray<T> {
|
||||
return [] as SortedArray<T>;
|
||||
}
|
||||
|
||||
export class ThrottledOperations {
|
||||
@@ -273,4 +223,73 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
namespace ts.server {
|
||||
export function insertSorted<T>(array: SortedArray<T>, insert: T, compare: Comparer<T>): void {
|
||||
if (array.length === 0) {
|
||||
array.push(insert);
|
||||
return;
|
||||
}
|
||||
|
||||
const insertIndex = binarySearch(array, insert, compare);
|
||||
if (insertIndex < 0) {
|
||||
array.splice(~insertIndex, 0, insert);
|
||||
}
|
||||
}
|
||||
|
||||
export function removeSorted<T>(array: SortedArray<T>, remove: T, compare: Comparer<T>): void {
|
||||
if (!array || array.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (array[0] === remove) {
|
||||
array.splice(0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const removeIndex = binarySearch(array, remove, compare);
|
||||
if (removeIndex >= 0) {
|
||||
array.splice(removeIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
export function toSortedArray(arr: string[]): SortedArray<string>;
|
||||
export function toSortedArray<T>(arr: T[], comparer: Comparer<T>): SortedArray<T>;
|
||||
export function toSortedArray<T>(arr: T[], comparer?: Comparer<T>): SortedArray<T> {
|
||||
arr.sort(comparer);
|
||||
return arr as SortedArray<T>;
|
||||
}
|
||||
|
||||
export function enumerateInsertsAndDeletes<T>(newItems: SortedReadonlyArray<T>, oldItems: SortedReadonlyArray<T>, inserted: (newItem: T) => void, deleted: (oldItem: T) => void, compare?: Comparer<T>) {
|
||||
compare = compare || compareValues;
|
||||
let newIndex = 0;
|
||||
let oldIndex = 0;
|
||||
const newLen = newItems.length;
|
||||
const oldLen = oldItems.length;
|
||||
while (newIndex < newLen && oldIndex < oldLen) {
|
||||
const newItem = newItems[newIndex];
|
||||
const oldItem = oldItems[oldIndex];
|
||||
const compareResult = compare(newItem, oldItem);
|
||||
if (compareResult === Comparison.LessThan) {
|
||||
inserted(newItem);
|
||||
newIndex++;
|
||||
}
|
||||
else if (compareResult === Comparison.GreaterThan) {
|
||||
deleted(oldItem);
|
||||
oldIndex++;
|
||||
}
|
||||
else {
|
||||
newIndex++;
|
||||
oldIndex++;
|
||||
}
|
||||
}
|
||||
while (newIndex < newLen) {
|
||||
inserted(newItems[newIndex++]);
|
||||
}
|
||||
while (oldIndex < oldLen) {
|
||||
deleted(oldItems[oldIndex++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,7 +146,7 @@ namespace ts.BreakpointResolver {
|
||||
|
||||
case SyntaxKind.ForOfStatement:
|
||||
// span in initializer
|
||||
return spanInInitializerOfForLike(<ForOfStatement | ForInStatement>node);
|
||||
return spanInInitializerOfForLike(<ForOfStatement>node);
|
||||
|
||||
case SyntaxKind.SwitchStatement:
|
||||
// span on switch(...)
|
||||
|
||||
+14
-14
@@ -462,7 +462,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getSemanticClassifications(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFile: SourceFile, classifiableNames: Map<string>, span: TextSpan): ClassifiedSpan[] {
|
||||
export function getSemanticClassifications(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFile: SourceFile, classifiableNames: UnderscoreEscapedMap<true>, span: TextSpan): ClassifiedSpan[] {
|
||||
return convertClassifications(getEncodedSemanticClassifications(typeChecker, cancellationToken, sourceFile, classifiableNames, span));
|
||||
}
|
||||
|
||||
@@ -487,7 +487,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getEncodedSemanticClassifications(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFile: SourceFile, classifiableNames: Map<string>, span: TextSpan): Classifications {
|
||||
export function getEncodedSemanticClassifications(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFile: SourceFile, classifiableNames: UnderscoreEscapedMap<true>, span: TextSpan): Classifications {
|
||||
const result: number[] = [];
|
||||
processNode(sourceFile);
|
||||
|
||||
@@ -557,7 +557,7 @@ namespace ts {
|
||||
// Only bother calling into the typechecker if this is an identifier that
|
||||
// could possibly resolve to a type name. This makes classification run
|
||||
// in a third of the time it would normally take.
|
||||
if (classifiableNames.get(identifier.text)) {
|
||||
if (classifiableNames.has(identifier.escapedText)) {
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
if (symbol) {
|
||||
const type = classifySymbol(symbol, getMeaningFromLocation(node));
|
||||
@@ -724,8 +724,8 @@ namespace ts {
|
||||
pushCommentRange(pos, tag.pos - pos);
|
||||
}
|
||||
|
||||
pushClassification(tag.atToken.pos, tag.atToken.end - tag.atToken.pos, ClassificationType.punctuation);
|
||||
pushClassification(tag.tagName.pos, tag.tagName.end - tag.tagName.pos, ClassificationType.docCommentTagName);
|
||||
pushClassification(tag.atToken.pos, tag.atToken.end - tag.atToken.pos, ClassificationType.punctuation); // "@"
|
||||
pushClassification(tag.tagName.pos, tag.tagName.end - tag.tagName.pos, ClassificationType.docCommentTagName); // e.g. "param"
|
||||
|
||||
pos = tag.tagName.end;
|
||||
|
||||
@@ -755,10 +755,10 @@ namespace ts {
|
||||
return;
|
||||
|
||||
function processJSDocParameterTag(tag: JSDocParameterTag) {
|
||||
if (tag.preParameterName) {
|
||||
pushCommentRange(pos, tag.preParameterName.pos - pos);
|
||||
pushClassification(tag.preParameterName.pos, tag.preParameterName.end - tag.preParameterName.pos, ClassificationType.parameterName);
|
||||
pos = tag.preParameterName.end;
|
||||
if (tag.isNameFirst) {
|
||||
pushCommentRange(pos, tag.name.pos - pos);
|
||||
pushClassification(tag.name.pos, tag.name.end - tag.name.pos, ClassificationType.parameterName);
|
||||
pos = tag.name.end;
|
||||
}
|
||||
|
||||
if (tag.typeExpression) {
|
||||
@@ -767,10 +767,10 @@ namespace ts {
|
||||
pos = tag.typeExpression.end;
|
||||
}
|
||||
|
||||
if (tag.postParameterName) {
|
||||
pushCommentRange(pos, tag.postParameterName.pos - pos);
|
||||
pushClassification(tag.postParameterName.pos, tag.postParameterName.end - tag.postParameterName.pos, ClassificationType.parameterName);
|
||||
pos = tag.postParameterName.end;
|
||||
if (!tag.isNameFirst) {
|
||||
pushCommentRange(pos, tag.name.pos - pos);
|
||||
pushClassification(tag.name.pos, tag.name.end - tag.name.pos, ClassificationType.parameterName);
|
||||
pos = tag.name.end;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -814,7 +814,7 @@ namespace ts {
|
||||
* False will mean that node is not classified and traverse routine should recurse into node contents.
|
||||
*/
|
||||
function tryClassifyNode(node: Node): boolean {
|
||||
if (isJSDocTag(node)) {
|
||||
if (isJSDoc(node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1.code],
|
||||
getCodeActions: (context: CodeFixContext) => {
|
||||
const sourceFile = context.sourceFile;
|
||||
const token = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false);
|
||||
const qualifiedName = getAncestor(token, SyntaxKind.QualifiedName) as QualifiedName;
|
||||
Debug.assert(!!qualifiedName, "Expected position to be owned by a qualified name.");
|
||||
if (!isIdentifier(qualifiedName.left)) {
|
||||
return undefined;
|
||||
}
|
||||
const leftText = qualifiedName.left.getText(sourceFile);
|
||||
const rightText = qualifiedName.right.getText(sourceFile);
|
||||
const replacement = createIndexedAccessTypeNode(
|
||||
createTypeReferenceNode(qualifiedName.left, /*typeArguments*/ undefined),
|
||||
createLiteralTypeNode(createLiteral(rightText)));
|
||||
const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
changeTracker.replaceNode(sourceFile, qualifiedName, replacement);
|
||||
|
||||
return [{
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Rewrite_as_the_indexed_access_type_0), [`${leftText}["${rightText}"]`]),
|
||||
changes: changeTracker.getChanges()
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
// If all fails, add an extra new line immediatlly before the error span.
|
||||
// If all fails, add an extra new line immediately before the error span.
|
||||
return {
|
||||
span: { start: position, length: 0 },
|
||||
newText: `${position === startPosition ? "" : newLineCharacter}// @ts-ignore${newLineCharacter}`
|
||||
@@ -67,4 +67,4 @@ namespace ts.codefix {
|
||||
}]
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,11 +58,7 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function pushAction(result: CodeAction[], newNodes: Node[], description: string): void {
|
||||
const newAction: CodeAction = {
|
||||
description: description,
|
||||
changes: newNodesToChanges(newNodes, openBrace, context)
|
||||
};
|
||||
result.push(newAction);
|
||||
result.push({ description, changes: newNodesToChanges(newNodes, openBrace, context) });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,9 @@ namespace ts.codefix {
|
||||
// figure out if the `this` access is actually inside the supercall
|
||||
// i.e. super(this.a), since in that case we won't suggest a fix
|
||||
if (superCall.expression && superCall.expression.kind === SyntaxKind.CallExpression) {
|
||||
const arguments = (<CallExpression>superCall.expression).arguments;
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
if ((<PropertyAccessExpression>arguments[i]).expression === token) {
|
||||
const expressionArguments = (<CallExpression>superCall.expression).arguments;
|
||||
for (let i = 0; i < expressionArguments.length; i++) {
|
||||
if ((<PropertyAccessExpression>expressionArguments[i]).expression === token) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -46,4 +46,4 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments.code],
|
||||
getCodeActions: getActionsForJSDocTypes
|
||||
});
|
||||
|
||||
function getActionsForJSDocTypes(context: CodeFixContext): CodeAction[] | undefined {
|
||||
const sourceFile = context.sourceFile;
|
||||
const node = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false);
|
||||
const decl = ts.findAncestor(node, n => n.kind === SyntaxKind.VariableDeclaration);
|
||||
if (!decl) return;
|
||||
const checker = context.program.getTypeChecker();
|
||||
|
||||
const jsdocType = (decl as VariableDeclaration).type;
|
||||
const original = getTextOfNode(jsdocType);
|
||||
const type = checker.getTypeFromTypeNode(jsdocType);
|
||||
const actions = [createAction(jsdocType, sourceFile.fileName, original, checker.typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTruncation))];
|
||||
if (jsdocType.kind === SyntaxKind.JSDocNullableType) {
|
||||
// for nullable types, suggest the flow-compatible `T | null | undefined`
|
||||
// in addition to the jsdoc/closure-compatible `T | null`
|
||||
const replacementWithUndefined = checker.typeToString(checker.getNullableType(type, TypeFlags.Undefined), /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTruncation);
|
||||
actions.push(createAction(jsdocType, sourceFile.fileName, original, replacementWithUndefined));
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
function createAction(declaration: TypeNode, fileName: string, original: string, replacement: string): CodeAction {
|
||||
return {
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [original, replacement]),
|
||||
changes: [{
|
||||
fileName,
|
||||
textChanges: [{
|
||||
span: { start: declaration.getStart(), length: declaration.getWidth() },
|
||||
newText: replacement
|
||||
}]
|
||||
}],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,8 @@ namespace ts.codefix {
|
||||
const node = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false); // TODO: GH#15852
|
||||
const checker = context.program.getTypeChecker();
|
||||
let suggestion: string;
|
||||
if (node.kind === SyntaxKind.Identifier && isPropertyAccessExpression(node.parent)) {
|
||||
if (isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
||||
Debug.assert(node.kind === SyntaxKind.Identifier);
|
||||
const containingType = checker.getTypeAtLocation(node.parent.expression);
|
||||
suggestion = checker.getSuggestionForNonexistentProperty(node as Identifier, containingType);
|
||||
}
|
||||
|
||||
+75
-65
@@ -18,83 +18,87 @@ namespace ts.codefix {
|
||||
|
||||
switch (token.kind) {
|
||||
case ts.SyntaxKind.Identifier:
|
||||
return deleteIdentifier();
|
||||
return deleteIdentifierOrPrefixWithUnderscore(<Identifier>token);
|
||||
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.NamespaceImport:
|
||||
return deleteNode(token.parent);
|
||||
return [deleteNode(token.parent)];
|
||||
|
||||
default:
|
||||
return deleteDefault();
|
||||
}
|
||||
|
||||
function deleteDefault() {
|
||||
function deleteDefault(): CodeAction[] | undefined {
|
||||
if (isDeclarationName(token)) {
|
||||
return deleteNode(token.parent);
|
||||
return [deleteNode(token.parent)];
|
||||
}
|
||||
else if (isLiteralComputedPropertyDeclarationName(token)) {
|
||||
return deleteNode(token.parent.parent);
|
||||
return [deleteNode(token.parent.parent)];
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function deleteIdentifier(): CodeAction[] | undefined {
|
||||
switch (token.parent.kind) {
|
||||
function prefixIdentifierWithUnderscore(identifier: Identifier): CodeAction {
|
||||
const startPosition = identifier.getStart(sourceFile, /*includeJsDocComment*/ false);
|
||||
return {
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Prefix_0_with_an_underscore), { 0: token.getText() }),
|
||||
changes: [{
|
||||
fileName: sourceFile.path,
|
||||
textChanges: [{
|
||||
span: { start: startPosition, length: 0 },
|
||||
newText: "_"
|
||||
}]
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
function deleteIdentifierOrPrefixWithUnderscore(identifier: Identifier): CodeAction[] | undefined {
|
||||
const parent = identifier.parent;
|
||||
switch (parent.kind) {
|
||||
case ts.SyntaxKind.VariableDeclaration:
|
||||
return deleteVariableDeclaration(<ts.VariableDeclaration>token.parent);
|
||||
return deleteVariableDeclarationOrPrefixWithUnderscore(identifier, <ts.VariableDeclaration>parent);
|
||||
|
||||
case SyntaxKind.TypeParameter:
|
||||
const typeParameters = (<DeclarationWithTypeParameters>token.parent.parent).typeParameters;
|
||||
const typeParameters = (<DeclarationWithTypeParameters>parent.parent).typeParameters;
|
||||
if (typeParameters.length === 1) {
|
||||
const previousToken = getTokenAtPosition(sourceFile, typeParameters.pos - 1, /*includeJsDocComment*/ false);
|
||||
if (!previousToken || previousToken.kind !== SyntaxKind.LessThanToken) {
|
||||
return deleteRange(typeParameters);
|
||||
}
|
||||
const nextToken = getTokenAtPosition(sourceFile, typeParameters.end, /*includeJsDocComment*/ false);
|
||||
if (!nextToken || nextToken.kind !== SyntaxKind.GreaterThanToken) {
|
||||
return deleteRange(typeParameters);
|
||||
}
|
||||
return deleteNodeRange(previousToken, nextToken);
|
||||
Debug.assert(previousToken.kind === SyntaxKind.LessThanToken);
|
||||
Debug.assert(nextToken.kind === SyntaxKind.GreaterThanToken);
|
||||
|
||||
return [deleteNodeRange(previousToken, nextToken)];
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
return [deleteNodeInList(parent)];
|
||||
}
|
||||
|
||||
case ts.SyntaxKind.Parameter:
|
||||
const functionDeclaration = <FunctionDeclaration>token.parent.parent;
|
||||
if (functionDeclaration.parameters.length === 1) {
|
||||
return deleteNode(token.parent);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
const functionDeclaration = <FunctionDeclaration>parent.parent;
|
||||
return [functionDeclaration.parameters.length === 1 ? deleteNode(parent) : deleteNodeInList(parent),
|
||||
prefixIdentifierWithUnderscore(identifier)];
|
||||
|
||||
// handle case where 'import a = A;'
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
const importEquals = getAncestor(token, SyntaxKind.ImportEqualsDeclaration);
|
||||
return deleteNode(importEquals);
|
||||
const importEquals = getAncestor(identifier, SyntaxKind.ImportEqualsDeclaration);
|
||||
return [deleteNode(importEquals)];
|
||||
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
const namedImports = <NamedImports>token.parent.parent;
|
||||
const namedImports = <NamedImports>parent.parent;
|
||||
if (namedImports.elements.length === 1) {
|
||||
// Only 1 import and it is unused. So the entire declaration should be removed.
|
||||
const importSpec = getAncestor(token, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importSpec);
|
||||
return deleteNamedImportBinding(namedImports);
|
||||
}
|
||||
else {
|
||||
// delete import specifier
|
||||
return deleteNodeInList(token.parent);
|
||||
return [deleteNodeInList(parent)];
|
||||
}
|
||||
|
||||
// handle case where "import d, * as ns from './file'"
|
||||
// or "'import {a, b as ns} from './file'"
|
||||
case SyntaxKind.ImportClause: // this covers both 'import |d|' and 'import |d,| *'
|
||||
const importClause = <ImportClause>token.parent;
|
||||
if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'|
|
||||
const importClause = <ImportClause>parent;
|
||||
if (!importClause.namedBindings) { // |import d from './file'|
|
||||
const importDecl = getAncestor(importClause, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importDecl);
|
||||
return [deleteNode(importDecl)];
|
||||
}
|
||||
else {
|
||||
// import |d,| * as ns from './file'
|
||||
@@ -102,64 +106,70 @@ namespace ts.codefix {
|
||||
const nextToken = getTokenAtPosition(sourceFile, importClause.name.end, /*includeJsDocComment*/ false);
|
||||
if (nextToken && nextToken.kind === SyntaxKind.CommaToken) {
|
||||
// shift first non-whitespace position after comma to the start position of the node
|
||||
return deleteRange({ pos: start, end: skipTrivia(sourceFile.text, nextToken.end, /*stopAfterLineBreaks*/ false, /*stopAtComments*/ true) });
|
||||
return [deleteRange({ pos: start, end: skipTrivia(sourceFile.text, nextToken.end, /*stopAfterLineBreaks*/ false, /*stopAtComments*/ true) })];
|
||||
}
|
||||
else {
|
||||
return deleteNode(importClause.name);
|
||||
return [deleteNode(importClause.name)];
|
||||
}
|
||||
}
|
||||
|
||||
case SyntaxKind.NamespaceImport:
|
||||
const namespaceImport = <NamespaceImport>token.parent;
|
||||
if (namespaceImport.name === token && !(<ImportClause>namespaceImport.parent).name) {
|
||||
const importDecl = getAncestor(namespaceImport, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importDecl);
|
||||
}
|
||||
else {
|
||||
const previousToken = getTokenAtPosition(sourceFile, namespaceImport.pos - 1, /*includeJsDocComment*/ false);
|
||||
if (previousToken && previousToken.kind === SyntaxKind.CommaToken) {
|
||||
const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart);
|
||||
return deleteRange({ pos: startPosition, end: namespaceImport.end });
|
||||
}
|
||||
return deleteRange(namespaceImport);
|
||||
}
|
||||
return deleteNamedImportBinding(<NamespaceImport>parent);
|
||||
|
||||
default:
|
||||
return deleteDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function deleteNamedImportBinding(namedBindings: NamedImportBindings): CodeAction[] | undefined {
|
||||
if ((<ImportClause>namedBindings.parent).name) {
|
||||
// Delete named imports while preserving the default import
|
||||
// import d|, * as ns| from './file'
|
||||
// import d|, { a }| from './file'
|
||||
const previousToken = getTokenAtPosition(sourceFile, namedBindings.pos - 1, /*includeJsDocComment*/ false);
|
||||
if (previousToken && previousToken.kind === SyntaxKind.CommaToken) {
|
||||
return [deleteRange({ pos: previousToken.getStart(), end: namedBindings.end })];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
else {
|
||||
// Delete the entire import declaration
|
||||
// |import * as ns from './file'|
|
||||
// |import { a } from './file'|
|
||||
const importDecl = getAncestor(namedBindings, SyntaxKind.ImportDeclaration);
|
||||
return [deleteNode(importDecl)];
|
||||
}
|
||||
}
|
||||
|
||||
// token.parent is a variableDeclaration
|
||||
function deleteVariableDeclaration(varDecl: ts.VariableDeclaration): CodeAction[] | undefined {
|
||||
function deleteVariableDeclarationOrPrefixWithUnderscore(identifier: Identifier, varDecl: ts.VariableDeclaration): CodeAction[] | undefined {
|
||||
switch (varDecl.parent.parent.kind) {
|
||||
case SyntaxKind.ForStatement:
|
||||
const forStatement = <ForStatement>varDecl.parent.parent;
|
||||
const forInitializer = <VariableDeclarationList>forStatement.initializer;
|
||||
if (forInitializer.declarations.length === 1) {
|
||||
return deleteNode(forInitializer);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(varDecl);
|
||||
}
|
||||
return [forInitializer.declarations.length === 1 ? deleteNode(forInitializer) : deleteNodeInList(varDecl)];
|
||||
|
||||
case SyntaxKind.ForOfStatement:
|
||||
const forOfStatement = <ForOfStatement>varDecl.parent.parent;
|
||||
Debug.assert(forOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList);
|
||||
const forOfInitializer = <VariableDeclarationList>forOfStatement.initializer;
|
||||
return replaceNode(forOfInitializer.declarations[0], createObjectLiteral());
|
||||
return [
|
||||
replaceNode(forOfInitializer.declarations[0], createObjectLiteral()),
|
||||
prefixIdentifierWithUnderscore(identifier)
|
||||
];
|
||||
|
||||
case SyntaxKind.ForInStatement:
|
||||
// There is no valid fix in the case of:
|
||||
// for .. in
|
||||
return undefined;
|
||||
return [prefixIdentifierWithUnderscore(identifier)];
|
||||
|
||||
default:
|
||||
const variableStatement = <VariableStatement>varDecl.parent.parent;
|
||||
if (variableStatement.declarationList.declarations.length === 1) {
|
||||
return deleteNode(variableStatement);
|
||||
return [deleteNode(variableStatement)];
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(varDecl);
|
||||
return [deleteNodeInList(varDecl)];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,11 +194,11 @@ namespace ts.codefix {
|
||||
return makeChange(textChanges.ChangeTracker.fromCodeFixContext(context).replaceNode(sourceFile, n, newNode));
|
||||
}
|
||||
|
||||
function makeChange(changeTracker: textChanges.ChangeTracker) {
|
||||
return [{
|
||||
function makeChange(changeTracker: textChanges.ChangeTracker): CodeAction {
|
||||
return {
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Remove_declaration_for_Colon_0), { 0: token.getText() }),
|
||||
changes: changeTracker.getChanges()
|
||||
}];
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
/// <reference path="correctQualifiedNameToIndexedAccessType.ts" />
|
||||
/// <reference path="fixClassIncorrectlyImplementsInterface.ts" />
|
||||
/// <reference path="fixAddMissingMember.ts" />
|
||||
/// <reference path="fixSpelling.ts" />
|
||||
@@ -6,7 +7,8 @@
|
||||
/// <reference path="fixConstructorForDerivedNeedSuperCall.ts" />
|
||||
/// <reference path="fixExtendsInterfaceBecomesImplements.ts" />
|
||||
/// <reference path="fixForgottenThisPropertyAccess.ts" />
|
||||
/// <reference path='unusedIdentifierFixes.ts' />
|
||||
/// <reference path='fixUnusedIdentifier.ts' />
|
||||
/// <reference path='fixJSDocTypes.ts' />
|
||||
/// <reference path='importFixes.ts' />
|
||||
/// <reference path='disableJsDiagnostics.ts' />
|
||||
/// <reference path='helpers.ts' />
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace ts.codefix {
|
||||
*/
|
||||
export function createMissingMemberNodes(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: Symbol[], checker: TypeChecker): Node[] {
|
||||
const classMembers = classDeclaration.symbol.members;
|
||||
const missingMembers = possiblyMissingSymbols.filter(symbol => !classMembers.has(symbol.getName()));
|
||||
const missingMembers = possiblyMissingSymbols.filter(symbol => !classMembers.has(symbol.escapedName));
|
||||
|
||||
let newNodes: Node[] = [];
|
||||
for (const symbol of missingMembers) {
|
||||
@@ -186,7 +186,7 @@ namespace ts.codefix {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
function createMethodImplementingSignatures(signatures: Signature[], name: PropertyName, optional: boolean, modifiers: Modifier[] | undefined): MethodDeclaration {
|
||||
function createMethodImplementingSignatures(signatures: ReadonlyArray<Signature>, name: PropertyName, optional: boolean, modifiers: ReadonlyArray<Modifier> | undefined): MethodDeclaration {
|
||||
/** This is *a* signature with the maximal number of arguments,
|
||||
* such that if there is a "maximal" signature without rest arguments,
|
||||
* this is one of them.
|
||||
@@ -205,7 +205,7 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
const maxNonRestArgs = maxArgsSignature.parameters.length - (maxArgsSignature.hasRestParameter ? 1 : 0);
|
||||
const maxArgsParameterSymbolNames = maxArgsSignature.parameters.map(symbol => symbol.getName());
|
||||
const maxArgsParameterSymbolNames = maxArgsSignature.parameters.map(symbol => symbol.name);
|
||||
|
||||
const parameters = createDummyParameters(maxNonRestArgs, maxArgsParameterSymbolNames, minArgumentCount, /*addAnyType*/ true);
|
||||
|
||||
@@ -231,7 +231,13 @@ namespace ts.codefix {
|
||||
/*returnType*/ undefined);
|
||||
}
|
||||
|
||||
export function createStubbedMethod(modifiers: Modifier[], name: PropertyName, optional: boolean, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], returnType: TypeNode | undefined) {
|
||||
export function createStubbedMethod(
|
||||
modifiers: ReadonlyArray<Modifier>,
|
||||
name: PropertyName,
|
||||
optional: boolean,
|
||||
typeParameters: ReadonlyArray<TypeParameterDeclaration> | undefined,
|
||||
parameters: ReadonlyArray<ParameterDeclaration>,
|
||||
returnType: TypeNode | undefined) {
|
||||
return createMethod(
|
||||
/*decorators*/ undefined,
|
||||
modifiers,
|
||||
|
||||
@@ -171,15 +171,18 @@ namespace ts.codefix {
|
||||
const defaultExport = checker.tryGetMemberInModuleExports("default", moduleSymbol);
|
||||
if (defaultExport) {
|
||||
const localSymbol = getLocalSymbolForExportDefault(defaultExport);
|
||||
if (localSymbol && localSymbol.name === name && checkSymbolHasMeaning(localSymbol, currentTokenMeaning)) {
|
||||
if (localSymbol && localSymbol.escapedName === name && checkSymbolHasMeaning(localSymbol, currentTokenMeaning)) {
|
||||
// check if this symbol is already used
|
||||
const symbolId = getUniqueSymbolId(localSymbol);
|
||||
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, name, /*isDefault*/ true));
|
||||
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, name, /*isNamespaceImport*/ true));
|
||||
}
|
||||
}
|
||||
|
||||
// "default" is a keyword and not a legal identifier for the import, so we don't expect it here
|
||||
Debug.assert(name !== "default");
|
||||
|
||||
// check exports with the same name
|
||||
const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExports(name, moduleSymbol);
|
||||
const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExportsAndProperties(name, moduleSymbol);
|
||||
if (exportSymbolWithIdenticalName && checkSymbolHasMeaning(exportSymbolWithIdenticalName, currentTokenMeaning)) {
|
||||
const symbolId = getUniqueSymbolId(exportSymbolWithIdenticalName);
|
||||
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, name));
|
||||
@@ -222,10 +225,7 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function getUniqueSymbolId(symbol: Symbol) {
|
||||
if (symbol.flags & SymbolFlags.Alias) {
|
||||
return getSymbolId(checker.getAliasedSymbol(symbol));
|
||||
}
|
||||
return getSymbolId(symbol);
|
||||
return getSymbolId(skipAlias(symbol, checker));
|
||||
}
|
||||
|
||||
function checkSymbolHasMeaning(symbol: Symbol, meaning: SemanticMeaning) {
|
||||
@@ -504,45 +504,120 @@ namespace ts.codefix {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const indexOfNodeModules = moduleFileName.indexOf("node_modules");
|
||||
if (indexOfNodeModules < 0) {
|
||||
const parts = getNodeModulePathParts(moduleFileName);
|
||||
|
||||
if (!parts) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let relativeFileName: string;
|
||||
if (sourceDirectory.indexOf(moduleFileName.substring(0, indexOfNodeModules - 1)) === 0) {
|
||||
// if node_modules folder is in this folder or any of its parent folder, no need to keep it.
|
||||
relativeFileName = moduleFileName.substring(indexOfNodeModules + 13 /* "node_modules\".length */);
|
||||
}
|
||||
else {
|
||||
relativeFileName = getRelativePath(moduleFileName, sourceDirectory);
|
||||
}
|
||||
// Simplify the full file path to something that can be resolved by Node.
|
||||
|
||||
relativeFileName = removeFileExtension(relativeFileName);
|
||||
if (endsWith(relativeFileName, "/index")) {
|
||||
relativeFileName = getDirectoryPath(relativeFileName);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
const moduleDirectory = getDirectoryPath(moduleFileName);
|
||||
const packageJsonContent = JSON.parse(context.host.readFile(combinePaths(moduleDirectory, "package.json")));
|
||||
// If the module could be imported by a directory name, use that directory's name
|
||||
let moduleSpecifier = getDirectoryOrExtensionlessFileName(moduleFileName);
|
||||
// Get a path that's relative to node_modules or the importing file's path
|
||||
moduleSpecifier = getNodeResolvablePath(moduleSpecifier);
|
||||
// If the module was found in @types, get the actual Node package name
|
||||
return getPackageNameFromAtTypesDirectory(moduleSpecifier);
|
||||
|
||||
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 (context.host.fileExists(packageJsonPath)) {
|
||||
const packageJsonContent = JSON.parse(context.host.readFile(packageJsonPath));
|
||||
if (packageJsonContent) {
|
||||
const mainFile = packageJsonContent.main || packageJsonContent.typings;
|
||||
if (mainFile) {
|
||||
const mainExportFile = toPath(mainFile, moduleDirectory, getCanonicalFileName);
|
||||
if (removeFileExtension(mainExportFile) === removeFileExtension(moduleFileName)) {
|
||||
relativeFileName = getDirectoryPath(relativeFileName);
|
||||
const mainFileRelative = packageJsonContent.typings || packageJsonContent.types || packageJsonContent.main;
|
||||
if (mainFileRelative) {
|
||||
const mainExportFile = toPath(mainFileRelative, packageRootPath, getCanonicalFileName);
|
||||
if (mainExportFile === getCanonicalFileName(path)) {
|
||||
return packageRootPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) { }
|
||||
|
||||
// We still have a file name - remove the extension
|
||||
const fullModulePathWithoutExtension = removeFileExtension(path);
|
||||
|
||||
// If the file is /index, it can be imported by its directory name
|
||||
if (getCanonicalFileName(fullModulePathWithoutExtension.substring(parts.fileNameIndex)) === "/index") {
|
||||
return fullModulePathWithoutExtension.substring(0, parts.fileNameIndex);
|
||||
}
|
||||
|
||||
return fullModulePathWithoutExtension;
|
||||
}
|
||||
|
||||
return getPackageNameFromAtTypesDirectory(relativeFileName);
|
||||
function getNodeResolvablePath(path: string): string {
|
||||
const basePath = path.substring(0, parts.topLevelNodeModulesIndex);
|
||||
if (sourceDirectory.indexOf(basePath) === 0) {
|
||||
// if node_modules folder is in this folder or any of its parent folders, no need to keep it.
|
||||
return path.substring(parts.topLevelPackageNameIndex + 1);
|
||||
}
|
||||
else {
|
||||
return getRelativePath(path, sourceDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getNodeModulePathParts(fullPath: string) {
|
||||
// If fullPath can't be valid module file within node_modules, returns undefined.
|
||||
// Example of expected pattern: /base/path/node_modules/[@scope/otherpackage/@otherscope/node_modules/]package/[subdirectory/]file.js
|
||||
// Returns indices: ^ ^ ^ ^
|
||||
|
||||
let topLevelNodeModulesIndex = 0;
|
||||
let topLevelPackageNameIndex = 0;
|
||||
let packageRootIndex = 0;
|
||||
let fileNameIndex = 0;
|
||||
|
||||
const enum States {
|
||||
BeforeNodeModules,
|
||||
NodeModules,
|
||||
Scope,
|
||||
PackageContent
|
||||
}
|
||||
|
||||
let partStart = 0;
|
||||
let partEnd = 0;
|
||||
let state = States.BeforeNodeModules;
|
||||
|
||||
while (partEnd >= 0) {
|
||||
partStart = partEnd;
|
||||
partEnd = fullPath.indexOf("/", partStart + 1);
|
||||
switch (state) {
|
||||
case States.BeforeNodeModules:
|
||||
if (fullPath.indexOf("/node_modules/", partStart) === partStart) {
|
||||
topLevelNodeModulesIndex = partStart;
|
||||
topLevelPackageNameIndex = partEnd;
|
||||
state = States.NodeModules;
|
||||
}
|
||||
break;
|
||||
case States.NodeModules:
|
||||
case States.Scope:
|
||||
if (state === States.NodeModules && fullPath.charAt(partStart + 1) === "@") {
|
||||
state = States.Scope;
|
||||
}
|
||||
else {
|
||||
packageRootIndex = partEnd;
|
||||
state = States.PackageContent;
|
||||
}
|
||||
break;
|
||||
case States.PackageContent:
|
||||
if (fullPath.indexOf("/node_modules/", partStart) === partStart) {
|
||||
state = States.NodeModules;
|
||||
}
|
||||
else {
|
||||
state = States.PackageContent;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fileNameIndex = partStart;
|
||||
|
||||
return state > States.NodeModules ? { topLevelNodeModulesIndex, topLevelPackageNameIndex, packageRootIndex, fileNameIndex } : undefined;
|
||||
}
|
||||
|
||||
function getPathRelativeToRootDirs(path: string, rootDirs: string[]) {
|
||||
for (const rootDir of rootDirs) {
|
||||
const relativeName = getRelativePathIfInDirectory(path, rootDir);
|
||||
@@ -568,7 +643,7 @@ namespace ts.codefix {
|
||||
|
||||
function getRelativePath(path: string, directoryPath: string) {
|
||||
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
||||
return moduleHasNonRelativeName(relativePath) ? "./" + relativePath : relativePath;
|
||||
return !pathIsRelative(relativePath) ? "./" + relativePath : relativePath;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+478
-238
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,26 @@
|
||||
/* @internal */
|
||||
namespace ts.DocumentHighlights {
|
||||
export function getDocumentHighlights(program: Program, cancellationToken: CancellationToken, sourceFile: SourceFile, position: number, sourceFilesToSearch: SourceFile[]): DocumentHighlights[] {
|
||||
export function getDocumentHighlights(program: Program, cancellationToken: CancellationToken, sourceFile: SourceFile, position: number, sourceFilesToSearch: SourceFile[]): DocumentHighlights[] | undefined {
|
||||
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
return node && (getSemanticDocumentHighlights(node, program, cancellationToken, sourceFilesToSearch) || getSyntacticDocumentHighlights(node, sourceFile));
|
||||
// Note that getTouchingWord indicates failure by returning the sourceFile node.
|
||||
if (node === sourceFile) return undefined;
|
||||
|
||||
Debug.assert(node.parent !== undefined);
|
||||
|
||||
if (isJsxOpeningElement(node.parent) && node.parent.tagName === node || isJsxClosingElement(node.parent)) {
|
||||
// For a JSX element, just highlight the matching tag, not all references.
|
||||
const { openingElement, closingElement } = node.parent.parent;
|
||||
const highlightSpans = [openingElement, closingElement].map(({ tagName }) => getHighlightSpanForNode(tagName, sourceFile));
|
||||
return [{ fileName: sourceFile.fileName, highlightSpans }];
|
||||
}
|
||||
|
||||
return getSemanticDocumentHighlights(node, program, cancellationToken, sourceFilesToSearch) || getSyntacticDocumentHighlights(node, sourceFile);
|
||||
}
|
||||
|
||||
function getHighlightSpanForNode(node: Node, sourceFile: SourceFile): HighlightSpan {
|
||||
const start = node.getStart(sourceFile);
|
||||
const end = node.getEnd();
|
||||
|
||||
return {
|
||||
fileName: sourceFile.fileName,
|
||||
textSpan: createTextSpanFromBounds(start, end),
|
||||
textSpan: createTextSpanFromNode(node, sourceFile),
|
||||
kind: HighlightSpanKind.none
|
||||
};
|
||||
}
|
||||
@@ -289,21 +298,20 @@ namespace ts.DocumentHighlights {
|
||||
const keywords: Node[] = [];
|
||||
const modifierFlag: ModifierFlags = getFlagFromModifier(modifier);
|
||||
|
||||
let nodes: Node[];
|
||||
let nodes: ReadonlyArray<Node>;
|
||||
switch (container.kind) {
|
||||
case SyntaxKind.ModuleBlock:
|
||||
case SyntaxKind.SourceFile:
|
||||
// Container is either a class declaration or the declaration is a classDeclaration
|
||||
if (modifierFlag & ModifierFlags.Abstract) {
|
||||
nodes = (<Node[]>(<ClassDeclaration>declaration).members).concat(declaration);
|
||||
nodes = [...(<ClassDeclaration>declaration).members, declaration];
|
||||
}
|
||||
else {
|
||||
nodes = (<Block>container).statements;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.Constructor:
|
||||
nodes = (<Node[]>(<ConstructorDeclaration>container).parameters).concat(
|
||||
(<ClassDeclaration>container.parent).members);
|
||||
nodes = [...(<ConstructorDeclaration>container).parameters, ...(<ClassDeclaration>container.parent).members];
|
||||
break;
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
@@ -317,11 +325,11 @@ namespace ts.DocumentHighlights {
|
||||
});
|
||||
|
||||
if (constructor) {
|
||||
nodes = nodes.concat(constructor.parameters);
|
||||
nodes = [...nodes, ...constructor.parameters];
|
||||
}
|
||||
}
|
||||
else if (modifierFlag & ModifierFlags.Abstract) {
|
||||
nodes = nodes.concat(container);
|
||||
nodes = [...nodes, container];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -598,7 +606,7 @@ namespace ts.DocumentHighlights {
|
||||
*/
|
||||
function isLabeledBy(node: Node, labelName: string) {
|
||||
for (let owner = node.parent; owner.kind === SyntaxKind.LabeledStatement; owner = owner.parent) {
|
||||
if ((<LabeledStatement>owner).label.text === labelName) {
|
||||
if ((<LabeledStatement>owner).label.escapedText === labelName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,17 +105,17 @@ namespace ts {
|
||||
export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory = ""): DocumentRegistry {
|
||||
// Maps from compiler setting target (ES3, ES5, etc.) to all the cached documents we have
|
||||
// for those settings.
|
||||
const buckets = createMap<FileMap<DocumentRegistryEntry>>();
|
||||
const buckets = createMap<Map<DocumentRegistryEntry>>();
|
||||
const getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames);
|
||||
|
||||
function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey {
|
||||
return <DocumentRegistryBucketKey>`_${settings.target}|${settings.module}|${settings.noResolve}|${settings.jsx}|${settings.allowJs}|${settings.baseUrl}|${JSON.stringify(settings.typeRoots)}|${JSON.stringify(settings.rootDirs)}|${JSON.stringify(settings.paths)}`;
|
||||
}
|
||||
|
||||
function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
|
||||
function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): Map<DocumentRegistryEntry> {
|
||||
let bucket = buckets.get(key);
|
||||
if (!bucket && createIfMissing) {
|
||||
buckets.set(key, bucket = createFileMap<DocumentRegistryEntry>());
|
||||
buckets.set(key, bucket = createMap<DocumentRegistryEntry>());
|
||||
}
|
||||
return bucket;
|
||||
}
|
||||
@@ -124,9 +124,9 @@ namespace ts {
|
||||
const bucketInfoArray = arrayFrom(buckets.keys()).filter(name => name && name.charAt(0) === "_").map(name => {
|
||||
const entries = buckets.get(name);
|
||||
const sourceFiles: { name: string; refCount: number; references: string[]; }[] = [];
|
||||
entries.forEachValue((key, entry) => {
|
||||
entries.forEach((entry, name) => {
|
||||
sourceFiles.push({
|
||||
name: key,
|
||||
name,
|
||||
refCount: entry.languageServiceRefCount,
|
||||
references: entry.owners.slice(0)
|
||||
});
|
||||
@@ -179,7 +179,7 @@ namespace ts {
|
||||
const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false, scriptKind);
|
||||
|
||||
entry = {
|
||||
sourceFile: sourceFile,
|
||||
sourceFile,
|
||||
languageServiceRefCount: 0,
|
||||
owners: []
|
||||
};
|
||||
@@ -222,7 +222,7 @@ namespace ts {
|
||||
|
||||
Debug.assert(entry.languageServiceRefCount >= 0);
|
||||
if (entry.languageServiceRefCount === 0) {
|
||||
bucket.remove(path);
|
||||
bucket.delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ namespace ts.FindAllReferences.Core {
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
return true;
|
||||
case SyntaxKind.CallExpression:
|
||||
return isRequireCall(node.parent as CallExpression, /*checkArgumentIsStringLiteral*/ false);
|
||||
return isRequireCall(node.parent as CallExpression, /*checkArgumentIsStringLiteral*/ false) || isImportCall(node.parent as CallExpression);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -432,7 +432,7 @@ namespace ts.FindAllReferences.Core {
|
||||
readonly location: Node;
|
||||
readonly symbol: Symbol;
|
||||
readonly text: string;
|
||||
readonly escapedText: string;
|
||||
readonly escapedText: __String;
|
||||
/** Only set if `options.implementations` is true. These are the symbols checked to get the implementations of a property access. */
|
||||
readonly parents: Symbol[] | undefined;
|
||||
|
||||
@@ -494,13 +494,12 @@ namespace ts.FindAllReferences.Core {
|
||||
createSearch(location: Node, symbol: Symbol, comingFrom: ImportExport | undefined, searchOptions: { text?: string, allSearchSymbols?: Symbol[] } = {}): Search {
|
||||
// Note: if this is an external module symbol, the name doesn't include quotes.
|
||||
const { text = stripQuotes(getDeclaredName(this.checker, symbol, location)), allSearchSymbols = undefined } = searchOptions;
|
||||
const escapedText = escapeIdentifier(text);
|
||||
const escapedText = escapeLeadingUnderscores(text);
|
||||
const parents = this.options.implementations && getParentSymbolsOfPropertyAccess(location, symbol, this.checker);
|
||||
return { location, symbol, comingFrom, text, escapedText, parents, includes };
|
||||
|
||||
function includes(referenceSymbol: Symbol): boolean {
|
||||
return allSearchSymbols ? contains(allSearchSymbols, referenceSymbol) : referenceSymbol === symbol;
|
||||
}
|
||||
return {
|
||||
location, symbol, comingFrom, text, escapedText, parents,
|
||||
includes: referenceSymbol => allSearchSymbols ? contains(allSearchSymbols, referenceSymbol) : referenceSymbol === symbol,
|
||||
};
|
||||
}
|
||||
|
||||
private readonly symbolIdToReferences: Entry[][] = [];
|
||||
@@ -594,20 +593,27 @@ namespace ts.FindAllReferences.Core {
|
||||
checker.getPropertySymbolOfDestructuringAssignment(<Identifier>location);
|
||||
}
|
||||
|
||||
function isObjectBindingPatternElementWithoutPropertyName(symbol: Symbol): boolean {
|
||||
function getObjectBindingElementWithoutPropertyName(symbol: Symbol): BindingElement | undefined {
|
||||
const bindingElement = getDeclarationOfKind<BindingElement>(symbol, SyntaxKind.BindingElement);
|
||||
return bindingElement &&
|
||||
if (bindingElement &&
|
||||
bindingElement.parent.kind === SyntaxKind.ObjectBindingPattern &&
|
||||
!bindingElement.propertyName;
|
||||
!bindingElement.propertyName) {
|
||||
return bindingElement;
|
||||
}
|
||||
}
|
||||
|
||||
function getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol: Symbol, checker: TypeChecker): Symbol | undefined {
|
||||
if (isObjectBindingPatternElementWithoutPropertyName(symbol)) {
|
||||
const bindingElement = getDeclarationOfKind<BindingElement>(symbol, SyntaxKind.BindingElement);
|
||||
const typeOfPattern = checker.getTypeAtLocation(bindingElement.parent);
|
||||
return typeOfPattern && checker.getPropertyOfType(typeOfPattern, (<Identifier>bindingElement.name).text);
|
||||
const bindingElement = getObjectBindingElementWithoutPropertyName(symbol);
|
||||
if (!bindingElement) return undefined;
|
||||
|
||||
const typeOfPattern = checker.getTypeAtLocation(bindingElement.parent);
|
||||
const propSymbol = typeOfPattern && checker.getPropertyOfType(typeOfPattern, (<Identifier>bindingElement.name).text);
|
||||
if (propSymbol && propSymbol.flags & SymbolFlags.Accessor) {
|
||||
// See GH#16922
|
||||
Debug.assert(!!(propSymbol.flags & SymbolFlags.Transient));
|
||||
return (propSymbol as TransientSymbol).target;
|
||||
}
|
||||
return undefined;
|
||||
return propSymbol;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -642,7 +648,7 @@ namespace ts.FindAllReferences.Core {
|
||||
|
||||
// If symbol is of object binding pattern element without property name we would want to
|
||||
// look for property too and that could be anywhere
|
||||
if (isObjectBindingPatternElementWithoutPropertyName(symbol)) {
|
||||
if (getObjectBindingElementWithoutPropertyName(symbol)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -679,9 +685,7 @@ namespace ts.FindAllReferences.Core {
|
||||
return parent ? scope.getSourceFile() : scope;
|
||||
}
|
||||
|
||||
function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile, fullStart = false): number[] {
|
||||
const start = fullStart ? container.getFullStart() : container.getStart(sourceFile);
|
||||
const end = container.getEnd();
|
||||
function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): number[] {
|
||||
const positions: number[] = [];
|
||||
|
||||
/// TODO: Cache symbol existence for files to save text search
|
||||
@@ -696,10 +700,10 @@ namespace ts.FindAllReferences.Core {
|
||||
const sourceLength = text.length;
|
||||
const symbolNameLength = symbolName.length;
|
||||
|
||||
let position = text.indexOf(symbolName, start);
|
||||
let position = text.indexOf(symbolName, container.pos);
|
||||
while (position >= 0) {
|
||||
// If we are past the end, stop looking
|
||||
if (position > end) break;
|
||||
if (position > container.end) break;
|
||||
|
||||
// We found a match. Make sure it's not part of a larger word (i.e. the char
|
||||
// before and after it have to be a non-identifier char).
|
||||
@@ -736,7 +740,7 @@ namespace ts.FindAllReferences.Core {
|
||||
// Compare the length so we filter out strict superstrings of the symbol we are looking for
|
||||
switch (node && node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
return unescapeIdentifier((node as Identifier).text).length === searchSymbolName.length;
|
||||
return (node as Identifier).text.length === searchSymbolName.length;
|
||||
|
||||
case SyntaxKind.StringLiteral:
|
||||
return (isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) &&
|
||||
@@ -760,8 +764,7 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
function addReferencesForKeywordInFile(sourceFile: SourceFile, kind: SyntaxKind, searchText: string, references: Push<NodeEntry>): void {
|
||||
// Want fullStart so we can find the symbol in JSDoc comments
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, sourceFile, /*fullStart*/ true);
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, sourceFile);
|
||||
for (const position of possiblePositions) {
|
||||
const referenceLocation = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
if (referenceLocation.kind === kind) {
|
||||
@@ -785,8 +788,7 @@ namespace ts.FindAllReferences.Core {
|
||||
return;
|
||||
}
|
||||
|
||||
const fullStart = state.options.findInComments || container.jsDoc !== undefined || forEach(search.symbol.declarations, d => d.kind === ts.SyntaxKind.JSDocTypedefTag);
|
||||
for (const position of getPossibleSymbolReferencePositions(sourceFile, search.text, container, fullStart)) {
|
||||
for (const position of getPossibleSymbolReferencePositions(sourceFile, search.text, container)) {
|
||||
getReferencesAtLocation(sourceFile, position, search, state);
|
||||
}
|
||||
}
|
||||
@@ -982,7 +984,7 @@ namespace ts.FindAllReferences.Core {
|
||||
* Reference the constructor and all calls to `new this()`.
|
||||
*/
|
||||
function findOwnConstructorReferences(classSymbol: Symbol, sourceFile: SourceFile, addNode: (node: Node) => void): void {
|
||||
for (const decl of classSymbol.members.get("__constructor").declarations) {
|
||||
for (const decl of classSymbol.members.get(InternalSymbolName.Constructor).declarations) {
|
||||
const ctrKeyword = ts.findChildOfKind(decl, ts.SyntaxKind.ConstructorKeyword, sourceFile)!;
|
||||
Debug.assert(decl.kind === SyntaxKind.Constructor && !!ctrKeyword);
|
||||
addNode(ctrKeyword);
|
||||
@@ -1006,7 +1008,7 @@ namespace ts.FindAllReferences.Core {
|
||||
/** Find references to `super` in the constructor of an extending class. */
|
||||
function findSuperConstructorAccesses(cls: ClassLikeDeclaration, addNode: (node: Node) => void): void {
|
||||
const symbol = cls.symbol;
|
||||
const ctr = symbol.members.get("__constructor");
|
||||
const ctr = symbol.members.get(InternalSymbolName.Constructor);
|
||||
if (!ctr) {
|
||||
return;
|
||||
}
|
||||
@@ -1054,16 +1056,17 @@ namespace ts.FindAllReferences.Core {
|
||||
if (isVariableLike(parent) && parent.type === containingTypeReference && parent.initializer && isImplementationExpression(parent.initializer)) {
|
||||
addReference(parent.initializer);
|
||||
}
|
||||
else if (isFunctionLike(parent) && parent.type === containingTypeReference && parent.body) {
|
||||
if (parent.body.kind === SyntaxKind.Block) {
|
||||
forEachReturnStatement(<Block>parent.body, returnStatement => {
|
||||
else if (isFunctionLike(parent) && parent.type === containingTypeReference && (parent as FunctionLikeDeclaration).body) {
|
||||
const body = (parent as FunctionLikeDeclaration).body;
|
||||
if (body.kind === SyntaxKind.Block) {
|
||||
forEachReturnStatement(<Block>body, returnStatement => {
|
||||
if (returnStatement.expression && isImplementationExpression(returnStatement.expression)) {
|
||||
addReference(returnStatement.expression);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (isImplementationExpression(<Expression>parent.body)) {
|
||||
addReference(parent.body);
|
||||
else if (isImplementationExpression(<Expression>body)) {
|
||||
addReference(body);
|
||||
}
|
||||
}
|
||||
else if (isAssertionExpression(parent) && isImplementationExpression(parent.expression)) {
|
||||
@@ -1438,7 +1441,7 @@ namespace ts.FindAllReferences.Core {
|
||||
|
||||
// Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions
|
||||
if (!implementations && rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
|
||||
getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result, /*previousIterationSymbolsCache*/ createMap<Symbol>(), checker);
|
||||
getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), checker);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1469,7 +1472,7 @@ namespace ts.FindAllReferences.Core {
|
||||
// the function will add any found symbol of the property-name, then its sub-routine will call
|
||||
// getPropertySymbolsFromBaseTypes again to walk up any base types to prevent revisiting already
|
||||
// visited symbol, interface "C", the sub-routine will pass the current symbol as previousIterationSymbol.
|
||||
if (previousIterationSymbolsCache.has(symbol.name)) {
|
||||
if (previousIterationSymbolsCache.has(symbol.escapedName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1496,7 +1499,7 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
// Visit the typeReference as well to see if it directly or indirectly use that property
|
||||
previousIterationSymbolsCache.set(symbol.name, symbol);
|
||||
previousIterationSymbolsCache.set(symbol.escapedName, symbol);
|
||||
getPropertySymbolsFromBaseTypes(type.symbol, propertyName, result, previousIterationSymbolsCache, checker);
|
||||
}
|
||||
}
|
||||
@@ -1556,7 +1559,7 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
const result: Symbol[] = [];
|
||||
getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result, /*previousIterationSymbolsCache*/ createMap<Symbol>(), state.checker);
|
||||
getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), state.checker);
|
||||
return find(result, search.includes);
|
||||
}
|
||||
|
||||
@@ -1573,7 +1576,7 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
return (<Identifier | LiteralExpression>node.name).text;
|
||||
return getTextOfIdentifierOrLiteral(node.name);
|
||||
}
|
||||
|
||||
/** Gets all symbols for one property. Does not get symbols for every property. */
|
||||
@@ -1648,7 +1651,7 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
}
|
||||
else if (isFunctionLike(node)) {
|
||||
return !!node.body || hasModifier(node, ModifierFlags.Ambient);
|
||||
return !!(node as FunctionLikeDeclaration).body || hasModifier(node, ModifierFlags.Ambient);
|
||||
}
|
||||
else {
|
||||
switch (node.kind) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user