mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into report-multiple-overload-errors
This commit is contained in:
@@ -274,6 +274,9 @@ namespace ts {
|
||||
if (isStringOrNumericLiteralLike(nameExpression)) {
|
||||
return escapeLeadingUnderscores(nameExpression.text);
|
||||
}
|
||||
if (isSignedNumericLiteral(nameExpression)) {
|
||||
return tokenToString(nameExpression.operator) + nameExpression.operand.text as __String;
|
||||
}
|
||||
|
||||
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
|
||||
return getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>nameExpression).name));
|
||||
|
||||
+84
-65
@@ -65,6 +65,7 @@ namespace ts {
|
||||
let typeCount = 0;
|
||||
let symbolCount = 0;
|
||||
let enumCount = 0;
|
||||
let instantiationCount = 0;
|
||||
let instantiationDepth = 0;
|
||||
let constraintDepth = 0;
|
||||
let currentNode: Node | undefined;
|
||||
@@ -722,6 +723,7 @@ namespace ts {
|
||||
NoIndexSignatures = 1 << 0,
|
||||
Writing = 1 << 1,
|
||||
CacheSymbol = 1 << 2,
|
||||
NoTupleBoundsCheck = 1 << 3,
|
||||
}
|
||||
|
||||
const enum CallbackCheck {
|
||||
@@ -4652,6 +4654,9 @@ namespace ts {
|
||||
if (!isIdentifierText(name, compilerOptions.target) && !isNumericLiteralName(name)) {
|
||||
return `"${escapeString(name, CharacterCodes.doubleQuote)}"`;
|
||||
}
|
||||
if (isNumericLiteralName(name) && startsWith(name, "-")) {
|
||||
return `[${name}]`;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
if (nameType.flags & TypeFlags.UniqueESSymbol) {
|
||||
@@ -5115,21 +5120,25 @@ namespace ts {
|
||||
}
|
||||
else if (isArrayLikeType(parentType)) {
|
||||
const indexType = getLiteralType(index);
|
||||
const declaredType = getConstraintForLocation(getIndexedAccessType(parentType, indexType, declaration.name), declaration.name);
|
||||
const accessFlags = hasDefaultValue(declaration) ? AccessFlags.NoTupleBoundsCheck : 0;
|
||||
const declaredType = getConstraintForLocation(getIndexedAccessTypeOrUndefined(parentType, indexType, declaration.name, accessFlags) || errorType, declaration.name);
|
||||
type = getFlowTypeOfDestructuring(declaration, declaredType);
|
||||
}
|
||||
else {
|
||||
type = elementType;
|
||||
}
|
||||
}
|
||||
// In strict null checking mode, if a default value of a non-undefined type is specified, remove
|
||||
// undefined from the final type.
|
||||
if (strictNullChecks && declaration.initializer && !(getFalsyFlags(checkDeclarationInitializer(declaration)) & TypeFlags.Undefined)) {
|
||||
type = getTypeWithFacts(type, TypeFacts.NEUndefined);
|
||||
if (!declaration.initializer) {
|
||||
return type;
|
||||
}
|
||||
return declaration.initializer && !getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration)) ?
|
||||
getUnionType([type, checkDeclarationInitializer(declaration)], UnionReduction.Subtype) :
|
||||
type;
|
||||
if (getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration))) {
|
||||
// In strict null checking mode, if a default value of a non-undefined type is specified, remove
|
||||
// undefined from the final type.
|
||||
return strictNullChecks && !(getFalsyFlags(checkDeclarationInitializer(declaration)) & TypeFlags.Undefined) ?
|
||||
getTypeWithFacts(type, TypeFacts.NEUndefined) :
|
||||
type;
|
||||
}
|
||||
return getUnionType([getTypeWithFacts(type, TypeFacts.NEUndefined), checkDeclarationInitializer(declaration)], UnionReduction.Subtype);
|
||||
}
|
||||
|
||||
function getTypeForDeclarationFromJSDocComment(declaration: Node) {
|
||||
@@ -10140,7 +10149,7 @@ namespace ts {
|
||||
propType;
|
||||
}
|
||||
if (everyType(objectType, isTupleType) && isNumericLiteralName(propName) && +propName >= 0) {
|
||||
if (accessNode && everyType(objectType, t => !(<TupleTypeReference>t).target.hasRestElement)) {
|
||||
if (accessNode && everyType(objectType, t => !(<TupleTypeReference>t).target.hasRestElement) && !(accessFlags & AccessFlags.NoTupleBoundsCheck)) {
|
||||
const indexNode = getIndexNodeForAccessExpression(accessNode);
|
||||
if (isTupleType(objectType)) {
|
||||
error(indexNode, Diagnostics.Tuple_type_0_of_length_1_has_no_element_at_index_2,
|
||||
@@ -11422,13 +11431,14 @@ namespace ts {
|
||||
if (!type || !mapper || mapper === identityMapper) {
|
||||
return type;
|
||||
}
|
||||
if (instantiationDepth === 50) {
|
||||
if (instantiationDepth === 50 || instantiationCount >= 5000000) {
|
||||
// We have reached 50 recursive type instantiations and there is a very high likelyhood we're dealing
|
||||
// with a combination of infinite generic types that perpetually generate new type identities. We stop
|
||||
// the recursion here by yielding the error type.
|
||||
error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite);
|
||||
return errorType;
|
||||
}
|
||||
instantiationCount++;
|
||||
instantiationDepth++;
|
||||
const result = instantiateTypeWorker(type, mapper);
|
||||
instantiationDepth--;
|
||||
@@ -13287,14 +13297,14 @@ namespace ts {
|
||||
if (!isGenericMappedType(source)) {
|
||||
const targetConstraint = getConstraintTypeFromMappedType(target);
|
||||
const sourceKeys = getIndexType(source, /*stringsOnly*/ undefined, /*noIndexSignatures*/ true);
|
||||
const hasOptionalUnionKeys = modifiers & MappedTypeModifiers.IncludeOptional && targetConstraint.flags & TypeFlags.Union;
|
||||
const filteredByApplicability = hasOptionalUnionKeys ? filterType(targetConstraint, t => !!isRelatedTo(t, sourceKeys)) : undefined;
|
||||
const includeOptional = modifiers & MappedTypeModifiers.IncludeOptional;
|
||||
const filteredByApplicability = includeOptional ? intersectTypes(targetConstraint, sourceKeys) : undefined;
|
||||
// A source type T is related to a target type { [P in Q]: X } if Q is related to keyof T and T[Q] is related to X.
|
||||
// A source type T is related to a target type { [P in Q]?: X } if some constituent Q' of Q is related to keyof T and T[Q'] is related to X.
|
||||
if (hasOptionalUnionKeys
|
||||
if (includeOptional
|
||||
? !(filteredByApplicability!.flags & TypeFlags.Never)
|
||||
: isRelatedTo(targetConstraint, sourceKeys)) {
|
||||
const indexingType = hasOptionalUnionKeys ? filteredByApplicability! : getTypeParameterFromMappedType(target);
|
||||
const indexingType = filteredByApplicability || getTypeParameterFromMappedType(target);
|
||||
const indexedAccessType = getIndexedAccessType(source, indexingType);
|
||||
const templateType = getTemplateTypeFromMappedType(target);
|
||||
if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
|
||||
@@ -15429,36 +15439,11 @@ namespace ts {
|
||||
inferFromTypes(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target));
|
||||
}
|
||||
else if (target.flags & TypeFlags.Conditional && !contravariant) {
|
||||
inferFromTypes(source, getTrueTypeFromConditionalType(<ConditionalType>target));
|
||||
inferFromTypes(source, getFalseTypeFromConditionalType(<ConditionalType>target));
|
||||
const targetTypes = [getTrueTypeFromConditionalType(<ConditionalType>target), getFalseTypeFromConditionalType(<ConditionalType>target)];
|
||||
inferToMultipleTypes(source, targetTypes, /*isIntersection*/ false);
|
||||
}
|
||||
else if (target.flags & TypeFlags.UnionOrIntersection) {
|
||||
// We infer from types that are not naked type variables first so that inferences we
|
||||
// make from nested naked type variables and given slightly higher priority by virtue
|
||||
// of being first in the candidates array.
|
||||
let typeVariableCount = 0;
|
||||
for (const t of (<UnionOrIntersectionType>target).types) {
|
||||
if (getInferenceInfoForType(t)) {
|
||||
typeVariableCount++;
|
||||
}
|
||||
else {
|
||||
inferFromTypes(source, t);
|
||||
}
|
||||
}
|
||||
// Inferences directly to naked type variables are given lower priority as they are
|
||||
// less specific. For example, when inferring from Promise<string> to T | Promise<T>,
|
||||
// we want to infer string for T, not Promise<string> | string. For intersection types
|
||||
// we only infer to single naked type variables.
|
||||
if (target.flags & TypeFlags.Union ? typeVariableCount !== 0 : typeVariableCount === 1) {
|
||||
const savePriority = priority;
|
||||
priority |= InferencePriority.NakedTypeVariable;
|
||||
for (const t of (<UnionOrIntersectionType>target).types) {
|
||||
if (getInferenceInfoForType(t)) {
|
||||
inferFromTypes(source, t);
|
||||
}
|
||||
}
|
||||
priority = savePriority;
|
||||
}
|
||||
inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, !!(target.flags & TypeFlags.Intersection));
|
||||
}
|
||||
else if (source.flags & TypeFlags.Union) {
|
||||
// Source is a union or intersection type, infer from each constituent type
|
||||
@@ -15556,6 +15541,35 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function inferToMultipleTypes(source: Type, targets: Type[], isIntersection: boolean) {
|
||||
// We infer from types that are not naked type variables first so that inferences we
|
||||
// make from nested naked type variables and given slightly higher priority by virtue
|
||||
// of being first in the candidates array.
|
||||
let typeVariableCount = 0;
|
||||
for (const t of targets) {
|
||||
if (getInferenceInfoForType(t)) {
|
||||
typeVariableCount++;
|
||||
}
|
||||
else {
|
||||
inferFromTypes(source, t);
|
||||
}
|
||||
}
|
||||
// Inferences directly to naked type variables are given lower priority as they are
|
||||
// less specific. For example, when inferring from Promise<string> to T | Promise<T>,
|
||||
// we want to infer string for T, not Promise<string> | string. For intersection types
|
||||
// we only infer to single naked type variables.
|
||||
if (isIntersection ? typeVariableCount === 1 : typeVariableCount !== 0) {
|
||||
const savePriority = priority;
|
||||
priority |= InferencePriority.NakedTypeVariable;
|
||||
for (const t of targets) {
|
||||
if (getInferenceInfoForType(t)) {
|
||||
inferFromTypes(source, t);
|
||||
}
|
||||
}
|
||||
priority = savePriority;
|
||||
}
|
||||
}
|
||||
|
||||
function inferToMappedType(source: Type, target: MappedType, constraintType: Type): boolean {
|
||||
if (constraintType.flags & TypeFlags.Union) {
|
||||
let result = false;
|
||||
@@ -19287,26 +19301,7 @@ namespace ts {
|
||||
function getArrayLiteralTupleTypeIfApplicable(elementTypes: Type[], contextualType: Type | undefined, hasRestElement: boolean, elementCount = elementTypes.length, readonly = false) {
|
||||
// Infer a tuple type when the contextual type is or contains a tuple-like type
|
||||
if (readonly || (contextualType && forEachType(contextualType, isTupleLikeType))) {
|
||||
const minLength = elementCount - (hasRestElement ? 1 : 0);
|
||||
const pattern = contextualType && contextualType.pattern;
|
||||
// If array literal is contextually typed by a binding pattern or an assignment pattern, pad the resulting
|
||||
// tuple type with the corresponding binding or assignment element types to make the lengths equal.
|
||||
if (!hasRestElement && pattern && (pattern.kind === SyntaxKind.ArrayBindingPattern || pattern.kind === SyntaxKind.ArrayLiteralExpression)) {
|
||||
const patternElements = (<BindingPattern | ArrayLiteralExpression>pattern).elements;
|
||||
for (let i = elementCount; i < patternElements.length; i++) {
|
||||
const e = patternElements[i];
|
||||
if (hasDefaultValue(e)) {
|
||||
elementTypes.push((<TypeReference>contextualType).typeArguments![i]);
|
||||
}
|
||||
else if (i < patternElements.length - 1 || !(e.kind === SyntaxKind.BindingElement && (<BindingElement>e).dotDotDotToken || e.kind === SyntaxKind.SpreadElement)) {
|
||||
if (e.kind !== SyntaxKind.OmittedExpression) {
|
||||
error(e, Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value);
|
||||
}
|
||||
elementTypes.push(strictNullChecks ? implicitNeverType : undefinedWideningType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return createTupleType(elementTypes, minLength, hasRestElement, readonly);
|
||||
return createTupleType(elementTypes, elementCount - (hasRestElement ? 1 : 0), hasRestElement, readonly);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23906,8 +23901,10 @@ namespace ts {
|
||||
if (isArrayLikeType(sourceType)) {
|
||||
// We create a synthetic expression so that getIndexedAccessType doesn't get confused
|
||||
// when the element is a SyntaxKind.ElementAccessExpression.
|
||||
const elementType = getIndexedAccessType(sourceType, indexType, createSyntheticExpression(element, indexType));
|
||||
const type = getFlowTypeOfDestructuring(element, elementType);
|
||||
const accessFlags = hasDefaultValue(element) ? AccessFlags.NoTupleBoundsCheck : 0;
|
||||
const elementType = getIndexedAccessTypeOrUndefined(sourceType, indexType, createSyntheticExpression(element, indexType), accessFlags) || errorType;
|
||||
const assignedType = hasDefaultValue(element) ? getTypeWithFacts(elementType, TypeFacts.NEUndefined) : elementType;
|
||||
const type = getFlowTypeOfDestructuring(element, assignedType);
|
||||
return checkDestructuringAssignment(element, type, checkMode);
|
||||
}
|
||||
return checkDestructuringAssignment(element, elementType, checkMode);
|
||||
@@ -24496,10 +24493,13 @@ namespace ts {
|
||||
function checkDeclarationInitializer(declaration: HasExpressionInitializer) {
|
||||
const initializer = getEffectiveInitializer(declaration)!;
|
||||
const type = getTypeOfExpression(initializer, /*cache*/ true);
|
||||
const padded = isParameter(declaration) && declaration.name.kind === SyntaxKind.ArrayBindingPattern &&
|
||||
isTupleType(type) && !type.target.hasRestElement && getTypeReferenceArity(type) < declaration.name.elements.length ?
|
||||
padTupleType(type, declaration.name) : type;
|
||||
const widened = getCombinedNodeFlags(declaration) & NodeFlags.Const ||
|
||||
isDeclarationReadonly(declaration) ||
|
||||
isTypeAssertion(initializer) ||
|
||||
isLiteralOfContextualType(type, getContextualType(initializer)) ? type : getWidenedLiteralType(type);
|
||||
isLiteralOfContextualType(padded, getContextualType(initializer)) ? padded : getWidenedLiteralType(padded);
|
||||
if (isInJSFile(declaration)) {
|
||||
if (widened.flags & TypeFlags.Nullable) {
|
||||
reportImplicitAny(declaration, anyType);
|
||||
@@ -24513,6 +24513,22 @@ namespace ts {
|
||||
return widened;
|
||||
}
|
||||
|
||||
function padTupleType(type: TupleTypeReference, pattern: ArrayBindingPattern) {
|
||||
const patternElements = pattern.elements;
|
||||
const arity = getTypeReferenceArity(type);
|
||||
const elementTypes = arity ? type.typeArguments!.slice() : [];
|
||||
for (let i = arity; i < patternElements.length; i++) {
|
||||
const e = patternElements[i];
|
||||
if (i < patternElements.length - 1 || !(e.kind === SyntaxKind.BindingElement && e.dotDotDotToken)) {
|
||||
elementTypes.push(!isOmittedExpression(e) && hasDefaultValue(e) ? getTypeFromBindingElement(e, /*includePatternInType*/ false, /*reportErrors*/ false) : anyType);
|
||||
if (!isOmittedExpression(e) && !hasDefaultValue(e)) {
|
||||
reportImplicitAny(e, anyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return createTupleType(elementTypes, type.target.minLength, /*hasRestElement*/ false, type.target.readonly);
|
||||
}
|
||||
|
||||
function isLiteralOfContextualType(candidateType: Type, contextualType: Type | undefined): boolean {
|
||||
if (contextualType) {
|
||||
if (contextualType.flags & TypeFlags.UnionOrIntersection) {
|
||||
@@ -24761,6 +24777,7 @@ namespace ts {
|
||||
function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type {
|
||||
const saveCurrentNode = currentNode;
|
||||
currentNode = node;
|
||||
instantiationCount = 0;
|
||||
const uninstantiatedType = checkExpressionWorker(node, checkMode, forceTuple);
|
||||
const type = instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode);
|
||||
if (isConstEnumObjectType(type)) {
|
||||
@@ -29482,6 +29499,7 @@ namespace ts {
|
||||
if (node) {
|
||||
const saveCurrentNode = currentNode;
|
||||
currentNode = node;
|
||||
instantiationCount = 0;
|
||||
checkSourceElementWorker(node);
|
||||
currentNode = saveCurrentNode;
|
||||
}
|
||||
@@ -29753,6 +29771,7 @@ namespace ts {
|
||||
function checkDeferredNode(node: Node) {
|
||||
const saveCurrentNode = currentNode;
|
||||
currentNode = node;
|
||||
instantiationCount = 0;
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
|
||||
+15
-9
@@ -1544,8 +1544,8 @@ namespace ts {
|
||||
export function createIf(expression: Expression, thenStatement: Statement, elseStatement?: Statement) {
|
||||
const node = <IfStatement>createSynthesizedNode(SyntaxKind.IfStatement);
|
||||
node.expression = expression;
|
||||
node.thenStatement = thenStatement;
|
||||
node.elseStatement = elseStatement;
|
||||
node.thenStatement = asEmbeddedStatement(thenStatement);
|
||||
node.elseStatement = asEmbeddedStatement(elseStatement);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -1559,7 +1559,7 @@ namespace ts {
|
||||
|
||||
export function createDo(statement: Statement, expression: Expression) {
|
||||
const node = <DoStatement>createSynthesizedNode(SyntaxKind.DoStatement);
|
||||
node.statement = statement;
|
||||
node.statement = asEmbeddedStatement(statement);
|
||||
node.expression = expression;
|
||||
return node;
|
||||
}
|
||||
@@ -1574,7 +1574,7 @@ namespace ts {
|
||||
export function createWhile(expression: Expression, statement: Statement) {
|
||||
const node = <WhileStatement>createSynthesizedNode(SyntaxKind.WhileStatement);
|
||||
node.expression = expression;
|
||||
node.statement = statement;
|
||||
node.statement = asEmbeddedStatement(statement);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -1590,7 +1590,7 @@ namespace ts {
|
||||
node.initializer = initializer;
|
||||
node.condition = condition;
|
||||
node.incrementor = incrementor;
|
||||
node.statement = statement;
|
||||
node.statement = asEmbeddedStatement(statement);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -1607,7 +1607,7 @@ namespace ts {
|
||||
const node = <ForInStatement>createSynthesizedNode(SyntaxKind.ForInStatement);
|
||||
node.initializer = initializer;
|
||||
node.expression = expression;
|
||||
node.statement = statement;
|
||||
node.statement = asEmbeddedStatement(statement);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -1624,7 +1624,7 @@ namespace ts {
|
||||
node.awaitModifier = awaitModifier;
|
||||
node.initializer = initializer;
|
||||
node.expression = expression;
|
||||
node.statement = statement;
|
||||
node.statement = asEmbeddedStatement(statement);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -1676,7 +1676,7 @@ namespace ts {
|
||||
export function createWith(expression: Expression, statement: Statement) {
|
||||
const node = <WithStatement>createSynthesizedNode(SyntaxKind.WithStatement);
|
||||
node.expression = expression;
|
||||
node.statement = statement;
|
||||
node.statement = asEmbeddedStatement(statement);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -1704,7 +1704,7 @@ namespace ts {
|
||||
export function createLabel(label: string | Identifier, statement: Statement) {
|
||||
const node = <LabeledStatement>createSynthesizedNode(SyntaxKind.LabeledStatement);
|
||||
node.label = asName(label);
|
||||
node.statement = statement;
|
||||
node.statement = asEmbeddedStatement(statement);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -3080,6 +3080,12 @@ namespace ts {
|
||||
return typeof value === "number" ? createToken(value) : value;
|
||||
}
|
||||
|
||||
function asEmbeddedStatement<T extends Node>(statement: T): T | EmptyStatement;
|
||||
function asEmbeddedStatement<T extends Node>(statement: T | undefined): T | EmptyStatement | undefined;
|
||||
function asEmbeddedStatement<T extends Node>(statement: T | undefined): T | EmptyStatement | undefined {
|
||||
return statement && isNotEmittedStatement(statement) ? setTextRange(setOriginalNode(createEmptyStatement(), statement), statement) : statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any EmitNode entries from parse-tree nodes.
|
||||
* @param sourceFile A source file.
|
||||
|
||||
@@ -1395,7 +1395,7 @@ namespace ts {
|
||||
const filePath = newSourceFile.path;
|
||||
addFileToFilesByName(newSourceFile, filePath, newSourceFile.resolvedPath);
|
||||
// Set the file as found during node modules search if it was found that way in old progra,
|
||||
if (oldProgram.isSourceFileFromExternalLibrary(oldProgram.getSourceFileByPath(filePath)!)) {
|
||||
if (oldProgram.isSourceFileFromExternalLibrary(oldProgram.getSourceFileByPath(newSourceFile.resolvedPath)!)) {
|
||||
sourceFilesFoundSearchingNodeModules.set(filePath, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2705,11 +2705,19 @@ namespace ts {
|
||||
return isStringLiteralLike(node) || isNumericLiteral(node);
|
||||
}
|
||||
|
||||
export function isSignedNumericLiteral(node: Node): node is PrefixUnaryExpression & { operand: NumericLiteral } {
|
||||
return isPrefixUnaryExpression(node) && (node.operator === SyntaxKind.PlusToken || node.operator === SyntaxKind.MinusToken) && isNumericLiteral(node.operand);
|
||||
}
|
||||
|
||||
/**
|
||||
* A declaration has a dynamic name if both of the following are true:
|
||||
* 1. The declaration has a computed property name
|
||||
* 2. The computed name is *not* expressed as Symbol.<name>, where name
|
||||
* is a property of the Symbol constructor that denotes a built in
|
||||
* A declaration has a dynamic name if all of the following are true:
|
||||
* 1. The declaration has a computed property name.
|
||||
* 2. The computed name is *not* expressed as a StringLiteral.
|
||||
* 3. The computed name is *not* expressed as a NumericLiteral.
|
||||
* 4. The computed name is *not* expressed as a PlusToken or MinusToken
|
||||
* immediately followed by a NumericLiteral.
|
||||
* 5. The computed name is *not* expressed as `Symbol.<name>`, where `<name>`
|
||||
* is a property of the Symbol constructor that denotes a built-in
|
||||
* Symbol.
|
||||
*/
|
||||
export function hasDynamicName(declaration: Declaration): declaration is DynamicNamedDeclaration {
|
||||
@@ -2720,6 +2728,7 @@ namespace ts {
|
||||
export function isDynamicName(name: DeclarationName): boolean {
|
||||
return name.kind === SyntaxKind.ComputedPropertyName &&
|
||||
!isStringOrNumericLiteralLike(name.expression) &&
|
||||
!isSignedNumericLiteral(name.expression) &&
|
||||
!isWellKnownSymbolSyntactically(name.expression);
|
||||
}
|
||||
|
||||
|
||||
@@ -501,7 +501,7 @@ namespace Harness {
|
||||
}
|
||||
|
||||
function enumerateTestFiles(runner: RunnerBase) {
|
||||
return runner.enumerateTestFiles();
|
||||
return runner.getTestFiles();
|
||||
}
|
||||
|
||||
function listFiles(path: string, spec: RegExp, options: { recursive?: boolean } = {}) {
|
||||
@@ -1311,6 +1311,13 @@ namespace Harness {
|
||||
if (jsCode.length && jsCode.charCodeAt(jsCode.length - 1) !== ts.CharacterCodes.lineFeed) {
|
||||
jsCode += "\r\n";
|
||||
}
|
||||
if (!result.diagnostics.length && !ts.endsWith(file.file, ts.Extension.Json)) {
|
||||
const fileParseResult = ts.createSourceFile(file.file, file.text, options.target || ts.ScriptTarget.ES3, /*parentNodes*/ false, ts.endsWith(file.file, "x") ? ts.ScriptKind.JSX : ts.ScriptKind.JS);
|
||||
if (ts.length(fileParseResult.parseDiagnostics)) {
|
||||
jsCode += getErrorBaseline([file.asTestFile()], fileParseResult.parseDiagnostics);
|
||||
return;
|
||||
}
|
||||
}
|
||||
jsCode += fileOutput(file, harnessSettings);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ type TestRunnerKind = CompilerTestKind | FourslashTestKind | "project" | "rwc" |
|
||||
type CompilerTestKind = "conformance" | "compiler";
|
||||
type FourslashTestKind = "fourslash" | "fourslash-shims" | "fourslash-shims-pp" | "fourslash-server";
|
||||
|
||||
let shards = 1;
|
||||
let shardId = 1;
|
||||
|
||||
abstract class RunnerBase {
|
||||
// contains the tests to run
|
||||
public tests: (string | Harness.FileBasedTest)[] = [];
|
||||
@@ -19,6 +22,14 @@ abstract class RunnerBase {
|
||||
|
||||
abstract enumerateTestFiles(): (string | Harness.FileBasedTest)[];
|
||||
|
||||
getTestFiles(): ReturnType<this["enumerateTestFiles"]> {
|
||||
const all = this.enumerateTestFiles();
|
||||
if (shards === 1) {
|
||||
return all as ReturnType<this["enumerateTestFiles"]>;
|
||||
}
|
||||
return all.filter((_val, idx) => idx % shards === (shardId - 1)) as ReturnType<this["enumerateTestFiles"]>;
|
||||
}
|
||||
|
||||
/** The working directory where tests are found. Needed for batch testing where the input path will differ from the output path inside baselines */
|
||||
public workingDirectory = "";
|
||||
|
||||
|
||||
@@ -2234,7 +2234,13 @@ namespace ts.server {
|
||||
getDocumentPositionMapper(project: Project, generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined {
|
||||
// Since declaration info and map file watches arent updating project's directory structure host (which can cache file structure) use host
|
||||
const declarationInfo = this.getOrCreateScriptInfoNotOpenedByClient(generatedFileName, project.currentDirectory, this.host);
|
||||
if (!declarationInfo) return undefined;
|
||||
if (!declarationInfo) {
|
||||
if (sourceFileName) {
|
||||
// Project contains source file and it generates the generated file name
|
||||
project.addGeneratedFileWatch(generatedFileName, sourceFileName);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Try to get from cache
|
||||
declarationInfo.getSnapshot(); // Ensure synchronized
|
||||
|
||||
@@ -109,12 +109,22 @@ namespace ts.server {
|
||||
return value instanceof ScriptInfo;
|
||||
}
|
||||
|
||||
interface GeneratedFileWatcher {
|
||||
generatedFilePath: Path;
|
||||
watcher: FileWatcher;
|
||||
}
|
||||
type GeneratedFileWatcherMap = GeneratedFileWatcher | Map<GeneratedFileWatcher>;
|
||||
function isGeneratedFileWatcher(watch: GeneratedFileWatcherMap): watch is GeneratedFileWatcher {
|
||||
return (watch as GeneratedFileWatcher).generatedFilePath !== undefined;
|
||||
}
|
||||
|
||||
export abstract class Project implements LanguageServiceHost, ModuleResolutionHost {
|
||||
private rootFiles: ScriptInfo[] = [];
|
||||
private rootFilesMap: Map<ProjectRoot> = createMap<ProjectRoot>();
|
||||
private program: Program | undefined;
|
||||
private externalFiles: SortedReadonlyArray<string> | undefined;
|
||||
private missingFilesMap: Map<FileWatcher> | undefined;
|
||||
private generatedFilesMap: GeneratedFileWatcherMap | undefined;
|
||||
private plugins: PluginModuleWithName[] = [];
|
||||
|
||||
/*@internal*/
|
||||
@@ -568,6 +578,7 @@ namespace ts.server {
|
||||
this.lastFileExceededProgramSize = lastFileExceededProgramSize;
|
||||
this.builderState = undefined;
|
||||
this.resolutionCache.closeTypeRootsWatch();
|
||||
this.clearGeneratedFileWatch();
|
||||
this.projectService.onUpdateLanguageServiceStateForProject(this, /*languageServiceEnabled*/ false);
|
||||
}
|
||||
|
||||
@@ -649,6 +660,7 @@ namespace ts.server {
|
||||
clearMap(this.missingFilesMap, closeFileWatcher);
|
||||
this.missingFilesMap = undefined!;
|
||||
}
|
||||
this.clearGeneratedFileWatch();
|
||||
|
||||
// signal language service to release source files acquired from document registry
|
||||
this.languageService.dispose();
|
||||
@@ -942,6 +954,39 @@ namespace ts.server {
|
||||
missingFilePath => this.addMissingFileWatcher(missingFilePath)
|
||||
);
|
||||
|
||||
if (this.generatedFilesMap) {
|
||||
const outPath = this.compilerOptions.outFile && this.compilerOptions.out;
|
||||
if (isGeneratedFileWatcher(this.generatedFilesMap)) {
|
||||
// --out
|
||||
if (!outPath || !this.isValidGeneratedFileWatcher(
|
||||
removeFileExtension(outPath) + Extension.Dts,
|
||||
this.generatedFilesMap,
|
||||
)) {
|
||||
this.clearGeneratedFileWatch();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// MultiFile
|
||||
if (outPath) {
|
||||
this.clearGeneratedFileWatch();
|
||||
}
|
||||
else {
|
||||
this.generatedFilesMap.forEach((watcher, source) => {
|
||||
const sourceFile = this.program!.getSourceFileByPath(source as Path);
|
||||
if (!sourceFile ||
|
||||
sourceFile.resolvedPath !== source ||
|
||||
!this.isValidGeneratedFileWatcher(
|
||||
getDeclarationEmitOutputFilePathWorker(sourceFile.fileName, this.compilerOptions, this.currentDirectory, this.program!.getCommonSourceDirectory(), this.getCanonicalFileName),
|
||||
watcher
|
||||
)) {
|
||||
closeFileWatcherOf(watcher);
|
||||
(this.generatedFilesMap as Map<GeneratedFileWatcher>).delete(source);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Watch the type locations that would be added to program as part of automatic type resolutions
|
||||
if (this.languageServiceEnabled) {
|
||||
this.resolutionCache.updateTypeRootsWatch();
|
||||
@@ -1006,6 +1051,61 @@ namespace ts.server {
|
||||
return !!this.missingFilesMap && this.missingFilesMap.has(path);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
addGeneratedFileWatch(generatedFile: string, sourceFile: string) {
|
||||
if (this.compilerOptions.outFile || this.compilerOptions.out) {
|
||||
// Single watcher
|
||||
if (!this.generatedFilesMap) {
|
||||
this.generatedFilesMap = this.createGeneratedFileWatcher(generatedFile);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Map
|
||||
const path = this.toPath(sourceFile);
|
||||
if (this.generatedFilesMap) {
|
||||
if (isGeneratedFileWatcher(this.generatedFilesMap)) {
|
||||
Debug.fail(`${this.projectName} Expected to not have --out watcher for generated file with options: ${JSON.stringify(this.compilerOptions)}`);
|
||||
return;
|
||||
}
|
||||
if (this.generatedFilesMap.has(path)) return;
|
||||
}
|
||||
else {
|
||||
this.generatedFilesMap = createMap();
|
||||
}
|
||||
this.generatedFilesMap.set(path, this.createGeneratedFileWatcher(generatedFile));
|
||||
}
|
||||
}
|
||||
|
||||
private createGeneratedFileWatcher(generatedFile: string): GeneratedFileWatcher {
|
||||
return {
|
||||
generatedFilePath: this.toPath(generatedFile),
|
||||
watcher: this.projectService.watchFactory.watchFile(
|
||||
this.projectService.host,
|
||||
generatedFile,
|
||||
() => this.projectService.delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(this),
|
||||
PollingInterval.High,
|
||||
WatchType.MissingGeneratedFile,
|
||||
this
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
private isValidGeneratedFileWatcher(generateFile: string, watcher: GeneratedFileWatcher) {
|
||||
return this.toPath(generateFile) === watcher.generatedFilePath;
|
||||
}
|
||||
|
||||
private clearGeneratedFileWatch() {
|
||||
if (this.generatedFilesMap) {
|
||||
if (isGeneratedFileWatcher(this.generatedFilesMap)) {
|
||||
closeFileWatcherOf(this.generatedFilesMap);
|
||||
}
|
||||
else {
|
||||
clearMap(this.generatedFilesMap, closeFileWatcherOf);
|
||||
}
|
||||
this.generatedFilesMap = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
getScriptInfoForNormalizedPath(fileName: NormalizedPath): ScriptInfo | undefined {
|
||||
const scriptInfo = this.projectService.getScriptInfoForPath(this.toPath(fileName));
|
||||
if (scriptInfo && !scriptInfo.isAttached(this)) {
|
||||
|
||||
@@ -227,5 +227,6 @@ namespace ts {
|
||||
NodeModulesForClosedScriptInfo = "node_modules for closed script infos in them",
|
||||
MissingSourceMapFile = "Missing source map file",
|
||||
NoopConfigFileForInferredRoot = "Noop Config file for the inferred project root",
|
||||
MissingGeneratedFile = "Missing generated file"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,9 +72,13 @@ namespace ts.SmartSelectionRange {
|
||||
function pushSelectionRange(start: number, end: number): void {
|
||||
// Skip empty ranges
|
||||
if (start !== end) {
|
||||
// Skip ranges that are identical to the parent
|
||||
const textSpan = createTextSpanFromBounds(start, end);
|
||||
if (!selectionRange || !textSpansEqual(textSpan, selectionRange.textSpan)) {
|
||||
if (!selectionRange || (
|
||||
// Skip ranges that are identical to the parent
|
||||
!textSpansEqual(textSpan, selectionRange.textSpan) &&
|
||||
// Skip ranges that don’t contain the original position
|
||||
textSpanIntersectsWithPosition(textSpan, pos)
|
||||
)) {
|
||||
selectionRange = { textSpan, ...selectionRange && { parent: selectionRange } };
|
||||
}
|
||||
}
|
||||
|
||||
+130
-104
@@ -847,9 +847,10 @@ namespace ts.textChanges {
|
||||
|
||||
/** Note: output node may be mutated input node. */
|
||||
export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLineCharacter: string): { text: string, node: Node } {
|
||||
const writer = new Writer(newLineCharacter);
|
||||
const omitTrailingSemicolon = !!sourceFile && !probablyUsesSemicolons(sourceFile);
|
||||
const writer = createWriter(newLineCharacter, omitTrailingSemicolon);
|
||||
const newLine = newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed;
|
||||
createPrinter({ newLine, neverAsciiEscape: true }, writer).writeNode(EmitHint.Unspecified, node, sourceFile, writer);
|
||||
createPrinter({ newLine, neverAsciiEscape: true, omitTrailingSemicolon }, writer).writeNode(EmitHint.Unspecified, node, sourceFile, writer);
|
||||
return { text: writer.getText(), node: assignPositionsToNode(node) };
|
||||
}
|
||||
}
|
||||
@@ -887,143 +888,168 @@ namespace ts.textChanges {
|
||||
return nodeArray;
|
||||
}
|
||||
|
||||
class Writer implements EmitTextWriter, PrintHandlers {
|
||||
private lastNonTriviaPosition = 0;
|
||||
private readonly writer: EmitTextWriter;
|
||||
interface TextChangesWriter extends EmitTextWriter, PrintHandlers {}
|
||||
|
||||
public readonly onEmitNode: PrintHandlers["onEmitNode"];
|
||||
public readonly onBeforeEmitNodeArray: PrintHandlers["onBeforeEmitNodeArray"];
|
||||
public readonly onAfterEmitNodeArray: PrintHandlers["onAfterEmitNodeArray"];
|
||||
public readonly onBeforeEmitToken: PrintHandlers["onBeforeEmitToken"];
|
||||
public readonly onAfterEmitToken: PrintHandlers["onAfterEmitToken"];
|
||||
function createWriter(newLine: string, omitTrailingSemicolon?: boolean): TextChangesWriter {
|
||||
let lastNonTriviaPosition = 0;
|
||||
|
||||
constructor(newLine: string) {
|
||||
this.writer = createTextWriter(newLine);
|
||||
this.onEmitNode = (hint, node, printCallback) => {
|
||||
if (node) {
|
||||
setPos(node, this.lastNonTriviaPosition);
|
||||
}
|
||||
printCallback(hint, node);
|
||||
if (node) {
|
||||
setEnd(node, this.lastNonTriviaPosition);
|
||||
}
|
||||
};
|
||||
this.onBeforeEmitNodeArray = nodes => {
|
||||
if (nodes) {
|
||||
setPos(nodes, this.lastNonTriviaPosition);
|
||||
}
|
||||
};
|
||||
this.onAfterEmitNodeArray = nodes => {
|
||||
if (nodes) {
|
||||
setEnd(nodes, this.lastNonTriviaPosition);
|
||||
}
|
||||
};
|
||||
this.onBeforeEmitToken = node => {
|
||||
if (node) {
|
||||
setPos(node, this.lastNonTriviaPosition);
|
||||
}
|
||||
};
|
||||
this.onAfterEmitToken = node => {
|
||||
if (node) {
|
||||
setEnd(node, this.lastNonTriviaPosition);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private setLastNonTriviaPosition(s: string, force: boolean) {
|
||||
const writer = omitTrailingSemicolon ? getTrailingSemicolonOmittingWriter(createTextWriter(newLine)) : createTextWriter(newLine);
|
||||
const onEmitNode: PrintHandlers["onEmitNode"] = (hint, node, printCallback) => {
|
||||
if (node) {
|
||||
setPos(node, lastNonTriviaPosition);
|
||||
}
|
||||
printCallback(hint, node);
|
||||
if (node) {
|
||||
setEnd(node, lastNonTriviaPosition);
|
||||
}
|
||||
};
|
||||
const onBeforeEmitNodeArray: PrintHandlers["onBeforeEmitNodeArray"] = nodes => {
|
||||
if (nodes) {
|
||||
setPos(nodes, lastNonTriviaPosition);
|
||||
}
|
||||
};
|
||||
const onAfterEmitNodeArray: PrintHandlers["onAfterEmitNodeArray"] = nodes => {
|
||||
if (nodes) {
|
||||
setEnd(nodes, lastNonTriviaPosition);
|
||||
}
|
||||
};
|
||||
const onBeforeEmitToken: PrintHandlers["onBeforeEmitToken"] = node => {
|
||||
if (node) {
|
||||
setPos(node, lastNonTriviaPosition);
|
||||
}
|
||||
};
|
||||
const onAfterEmitToken: PrintHandlers["onAfterEmitToken"] = node => {
|
||||
if (node) {
|
||||
setEnd(node, lastNonTriviaPosition);
|
||||
}
|
||||
};
|
||||
|
||||
function setLastNonTriviaPosition(s: string, force: boolean) {
|
||||
if (force || !isTrivia(s)) {
|
||||
this.lastNonTriviaPosition = this.writer.getTextPos();
|
||||
lastNonTriviaPosition = writer.getTextPos();
|
||||
let i = 0;
|
||||
while (isWhiteSpaceLike(s.charCodeAt(s.length - i - 1))) {
|
||||
i++;
|
||||
}
|
||||
// trim trailing whitespaces
|
||||
this.lastNonTriviaPosition -= i;
|
||||
lastNonTriviaPosition -= i;
|
||||
}
|
||||
}
|
||||
|
||||
write(s: string): void {
|
||||
this.writer.write(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ false);
|
||||
function write(s: string): void {
|
||||
writer.write(s);
|
||||
setLastNonTriviaPosition(s, /*force*/ false);
|
||||
}
|
||||
writeComment(s: string): void {
|
||||
this.writer.writeComment(s);
|
||||
function writeComment(s: string): void {
|
||||
writer.writeComment(s);
|
||||
}
|
||||
writeKeyword(s: string): void {
|
||||
this.writer.writeKeyword(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ false);
|
||||
function writeKeyword(s: string): void {
|
||||
writer.writeKeyword(s);
|
||||
setLastNonTriviaPosition(s, /*force*/ false);
|
||||
}
|
||||
writeOperator(s: string): void {
|
||||
this.writer.writeOperator(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ false);
|
||||
function writeOperator(s: string): void {
|
||||
writer.writeOperator(s);
|
||||
setLastNonTriviaPosition(s, /*force*/ false);
|
||||
}
|
||||
writePunctuation(s: string): void {
|
||||
this.writer.writePunctuation(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ false);
|
||||
function writePunctuation(s: string): void {
|
||||
writer.writePunctuation(s);
|
||||
setLastNonTriviaPosition(s, /*force*/ false);
|
||||
}
|
||||
writeTrailingSemicolon(s: string): void {
|
||||
this.writer.writeTrailingSemicolon(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ false);
|
||||
function writeTrailingSemicolon(s: string): void {
|
||||
writer.writeTrailingSemicolon(s);
|
||||
setLastNonTriviaPosition(s, /*force*/ false);
|
||||
}
|
||||
writeParameter(s: string): void {
|
||||
this.writer.writeParameter(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ false);
|
||||
function writeParameter(s: string): void {
|
||||
writer.writeParameter(s);
|
||||
setLastNonTriviaPosition(s, /*force*/ false);
|
||||
}
|
||||
writeProperty(s: string): void {
|
||||
this.writer.writeProperty(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ false);
|
||||
function writeProperty(s: string): void {
|
||||
writer.writeProperty(s);
|
||||
setLastNonTriviaPosition(s, /*force*/ false);
|
||||
}
|
||||
writeSpace(s: string): void {
|
||||
this.writer.writeSpace(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ false);
|
||||
function writeSpace(s: string): void {
|
||||
writer.writeSpace(s);
|
||||
setLastNonTriviaPosition(s, /*force*/ false);
|
||||
}
|
||||
writeStringLiteral(s: string): void {
|
||||
this.writer.writeStringLiteral(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ false);
|
||||
function writeStringLiteral(s: string): void {
|
||||
writer.writeStringLiteral(s);
|
||||
setLastNonTriviaPosition(s, /*force*/ false);
|
||||
}
|
||||
writeSymbol(s: string, sym: Symbol): void {
|
||||
this.writer.writeSymbol(s, sym);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ false);
|
||||
function writeSymbol(s: string, sym: Symbol): void {
|
||||
writer.writeSymbol(s, sym);
|
||||
setLastNonTriviaPosition(s, /*force*/ false);
|
||||
}
|
||||
writeLine(): void {
|
||||
this.writer.writeLine();
|
||||
function writeLine(): void {
|
||||
writer.writeLine();
|
||||
}
|
||||
increaseIndent(): void {
|
||||
this.writer.increaseIndent();
|
||||
function increaseIndent(): void {
|
||||
writer.increaseIndent();
|
||||
}
|
||||
decreaseIndent(): void {
|
||||
this.writer.decreaseIndent();
|
||||
function decreaseIndent(): void {
|
||||
writer.decreaseIndent();
|
||||
}
|
||||
getText(): string {
|
||||
return this.writer.getText();
|
||||
function getText(): string {
|
||||
return writer.getText();
|
||||
}
|
||||
rawWrite(s: string): void {
|
||||
this.writer.rawWrite(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ false);
|
||||
function rawWrite(s: string): void {
|
||||
writer.rawWrite(s);
|
||||
setLastNonTriviaPosition(s, /*force*/ false);
|
||||
}
|
||||
writeLiteral(s: string): void {
|
||||
this.writer.writeLiteral(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ true);
|
||||
function writeLiteral(s: string): void {
|
||||
writer.writeLiteral(s);
|
||||
setLastNonTriviaPosition(s, /*force*/ true);
|
||||
}
|
||||
getTextPos(): number {
|
||||
return this.writer.getTextPos();
|
||||
function getTextPos(): number {
|
||||
return writer.getTextPos();
|
||||
}
|
||||
getLine(): number {
|
||||
return this.writer.getLine();
|
||||
function getLine(): number {
|
||||
return writer.getLine();
|
||||
}
|
||||
getColumn(): number {
|
||||
return this.writer.getColumn();
|
||||
function getColumn(): number {
|
||||
return writer.getColumn();
|
||||
}
|
||||
getIndent(): number {
|
||||
return this.writer.getIndent();
|
||||
function getIndent(): number {
|
||||
return writer.getIndent();
|
||||
}
|
||||
isAtStartOfLine(): boolean {
|
||||
return this.writer.isAtStartOfLine();
|
||||
function isAtStartOfLine(): boolean {
|
||||
return writer.isAtStartOfLine();
|
||||
}
|
||||
clear(): void {
|
||||
this.writer.clear();
|
||||
this.lastNonTriviaPosition = 0;
|
||||
function clear(): void {
|
||||
writer.clear();
|
||||
lastNonTriviaPosition = 0;
|
||||
}
|
||||
|
||||
return {
|
||||
onEmitNode,
|
||||
onBeforeEmitNodeArray,
|
||||
onAfterEmitNodeArray,
|
||||
onBeforeEmitToken,
|
||||
onAfterEmitToken,
|
||||
write,
|
||||
writeComment,
|
||||
writeKeyword,
|
||||
writeOperator,
|
||||
writePunctuation,
|
||||
writeTrailingSemicolon,
|
||||
writeParameter,
|
||||
writeProperty,
|
||||
writeSpace,
|
||||
writeStringLiteral,
|
||||
writeSymbol,
|
||||
writeLine,
|
||||
increaseIndent,
|
||||
decreaseIndent,
|
||||
getText,
|
||||
rawWrite,
|
||||
writeLiteral,
|
||||
getTextPos,
|
||||
getLine,
|
||||
getColumn,
|
||||
getIndent,
|
||||
isAtStartOfLine,
|
||||
clear
|
||||
};
|
||||
}
|
||||
|
||||
function getInsertionPositionAtSourceFileTop(sourceFile: SourceFile): number {
|
||||
|
||||
@@ -1991,4 +1991,51 @@ namespace ts {
|
||||
});
|
||||
return typeIsAccessible ? res : undefined;
|
||||
}
|
||||
|
||||
export function syntaxUsuallyHasTrailingSemicolon(kind: SyntaxKind) {
|
||||
return kind === SyntaxKind.VariableStatement
|
||||
|| kind === SyntaxKind.ExpressionStatement
|
||||
|| kind === SyntaxKind.DoStatement
|
||||
|| kind === SyntaxKind.ContinueStatement
|
||||
|| kind === SyntaxKind.BreakStatement
|
||||
|| kind === SyntaxKind.ReturnStatement
|
||||
|| kind === SyntaxKind.ThrowStatement
|
||||
|| kind === SyntaxKind.DebuggerStatement
|
||||
|| kind === SyntaxKind.PropertyDeclaration
|
||||
|| kind === SyntaxKind.TypeAliasDeclaration
|
||||
|| kind === SyntaxKind.ImportDeclaration
|
||||
|| kind === SyntaxKind.ImportEqualsDeclaration
|
||||
|| kind === SyntaxKind.ExportDeclaration;
|
||||
}
|
||||
|
||||
export function probablyUsesSemicolons(sourceFile: SourceFile): boolean {
|
||||
let withSemicolon = 0;
|
||||
let withoutSemicolon = 0;
|
||||
const nStatementsToObserve = 5;
|
||||
forEachChild(sourceFile, function visit(node): boolean | undefined {
|
||||
if (syntaxUsuallyHasTrailingSemicolon(node.kind)) {
|
||||
const lastToken = node.getLastToken(sourceFile);
|
||||
if (lastToken && lastToken.kind === SyntaxKind.SemicolonToken) {
|
||||
withSemicolon++;
|
||||
}
|
||||
else {
|
||||
withoutSemicolon++;
|
||||
}
|
||||
}
|
||||
if (withSemicolon + withoutSemicolon >= nStatementsToObserve) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return forEachChild(node, visit);
|
||||
});
|
||||
|
||||
// One statement missing a semicolon isn’t sufficient evidence to say the user
|
||||
// doesn’t want semicolons, because they may not even be done writing that statement.
|
||||
if (withSemicolon === 0 && withoutSemicolon <= 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If even 2/5 places have a semicolon, the user probably wants semicolons
|
||||
return withSemicolon / withoutSemicolon > 1 / nStatementsToObserve;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ abstract class ExternalCompileRunnerBase extends RunnerBase {
|
||||
*/
|
||||
initializeTests(): void {
|
||||
// Read in and evaluate the test list
|
||||
const testList = this.tests && this.tests.length ? this.tests : this.enumerateTestFiles();
|
||||
const testList = this.tests && this.tests.length ? this.tests : this.getTestFiles();
|
||||
|
||||
// tslint:disable-next-line:no-this-assignment
|
||||
const cls = this;
|
||||
@@ -113,7 +113,7 @@ class DockerfileRunner extends ExternalCompileRunnerBase {
|
||||
}
|
||||
initializeTests(): void {
|
||||
// Read in and evaluate the test list
|
||||
const testList = this.tests && this.tests.length ? this.tests : this.enumerateTestFiles();
|
||||
const testList = this.tests && this.tests.length ? this.tests : this.getTestFiles();
|
||||
|
||||
// tslint:disable-next-line:no-this-assignment
|
||||
const cls = this;
|
||||
@@ -201,7 +201,7 @@ function sanitizeTimestamps(result: string): string {
|
||||
function sanitizeVersionSpecifiers(result: string): string {
|
||||
return result
|
||||
.replace(/\d+.\d+.\d+-insiders.\d\d\d\d\d\d\d\d/g, "X.X.X-insiders.xxxxxxxx")
|
||||
.replace(/([@v])\d+\.\d+\.\d+/g, "$1X.X.X");
|
||||
.replace(/([@v\()])\d+\.\d+\.\d+/g, "$1X.X.X");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -222,7 +222,7 @@ namespace Harness.Parallel.Host {
|
||||
console.log("Discovering runner-based tests...");
|
||||
const discoverStart = +(new Date());
|
||||
for (const runner of runners) {
|
||||
for (const test of runner.enumerateTestFiles()) {
|
||||
for (const test of runner.getTestFiles()) {
|
||||
const file = typeof test === "string" ? test : test.file;
|
||||
let size: number;
|
||||
if (!perfData) {
|
||||
|
||||
@@ -32,7 +32,11 @@ namespace project {
|
||||
|
||||
export class ProjectRunner extends RunnerBase {
|
||||
public enumerateTestFiles() {
|
||||
return this.enumerateFiles("tests/cases/project", /\.json$/, { recursive: true });
|
||||
const all = this.enumerateFiles("tests/cases/project", /\.json$/, { recursive: true });
|
||||
if (shards === 1) {
|
||||
return all;
|
||||
}
|
||||
return all.filter((_val, idx) => idx % shards === (shardId - 1));
|
||||
}
|
||||
|
||||
public kind(): TestRunnerKind {
|
||||
|
||||
@@ -80,6 +80,8 @@ interface TestConfig {
|
||||
timeout?: number;
|
||||
keepFailed?: boolean;
|
||||
skipPercent?: number;
|
||||
shardId?: number;
|
||||
shards?: number;
|
||||
}
|
||||
|
||||
interface TaskSet {
|
||||
@@ -114,6 +116,12 @@ function handleTestConfig() {
|
||||
if (testConfig.skipPercent !== undefined) {
|
||||
skipPercent = testConfig.skipPercent;
|
||||
}
|
||||
if (testConfig.shardId) {
|
||||
shardId = testConfig.shardId;
|
||||
}
|
||||
if (testConfig.shards) {
|
||||
shards = testConfig.shards;
|
||||
}
|
||||
|
||||
if (testConfig.stackTraceLimit === "full") {
|
||||
(<any>Error).stackTraceLimit = Infinity;
|
||||
@@ -129,6 +137,9 @@ function handleTestConfig() {
|
||||
|
||||
const runnerConfig = testConfig.runners || testConfig.test;
|
||||
if (runnerConfig && runnerConfig.length > 0) {
|
||||
if (testConfig.runners) {
|
||||
runUnitTests = runnerConfig.indexOf("unittest") !== -1;
|
||||
}
|
||||
for (const option of runnerConfig) {
|
||||
if (!option) {
|
||||
continue;
|
||||
|
||||
@@ -225,7 +225,7 @@ class RWCRunner extends RunnerBase {
|
||||
*/
|
||||
public initializeTests(): void {
|
||||
// Read in and evaluate the test list
|
||||
for (const test of this.tests && this.tests.length ? this.tests : this.enumerateTestFiles()) {
|
||||
for (const test of this.tests && this.tests.length ? this.tests : this.getTestFiles()) {
|
||||
this.runTest(typeof test === "string" ? test : test.file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ class Test262BaselineRunner extends RunnerBase {
|
||||
public initializeTests() {
|
||||
// this will set up a series of describe/it blocks to run between the setup and cleanup phases
|
||||
if (this.tests.length === 0) {
|
||||
const testFiles = this.enumerateTestFiles();
|
||||
const testFiles = this.getTestFiles();
|
||||
testFiles.forEach(fn => {
|
||||
this.runTest(fn);
|
||||
});
|
||||
|
||||
@@ -94,6 +94,7 @@ namespace ts.projectSystem {
|
||||
describe("with main and depedency project", () => {
|
||||
const projectLocation = "/user/username/projects/myproject";
|
||||
const dependecyLocation = `${projectLocation}/dependency`;
|
||||
const dependecyDeclsLocation = `${projectLocation}/decls`;
|
||||
const mainLocation = `${projectLocation}/main`;
|
||||
const dependencyTs: File = {
|
||||
path: `${dependecyLocation}/FnS.ts`,
|
||||
@@ -106,7 +107,7 @@ export function fn5() { }
|
||||
};
|
||||
const dependencyConfig: File = {
|
||||
path: `${dependecyLocation}/tsconfig.json`,
|
||||
content: JSON.stringify({ compilerOptions: { composite: true, declarationMap: true } })
|
||||
content: JSON.stringify({ compilerOptions: { composite: true, declarationMap: true, declarationDir: "../decls" } })
|
||||
};
|
||||
|
||||
const mainTs: File = {
|
||||
@@ -117,7 +118,7 @@ export function fn5() { }
|
||||
fn3,
|
||||
fn4,
|
||||
fn5
|
||||
} from '../dependency/fns'
|
||||
} from '../decls/fns'
|
||||
|
||||
fn1();
|
||||
fn2();
|
||||
@@ -142,9 +143,9 @@ fn5();
|
||||
path: `${projectLocation}/random/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
const dtsLocation = `${dependecyLocation}/FnS.d.ts`;
|
||||
const dtsLocation = `${dependecyDeclsLocation}/FnS.d.ts`;
|
||||
const dtsPath = dtsLocation.toLowerCase() as Path;
|
||||
const dtsMapLocation = `${dtsLocation}.map`;
|
||||
const dtsMapLocation = `${dependecyDeclsLocation}/FnS.d.ts.map`;
|
||||
const dtsMapPath = dtsMapLocation.toLowerCase() as Path;
|
||||
|
||||
const files = [dependencyTs, dependencyConfig, mainTs, mainConfig, libFile, randomFile, randomConfig];
|
||||
@@ -217,7 +218,7 @@ fn5();
|
||||
start: { line: fn + 1, offset: 5 },
|
||||
end: { line: fn + 1, offset: 8 },
|
||||
contextStart: { line: 1, offset: 1 },
|
||||
contextEnd: { line: 7, offset: 27 }
|
||||
contextEnd: { line: 7, offset: 22 }
|
||||
};
|
||||
}
|
||||
function usageSpan(fn: number): protocol.TextSpan {
|
||||
@@ -287,19 +288,25 @@ fn5();
|
||||
function verifyDocumentPositionMapperUpdates(
|
||||
mainScenario: string,
|
||||
verifier: ReadonlyArray<DocumentPositionMapperVerifier>,
|
||||
closedInfos: ReadonlyArray<string>) {
|
||||
closedInfos: ReadonlyArray<string>,
|
||||
withRefs: boolean) {
|
||||
|
||||
const openFiles = verifier.map(v => v.openFile);
|
||||
const expectedProjectActualFiles = verifier.map(v => v.expectedProjectActualFiles);
|
||||
const actionGetters = verifier.map(v => v.actionGetter);
|
||||
const openFileLastLines = verifier.map(v => v.openFileLastLine);
|
||||
|
||||
const configFiles = openFiles.map(openFile => `${getDirectoryPath(openFile.path)}/tsconfig.json`);
|
||||
const openInfos = openFiles.map(f => f.path);
|
||||
// When usage and dependency are used, dependency config is part of closedInfo so ignore
|
||||
const otherWatchedFiles = verifier.length > 1 ? [configFiles[0]] : configFiles;
|
||||
const otherWatchedFiles = withRefs && verifier.length > 1 ? [configFiles[0]] : configFiles;
|
||||
function openTsFile(onHostCreate?: (host: TestServerHost) => void) {
|
||||
const host = createHost(files, [mainConfig.path]);
|
||||
if (!withRefs) {
|
||||
// Erase project reference
|
||||
host.writeFile(mainConfig.path, JSON.stringify({
|
||||
compilerOptions: { composite: true, declarationMap: true }
|
||||
}));
|
||||
}
|
||||
if (onHostCreate) {
|
||||
onHostCreate(host);
|
||||
}
|
||||
@@ -336,7 +343,7 @@ fn5();
|
||||
);
|
||||
}
|
||||
|
||||
function verifyInfosWhenNoDtsFile(session: TestSession, host: TestServerHost, dependencyTsAndMapOk?: true) {
|
||||
function verifyInfosWhenNoDtsFile(session: TestSession, host: TestServerHost, watchDts: boolean, dependencyTsAndMapOk?: true) {
|
||||
const dtsMapClosedInfo = firstDefined(closedInfos, f => f.toLowerCase() === dtsMapPath ? f : undefined);
|
||||
const dtsClosedInfo = firstDefined(closedInfos, f => f.toLowerCase() === dtsPath ? f : undefined);
|
||||
verifyInfosWithRandom(
|
||||
@@ -344,8 +351,7 @@ fn5();
|
||||
host,
|
||||
openInfos,
|
||||
closedInfos.filter(f => (dependencyTsAndMapOk || f !== dtsMapClosedInfo) && f !== dtsClosedInfo && (dependencyTsAndMapOk || f !== dependencyTs.path)),
|
||||
// When project actual file contains dts, it needs to be watched
|
||||
dtsClosedInfo && expectedProjectActualFiles.some(expectedProjectActualFiles => expectedProjectActualFiles.some(f => f.toLowerCase() === dtsPath)) ?
|
||||
dtsClosedInfo && watchDts ?
|
||||
otherWatchedFiles.concat(dtsClosedInfo) :
|
||||
otherWatchedFiles
|
||||
);
|
||||
@@ -361,22 +367,22 @@ fn5();
|
||||
}
|
||||
}
|
||||
|
||||
function action(actionGetter: SessionActionGetter, fn: number, session: TestSession) {
|
||||
const { reqName, request, expectedResponse, expectedResponseNoMap, expectedResponseNoDts } = actionGetter(fn);
|
||||
function action(verifier: DocumentPositionMapperVerifier, fn: number, session: TestSession) {
|
||||
const { reqName, request, expectedResponse, expectedResponseNoMap, expectedResponseNoDts } = verifier.actionGetter(fn);
|
||||
const { response } = session.executeCommandSeq(request);
|
||||
return { reqName, response, expectedResponse, expectedResponseNoMap, expectedResponseNoDts };
|
||||
return { reqName, response, expectedResponse, expectedResponseNoMap, expectedResponseNoDts, verifier };
|
||||
}
|
||||
|
||||
function firstAction(session: TestSession) {
|
||||
actionGetters.forEach(actionGetter => action(actionGetter, 1, session));
|
||||
verifier.forEach(v => action(v, 1, session));
|
||||
}
|
||||
|
||||
function verifyAllFnActionWorker(session: TestSession, verifyAction: (result: ReturnType<typeof action>, dtsInfo: server.ScriptInfo | undefined, isFirst: boolean) => void, dtsAbsent?: true) {
|
||||
// action
|
||||
let isFirst = true;
|
||||
for (const actionGetter of actionGetters) {
|
||||
for (const v of verifier) {
|
||||
for (let fn = 1; fn <= 5; fn++) {
|
||||
const result = action(actionGetter, fn, session);
|
||||
const result = action(v, fn, session);
|
||||
const dtsInfo = session.getProjectService().filenameToScriptInfo.get(dtsPath);
|
||||
if (dtsAbsent) {
|
||||
assert.isUndefined(dtsInfo);
|
||||
@@ -449,9 +455,17 @@ fn5();
|
||||
dependencyTsAndMapOk?: true
|
||||
) {
|
||||
// action
|
||||
verifyAllFnActionWorker(session, ({ reqName, response, expectedResponse, expectedResponseNoDts }) => {
|
||||
verifyAllFnActionWorker(session, ({ reqName, response, expectedResponse, expectedResponseNoDts, verifier }) => {
|
||||
assert.deepEqual(response, expectedResponseNoDts || expectedResponse, `Failed on ${reqName}`);
|
||||
verifyInfosWhenNoDtsFile(session, host, dependencyTsAndMapOk);
|
||||
verifyInfosWhenNoDtsFile(
|
||||
session,
|
||||
host,
|
||||
// Even when project actual file contains dts, its not watched because the dts is in another folder and module resolution just fails
|
||||
// instead of succeeding to source file and then mapping using project reference (When using usage location)
|
||||
// But watched if sourcemapper is in source project since we need to keep track of dts to update the source mapper for any potential usages
|
||||
verifier.expectedProjectActualFiles.every(f => f.toLowerCase() !== dtsPath),
|
||||
dependencyTsAndMapOk,
|
||||
);
|
||||
}, /*dtsAbsent*/ true);
|
||||
}
|
||||
|
||||
@@ -535,7 +549,11 @@ fn5();
|
||||
// Collecting at this point retains dependency.d.ts and map watcher
|
||||
closeFilesForSession([randomFile], session);
|
||||
openFilesForSession([randomFile], session);
|
||||
verifyInfosWhenNoDtsFile(session, host);
|
||||
verifyInfosWhenNoDtsFile(
|
||||
session,
|
||||
host,
|
||||
!!forEach(verifier, v => v.expectedProjectActualFiles.every(f => f.toLowerCase() !== dtsPath))
|
||||
);
|
||||
|
||||
// Closing open file, removes dependencies too
|
||||
closeFilesForSession([...openFiles, randomFile], session);
|
||||
@@ -616,7 +634,7 @@ fn5();
|
||||
"when dependency file's map changes",
|
||||
host => host.writeFile(
|
||||
dtsMapLocation,
|
||||
`{"version":3,"file":"FnS.d.ts","sourceRoot":"","sources":["FnS.ts"],"names":[],"mappings":"AAAA,wBAAgB,GAAG,SAAM;AACzB,wBAAgB,GAAG,SAAM;AACzB,wBAAgB,GAAG,SAAM;AACzB,wBAAgB,GAAG,SAAM;AACzB,wBAAgB,GAAG,SAAM;AACzB,eAAO,MAAM,CAAC,KAAK,CAAC"}`
|
||||
`{"version":3,"file":"FnS.d.ts","sourceRoot":"","sources":["../dependency/FnS.ts"],"names":[],"mappings":"AAAA,wBAAgB,GAAG,SAAM;AACzB,wBAAgB,GAAG,SAAM;AACzB,wBAAgB,GAAG,SAAM;AACzB,wBAAgB,GAAG,SAAM;AACzB,wBAAgB,GAAG,SAAM;AACzB,eAAO,MAAM,CAAC,KAAK,CAAC"}`
|
||||
),
|
||||
/*afterActionDocumentPositionMapperNotEquals*/ true
|
||||
);
|
||||
@@ -635,44 +653,133 @@ fn5();
|
||||
);
|
||||
}
|
||||
|
||||
const usageVerifier: DocumentPositionMapperVerifier = {
|
||||
openFile: mainTs,
|
||||
expectedProjectActualFiles: [mainTs.path, libFile.path, mainConfig.path, dtsPath],
|
||||
actionGetter: gotoDefintinionFromMainTs,
|
||||
openFileLastLine: 14
|
||||
};
|
||||
describe("from project that uses dependency", () => {
|
||||
const closedInfos = [dependencyTs.path, dependencyConfig.path, libFile.path, dtsPath, dtsMapLocation];
|
||||
verifyDocumentPositionMapperUpdates(
|
||||
"can go to definition correctly",
|
||||
[usageVerifier],
|
||||
closedInfos
|
||||
);
|
||||
});
|
||||
function verifyScenarios(withRefs: boolean) {
|
||||
describe(withRefs ? "when main tsconfig has project reference" : "when main tsconfig doesnt have project reference", () => {
|
||||
const usageVerifier: DocumentPositionMapperVerifier = {
|
||||
openFile: mainTs,
|
||||
expectedProjectActualFiles: [mainTs.path, libFile.path, mainConfig.path, dtsPath],
|
||||
actionGetter: gotoDefintinionFromMainTs,
|
||||
openFileLastLine: 14
|
||||
};
|
||||
describe("from project that uses dependency", () => {
|
||||
const closedInfos = withRefs ?
|
||||
[dependencyTs.path, dependencyConfig.path, libFile.path, dtsPath, dtsMapLocation] :
|
||||
[dependencyTs.path, libFile.path, dtsPath, dtsMapLocation];
|
||||
verifyDocumentPositionMapperUpdates(
|
||||
"can go to definition correctly",
|
||||
[usageVerifier],
|
||||
closedInfos,
|
||||
withRefs
|
||||
);
|
||||
});
|
||||
|
||||
const definingVerifier: DocumentPositionMapperVerifier = {
|
||||
openFile: dependencyTs,
|
||||
expectedProjectActualFiles: [dependencyTs.path, libFile.path, dependencyConfig.path],
|
||||
actionGetter: renameFromDependencyTs,
|
||||
openFileLastLine: 6
|
||||
};
|
||||
describe("from defining project", () => {
|
||||
const closedInfos = [libFile.path, dtsLocation, dtsMapLocation];
|
||||
verifyDocumentPositionMapperUpdates(
|
||||
"rename locations from dependency",
|
||||
[definingVerifier],
|
||||
closedInfos
|
||||
);
|
||||
});
|
||||
const definingVerifier: DocumentPositionMapperVerifier = {
|
||||
openFile: dependencyTs,
|
||||
expectedProjectActualFiles: [dependencyTs.path, libFile.path, dependencyConfig.path],
|
||||
actionGetter: renameFromDependencyTs,
|
||||
openFileLastLine: 6,
|
||||
};
|
||||
describe("from defining project", () => {
|
||||
const closedInfos = [libFile.path, dtsLocation, dtsMapLocation];
|
||||
verifyDocumentPositionMapperUpdates(
|
||||
"rename locations from dependency",
|
||||
[definingVerifier],
|
||||
closedInfos,
|
||||
withRefs
|
||||
);
|
||||
});
|
||||
|
||||
describe("when opening depedency and usage project", () => {
|
||||
const closedInfos = [libFile.path, dtsPath, dtsMapLocation, dependencyConfig.path];
|
||||
verifyDocumentPositionMapperUpdates(
|
||||
"goto Definition in usage and rename locations from defining project",
|
||||
[usageVerifier, { ...definingVerifier, actionGetter: renameFromDependencyTsWithBothProjectsOpen }],
|
||||
closedInfos
|
||||
);
|
||||
});
|
||||
describe("when opening depedency and usage project", () => {
|
||||
const closedInfos = withRefs ?
|
||||
[libFile.path, dtsPath, dtsMapLocation, dependencyConfig.path] :
|
||||
[libFile.path, dtsPath, dtsMapLocation];
|
||||
verifyDocumentPositionMapperUpdates(
|
||||
"goto Definition in usage and rename locations from defining project",
|
||||
[usageVerifier, { ...definingVerifier, actionGetter: renameFromDependencyTsWithBothProjectsOpen }],
|
||||
closedInfos,
|
||||
withRefs
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
verifyScenarios(/*withRefs*/ false);
|
||||
verifyScenarios(/*withRefs*/ true);
|
||||
});
|
||||
|
||||
it("reusing d.ts files from composite and non composite projects", () => {
|
||||
const projectLocation = "/user/username/projects/myproject";
|
||||
const configA: File = {
|
||||
path: `${projectLocation}/compositea/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
composite: true,
|
||||
outDir: "../dist/",
|
||||
rootDir: "../",
|
||||
baseUrl: "../",
|
||||
paths: { "@ref/*": ["./dist/*"] }
|
||||
}
|
||||
})
|
||||
};
|
||||
const aTs: File = {
|
||||
path: `${projectLocation}/compositea/a.ts`,
|
||||
content: `import { b } from "@ref/compositeb/b";`
|
||||
};
|
||||
const a2Ts: File = {
|
||||
path: `${projectLocation}/compositea/a2.ts`,
|
||||
content: `export const x = 10;`
|
||||
};
|
||||
const configB: File = {
|
||||
path: `${projectLocation}/compositeb/tsconfig.json`,
|
||||
content: configA.content
|
||||
};
|
||||
const bTs: File = {
|
||||
path: `${projectLocation}/compositeb/b.ts`,
|
||||
content: "export function b() {}"
|
||||
};
|
||||
const bDts: File = {
|
||||
path: `${projectLocation}/dist/compositeb/b.d.ts`,
|
||||
content: "export declare function b(): void;"
|
||||
};
|
||||
const configC: File = {
|
||||
path: `${projectLocation}/compositec/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
composite: true,
|
||||
outDir: "../dist/",
|
||||
rootDir: "../",
|
||||
baseUrl: "../",
|
||||
paths: { "@ref/*": ["./*"] }
|
||||
},
|
||||
references: [{ path: "../compositeb" }]
|
||||
})
|
||||
};
|
||||
const cTs: File = {
|
||||
path: `${projectLocation}/compositec/c.ts`,
|
||||
content: aTs.content
|
||||
};
|
||||
const files = [libFile, aTs, a2Ts, configA, bDts, bTs, configB, cTs, configC];
|
||||
const host = createServerHost(files);
|
||||
const service = createProjectService(host);
|
||||
service.openClientFile(aTs.path);
|
||||
service.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
|
||||
// project A referencing b.d.ts without project reference
|
||||
const projectA = service.configuredProjects.get(configA.path)!;
|
||||
assert.isDefined(projectA);
|
||||
checkProjectActualFiles(projectA, [aTs.path, a2Ts.path, bDts.path, libFile.path, configA.path]);
|
||||
|
||||
// reuses b.d.ts but sets the path and resolved path since projectC has project references
|
||||
// as the real resolution was to b.ts
|
||||
service.openClientFile(cTs.path);
|
||||
service.checkNumberOfProjects({ configuredProjects: 2 });
|
||||
const projectC = service.configuredProjects.get(configC.path)!;
|
||||
checkProjectActualFiles(projectC, [cTs.path, bDts.path, libFile.path, configC.path]);
|
||||
|
||||
// Now new project for project A tries to reuse b but there is no filesByName mapping for b's source location
|
||||
host.writeFile(a2Ts.path, `${a2Ts.content}export const y = 30;`);
|
||||
assert.isTrue(projectA.dirty);
|
||||
projectA.updateGraph();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user