mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' of github.com:Microsoft/TypeScript into rest-param-destructuring
This commit is contained in:
@@ -2072,7 +2072,10 @@ namespace ts {
|
||||
if (isSpecialPropertyDeclaration(node as PropertyAccessExpression)) {
|
||||
bindSpecialPropertyDeclaration(node as PropertyAccessExpression);
|
||||
}
|
||||
if (isInJavaScriptFile(node) && isModuleExportsPropertyAccessExpression(node as PropertyAccessExpression)) {
|
||||
if (isInJavaScriptFile(node) &&
|
||||
file.commonJsModuleIndicator &&
|
||||
isModuleExportsPropertyAccessExpression(node as PropertyAccessExpression) &&
|
||||
!lookupSymbolForNameWorker(container, "module" as __String)) {
|
||||
declareSymbol(container.locals!, /*parent*/ undefined, (node as PropertyAccessExpression).expression as Identifier,
|
||||
SymbolFlags.FunctionScopedVariable | SymbolFlags.ModuleExports, SymbolFlags.FunctionScopedVariableExcludes);
|
||||
}
|
||||
|
||||
+352
-235
@@ -198,13 +198,10 @@ namespace ts {
|
||||
},
|
||||
isContextSensitive,
|
||||
getFullyQualifiedName,
|
||||
getResolvedSignature: (nodeIn, candidatesOutArray, theArgumentCount) => {
|
||||
const node = getParseTreeNode(nodeIn, isCallLikeExpression);
|
||||
apparentArgumentCount = theArgumentCount;
|
||||
const res = node ? getResolvedSignature(node, candidatesOutArray) : undefined;
|
||||
apparentArgumentCount = undefined;
|
||||
return res;
|
||||
},
|
||||
getResolvedSignature: (node, candidatesOutArray, agumentCount) =>
|
||||
getResolvedSignatureWorker(node, candidatesOutArray, agumentCount, /*isForSignatureHelp*/ false),
|
||||
getResolvedSignatureForSignatureHelp: (node, candidatesOutArray, agumentCount) =>
|
||||
getResolvedSignatureWorker(node, candidatesOutArray, agumentCount, /*isForSignatureHelp*/ true),
|
||||
getConstantValue: nodeIn => {
|
||||
const node = getParseTreeNode(nodeIn, canHaveConstantValue);
|
||||
return node ? getConstantValue(node) : undefined;
|
||||
@@ -354,6 +351,14 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
|
||||
function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, isForSignatureHelp: boolean): Signature | undefined {
|
||||
const node = getParseTreeNode(nodeIn, isCallLikeExpression);
|
||||
apparentArgumentCount = argumentCount;
|
||||
const res = node ? getResolvedSignature(node, candidatesOutArray, isForSignatureHelp) : undefined;
|
||||
apparentArgumentCount = undefined;
|
||||
return res;
|
||||
}
|
||||
|
||||
const tupleTypes = createMap<GenericType>();
|
||||
const unionTypes = createMap<UnionType>();
|
||||
const intersectionTypes = createMap<IntersectionType>();
|
||||
@@ -1834,7 +1839,7 @@ namespace ts {
|
||||
if (symbol.flags & SymbolFlags.Variable) {
|
||||
const typeAnnotation = (<VariableDeclaration>symbol.valueDeclaration).type;
|
||||
if (typeAnnotation) {
|
||||
return resolveSymbol(getPropertyOfType(getTypeFromTypeNode(typeAnnotation), name)); // TODO: GH#18217
|
||||
return resolveSymbol(getPropertyOfType(getTypeFromTypeNode(typeAnnotation), name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3883,14 +3888,15 @@ namespace ts {
|
||||
const links = getSymbolLinks(symbol);
|
||||
let specifier = links.specifierCache && links.specifierCache.get(contextFile.path);
|
||||
if (!specifier) {
|
||||
specifier = flatten(moduleSpecifiers.getModuleSpecifiers(
|
||||
specifier = moduleSpecifiers.getModuleSpecifierForDeclarationFile(
|
||||
symbol,
|
||||
compilerOptions,
|
||||
contextFile,
|
||||
context.tracker.moduleResolverHost,
|
||||
context.tracker.moduleResolverHost.getSourceFiles!(), // TODO: GH#18217
|
||||
{ importModuleSpecifierPreference: "non-relative" }
|
||||
))[0];
|
||||
{ importModuleSpecifierPreference: "non-relative" },
|
||||
host.redirectTargetsMap,
|
||||
);
|
||||
links.specifierCache = links.specifierCache || createMap();
|
||||
links.specifierCache.set(contextFile.path, specifier);
|
||||
}
|
||||
@@ -4988,86 +4994,87 @@ namespace ts {
|
||||
|
||||
function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type {
|
||||
const links = getSymbolLinks(symbol);
|
||||
if (!links.type) {
|
||||
// Handle prototype property
|
||||
if (symbol.flags & SymbolFlags.Prototype) {
|
||||
return links.type = getTypeOfPrototypeProperty(symbol);
|
||||
}
|
||||
// CommonsJS require and module both have type any.
|
||||
if (symbol === requireSymbol) {
|
||||
return links.type = anyType;
|
||||
}
|
||||
if (symbol.flags & SymbolFlags.ModuleExports) {
|
||||
const fileSymbol = getSymbolOfNode(getSourceFileOfNode(symbol.valueDeclaration));
|
||||
const members = createSymbolTable();
|
||||
members.set("exports" as __String, fileSymbol);
|
||||
return links.type = createAnonymousType(symbol, members, emptyArray, emptyArray, undefined, undefined);
|
||||
}
|
||||
// Handle catch clause variables
|
||||
const declaration = symbol.valueDeclaration;
|
||||
if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) {
|
||||
return links.type = anyType;
|
||||
}
|
||||
// Handle export default expressions
|
||||
if (isSourceFile(declaration)) {
|
||||
const jsonSourceFile = cast(declaration, isJsonSourceFile);
|
||||
return links.type = jsonSourceFile.statements.length ? checkExpression(jsonSourceFile.statements[0].expression) : emptyObjectType;
|
||||
}
|
||||
if (declaration.kind === SyntaxKind.ExportAssignment) {
|
||||
return links.type = checkExpression((<ExportAssignment>declaration).expression);
|
||||
}
|
||||
// Handle variable, parameter or property
|
||||
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
|
||||
return errorType;
|
||||
}
|
||||
|
||||
let type = getJSSpecialType(symbol, declaration);
|
||||
if (!type) {
|
||||
if (isJSDocPropertyLikeTag(declaration)
|
||||
|| isPropertyAccessExpression(declaration)
|
||||
|| isIdentifier(declaration)
|
||||
|| isClassDeclaration(declaration)
|
||||
|| isFunctionDeclaration(declaration)
|
||||
|| (isMethodDeclaration(declaration) && !isObjectLiteralMethod(declaration))
|
||||
|| isMethodSignature(declaration)) {
|
||||
// Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty`
|
||||
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
|
||||
return getTypeOfFuncClassEnumModule(symbol);
|
||||
}
|
||||
type = tryGetTypeFromEffectiveTypeNode(declaration) || anyType;
|
||||
}
|
||||
else if (isPropertyAssignment(declaration)) {
|
||||
type = tryGetTypeFromEffectiveTypeNode(declaration) || checkPropertyAssignment(declaration);
|
||||
}
|
||||
else if (isJsxAttribute(declaration)) {
|
||||
type = tryGetTypeFromEffectiveTypeNode(declaration) || checkJsxAttribute(declaration);
|
||||
}
|
||||
else if (isShorthandPropertyAssignment(declaration)) {
|
||||
type = tryGetTypeFromEffectiveTypeNode(declaration) || checkExpressionForMutableLocation(declaration.name, CheckMode.Normal);
|
||||
}
|
||||
else if (isObjectLiteralMethod(declaration)) {
|
||||
type = tryGetTypeFromEffectiveTypeNode(declaration) || checkObjectLiteralMethod(declaration, CheckMode.Normal);
|
||||
}
|
||||
else if (isParameter(declaration)
|
||||
|| isPropertyDeclaration(declaration)
|
||||
|| isPropertySignature(declaration)
|
||||
|| isVariableDeclaration(declaration)
|
||||
|| isBindingElement(declaration)) {
|
||||
type = getWidenedTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true);
|
||||
}
|
||||
else {
|
||||
return Debug.fail("Unhandled declaration kind! " + Debug.showSyntaxKind(declaration) + " for " + Debug.showSymbol(symbol));
|
||||
}
|
||||
}
|
||||
|
||||
if (!popTypeResolution()) {
|
||||
type = reportCircularityError(symbol);
|
||||
}
|
||||
links.type = type;
|
||||
}
|
||||
return links.type;
|
||||
return links.type || (links.type = getTypeOfVariableOrParameterOrPropertyWorker(symbol));
|
||||
}
|
||||
|
||||
function getTypeOfVariableOrParameterOrPropertyWorker(symbol: Symbol) {
|
||||
// Handle prototype property
|
||||
if (symbol.flags & SymbolFlags.Prototype) {
|
||||
return getTypeOfPrototypeProperty(symbol);
|
||||
}
|
||||
// CommonsJS require and module both have type any.
|
||||
if (symbol === requireSymbol) {
|
||||
return anyType;
|
||||
}
|
||||
if (symbol.flags & SymbolFlags.ModuleExports) {
|
||||
const fileSymbol = getSymbolOfNode(getSourceFileOfNode(symbol.valueDeclaration));
|
||||
const members = createSymbolTable();
|
||||
members.set("exports" as __String, fileSymbol);
|
||||
return createAnonymousType(symbol, members, emptyArray, emptyArray, undefined, undefined);
|
||||
}
|
||||
// Handle catch clause variables
|
||||
const declaration = symbol.valueDeclaration;
|
||||
if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) {
|
||||
return anyType;
|
||||
}
|
||||
// Handle export default expressions
|
||||
if (isSourceFile(declaration)) {
|
||||
const jsonSourceFile = cast(declaration, isJsonSourceFile);
|
||||
return jsonSourceFile.statements.length ? checkExpression(jsonSourceFile.statements[0].expression) : emptyObjectType;
|
||||
}
|
||||
if (declaration.kind === SyntaxKind.ExportAssignment) {
|
||||
return checkExpression((<ExportAssignment>declaration).expression);
|
||||
}
|
||||
|
||||
// Handle variable, parameter or property
|
||||
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
|
||||
return errorType;
|
||||
}
|
||||
let type = getJSSpecialType(symbol, declaration);
|
||||
if (!type) {
|
||||
if (isJSDocPropertyLikeTag(declaration)
|
||||
|| isPropertyAccessExpression(declaration)
|
||||
|| isIdentifier(declaration)
|
||||
|| isClassDeclaration(declaration)
|
||||
|| isFunctionDeclaration(declaration)
|
||||
|| (isMethodDeclaration(declaration) && !isObjectLiteralMethod(declaration))
|
||||
|| isMethodSignature(declaration)) {
|
||||
// Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty`
|
||||
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
|
||||
return getTypeOfFuncClassEnumModule(symbol);
|
||||
}
|
||||
type = tryGetTypeFromEffectiveTypeNode(declaration) || anyType;
|
||||
}
|
||||
else if (isPropertyAssignment(declaration)) {
|
||||
type = tryGetTypeFromEffectiveTypeNode(declaration) || checkPropertyAssignment(declaration);
|
||||
}
|
||||
else if (isJsxAttribute(declaration)) {
|
||||
type = tryGetTypeFromEffectiveTypeNode(declaration) || checkJsxAttribute(declaration);
|
||||
}
|
||||
else if (isShorthandPropertyAssignment(declaration)) {
|
||||
type = tryGetTypeFromEffectiveTypeNode(declaration) || checkExpressionForMutableLocation(declaration.name, CheckMode.Normal);
|
||||
}
|
||||
else if (isObjectLiteralMethod(declaration)) {
|
||||
type = tryGetTypeFromEffectiveTypeNode(declaration) || checkObjectLiteralMethod(declaration, CheckMode.Normal);
|
||||
}
|
||||
else if (isParameter(declaration)
|
||||
|| isPropertyDeclaration(declaration)
|
||||
|| isPropertySignature(declaration)
|
||||
|| isVariableDeclaration(declaration)
|
||||
|| isBindingElement(declaration)) {
|
||||
type = getWidenedTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true);
|
||||
}
|
||||
else {
|
||||
return Debug.fail("Unhandled declaration kind! " + Debug.showSyntaxKind(declaration) + " for " + Debug.showSymbol(symbol));
|
||||
}
|
||||
}
|
||||
|
||||
if (!popTypeResolution()) {
|
||||
type = reportCircularityError(symbol);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
function getJSSpecialType(symbol: Symbol, decl: Declaration): Type | undefined {
|
||||
if (!isInJavaScriptFile(decl)) {
|
||||
return undefined;
|
||||
@@ -5118,20 +5125,25 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getAnnotatedAccessorType(accessor: AccessorDeclaration | undefined): Type | undefined {
|
||||
function getAnnotatedAccessorTypeNode(accessor: AccessorDeclaration | undefined): TypeNode | undefined {
|
||||
if (accessor) {
|
||||
if (accessor.kind === SyntaxKind.GetAccessor) {
|
||||
const getterTypeAnnotation = getEffectiveReturnTypeNode(accessor);
|
||||
return getterTypeAnnotation && getTypeFromTypeNode(getterTypeAnnotation);
|
||||
return getterTypeAnnotation;
|
||||
}
|
||||
else {
|
||||
const setterTypeAnnotation = getEffectiveSetAccessorTypeAnnotationNode(accessor);
|
||||
return setterTypeAnnotation && getTypeFromTypeNode(setterTypeAnnotation);
|
||||
return setterTypeAnnotation;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getAnnotatedAccessorType(accessor: AccessorDeclaration | undefined): Type | undefined {
|
||||
const node = getAnnotatedAccessorTypeNode(accessor);
|
||||
return node && getTypeFromTypeNode(node);
|
||||
}
|
||||
|
||||
function getAnnotatedAccessorThisParameter(accessor: AccessorDeclaration): Symbol | undefined {
|
||||
const parameter = getAccessorThisParameter(accessor);
|
||||
return parameter && parameter.symbol;
|
||||
@@ -5143,64 +5155,65 @@ namespace ts {
|
||||
|
||||
function getTypeOfAccessors(symbol: Symbol): Type {
|
||||
const links = getSymbolLinks(symbol);
|
||||
if (!links.type) {
|
||||
const getter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.GetAccessor);
|
||||
const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor);
|
||||
return links.type || (links.type = getTypeOfAccessorsWorker(symbol));
|
||||
}
|
||||
|
||||
if (getter && isInJavaScriptFile(getter)) {
|
||||
const jsDocType = getTypeForDeclarationFromJSDocComment(getter);
|
||||
if (jsDocType) {
|
||||
return links.type = jsDocType;
|
||||
}
|
||||
function getTypeOfAccessorsWorker(symbol: Symbol): Type {
|
||||
const getter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.GetAccessor);
|
||||
const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor);
|
||||
|
||||
if (getter && isInJavaScriptFile(getter)) {
|
||||
const jsDocType = getTypeForDeclarationFromJSDocComment(getter);
|
||||
if (jsDocType) {
|
||||
return jsDocType;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
|
||||
return errorType;
|
||||
}
|
||||
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
|
||||
return errorType;
|
||||
}
|
||||
|
||||
let type: Type;
|
||||
let type: Type;
|
||||
|
||||
// First try to see if the user specified a return type on the get-accessor.
|
||||
const getterReturnType = getAnnotatedAccessorType(getter);
|
||||
if (getterReturnType) {
|
||||
type = getterReturnType;
|
||||
// First try to see if the user specified a return type on the get-accessor.
|
||||
const getterReturnType = getAnnotatedAccessorType(getter);
|
||||
if (getterReturnType) {
|
||||
type = getterReturnType;
|
||||
}
|
||||
else {
|
||||
// If the user didn't specify a return type, try to use the set-accessor's parameter type.
|
||||
const setterParameterType = getAnnotatedAccessorType(setter);
|
||||
if (setterParameterType) {
|
||||
type = setterParameterType;
|
||||
}
|
||||
else {
|
||||
// If the user didn't specify a return type, try to use the set-accessor's parameter type.
|
||||
const setterParameterType = getAnnotatedAccessorType(setter);
|
||||
if (setterParameterType) {
|
||||
type = setterParameterType;
|
||||
// If there are no specified types, try to infer it from the body of the get accessor if it exists.
|
||||
if (getter && getter.body) {
|
||||
type = getReturnTypeFromBody(getter);
|
||||
}
|
||||
// Otherwise, fall back to 'any'.
|
||||
else {
|
||||
// If there are no specified types, try to infer it from the body of the get accessor if it exists.
|
||||
if (getter && getter.body) {
|
||||
type = getReturnTypeFromBody(getter);
|
||||
}
|
||||
// Otherwise, fall back to 'any'.
|
||||
else {
|
||||
if (noImplicitAny) {
|
||||
if (setter) {
|
||||
error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol));
|
||||
}
|
||||
else {
|
||||
Debug.assert(!!getter, "there must existed getter as we are current checking either setter or getter in this function");
|
||||
error(getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol));
|
||||
}
|
||||
if (noImplicitAny) {
|
||||
if (setter) {
|
||||
error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol));
|
||||
}
|
||||
else {
|
||||
Debug.assert(!!getter, "there must existed getter as we are current checking either setter or getter in this function");
|
||||
error(getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol));
|
||||
}
|
||||
type = anyType;
|
||||
}
|
||||
type = anyType;
|
||||
}
|
||||
}
|
||||
if (!popTypeResolution()) {
|
||||
type = anyType;
|
||||
if (noImplicitAny) {
|
||||
const getter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.GetAccessor);
|
||||
error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol));
|
||||
}
|
||||
}
|
||||
links.type = type;
|
||||
}
|
||||
return links.type;
|
||||
if (!popTypeResolution()) {
|
||||
type = anyType;
|
||||
if (noImplicitAny) {
|
||||
const getter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.GetAccessor);
|
||||
error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol));
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
function getBaseTypeVariableOfClass(symbol: Symbol) {
|
||||
@@ -5210,6 +5223,7 @@ namespace ts {
|
||||
|
||||
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
|
||||
let links = getSymbolLinks(symbol);
|
||||
const originalLinks = links;
|
||||
if (!links.type) {
|
||||
const jsDeclaration = getDeclarationOfJSInitializer(symbol.valueDeclaration);
|
||||
if (jsDeclaration) {
|
||||
@@ -5228,48 +5242,47 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (symbol.flags & SymbolFlags.Module && isShorthandAmbientModuleSymbol(symbol)) {
|
||||
links.type = anyType;
|
||||
}
|
||||
else if (symbol.valueDeclaration.kind === SyntaxKind.BinaryExpression ||
|
||||
symbol.valueDeclaration.kind === SyntaxKind.PropertyAccessExpression && symbol.valueDeclaration.parent.kind === SyntaxKind.BinaryExpression) {
|
||||
links.type = getWidenedTypeFromJSSpecialPropertyDeclarations(symbol);
|
||||
}
|
||||
else {
|
||||
if (symbol.flags & SymbolFlags.ValueModule && symbol.valueDeclaration && isSourceFile(symbol.valueDeclaration) && symbol.valueDeclaration.commonJsModuleIndicator) {
|
||||
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
|
||||
return errorType;
|
||||
}
|
||||
const resolvedModule = resolveExternalModuleSymbol(symbol);
|
||||
if (resolvedModule !== symbol) {
|
||||
const exportEquals = getMergedSymbol(symbol.exports!.get(InternalSymbolName.ExportEquals)!);
|
||||
links.type = getWidenedTypeFromJSSpecialPropertyDeclarations(exportEquals, exportEquals === resolvedModule ? undefined : resolvedModule);
|
||||
}
|
||||
if (!popTypeResolution()) {
|
||||
links.type = reportCircularityError(symbol);
|
||||
}
|
||||
}
|
||||
if (!links.type) {
|
||||
const type = createObjectType(ObjectFlags.Anonymous, symbol);
|
||||
if (symbol.flags & SymbolFlags.Class) {
|
||||
const baseTypeVariable = getBaseTypeVariableOfClass(symbol);
|
||||
links.type = baseTypeVariable ? getIntersectionType([type, baseTypeVariable]) : type;
|
||||
}
|
||||
else {
|
||||
links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? getOptionalType(type) : type;
|
||||
}
|
||||
}
|
||||
}
|
||||
originalLinks.type = links.type = getTypeOfFuncClassEnumModuleWorker(symbol);
|
||||
}
|
||||
return links.type;
|
||||
}
|
||||
|
||||
function getTypeOfFuncClassEnumModuleWorker(symbol: Symbol): Type {
|
||||
const declaration = symbol.valueDeclaration;
|
||||
if (symbol.flags & SymbolFlags.Module && isShorthandAmbientModuleSymbol(symbol)) {
|
||||
return anyType;
|
||||
}
|
||||
else if (declaration.kind === SyntaxKind.BinaryExpression ||
|
||||
declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) {
|
||||
return getWidenedTypeFromJSSpecialPropertyDeclarations(symbol);
|
||||
}
|
||||
else if (symbol.flags & SymbolFlags.ValueModule && declaration && isSourceFile(declaration) && declaration.commonJsModuleIndicator) {
|
||||
const resolvedModule = resolveExternalModuleSymbol(symbol);
|
||||
if (resolvedModule !== symbol) {
|
||||
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
|
||||
return errorType;
|
||||
}
|
||||
const exportEquals = getMergedSymbol(symbol.exports!.get(InternalSymbolName.ExportEquals)!);
|
||||
const type = getWidenedTypeFromJSSpecialPropertyDeclarations(exportEquals, exportEquals === resolvedModule ? undefined : resolvedModule);
|
||||
if (!popTypeResolution()) {
|
||||
return reportCircularityError(symbol);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
}
|
||||
const type = createObjectType(ObjectFlags.Anonymous, symbol);
|
||||
if (symbol.flags & SymbolFlags.Class) {
|
||||
const baseTypeVariable = getBaseTypeVariableOfClass(symbol);
|
||||
return baseTypeVariable ? getIntersectionType([type, baseTypeVariable]) : type;
|
||||
}
|
||||
else {
|
||||
return strictNullChecks && symbol.flags & SymbolFlags.Optional ? getOptionalType(type) : type;
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeOfEnumMember(symbol: Symbol): Type {
|
||||
const links = getSymbolLinks(symbol);
|
||||
if (!links.type) {
|
||||
links.type = getDeclaredTypeOfEnumMember(symbol);
|
||||
}
|
||||
return links.type;
|
||||
return links.type || (links.type = getDeclaredTypeOfEnumMember(symbol));
|
||||
}
|
||||
|
||||
function getTypeOfAlias(symbol: Symbol): Type {
|
||||
@@ -5298,7 +5311,7 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
|
||||
return errorType;
|
||||
return links.type = errorType;
|
||||
}
|
||||
symbolInstantiationDepth++;
|
||||
let type = instantiateType(getTypeOfSymbol(links.target!), links.mapper!);
|
||||
@@ -6853,11 +6866,7 @@ namespace ts {
|
||||
return obj.properties.some(property => {
|
||||
const name = property.name && getTextOfPropertyName(property.name);
|
||||
const expected = name === undefined ? undefined : getTypeOfPropertyOfType(contextualType, name);
|
||||
if (expected && typeIsLiteralType(expected)) {
|
||||
const actual = getTypeOfNode(property);
|
||||
return !!actual && !isTypeIdenticalTo(actual, expected);
|
||||
}
|
||||
return false;
|
||||
return !!expected && typeIsLiteralType(expected) && !isTypeIdenticalTo(getTypeOfNode(property), expected);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8128,6 +8137,10 @@ namespace ts {
|
||||
// TODO: GH#18217 (should the `|| assignedType` be at a lower precedence?)
|
||||
return (referenceType && assignedType ? getIntersectionType([assignedType, referenceType]) : referenceType || assignedType)!;
|
||||
}
|
||||
const enumTag = getJSDocEnumTag(symbol.valueDeclaration);
|
||||
if (enumTag && enumTag.typeExpression) {
|
||||
return getTypeFromTypeNode(enumTag.typeExpression);
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeReferenceTypeWorker(node: NodeWithTypeArguments, symbol: Symbol, typeArguments: Type[] | undefined): Type | undefined {
|
||||
@@ -8185,7 +8198,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isJSDocTypeReference(node: Node): node is TypeReferenceNode {
|
||||
return !!(node.flags & NodeFlags.JSDoc) && node.kind === SyntaxKind.TypeReference;
|
||||
return !!(node.flags & NodeFlags.JSDoc) && (node.kind === SyntaxKind.TypeReference || node.kind === SyntaxKind.ImportType);
|
||||
}
|
||||
|
||||
function checkNoTypeArguments(node: NodeWithTypeArguments, symbol?: Symbol) {
|
||||
@@ -8432,6 +8445,10 @@ namespace ts {
|
||||
return createTypeFromGenericGlobalType(globalArrayType, [elementType]);
|
||||
}
|
||||
|
||||
function createReadonlyArrayType(elementType: Type): ObjectType {
|
||||
return createTypeFromGenericGlobalType(globalReadonlyArrayType, [elementType]);
|
||||
}
|
||||
|
||||
function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type {
|
||||
const links = getNodeLinks(node);
|
||||
if (!links.resolvedType) {
|
||||
@@ -9454,7 +9471,7 @@ namespace ts {
|
||||
links.resolvedSymbol = unknownSymbol;
|
||||
return links.resolvedType = errorType;
|
||||
}
|
||||
const targetMeaning = node.isTypeOf ? SymbolFlags.Value : SymbolFlags.Type;
|
||||
const targetMeaning = node.isTypeOf ? SymbolFlags.Value : node.flags & NodeFlags.JSDoc ? SymbolFlags.Value | SymbolFlags.Type : SymbolFlags.Type;
|
||||
// TODO: Future work: support unions/generics/whatever via a deferred import-type
|
||||
const innerModuleSymbol = resolveExternalModuleName(node, node.argument.literal);
|
||||
if (!innerModuleSymbol) {
|
||||
@@ -9821,7 +9838,7 @@ namespace ts {
|
||||
case SyntaxKind.Identifier:
|
||||
case SyntaxKind.QualifiedName:
|
||||
const symbol = getSymbolAtLocation(node);
|
||||
return (symbol && getDeclaredTypeOfSymbol(symbol))!; // TODO: GH#18217
|
||||
return symbol ? getDeclaredTypeOfSymbol(symbol) : errorType;
|
||||
default:
|
||||
return errorType;
|
||||
}
|
||||
@@ -10087,11 +10104,16 @@ namespace ts {
|
||||
}
|
||||
|
||||
function instantiateMappedType(type: MappedType, mapper: TypeMapper): Type {
|
||||
// Check if we have a homomorphic mapped type, i.e. a type of the form { [P in keyof T]: X } for some
|
||||
// type variable T. If so, the mapped type is distributive over a union type and when T is instantiated
|
||||
// to a union type A | B, we produce { [P in keyof A]: X } | { [P in keyof B]: X }. Furthermore, for
|
||||
// homomorphic mapped types we leave primitive types alone. For example, when T is instantiated to a
|
||||
// union type A | undefined, we produce { [P in keyof A]: X } | undefined.
|
||||
// For a momomorphic mapped type { [P in keyof T]: X }, where T is some type variable, the mapping
|
||||
// operation depends on T as follows:
|
||||
// * If T is a primitive type no mapping is performed and the result is simply T.
|
||||
// * If T is a union type we distribute the mapped type over the union.
|
||||
// * If T is an array we map to an array where the element type has been transformed.
|
||||
// * If T is a tuple we map to a tuple where the element types have been transformed.
|
||||
// * Otherwise we map to an object type where the type of each property has been transformed.
|
||||
// For example, when T is instantiated to a union type A | B, we produce { [P in keyof A]: X } |
|
||||
// { [P in keyof B]: X }, and when when T is instantiated to a union type A | undefined, we produce
|
||||
// { [P in keyof A]: X } | undefined.
|
||||
const constraintType = getConstraintTypeFromMappedType(type);
|
||||
if (constraintType.flags & TypeFlags.Index) {
|
||||
const typeVariable = (<IndexType>constraintType).type;
|
||||
@@ -10100,7 +10122,11 @@ namespace ts {
|
||||
if (typeVariable !== mappedTypeVariable) {
|
||||
return mapType(mappedTypeVariable, t => {
|
||||
if (isMappableType(t)) {
|
||||
return instantiateAnonymousType(type, createReplacementMapper(typeVariable, t, mapper));
|
||||
const replacementMapper = createReplacementMapper(typeVariable, t, mapper);
|
||||
return isArrayType(t) ? createArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper)) :
|
||||
isReadonlyArrayType(t) ? createReadonlyArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper)) :
|
||||
isTupleType(t) ? instantiateMappedTupleType(t, type, replacementMapper) :
|
||||
instantiateAnonymousType(type, replacementMapper);
|
||||
}
|
||||
return t;
|
||||
});
|
||||
@@ -10114,6 +10140,26 @@ namespace ts {
|
||||
return type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection);
|
||||
}
|
||||
|
||||
function instantiateMappedTupleType(tupleType: TupleTypeReference, mappedType: MappedType, mapper: TypeMapper) {
|
||||
const minLength = tupleType.target.minLength;
|
||||
const elementTypes = map(tupleType.typeArguments || emptyArray, (_, i) =>
|
||||
instantiateMappedTypeTemplate(mappedType, getLiteralType("" + i), i >= minLength, mapper));
|
||||
const modifiers = getMappedTypeModifiers(mappedType);
|
||||
const newMinLength = modifiers & MappedTypeModifiers.IncludeOptional ? 0 :
|
||||
modifiers & MappedTypeModifiers.ExcludeOptional ? getTypeReferenceArity(tupleType) - (tupleType.target.hasRestElement ? 1 : 0) :
|
||||
minLength;
|
||||
return createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, tupleType.target.associatedNames);
|
||||
}
|
||||
|
||||
function instantiateMappedTypeTemplate(type: MappedType, key: Type, isOptional: boolean, mapper: TypeMapper) {
|
||||
const templateMapper = combineTypeMappers(mapper, createTypeMapper([getTypeParameterFromMappedType(type)], [key]));
|
||||
const propType = instantiateType(getTemplateTypeFromMappedType(<MappedType>type.target || type), templateMapper);
|
||||
const modifiers = getMappedTypeModifiers(type);
|
||||
return strictNullChecks && modifiers & MappedTypeModifiers.IncludeOptional && !isTypeAssignableTo(undefinedType, propType) ? getOptionalType(propType) :
|
||||
strictNullChecks && modifiers & MappedTypeModifiers.ExcludeOptional && isOptional ? getTypeWithFacts(propType, TypeFacts.NEUndefined) :
|
||||
propType;
|
||||
}
|
||||
|
||||
function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType {
|
||||
const result = <AnonymousType>createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol);
|
||||
if (type.objectFlags & ObjectFlags.Mapped) {
|
||||
@@ -12441,6 +12487,10 @@ namespace ts {
|
||||
return !!(getObjectFlags(type) & ObjectFlags.Reference) && (<TypeReference>type).target === globalArrayType;
|
||||
}
|
||||
|
||||
function isReadonlyArrayType(type: Type): boolean {
|
||||
return !!(getObjectFlags(type) & ObjectFlags.Reference) && (<TypeReference>type).target === globalReadonlyArrayType;
|
||||
}
|
||||
|
||||
function isArrayLikeType(type: Type): boolean {
|
||||
// A type is array-like if it is a reference to the global Array or global ReadonlyArray type,
|
||||
// or if it is not the undefined or null type and if it is assignable to ReadonlyArray<any>
|
||||
@@ -12996,6 +13046,22 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
// For arrays and tuples we infer new arrays and tuples where the reverse mapping has been
|
||||
// applied to the element type(s).
|
||||
if (isArrayType(source)) {
|
||||
return createArrayType(inferReverseMappedType((<TypeReference>source).typeArguments![0], target));
|
||||
}
|
||||
if (isReadonlyArrayType(source)) {
|
||||
return createReadonlyArrayType(inferReverseMappedType((<TypeReference>source).typeArguments![0], target));
|
||||
}
|
||||
if (isTupleType(source)) {
|
||||
const elementTypes = map(source.typeArguments || emptyArray, t => inferReverseMappedType(t, target));
|
||||
const minLength = getMappedTypeModifiers(target) & MappedTypeModifiers.IncludeOptional ?
|
||||
getTypeReferenceArity(source) - (source.target.hasRestElement ? 1 : 0) : source.target.minLength;
|
||||
return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.associatedNames);
|
||||
}
|
||||
// For all other object types we infer a new object type where the reverse mapping has been
|
||||
// applied to the type of each property.
|
||||
const reversed = createObjectType(ObjectFlags.ReverseMapped | ObjectFlags.Anonymous, /*symbol*/ undefined) as ReverseMappedType;
|
||||
reversed.source = source;
|
||||
reversed.mappedType = target;
|
||||
@@ -15340,8 +15406,8 @@ namespace ts {
|
||||
(!isInParameterInitializerBeforeContainingFunction(node) || getThisParameter(container))) {
|
||||
// Note: a parameter initializer should refer to class-this unless function-this is explicitly annotated.
|
||||
|
||||
// If this is a function in a JS file, it might be a class method. Check if it's the RHS
|
||||
// of a x.prototype.y = function [name]() { .... }
|
||||
// If this is a function in a JS file, it might be a class method.
|
||||
// Check if it's the RHS of a x.prototype.y = function [name]() { .... }
|
||||
if (container.kind === SyntaxKind.FunctionExpression &&
|
||||
container.parent.kind === SyntaxKind.BinaryExpression &&
|
||||
getSpecialPropertyAssignmentKind(container.parent as BinaryExpression) === SpecialPropertyAssignmentKind.PrototypeProperty) {
|
||||
@@ -15355,6 +15421,17 @@ namespace ts {
|
||||
return getFlowTypeOfReference(node, getInferredClassType(classSymbol));
|
||||
}
|
||||
}
|
||||
// Check if it's a constructor definition, can be either a variable decl or function decl
|
||||
// i.e.
|
||||
// * /** @constructor */ function [name]() { ... }
|
||||
// * /** @constructor */ var x = function() { ... }
|
||||
else if ((container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.FunctionDeclaration) &&
|
||||
getJSDocClassTag(container)) {
|
||||
const classType = getJavaScriptClassType(container.symbol);
|
||||
if (classType) {
|
||||
return getFlowTypeOfReference(node, classType);
|
||||
}
|
||||
}
|
||||
|
||||
const thisType = getThisTypeOfDeclaration(container) || getContextualThisParameterType(container);
|
||||
if (thisType) {
|
||||
@@ -16563,7 +16640,8 @@ namespace ts {
|
||||
const contextualTypeHasPattern = contextualType && contextualType.pattern &&
|
||||
(contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression);
|
||||
const isInJSFile = isInJavaScriptFile(node) && !isInJsonFile(node);
|
||||
const isJSObjectLiteral = !contextualType && isInJSFile;
|
||||
const enumTag = getJSDocEnumTag(node);
|
||||
const isJSObjectLiteral = !contextualType && isInJSFile && !enumTag;
|
||||
let typeFlags: TypeFlags = 0;
|
||||
let patternWithComputedProperties = false;
|
||||
let hasComputedStringProperty = false;
|
||||
@@ -16588,6 +16666,9 @@ namespace ts {
|
||||
checkTypeAssignableTo(type, jsDocType, memberDecl);
|
||||
type = jsDocType;
|
||||
}
|
||||
else if (enumTag && enumTag.typeExpression) {
|
||||
checkTypeAssignableTo(type, getTypeFromTypeNode(enumTag.typeExpression), memberDecl);
|
||||
}
|
||||
}
|
||||
typeFlags |= type.flags;
|
||||
const nameType = computedNameType && computedNameType.flags & TypeFlags.StringOrNumberLiteralOrUnique ?
|
||||
@@ -16622,6 +16703,7 @@ namespace ts {
|
||||
symbolToString(member), typeToString(contextualType!));
|
||||
}
|
||||
}
|
||||
|
||||
prop.declarations = member.declarations;
|
||||
prop.parent = member.parent;
|
||||
if (member.valueDeclaration) {
|
||||
@@ -17132,7 +17214,7 @@ namespace ts {
|
||||
const jsxStatelessElementType = getJsxStatelessElementTypeAt(openingLikeElement);
|
||||
if (jsxStatelessElementType) {
|
||||
// We don't call getResolvedSignature here because we have already resolve the type of JSX Element.
|
||||
const callSignature = getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, /*candidatesOutArray*/ undefined);
|
||||
const callSignature = getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, /*candidatesOutArray*/ undefined, /*isForSignatureHelp*/ false);
|
||||
if (callSignature !== unknownSignature) {
|
||||
const callReturnType = callSignature && getReturnTypeOfSignature(callSignature);
|
||||
let paramType = callReturnType && (callSignature!.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature!.parameters[0]));
|
||||
@@ -17169,7 +17251,7 @@ namespace ts {
|
||||
if (jsxStatelessElementType) {
|
||||
// We don't call getResolvedSignature because here we have already resolve the type of JSX Element.
|
||||
const candidatesOutArray: Signature[] = [];
|
||||
getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, candidatesOutArray);
|
||||
getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, candidatesOutArray, /*isForSignatureHelp*/ false);
|
||||
let result: Type | undefined;
|
||||
let allMatchingAttributesType: Type | undefined;
|
||||
for (const candidate of candidatesOutArray) {
|
||||
@@ -19032,7 +19114,7 @@ namespace ts {
|
||||
return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, paramCount, typeArguments.length);
|
||||
}
|
||||
|
||||
function resolveCall(node: CallLikeExpression, signatures: ReadonlyArray<Signature>, candidatesOutArray: Signature[] | undefined, fallbackError?: DiagnosticMessage): Signature {
|
||||
function resolveCall(node: CallLikeExpression, signatures: ReadonlyArray<Signature>, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean, fallbackError?: DiagnosticMessage): Signature {
|
||||
const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
|
||||
const isDecorator = node.kind === SyntaxKind.Decorator;
|
||||
const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node);
|
||||
@@ -19118,7 +19200,7 @@ namespace ts {
|
||||
// If we are in signature help, a trailing comma indicates that we intend to provide another argument,
|
||||
// so we will only accept overloads with arity at least 1 higher than the current number of provided arguments.
|
||||
const signatureHelpTrailingComma =
|
||||
candidatesOutArray && node.kind === SyntaxKind.CallExpression && node.arguments.hasTrailingComma;
|
||||
isForSignatureHelp && node.kind === SyntaxKind.CallExpression && node.arguments.hasTrailingComma;
|
||||
|
||||
// Section 4.12.1:
|
||||
// if the candidate list contains one or more signatures for which the type of each argument
|
||||
@@ -19329,7 +19411,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
const typeArgumentNodes: ReadonlyArray<TypeNode> = callLikeExpressionMayHaveTypeArguments(node) ? node.typeArguments || emptyArray : emptyArray;
|
||||
const typeArguments = typeArgumentNodes.map(n => getTypeOfNode(n) || anyType);
|
||||
const typeArguments = typeArgumentNodes.map(getTypeOfNode);
|
||||
while (typeArguments.length > typeParameters.length) {
|
||||
typeArguments.pop();
|
||||
}
|
||||
@@ -19360,7 +19442,7 @@ namespace ts {
|
||||
return maxParamsIndex;
|
||||
}
|
||||
|
||||
function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[] | undefined): Signature {
|
||||
function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
|
||||
if (node.expression.kind === SyntaxKind.SuperKeyword) {
|
||||
const superType = checkSuperExpression(node.expression);
|
||||
if (isTypeAny(superType)) {
|
||||
@@ -19375,7 +19457,7 @@ namespace ts {
|
||||
const baseTypeNode = getEffectiveBaseTypeNode(getContainingClass(node)!);
|
||||
if (baseTypeNode) {
|
||||
const baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments, baseTypeNode);
|
||||
return resolveCall(node, baseConstructors, candidatesOutArray);
|
||||
return resolveCall(node, baseConstructors, candidatesOutArray, isForSignatureHelp);
|
||||
}
|
||||
}
|
||||
return resolveUntypedCall(node);
|
||||
@@ -19428,7 +19510,12 @@ namespace ts {
|
||||
}
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
return resolveCall(node, callSignatures, candidatesOutArray);
|
||||
// If the function is explicitly marked with `@class`, then it must be constructed.
|
||||
if (callSignatures.some(sig => isInJavaScriptFile(sig.declaration) && !!getJSDocClassTag(sig.declaration!))) {
|
||||
error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType));
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
return resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -19442,7 +19529,7 @@ namespace ts {
|
||||
!numCallSignatures && !numConstructSignatures && !(apparentFuncType.flags & (TypeFlags.Union | TypeFlags.Never)) && isTypeAssignableTo(funcType, globalFunctionType);
|
||||
}
|
||||
|
||||
function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[] | undefined): Signature {
|
||||
function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
|
||||
if (node.arguments && languageVersion < ScriptTarget.ES5) {
|
||||
const spreadIndex = getSpreadArgumentIndex(node.arguments);
|
||||
if (spreadIndex >= 0) {
|
||||
@@ -19495,7 +19582,7 @@ namespace ts {
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
|
||||
return resolveCall(node, constructSignatures, candidatesOutArray);
|
||||
return resolveCall(node, constructSignatures, candidatesOutArray, isForSignatureHelp);
|
||||
}
|
||||
|
||||
// If expressionType's apparent type is an object type with no construct signatures but
|
||||
@@ -19504,7 +19591,7 @@ namespace ts {
|
||||
// operation is Any. It is an error to have a Void this type.
|
||||
const callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call);
|
||||
if (callSignatures.length) {
|
||||
const signature = resolveCall(node, callSignatures, candidatesOutArray);
|
||||
const signature = resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp);
|
||||
if (!isJavaScriptConstructor(signature.declaration) && getReturnTypeOfSignature(signature) !== voidType) {
|
||||
error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword);
|
||||
}
|
||||
@@ -19611,7 +19698,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function resolveTaggedTemplateExpression(node: TaggedTemplateExpression, candidatesOutArray: Signature[] | undefined): Signature {
|
||||
function resolveTaggedTemplateExpression(node: TaggedTemplateExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
|
||||
const tagType = checkExpression(node.tag);
|
||||
const apparentType = getApparentType(tagType);
|
||||
|
||||
@@ -19632,7 +19719,7 @@ namespace ts {
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
|
||||
return resolveCall(node, callSignatures, candidatesOutArray);
|
||||
return resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -19663,7 +19750,7 @@ namespace ts {
|
||||
/**
|
||||
* Resolves a decorator as if it were a call expression.
|
||||
*/
|
||||
function resolveDecorator(node: Decorator, candidatesOutArray: Signature[] | undefined): Signature {
|
||||
function resolveDecorator(node: Decorator, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
|
||||
const funcType = checkExpression(node.expression);
|
||||
const apparentType = getApparentType(funcType);
|
||||
if (apparentType === errorType) {
|
||||
@@ -19692,7 +19779,7 @@ namespace ts {
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
|
||||
return resolveCall(node, callSignatures, candidatesOutArray, headMessage);
|
||||
return resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp, headMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -19717,32 +19804,32 @@ namespace ts {
|
||||
* @param candidatesOutArray an array of signature to be filled in by the function. It is passed by signature help in the language service;
|
||||
* the function will fill it up with appropriate candidate signatures
|
||||
*/
|
||||
function getResolvedJsxStatelessFunctionSignature(openingLikeElement: JsxOpeningLikeElement, elementType: Type, candidatesOutArray: Signature[] | undefined): Signature | undefined {
|
||||
function getResolvedJsxStatelessFunctionSignature(openingLikeElement: JsxOpeningLikeElement, elementType: Type, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature | undefined {
|
||||
Debug.assert(!(elementType.flags & TypeFlags.Union));
|
||||
const callSignatures = elementType && getSignaturesOfType(elementType, SignatureKind.Call);
|
||||
if (callSignatures && callSignatures.length > 0) {
|
||||
return resolveCall(openingLikeElement, callSignatures, candidatesOutArray);
|
||||
return resolveCall(openingLikeElement, callSignatures, candidatesOutArray, isForSignatureHelp);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature {
|
||||
function resolveSignature(node: CallLikeExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.CallExpression:
|
||||
return resolveCallExpression(node, candidatesOutArray);
|
||||
return resolveCallExpression(node, candidatesOutArray, isForSignatureHelp);
|
||||
case SyntaxKind.NewExpression:
|
||||
return resolveNewExpression(node, candidatesOutArray);
|
||||
return resolveNewExpression(node, candidatesOutArray, isForSignatureHelp);
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
return resolveTaggedTemplateExpression(node, candidatesOutArray);
|
||||
return resolveTaggedTemplateExpression(node, candidatesOutArray, isForSignatureHelp);
|
||||
case SyntaxKind.Decorator:
|
||||
return resolveDecorator(node, candidatesOutArray);
|
||||
return resolveDecorator(node, candidatesOutArray, isForSignatureHelp);
|
||||
case SyntaxKind.JsxOpeningElement:
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
// This code-path is called by language service
|
||||
const exprTypes = checkExpression(node.tagName);
|
||||
return forEachType(exprTypes, exprType => {
|
||||
const sfcResult = getResolvedJsxStatelessFunctionSignature(node, exprType, candidatesOutArray);
|
||||
const sfcResult = getResolvedJsxStatelessFunctionSignature(node, exprType, candidatesOutArray, isForSignatureHelp);
|
||||
if (sfcResult && sfcResult !== unknownSignature) {
|
||||
return sfcResult;
|
||||
}
|
||||
@@ -19763,7 +19850,7 @@ namespace ts {
|
||||
* the function will fill it up with appropriate candidate signatures
|
||||
* @return a signature of the call-like expression or undefined if one can't be found
|
||||
*/
|
||||
function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature {
|
||||
function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[] | undefined, isForSignatureHelp = false): Signature {
|
||||
const links = getNodeLinks(node);
|
||||
// If getResolvedSignature has already been called, we will have cached the resolvedSignature.
|
||||
// However, it is possible that either candidatesOutArray was not passed in the first time,
|
||||
@@ -19774,7 +19861,7 @@ namespace ts {
|
||||
return cached;
|
||||
}
|
||||
links.resolvedSignature = resolvingSignature;
|
||||
const result = resolveSignature(node, candidatesOutArray);
|
||||
const result = resolveSignature(node, candidatesOutArray, isForSignatureHelp);
|
||||
// If signature resolution originated in control flow type analysis (for example to compute the
|
||||
// assigned type in a flow assignment) we don't cache the result as it may be based on temporary
|
||||
// types from the control flow analysis.
|
||||
@@ -22613,6 +22700,7 @@ namespace ts {
|
||||
grammarErrorOnNode(node, Diagnostics.infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type);
|
||||
}
|
||||
checkSourceElement(node.typeParameter);
|
||||
registerForUnusedIdentifiersCheck(node);
|
||||
}
|
||||
|
||||
function checkImportType(node: ImportTypeNode) {
|
||||
@@ -23433,9 +23521,13 @@ namespace ts {
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
const otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor;
|
||||
const otherAccessor = getDeclarationOfKind<AccessorDeclaration>(getSymbolOfNode(node as AccessorDeclaration), otherKind);
|
||||
markDecoratorMedataDataTypeNodeAsReferenced(getAnnotatedAccessorTypeNode(node as AccessorDeclaration) || otherAccessor && getAnnotatedAccessorTypeNode(otherAccessor));
|
||||
break;
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
for (const parameter of (<FunctionLikeDeclaration>node).parameters) {
|
||||
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter));
|
||||
}
|
||||
@@ -23641,7 +23733,8 @@ namespace ts {
|
||||
type PotentiallyUnusedIdentifier =
|
||||
| SourceFile | ModuleDeclaration | ClassLikeDeclaration | InterfaceDeclaration
|
||||
| Block | CaseBlock | ForStatement | ForInStatement | ForOfStatement
|
||||
| Exclude<SignatureDeclaration, IndexSignatureDeclaration | JSDocFunctionType> | TypeAliasDeclaration;
|
||||
| Exclude<SignatureDeclaration, IndexSignatureDeclaration | JSDocFunctionType> | TypeAliasDeclaration
|
||||
| InferTypeNode;
|
||||
|
||||
function checkUnusedIdentifiers(potentiallyUnusedIdentifiers: ReadonlyArray<PotentiallyUnusedIdentifier>, addDiagnostic: AddUnusedDiagnostic) {
|
||||
for (const node of potentiallyUnusedIdentifiers) {
|
||||
@@ -23681,6 +23774,7 @@ namespace ts {
|
||||
case SyntaxKind.FunctionType:
|
||||
case SyntaxKind.ConstructorType:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
case SyntaxKind.InferType:
|
||||
checkUnusedTypeParameters(node, addDiagnostic);
|
||||
break;
|
||||
default:
|
||||
@@ -23734,21 +23828,48 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function checkUnusedTypeParameters(
|
||||
node: ClassDeclaration | ClassExpression | FunctionDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction | ConstructorDeclaration | SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration,
|
||||
addDiagnostic: AddUnusedDiagnostic,
|
||||
): void {
|
||||
function checkUnusedTypeParameters(node: ClassLikeDeclaration | SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration | InferTypeNode, addDiagnostic: AddUnusedDiagnostic): void {
|
||||
// Only report errors on the last declaration for the type parameter container;
|
||||
// this ensures that all uses have been accounted for.
|
||||
const typeParameters = getEffectiveTypeParameterDeclarations(node);
|
||||
if (!(node.flags & NodeFlags.Ambient) && last(getSymbolOfNode(node).declarations) === node) {
|
||||
if (node.flags & NodeFlags.Ambient || node.kind !== SyntaxKind.InferType && last(getSymbolOfNode(node).declarations) !== node) return;
|
||||
|
||||
if (node.kind === SyntaxKind.InferType) {
|
||||
const { typeParameter } = node;
|
||||
if (isTypeParameterUnused(typeParameter)) {
|
||||
addDiagnostic(node, UnusedKind.Parameter, createDiagnosticForNode(node, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(typeParameter.name)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const typeParameters = getEffectiveTypeParameterDeclarations(node);
|
||||
const seenParentsWithEveryUnused = new NodeSet<DeclarationWithTypeParameterChildren>();
|
||||
|
||||
for (const typeParameter of typeParameters) {
|
||||
if (!(getMergedSymbol(typeParameter.symbol).isReferenced! & SymbolFlags.TypeParameter) && !isIdentifierThatStartsWithUnderscore(typeParameter.name)) {
|
||||
addDiagnostic(typeParameter, UnusedKind.Parameter, createDiagnosticForNode(typeParameter.name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(typeParameter.symbol)));
|
||||
if (!isTypeParameterUnused(typeParameter)) continue;
|
||||
|
||||
const name = idText(typeParameter.name);
|
||||
const { parent } = typeParameter;
|
||||
if (parent.kind !== SyntaxKind.InferType && parent.typeParameters!.every(isTypeParameterUnused)) {
|
||||
if (seenParentsWithEveryUnused.tryAdd(parent)) {
|
||||
const range = isJSDocTemplateTag(parent)
|
||||
// Whole @template tag
|
||||
? rangeOfNode(parent)
|
||||
// Include the `<>` in the error message
|
||||
: rangeOfTypeParameters(parent.typeParameters!);
|
||||
const only = typeParameters.length === 1;
|
||||
const message = only ? Diagnostics._0_is_declared_but_its_value_is_never_read : Diagnostics.All_type_parameters_are_unused;
|
||||
const arg0 = only ? name : undefined;
|
||||
addDiagnostic(typeParameter, UnusedKind.Parameter, createFileDiagnostic(getSourceFileOfNode(parent), range.pos, range.end - range.pos, message, arg0));
|
||||
}
|
||||
}
|
||||
else {
|
||||
addDiagnostic(typeParameter, UnusedKind.Parameter, createDiagnosticForNode(typeParameter, Diagnostics._0_is_declared_but_its_value_is_never_read, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function isTypeParameterUnused(typeParameter: TypeParameterDeclaration): boolean {
|
||||
return !(getMergedSymbol(typeParameter.symbol).isReferenced! & SymbolFlags.TypeParameter) && !isIdentifierThatStartsWithUnderscore(typeParameter.name);
|
||||
}
|
||||
|
||||
function addToGroup<K, V>(map: Map<[K, V[]]>, key: K, value: V, getKey: (key: K) => number | string): void {
|
||||
const keyString = String(getKey(key));
|
||||
@@ -27143,7 +27264,7 @@ namespace ts {
|
||||
grandParent.kind === SyntaxKind.ObjectBindingPattern &&
|
||||
node === (<BindingElement>parent).propertyName) {
|
||||
const typeOfPattern = getTypeOfNode(grandParent);
|
||||
const propertyDeclaration = typeOfPattern && getPropertyOfType(typeOfPattern, (<Identifier>node).escapedText);
|
||||
const propertyDeclaration = getPropertyOfType(typeOfPattern, (<Identifier>node).escapedText);
|
||||
|
||||
if (propertyDeclaration) {
|
||||
return propertyDeclaration;
|
||||
@@ -27243,7 +27364,7 @@ namespace ts {
|
||||
if (isPartOfTypeNode(node)) {
|
||||
let typeFromTypeNode = getTypeFromTypeNode(<TypeNode>node);
|
||||
|
||||
if (typeFromTypeNode && isExpressionWithTypeArgumentsInClassImplementsClause(node)) {
|
||||
if (isExpressionWithTypeArgumentsInClassImplementsClause(node)) {
|
||||
const containingClass = getContainingClass(node)!;
|
||||
const classType = getTypeOfNode(containingClass) as InterfaceType;
|
||||
typeFromTypeNode = getTypeWithThisArgument(typeFromTypeNode, classType.thisType);
|
||||
@@ -27261,8 +27382,8 @@ namespace ts {
|
||||
// extends clause of a class. We handle that case here.
|
||||
const classNode = getContainingClass(node)!;
|
||||
const classType = getDeclaredTypeOfSymbol(getSymbolOfNode(classNode)) as InterfaceType;
|
||||
const baseType = getBaseTypes(classType)[0];
|
||||
return baseType && getTypeWithThisArgument(baseType, classType.thisType);
|
||||
const baseType = firstOrUndefined(getBaseTypes(classType));
|
||||
return baseType ? getTypeWithThisArgument(baseType, classType.thisType) : errorType;
|
||||
}
|
||||
|
||||
if (isTypeDeclaration(node)) {
|
||||
@@ -27959,8 +28080,8 @@ namespace ts {
|
||||
const otherAccessor = getDeclarationOfKind<AccessorDeclaration>(getSymbolOfNode(accessor), otherKind);
|
||||
const firstAccessor = otherAccessor && (otherAccessor.pos < accessor.pos) ? otherAccessor : accessor;
|
||||
const secondAccessor = otherAccessor && (otherAccessor.pos < accessor.pos) ? accessor : otherAccessor;
|
||||
const setAccessor = accessor.kind === SyntaxKind.SetAccessor ? accessor : otherAccessor;
|
||||
const getAccessor = accessor.kind === SyntaxKind.GetAccessor ? accessor : otherAccessor;
|
||||
const setAccessor = accessor.kind === SyntaxKind.SetAccessor ? accessor : otherAccessor as SetAccessorDeclaration;
|
||||
const getAccessor = accessor.kind === SyntaxKind.GetAccessor ? accessor : otherAccessor as GetAccessorDeclaration;
|
||||
return {
|
||||
firstAccessor,
|
||||
secondAccessor,
|
||||
@@ -29184,10 +29305,6 @@ namespace ts {
|
||||
}
|
||||
checkGrammarForDisallowedTrailingComma(elements, Diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma);
|
||||
|
||||
if (node.name.kind === SyntaxKind.ArrayBindingPattern || node.name.kind === SyntaxKind.ObjectBindingPattern) {
|
||||
return grammarErrorOnNode(node.name, Diagnostics.A_rest_element_cannot_contain_a_binding_pattern);
|
||||
}
|
||||
|
||||
if (node.propertyName) {
|
||||
return grammarErrorOnNode(node.name, Diagnostics.A_rest_element_cannot_have_a_property_name);
|
||||
}
|
||||
|
||||
@@ -2093,7 +2093,7 @@ namespace ts {
|
||||
return arg => f(arg) || g(arg);
|
||||
}
|
||||
|
||||
export function assertTypeIsNever(_: never): void { } // tslint:disable-line no-empty
|
||||
export function assertType<T>(_: T): void { } // tslint:disable-line no-empty
|
||||
|
||||
export function singleElementArray<T>(t: T | undefined): T[] | undefined {
|
||||
return t === undefined ? undefined : [t];
|
||||
|
||||
@@ -3655,6 +3655,10 @@
|
||||
"category": "Message",
|
||||
"code": 6204
|
||||
},
|
||||
"All type parameters are unused": {
|
||||
"category": "Error",
|
||||
"code": 6205
|
||||
},
|
||||
|
||||
"Projects to reference": {
|
||||
"category": "Message",
|
||||
@@ -4208,6 +4212,14 @@
|
||||
"category": "Message",
|
||||
"code": 90010
|
||||
},
|
||||
"Remove template tag": {
|
||||
"category": "Message",
|
||||
"code": 90011
|
||||
},
|
||||
"Remove type parameters": {
|
||||
"category": "Message",
|
||||
"code": 90012
|
||||
},
|
||||
"Import '{0}' from module \"{1}\"": {
|
||||
"category": "Message",
|
||||
"code": 90013
|
||||
@@ -4276,6 +4288,22 @@
|
||||
"category": "Message",
|
||||
"code": 90029
|
||||
},
|
||||
"Replace 'infer {0}' with 'unknown'": {
|
||||
"category": "Message",
|
||||
"code": 90030
|
||||
},
|
||||
"Replace all unused 'infer' with 'unknown'": {
|
||||
"category": "Message",
|
||||
"code": 90031
|
||||
},
|
||||
"Import default '{0}' from module \"{1}\"": {
|
||||
"category": "Message",
|
||||
"code": 90032
|
||||
},
|
||||
"Add default import '{0}' to existing import declaration from \"{1}\"": {
|
||||
"category": "Message",
|
||||
"code": 90033
|
||||
},
|
||||
"Convert function to an ES2015 class": {
|
||||
"category": "Message",
|
||||
"code": 95001
|
||||
|
||||
+13
-48
@@ -1058,7 +1058,7 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createTaggedTemplate(tag: Expression, template: TemplateLiteral): TaggedTemplateExpression;
|
||||
/** @deprecated */ export function createTaggedTemplate(tag: Expression, template: TemplateLiteral): TaggedTemplateExpression;
|
||||
export function createTaggedTemplate(tag: Expression, typeArguments: ReadonlyArray<TypeNode> | undefined, template: TemplateLiteral): TaggedTemplateExpression;
|
||||
/** @internal */
|
||||
export function createTaggedTemplate(tag: Expression, typeArgumentsOrTemplate: ReadonlyArray<TypeNode> | TemplateLiteral | undefined, template?: TemplateLiteral): TaggedTemplateExpression;
|
||||
@@ -1076,7 +1076,7 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateTaggedTemplate(node: TaggedTemplateExpression, tag: Expression, template: TemplateLiteral): TaggedTemplateExpression;
|
||||
/** @deprecated */ export function updateTaggedTemplate(node: TaggedTemplateExpression, tag: Expression, template: TemplateLiteral): TaggedTemplateExpression;
|
||||
export function updateTaggedTemplate(node: TaggedTemplateExpression, tag: Expression, typeArguments: ReadonlyArray<TypeNode> | undefined, template: TemplateLiteral): TaggedTemplateExpression;
|
||||
export function updateTaggedTemplate(node: TaggedTemplateExpression, tag: Expression, typeArgumentsOrTemplate: ReadonlyArray<TypeNode> | TemplateLiteral | undefined, template?: TemplateLiteral) {
|
||||
return node.tag !== tag
|
||||
@@ -1168,14 +1168,6 @@ namespace ts {
|
||||
node.body = parenthesizeConciseBody(body);
|
||||
return node;
|
||||
}
|
||||
|
||||
/** @deprecated */ export function updateArrowFunction(
|
||||
node: ArrowFunction,
|
||||
modifiers: ReadonlyArray<Modifier> | undefined,
|
||||
typeParameters: ReadonlyArray<TypeParameterDeclaration> | undefined,
|
||||
parameters: ReadonlyArray<ParameterDeclaration>,
|
||||
type: TypeNode | undefined,
|
||||
body: ConciseBody): ArrowFunction;
|
||||
export function updateArrowFunction(
|
||||
node: ArrowFunction,
|
||||
modifiers: ReadonlyArray<Modifier> | undefined,
|
||||
@@ -1183,28 +1175,8 @@ namespace ts {
|
||||
parameters: ReadonlyArray<ParameterDeclaration>,
|
||||
type: TypeNode | undefined,
|
||||
equalsGreaterThanToken: Token<SyntaxKind.EqualsGreaterThanToken>,
|
||||
body: ConciseBody): ArrowFunction;
|
||||
export function updateArrowFunction(
|
||||
node: ArrowFunction,
|
||||
modifiers: ReadonlyArray<Modifier> | undefined,
|
||||
typeParameters: ReadonlyArray<TypeParameterDeclaration> | undefined,
|
||||
parameters: ReadonlyArray<ParameterDeclaration>,
|
||||
type: TypeNode | undefined,
|
||||
equalsGreaterThanTokenOrBody: Token<SyntaxKind.EqualsGreaterThanToken> | ConciseBody,
|
||||
bodyOrUndefined?: ConciseBody,
|
||||
body: ConciseBody
|
||||
): ArrowFunction {
|
||||
let equalsGreaterThanToken: Token<SyntaxKind.EqualsGreaterThanToken>;
|
||||
let body: ConciseBody;
|
||||
if (bodyOrUndefined === undefined) {
|
||||
equalsGreaterThanToken = node.equalsGreaterThanToken;
|
||||
body = cast(equalsGreaterThanTokenOrBody, isConciseBody);
|
||||
}
|
||||
else {
|
||||
equalsGreaterThanToken = cast(equalsGreaterThanTokenOrBody, (n): n is Token<SyntaxKind.EqualsGreaterThanToken> =>
|
||||
n.kind === SyntaxKind.EqualsGreaterThanToken);
|
||||
body = bodyOrUndefined;
|
||||
}
|
||||
|
||||
return node.modifiers !== modifiers
|
||||
|| node.typeParameters !== typeParameters
|
||||
|| node.parameters !== parameters
|
||||
@@ -1306,7 +1278,7 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createConditional(condition: Expression, whenTrue: Expression, whenFalse: Expression): ConditionalExpression;
|
||||
/** @deprecated */ export function createConditional(condition: Expression, whenTrue: Expression, whenFalse: Expression): ConditionalExpression;
|
||||
export function createConditional(condition: Expression, questionToken: QuestionToken, whenTrue: Expression, colonToken: ColonToken, whenFalse: Expression): ConditionalExpression;
|
||||
export function createConditional(condition: Expression, questionTokenOrWhenTrue: QuestionToken | Expression, whenTrueOrWhenFalse: Expression, colonToken?: ColonToken, whenFalse?: Expression) {
|
||||
const node = <ConditionalExpression>createSynthesizedNode(SyntaxKind.ConditionalExpression);
|
||||
@@ -1317,26 +1289,14 @@ namespace ts {
|
||||
node.whenFalse = parenthesizeSubexpressionOfConditionalExpression(whenFalse ? whenFalse : whenTrueOrWhenFalse);
|
||||
return node;
|
||||
}
|
||||
|
||||
/** @deprecated */ export function updateConditional(
|
||||
node: ConditionalExpression,
|
||||
condition: Expression,
|
||||
whenTrue: Expression,
|
||||
whenFalse: Expression): ConditionalExpression;
|
||||
export function updateConditional(
|
||||
node: ConditionalExpression,
|
||||
condition: Expression,
|
||||
questionToken: Token<SyntaxKind.QuestionToken>,
|
||||
whenTrue: Expression,
|
||||
colonToken: Token<SyntaxKind.ColonToken>,
|
||||
whenFalse: Expression): ConditionalExpression;
|
||||
export function updateConditional(node: ConditionalExpression, condition: Expression, ...args: any[]) {
|
||||
if (args.length === 2) {
|
||||
const [whenTrue, whenFalse] = args;
|
||||
return updateConditional(node, condition, node.questionToken, whenTrue, node.colonToken, whenFalse);
|
||||
}
|
||||
Debug.assert(args.length === 4);
|
||||
const [questionToken, whenTrue, colonToken, whenFalse] = args;
|
||||
whenFalse: Expression
|
||||
): ConditionalExpression {
|
||||
return node.condition !== condition
|
||||
|| node.questionToken !== questionToken
|
||||
|| node.whenTrue !== whenTrue
|
||||
@@ -2231,7 +2191,7 @@ namespace ts {
|
||||
export function createJsxSelfClosingElement(tagName: JsxTagNameExpression, typeArguments: ReadonlyArray<TypeNode> | undefined, attributes: JsxAttributes) {
|
||||
const node = <JsxSelfClosingElement>createSynthesizedNode(SyntaxKind.JsxSelfClosingElement);
|
||||
node.tagName = tagName;
|
||||
node.typeArguments = typeArguments && createNodeArray(typeArguments);
|
||||
node.typeArguments = asNodeArray(typeArguments);
|
||||
node.attributes = attributes;
|
||||
return node;
|
||||
}
|
||||
@@ -2247,7 +2207,7 @@ namespace ts {
|
||||
export function createJsxOpeningElement(tagName: JsxTagNameExpression, typeArguments: ReadonlyArray<TypeNode> | undefined, attributes: JsxAttributes) {
|
||||
const node = <JsxOpeningElement>createSynthesizedNode(SyntaxKind.JsxOpeningElement);
|
||||
node.tagName = tagName;
|
||||
node.typeArguments = typeArguments && createNodeArray(typeArguments);
|
||||
node.typeArguments = asNodeArray(typeArguments);
|
||||
node.attributes = attributes;
|
||||
return node;
|
||||
}
|
||||
@@ -4024,6 +3984,11 @@ namespace ts {
|
||||
const binaryOperatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, binaryOperator);
|
||||
const binaryOperatorAssociativity = getOperatorAssociativity(SyntaxKind.BinaryExpression, binaryOperator);
|
||||
const emittedOperand = skipPartiallyEmittedExpressions(operand);
|
||||
if (!isLeftSideOfBinary && operand.kind === SyntaxKind.ArrowFunction && binaryOperatorPrecedence > 4) {
|
||||
// We need to parenthesize arrow functions on the right side to avoid it being
|
||||
// parsed as parenthesized expression: `a && (() => {})`
|
||||
return true;
|
||||
}
|
||||
const operandPrecedence = getExpressionPrecedence(emittedOperand);
|
||||
switch (compareValues(operandPrecedence, binaryOperatorPrecedence)) {
|
||||
case Comparison.LessThan:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Used by importFixes to synthesize import module specifiers.
|
||||
// Used by importFixes, getEditsForFileRename, and declaration emit to synthesize import module specifiers.
|
||||
/* @internal */
|
||||
namespace ts.moduleSpecifiers {
|
||||
export interface ModuleSpecifierPreferences {
|
||||
@@ -14,13 +14,26 @@ namespace ts.moduleSpecifiers {
|
||||
host: ModuleSpecifierResolutionHost,
|
||||
files: ReadonlyArray<SourceFile>,
|
||||
preferences: ModuleSpecifierPreferences = {},
|
||||
redirectTargetsMap: RedirectTargetsMap,
|
||||
): string {
|
||||
const info = getInfo(compilerOptions, importingSourceFile, importingSourceFileName, host);
|
||||
const modulePaths = getAllModulePaths(files, toFileName, info.getCanonicalFileName, host);
|
||||
const modulePaths = getAllModulePaths(files, importingSourceFileName, toFileName, info.getCanonicalFileName, host, redirectTargetsMap);
|
||||
return firstDefined(modulePaths, moduleFileName => getGlobalModuleSpecifier(moduleFileName, info, host, compilerOptions)) ||
|
||||
first(getLocalModuleSpecifiers(toFileName, info, compilerOptions, preferences));
|
||||
}
|
||||
|
||||
export function getModuleSpecifierForDeclarationFile(
|
||||
moduleSymbol: Symbol,
|
||||
compilerOptions: CompilerOptions,
|
||||
importingSourceFile: SourceFile,
|
||||
host: ModuleSpecifierResolutionHost,
|
||||
files: ReadonlyArray<SourceFile>,
|
||||
preferences: ModuleSpecifierPreferences,
|
||||
redirectTargetsMap: RedirectTargetsMap,
|
||||
): string {
|
||||
return first(first(getModuleSpecifiers(moduleSymbol, compilerOptions, importingSourceFile, host, files, preferences, redirectTargetsMap)));
|
||||
}
|
||||
|
||||
// For each symlink/original for a module, returns a list of ways to import that file.
|
||||
export function getModuleSpecifiers(
|
||||
moduleSymbol: Symbol,
|
||||
@@ -29,6 +42,7 @@ namespace ts.moduleSpecifiers {
|
||||
host: ModuleSpecifierResolutionHost,
|
||||
files: ReadonlyArray<SourceFile>,
|
||||
preferences: ModuleSpecifierPreferences,
|
||||
redirectTargetsMap: RedirectTargetsMap,
|
||||
): ReadonlyArray<ReadonlyArray<string>> {
|
||||
const ambient = tryGetModuleNameFromAmbientModule(moduleSymbol);
|
||||
if (ambient) return [[ambient]];
|
||||
@@ -37,7 +51,8 @@ namespace ts.moduleSpecifiers {
|
||||
if (!files) {
|
||||
return Debug.fail("Files list must be present to resolve symlinks in specifier resolution");
|
||||
}
|
||||
const modulePaths = getAllModulePaths(files, getSourceFileOfNode(moduleSymbol.valueDeclaration).fileName, info.getCanonicalFileName, host);
|
||||
const moduleSourceFile = getSourceFileOfNode(moduleSymbol.valueDeclaration);
|
||||
const modulePaths = getAllModulePaths(files, importingSourceFile.path, moduleSourceFile.fileName, info.getCanonicalFileName, host, redirectTargetsMap);
|
||||
|
||||
const global = mapDefined(modulePaths, moduleFileName => getGlobalModuleSpecifier(moduleFileName, info, host, compilerOptions));
|
||||
return global.length ? global.map(g => [g]) : modulePaths.map(moduleFileName =>
|
||||
@@ -142,64 +157,69 @@ namespace ts.moduleSpecifiers {
|
||||
return firstDefined(imports, ({ text }) => pathIsRelative(text) ? fileExtensionIs(text, Extension.Js) : undefined) || false;
|
||||
}
|
||||
|
||||
function discoverProbableSymlinks(files: ReadonlyArray<SourceFile>, getCanonicalFileName: (file: string) => string, host: ModuleSpecifierResolutionHost) {
|
||||
function stringsEqual(a: string, b: string, getCanonicalFileName: GetCanonicalFileName): boolean {
|
||||
return getCanonicalFileName(a) === getCanonicalFileName(b);
|
||||
}
|
||||
|
||||
// KLUDGE: Don't assume one 'node_modules' links to another. More likely a single directory inside the node_modules is the symlink.
|
||||
// ALso, don't assume that an `@foo` directory is linked. More likely the contents of that are linked.
|
||||
function isNodeModulesOrScopedPackageDirectory(s: string, getCanonicalFileName: GetCanonicalFileName): boolean {
|
||||
return getCanonicalFileName(s) === "node_modules" || startsWith(s, "@");
|
||||
}
|
||||
|
||||
function guessDirectorySymlink(a: string, b: string, cwd: string, getCanonicalFileName: GetCanonicalFileName): [string, string] {
|
||||
const aParts = getPathComponents(toPath(a, cwd, getCanonicalFileName));
|
||||
const bParts = getPathComponents(toPath(b, cwd, getCanonicalFileName));
|
||||
while (!isNodeModulesOrScopedPackageDirectory(aParts[aParts.length - 2], getCanonicalFileName) &&
|
||||
!isNodeModulesOrScopedPackageDirectory(bParts[bParts.length - 2], getCanonicalFileName) &&
|
||||
stringsEqual(aParts[aParts.length - 1], bParts[bParts.length - 1], getCanonicalFileName)) {
|
||||
aParts.pop();
|
||||
bParts.pop();
|
||||
}
|
||||
return [getPathFromPathComponents(aParts), getPathFromPathComponents(bParts)];
|
||||
}
|
||||
|
||||
function discoverProbableSymlinks(files: ReadonlyArray<SourceFile>, getCanonicalFileName: GetCanonicalFileName, cwd: string): ReadonlyMap<string> {
|
||||
const result = createMap<string>();
|
||||
const symlinks = mapDefined(files, sf =>
|
||||
sf.resolvedModules && firstDefinedIterator(sf.resolvedModules.values(), res =>
|
||||
res && res.originalPath && res.resolvedFileName !== res.originalPath ? [res.resolvedFileName, res.originalPath] : undefined));
|
||||
const result = createMap<string>();
|
||||
if (symlinks) {
|
||||
const currentDirectory = host.getCurrentDirectory ? host.getCurrentDirectory() : "";
|
||||
const compareStrings = (!host.useCaseSensitiveFileNames || host.useCaseSensitiveFileNames()) ? compareStringsCaseSensitive : compareStringsCaseInsensitive;
|
||||
for (const [resolvedPath, originalPath] of symlinks) {
|
||||
const resolvedParts = getPathComponents(toPath(resolvedPath, currentDirectory, getCanonicalFileName));
|
||||
const originalParts = getPathComponents(toPath(originalPath, currentDirectory, getCanonicalFileName));
|
||||
while (compareStrings(resolvedParts[resolvedParts.length - 1], originalParts[originalParts.length - 1]) === Comparison.EqualTo) {
|
||||
resolvedParts.pop();
|
||||
originalParts.pop();
|
||||
}
|
||||
result.set(getPathFromPathComponents(originalParts), getPathFromPathComponents(resolvedParts));
|
||||
}
|
||||
for (const [resolvedPath, originalPath] of symlinks) {
|
||||
const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedPath, originalPath, cwd, getCanonicalFileName);
|
||||
result.set(commonOriginal, commonResolved);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getAllModulePathsUsingIndirectSymlinks(files: ReadonlyArray<SourceFile>, target: string, getCanonicalFileName: (file: string) => string, host: ModuleSpecifierResolutionHost) {
|
||||
const links = discoverProbableSymlinks(files, getCanonicalFileName, host);
|
||||
const paths = arrayFrom(links.keys());
|
||||
let options: string[] | undefined;
|
||||
const compareStrings = (!host.useCaseSensitiveFileNames || host.useCaseSensitiveFileNames()) ? compareStringsCaseSensitive : compareStringsCaseInsensitive;
|
||||
for (const path of paths) {
|
||||
const resolved = links.get(path)!;
|
||||
if (compareStrings(target.slice(0, resolved.length + 1), resolved + "/") === Comparison.EqualTo) {
|
||||
const relative = getRelativePathFromDirectory(resolved, target, getCanonicalFileName);
|
||||
const option = resolvePath(path, relative);
|
||||
if (!host.fileExists || host.fileExists(option)) {
|
||||
if (!options) options = [];
|
||||
options.push(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options) {
|
||||
options.push(target); // Since these are speculative, we also include the original resolved name as a possibility
|
||||
return options;
|
||||
}
|
||||
return [target];
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for existing imports that use symlinks to this module.
|
||||
* Only if no symlink is available, the real path will be used.
|
||||
* Symlinks will be returned first so they are preferred over the real path.
|
||||
*/
|
||||
function getAllModulePaths(files: ReadonlyArray<SourceFile>, importedFileName: string, getCanonicalFileName: (file: string) => string, host: ModuleSpecifierResolutionHost): ReadonlyArray<string> {
|
||||
const symlinks = mapDefined(files, sf =>
|
||||
sf.resolvedModules && firstDefinedIterator(sf.resolvedModules.values(), res =>
|
||||
res && res.resolvedFileName === importedFileName ? res.originalPath : undefined));
|
||||
function getAllModulePaths(files: ReadonlyArray<SourceFile>, importingFileName: string, importedFileName: string, getCanonicalFileName: GetCanonicalFileName, host: ModuleSpecifierResolutionHost, redirectTargetsMap: RedirectTargetsMap): ReadonlyArray<string> {
|
||||
const redirects = redirectTargetsMap.get(importedFileName);
|
||||
const importedFileNames = redirects ? [...redirects, importedFileName] : [importedFileName];
|
||||
const cwd = host.getCurrentDirectory ? host.getCurrentDirectory() : "";
|
||||
const baseOptions = getAllModulePathsUsingIndirectSymlinks(files, getNormalizedAbsolutePath(importedFileName, cwd), getCanonicalFileName, host);
|
||||
if (symlinks.length === 0) {
|
||||
return baseOptions;
|
||||
}
|
||||
return deduplicate(concatenate(baseOptions, flatMap(symlinks, importedFileName => getAllModulePathsUsingIndirectSymlinks(files, getNormalizedAbsolutePath(importedFileName, cwd), getCanonicalFileName, host))));
|
||||
const targets = importedFileNames.map(f => getNormalizedAbsolutePath(f, cwd));
|
||||
const links = discoverProbableSymlinks(files, getCanonicalFileName, cwd);
|
||||
|
||||
const result: string[] = [];
|
||||
const compareStrings = (!host.useCaseSensitiveFileNames || host.useCaseSensitiveFileNames()) ? compareStringsCaseSensitive : compareStringsCaseInsensitive;
|
||||
links.forEach((resolved, path) => {
|
||||
if (startsWithDirectory(importingFileName, resolved, getCanonicalFileName)) {
|
||||
return; // Don't want to a package to globally import from itself
|
||||
}
|
||||
|
||||
const target = targets.find(t => compareStrings(t.slice(0, resolved.length + 1), resolved + "/") === Comparison.EqualTo);
|
||||
if (target === undefined) return;
|
||||
|
||||
const relative = getRelativePathFromDirectory(resolved, target, getCanonicalFileName);
|
||||
const option = resolvePath(path, relative);
|
||||
if (!host.fileExists || host.fileExists(option)) {
|
||||
result.push(option);
|
||||
}
|
||||
});
|
||||
result.push(...targets);
|
||||
return result;
|
||||
}
|
||||
|
||||
function getRelativePathNParents(relativePath: string): number {
|
||||
|
||||
+20
-6
@@ -491,6 +491,8 @@ namespace ts {
|
||||
visitNode(cbNode, (node as JSDocCallbackTag).typeExpression);
|
||||
case SyntaxKind.JSDocThisTag:
|
||||
return visitNode(cbNode, (node as JSDocThisTag).typeExpression);
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
return visitNode(cbNode, (node as JSDocEnumTag).typeExpression);
|
||||
case SyntaxKind.JSDocSignature:
|
||||
return visitNodes(cbNode, cbNodes, node.decorators) ||
|
||||
visitNodes(cbNode, cbNodes, node.modifiers) ||
|
||||
@@ -6398,7 +6400,7 @@ namespace ts {
|
||||
switch (token()) {
|
||||
case SyntaxKind.AtToken:
|
||||
if (state === JSDocState.BeginningOfLine || state === JSDocState.SawAsterisk) {
|
||||
removeTrailingNewlines(comments);
|
||||
removeTrailingWhitespace(comments);
|
||||
addTag(parseTag(indent));
|
||||
// NOTE: According to usejsdoc.org, a tag goes to end of line, except the last tag.
|
||||
// Real-world comments may break this rule, so "BeginningOfLine" will not be a real line beginning
|
||||
@@ -6458,7 +6460,7 @@ namespace ts {
|
||||
nextJSDocToken();
|
||||
}
|
||||
removeLeadingNewlines(comments);
|
||||
removeTrailingNewlines(comments);
|
||||
removeTrailingWhitespace(comments);
|
||||
return createJSDocComment();
|
||||
});
|
||||
|
||||
@@ -6468,8 +6470,8 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function removeTrailingNewlines(comments: string[]) {
|
||||
while (comments.length && (comments[comments.length - 1] === "\n" || comments[comments.length - 1] === "\r")) {
|
||||
function removeTrailingWhitespace(comments: string[]) {
|
||||
while (comments.length && comments[comments.length - 1].trim() === "") {
|
||||
comments.pop();
|
||||
}
|
||||
}
|
||||
@@ -6527,6 +6529,9 @@ namespace ts {
|
||||
case "this":
|
||||
tag = parseThisTag(atToken, tagName);
|
||||
break;
|
||||
case "enum":
|
||||
tag = parseEnumTag(atToken, tagName);
|
||||
break;
|
||||
case "arg":
|
||||
case "argument":
|
||||
case "param":
|
||||
@@ -6627,7 +6632,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
removeLeadingNewlines(comments);
|
||||
removeTrailingNewlines(comments);
|
||||
removeTrailingWhitespace(comments);
|
||||
return comments.length === 0 ? undefined : comments.join("");
|
||||
}
|
||||
|
||||
@@ -6817,6 +6822,15 @@ namespace ts {
|
||||
return finishNode(tag);
|
||||
}
|
||||
|
||||
function parseEnumTag(atToken: AtToken, tagName: Identifier): JSDocEnumTag {
|
||||
const tag = <JSDocEnumTag>createNode(SyntaxKind.JSDocEnumTag, atToken.pos);
|
||||
tag.atToken = atToken;
|
||||
tag.tagName = tagName;
|
||||
tag.typeExpression = parseJSDocTypeExpression(/*mayOmitBraces*/ true);
|
||||
skipWhitespace();
|
||||
return finishNode(tag);
|
||||
}
|
||||
|
||||
function parseTypedefTag(atToken: AtToken, tagName: Identifier, indent: number): JSDocTypedefTag {
|
||||
const typeExpression = tryParseTypeExpression();
|
||||
skipWhitespace();
|
||||
@@ -7030,8 +7044,8 @@ namespace ts {
|
||||
skipWhitespace();
|
||||
const typeParameter = <TypeParameterDeclaration>createNode(SyntaxKind.TypeParameter);
|
||||
typeParameter.name = parseJSDocIdentifierName(Diagnostics.Unexpected_token_A_type_parameter_name_was_expected_without_curly_braces);
|
||||
skipWhitespace();
|
||||
finishNode(typeParameter);
|
||||
skipWhitespace();
|
||||
typeParameters.push(typeParameter);
|
||||
} while (parseOptionalJsdoc(SyntaxKind.CommaToken));
|
||||
|
||||
|
||||
+11
-19
@@ -634,7 +634,8 @@ namespace ts {
|
||||
const packageIdToSourceFile = createMap<SourceFile>();
|
||||
// Maps from a SourceFile's `.path` to the name of the package it was imported with.
|
||||
let sourceFileToPackageName = createMap<string>();
|
||||
let redirectTargetsSet = createMap<true>();
|
||||
// Key is a file name. Value is the (non-empty, or undefined) list of files that redirect to it.
|
||||
let redirectTargetsMap = createMultiMap<string>();
|
||||
|
||||
const filesByName = createMap<SourceFile | undefined>();
|
||||
let missingFilePaths: ReadonlyArray<Path> | undefined;
|
||||
@@ -752,7 +753,7 @@ namespace ts {
|
||||
getSourceFileFromReference,
|
||||
getLibFileFromReference,
|
||||
sourceFileToPackageName,
|
||||
redirectTargetsSet,
|
||||
redirectTargetsMap,
|
||||
isEmittedFile,
|
||||
getConfigFileParsingDiagnostics,
|
||||
getResolvedModuleWithFailedLookupLocationsFromCache,
|
||||
@@ -1073,7 +1074,7 @@ namespace ts {
|
||||
fileChanged = false;
|
||||
newSourceFile = oldSourceFile; // Use the redirect.
|
||||
}
|
||||
else if (oldProgram.redirectTargetsSet.has(oldSourceFile.path)) {
|
||||
else if (oldProgram.redirectTargetsMap.has(oldSourceFile.path)) {
|
||||
// If a redirected-to source file changes, the redirect may be broken.
|
||||
if (newSourceFile !== oldSourceFile) {
|
||||
return oldProgram.structureIsReused = StructureIsReused.Not;
|
||||
@@ -1220,7 +1221,7 @@ namespace ts {
|
||||
resolvedProjectReferences = oldProgram.getProjectReferences();
|
||||
|
||||
sourceFileToPackageName = oldProgram.sourceFileToPackageName;
|
||||
redirectTargetsSet = oldProgram.redirectTargetsSet;
|
||||
redirectTargetsMap = oldProgram.redirectTargetsMap;
|
||||
|
||||
return oldProgram.structureIsReused = StructureIsReused.Completely;
|
||||
}
|
||||
@@ -2079,7 +2080,7 @@ namespace ts {
|
||||
// Some other SourceFile already exists with this package name and version.
|
||||
// Instead of creating a duplicate, just redirect to the existing one.
|
||||
const dupFile = createRedirectSourceFile(fileFromPackageId, file!, fileName, path); // TODO: GH#18217
|
||||
redirectTargetsSet.set(fileFromPackageId.path, true);
|
||||
redirectTargetsMap.add(fileFromPackageId.path, fileName);
|
||||
filesByName.set(path, dupFile);
|
||||
sourceFileToPackageName.set(path, packageId.name);
|
||||
processingOtherFiles!.push(dupFile);
|
||||
@@ -2711,12 +2712,12 @@ namespace ts {
|
||||
|
||||
function createDiagnosticForReference(index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) {
|
||||
const referencesSyntax = getProjectReferencesSyntax();
|
||||
if (referencesSyntax) {
|
||||
if (createOptionDiagnosticInArrayLiteralSyntax(referencesSyntax, index, message, arg0, arg1)) {
|
||||
return;
|
||||
}
|
||||
if (referencesSyntax && referencesSyntax.elements.length > index) {
|
||||
programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, referencesSyntax.elements[index], message, arg0, arg1));
|
||||
}
|
||||
else {
|
||||
programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1));
|
||||
}
|
||||
programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1));
|
||||
}
|
||||
|
||||
function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number) {
|
||||
@@ -2769,15 +2770,6 @@ namespace ts {
|
||||
return !!props.length;
|
||||
}
|
||||
|
||||
function createOptionDiagnosticInArrayLiteralSyntax(arrayLiteral: ArrayLiteralExpression, index: number, message: DiagnosticMessage, arg0: string | number | undefined, arg1?: string | number, arg2?: string | number): boolean {
|
||||
if (arrayLiteral.elements.length <= index) {
|
||||
// Out-of-bounds
|
||||
return false;
|
||||
}
|
||||
programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, arrayLiteral.elements[index], message, arg0, arg1, arg2));
|
||||
return false; // TODO: GH#18217 This function always returns `false`!`
|
||||
}
|
||||
|
||||
function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) {
|
||||
hasEmitBlockingDiagnostics.set(toPath(emitFileName), true);
|
||||
programDiagnostics.add(diag);
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace ts {
|
||||
getGlobalCache?(): string | undefined;
|
||||
writeLog(s: string): void;
|
||||
maxNumberOfFilesToIterateForInvalidation?: number;
|
||||
getCurrentProgram(): Program;
|
||||
getCurrentProgram(): Program | undefined;
|
||||
}
|
||||
|
||||
interface DirectoryWatchesOfFailedLookup {
|
||||
@@ -398,8 +398,17 @@ namespace ts {
|
||||
|
||||
function getDirectoryToWatchFailedLookupLocation(failedLookupLocation: string, failedLookupLocationPath: Path): DirectoryOfFailedLookupWatch {
|
||||
if (isInDirectoryPath(rootPath, failedLookupLocationPath)) {
|
||||
// Always watch root directory recursively
|
||||
return { dir: rootDir!, dirPath: rootPath }; // TODO: GH#18217
|
||||
failedLookupLocation = isRootedDiskPath(failedLookupLocation) ? failedLookupLocation : getNormalizedAbsolutePath(failedLookupLocation, getCurrentDirectory());
|
||||
Debug.assert(failedLookupLocation.length === failedLookupLocationPath.length, `FailedLookup: ${failedLookupLocation} failedLookupLocationPath: ${failedLookupLocationPath}`); // tslint:disable-line
|
||||
const subDirectoryInRoot = failedLookupLocationPath.indexOf(directorySeparator, rootPath.length + 1);
|
||||
if (subDirectoryInRoot !== -1) {
|
||||
// Instead of watching root, watch directory in root to avoid watching excluded directories not needed for module resolution
|
||||
return { dir: failedLookupLocation.substr(0, subDirectoryInRoot), dirPath: failedLookupLocationPath.substr(0, subDirectoryInRoot) as Path };
|
||||
}
|
||||
else {
|
||||
// Always watch root directory non recursively
|
||||
return { dir: rootDir!, dirPath: rootPath, nonRecursive: false }; // TODO: GH#18217
|
||||
}
|
||||
}
|
||||
|
||||
return getDirectoryToWatchFromFailedLookupLocationDirectory(
|
||||
@@ -478,6 +487,7 @@ namespace ts {
|
||||
customFailedLookupPaths.set(failedLookupLocationPath, refCount + 1);
|
||||
}
|
||||
if (dirPath === rootPath) {
|
||||
Debug.assert(!nonRecursive);
|
||||
setAtRoot = true;
|
||||
}
|
||||
else {
|
||||
@@ -487,8 +497,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (setAtRoot) {
|
||||
// This is always recursive
|
||||
setDirectoryWatcher(rootDir!, rootPath); // TODO: GH#18217
|
||||
// This is always non recursive
|
||||
setDirectoryWatcher(rootDir!, rootPath, /*nonRecursive*/ true); // TODO: GH#18217
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,7 +507,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function watchFailedLookupLocationOfNonRelativeModuleResolutions(resolutions: ResolutionWithFailedLookupLocations[], name: string) {
|
||||
const updateResolution = resolutionHost.getCurrentProgram().getTypeChecker().tryFindAmbientModuleWithoutAugmentations(name) ?
|
||||
const program = resolutionHost.getCurrentProgram();
|
||||
const updateResolution = program && program.getTypeChecker().tryFindAmbientModuleWithoutAugmentations(name) ?
|
||||
setRefCountToUndefined : watchFailedLookupLocationOfResolution;
|
||||
resolutions.forEach(updateResolution);
|
||||
}
|
||||
|
||||
@@ -1752,6 +1752,12 @@ namespace ts {
|
||||
type SerializedEntityNameAsExpression = Identifier | BinaryExpression | PropertyAccessExpression;
|
||||
type SerializedTypeNode = SerializedEntityNameAsExpression | VoidExpression | ConditionalExpression;
|
||||
|
||||
function getAccessorTypeNode(node: AccessorDeclaration) {
|
||||
const accessors = resolver.getAllAccessorDeclarations(node);
|
||||
return accessors.setAccessor && getSetAccessorTypeAnnotationNode(accessors.setAccessor)
|
||||
|| accessors.getAccessor && getEffectiveReturnTypeNode(accessors.getAccessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the type of a node for use with decorator type metadata.
|
||||
*
|
||||
@@ -1761,10 +1767,10 @@ namespace ts {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.Parameter:
|
||||
case SyntaxKind.GetAccessor:
|
||||
return serializeTypeNode((<PropertyDeclaration | ParameterDeclaration | GetAccessorDeclaration>node).type);
|
||||
case SyntaxKind.SetAccessor:
|
||||
return serializeTypeNode(getSetAccessorTypeAnnotationNode(<SetAccessorDeclaration>node));
|
||||
case SyntaxKind.GetAccessor:
|
||||
return serializeTypeNode(getAccessorTypeNode(node as AccessorDeclaration));
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
|
||||
@@ -1268,7 +1268,7 @@ namespace ts {
|
||||
// Don't report status on "solution" projects
|
||||
break;
|
||||
default:
|
||||
assertTypeIsNever(status);
|
||||
assertType<never>(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-5
@@ -372,6 +372,7 @@ namespace ts {
|
||||
JSDocAugmentsTag,
|
||||
JSDocClassTag,
|
||||
JSDocCallbackTag,
|
||||
JSDocEnumTag,
|
||||
JSDocParameterTag,
|
||||
JSDocReturnTag,
|
||||
JSDocThisTag,
|
||||
@@ -581,6 +582,7 @@ namespace ts {
|
||||
| FunctionTypeNode
|
||||
| ConstructorTypeNode
|
||||
| JSDocFunctionType
|
||||
| ExportDeclaration
|
||||
| EndOfFileToken;
|
||||
|
||||
export type HasType =
|
||||
@@ -858,6 +860,7 @@ namespace ts {
|
||||
name?: PropertyName;
|
||||
}
|
||||
|
||||
/** Unlike ObjectLiteralElement, excludes JSXAttribute and JSXSpreadAttribute. */
|
||||
export type ObjectLiteralElementLike
|
||||
= PropertyAssignment
|
||||
| ShorthandPropertyAssignment
|
||||
@@ -2202,7 +2205,7 @@ namespace ts {
|
||||
name: Identifier;
|
||||
}
|
||||
|
||||
export interface ExportDeclaration extends DeclarationStatement {
|
||||
export interface ExportDeclaration extends DeclarationStatement, JSDocContainer {
|
||||
kind: SyntaxKind.ExportDeclaration;
|
||||
parent: SourceFile | ModuleBlock;
|
||||
/** Will not be assigned in the case of `export * from "foo";` */
|
||||
@@ -2348,6 +2351,11 @@ namespace ts {
|
||||
kind: SyntaxKind.JSDocClassTag;
|
||||
}
|
||||
|
||||
export interface JSDocEnumTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocEnumTag;
|
||||
typeExpression?: JSDocTypeExpression;
|
||||
}
|
||||
|
||||
export interface JSDocThisTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocThisTag;
|
||||
typeExpression?: JSDocTypeExpression;
|
||||
@@ -2543,7 +2551,7 @@ namespace ts {
|
||||
/**
|
||||
* If two source files are for the same version of the same package, one will redirect to the other.
|
||||
* (See `createRedirectSourceFile` in program.ts.)
|
||||
* The redirect will have this set. The redirected-to source file will be in `redirectTargetsSet`.
|
||||
* The redirect will have this set. The redirected-to source file will be in `redirectTargetsMap`.
|
||||
*/
|
||||
/* @internal */ redirectInfo?: RedirectInfo;
|
||||
|
||||
@@ -2790,7 +2798,7 @@ namespace ts {
|
||||
/** Given a source file, get the name of the package it was imported from. */
|
||||
/* @internal */ sourceFileToPackageName: Map<string>;
|
||||
/** Set of all source files that some other source file redirects to. */
|
||||
/* @internal */ redirectTargetsSet: Map<true>;
|
||||
/* @internal */ redirectTargetsMap: MultiMap<string>;
|
||||
/** Is the file emitted file */
|
||||
/* @internal */ isEmittedFile(file: string): boolean;
|
||||
|
||||
@@ -2799,6 +2807,9 @@ namespace ts {
|
||||
getProjectReferences(): (ResolvedProjectReference | undefined)[] | undefined;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export type RedirectTargetsMap = ReadonlyMap<ReadonlyArray<string>>;
|
||||
|
||||
export interface ResolvedProjectReference {
|
||||
commandLine: ParsedCommandLine;
|
||||
sourceFile: SourceFile;
|
||||
@@ -2876,6 +2887,8 @@ namespace ts {
|
||||
getSourceFiles(): ReadonlyArray<SourceFile>;
|
||||
getSourceFile(fileName: string): SourceFile | undefined;
|
||||
getResolvedTypeReferenceDirectives(): ReadonlyMap<ResolvedTypeReferenceDirective>;
|
||||
|
||||
readonly redirectTargetsMap: RedirectTargetsMap;
|
||||
}
|
||||
|
||||
export interface TypeChecker {
|
||||
@@ -2964,6 +2977,7 @@ namespace ts {
|
||||
* @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;
|
||||
/* @internal */ getResolvedSignatureForSignatureHelp(node: CallLikeExpression, candidatesOutArray?: Signature[], argumentCount?: number): Signature | undefined;
|
||||
getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature | undefined;
|
||||
isImplementationOfOverload(node: SignatureDeclaration): boolean | undefined;
|
||||
isUndefinedSymbol(symbol: Symbol): boolean;
|
||||
@@ -3306,8 +3320,8 @@ namespace ts {
|
||||
export interface AllAccessorDeclarations {
|
||||
firstAccessor: AccessorDeclaration;
|
||||
secondAccessor: AccessorDeclaration | undefined;
|
||||
getAccessor: AccessorDeclaration | undefined;
|
||||
setAccessor: AccessorDeclaration | undefined;
|
||||
getAccessor: GetAccessorDeclaration | undefined;
|
||||
setAccessor: SetAccessorDeclaration | undefined;
|
||||
}
|
||||
|
||||
/** Indicates how to serialize the name for a TypeReferenceNode when emitting decorator metadata */
|
||||
|
||||
+111
-26
@@ -563,11 +563,6 @@ namespace ts {
|
||||
return isString(value) ? '"' + escapeNonAsciiString(value) + '"' : "" + value;
|
||||
}
|
||||
|
||||
// Add an extra underscore to identifiers that start with two underscores to avoid issues with magic names like '__proto__'
|
||||
export function escapeLeadingUnderscores(identifier: string): __String {
|
||||
return (identifier.length >= 2 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ ? "_" + identifier : identifier) as __String;
|
||||
}
|
||||
|
||||
// Make an identifier from an external module name by extracting the string after the last "/" and replacing
|
||||
// all non-alphanumeric characters with underscores
|
||||
export function makeIdentifierFromModuleName(moduleName: string): string {
|
||||
@@ -676,6 +671,19 @@ namespace ts {
|
||||
|
||||
export function isDeclarationWithTypeParameters(node: Node): node is DeclarationWithTypeParameters;
|
||||
export function isDeclarationWithTypeParameters(node: DeclarationWithTypeParameters): node is DeclarationWithTypeParameters {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocSignature:
|
||||
return true;
|
||||
default:
|
||||
assertType<DeclarationWithTypeParameterChildren>(node);
|
||||
return isDeclarationWithTypeParameterChildren(node);
|
||||
}
|
||||
}
|
||||
|
||||
export function isDeclarationWithTypeParameterChildren(node: Node): node is DeclarationWithTypeParameterChildren;
|
||||
export function isDeclarationWithTypeParameterChildren(node: DeclarationWithTypeParameterChildren): node is DeclarationWithTypeParameterChildren {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
@@ -696,12 +704,9 @@ namespace ts {
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocSignature:
|
||||
return true;
|
||||
default:
|
||||
assertTypeIsNever(node);
|
||||
assertType<never>(node);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -786,7 +791,7 @@ namespace ts {
|
||||
return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
export function createDiagnosticForNodeArray(sourceFile: SourceFile, nodes: NodeArray<Node>, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
|
||||
export function createDiagnosticForNodeArray(sourceFile: SourceFile, nodes: NodeArray<Node>, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): DiagnosticWithLocation {
|
||||
const start = skipTrivia(sourceFile.text, nodes.pos);
|
||||
return createFileDiagnostic(sourceFile, start, nodes.end - start, message, arg0, arg1, arg2, arg3);
|
||||
}
|
||||
@@ -3305,8 +3310,8 @@ namespace ts {
|
||||
// TODO: GH#18217
|
||||
let firstAccessor!: AccessorDeclaration;
|
||||
let secondAccessor!: AccessorDeclaration;
|
||||
let getAccessor!: AccessorDeclaration;
|
||||
let setAccessor!: AccessorDeclaration;
|
||||
let getAccessor!: GetAccessorDeclaration;
|
||||
let setAccessor!: SetAccessorDeclaration;
|
||||
if (hasDynamicName(accessor)) {
|
||||
firstAccessor = accessor;
|
||||
if (accessor.kind === SyntaxKind.GetAccessor) {
|
||||
@@ -3334,11 +3339,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (member.kind === SyntaxKind.GetAccessor && !getAccessor) {
|
||||
getAccessor = <AccessorDeclaration>member;
|
||||
getAccessor = <GetAccessorDeclaration>member;
|
||||
}
|
||||
|
||||
if (member.kind === SyntaxKind.SetAccessor && !setAccessor) {
|
||||
setAccessor = <AccessorDeclaration>member;
|
||||
setAccessor = <SetAccessorDeclaration>member;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4794,6 +4799,11 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Add an extra underscore to identifiers that start with two underscores to avoid issues with magic names like '__proto__' */
|
||||
export function escapeLeadingUnderscores(identifier: string): __String {
|
||||
return (identifier.length >= 2 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ ? "_" + identifier : identifier) as __String;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove extra underscore from escaped identifier text content.
|
||||
*
|
||||
@@ -4985,6 +4995,11 @@ namespace ts {
|
||||
return getFirstJSDocTag(node, isJSDocClassTag);
|
||||
}
|
||||
|
||||
/** Gets the JSDoc enum tag for the node if present */
|
||||
export function getJSDocEnumTag(node: Node): JSDocEnumTag | undefined {
|
||||
return getFirstJSDocTag(node, isJSDocEnumTag);
|
||||
}
|
||||
|
||||
/** Gets the JSDoc this tag for the node if present */
|
||||
export function getJSDocThisTag(node: Node): JSDocThisTag | undefined {
|
||||
return getFirstJSDocTag(node, isJSDocThisTag);
|
||||
@@ -5749,6 +5764,10 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.JSDocClassTag;
|
||||
}
|
||||
|
||||
export function isJSDocEnumTag(node: Node): node is JSDocEnumTag {
|
||||
return node.kind === SyntaxKind.JSDocEnumTag;
|
||||
}
|
||||
|
||||
export function isJSDocThisTag(node: Node): node is JSDocThisTag {
|
||||
return node.kind === SyntaxKind.JSDocThisTag;
|
||||
}
|
||||
@@ -6622,18 +6641,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function isObjectLiteralElement(node: Node): node is ObjectLiteralElement {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.JsxAttribute:
|
||||
case SyntaxKind.JsxSpreadAttribute:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
case SyntaxKind.ShorthandPropertyAssignment:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return node.kind === SyntaxKind.JsxAttribute || node.kind === SyntaxKind.JsxSpreadAttribute || isObjectLiteralElementLike(node);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -7149,6 +7157,12 @@ namespace ts {
|
||||
return path.slice(0, Math.max(rootLength, path.lastIndexOf(directorySeparator)));
|
||||
}
|
||||
|
||||
export function startsWithDirectory(fileName: string, directoryName: string, getCanonicalFileName: GetCanonicalFileName): boolean {
|
||||
const canonicalFileName = getCanonicalFileName(fileName);
|
||||
const canonicalDirectoryName = getCanonicalFileName(directoryName);
|
||||
return startsWith(canonicalFileName, canonicalDirectoryName + "/") || startsWith(canonicalFileName, canonicalDirectoryName + "\\");
|
||||
}
|
||||
|
||||
export function isUrl(path: string) {
|
||||
return getEncodedRootLength(path) < 0;
|
||||
}
|
||||
@@ -8128,4 +8142,75 @@ namespace ts {
|
||||
}
|
||||
return { min, max };
|
||||
}
|
||||
|
||||
export interface ReadonlyNodeSet<TNode extends Node> {
|
||||
has(node: TNode): boolean;
|
||||
forEach(cb: (node: TNode) => void): void;
|
||||
some(pred: (node: TNode) => boolean): boolean;
|
||||
}
|
||||
|
||||
export class NodeSet<TNode extends Node> implements ReadonlyNodeSet<TNode> {
|
||||
private map = createMap<TNode>();
|
||||
|
||||
add(node: TNode): void {
|
||||
this.map.set(String(getNodeId(node)), node);
|
||||
}
|
||||
tryAdd(node: TNode): boolean {
|
||||
if (this.has(node)) return false;
|
||||
this.add(node);
|
||||
return true;
|
||||
}
|
||||
has(node: TNode): boolean {
|
||||
return this.map.has(String(getNodeId(node)));
|
||||
}
|
||||
forEach(cb: (node: TNode) => void): void {
|
||||
this.map.forEach(cb);
|
||||
}
|
||||
some(pred: (node: TNode) => boolean): boolean {
|
||||
return forEachEntry(this.map, pred) || false;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ReadonlyNodeMap<TNode extends Node, TValue> {
|
||||
get(node: TNode): TValue | undefined;
|
||||
has(node: TNode): boolean;
|
||||
}
|
||||
|
||||
export class NodeMap<TNode extends Node, TValue> implements ReadonlyNodeMap<TNode, TValue> {
|
||||
private map = createMap<{ node: TNode, value: TValue }>();
|
||||
|
||||
get(node: TNode): TValue | undefined {
|
||||
const res = this.map.get(String(getNodeId(node)));
|
||||
return res && res.value;
|
||||
}
|
||||
|
||||
getOrUpdate(node: TNode, setValue: () => TValue): TValue {
|
||||
const res = this.get(node);
|
||||
if (res) return res;
|
||||
const value = setValue();
|
||||
this.set(node, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
set(node: TNode, value: TValue): void {
|
||||
this.map.set(String(getNodeId(node)), { node, value });
|
||||
}
|
||||
|
||||
has(node: TNode): boolean {
|
||||
return this.map.has(String(getNodeId(node)));
|
||||
}
|
||||
|
||||
forEach(cb: (value: TValue, node: TNode) => void): void {
|
||||
this.map.forEach(({ node, value }) => cb(value, node));
|
||||
}
|
||||
}
|
||||
|
||||
export function rangeOfNode(node: Node): TextRange {
|
||||
return { pos: getTokenPosOfNode(node), end: node.end };
|
||||
}
|
||||
|
||||
export function rangeOfTypeParameters(typeParameters: NodeArray<TypeParameterDeclaration>): TextRange {
|
||||
// Include the `<>`
|
||||
return { pos: typeParameters.pos - 1, end: typeParameters.end + 1 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1496,8 +1496,8 @@ Actual: ${stringify(fullActual)}`);
|
||||
const actualTags = selectedItem.tags;
|
||||
assert.equal(actualTags.length, (options.tags || ts.emptyArray).length, this.assertionMessageAtLastKnownMarker("signature help tags"));
|
||||
ts.zipWith((options.tags || ts.emptyArray), actualTags, (expectedTag, actualTag) => {
|
||||
assert.equal(expectedTag.name, actualTag.name);
|
||||
assert.equal(expectedTag.text, actualTag.text, this.assertionMessageAtLastKnownMarker("signature help tag " + actualTag.name));
|
||||
assert.equal(actualTag.name, expectedTag.name);
|
||||
assert.equal(actualTag.text, expectedTag.text, this.assertionMessageAtLastKnownMarker("signature help tag " + actualTag.name));
|
||||
});
|
||||
|
||||
const allKeys: ReadonlyArray<keyof FourSlashInterface.VerifySignatureHelpOptions> = [
|
||||
|
||||
@@ -75,10 +75,17 @@ class TypeWriterWalker {
|
||||
const sourceText = ts.getSourceTextOfNodeFromSourceFile(this.currentSourceFile, node);
|
||||
|
||||
if (!isSymbolWalk) {
|
||||
// Don't try to get the type of something that's already a type.
|
||||
// Exception for `T` in `type T = something` because that may evaluate to some interesting type.
|
||||
if (ts.isPartOfTypeNode(node) || ts.isIdentifier(node) && !(ts.getMeaningFromDeclaration(node.parent) & ts.SemanticMeaning.Value) && !(ts.isTypeAlias(node.parent) && node.parent.name === node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Workaround to ensure we output 'C' instead of 'typeof C' for base class expressions
|
||||
// let type = this.checker.getTypeAtLocation(node);
|
||||
const type = node.parent && ts.isExpressionWithTypeArgumentsInClassExtendsClause(node.parent) && this.checker.getTypeAtLocation(node.parent) || this.checker.getTypeAtLocation(node);
|
||||
const typeString = type ? this.checker.typeToString(type, node.parent, ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.AllowUniqueESSymbolType) : "No type information available!";
|
||||
let type = ts.isExpressionWithTypeArgumentsInClassExtendsClause(node.parent) ? this.checker.getTypeAtLocation(node.parent) : undefined;
|
||||
if (!type || type.flags & ts.TypeFlags.Any) type = this.checker.getTypeAtLocation(node);
|
||||
const typeString = this.checker.typeToString(type, node.parent, ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.AllowUniqueESSymbolType);
|
||||
return {
|
||||
line: lineAndCharacter.line,
|
||||
syntaxKind: node.kind,
|
||||
|
||||
@@ -583,6 +583,10 @@ interface Array<T> {}`
|
||||
return;
|
||||
}
|
||||
this.invokeFileWatcher(fileOrDirectory.fullPath, FileWatcherEventKind.Created);
|
||||
if (isFsFolder(fileOrDirectory)) {
|
||||
this.invokeDirectoryWatcher(fileOrDirectory.fullPath, "");
|
||||
this.invokeWatchedDirectoriesRecursiveCallback(fileOrDirectory.fullPath, "");
|
||||
}
|
||||
this.invokeDirectoryWatcher(folder.fullPath, fileOrDirectory.fullPath);
|
||||
}
|
||||
|
||||
@@ -599,12 +603,11 @@ interface Array<T> {}`
|
||||
this.invokeFileWatcher(fileOrDirectory.fullPath, FileWatcherEventKind.Deleted);
|
||||
if (isFsFolder(fileOrDirectory)) {
|
||||
Debug.assert(fileOrDirectory.entries.length === 0 || isRenaming);
|
||||
const relativePath = this.getRelativePathToDirectory(fileOrDirectory.fullPath, fileOrDirectory.fullPath);
|
||||
// Invoke directory and recursive directory watcher for the folder
|
||||
// Here we arent invoking recursive directory watchers for the base folders
|
||||
// since that is something we would want to do for both file as well as folder we are deleting
|
||||
this.invokeWatchedDirectoriesCallback(fileOrDirectory.fullPath, relativePath);
|
||||
this.invokeWatchedDirectoriesRecursiveCallback(fileOrDirectory.fullPath, relativePath);
|
||||
this.invokeWatchedDirectoriesCallback(fileOrDirectory.fullPath, "");
|
||||
this.invokeWatchedDirectoriesRecursiveCallback(fileOrDirectory.fullPath, "");
|
||||
}
|
||||
|
||||
if (basePath !== fileOrDirectory.path) {
|
||||
|
||||
Vendored
+2313
-1900
File diff suppressed because it is too large
Load Diff
Vendored
+32
-4
@@ -31,10 +31,13 @@ interface DOMStringList {
|
||||
|
||||
interface DOMTokenList {
|
||||
[Symbol.iterator](): IterableIterator<string>;
|
||||
entries(): IterableIterator<[number, string]>;
|
||||
keys(): IterableIterator<number>;
|
||||
values(): IterableIterator<string>;
|
||||
}
|
||||
|
||||
interface DataTransferItemList {
|
||||
[Symbol.iterator](): IterableIterator<File>;
|
||||
[Symbol.iterator](): IterableIterator<DataTransferItem>;
|
||||
}
|
||||
|
||||
interface FileList {
|
||||
@@ -67,9 +70,10 @@ interface HTMLCollectionBase {
|
||||
|
||||
interface HTMLCollectionOf<T extends Element> {
|
||||
[Symbol.iterator](): IterableIterator<T>;
|
||||
entries(): IterableIterator<[number, T]>;
|
||||
keys(): IterableIterator<number>;
|
||||
values(): IterableIterator<T>;
|
||||
}
|
||||
|
||||
interface HTMLFormElement {
|
||||
[Symbol.iterator](): IterableIterator<Element>;
|
||||
}
|
||||
|
||||
interface HTMLSelectElement {
|
||||
@@ -147,10 +151,34 @@ interface PluginArray {
|
||||
interface RTCStatsReport extends ReadonlyMap<string, any> {
|
||||
}
|
||||
|
||||
interface SVGLengthList {
|
||||
[Symbol.iterator](): IterableIterator<SVGLength>;
|
||||
}
|
||||
|
||||
interface SVGNumberList {
|
||||
[Symbol.iterator](): IterableIterator<SVGNumber>;
|
||||
}
|
||||
|
||||
interface SVGStringList {
|
||||
[Symbol.iterator](): IterableIterator<string>;
|
||||
}
|
||||
|
||||
interface SourceBufferList {
|
||||
[Symbol.iterator](): IterableIterator<SourceBuffer>;
|
||||
}
|
||||
|
||||
interface SpeechGrammarList {
|
||||
[Symbol.iterator](): IterableIterator<SpeechGrammar>;
|
||||
}
|
||||
|
||||
interface SpeechRecognitionResult {
|
||||
[Symbol.iterator](): IterableIterator<SpeechRecognitionAlternative>;
|
||||
}
|
||||
|
||||
interface SpeechRecognitionResultList {
|
||||
[Symbol.iterator](): IterableIterator<SpeechRecognitionResult>;
|
||||
}
|
||||
|
||||
interface StyleSheetList {
|
||||
[Symbol.iterator](): IterableIterator<StyleSheet>;
|
||||
}
|
||||
|
||||
Vendored
+477
-94
File diff suppressed because it is too large
Load Diff
@@ -326,6 +326,13 @@ namespace ts.server {
|
||||
syntaxOnly?: boolean;
|
||||
}
|
||||
|
||||
interface OriginalFileInfo { fileName: NormalizedPath; path: Path; }
|
||||
type OpenScriptInfoOrClosedFileInfo = ScriptInfo | OriginalFileInfo;
|
||||
|
||||
function isOpenScriptInfo(infoOrFileName: OpenScriptInfoOrClosedFileInfo): infoOrFileName is ScriptInfo {
|
||||
return !!(infoOrFileName as ScriptInfo).containingProjects;
|
||||
}
|
||||
|
||||
function getDetailWatchInfo(watchType: WatchType, project: Project | undefined) {
|
||||
return `Project: ${project ? project.getProjectName() : ""} WatchType: ${watchType}`;
|
||||
}
|
||||
@@ -700,7 +707,7 @@ namespace ts.server {
|
||||
|
||||
/* @internal */
|
||||
private forEachProject(cb: (project: Project) => void) {
|
||||
for (const p of this.inferredProjects) cb(p);
|
||||
this.inferredProjects.forEach(cb);
|
||||
this.configuredProjects.forEach(cb);
|
||||
this.externalProjects.forEach(cb);
|
||||
}
|
||||
@@ -1044,12 +1051,12 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
private configFileExists(configFileName: NormalizedPath, canonicalConfigFilePath: string, info: ScriptInfo) {
|
||||
private configFileExists(configFileName: NormalizedPath, canonicalConfigFilePath: string, info: OpenScriptInfoOrClosedFileInfo) {
|
||||
let configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath);
|
||||
if (configFileExistenceInfo) {
|
||||
// By default the info would get impacted by presence of config file since its in the detection path
|
||||
// Only adding the info as a root to inferred project will need the existence to be watched by file watcher
|
||||
if (!configFileExistenceInfo.openFilesImpactedByConfigFile.has(info.path)) {
|
||||
if (isOpenScriptInfo(info) && !configFileExistenceInfo.openFilesImpactedByConfigFile.has(info.path)) {
|
||||
configFileExistenceInfo.openFilesImpactedByConfigFile.set(info.path, false);
|
||||
this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.OpenFilesImpactedByConfigFileAdd);
|
||||
}
|
||||
@@ -1066,9 +1073,11 @@ namespace ts.server {
|
||||
// Or the whole chain of config files for the roots of the inferred projects
|
||||
|
||||
// Cache the host value of file exists and add the info to map of open files impacted by this config file
|
||||
const openFilesImpactedByConfigFile = createMap<boolean>();
|
||||
openFilesImpactedByConfigFile.set(info.path, false);
|
||||
const exists = this.host.fileExists(configFileName);
|
||||
const openFilesImpactedByConfigFile = createMap<boolean>();
|
||||
if (isOpenScriptInfo(info)) {
|
||||
openFilesImpactedByConfigFile.set(info.path, false);
|
||||
}
|
||||
configFileExistenceInfo = { exists, openFilesImpactedByConfigFile };
|
||||
this.configFileExistenceInfoCache.set(canonicalConfigFilePath, configFileExistenceInfo);
|
||||
this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.OpenFilesImpactedByConfigFileAdd);
|
||||
@@ -1180,7 +1189,7 @@ namespace ts.server {
|
||||
*/
|
||||
private stopWatchingConfigFilesForClosedScriptInfo(info: ScriptInfo) {
|
||||
Debug.assert(!info.isScriptOpen());
|
||||
this.forEachConfigFileLocation(info, /*infoShouldBeOpen*/ true, (configFileName, canonicalConfigFilePath) => {
|
||||
this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => {
|
||||
const configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath);
|
||||
if (configFileExistenceInfo) {
|
||||
const infoIsRootOfInferredProject = configFileExistenceInfo.openFilesImpactedByConfigFile.get(info.path);
|
||||
@@ -1214,7 +1223,7 @@ namespace ts.server {
|
||||
/* @internal */
|
||||
startWatchingConfigFilesForInferredProjectRoot(info: ScriptInfo) {
|
||||
Debug.assert(info.isScriptOpen());
|
||||
this.forEachConfigFileLocation(info, /*infoShouldBeOpen*/ true, (configFileName, canonicalConfigFilePath) => {
|
||||
this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => {
|
||||
let configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath);
|
||||
if (!configFileExistenceInfo) {
|
||||
// Create the cache
|
||||
@@ -1242,7 +1251,7 @@ namespace ts.server {
|
||||
*/
|
||||
/* @internal */
|
||||
stopWatchingConfigFilesForInferredProjectRoot(info: ScriptInfo) {
|
||||
this.forEachConfigFileLocation(info, /*infoShouldBeOpen*/ true, (configFileName, canonicalConfigFilePath) => {
|
||||
this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => {
|
||||
const configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath);
|
||||
if (configFileExistenceInfo && configFileExistenceInfo.openFilesImpactedByConfigFile.has(info.path)) {
|
||||
Debug.assert(info.isScriptOpen());
|
||||
@@ -1265,12 +1274,12 @@ namespace ts.server {
|
||||
* The server must start searching from the directory containing
|
||||
* the newly opened file.
|
||||
*/
|
||||
private forEachConfigFileLocation(info: ScriptInfo, infoShouldBeOpen: boolean, action: (configFileName: NormalizedPath, canonicalConfigFilePath: string) => boolean | void) {
|
||||
private forEachConfigFileLocation(info: OpenScriptInfoOrClosedFileInfo, action: (configFileName: NormalizedPath, canonicalConfigFilePath: string) => boolean | void) {
|
||||
if (this.syntaxOnly) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Debug.assert(!infoShouldBeOpen || this.openFiles.has(info.path));
|
||||
Debug.assert(!isOpenScriptInfo(info) || this.openFiles.has(info.path));
|
||||
const projectRootPath = this.openFiles.get(info.path);
|
||||
|
||||
let searchPath = asNormalizedPath(getDirectoryPath(info.fileName));
|
||||
@@ -1309,11 +1318,13 @@ namespace ts.server {
|
||||
* current directory (the directory in which tsc was invoked).
|
||||
* The server must start searching from the directory containing
|
||||
* the newly opened file.
|
||||
* If script info is passed in, it is asserted to be open script info
|
||||
* otherwise just file name
|
||||
*/
|
||||
private getConfigFileNameForFile(info: ScriptInfo, infoShouldBeOpen: boolean) {
|
||||
if (infoShouldBeOpen) Debug.assert(info.isScriptOpen());
|
||||
private getConfigFileNameForFile(info: OpenScriptInfoOrClosedFileInfo) {
|
||||
if (isOpenScriptInfo(info)) Debug.assert(info.isScriptOpen());
|
||||
this.logger.info(`Search path: ${getDirectoryPath(info.fileName)}`);
|
||||
const configFileName = this.forEachConfigFileLocation(info, infoShouldBeOpen, (configFileName, canonicalConfigFilePath) =>
|
||||
const configFileName = this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) =>
|
||||
this.configFileExists(configFileName, canonicalConfigFilePath, info));
|
||||
if (configFileName) {
|
||||
this.logger.info(`For info: ${info.fileName} :: Config file name: ${configFileName}`);
|
||||
@@ -1683,10 +1694,12 @@ namespace ts.server {
|
||||
if (!this.eventHandler || this.suppressDiagnosticEvents) {
|
||||
return;
|
||||
}
|
||||
const diagnostics = project.getLanguageService().getCompilerOptionsDiagnostics();
|
||||
diagnostics.push(...project.getAllProjectErrors());
|
||||
|
||||
this.eventHandler(<ConfigFileDiagEvent>{
|
||||
eventName: ConfigFileDiagEvent,
|
||||
data: { configFileName: project.getConfigFilePath(), diagnostics: project.getAllProjectErrors(), triggerFile }
|
||||
data: { configFileName: project.getConfigFilePath(), diagnostics, triggerFile }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2003,7 +2016,7 @@ namespace ts.server {
|
||||
// we first detect if there is already a configured project created for it: if so,
|
||||
// we re- read the tsconfig file content and update the project only if we havent already done so
|
||||
// otherwise we create a new one.
|
||||
const configFileName = this.getConfigFileNameForFile(info, /*infoShouldBeOpen*/ true);
|
||||
const configFileName = this.getConfigFileNameForFile(info);
|
||||
if (configFileName) {
|
||||
const project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
if (!project) {
|
||||
@@ -2091,17 +2104,40 @@ namespace ts.server {
|
||||
return this.openClientFileWithNormalizedPath(toNormalizedPath(fileName), fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath ? toNormalizedPath(projectRootPath) : undefined);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
getProjectForFileWithoutOpening(fileName: NormalizedPath): { readonly scriptInfo: ScriptInfo, readonly projects: ReadonlyArray<Project> } | undefined {
|
||||
const scriptInfo = this.filenameToScriptInfo.get(fileName) ||
|
||||
this.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(fileName, this.currentDirectory, /*fileContent*/ undefined, /*scriptKind*/ undefined, /*hasMixedContent*/ undefined);
|
||||
if (!scriptInfo) return undefined;
|
||||
if (scriptInfo.containingProjects.length) {
|
||||
return { scriptInfo, projects: scriptInfo.containingProjects };
|
||||
/*@internal*/
|
||||
getOriginalLocationEnsuringConfiguredProject(project: Project, location: sourcemaps.SourceMappableLocation): sourcemaps.SourceMappableLocation | undefined {
|
||||
const originalLocation = project.getSourceMapper().tryGetOriginalLocation(location);
|
||||
if (!originalLocation) return undefined;
|
||||
|
||||
const { fileName } = originalLocation;
|
||||
if (!this.getScriptInfo(fileName) && !this.host.fileExists(fileName)) return undefined;
|
||||
|
||||
const originalFileInfo: OriginalFileInfo = { fileName: toNormalizedPath(fileName), path: this.toPath(fileName) };
|
||||
const configFileName = this.getConfigFileNameForFile(originalFileInfo);
|
||||
if (!configFileName) return undefined;
|
||||
|
||||
const configuredProject = this.findConfiguredProjectByProjectName(configFileName) || this.createConfiguredProject(configFileName);
|
||||
updateProjectIfDirty(configuredProject);
|
||||
// Keep this configured project as referenced from project
|
||||
addOriginalConfiguredProject(configuredProject);
|
||||
|
||||
const originalScriptInfo = this.getScriptInfo(fileName);
|
||||
if (!originalScriptInfo || !originalScriptInfo.containingProjects.length) return undefined;
|
||||
|
||||
// Add configured projects as referenced
|
||||
originalScriptInfo.containingProjects.forEach(project => {
|
||||
if (project.projectKind === ProjectKind.Configured) {
|
||||
addOriginalConfiguredProject(project as ConfiguredProject);
|
||||
}
|
||||
});
|
||||
return originalLocation;
|
||||
|
||||
function addOriginalConfiguredProject(originalProject: ConfiguredProject) {
|
||||
if (!project.originalConfiguredProjects) {
|
||||
project.originalConfiguredProjects = createMap<true>();
|
||||
}
|
||||
project.originalConfiguredProjects.set(originalProject.canonicalConfigFilePath, true);
|
||||
}
|
||||
const configFileName = this.getConfigFileNameForFile(scriptInfo, /*infoShouldBeOpen*/ false);
|
||||
const project = configFileName === undefined ? undefined : this.findConfiguredProjectByProjectName(configFileName) || this.createConfiguredProject(configFileName);
|
||||
return project && project.containsScriptInfo(scriptInfo) ? { scriptInfo, projects: [project] } : undefined;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -2126,7 +2162,7 @@ namespace ts.server {
|
||||
this.openFiles.set(info.path, projectRootPath);
|
||||
let project: ConfiguredProject | ExternalProject | undefined = this.findExternalProjectContainingOpenScriptInfo(info);
|
||||
if (!project && !this.syntaxOnly) { // Checking syntaxOnly is an optimization
|
||||
configFileName = this.getConfigFileNameForFile(info, /*infoShouldBeOpen*/ true);
|
||||
configFileName = this.getConfigFileNameForFile(info);
|
||||
if (configFileName) {
|
||||
project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
if (!project) {
|
||||
@@ -2164,14 +2200,9 @@ namespace ts.server {
|
||||
}
|
||||
Debug.assert(!info.isOrphan());
|
||||
|
||||
// Remove the configured projects that have zero references from open files.
|
||||
// This was postponed from closeOpenFile to after opening next file,
|
||||
// so that we can reuse the project if we need to right away
|
||||
this.configuredProjects.forEach(project => {
|
||||
if (!project.hasOpenRef()) {
|
||||
this.removeProject(project);
|
||||
}
|
||||
});
|
||||
this.removeOrphanConfiguredProjects();
|
||||
|
||||
// Remove orphan inferred projects now that we have reused projects
|
||||
// We need to create a duplicate because we cant guarantee order after removal
|
||||
@@ -2199,6 +2230,30 @@ namespace ts.server {
|
||||
return { configFileName, configFileErrors };
|
||||
}
|
||||
|
||||
private removeOrphanConfiguredProjects() {
|
||||
const toRemoveConfiguredProjects = cloneMap(this.configuredProjects);
|
||||
|
||||
// Do not remove configured projects that are used as original projects of other
|
||||
this.inferredProjects.forEach(markOriginalProjectsAsUsed);
|
||||
this.externalProjects.forEach(markOriginalProjectsAsUsed);
|
||||
this.configuredProjects.forEach(project => {
|
||||
// If project has open ref (there are more than zero references from external project/open file), keep it alive as well as any project it references
|
||||
if (project.hasOpenRef()) {
|
||||
toRemoveConfiguredProjects.delete(project.canonicalConfigFilePath);
|
||||
markOriginalProjectsAsUsed(project);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove all the non marked projects
|
||||
toRemoveConfiguredProjects.forEach(project => this.removeProject(project));
|
||||
|
||||
function markOriginalProjectsAsUsed(project: Project) {
|
||||
if (!project.isOrphan() && project.originalConfiguredProjects) {
|
||||
project.originalConfiguredProjects.forEach((_value, configuredProjectPath) => toRemoveConfiguredProjects.delete(configuredProjectPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private telemetryOnOpenFile(scriptInfo: ScriptInfo): void {
|
||||
if (this.syntaxOnly || !this.eventHandler || !scriptInfo.isJavaScript() || !addToSeen(this.allJsFilesForOpenFileTelemetry, scriptInfo.path)) {
|
||||
return;
|
||||
|
||||
@@ -158,6 +158,9 @@ namespace ts.server {
|
||||
/*@internal*/
|
||||
typingFiles: SortedReadonlyArray<string> = emptyArray;
|
||||
|
||||
/*@internal*/
|
||||
originalConfiguredProjects: Map<true> | undefined;
|
||||
|
||||
private readonly cancellationToken: ThrottledCancellationToken;
|
||||
|
||||
public isNonTsProject() {
|
||||
@@ -711,7 +714,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
containsFile(filename: NormalizedPath, requireOpen?: boolean): boolean {
|
||||
const info = this.projectService.getScriptInfoForPath(this.toPath(filename));
|
||||
const info = this.projectService.getScriptInfoForNormalizedPath(filename);
|
||||
if (info && (info.isScriptOpen() || !requireOpen)) {
|
||||
return this.containsScriptInfo(info);
|
||||
}
|
||||
|
||||
+25
-29
@@ -258,10 +258,6 @@ namespace ts.server {
|
||||
readonly symLinkedProjects: MultiMap<Project>;
|
||||
};
|
||||
|
||||
function isProjectsArray(projects: Projects): projects is ReadonlyArray<Project> {
|
||||
return !!(<ReadonlyArray<Project>>projects).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* This helper function processes a list of projects and return the concatenated, sortd and deduplicated output of processing each project.
|
||||
*/
|
||||
@@ -273,8 +269,8 @@ namespace ts.server {
|
||||
comparer?: (a: U, b: U) => number,
|
||||
areEqual?: (a: U, b: U) => boolean,
|
||||
): U[] {
|
||||
const outputs = flatMap(isProjectsArray(projects) ? projects : projects.projects, project => action(project, defaultValue));
|
||||
if (!isProjectsArray(projects) && projects.symLinkedProjects) {
|
||||
const outputs = flatMap(isArray(projects) ? projects : projects.projects, project => action(project, defaultValue));
|
||||
if (!isArray(projects) && projects.symLinkedProjects) {
|
||||
projects.symLinkedProjects.forEach((projects, path) => {
|
||||
const value = getValue(path as Path);
|
||||
outputs.push(...flatMap(projects, project => action(project, value)));
|
||||
@@ -370,7 +366,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
function forEachProjectInProjects(projects: Projects, path: string | undefined, cb: (project: Project, path: string | undefined) => void): void {
|
||||
for (const project of isProjectsArray(projects) ? projects : projects.projects) {
|
||||
for (const project of isArray(projects) ? projects : projects.projects) {
|
||||
cb(project, path);
|
||||
}
|
||||
if (!isArray(projects) && projects.symLinkedProjects) {
|
||||
@@ -431,23 +427,20 @@ namespace ts.server {
|
||||
if (projectAndLocation.project.getCancellationToken().isCancellationRequested()) return undefined; // Skip rest of toDo if cancelled
|
||||
cb(projectAndLocation, (project, location) => {
|
||||
seenProjects.set(projectAndLocation.project.projectName, true);
|
||||
const originalLocation = project.getSourceMapper().tryGetOriginalLocation(location);
|
||||
const originalLocation = projectService.getOriginalLocationEnsuringConfiguredProject(project, location);
|
||||
if (!originalLocation) return false;
|
||||
const originalProjectAndScriptInfo = projectService.getProjectForFileWithoutOpening(toNormalizedPath(originalLocation.fileName));
|
||||
if (!originalProjectAndScriptInfo) return false;
|
||||
|
||||
if (originalProjectAndScriptInfo) {
|
||||
toDo = toDo || [];
|
||||
const originalScriptInfo = projectService.getScriptInfo(originalLocation.fileName)!;
|
||||
toDo = toDo || [];
|
||||
|
||||
for (const project of originalProjectAndScriptInfo.projects) {
|
||||
addToTodo({ project, location: originalLocation as TLocation }, toDo, seenProjects);
|
||||
}
|
||||
const symlinkedProjectsMap = projectService.getSymlinkedProjects(originalProjectAndScriptInfo.scriptInfo);
|
||||
if (symlinkedProjectsMap) {
|
||||
symlinkedProjectsMap.forEach((symlinkedProjects) => {
|
||||
for (const symlinkedProject of symlinkedProjects) addToTodo({ project: symlinkedProject, location: originalLocation as TLocation }, toDo!, seenProjects);
|
||||
});
|
||||
}
|
||||
for (const project of originalScriptInfo.containingProjects) {
|
||||
addToTodo({ project, location: originalLocation as TLocation }, toDo, seenProjects);
|
||||
}
|
||||
const symlinkedProjectsMap = projectService.getSymlinkedProjects(originalScriptInfo);
|
||||
if (symlinkedProjectsMap) {
|
||||
symlinkedProjectsMap.forEach((symlinkedProjects) => {
|
||||
for (const symlinkedProject of symlinkedProjects) addToTodo({ project: symlinkedProject, location: originalLocation as TLocation }, toDo!, seenProjects);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@@ -1106,7 +1099,7 @@ namespace ts.server {
|
||||
return project.getLanguageService().getRenameInfo(file, position);
|
||||
}
|
||||
|
||||
private getProjects(args: protocol.FileRequestArgs): Projects {
|
||||
private getProjects(args: protocol.FileRequestArgs, getScriptInfoEnsuringProjectsUptoDate?: boolean, ignoreNoProjectError?: boolean): Projects {
|
||||
let projects: ReadonlyArray<Project> | undefined;
|
||||
let symLinkedProjects: MultiMap<Project> | undefined;
|
||||
if (args.projectFileName) {
|
||||
@@ -1116,13 +1109,18 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
else {
|
||||
const scriptInfo = this.projectService.getScriptInfo(args.file)!;
|
||||
const scriptInfo = getScriptInfoEnsuringProjectsUptoDate ?
|
||||
this.projectService.getScriptInfoEnsuringProjectsUptoDate(args.file) :
|
||||
this.projectService.getScriptInfo(args.file);
|
||||
if (!scriptInfo) {
|
||||
return ignoreNoProjectError ? emptyArray : Errors.ThrowNoProject();
|
||||
}
|
||||
projects = scriptInfo.containingProjects;
|
||||
symLinkedProjects = this.projectService.getSymlinkedProjects(scriptInfo);
|
||||
}
|
||||
// filter handles case when 'projects' is undefined
|
||||
projects = filter(projects, p => p.languageServiceEnabled && !p.isOrphan());
|
||||
if ((!projects || !projects.length) && !symLinkedProjects) {
|
||||
if (!ignoreNoProjectError && (!projects || !projects.length) && !symLinkedProjects) {
|
||||
return Errors.ThrowNoProject();
|
||||
}
|
||||
return symLinkedProjects ? { projects: projects!, symLinkedProjects } : projects!; // TODO: GH#18217
|
||||
@@ -1465,18 +1463,16 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): ReadonlyArray<protocol.CompileOnSaveAffectedFileListSingleProject> {
|
||||
const info = this.projectService.getScriptInfoEnsuringProjectsUptoDate(args.file);
|
||||
const projects = this.getProjects(args, /*getScriptInfoEnsuringProjectsUptoDate*/ true, /*ignoreNoProjectError*/ true);
|
||||
const info = this.projectService.getScriptInfo(args.file);
|
||||
if (!info) {
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
// if specified a project, we only return affected file list in this project
|
||||
const projects = args.projectFileName ? [this.projectService.findProject(args.projectFileName)!] : info.containingProjects;
|
||||
const symLinkedProjects = !args.projectFileName && this.projectService.getSymlinkedProjects(info);
|
||||
return combineProjectOutput(
|
||||
info,
|
||||
path => this.projectService.getScriptInfoForPath(path)!,
|
||||
symLinkedProjects ? { projects, symLinkedProjects } : projects,
|
||||
projects,
|
||||
(project, info) => {
|
||||
let result: protocol.CompileOnSaveAffectedFileListSingleProject | undefined;
|
||||
if (project.compileOnSaveEnabled && project.languageServiceEnabled && !project.isOrphan() && !project.getCompilationSettings().noEmit) {
|
||||
|
||||
@@ -3,6 +3,7 @@ namespace ts.codefix {
|
||||
const fixName = "unusedIdentifier";
|
||||
const fixIdPrefix = "unusedIdentifier_prefix";
|
||||
const fixIdDelete = "unusedIdentifier_delete";
|
||||
const fixIdInfer = "unusedIdentifier_infer";
|
||||
const errorCodes = [
|
||||
Diagnostics._0_is_declared_but_its_value_is_never_read.code,
|
||||
Diagnostics._0_is_declared_but_never_used.code,
|
||||
@@ -10,6 +11,7 @@ namespace ts.codefix {
|
||||
Diagnostics.All_imports_in_import_declaration_are_unused.code,
|
||||
Diagnostics.All_destructured_elements_are_unused.code,
|
||||
Diagnostics.All_variables_are_unused.code,
|
||||
Diagnostics.All_type_parameters_are_unused.code,
|
||||
];
|
||||
|
||||
registerCodeFix({
|
||||
@@ -20,28 +22,42 @@ namespace ts.codefix {
|
||||
const sourceFiles = program.getSourceFiles();
|
||||
const token = getTokenAtPosition(sourceFile, context.span.start);
|
||||
|
||||
if (isJSDocTemplateTag(token)) {
|
||||
return [createDeleteFix(textChanges.ChangeTracker.with(context, t => t.delete(sourceFile, token)), Diagnostics.Remove_template_tag)];
|
||||
}
|
||||
if (token.kind === SyntaxKind.LessThanToken) {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => deleteTypeParameters(t, sourceFile, token));
|
||||
return [createDeleteFix(changes, Diagnostics.Remove_type_parameters)];
|
||||
}
|
||||
const importDecl = tryGetFullImport(token);
|
||||
if (importDecl) {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => t.delete(sourceFile, importDecl));
|
||||
return [createCodeFixAction(fixName, changes, [Diagnostics.Remove_import_from_0, showModuleSpecifier(importDecl)], fixIdDelete, Diagnostics.Delete_all_unused_declarations)];
|
||||
return [createDeleteFix(changes, [Diagnostics.Remove_import_from_0, showModuleSpecifier(importDecl)])];
|
||||
}
|
||||
const delDestructure = textChanges.ChangeTracker.with(context, t =>
|
||||
tryDeleteFullDestructure(token, t, sourceFile, checker, sourceFiles, /*isFixAll*/ false));
|
||||
if (delDestructure.length) {
|
||||
return [createCodeFixAction(fixName, delDestructure, Diagnostics.Remove_destructuring, fixIdDelete, Diagnostics.Delete_all_unused_declarations)];
|
||||
return [createDeleteFix(delDestructure, Diagnostics.Remove_destructuring)];
|
||||
}
|
||||
const delVar = textChanges.ChangeTracker.with(context, t => tryDeleteFullVariableStatement(sourceFile, token, t));
|
||||
if (delVar.length) {
|
||||
return [createCodeFixAction(fixName, delVar, Diagnostics.Remove_variable_statement, fixIdDelete, Diagnostics.Delete_all_unused_declarations)];
|
||||
return [createDeleteFix(delVar, Diagnostics.Remove_variable_statement)];
|
||||
}
|
||||
|
||||
const result: CodeFixAction[] = [];
|
||||
|
||||
const deletion = textChanges.ChangeTracker.with(context, t =>
|
||||
tryDeleteDeclaration(sourceFile, token, t, checker, sourceFiles, /*isFixAll*/ false));
|
||||
if (deletion.length) {
|
||||
const name = isComputedPropertyName(token.parent) ? token.parent : token;
|
||||
result.push(createCodeFixAction(fixName, deletion, [Diagnostics.Remove_declaration_for_Colon_0, name.getText(sourceFile)], fixIdDelete, Diagnostics.Delete_all_unused_declarations));
|
||||
if (token.kind === SyntaxKind.InferKeyword) {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => changeInferToUnknown(t, sourceFile, token));
|
||||
const name = cast(token.parent, isInferTypeNode).typeParameter.name.text;
|
||||
result.push(createCodeFixAction(fixName, changes, [Diagnostics.Replace_infer_0_with_unknown, name], fixIdInfer, Diagnostics.Replace_all_unused_infer_with_unknown));
|
||||
}
|
||||
else {
|
||||
const deletion = textChanges.ChangeTracker.with(context, t =>
|
||||
tryDeleteDeclaration(sourceFile, token, t, checker, sourceFiles, /*isFixAll*/ false));
|
||||
if (deletion.length) {
|
||||
const name = isComputedPropertyName(token.parent) ? token.parent : token;
|
||||
result.push(createDeleteFix(deletion, [Diagnostics.Remove_declaration_for_Colon_0, name.getText(sourceFile)]));
|
||||
}
|
||||
}
|
||||
|
||||
const prefix = textChanges.ChangeTracker.with(context, t => tryPrefixDeclaration(t, errorCode, sourceFile, token));
|
||||
@@ -51,7 +67,7 @@ namespace ts.codefix {
|
||||
|
||||
return result;
|
||||
},
|
||||
fixIds: [fixIdPrefix, fixIdDelete],
|
||||
fixIds: [fixIdPrefix, fixIdDelete, fixIdInfer],
|
||||
getAllCodeActions: context => {
|
||||
const { sourceFile, program } = context;
|
||||
const checker = program.getTypeChecker();
|
||||
@@ -60,21 +76,31 @@ namespace ts.codefix {
|
||||
const token = getTokenAtPosition(sourceFile, diag.start);
|
||||
switch (context.fixId) {
|
||||
case fixIdPrefix:
|
||||
if (isIdentifier(token) && canPrefix(token)) {
|
||||
tryPrefixDeclaration(changes, diag.code, sourceFile, token);
|
||||
}
|
||||
tryPrefixDeclaration(changes, diag.code, sourceFile, token);
|
||||
break;
|
||||
case fixIdDelete: {
|
||||
if (token.kind === SyntaxKind.InferKeyword) break; // Can't delete
|
||||
const importDecl = tryGetFullImport(token);
|
||||
if (importDecl) {
|
||||
changes.delete(sourceFile, importDecl);
|
||||
}
|
||||
else if (isJSDocTemplateTag(token)) {
|
||||
changes.delete(sourceFile, token);
|
||||
}
|
||||
else if (token.kind === SyntaxKind.LessThanToken) {
|
||||
deleteTypeParameters(changes, sourceFile, token);
|
||||
}
|
||||
else if (!tryDeleteFullDestructure(token, changes, sourceFile, checker, sourceFiles, /*isFixAll*/ true) &&
|
||||
!tryDeleteFullVariableStatement(sourceFile, token, changes)) {
|
||||
tryDeleteDeclaration(sourceFile, token, changes, checker, sourceFiles, /*isFixAll*/ true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case fixIdInfer:
|
||||
if (token.kind === SyntaxKind.InferKeyword) {
|
||||
changeInferToUnknown(changes, sourceFile, token);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Debug.fail(JSON.stringify(context.fixId));
|
||||
}
|
||||
@@ -82,6 +108,18 @@ namespace ts.codefix {
|
||||
},
|
||||
});
|
||||
|
||||
function changeInferToUnknown(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node): void {
|
||||
changes.replaceNode(sourceFile, token.parent, createKeywordTypeNode(SyntaxKind.UnknownKeyword));
|
||||
}
|
||||
|
||||
function createDeleteFix(changes: FileTextChanges[], diag: DiagnosticAndArguments): CodeFixAction {
|
||||
return createCodeFixAction(fixName, changes, diag, fixIdDelete, Diagnostics.Delete_all_unused_declarations);
|
||||
}
|
||||
|
||||
function deleteTypeParameters(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node): void {
|
||||
changes.delete(sourceFile, Debug.assertDefined(cast(token.parent, isDeclarationWithTypeParameterChildren).typeParameters));
|
||||
}
|
||||
|
||||
// Sometimes the diagnostic span is an entire ImportDeclaration, so we should remove the whole thing.
|
||||
function tryGetFullImport(token: Node): ImportDeclaration | undefined {
|
||||
return token.kind === SyntaxKind.ImportKeyword ? tryCast(token.parent, isImportDeclaration) : undefined;
|
||||
@@ -110,7 +148,11 @@ namespace ts.codefix {
|
||||
|
||||
function tryPrefixDeclaration(changes: textChanges.ChangeTracker, errorCode: number, sourceFile: SourceFile, token: Node): void {
|
||||
// Don't offer to prefix a property.
|
||||
if (errorCode !== Diagnostics.Property_0_is_declared_but_its_value_is_never_read.code && isIdentifier(token) && canPrefix(token)) {
|
||||
if (errorCode === Diagnostics.Property_0_is_declared_but_its_value_is_never_read.code) return;
|
||||
if (token.kind === SyntaxKind.InferKeyword) {
|
||||
token = cast(token.parent, isInferTypeNode).typeParameter.name;
|
||||
}
|
||||
if (isIdentifier(token) && canPrefix(token)) {
|
||||
changes.replaceNode(sourceFile, token, createIdentifier(`_${token.text}`));
|
||||
}
|
||||
}
|
||||
@@ -118,6 +160,7 @@ namespace ts.codefix {
|
||||
function canPrefix(token: Identifier): boolean {
|
||||
switch (token.parent.kind) {
|
||||
case SyntaxKind.Parameter:
|
||||
case SyntaxKind.TypeParameter:
|
||||
return true;
|
||||
case SyntaxKind.VariableDeclaration: {
|
||||
const varDecl = token.parent as VariableDeclaration;
|
||||
|
||||
@@ -275,7 +275,7 @@ namespace ts.codefix {
|
||||
): ReadonlyArray<FixAddNewImport | FixUseImportType> {
|
||||
const isJs = isSourceFileJavaScript(sourceFile);
|
||||
const choicesForEachExportingModule = flatMap<SymbolExportInfo, ReadonlyArray<FixAddNewImport | FixUseImportType>>(moduleSymbols, ({ moduleSymbol, importKind, exportedSymbolIsTypeOnly }) => {
|
||||
const modulePathsGroups = moduleSpecifiers.getModuleSpecifiers(moduleSymbol, program.getCompilerOptions(), sourceFile, host, program.getSourceFiles(), preferences);
|
||||
const modulePathsGroups = moduleSpecifiers.getModuleSpecifiers(moduleSymbol, program.getCompilerOptions(), sourceFile, host, program.getSourceFiles(), preferences, program.redirectTargetsMap);
|
||||
return modulePathsGroups.map(group => group.map((moduleSpecifier): FixAddNewImport | FixUseImportType =>
|
||||
// `position` should only be undefined at a missing jsx namespace, in which case we shouldn't be looking for pure types.
|
||||
exportedSymbolIsTypeOnly && isJs ? { kind: ImportFixKind.ImportType, moduleSpecifier, position: Debug.assertDefined(position) } : { kind: ImportFixKind.AddNew, moduleSpecifier, importKind }));
|
||||
@@ -465,14 +465,14 @@ namespace ts.codefix {
|
||||
const { importClause, importKind } = fix;
|
||||
doAddExistingFix(changes, sourceFile, importClause, importKind === ImportKind.Default ? symbolName : undefined, importKind === ImportKind.Named ? [symbolName] : emptyArray);
|
||||
const moduleSpecifierWithoutQuotes = stripQuotes(importClause.parent.moduleSpecifier.getText());
|
||||
return [Diagnostics.Add_0_to_existing_import_declaration_from_1, symbolName, moduleSpecifierWithoutQuotes];
|
||||
return [importKind === ImportKind.Default ? Diagnostics.Add_default_import_0_to_existing_import_declaration_from_1 : Diagnostics.Add_0_to_existing_import_declaration_from_1, symbolName, moduleSpecifierWithoutQuotes]; // you too!
|
||||
}
|
||||
case ImportFixKind.AddNew: {
|
||||
const { importKind, moduleSpecifier } = fix;
|
||||
addNewImports(changes, sourceFile, moduleSpecifier, quotePreference, importKind === ImportKind.Default ? { defaultImport: symbolName, namedImports: emptyArray, namespaceLikeImport: undefined }
|
||||
: importKind === ImportKind.Named ? { defaultImport: undefined, namedImports: [symbolName], namespaceLikeImport: undefined }
|
||||
: { defaultImport: undefined, namedImports: emptyArray, namespaceLikeImport: { importKind, name: symbolName } });
|
||||
return [Diagnostics.Import_0_from_module_1, symbolName, moduleSpecifier];
|
||||
return [importKind === ImportKind.Default ? Diagnostics.Import_default_0_from_module_1 : Diagnostics.Import_0_from_module_1, symbolName, moduleSpecifier];
|
||||
}
|
||||
default:
|
||||
return Debug.assertNever(fix);
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace ts.DocumentHighlights {
|
||||
const map = arrayToMultiMap(referenceEntries.map(FindAllReferences.toHighlightSpan), e => e.fileName, e => e.span);
|
||||
return arrayFrom(map.entries(), ([fileName, highlightSpans]) => {
|
||||
if (!sourceFilesSet.has(fileName)) {
|
||||
Debug.assert(program.redirectTargetsSet.has(fileName));
|
||||
Debug.assert(program.redirectTargetsMap.has(fileName));
|
||||
const redirectTarget = program.getSourceFile(fileName);
|
||||
const redirect = find(sourceFilesToSearch, f => !!f.redirectInfo && f.redirectInfo.redirectTarget === redirectTarget)!;
|
||||
fileName = redirect.fileName;
|
||||
|
||||
@@ -354,12 +354,15 @@ namespace ts.formatting {
|
||||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
return getListIfStartEndIsInListRange((<ClassDeclaration | ClassExpression | InterfaceDeclaration | TypeAliasDeclaration>node.parent).typeParameters, node.getStart(sourceFile), end);
|
||||
case SyntaxKind.JSDocTemplateTag: {
|
||||
const { typeParameters } = <ClassDeclaration | ClassExpression | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag>node.parent;
|
||||
return getListIfStartEndIsInListRange(typeParameters, node.getStart(sourceFile), end);
|
||||
}
|
||||
case SyntaxKind.NewExpression:
|
||||
case SyntaxKind.CallExpression: {
|
||||
const start = node.getStart(sourceFile);
|
||||
return getListIfStartEndIsInListRange((<CallExpression>node.parent).typeArguments, start, end) ||
|
||||
getListIfStartEndIsInListRange((<CallExpression>node.parent).arguments, start, end);
|
||||
return getListIfStartEndIsInListRange((<CallExpression | NewExpression>node.parent).typeArguments, start, end) ||
|
||||
getListIfStartEndIsInListRange((<CallExpression | NewExpression>node.parent).arguments, start, end);
|
||||
}
|
||||
case SyntaxKind.VariableDeclarationList:
|
||||
return getListIfStartEndIsInListRange((<VariableDeclarationList>node.parent).declarations, node.getStart(sourceFile), end);
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace ts {
|
||||
|
||||
// Need an update if the imported file moved, or the importing file moved and was using a relative path.
|
||||
return toImport !== undefined && (toImport.updated || (importingSourceFileMoved && pathIsRelative(importLiteral.text)))
|
||||
? moduleSpecifiers.getModuleSpecifier(program.getCompilerOptions(), sourceFile, newImportFromPath, toImport.newFileName, host, allFiles, preferences)
|
||||
? moduleSpecifiers.getModuleSpecifier(program.getCompilerOptions(), sourceFile, newImportFromPath, toImport.newFileName, host, allFiles, preferences, program.redirectTargetsMap)
|
||||
: undefined;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -272,14 +272,16 @@ namespace ts.JsDoc {
|
||||
*/
|
||||
|
||||
export function getDocCommentTemplateAtPosition(newLine: string, sourceFile: SourceFile, position: number): TextInsertion | undefined {
|
||||
// Check if in a context where we don't want to perform any insertion
|
||||
if (isInString(sourceFile, position) || isInComment(sourceFile, position) || hasDocComment(sourceFile, position)) {
|
||||
const tokenAtPos = getTokenAtPosition(sourceFile, position);
|
||||
const existingDocComment = findAncestor(tokenAtPos, isJSDoc);
|
||||
if (existingDocComment && (existingDocComment.comment !== undefined || length(existingDocComment.tags))) {
|
||||
// Non-empty comment already exists.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const tokenAtPos = getTokenAtPosition(sourceFile, position);
|
||||
const tokenStart = tokenAtPos.getStart(sourceFile);
|
||||
if (!tokenAtPos || tokenStart < position) {
|
||||
// Don't provide a doc comment template based on a *previous* node. (But an existing empty jsdoc comment will likely start before `position`.)
|
||||
if (!existingDocComment && tokenStart < position) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -1240,7 +1240,7 @@ namespace ts.refactor.extractSymbol {
|
||||
return scope.members;
|
||||
}
|
||||
else {
|
||||
assertTypeIsNever(scope);
|
||||
assertType<never>(scope);
|
||||
}
|
||||
|
||||
return emptyArray;
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace ts.SignatureHelp {
|
||||
return undefined;
|
||||
}
|
||||
const candidates: Signature[] = [];
|
||||
const resolvedSignature = checker.getResolvedSignature(invocation.node, candidates, argumentInfo.argumentCount)!; // TODO: GH#18217
|
||||
const resolvedSignature = checker.getResolvedSignatureForSignatureHelp(invocation.node, candidates, argumentInfo.argumentCount)!; // TODO: GH#18217
|
||||
return candidates.length === 0 ? undefined : { candidates, resolvedSignature };
|
||||
}
|
||||
else if (invocation.kind === InvocationKind.TypeArgs) {
|
||||
@@ -101,31 +101,16 @@ namespace ts.SignatureHelp {
|
||||
function createJavaScriptSignatureHelpItems(argumentInfo: ArgumentListInfo, program: Program, cancellationToken: CancellationToken): SignatureHelpItems | undefined {
|
||||
// See if we can find some symbol with the call expression name that has call signatures.
|
||||
const expression = getExpressionFromInvocation(argumentInfo.invocation);
|
||||
const name = isIdentifier(expression) ? expression : isPropertyAccessExpression(expression) ? expression.name : undefined;
|
||||
if (!name || !name.escapedText) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const name = isIdentifier(expression) ? expression.text : isPropertyAccessExpression(expression) ? expression.name.text : undefined;
|
||||
const typeChecker = program.getTypeChecker();
|
||||
for (const sourceFile of program.getSourceFiles()) {
|
||||
const nameToDeclarations = sourceFile.getNamedDeclarations();
|
||||
const declarations = nameToDeclarations.get(name.text);
|
||||
|
||||
if (declarations) {
|
||||
for (const declaration of declarations) {
|
||||
const symbol = declaration.symbol;
|
||||
if (symbol) {
|
||||
const type = typeChecker.getTypeOfSymbolAtLocation(symbol, declaration);
|
||||
if (type) {
|
||||
const callSignatures = type.getCallSignatures();
|
||||
if (callSignatures && callSignatures.length) {
|
||||
return typeChecker.runWithCancellationToken(cancellationToken, typeChecker => createSignatureHelpItems(callSignatures, callSignatures[0], argumentInfo, sourceFile, typeChecker));
|
||||
}
|
||||
}
|
||||
}
|
||||
return name === undefined ? undefined : firstDefined(program.getSourceFiles(), sourceFile =>
|
||||
firstDefined(sourceFile.getNamedDeclarations().get(name), declaration => {
|
||||
const type = declaration.symbol && typeChecker.getTypeOfSymbolAtLocation(declaration.symbol, declaration);
|
||||
const callSignatures = type && type.getCallSignatures();
|
||||
if (callSignatures && callSignatures.length) {
|
||||
return typeChecker.runWithCancellationToken(cancellationToken, typeChecker => createSignatureHelpItems(callSignatures, callSignatures[0], argumentInfo, sourceFile, typeChecker));
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
function lessThanFollowsCalledExpression(startingToken: Node, sourceFile: SourceFile, calledExpression: Expression) {
|
||||
|
||||
+14
-39
@@ -213,7 +213,7 @@ namespace ts.textChanges {
|
||||
private readonly changes: Change[] = [];
|
||||
private readonly newFiles: { readonly oldFile: SourceFile, readonly fileName: string, readonly statements: ReadonlyArray<Statement> }[] = [];
|
||||
private readonly classesWithNodesInsertedAtStart = createMap<ClassDeclaration>(); // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
|
||||
private readonly deletedNodes: { readonly sourceFile: SourceFile, readonly node: Node }[] = [];
|
||||
private readonly deletedNodes: { readonly sourceFile: SourceFile, readonly node: Node | NodeArray<TypeParameterDeclaration> }[] = [];
|
||||
|
||||
public static fromContext(context: TextChangesContext): ChangeTracker {
|
||||
return new ChangeTracker(getNewLineOrDefaultFromHost(context.host, context.formatContext.options), context.formatContext);
|
||||
@@ -233,8 +233,8 @@ namespace ts.textChanges {
|
||||
return this;
|
||||
}
|
||||
|
||||
delete(sourceFile: SourceFile, node: Node): void {
|
||||
this.deletedNodes.push({ sourceFile, node, });
|
||||
delete(sourceFile: SourceFile, node: Node | NodeArray<TypeParameterDeclaration>): void {
|
||||
this.deletedNodes.push({ sourceFile, node });
|
||||
}
|
||||
|
||||
public deleteModifier(sourceFile: SourceFile, modifier: Modifier): void {
|
||||
@@ -661,7 +661,12 @@ namespace ts.textChanges {
|
||||
const deletedNodesInLists = new NodeSet(); // Stores ids of nodes in lists that we already deleted. Used to avoid deleting `, ` twice in `a, b`.
|
||||
for (const { sourceFile, node } of this.deletedNodes) {
|
||||
if (!this.deletedNodes.some(d => d.sourceFile === sourceFile && rangeContainsRangeExclusive(d.node, node))) {
|
||||
deleteDeclaration.deleteDeclaration(this, deletedNodesInLists, sourceFile, node);
|
||||
if (isArray(node)) {
|
||||
this.deleteRange(sourceFile, rangeOfTypeParameters(node));
|
||||
}
|
||||
else {
|
||||
deleteDeclaration.deleteDeclaration(this, deletedNodesInLists, sourceFile, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1001,7 +1006,7 @@ namespace ts.textChanges {
|
||||
}
|
||||
|
||||
namespace deleteDeclaration {
|
||||
export function deleteDeclaration(changes: ChangeTracker, deletedNodesInLists: NodeSet, sourceFile: SourceFile, node: Node): void {
|
||||
export function deleteDeclaration(changes: ChangeTracker, deletedNodesInLists: NodeSet<Node>, sourceFile: SourceFile, node: Node): void {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Parameter: {
|
||||
const oldFunction = node.parent;
|
||||
@@ -1052,33 +1057,9 @@ namespace ts.textChanges {
|
||||
deleteVariableDeclaration(changes, deletedNodesInLists, sourceFile, node as VariableDeclaration);
|
||||
break;
|
||||
|
||||
case SyntaxKind.TypeParameter: {
|
||||
const typeParam = node as TypeParameterDeclaration;
|
||||
switch (typeParam.parent.kind) {
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
changes.deleteRange(sourceFile, getRangeToDeleteJsDocTag(typeParam.parent, sourceFile));
|
||||
break;
|
||||
case SyntaxKind.InferType:
|
||||
// TODO: GH#25594
|
||||
break;
|
||||
default: {
|
||||
const typeParameters = getEffectiveTypeParameterDeclarations(typeParam.parent);
|
||||
if (typeParameters.length === 1) {
|
||||
const { pos, end } = cast(typeParameters, isNodeArray);
|
||||
const previousToken = getTokenAtPosition(sourceFile, pos - 1);
|
||||
const nextToken = getTokenAtPosition(sourceFile, end);
|
||||
Debug.assert(previousToken.kind === SyntaxKind.LessThanToken);
|
||||
Debug.assert(nextToken.kind === SyntaxKind.GreaterThanToken);
|
||||
|
||||
changes.deleteNodeRange(sourceFile, previousToken, nextToken);
|
||||
}
|
||||
else {
|
||||
deleteNodeInList(changes, deletedNodesInLists, sourceFile, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
case SyntaxKind.TypeParameter:
|
||||
deleteNodeInList(changes, deletedNodesInLists, sourceFile, node);
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
const namedImports = (node as ImportSpecifier).parent;
|
||||
@@ -1144,7 +1125,7 @@ namespace ts.textChanges {
|
||||
}
|
||||
}
|
||||
|
||||
function deleteVariableDeclaration(changes: ChangeTracker, deletedNodesInLists: NodeSet, sourceFile: SourceFile, node: VariableDeclaration): void {
|
||||
function deleteVariableDeclaration(changes: ChangeTracker, deletedNodesInLists: NodeSet<Node>, sourceFile: SourceFile, node: VariableDeclaration): void {
|
||||
const { parent } = node;
|
||||
|
||||
if (parent.kind === SyntaxKind.CatchClause) {
|
||||
@@ -1177,12 +1158,6 @@ namespace ts.textChanges {
|
||||
Debug.assertNever(gp);
|
||||
}
|
||||
}
|
||||
|
||||
function getRangeToDeleteJsDocTag(node: JSDocTag, sourceFile: SourceFile): TextRange {
|
||||
const { parent } = node;
|
||||
const toDelete = parent.kind === SyntaxKind.JSDocComment && parent.comment === undefined && parent.tags!.length === 1 ? parent : node;
|
||||
return createTextRangeFromNode(toDelete, sourceFile);
|
||||
}
|
||||
}
|
||||
|
||||
/** Warning: This deletes comments too. See `copyComments` in `convertFunctionToEs6Class`. */
|
||||
@@ -1193,7 +1168,7 @@ namespace ts.textChanges {
|
||||
changes.deleteRange(sourceFile, { pos: startPosition, end: endPosition });
|
||||
}
|
||||
|
||||
function deleteNodeInList(changes: ChangeTracker, deletedNodesInLists: NodeSet, sourceFile: SourceFile, node: Node): void {
|
||||
function deleteNodeInList(changes: ChangeTracker, deletedNodesInLists: NodeSet<Node>, sourceFile: SourceFile, node: Node): void {
|
||||
const containingList = Debug.assertDefined(formatting.SmartIndenter.getContainingList(node, sourceFile));
|
||||
const index = indexOfNode(containingList, node);
|
||||
Debug.assert(index !== -1);
|
||||
|
||||
@@ -374,7 +374,7 @@ namespace ts {
|
||||
case SpecialPropertyAssignmentKind.Prototype:
|
||||
return ScriptElementKind.localClassElement;
|
||||
default: {
|
||||
assertTypeIsNever(kind);
|
||||
assertType<never>(kind);
|
||||
return ScriptElementKind.unknown;
|
||||
}
|
||||
}
|
||||
@@ -1358,63 +1358,6 @@ namespace ts {
|
||||
return getPropertySymbolsFromBaseTypes(memberSymbol.parent!, memberSymbol.name, checker, _ => true) || false;
|
||||
}
|
||||
|
||||
export interface ReadonlyNodeSet {
|
||||
has(node: Node): boolean;
|
||||
forEach(cb: (node: Node) => void): void;
|
||||
some(pred: (node: Node) => boolean): boolean;
|
||||
}
|
||||
|
||||
export class NodeSet implements ReadonlyNodeSet {
|
||||
private map = createMap<Node>();
|
||||
|
||||
add(node: Node): void {
|
||||
this.map.set(String(getNodeId(node)), node);
|
||||
}
|
||||
has(node: Node): boolean {
|
||||
return this.map.has(String(getNodeId(node)));
|
||||
}
|
||||
forEach(cb: (node: Node) => void): void {
|
||||
this.map.forEach(cb);
|
||||
}
|
||||
some(pred: (node: Node) => boolean): boolean {
|
||||
return forEachEntry(this.map, pred) || false;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ReadonlyNodeMap<TNode extends Node, TValue> {
|
||||
get(node: TNode): TValue | undefined;
|
||||
has(node: TNode): boolean;
|
||||
}
|
||||
|
||||
export class NodeMap<TNode extends Node, TValue> implements ReadonlyNodeMap<TNode, TValue> {
|
||||
private map = createMap<{ node: TNode, value: TValue }>();
|
||||
|
||||
get(node: TNode): TValue | undefined {
|
||||
const res = this.map.get(String(getNodeId(node)));
|
||||
return res && res.value;
|
||||
}
|
||||
|
||||
getOrUpdate(node: TNode, setValue: () => TValue): TValue {
|
||||
const res = this.get(node);
|
||||
if (res) return res;
|
||||
const value = setValue();
|
||||
this.set(node, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
set(node: TNode, value: TValue): void {
|
||||
this.map.set(String(getNodeId(node)), { node, value });
|
||||
}
|
||||
|
||||
has(node: TNode): boolean {
|
||||
return this.map.has(String(getNodeId(node)));
|
||||
}
|
||||
|
||||
forEach(cb: (value: TValue, node: TNode) => void): void {
|
||||
this.map.forEach(({ node, value }) => cb(value, node));
|
||||
}
|
||||
}
|
||||
|
||||
export function getParentNodeInSpan(node: Node | undefined, file: SourceFile, span: TextSpan): Node | undefined {
|
||||
if (!node) return undefined;
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
namespace ts {
|
||||
describe("FactoryAPI", () => {
|
||||
function assertSyntaxKind(node: Node, expected: SyntaxKind) {
|
||||
assert.strictEqual(node.kind, expected, `Actual: ${Debug.showSyntaxKind(node)} Expected: ${(ts as any).SyntaxKind[expected]}`);
|
||||
}
|
||||
describe("createExportAssignment", () => {
|
||||
it("parenthesizes default export if necessary", () => {
|
||||
function checkExpression(expression: Expression) {
|
||||
@@ -9,7 +12,7 @@ namespace ts {
|
||||
/*isExportEquals*/ false,
|
||||
expression,
|
||||
);
|
||||
assert.strictEqual(node.expression.kind, SyntaxKind.ParenthesizedExpression);
|
||||
assertSyntaxKind(node.expression, SyntaxKind.ParenthesizedExpression);
|
||||
}
|
||||
|
||||
const clazz = createClassExpression(/*modifiers*/ undefined, "C", /*typeParameters*/ undefined, /*heritageClauses*/ undefined, [
|
||||
@@ -39,7 +42,7 @@ namespace ts {
|
||||
/*equalsGreaterThanToken*/ undefined,
|
||||
body,
|
||||
);
|
||||
assert.strictEqual(node.body.kind, SyntaxKind.ParenthesizedExpression);
|
||||
assertSyntaxKind(node.body, SyntaxKind.ParenthesizedExpression);
|
||||
}
|
||||
|
||||
checkBody(createObjectLiteral());
|
||||
@@ -50,5 +53,30 @@ namespace ts {
|
||||
checkBody(createBinary(createLiteral("a"), SyntaxKind.CommaToken, createLiteral("b")));
|
||||
});
|
||||
});
|
||||
|
||||
describe("createBinaryExpression", () => {
|
||||
it("parenthesizes arrow function in RHS if necessary", () => {
|
||||
const lhs = createIdentifier("foo");
|
||||
const rhs = createArrowFunction(
|
||||
/*modifiers*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
[],
|
||||
/*type*/ undefined,
|
||||
/*equalsGreaterThanToken*/ undefined,
|
||||
createBlock([]),
|
||||
);
|
||||
function checkRhs(operator: BinaryOperator, expectParens: boolean) {
|
||||
const node = createBinary(lhs, operator, rhs);
|
||||
assertSyntaxKind(node.right, expectParens ? SyntaxKind.ParenthesizedExpression : SyntaxKind.ArrowFunction);
|
||||
}
|
||||
|
||||
checkRhs(SyntaxKind.CommaToken, /*expectParens*/ false);
|
||||
checkRhs(SyntaxKind.EqualsToken, /*expectParens*/ false);
|
||||
checkRhs(SyntaxKind.PlusEqualsToken, /*expectParens*/ false);
|
||||
checkRhs(SyntaxKind.BarBarToken, /*expectParens*/ true);
|
||||
checkRhs(SyntaxKind.AmpersandAmpersandToken, /*expectParens*/ true);
|
||||
checkRhs(SyntaxKind.EqualsEqualsToken, /*expectParens*/ true);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2262,8 +2262,11 @@ declare module "fs" {
|
||||
const files = [file, module, libFile];
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const watch = createWatchOfFilesAndCompilerOptions([file.path], host);
|
||||
|
||||
checkProgramActualFiles(watch(), [file.path, libFile.path]);
|
||||
checkOutputErrorsInitial(host, [getDiagnosticModuleNotFoundOfFile(watch(), file, "qqq")]);
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
|
||||
checkWatchedDirectories(host, [`${currentDirectory}/node_modules`, `${currentDirectory}/node_modules/@types`], /*recursive*/ true);
|
||||
|
||||
host.renameFolder(`${currentDirectory}/node_modules2`, `${currentDirectory}/node_modules`);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
@@ -2622,7 +2625,7 @@ declare module "fs" {
|
||||
createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkWatchedFilesDetailed(host, [libFile.path, mainFile.path, config.path, linkedPackageIndex.path, linkedPackageOther.path], 1);
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, [mainPackageRoot, linkedPackageRoot, `${mainPackageRoot}/node_modules/@types`, `${projectRoot}/node_modules/@types`], 1, /*recursive*/ true);
|
||||
checkWatchedDirectoriesDetailed(host, [`${mainPackageRoot}/@scoped`, `${mainPackageRoot}/node_modules`, linkedPackageRoot, `${mainPackageRoot}/node_modules/@types`, `${projectRoot}/node_modules/@types`], 1, /*recursive*/ true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -176,6 +176,18 @@ namespace ts.projectSystem {
|
||||
return { ts: 0, tsx: 0, dts: 0, js: 0, jsx: 0, deferred: 0, ...nonZeroStats };
|
||||
}
|
||||
|
||||
export interface ConfigFileDiagnostic {
|
||||
fileName: string | undefined;
|
||||
start: number | undefined;
|
||||
length: number | undefined;
|
||||
messageText: string;
|
||||
category: DiagnosticCategory;
|
||||
code: number;
|
||||
reportsUnnecessary?: {};
|
||||
source?: string;
|
||||
relatedInformation?: DiagnosticRelatedInformation[];
|
||||
}
|
||||
|
||||
export class TestServerEventManager {
|
||||
private events: server.ProjectServiceEvent[] = [];
|
||||
readonly session: TestSession;
|
||||
@@ -216,10 +228,14 @@ namespace ts.projectSystem {
|
||||
this.events.forEach(event => assert.notEqual(event.eventName, eventName));
|
||||
}
|
||||
|
||||
checkSingleConfigFileDiagEvent(configFileName: string, triggerFile: string) {
|
||||
checkSingleConfigFileDiagEvent(configFileName: string, triggerFile: string, errors: ReadonlyArray<ConfigFileDiagnostic>) {
|
||||
const eventData = this.getEvent<server.ConfigFileDiagEvent>(server.ConfigFileDiagEvent);
|
||||
assert.equal(eventData.configFileName, configFileName);
|
||||
assert.equal(eventData.triggerFile, triggerFile);
|
||||
const actual = eventData.diagnostics.map(({ file, messageText, ...rest }) => ({ fileName: file && file.fileName, messageText: isString(messageText) ? messageText : "", ...rest }));
|
||||
if (errors) {
|
||||
assert.deepEqual(actual, errors);
|
||||
}
|
||||
}
|
||||
|
||||
assertProjectInfoTelemetryEvent(partial: Partial<server.ProjectInfoTelemetryEventData>, configFile = "/tsconfig.json"): void {
|
||||
@@ -622,8 +638,8 @@ namespace ts.projectSystem {
|
||||
const configFileLocations = ["/a/b/c/", "/a/b/", "/a/", "/"];
|
||||
const configFiles = flatMap(configFileLocations, location => [location + "tsconfig.json", location + "jsconfig.json"]);
|
||||
checkWatchedFiles(host, configFiles.concat(libFile.path, moduleFile.path));
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, ["/a/b/c", combinePaths(getDirectoryPath(appFile.path), nodeModulesAtTypes)], /*recursive*/ true);
|
||||
checkWatchedDirectories(host, ["/a/b/c"], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, [combinePaths(getDirectoryPath(appFile.path), nodeModulesAtTypes)], /*recursive*/ true);
|
||||
});
|
||||
|
||||
it("can handle tsconfig file name with difference casing", () => {
|
||||
@@ -3035,21 +3051,18 @@ namespace ts.projectSystem {
|
||||
assert.isDefined(project);
|
||||
checkProjectActualFiles(project, map(files, file => file.path));
|
||||
checkWatchedFiles(host, mapDefined(files, file => file === file1 ? undefined : file.path));
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
const watchedRecursiveDirectories = ["/a/b/node_modules/@types"];
|
||||
watchedRecursiveDirectories.push("/a/b");
|
||||
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
|
||||
checkWatchedDirectoriesDetailed(host, ["/a/b"], 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, ["/a/b/node_modules/@types"], 1, /*recursive*/ true);
|
||||
|
||||
files.push(file2);
|
||||
host.reloadFS(files);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
watchedRecursiveDirectories.pop();
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
assert.strictEqual(projectService.configuredProjects.get(configFile.path), project);
|
||||
checkProjectActualFiles(project, mapDefined(files, file => file === file2a ? undefined : file.path));
|
||||
checkWatchedFiles(host, mapDefined(files, file => file === file1 ? undefined : file.path));
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, ["/a/b/node_modules/@types"], 1, /*recursive*/ true);
|
||||
|
||||
// On next file open the files file2a should be closed and not watched any more
|
||||
projectService.openClientFile(file2.path);
|
||||
@@ -3057,8 +3070,8 @@ namespace ts.projectSystem {
|
||||
assert.strictEqual(projectService.configuredProjects.get(configFile.path), project);
|
||||
checkProjectActualFiles(project, mapDefined(files, file => file === file2a ? undefined : file.path));
|
||||
checkWatchedFiles(host, [libFile.path, configFile.path]);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, ["/a/b/node_modules/@types"], 1, /*recursive*/ true);
|
||||
|
||||
});
|
||||
|
||||
@@ -3098,7 +3111,7 @@ namespace ts.projectSystem {
|
||||
checkWatchedFiles(host, [libFile.path, module1.path, module2.path, configFile.path]);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
const watchedRecursiveDirectories = getTypeRootsFromLocation(root + "/a/b/src");
|
||||
watchedRecursiveDirectories.push(`${root}/a/b/src`, `${root}/a/b/node_modules`);
|
||||
watchedRecursiveDirectories.push(`${root}/a/b/src/node_modules`, `${root}/a/b/node_modules`);
|
||||
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
|
||||
});
|
||||
|
||||
@@ -3479,6 +3492,102 @@ namespace ts.projectSystem {
|
||||
openFilesForSession([{ file, projectRootPath }], session);
|
||||
}
|
||||
});
|
||||
|
||||
describe("CompileOnSaveAffectedFileListRequest with and without projectFileName in request", () => {
|
||||
const projectRoot = "/user/username/projects/myproject";
|
||||
const core: File = {
|
||||
path: `${projectRoot}/core/core.ts`,
|
||||
content: "let z = 10;"
|
||||
};
|
||||
const app1: File = {
|
||||
path: `${projectRoot}/app1/app.ts`,
|
||||
content: "let x = 10;"
|
||||
};
|
||||
const app2: File = {
|
||||
path: `${projectRoot}/app2/app.ts`,
|
||||
content: "let y = 10;"
|
||||
};
|
||||
const app1Config: File = {
|
||||
path: `${projectRoot}/app1/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
files: ["app.ts", "../core/core.ts"],
|
||||
compilerOptions: { outFile : "build/output.js" },
|
||||
compileOnSave: true
|
||||
})
|
||||
};
|
||||
const app2Config: File = {
|
||||
path: `${projectRoot}/app2/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
files: ["app.ts", "../core/core.ts"],
|
||||
compilerOptions: { outFile: "build/output.js" },
|
||||
compileOnSave: true
|
||||
})
|
||||
};
|
||||
const files = [libFile, core, app1, app2, app1Config, app2Config];
|
||||
|
||||
function insertString(session: TestSession, file: File) {
|
||||
session.executeCommandSeq<protocol.ChangeRequest>({
|
||||
command: protocol.CommandTypes.Change,
|
||||
arguments: {
|
||||
file: file.path,
|
||||
line: 1,
|
||||
offset: 1,
|
||||
endLine: 1,
|
||||
endOffset: 1,
|
||||
insertString: "let k = 1"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getSession() {
|
||||
const host = createServerHost(files);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([app1, app2, core], session);
|
||||
const service = session.getProjectService();
|
||||
checkNumberOfProjects(session.getProjectService(), { configuredProjects: 2 });
|
||||
const project1 = service.configuredProjects.get(app1Config.path)!;
|
||||
const project2 = service.configuredProjects.get(app2Config.path)!;
|
||||
checkProjectActualFiles(project1, [libFile.path, app1.path, core.path, app1Config.path]);
|
||||
checkProjectActualFiles(project2, [libFile.path, app2.path, core.path, app2Config.path]);
|
||||
insertString(session, app1);
|
||||
insertString(session, app2);
|
||||
assert.equal(project1.dirty, true);
|
||||
assert.equal(project2.dirty, true);
|
||||
return session;
|
||||
}
|
||||
|
||||
it("when projectFile is specified", () => {
|
||||
const session = getSession();
|
||||
const response = session.executeCommandSeq<protocol.CompileOnSaveAffectedFileListRequest>({
|
||||
command: protocol.CommandTypes.CompileOnSaveAffectedFileList,
|
||||
arguments: {
|
||||
file: core.path,
|
||||
projectFileName: app1Config.path
|
||||
}
|
||||
}).response;
|
||||
assert.deepEqual(response, [
|
||||
{ projectFileName: app1Config.path, fileNames: [core.path, app1.path], projectUsesOutFile: true }
|
||||
]);
|
||||
assert.equal(session.getProjectService().configuredProjects.get(app1Config.path)!.dirty, false);
|
||||
assert.equal(session.getProjectService().configuredProjects.get(app2Config.path)!.dirty, true);
|
||||
});
|
||||
|
||||
it("when projectFile is not specified", () => {
|
||||
const session = getSession();
|
||||
const response = session.executeCommandSeq<protocol.CompileOnSaveAffectedFileListRequest>({
|
||||
command: protocol.CommandTypes.CompileOnSaveAffectedFileList,
|
||||
arguments: {
|
||||
file: core.path
|
||||
}
|
||||
}).response;
|
||||
assert.deepEqual(response, [
|
||||
{ projectFileName: app1Config.path, fileNames: [core.path, app1.path], projectUsesOutFile: true },
|
||||
{ projectFileName: app2Config.path, fileNames: [core.path, app2.path], projectUsesOutFile: true }
|
||||
]);
|
||||
assert.equal(session.getProjectService().configuredProjects.get(app1Config.path)!.dirty, false);
|
||||
assert.equal(session.getProjectService().configuredProjects.get(app2Config.path)!.dirty, false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem Proper errors", () => {
|
||||
@@ -4698,13 +4807,41 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem Configure file diagnostics events", () => {
|
||||
function getUnknownCompilerOptionDiagnostic(configFile: File, prop: string): ConfigFileDiagnostic {
|
||||
const d = Diagnostics.Unknown_compiler_option_0;
|
||||
const start = configFile.content.indexOf(prop) - 1; // start at "prop"
|
||||
return {
|
||||
fileName: configFile.path,
|
||||
start,
|
||||
length: prop.length + 2,
|
||||
messageText: formatStringFromArgs(d.message, [prop]),
|
||||
category: d.category,
|
||||
code: d.code,
|
||||
reportsUnnecessary: undefined
|
||||
};
|
||||
}
|
||||
|
||||
function getFileNotFoundDiagnostic(configFile: File, relativeFileName: string): ConfigFileDiagnostic {
|
||||
const findString = `{"path":"./${relativeFileName}"}`;
|
||||
const d = Diagnostics.File_0_does_not_exist;
|
||||
const start = configFile.content.indexOf(findString);
|
||||
return {
|
||||
fileName: configFile.path,
|
||||
start,
|
||||
length: findString.length,
|
||||
messageText: formatStringFromArgs(d.message, [`${getDirectoryPath(configFile.path)}/${relativeFileName}`]),
|
||||
category: d.category,
|
||||
code: d.code,
|
||||
reportsUnnecessary: undefined
|
||||
};
|
||||
}
|
||||
|
||||
it("are generated when the config file has errors", () => {
|
||||
const file = {
|
||||
const file: File = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 10"
|
||||
};
|
||||
const configFile = {
|
||||
const configFile: File = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"compilerOptions": {
|
||||
@@ -4713,29 +4850,32 @@ namespace ts.projectSystem {
|
||||
}
|
||||
}`
|
||||
};
|
||||
const serverEventManager = new TestServerEventManager([file, configFile]);
|
||||
const serverEventManager = new TestServerEventManager([file, libFile, configFile]);
|
||||
openFilesForSession([file], serverEventManager.session);
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, [
|
||||
getUnknownCompilerOptionDiagnostic(configFile, "foo"),
|
||||
getUnknownCompilerOptionDiagnostic(configFile, "allowJS")
|
||||
]);
|
||||
});
|
||||
|
||||
it("are generated when the config file doesn't have errors", () => {
|
||||
const file = {
|
||||
const file: File = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 10"
|
||||
};
|
||||
const configFile = {
|
||||
const configFile: File = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"compilerOptions": {}
|
||||
}`
|
||||
};
|
||||
const serverEventManager = new TestServerEventManager([file, configFile]);
|
||||
const serverEventManager = new TestServerEventManager([file, libFile, configFile]);
|
||||
openFilesForSession([file], serverEventManager.session);
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, emptyArray);
|
||||
});
|
||||
|
||||
it("are generated when the config file changes", () => {
|
||||
const file = {
|
||||
const file: File = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 10"
|
||||
};
|
||||
@@ -4746,37 +4886,40 @@ namespace ts.projectSystem {
|
||||
}`
|
||||
};
|
||||
|
||||
const serverEventManager = new TestServerEventManager([file, configFile]);
|
||||
const files = [file, libFile, configFile];
|
||||
const serverEventManager = new TestServerEventManager(files);
|
||||
openFilesForSession([file], serverEventManager.session);
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, emptyArray);
|
||||
|
||||
configFile.content = `{
|
||||
"compilerOptions": {
|
||||
"haha": 123
|
||||
}
|
||||
}`;
|
||||
serverEventManager.host.reloadFS([file, configFile]);
|
||||
serverEventManager.host.reloadFS(files);
|
||||
serverEventManager.host.runQueuedTimeoutCallbacks();
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path);
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path, [
|
||||
getUnknownCompilerOptionDiagnostic(configFile, "haha")
|
||||
]);
|
||||
|
||||
configFile.content = `{
|
||||
"compilerOptions": {}
|
||||
}`;
|
||||
serverEventManager.host.reloadFS([file, configFile]);
|
||||
serverEventManager.host.reloadFS(files);
|
||||
serverEventManager.host.runQueuedTimeoutCallbacks();
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path);
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path, emptyArray);
|
||||
});
|
||||
|
||||
it("are not generated when the config file does not include file opened and config file has errors", () => {
|
||||
const file = {
|
||||
const file: File = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 10"
|
||||
};
|
||||
const file2 = {
|
||||
const file2: File = {
|
||||
path: "/a/b/test.ts",
|
||||
content: "let x = 10"
|
||||
};
|
||||
const configFile = {
|
||||
const configFile: File = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"compilerOptions": {
|
||||
@@ -4792,11 +4935,11 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("are not generated when the config file has errors but suppressDiagnosticEvents is true", () => {
|
||||
const file = {
|
||||
const file: File = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 10"
|
||||
};
|
||||
const configFile = {
|
||||
const configFile: File = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"compilerOptions": {
|
||||
@@ -4805,21 +4948,21 @@ namespace ts.projectSystem {
|
||||
}
|
||||
}`
|
||||
};
|
||||
const serverEventManager = new TestServerEventManager([file, configFile], /*suppressDiagnosticEvents*/ true);
|
||||
const serverEventManager = new TestServerEventManager([file, libFile, configFile], /*suppressDiagnosticEvents*/ true);
|
||||
openFilesForSession([file], serverEventManager.session);
|
||||
serverEventManager.hasZeroEvent("configFileDiag");
|
||||
});
|
||||
|
||||
it("are not generated when the config file does not include file opened and doesnt contain any errors", () => {
|
||||
const file = {
|
||||
const file: File = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 10"
|
||||
};
|
||||
const file2 = {
|
||||
const file2: File = {
|
||||
path: "/a/b/test.ts",
|
||||
content: "let x = 10"
|
||||
};
|
||||
const configFile = {
|
||||
const configFile: File = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"files": ["app.ts"]
|
||||
@@ -4830,6 +4973,27 @@ namespace ts.projectSystem {
|
||||
openFilesForSession([file2], serverEventManager.session);
|
||||
serverEventManager.hasZeroEvent("configFileDiag");
|
||||
});
|
||||
|
||||
it("contains the project reference errors", () => {
|
||||
const file: File = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 10"
|
||||
};
|
||||
const noSuchTsconfig = "no-such-tsconfig.json";
|
||||
const configFile: File = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"files": ["app.ts"],
|
||||
"references": [{"path":"./${noSuchTsconfig}"}]
|
||||
}`
|
||||
};
|
||||
|
||||
const serverEventManager = new TestServerEventManager([file, libFile, configFile]);
|
||||
openFilesForSession([file], serverEventManager.session);
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, [
|
||||
getFileNotFoundDiagnostic(configFile, noSuchTsconfig)
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem skipLibCheck", () => {
|
||||
@@ -6780,7 +6944,7 @@ namespace ts.projectSystem {
|
||||
const { configFileName } = projectService.openClientFile(file1.path);
|
||||
assert.equal(configFileName, tsconfigFile.path as server.NormalizedPath, `should find config`); // tslint:disable-line no-unnecessary-type-assertion (TODO: GH#18217)
|
||||
checkNumberOfConfiguredProjects(projectService, 1);
|
||||
const watchingRecursiveDirectories = [`${canonicalFrontendDir}/src`, canonicalFrontendDir].concat(getNodeModuleDirectories(getDirectoryPath(canonicalFrontendDir)));
|
||||
const watchingRecursiveDirectories = [`${canonicalFrontendDir}/src`, `${canonicalFrontendDir}/types`, `${canonicalFrontendDir}/node_modules`].concat(getNodeModuleDirectories(getDirectoryPath(canonicalFrontendDir)));
|
||||
|
||||
const project = projectService.configuredProjects.get(canonicalConfigPath)!;
|
||||
verifyProjectAndWatchedDirectories();
|
||||
@@ -6934,7 +7098,7 @@ namespace ts.projectSystem {
|
||||
const projectService = createProjectService(host);
|
||||
const { configFileName } = projectService.openClientFile(app.path);
|
||||
assert.equal(configFileName, tsconfigJson.path as server.NormalizedPath, `should find config`); // TODO: GH#18217
|
||||
const recursiveWatchedDirectories: string[] = [appFolder].concat(getNodeModuleDirectories(getDirectoryPath(appFolder)));
|
||||
const recursiveWatchedDirectories: string[] = [`${appFolder}`, `${appFolder}/node_modules`].concat(getNodeModuleDirectories(getDirectoryPath(appFolder)));
|
||||
verifyProject();
|
||||
|
||||
let timeoutAfterReloadFs = timeoutDuringPartialInstallation;
|
||||
@@ -7014,7 +7178,7 @@ namespace ts.projectSystem {
|
||||
const lodashIndexPath = root + "/a/b/node_modules/@types/lodash/index.d.ts";
|
||||
projectFiles.push(find(filesAndFoldersToAdd, f => f.path === lodashIndexPath)!);
|
||||
// we would now not have failed lookup in the parent of appFolder since lodash is available
|
||||
recursiveWatchedDirectories.length = 1;
|
||||
recursiveWatchedDirectories.length = 2;
|
||||
// npm installation complete, timeout after reload fs
|
||||
timeoutAfterReloadFs = true;
|
||||
verifyAfterPartialOrCompleteNpmInstall(2);
|
||||
@@ -7534,9 +7698,9 @@ namespace ts.projectSystem {
|
||||
const openFiles = [file1.path];
|
||||
const watchedRecursiveDirectories = useSlashRootAsSomeNotRootFolderInUserDirectory ?
|
||||
// Folders of node_modules lookup not in changedRoot
|
||||
["a/b/project", "a/b/node_modules", "a/node_modules", "node_modules"].map(v => rootFolder + v) :
|
||||
["a/b/project", "a/b/project/node_modules", "a/b/node_modules", "a/node_modules", "node_modules"].map(v => rootFolder + v) :
|
||||
// Folder of tsconfig
|
||||
["/a/b/project"];
|
||||
["/a/b/project", "/a/b/project/node_modules"];
|
||||
const host = createServerHost(projectFiles);
|
||||
const { session, verifyInitialOpen, verifyProjectsUpdatedInBackgroundEventHandler } = createSession(host);
|
||||
const projectService = session.getProjectService();
|
||||
@@ -7565,7 +7729,7 @@ namespace ts.projectSystem {
|
||||
host.reloadFS(projectFiles);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
if (useSlashRootAsSomeNotRootFolderInUserDirectory) {
|
||||
watchedRecursiveDirectories.length = 2;
|
||||
watchedRecursiveDirectories.length = 3;
|
||||
}
|
||||
else {
|
||||
// file2 addition wont be detected
|
||||
@@ -7993,10 +8157,10 @@ new C();`
|
||||
checkCompleteEvent(session, 2, expectedSequenceId);
|
||||
}
|
||||
|
||||
function verifyWatchedFilesAndDirectories(host: TestServerHost, files: string[], recursiveDirectories: string[], nonRecursiveDirectories: string[]) {
|
||||
function verifyWatchedFilesAndDirectories(host: TestServerHost, files: string[], recursiveDirectories: ReadonlyMap<number>, nonRecursiveDirectories: string[]) {
|
||||
checkWatchedFilesDetailed(host, files.filter(f => f !== recognizersDateTimeSrcFile.path), 1);
|
||||
checkWatchedDirectoriesDetailed(host, nonRecursiveDirectories, 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, recursiveDirectories, 1, /*recursive*/ true);
|
||||
checkWatchedDirectoriesDetailed(host, nonRecursiveDirectories, 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, recursiveDirectories, /*recursive*/ true);
|
||||
}
|
||||
|
||||
function createSessionAndOpenFile(host: TestServerHost) {
|
||||
@@ -8017,8 +8181,16 @@ new C();`
|
||||
const filesWithNodeModulesSetup = [...filesWithSources, nodeModulesRecorgnizersText];
|
||||
const filesAfterCompilation = [...filesWithNodeModulesSetup, recongnizerTextDistTypingFile];
|
||||
|
||||
const watchedDirectoriesWithResolvedModule = [`${recognizersDateTime}/src`, ...(withPathMapping ? emptyArray : [recognizersDateTime]), ...getTypeRootsFromLocation(recognizersDateTime)];
|
||||
const watchedDirectoriesWithUnresolvedModule = [recognizersDateTime, ...(withPathMapping ? [recognizersText] : emptyArray), ...watchedDirectoriesWithResolvedModule, ...getNodeModuleDirectories(packages)];
|
||||
const watchedDirectoriesWithResolvedModule = arrayToMap(getTypeRootsFromLocation(recognizersDateTime), k => k, () => 1);
|
||||
watchedDirectoriesWithResolvedModule.set(`${recognizersDateTime}/src`, withPathMapping ? 1 : 2); // wild card + failed lookups
|
||||
if (!withPathMapping) {
|
||||
watchedDirectoriesWithResolvedModule.set(`${recognizersDateTime}/node_modules`, 1); // failed lookups
|
||||
}
|
||||
const watchedDirectoriesWithUnresolvedModule = cloneMap(watchedDirectoriesWithResolvedModule);
|
||||
watchedDirectoriesWithUnresolvedModule.set(`${recognizersDateTime}/src`, 2); // wild card + failed lookups
|
||||
[`${recognizersDateTime}/node_modules`, ...(withPathMapping ? [recognizersText] : emptyArray), ...getNodeModuleDirectories(packages)].forEach(d => {
|
||||
watchedDirectoriesWithUnresolvedModule.set(d, 1);
|
||||
});
|
||||
const nonRecursiveWatchedDirectories = withPathMapping ? [packages] : emptyArray;
|
||||
|
||||
function verifyProjectWithResolvedModule(session: TestSession) {
|
||||
@@ -8251,11 +8423,10 @@ new C();`
|
||||
return `Reusing resolution of module '${moduleName}' to file '${file.path}' from old program.`;
|
||||
}
|
||||
|
||||
function verifyWatchesWithConfigFile(host: TestServerHost, files: File[], openFile: File) {
|
||||
function verifyWatchesWithConfigFile(host: TestServerHost, files: File[], openFile: File, extraExpectedDirectories?: ReadonlyArray<string>) {
|
||||
checkWatchedFiles(host, mapDefined(files, f => f === openFile ? undefined : f.path));
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
const configDirectory = getDirectoryPath(configFile.path);
|
||||
checkWatchedDirectories(host, [configDirectory, `${configDirectory}/${nodeModulesAtTypes}`], /*recursive*/ true);
|
||||
checkWatchedDirectories(host, [projectLocation, `${projectLocation}/${nodeModulesAtTypes}`, ...(extraExpectedDirectories || emptyArray)], /*recursive*/ true);
|
||||
}
|
||||
|
||||
describe("from files in same folder", () => {
|
||||
@@ -8299,6 +8470,7 @@ new C();`
|
||||
});
|
||||
|
||||
it("non relative module name", () => {
|
||||
const expectedNonRelativeDirectories = [`${projectLocation}/node_modules`, `${projectLocation}/src`];
|
||||
const module1Name = "module1";
|
||||
const module2Name = "module2";
|
||||
const fileContent = `import { module1 } from "${module1Name}";import { module2 } from "${module2Name}";`;
|
||||
@@ -8312,7 +8484,7 @@ new C();`
|
||||
const expectedTrace = getExpectedNonRelativeModuleResolutionTrace(host, file1, module1, module1Name);
|
||||
getExpectedNonRelativeModuleResolutionTrace(host, file1, module2, module2Name, expectedTrace);
|
||||
verifyTrace(resolutionTrace, expectedTrace);
|
||||
verifyWatchesWithConfigFile(host, files, file1);
|
||||
verifyWatchesWithConfigFile(host, files, file1, expectedNonRelativeDirectories);
|
||||
|
||||
file1.content += fileContent;
|
||||
file2.content += fileContent;
|
||||
@@ -8322,7 +8494,7 @@ new C();`
|
||||
getExpectedReusingResolutionFromOldProgram(file1, module1Name),
|
||||
getExpectedReusingResolutionFromOldProgram(file1, module2Name)
|
||||
]);
|
||||
verifyWatchesWithConfigFile(host, files, file1);
|
||||
verifyWatchesWithConfigFile(host, files, file1, expectedNonRelativeDirectories);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8391,6 +8563,7 @@ new C();`
|
||||
});
|
||||
|
||||
it("non relative module name", () => {
|
||||
const expectedNonRelativeDirectories = [`${projectLocation}/node_modules`, `${projectLocation}/product`];
|
||||
const module1Name = "module1";
|
||||
const module2Name = "module2";
|
||||
const fileContent = `import { module1 } from "${module1Name}";import { module2 } from "${module2Name}";`;
|
||||
@@ -8410,7 +8583,7 @@ new C();`
|
||||
getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file3, module1, module1Name, getDirectoryPath(file4.path), expectedTrace);
|
||||
getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file3, module2, module2Name, getDirectoryPath(file4.path), expectedTrace);
|
||||
verifyTrace(resolutionTrace, expectedTrace);
|
||||
verifyWatchesWithConfigFile(host, files, file1);
|
||||
verifyWatchesWithConfigFile(host, files, file1, expectedNonRelativeDirectories);
|
||||
|
||||
file1.content += fileContent;
|
||||
file2.content += fileContent;
|
||||
@@ -8423,7 +8596,7 @@ new C();`
|
||||
getExpectedReusingResolutionFromOldProgram(file1, module1Name),
|
||||
getExpectedReusingResolutionFromOldProgram(file1, module2Name)
|
||||
]);
|
||||
verifyWatchesWithConfigFile(host, files, file1);
|
||||
verifyWatchesWithConfigFile(host, files, file1, expectedNonRelativeDirectories);
|
||||
});
|
||||
|
||||
it("non relative module name from inferred project", () => {
|
||||
@@ -8460,7 +8633,7 @@ new C();`
|
||||
watchedFiles.push(combinePaths(d, "tsconfig.json"), combinePaths(d, "jsconfig.json"));
|
||||
});
|
||||
const watchedRecursiveDirectories = getTypeRootsFromLocation(currentDirectory).concat([
|
||||
currentDirectory, `${projectLocation}/product/${nodeModules}`,
|
||||
`${currentDirectory}/node_modules`, `${currentDirectory}/feature`, `${projectLocation}/product/${nodeModules}`,
|
||||
`${projectLocation}/${nodeModules}`, `${projectLocation}/product/test/${nodeModules}`,
|
||||
`${projectLocation}/product/test/src/${nodeModules}`
|
||||
]);
|
||||
@@ -8530,7 +8703,6 @@ export const x = 10;`
|
||||
outDir: "../out",
|
||||
baseUrl: "./",
|
||||
typeRoots: ["typings"]
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -8546,13 +8718,15 @@ export const x = 10;`
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false); // since fs resolves to ambient module, shouldnt watch failed lookup
|
||||
}
|
||||
else {
|
||||
checkWatchedDirectoriesDetailed(host, [`${projectRoot}`], 1, /*recursive*/ false); // failed lookup for fs
|
||||
checkWatchedDirectoriesDetailed(host, [`${projectRoot}`, `${projectRoot}/src`], 1, /*recursive*/ false); // failed lookup for fs
|
||||
}
|
||||
const expectedWatchedDirectories = createMap<number>();
|
||||
expectedWatchedDirectories.set(`${projectRoot}/src`, 2); // Wild card and failed lookup
|
||||
expectedWatchedDirectories.set(`${projectRoot}/src`, 1); // Wild card
|
||||
expectedWatchedDirectories.set(`${projectRoot}/src/somefolder`, 1); // failedLookup for somefolder/module2
|
||||
expectedWatchedDirectories.set(`${projectRoot}/src/node_modules`, 1); // failed lookup for somefolder/module2
|
||||
expectedWatchedDirectories.set(`${projectRoot}/somefolder`, 1); // failed lookup for somefolder/module2
|
||||
expectedWatchedDirectories.set(`${projectRoot}/node_modules`, 1); // failed lookup for with node_modules/@types/fs
|
||||
expectedWatchedDirectories.set(`${projectRoot}/src/typings`, 1); // typeroot directory
|
||||
expectedWatchedDirectories.set(`${projectRoot}/src/typings`, useNodeFile ? 1 : 2); // typeroot directory + failed lookup if not using node file
|
||||
checkWatchedDirectoriesDetailed(host, expectedWatchedDirectories, /*recursive*/ true);
|
||||
}
|
||||
|
||||
@@ -8943,7 +9117,7 @@ export function Test2() {
|
||||
});
|
||||
});
|
||||
|
||||
function makeSampleProjects() {
|
||||
describe("tsserverProjectSystem project references", () => {
|
||||
const aTs: File = {
|
||||
path: "/a/a.ts",
|
||||
content: "export function fnA() {}\nexport interface IfaceA {}\nexport const instanceA: IfaceA = {};",
|
||||
@@ -8999,62 +9173,121 @@ export function Test2() {
|
||||
content: `export declare function fnB(): void;\n//# source${""}MappingURL=b.d.ts.map`,
|
||||
};
|
||||
|
||||
const dummyFile: File = {
|
||||
path: "/dummy/dummy.ts",
|
||||
content: "let a = 10;"
|
||||
};
|
||||
|
||||
const userTs: File = {
|
||||
path: "/user/user.ts",
|
||||
content: 'import { fnA, instanceA } from "../a/bin/a";\nimport { fnB } from "../b/bin/b";\nexport function fnUser() { fnA(); fnB(); instanceA; }',
|
||||
};
|
||||
|
||||
const host = createServerHost([aTs, aTsconfig, aDtsMap, aDts, bTsconfig, bTs, bDtsMap, bDts, userTs]);
|
||||
const session = createSession(host);
|
||||
function makeSampleProjects() {
|
||||
const host = createServerHost([aTs, aTsconfig, aDtsMap, aDts, bTsconfig, bTs, bDtsMap, bDts, userTs, dummyFile]);
|
||||
const session = createSession(host);
|
||||
|
||||
checkDeclarationFiles(aTs, session, [aDtsMap, aDts]);
|
||||
checkDeclarationFiles(bTs, session, [bDtsMap, bDts]);
|
||||
checkDeclarationFiles(aTs, session, [aDtsMap, aDts]);
|
||||
checkDeclarationFiles(bTs, session, [bDtsMap, bDts]);
|
||||
|
||||
// Testing what happens if we delete the original sources.
|
||||
host.removeFile(bTs.path);
|
||||
// Testing what happens if we delete the original sources.
|
||||
host.removeFile(bTs.path);
|
||||
|
||||
openFilesForSession([userTs], session);
|
||||
openFilesForSession([userTs], session);
|
||||
const service = session.getProjectService();
|
||||
checkNumberOfProjects(service, { inferredProjects: 1 });
|
||||
return session;
|
||||
}
|
||||
|
||||
return { session, aTs, bDts, userTs };
|
||||
}
|
||||
function verifyInferredProjectUnchanged(session: TestSession) {
|
||||
checkProjectActualFiles(session.getProjectService().inferredProjects[0], [userTs.path, aDts.path, bDts.path]);
|
||||
}
|
||||
|
||||
function verifyDummyProject(session: TestSession) {
|
||||
checkProjectActualFiles(session.getProjectService().inferredProjects[0], [dummyFile.path]);
|
||||
}
|
||||
|
||||
function verifyOnlyOrphanInferredProject(session: TestSession) {
|
||||
openFilesForSession([dummyFile], session);
|
||||
checkNumberOfProjects(session.getProjectService(), { inferredProjects: 1 });
|
||||
verifyDummyProject(session);
|
||||
}
|
||||
|
||||
function verifySingleInferredProject(session: TestSession) {
|
||||
checkNumberOfProjects(session.getProjectService(), { inferredProjects: 1 });
|
||||
verifyInferredProjectUnchanged(session);
|
||||
|
||||
// Close user file should close all the projects after opening dummy file
|
||||
closeFilesForSession([userTs], session);
|
||||
verifyOnlyOrphanInferredProject(session);
|
||||
}
|
||||
|
||||
function verifyATsConfigProject(session: TestSession) {
|
||||
checkProjectActualFiles(session.getProjectService().configuredProjects.get(aTsconfig.path)!, [aTs.path, aTsconfig.path]);
|
||||
}
|
||||
|
||||
function verifyATsConfigOriginalProject(session: TestSession) {
|
||||
checkNumberOfProjects(session.getProjectService(), { inferredProjects: 1, configuredProjects: 1 });
|
||||
verifyInferredProjectUnchanged(session);
|
||||
verifyATsConfigProject(session);
|
||||
// Close user file should close all the projects
|
||||
closeFilesForSession([userTs], session);
|
||||
verifyOnlyOrphanInferredProject(session);
|
||||
}
|
||||
|
||||
function verifyATsConfigWhenOpened(session: TestSession) {
|
||||
checkNumberOfProjects(session.getProjectService(), { inferredProjects: 1, configuredProjects: 1 });
|
||||
verifyInferredProjectUnchanged(session);
|
||||
verifyATsConfigProject(session);
|
||||
|
||||
closeFilesForSession([userTs], session);
|
||||
openFilesForSession([dummyFile], session);
|
||||
checkNumberOfProjects(session.getProjectService(), { inferredProjects: 1, configuredProjects: 1 });
|
||||
verifyDummyProject(session);
|
||||
verifyATsConfigProject(session); // ATsConfig should still be alive
|
||||
}
|
||||
|
||||
describe("tsserverProjectSystem project references", () => {
|
||||
it("goToDefinition", () => {
|
||||
const { session, aTs, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
const response = executeSessionRequest<protocol.DefinitionRequest, protocol.DefinitionResponse>(session, protocol.CommandTypes.Definition, protocolFileLocationFromSubstring(userTs, "fnA()"));
|
||||
assert.deepEqual(response, [protocolFileSpanFromSubstring(aTs, "fnA")]);
|
||||
verifySingleInferredProject(session);
|
||||
});
|
||||
|
||||
it("getDefinitionAndBoundSpan", () => {
|
||||
const { session, aTs, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
const response = executeSessionRequest<protocol.DefinitionAndBoundSpanRequest, protocol.DefinitionAndBoundSpanResponse>(session, protocol.CommandTypes.DefinitionAndBoundSpan, protocolFileLocationFromSubstring(userTs, "fnA()"));
|
||||
assert.deepEqual(response, {
|
||||
textSpan: protocolTextSpanFromSubstring(userTs.content, "fnA", { index: 1 }),
|
||||
definitions: [protocolFileSpanFromSubstring(aTs, "fnA")],
|
||||
});
|
||||
verifySingleInferredProject(session);
|
||||
});
|
||||
|
||||
it("goToType", () => {
|
||||
const { session, aTs, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
const response = executeSessionRequest<protocol.TypeDefinitionRequest, protocol.TypeDefinitionResponse>(session, protocol.CommandTypes.TypeDefinition, protocolFileLocationFromSubstring(userTs, "instanceA"));
|
||||
assert.deepEqual(response, [protocolFileSpanFromSubstring(aTs, "IfaceA")]);
|
||||
verifySingleInferredProject(session);
|
||||
});
|
||||
|
||||
it("goToImplementation", () => {
|
||||
const { session, aTs, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
const response = executeSessionRequest<protocol.ImplementationRequest, protocol.ImplementationResponse>(session, protocol.CommandTypes.Implementation, protocolFileLocationFromSubstring(userTs, "fnA()"));
|
||||
assert.deepEqual(response, [protocolFileSpanFromSubstring(aTs, "fnA")]);
|
||||
verifySingleInferredProject(session);
|
||||
});
|
||||
|
||||
it("goToDefinition -- target does not exist", () => {
|
||||
const { session, bDts, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
const response = executeSessionRequest<protocol.DefinitionRequest, protocol.DefinitionResponse>(session, CommandNames.Definition, protocolFileLocationFromSubstring(userTs, "fnB()"));
|
||||
// bTs does not exist, so stick with bDts
|
||||
assert.deepEqual(response, [protocolFileSpanFromSubstring(bDts, "fnB")]);
|
||||
verifySingleInferredProject(session);
|
||||
});
|
||||
|
||||
it("navigateTo", () => {
|
||||
const { session, aTs, bDts, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
const response = executeSessionRequest<protocol.NavtoRequest, protocol.NavtoResponse>(session, CommandNames.Navto, { file: userTs.path, searchValue: "fn" });
|
||||
assert.deepEqual<ReadonlyArray<protocol.NavtoItem> | undefined>(response, [
|
||||
{
|
||||
@@ -9082,6 +9315,8 @@ export function Test2() {
|
||||
kindModifiers: "export",
|
||||
},
|
||||
]);
|
||||
|
||||
verifyATsConfigOriginalProject(session);
|
||||
});
|
||||
|
||||
const referenceATs = (aTs: File): protocol.ReferencesResponseItem => makeReferenceItem(aTs, /*isDefinition*/ true, "fnA", "export function fnA() {}");
|
||||
@@ -9091,7 +9326,7 @@ export function Test2() {
|
||||
];
|
||||
|
||||
it("findAllReferences", () => {
|
||||
const { session, aTs, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
|
||||
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(session, protocol.CommandTypes.References, protocolFileLocationFromSubstring(userTs, "fnA()"));
|
||||
assert.deepEqual<protocol.ReferencesResponseBody | undefined>(response, {
|
||||
@@ -9100,10 +9335,12 @@ export function Test2() {
|
||||
symbolStartOffset: protocolLocationFromSubstring(userTs.content, "fnA()").offset,
|
||||
symbolDisplayString: "(alias) fnA(): void\nimport fnA",
|
||||
});
|
||||
|
||||
verifyATsConfigOriginalProject(session);
|
||||
});
|
||||
|
||||
it("findAllReferences -- starting at definition", () => {
|
||||
const { session, aTs, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
openFilesForSession([aTs], session); // If it's not opened, the reference isn't found.
|
||||
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(session, protocol.CommandTypes.References, protocolFileLocationFromSubstring(aTs, "fnA"));
|
||||
assert.deepEqual<protocol.ReferencesResponseBody | undefined>(response, {
|
||||
@@ -9112,10 +9349,11 @@ export function Test2() {
|
||||
symbolStartOffset: protocolLocationFromSubstring(aTs.content, "fnA").offset,
|
||||
symbolDisplayString: "function fnA(): void",
|
||||
});
|
||||
verifyATsConfigWhenOpened(session);
|
||||
});
|
||||
|
||||
it("findAllReferencesFull", () => {
|
||||
const { session, aTs, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
|
||||
interface ReferencesFullRequest extends protocol.FileLocationRequest { command: protocol.CommandTypes.ReferencesFull; }
|
||||
interface ReferencesFullResponse extends protocol.Response { body: ReadonlyArray<ReferencedSymbol>; }
|
||||
@@ -9172,10 +9410,11 @@ export function Test2() {
|
||||
],
|
||||
}
|
||||
]);
|
||||
verifyATsConfigOriginalProject(session);
|
||||
});
|
||||
|
||||
it("findAllReferences -- target does not exist", () => {
|
||||
const { session, bDts, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
|
||||
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(session, protocol.CommandTypes.References, protocolFileLocationFromSubstring(userTs, "fnB()"));
|
||||
assert.deepEqual<protocol.ReferencesResponseBody | undefined>(response, {
|
||||
@@ -9188,6 +9427,7 @@ export function Test2() {
|
||||
symbolStartOffset: protocolLocationFromSubstring(userTs.content, "fnB()").offset,
|
||||
symbolDisplayString: "(alias) fnB(): void\nimport fnB",
|
||||
});
|
||||
verifySingleInferredProject(session);
|
||||
});
|
||||
|
||||
const renameATs = (aTs: File): protocol.SpanGroup => ({
|
||||
@@ -9203,7 +9443,7 @@ export function Test2() {
|
||||
});
|
||||
|
||||
it("renameLocations", () => {
|
||||
const { session, aTs, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
const response = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(userTs, "fnA()"));
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response, {
|
||||
info: {
|
||||
@@ -9216,10 +9456,11 @@ export function Test2() {
|
||||
},
|
||||
locs: [renameUserTs(userTs), renameATs(aTs)],
|
||||
});
|
||||
verifyATsConfigOriginalProject(session);
|
||||
});
|
||||
|
||||
it("renameLocations -- starting at definition", () => {
|
||||
const { session, aTs, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
openFilesForSession([aTs], session); // If it's not opened, the reference isn't found.
|
||||
const response = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(aTs, "fnA"));
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response, {
|
||||
@@ -9233,20 +9474,22 @@ export function Test2() {
|
||||
},
|
||||
locs: [renameATs(aTs), renameUserTs(userTs)],
|
||||
});
|
||||
verifyATsConfigWhenOpened(session);
|
||||
});
|
||||
|
||||
it("renameLocationsFull", () => {
|
||||
const { session, aTs, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
const response = executeSessionRequest<protocol.RenameFullRequest, protocol.RenameFullResponse>(session, protocol.CommandTypes.RenameLocationsFull, protocolFileLocationFromSubstring(userTs, "fnA()"));
|
||||
assert.deepEqual<ReadonlyArray<RenameLocation>>(response, [
|
||||
documentSpanFromSubstring(userTs, "fnA"),
|
||||
documentSpanFromSubstring(userTs, "fnA", { index: 1 }),
|
||||
documentSpanFromSubstring(aTs, "fnA"),
|
||||
]);
|
||||
verifyATsConfigOriginalProject(session);
|
||||
});
|
||||
|
||||
it("renameLocations -- target does not exist", () => {
|
||||
const { session, bDts, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
const response = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(userTs, "fnB()"));
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response, {
|
||||
info: {
|
||||
@@ -9271,11 +9514,11 @@ export function Test2() {
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
verifySingleInferredProject(session);
|
||||
});
|
||||
|
||||
it("getEditsForFileRename", () => {
|
||||
const { session, aTs, userTs } = makeSampleProjects();
|
||||
const session = makeSampleProjects();
|
||||
const response = executeSessionRequest<protocol.GetEditsForFileRenameRequest, protocol.GetEditsForFileRenameResponse>(session, protocol.CommandTypes.GetEditsForFileRename, {
|
||||
oldFilePath: aTs.path,
|
||||
newFilePath: "/a/aNew.ts",
|
||||
@@ -9288,10 +9531,64 @@ export function Test2() {
|
||||
],
|
||||
},
|
||||
]);
|
||||
verifySingleInferredProject(session);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Untitled files", () => {
|
||||
describe("duplicate packages", () => {
|
||||
// Tests that 'moduleSpecifiers.ts' will import from the redirecting file, and not from the file it redirects to, if that can provide a global module specifier.
|
||||
it("works with import fixes", () => {
|
||||
const packageContent = "export const foo: number;";
|
||||
const packageJsonContent = JSON.stringify({ name: "foo", version: "1.2.3" });
|
||||
const aFooIndex: File = { path: "/a/node_modules/foo/index.d.ts", content: packageContent };
|
||||
const aFooPackage: File = { path: "/a/node_modules/foo/package.json", content: packageJsonContent };
|
||||
const bFooIndex: File = { path: "/b/node_modules/foo/index.d.ts", content: packageContent };
|
||||
const bFooPackage: File = { path: "/b/node_modules/foo/package.json", content: packageJsonContent };
|
||||
|
||||
const userContent = 'import("foo");\nfoo';
|
||||
const aUser: File = { path: "/a/user.ts", content: userContent };
|
||||
const bUser: File = { path: "/b/user.ts", content: userContent };
|
||||
const tsconfig: File = {
|
||||
path: "/tsconfig.json",
|
||||
content: "{}",
|
||||
};
|
||||
|
||||
const host = createServerHost([aFooIndex, aFooPackage, bFooIndex, bFooPackage, aUser, bUser, tsconfig]);
|
||||
const session = createSession(host);
|
||||
|
||||
openFilesForSession([aUser, bUser], session);
|
||||
|
||||
for (const user of [aUser, bUser]) {
|
||||
const response = executeSessionRequest<protocol.CodeFixRequest, protocol.CodeFixResponse>(session, protocol.CommandTypes.GetCodeFixes, {
|
||||
file: user.path,
|
||||
startLine: 2,
|
||||
startOffset: 1,
|
||||
endLine: 2,
|
||||
endOffset: 4,
|
||||
errorCodes: [Diagnostics.Cannot_find_name_0.code],
|
||||
});
|
||||
assert.deepEqual<ReadonlyArray<protocol.CodeFixAction> | undefined>(response, [
|
||||
{
|
||||
description: `Import 'foo' from module "foo"`,
|
||||
fixName: "import",
|
||||
fixId: "fixMissingImport",
|
||||
fixAllDescription: "Add all missing imports",
|
||||
changes: [{
|
||||
fileName: user.path,
|
||||
textChanges: [{
|
||||
start: { line: 1, offset: 1 },
|
||||
end: { line: 1, offset: 1 },
|
||||
newText: 'import { foo } from "foo";\n\n',
|
||||
}],
|
||||
}],
|
||||
commands: undefined,
|
||||
},
|
||||
]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem Untitled files", () => {
|
||||
it("Can convert positions to locations", () => {
|
||||
const aTs: File = { path: "/proj/a.ts", content: "" };
|
||||
const tsconfig: File = { path: "/proj/tsconfig.json", content: "{}" };
|
||||
|
||||
@@ -146,8 +146,10 @@ namespace ts.projectSystem {
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
|
||||
|
||||
const expectedWatchedDirectoriesRecursive = createMap<number>();
|
||||
expectedWatchedDirectoriesRecursive.set("/a/b", 2); // TypingInstaller and wild card
|
||||
expectedWatchedDirectoriesRecursive.set("/a/b", 1); // wild card
|
||||
expectedWatchedDirectoriesRecursive.set("/a/b/node_modules/@types", 1); // type root watch
|
||||
expectedWatchedDirectoriesRecursive.set("/a/b/node_modules", 1); // TypingInstaller
|
||||
expectedWatchedDirectoriesRecursive.set("/a/b/bower_components", 1); // TypingInstaller
|
||||
checkWatchedDirectoriesDetailed(host, expectedWatchedDirectoriesRecursive, /*recursive*/ true);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
@@ -844,9 +846,7 @@ namespace ts.projectSystem {
|
||||
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
|
||||
|
||||
const watchedRecursiveDirectoriesExpected = createMap<number>();
|
||||
watchedRecursiveDirectoriesExpected.set("/", 2); // wild card + type installer
|
||||
checkWatchedDirectoriesDetailed(host, watchedRecursiveDirectoriesExpected, /*recursive*/ true);
|
||||
checkWatchedDirectoriesDetailed(host, ["/", "/node_modules", "/bower_components"], 1, /*recursive*/ true);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
|
||||
@@ -455,7 +455,7 @@ namespace ts.server {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assertTypeIsNever(response);
|
||||
assertType<never>(response);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,18 +64,24 @@ namespace ts.server.typingsInstaller {
|
||||
onRequestCompleted: RequestCompletedAction;
|
||||
}
|
||||
|
||||
function isPackageOrBowerJson(fileName: string) {
|
||||
const base = getBaseFileName(fileName);
|
||||
return base === "package.json" || base === "bower.json";
|
||||
function endsWith(str: string, suffix: string, caseSensitive: boolean): boolean {
|
||||
const expectedPos = str.length - suffix.length;
|
||||
return expectedPos >= 0 &&
|
||||
(str.indexOf(suffix, expectedPos) === expectedPos ||
|
||||
(!caseSensitive && compareStringsCaseInsensitive(str.substr(expectedPos), suffix) === Comparison.EqualTo));
|
||||
}
|
||||
|
||||
function getDirectoryExcludingNodeModulesOrBowerComponents(f: string) {
|
||||
const indexOfNodeModules = f.indexOf("/node_modules/");
|
||||
const indexOfBowerComponents = f.indexOf("/bower_components/");
|
||||
const subStrLength = indexOfNodeModules === -1 || indexOfBowerComponents === -1 ?
|
||||
Math.max(indexOfNodeModules, indexOfBowerComponents) :
|
||||
Math.min(indexOfNodeModules, indexOfBowerComponents);
|
||||
return subStrLength === -1 ? f : f.substr(0, subStrLength);
|
||||
function isPackageOrBowerJson(fileName: string, caseSensitive: boolean) {
|
||||
return endsWith(fileName, "/package.json", caseSensitive) || endsWith(fileName, "/bower.json", caseSensitive);
|
||||
}
|
||||
|
||||
function sameFiles(a: string, b: string, caseSensitive: boolean) {
|
||||
return a === b || (!caseSensitive && compareStringsCaseInsensitive(a, b) === Comparison.EqualTo);
|
||||
}
|
||||
|
||||
const enum ProjectWatcherType {
|
||||
FileWatcher = "FileWatcher",
|
||||
DirectoryWatcher = "DirectoryWatcher"
|
||||
}
|
||||
|
||||
type ProjectWatchers = Map<FileWatcher> & { isInvoked?: boolean; };
|
||||
@@ -88,7 +94,7 @@ namespace ts.server.typingsInstaller {
|
||||
private safeList: JsTyping.SafeList | undefined;
|
||||
readonly pendingRunRequests: PendingRequest[] = [];
|
||||
private readonly toCanonicalFileName: GetCanonicalFileName;
|
||||
private readonly globalCacheCanonicalPackageJsonPath: string;
|
||||
private readonly globalCachePackageJsonPath: string;
|
||||
|
||||
private installRunCount = 1;
|
||||
private inFlightRequestCount = 0;
|
||||
@@ -103,7 +109,7 @@ namespace ts.server.typingsInstaller {
|
||||
private readonly throttleLimit: number,
|
||||
protected readonly log = nullLog) {
|
||||
this.toCanonicalFileName = createGetCanonicalFileName(installTypingHost.useCaseSensitiveFileNames);
|
||||
this.globalCacheCanonicalPackageJsonPath = combinePaths(this.toCanonicalFileName(globalCachePath), "package.json");
|
||||
this.globalCachePackageJsonPath = combinePaths(globalCachePath, "package.json");
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(`Global cache location '${globalCachePath}', safe file path '${safeListPath}', types map path ${typesMapLocation}`);
|
||||
}
|
||||
@@ -406,84 +412,79 @@ namespace ts.server.typingsInstaller {
|
||||
watchers.isInvoked = false;
|
||||
|
||||
const isLoggingEnabled = this.log.isEnabled();
|
||||
const createProjectWatcher = (path: string, createWatch: (path: string) => FileWatcher) => {
|
||||
toRemove.delete(path);
|
||||
if (watchers.has(path)) {
|
||||
const createProjectWatcher = (path: string, projectWatcherType: ProjectWatcherType) => {
|
||||
const canonicalPath = this.toCanonicalFileName(path);
|
||||
toRemove.delete(canonicalPath);
|
||||
if (watchers.has(canonicalPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
watchers.set(path, createWatch(path));
|
||||
};
|
||||
const createProjectFileWatcher = (file: string): FileWatcher => {
|
||||
if (isLoggingEnabled) {
|
||||
this.log.writeLine(`FileWatcher:: Added:: WatchInfo: ${file}`);
|
||||
this.log.writeLine(`${projectWatcherType}:: Added:: WatchInfo: ${path}`);
|
||||
}
|
||||
const watcher = this.installTypingHost.watchFile!(file, (f, eventKind) => { // TODO: GH#18217
|
||||
if (isLoggingEnabled) {
|
||||
this.log.writeLine(`FileWatcher:: Triggered with ${f} eventKind: ${FileWatcherEventKind[eventKind]}:: WatchInfo: ${file}:: handler is already invoked '${watchers.isInvoked}'`);
|
||||
}
|
||||
if (!watchers.isInvoked) {
|
||||
watchers.isInvoked = true;
|
||||
this.sendResponse({ projectName, kind: ActionInvalidate });
|
||||
}
|
||||
}, /*pollingInterval*/ 2000);
|
||||
const watcher = projectWatcherType === ProjectWatcherType.FileWatcher ?
|
||||
this.installTypingHost.watchFile!(path, (f, eventKind) => { // TODO: GH#18217
|
||||
if (isLoggingEnabled) {
|
||||
this.log.writeLine(`FileWatcher:: Triggered with ${f} eventKind: ${FileWatcherEventKind[eventKind]}:: WatchInfo: ${path}:: handler is already invoked '${watchers.isInvoked}'`);
|
||||
}
|
||||
if (!watchers.isInvoked) {
|
||||
watchers.isInvoked = true;
|
||||
this.sendResponse({ projectName, kind: ActionInvalidate });
|
||||
}
|
||||
}, /*pollingInterval*/ 2000) :
|
||||
this.installTypingHost.watchDirectory!(path, f => { // TODO: GH#18217
|
||||
if (isLoggingEnabled) {
|
||||
this.log.writeLine(`DirectoryWatcher:: Triggered with ${f} :: WatchInfo: ${path} recursive :: handler is already invoked '${watchers.isInvoked}'`);
|
||||
}
|
||||
if (watchers.isInvoked || !fileExtensionIs(f, Extension.Json)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return isLoggingEnabled ? {
|
||||
if (isPackageOrBowerJson(f, this.installTypingHost.useCaseSensitiveFileNames) &&
|
||||
!sameFiles(f, this.globalCachePackageJsonPath, this.installTypingHost.useCaseSensitiveFileNames)) {
|
||||
watchers.isInvoked = true;
|
||||
this.sendResponse({ projectName, kind: ActionInvalidate });
|
||||
}
|
||||
}, /*recursive*/ true);
|
||||
|
||||
watchers.set(canonicalPath, isLoggingEnabled ? {
|
||||
close: () => {
|
||||
this.log.writeLine(`FileWatcher:: Closed:: WatchInfo: ${file}`);
|
||||
this.log.writeLine(`${projectWatcherType}:: Closed:: WatchInfo: ${path}`);
|
||||
watcher.close();
|
||||
}
|
||||
} : watcher;
|
||||
};
|
||||
const createProjectDirectoryWatcher = (dir: string): FileWatcher => {
|
||||
if (isLoggingEnabled) {
|
||||
this.log.writeLine(`DirectoryWatcher:: Added:: WatchInfo: ${dir} recursive`);
|
||||
}
|
||||
const watcher = this.installTypingHost.watchDirectory!(dir, f => { // TODO: GH#18217
|
||||
if (isLoggingEnabled) {
|
||||
this.log.writeLine(`DirectoryWatcher:: Triggered with ${f} :: WatchInfo: ${dir} recursive :: handler is already invoked '${watchers.isInvoked}'`);
|
||||
}
|
||||
if (watchers.isInvoked) {
|
||||
return;
|
||||
}
|
||||
f = this.toCanonicalFileName(f);
|
||||
if (f !== this.globalCacheCanonicalPackageJsonPath && isPackageOrBowerJson(f)) {
|
||||
watchers.isInvoked = true;
|
||||
this.sendResponse({ projectName, kind: ActionInvalidate });
|
||||
}
|
||||
}, /*recursive*/ true);
|
||||
|
||||
return isLoggingEnabled ? {
|
||||
close: () => {
|
||||
this.log.writeLine(`DirectoryWatcher:: Closed:: WatchInfo: ${dir} recursive`);
|
||||
watcher.close();
|
||||
}
|
||||
} : watcher;
|
||||
} : watcher);
|
||||
};
|
||||
|
||||
// Create watches from list of files
|
||||
for (const file of files) {
|
||||
const filePath = this.toCanonicalFileName(file);
|
||||
if (isPackageOrBowerJson(filePath)) {
|
||||
if (file.endsWith("/package.json") || file.endsWith("/bower.json")) {
|
||||
// package.json or bower.json exists, watch the file to detect changes and update typings
|
||||
createProjectWatcher(filePath, createProjectFileWatcher);
|
||||
createProjectWatcher(file, ProjectWatcherType.FileWatcher);
|
||||
continue;
|
||||
}
|
||||
|
||||
// path in projectRoot, watch project root
|
||||
if (containsPath(projectRootPath, filePath, projectRootPath, !this.installTypingHost.useCaseSensitiveFileNames)) {
|
||||
createProjectWatcher(projectRootPath, createProjectDirectoryWatcher);
|
||||
if (containsPath(projectRootPath, file, projectRootPath, !this.installTypingHost.useCaseSensitiveFileNames)) {
|
||||
const subDirectory = file.indexOf(directorySeparator, projectRootPath.length + 1);
|
||||
if (subDirectory !== -1) {
|
||||
// Watch subDirectory
|
||||
createProjectWatcher(file.substr(0, subDirectory), ProjectWatcherType.DirectoryWatcher);
|
||||
}
|
||||
else {
|
||||
// Watch the directory itself
|
||||
createProjectWatcher(file, ProjectWatcherType.DirectoryWatcher);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// path in global cache, watch global cache
|
||||
if (containsPath(this.globalCachePath, filePath, projectRootPath, !this.installTypingHost.useCaseSensitiveFileNames)) {
|
||||
createProjectWatcher(this.globalCachePath, createProjectDirectoryWatcher);
|
||||
if (containsPath(this.globalCachePath, file, projectRootPath, !this.installTypingHost.useCaseSensitiveFileNames)) {
|
||||
createProjectWatcher(this.globalCachePath, ProjectWatcherType.DirectoryWatcher);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get path without node_modules and bower_components
|
||||
createProjectWatcher(getDirectoryExcludingNodeModulesOrBowerComponents(getDirectoryPath(filePath)), createProjectDirectoryWatcher);
|
||||
// watch node_modules or bower_components
|
||||
createProjectWatcher(file, ProjectWatcherType.DirectoryWatcher);
|
||||
}
|
||||
|
||||
// Remove unused watches
|
||||
|
||||
Reference in New Issue
Block a user