mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into fix32349
This commit is contained in:
+216
-105
@@ -42,10 +42,10 @@ namespace ts {
|
||||
iterableCacheKey: "iterationTypesOfAsyncIterable" | "iterationTypesOfIterable";
|
||||
iteratorCacheKey: "iterationTypesOfAsyncIterator" | "iterationTypesOfIterator";
|
||||
iteratorSymbolName: "asyncIterator" | "iterator";
|
||||
getGlobalIteratorType: (reportErrors: boolean) => Type;
|
||||
getGlobalIterableType: (reportErrors: boolean) => Type;
|
||||
getGlobalIterableIteratorType: (reportErrors: boolean) => Type;
|
||||
getGlobalGeneratorType: (reportErrors: boolean) => Type;
|
||||
getGlobalIteratorType: (reportErrors: boolean) => GenericType;
|
||||
getGlobalIterableType: (reportErrors: boolean) => GenericType;
|
||||
getGlobalIterableIteratorType: (reportErrors: boolean) => GenericType;
|
||||
getGlobalGeneratorType: (reportErrors: boolean) => GenericType;
|
||||
resolveIterationType: (type: Type, errorNode: Node | undefined) => Type | undefined;
|
||||
mustHaveANextMethodDiagnostic: DiagnosticMessage;
|
||||
mustBeAMethodDiagnostic: DiagnosticMessage;
|
||||
@@ -9531,24 +9531,10 @@ namespace ts {
|
||||
return createTypeFromGenericGlobalType(getGlobalTypedPropertyDescriptorType(), [propertyType]);
|
||||
}
|
||||
|
||||
function createAsyncGeneratorType(yieldType: Type, returnType: Type, nextType: Type) {
|
||||
const globalAsyncGeneratorType = getGlobalAsyncGeneratorType(/*reportErrors*/ true);
|
||||
if (globalAsyncGeneratorType !== emptyGenericType) {
|
||||
yieldType = getAwaitedType(yieldType) || unknownType;
|
||||
returnType = getAwaitedType(returnType) || unknownType;
|
||||
nextType = getAwaitedType(nextType) || unknownType;
|
||||
}
|
||||
return createTypeFromGenericGlobalType(globalAsyncGeneratorType, [yieldType, returnType, nextType]);
|
||||
}
|
||||
|
||||
function createIterableType(iteratedType: Type): Type {
|
||||
return createTypeFromGenericGlobalType(getGlobalIterableType(/*reportErrors*/ true), [iteratedType]);
|
||||
}
|
||||
|
||||
function createGeneratorType(yieldType: Type, returnType: Type, nextType: Type) {
|
||||
return createTypeFromGenericGlobalType(getGlobalGeneratorType(/*reportErrors*/ true), [yieldType, returnType, nextType]);
|
||||
}
|
||||
|
||||
function createArrayType(elementType: Type, readonly?: boolean): ObjectType {
|
||||
return createTypeFromGenericGlobalType(readonly ? globalReadonlyArrayType : globalArrayType, [elementType]);
|
||||
}
|
||||
@@ -9904,7 +9890,7 @@ namespace ts {
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
||||
function addTypeToIntersection(typeSet: Type[], includes: TypeFlags, type: Type) {
|
||||
function addTypeToIntersection(typeSet: Map<Type>, includes: TypeFlags, type: Type) {
|
||||
const flags = type.flags;
|
||||
if (flags & TypeFlags.Intersection) {
|
||||
return addTypesToIntersection(typeSet, includes, (<IntersectionType>type).types);
|
||||
@@ -9912,20 +9898,20 @@ namespace ts {
|
||||
if (isEmptyAnonymousObjectType(type)) {
|
||||
if (!(includes & TypeFlags.IncludesEmptyObject)) {
|
||||
includes |= TypeFlags.IncludesEmptyObject;
|
||||
typeSet.push(type);
|
||||
typeSet.set(type.id.toString(), type);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (flags & TypeFlags.AnyOrUnknown) {
|
||||
if (type === wildcardType) includes |= TypeFlags.IncludesWildcard;
|
||||
}
|
||||
else if ((strictNullChecks || !(flags & TypeFlags.Nullable)) && !contains(typeSet, type)) {
|
||||
else if ((strictNullChecks || !(flags & TypeFlags.Nullable)) && !typeSet.has(type.id.toString())) {
|
||||
if (type.flags & TypeFlags.Unit && includes & TypeFlags.Unit) {
|
||||
// We have seen two distinct unit types which means we should reduce to an
|
||||
// empty intersection. Adding TypeFlags.NonPrimitive causes that to happen.
|
||||
includes |= TypeFlags.NonPrimitive;
|
||||
}
|
||||
typeSet.push(type);
|
||||
typeSet.set(type.id.toString(), type);
|
||||
}
|
||||
includes |= flags & TypeFlags.IncludesMask;
|
||||
}
|
||||
@@ -9934,7 +9920,7 @@ namespace ts {
|
||||
|
||||
// Add the given types to the given type set. Order is preserved, freshness is removed from literal
|
||||
// types, duplicates are removed, and nested types of the given kind are flattened into the set.
|
||||
function addTypesToIntersection(typeSet: Type[], includes: TypeFlags, types: ReadonlyArray<Type>) {
|
||||
function addTypesToIntersection(typeSet: Map<Type>, includes: TypeFlags, types: ReadonlyArray<Type>) {
|
||||
for (const type of types) {
|
||||
includes = addTypeToIntersection(typeSet, includes, getRegularTypeOfLiteralType(type));
|
||||
}
|
||||
@@ -10041,8 +10027,9 @@ namespace ts {
|
||||
// Also, unlike union types, the order of the constituent types is preserved in order that overload resolution
|
||||
// for intersections of types with signatures can be deterministic.
|
||||
function getIntersectionType(types: ReadonlyArray<Type>, aliasSymbol?: Symbol, aliasTypeArguments?: ReadonlyArray<Type>): Type {
|
||||
const typeSet: Type[] = [];
|
||||
const includes = addTypesToIntersection(typeSet, 0, types);
|
||||
const typeMembershipMap: Map<Type> = createMap();
|
||||
const includes = addTypesToIntersection(typeMembershipMap, 0, types);
|
||||
const typeSet: Type[] = arrayFrom(typeMembershipMap.values());
|
||||
// An intersection type is considered empty if it contains
|
||||
// the type never, or
|
||||
// more than one unit type or,
|
||||
@@ -13553,6 +13540,9 @@ namespace ts {
|
||||
if (relation !== identityRelation) {
|
||||
source = getApparentType(source);
|
||||
}
|
||||
else if (isGenericMappedType(source)) {
|
||||
return Ternary.False;
|
||||
}
|
||||
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target &&
|
||||
!(getObjectFlags(source) & ObjectFlags.MarkerType || getObjectFlags(target) & ObjectFlags.MarkerType)) {
|
||||
// We have type references to the same generic type, and the type references are not marker
|
||||
@@ -15266,8 +15256,8 @@ namespace ts {
|
||||
const inference = inferences[i];
|
||||
if (t === inference.typeParameter) {
|
||||
if (fix && !inference.isFixed) {
|
||||
clearCachedInferences(inferences);
|
||||
inference.isFixed = true;
|
||||
inference.inferredType = undefined;
|
||||
}
|
||||
return getInferredType(context, i);
|
||||
}
|
||||
@@ -15275,6 +15265,14 @@ namespace ts {
|
||||
return t;
|
||||
}
|
||||
|
||||
function clearCachedInferences(inferences: InferenceInfo[]) {
|
||||
for (const inference of inferences) {
|
||||
if (!inference.isFixed) {
|
||||
inference.inferredType = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createInferenceInfo(typeParameter: TypeParameter): InferenceInfo {
|
||||
return {
|
||||
typeParameter,
|
||||
@@ -15461,9 +15459,11 @@ namespace ts {
|
||||
|
||||
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) {
|
||||
let symbolStack: Symbol[];
|
||||
let visited: Map<boolean>;
|
||||
let visited: Map<number>;
|
||||
let bivariant = false;
|
||||
let propagationType: Type;
|
||||
let inferenceCount = 0;
|
||||
let inferenceIncomplete = false;
|
||||
let allowComplexConstraintInference = true;
|
||||
inferFromTypes(originalSource, originalTarget);
|
||||
|
||||
@@ -15505,23 +15505,28 @@ namespace ts {
|
||||
// of all their possible values.
|
||||
let matchingTypes: Type[] | undefined;
|
||||
for (const t of (<UnionOrIntersectionType>source).types) {
|
||||
if (typeIdenticalToSomeType(t, (<UnionOrIntersectionType>target).types)) {
|
||||
(matchingTypes || (matchingTypes = [])).push(t);
|
||||
inferFromTypes(t, t);
|
||||
}
|
||||
else if (t.flags & (TypeFlags.NumberLiteral | TypeFlags.StringLiteral)) {
|
||||
const b = getBaseTypeOfLiteralType(t);
|
||||
if (typeIdenticalToSomeType(b, (<UnionOrIntersectionType>target).types)) {
|
||||
(matchingTypes || (matchingTypes = [])).push(t, b);
|
||||
}
|
||||
const matched = findMatchedType(t, <UnionOrIntersectionType>target);
|
||||
if (matched) {
|
||||
(matchingTypes || (matchingTypes = [])).push(matched);
|
||||
inferFromTypes(matched, matched);
|
||||
}
|
||||
}
|
||||
// Next, to improve the quality of inferences, reduce the source and target types by
|
||||
// removing the identically matched constituents. For example, when inferring from
|
||||
// 'string | string[]' to 'string | T' we reduce the types to 'string[]' and 'T'.
|
||||
if (matchingTypes) {
|
||||
source = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>source, matchingTypes);
|
||||
target = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
|
||||
const s = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>source, matchingTypes);
|
||||
const t = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
|
||||
if (!(s && t)) return;
|
||||
source = s;
|
||||
target = t;
|
||||
}
|
||||
}
|
||||
else if (target.flags & TypeFlags.Union && !(target.flags & TypeFlags.EnumLiteral) || target.flags & TypeFlags.Intersection) {
|
||||
const matched = findMatchedType(source, <UnionOrIntersectionType>target);
|
||||
if (matched) {
|
||||
inferFromTypes(matched, matched);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) {
|
||||
@@ -15554,26 +15559,27 @@ namespace ts {
|
||||
if (contravariant && !bivariant) {
|
||||
if (!contains(inference.contraCandidates, candidate)) {
|
||||
inference.contraCandidates = append(inference.contraCandidates, candidate);
|
||||
inference.inferredType = undefined;
|
||||
clearCachedInferences(inferences);
|
||||
}
|
||||
}
|
||||
else if (!contains(inference.candidates, candidate)) {
|
||||
inference.candidates = append(inference.candidates, candidate);
|
||||
inference.inferredType = undefined;
|
||||
clearCachedInferences(inferences);
|
||||
}
|
||||
}
|
||||
if (!(priority & InferencePriority.ReturnType) && target.flags & TypeFlags.TypeParameter && inference.topLevel && !isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
|
||||
inference.topLevel = false;
|
||||
inference.inferredType = undefined;
|
||||
clearCachedInferences(inferences);
|
||||
}
|
||||
}
|
||||
inferenceCount++;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine
|
||||
const simplified = getSimplifiedType(target, /*writing*/ false);
|
||||
if (simplified !== target) {
|
||||
inferFromTypesOnce(source, simplified);
|
||||
invokeOnce(source, simplified, inferFromTypes);
|
||||
}
|
||||
else if (target.flags & TypeFlags.IndexedAccess) {
|
||||
const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false);
|
||||
@@ -15582,13 +15588,14 @@ namespace ts {
|
||||
if (indexType.flags & TypeFlags.Instantiable) {
|
||||
const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false);
|
||||
if (simplified && simplified !== target) {
|
||||
inferFromTypesOnce(source, simplified);
|
||||
invokeOnce(source, simplified, inferFromTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
|
||||
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (
|
||||
(<TypeReference>source).target === (<TypeReference>target).target || isArrayType(source) && isArrayType(target))) {
|
||||
// If source and target are references to the same generic type, infer from type arguments
|
||||
inferFromTypeArguments((<TypeReference>source).typeArguments || emptyArray, (<TypeReference>target).typeArguments || emptyArray, getVariances((<TypeReference>source).target));
|
||||
}
|
||||
@@ -15618,10 +15625,10 @@ namespace ts {
|
||||
}
|
||||
else if (target.flags & TypeFlags.Conditional && !contravariant) {
|
||||
const targetTypes = [getTrueTypeFromConditionalType(<ConditionalType>target), getFalseTypeFromConditionalType(<ConditionalType>target)];
|
||||
inferToMultipleTypes(source, targetTypes, /*isIntersection*/ false);
|
||||
inferToMultipleTypes(source, targetTypes, target.flags);
|
||||
}
|
||||
else if (target.flags & TypeFlags.UnionOrIntersection) {
|
||||
inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, !!(target.flags & TypeFlags.Intersection));
|
||||
inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, target.flags);
|
||||
}
|
||||
else if (source.flags & TypeFlags.Union) {
|
||||
// Source is a union or intersection type, infer from each constituent type
|
||||
@@ -15650,39 +15657,22 @@ namespace ts {
|
||||
source = apparentSource;
|
||||
}
|
||||
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
|
||||
const key = source.id + "," + target.id;
|
||||
if (visited && visited.get(key)) {
|
||||
return;
|
||||
}
|
||||
(visited || (visited = createMap<boolean>())).set(key, true);
|
||||
// If we are already processing another target type with the same associated symbol (such as
|
||||
// an instantiation of the same generic type), we do not explore this target as it would yield
|
||||
// no further inferences. We exclude the static side of classes from this check since it shares
|
||||
// its symbol with the instance side which would lead to false positives.
|
||||
const isNonConstructorObject = target.flags & TypeFlags.Object &&
|
||||
!(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class);
|
||||
const symbol = isNonConstructorObject ? target.symbol : undefined;
|
||||
if (symbol) {
|
||||
if (contains(symbolStack, symbol)) {
|
||||
return;
|
||||
}
|
||||
(symbolStack || (symbolStack = [])).push(symbol);
|
||||
inferFromObjectTypes(source, target);
|
||||
symbolStack.pop();
|
||||
}
|
||||
else {
|
||||
inferFromObjectTypes(source, target);
|
||||
}
|
||||
invokeOnce(source, target, inferFromObjectTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function inferFromTypesOnce(source: Type, target: Type) {
|
||||
const key = source.id + "," + target.id;
|
||||
if (!visited || !visited.get(key)) {
|
||||
(visited || (visited = createMap<boolean>())).set(key, true);
|
||||
inferFromTypes(source, target);
|
||||
}
|
||||
function invokeOnce(source: Type, target: Type, action: (source: Type, target: Type) => void) {
|
||||
const key = source.id + "," + target.id;
|
||||
const count = visited && visited.get(key);
|
||||
if (count !== undefined) {
|
||||
inferenceCount += count;
|
||||
return;
|
||||
}
|
||||
(visited || (visited = createMap<number>())).set(key, 0);
|
||||
const startCount = inferenceCount;
|
||||
action(source, target);
|
||||
visited.set(key, inferenceCount - startCount);
|
||||
}
|
||||
|
||||
function inferFromTypeArguments(sourceTypes: readonly Type[], targetTypes: readonly Type[], variances: readonly VarianceFlags[]) {
|
||||
@@ -15719,24 +15709,60 @@ 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.
|
||||
function inferToMultipleTypes(source: Type, targets: Type[], targetFlags: TypeFlags) {
|
||||
let typeVariableCount = 0;
|
||||
for (const t of targets) {
|
||||
if (getInferenceInfoForType(t)) {
|
||||
typeVariableCount++;
|
||||
if (targetFlags & TypeFlags.Union) {
|
||||
let nakedTypeVariable: Type | undefined;
|
||||
const sources = source.flags & TypeFlags.Union ? (<UnionType>source).types : [source];
|
||||
const matched = new Array<boolean>(sources.length);
|
||||
const saveInferenceIncomplete = inferenceIncomplete;
|
||||
inferenceIncomplete = false;
|
||||
// First infer to types that are not naked type variables. For each source type we
|
||||
// track whether inferences were made from that particular type to some target.
|
||||
for (const t of targets) {
|
||||
if (getInferenceInfoForType(t)) {
|
||||
nakedTypeVariable = t;
|
||||
typeVariableCount++;
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < sources.length; i++) {
|
||||
const count = inferenceCount;
|
||||
inferFromTypes(sources[i], t);
|
||||
if (count !== inferenceCount) matched[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
inferFromTypes(source, t);
|
||||
const inferenceComplete = !inferenceIncomplete;
|
||||
inferenceIncomplete = inferenceIncomplete || saveInferenceIncomplete;
|
||||
// If the target has a single naked type variable and inference completed (meaning we
|
||||
// explored the types fully), create a union of the source types from which no inferences
|
||||
// have been made so far and infer from that union to the naked type variable.
|
||||
if (typeVariableCount === 1 && inferenceComplete) {
|
||||
const unmatched = flatMap(sources, (s, i) => matched[i] ? undefined : s);
|
||||
if (unmatched.length) {
|
||||
inferFromTypes(getUnionType(unmatched), nakedTypeVariable!);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 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.
|
||||
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) {
|
||||
if (targetFlags & TypeFlags.Intersection ? typeVariableCount === 1 : typeVariableCount > 0) {
|
||||
const savePriority = priority;
|
||||
priority |= InferencePriority.NakedTypeVariable;
|
||||
for (const t of targets) {
|
||||
@@ -15805,6 +15831,28 @@ namespace ts {
|
||||
}
|
||||
|
||||
function inferFromObjectTypes(source: Type, target: Type) {
|
||||
// If we are already processing another target type with the same associated symbol (such as
|
||||
// an instantiation of the same generic type), we do not explore this target as it would yield
|
||||
// no further inferences. We exclude the static side of classes from this check since it shares
|
||||
// its symbol with the instance side which would lead to false positives.
|
||||
const isNonConstructorObject = target.flags & TypeFlags.Object &&
|
||||
!(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class);
|
||||
const symbol = isNonConstructorObject ? target.symbol : undefined;
|
||||
if (symbol) {
|
||||
if (contains(symbolStack, symbol)) {
|
||||
inferenceIncomplete = true;
|
||||
return;
|
||||
}
|
||||
(symbolStack || (symbolStack = [])).push(symbol);
|
||||
inferFromObjectTypesWorker(source, target);
|
||||
symbolStack.pop();
|
||||
}
|
||||
else {
|
||||
inferFromObjectTypesWorker(source, target);
|
||||
}
|
||||
}
|
||||
|
||||
function inferFromObjectTypesWorker(source: Type, target: Type) {
|
||||
if (isGenericMappedType(source) && isGenericMappedType(target)) {
|
||||
// The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer
|
||||
// from S to T and from X to Y.
|
||||
@@ -15907,15 +15955,35 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function typeIdenticalToSomeType(type: Type, types: Type[]): boolean {
|
||||
function isMatchableType(type: Type) {
|
||||
// We exclude non-anonymous object types because some frameworks (e.g. Ember) rely on the ability to
|
||||
// infer between types that don't witness their type variables. Such types would otherwise be eliminated
|
||||
// because they appear identical.
|
||||
return !(type.flags & TypeFlags.Object) || !!(getObjectFlags(type) & ObjectFlags.Anonymous);
|
||||
}
|
||||
|
||||
function typeMatchedBySomeType(type: Type, types: Type[]): boolean {
|
||||
for (const t of types) {
|
||||
if (isTypeIdenticalTo(t, type)) {
|
||||
if (t === type || isMatchableType(t) && isMatchableType(type) && isTypeIdenticalTo(t, type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function findMatchedType(type: Type, target: UnionOrIntersectionType) {
|
||||
if (typeMatchedBySomeType(type, target.types)) {
|
||||
return type;
|
||||
}
|
||||
if (type.flags & (TypeFlags.NumberLiteral | TypeFlags.StringLiteral) && target.flags & TypeFlags.Union) {
|
||||
const base = getBaseTypeOfLiteralType(type);
|
||||
if (typeMatchedBySomeType(base, target.types)) {
|
||||
return base;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new union or intersection type computed by removing a given set of types
|
||||
* from a given union or intersection type.
|
||||
@@ -15923,11 +15991,11 @@ namespace ts {
|
||||
function removeTypesFromUnionOrIntersection(type: UnionOrIntersectionType, typesToRemove: Type[]) {
|
||||
const reducedTypes: Type[] = [];
|
||||
for (const t of type.types) {
|
||||
if (!typeIdenticalToSomeType(t, typesToRemove)) {
|
||||
if (!typeMatchedBySomeType(t, typesToRemove)) {
|
||||
reducedTypes.push(t);
|
||||
}
|
||||
}
|
||||
return type.flags & TypeFlags.Union ? getUnionType(reducedTypes) : getIntersectionType(reducedTypes);
|
||||
return reducedTypes.length ? type.flags & TypeFlags.Union ? getUnionType(reducedTypes) : getIntersectionType(reducedTypes) : undefined;
|
||||
}
|
||||
|
||||
function hasPrimitiveConstraint(type: TypeParameter): boolean {
|
||||
@@ -20579,10 +20647,15 @@ namespace ts {
|
||||
}
|
||||
propType = getConstraintForLocation(getTypeOfSymbol(prop), node);
|
||||
}
|
||||
return getFlowTypeOfAccessExpression(node, prop, propType, right);
|
||||
}
|
||||
|
||||
function getFlowTypeOfAccessExpression(node: ElementAccessExpression | PropertyAccessExpression | QualifiedName, prop: Symbol | undefined, propType: Type, errorNode: Node) {
|
||||
// Only compute control flow type if this is a property access expression that isn't an
|
||||
// assignment target, and the referenced property was declared as a variable, property,
|
||||
// accessor, or optional method.
|
||||
if (node.kind !== SyntaxKind.PropertyAccessExpression ||
|
||||
const assignmentKind = getAssignmentTargetKind(node);
|
||||
if (node.kind !== SyntaxKind.ElementAccessExpression && node.kind !== SyntaxKind.PropertyAccessExpression ||
|
||||
assignmentKind === AssignmentKind.Definite ||
|
||||
prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
|
||||
return propType;
|
||||
@@ -20592,7 +20665,7 @@ namespace ts {
|
||||
// and if we are in a constructor of the same class as the property declaration, assume that
|
||||
// the property is uninitialized at the top of the control flow.
|
||||
let assumeUninitialized = false;
|
||||
if (strictNullChecks && strictPropertyInitialization && left.kind === SyntaxKind.ThisKeyword) {
|
||||
if (strictNullChecks && strictPropertyInitialization && node.expression.kind === SyntaxKind.ThisKeyword) {
|
||||
const declaration = prop && prop.valueDeclaration;
|
||||
if (declaration && isInstancePropertyWithoutInitializer(declaration)) {
|
||||
const flowContainer = getControlFlowContainer(node);
|
||||
@@ -20609,7 +20682,7 @@ namespace ts {
|
||||
}
|
||||
const flowType = getFlowTypeOfReference(node, propType, assumeUninitialized ? getOptionalType(propType) : propType);
|
||||
if (assumeUninitialized && !(getFalsyFlags(propType) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
|
||||
error(right, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(prop!)); // TODO: GH#18217
|
||||
error(errorNode, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(prop!)); // TODO: GH#18217
|
||||
// Return the declared type to reduce follow-on errors
|
||||
return propType;
|
||||
}
|
||||
@@ -20966,7 +21039,7 @@ namespace ts {
|
||||
AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0) :
|
||||
AccessFlags.None;
|
||||
const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, node, accessFlags) || errorType;
|
||||
return checkIndexedAccessIndexType(indexedAccessType, node);
|
||||
return checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, indexedAccessType.symbol, indexedAccessType, indexExpression), node);
|
||||
}
|
||||
|
||||
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
|
||||
@@ -22750,7 +22823,7 @@ namespace ts {
|
||||
isVariableDeclaration(decl.parent) && getSymbolOfNode(decl.parent));
|
||||
const prototype = assignmentSymbol && assignmentSymbol.exports && assignmentSymbol.exports.get("prototype" as __String);
|
||||
const init = prototype && prototype.valueDeclaration && getAssignedJSPrototype(prototype.valueDeclaration);
|
||||
return init ? checkExpression(init) : undefined;
|
||||
return init ? getWidenedType(checkExpressionCached(init)) : undefined;
|
||||
}
|
||||
|
||||
function getAssignedJSPrototype(node: Node) {
|
||||
@@ -23393,9 +23466,36 @@ namespace ts {
|
||||
}
|
||||
|
||||
function createGeneratorReturnType(yieldType: Type, returnType: Type, nextType: Type, isAsyncGenerator: boolean) {
|
||||
return isAsyncGenerator
|
||||
? createAsyncGeneratorType(yieldType, returnType, nextType)
|
||||
: createGeneratorType(yieldType, returnType, nextType);
|
||||
const resolver = isAsyncGenerator ? asyncIterationTypesResolver : syncIterationTypesResolver;
|
||||
const globalGeneratorType = resolver.getGlobalGeneratorType(/*reportErrors*/ false);
|
||||
yieldType = resolver.resolveIterationType(yieldType, /*errorNode*/ undefined) || unknownType;
|
||||
returnType = resolver.resolveIterationType(returnType, /*errorNode*/ undefined) || unknownType;
|
||||
nextType = resolver.resolveIterationType(nextType, /*errorNode*/ undefined) || unknownType;
|
||||
if (globalGeneratorType === emptyGenericType) {
|
||||
// Fall back to the global IterableIterator if returnType is assignable to the expected return iteration
|
||||
// type of IterableIterator, and the expected next iteration type of IterableIterator is assignable to
|
||||
// nextType.
|
||||
const globalType = resolver.getGlobalIterableIteratorType(/*reportErrors*/ false);
|
||||
const iterationTypes = globalType !== emptyGenericType ? getIterationTypesOfGlobalIterableType(globalType, resolver) : undefined;
|
||||
const iterableIteratorReturnType = iterationTypes ? iterationTypes.returnType : anyType;
|
||||
const iterableIteratorNextType = iterationTypes ? iterationTypes.nextType : undefinedType;
|
||||
if (isTypeAssignableTo(returnType, iterableIteratorReturnType) &&
|
||||
isTypeAssignableTo(iterableIteratorNextType, nextType)) {
|
||||
if (globalType !== emptyGenericType) {
|
||||
return createTypeFromGenericGlobalType(globalType, [yieldType]);
|
||||
}
|
||||
|
||||
// The global IterableIterator type doesn't exist, so report an error
|
||||
resolver.getGlobalIterableIteratorType(/*reportErrors*/ true);
|
||||
return emptyObjectType;
|
||||
}
|
||||
|
||||
// The global Generator type doesn't exist, so report an error
|
||||
resolver.getGlobalGeneratorType(/*reportErrors*/ true);
|
||||
return emptyObjectType;
|
||||
}
|
||||
|
||||
return createTypeFromGenericGlobalType(globalGeneratorType, [yieldType, returnType, nextType]);
|
||||
}
|
||||
|
||||
function checkAndAggregateYieldOperandTypes(func: FunctionLikeDeclaration, checkMode: CheckMode | undefined) {
|
||||
@@ -24714,6 +24814,12 @@ namespace ts {
|
||||
|| anyType;
|
||||
}
|
||||
|
||||
const contextualReturnType = getContextualReturnType(func);
|
||||
if (contextualReturnType) {
|
||||
return getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Next, contextualReturnType, isAsync)
|
||||
|| anyType;
|
||||
}
|
||||
|
||||
return anyType;
|
||||
}
|
||||
|
||||
@@ -26403,7 +26509,11 @@ namespace ts {
|
||||
* The runtime behavior of the `await` keyword.
|
||||
*/
|
||||
function checkAwaitedType(type: Type, errorNode: Node, diagnosticMessage: DiagnosticMessage, arg0?: string | number): Type {
|
||||
return getAwaitedType(type, errorNode, diagnosticMessage, arg0) || errorType;
|
||||
const awaitedType = getAwaitedType(type, errorNode, diagnosticMessage, arg0);
|
||||
if (awaitedType === type && !(type.flags & TypeFlags.AnyOrUnknown)) {
|
||||
addErrorOrSuggestion(/*isError*/ false, createDiagnosticForNode(errorNode, Diagnostics.await_has_no_effect_on_the_type_of_this_expression));
|
||||
}
|
||||
return awaitedType || errorType;
|
||||
}
|
||||
|
||||
function getAwaitedType(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, arg0?: string | number): Type | undefined {
|
||||
@@ -28228,6 +28338,13 @@ namespace ts {
|
||||
return (type as IterableOrIteratorType)[resolver.iterableCacheKey];
|
||||
}
|
||||
|
||||
function getIterationTypesOfGlobalIterableType(globalType: Type, resolver: IterationTypesResolver) {
|
||||
const globalIterationTypes =
|
||||
getIterationTypesOfIterableCached(globalType, resolver) ||
|
||||
getIterationTypesOfIterableSlow(globalType, resolver, /*errorNode*/ undefined);
|
||||
return globalIterationTypes === noIterationTypes ? defaultIterationTypes : globalIterationTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the *yield*, *return*, and *next* types of an `Iterable`-like or `AsyncIterable`-like
|
||||
* type from from common heuristics.
|
||||
@@ -28253,10 +28370,7 @@ namespace ts {
|
||||
// iteration types of their `[Symbol.iterator]()` method. The same is true for their async cousins.
|
||||
// While we define these as `any` and `undefined` in our libs by default, a custom lib *could* use
|
||||
// different definitions.
|
||||
const globalIterationTypes =
|
||||
getIterationTypesOfIterableCached(globalType, resolver) ||
|
||||
getIterationTypesOfIterableSlow(globalType, resolver, /*errorNode*/ undefined);
|
||||
const { returnType, nextType } = globalIterationTypes === noIterationTypes ? defaultIterationTypes : globalIterationTypes;
|
||||
const { returnType, nextType } = getIterationTypesOfGlobalIterableType(globalType, resolver);
|
||||
return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = createIterationTypes(yieldType, returnType, nextType);
|
||||
}
|
||||
|
||||
@@ -33013,9 +33127,6 @@ namespace ts {
|
||||
return grammarErrorAtPos(node, node.end - 1, ";".length, Diagnostics._0_expected, "{");
|
||||
}
|
||||
}
|
||||
else if (isClassLike(node.parent) && isStringLiteral(node.name) && node.name.text === "constructor" && (!compilerOptions.target || compilerOptions.target < ScriptTarget.ES5)) {
|
||||
return grammarErrorOnNode(node.name, Diagnostics.Quoted_constructors_have_previously_been_interpreted_as_methods_which_is_incorrect_In_TypeScript_3_6_they_will_be_correctly_parsed_as_constructors_In_the_meantime_consider_using_constructor_to_write_a_constructor_or_constructor_to_write_a_method);
|
||||
}
|
||||
if (checkGrammarForGenerator(node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4635,6 +4635,11 @@
|
||||
"category": "Suggestion",
|
||||
"code": 80006
|
||||
},
|
||||
"'await' has no effect on the type of this expression.": {
|
||||
"category": "Suggestion",
|
||||
"code": 80007
|
||||
},
|
||||
|
||||
"Add missing 'super()' call": {
|
||||
"category": "Message",
|
||||
"code": 90001
|
||||
@@ -5095,15 +5100,19 @@
|
||||
"category": "Message",
|
||||
"code": 95085
|
||||
},
|
||||
"Remove unnecessary 'await'": {
|
||||
"category": "Message",
|
||||
"code": 95086
|
||||
},
|
||||
"Remove all unnecessary uses of 'await'": {
|
||||
"category": "Message",
|
||||
"code": 95087
|
||||
},
|
||||
|
||||
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
|
||||
"category": "Error",
|
||||
"code": 18004
|
||||
},
|
||||
"Quoted constructors have previously been interpreted as methods, which is incorrect. In TypeScript 3.6, they will be correctly parsed as constructors. In the meantime, consider using 'constructor()' to write a constructor, or '[\"constructor\"]()' to write a method.": {
|
||||
"category": "Error",
|
||||
"code": 18005
|
||||
},
|
||||
"Classes may not have a field named 'constructor'.": {
|
||||
"category": "Error",
|
||||
"code": 18006
|
||||
|
||||
@@ -411,7 +411,11 @@ namespace ts {
|
||||
}
|
||||
);
|
||||
if (emitOnlyDtsFiles && declarationTransform.transformed[0].kind === SyntaxKind.SourceFile) {
|
||||
const sourceFile = declarationTransform.transformed[0] as SourceFile;
|
||||
// Improved narrowing in master/3.6 makes this cast unnecessary, triggering a lint rule.
|
||||
// But at the same time, the LKG (3.5) necessitates it because it doesn’t narrow.
|
||||
// Once the LKG is updated to 3.6, this comment, the cast to `SourceFile`, and the
|
||||
// tslint directive can be all be removed.
|
||||
const sourceFile = declarationTransform.transformed[0] as SourceFile; // tslint:disable-line
|
||||
exportedModulesFromDeclarationEmit = sourceFile.exportedModulesFromDeclarationEmit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4701,7 +4701,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getLeftmostExpression(node: Expression, stopAtCallExpressions: boolean) {
|
||||
export function getLeftmostExpression(node: Expression, stopAtCallExpressions: boolean) {
|
||||
while (true) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
|
||||
+26
-8
@@ -5656,12 +5656,27 @@ namespace ts {
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseConstructorDeclaration(node: ConstructorDeclaration): ConstructorDeclaration {
|
||||
node.kind = SyntaxKind.Constructor;
|
||||
parseExpected(SyntaxKind.ConstructorKeyword);
|
||||
fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node);
|
||||
node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None, Diagnostics.or_expected);
|
||||
return finishNode(node);
|
||||
function parseConstructorName() {
|
||||
if (token() === SyntaxKind.ConstructorKeyword) {
|
||||
return parseExpected(SyntaxKind.ConstructorKeyword);
|
||||
}
|
||||
if (token() === SyntaxKind.StringLiteral && lookAhead(nextToken) === SyntaxKind.OpenParenToken) {
|
||||
return tryParse(() => {
|
||||
const literalNode = parseLiteralNode();
|
||||
return literalNode.text === "constructor" ? literalNode : undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function tryParseConstructorDeclaration(node: ConstructorDeclaration): ConstructorDeclaration | undefined {
|
||||
return tryParse(() => {
|
||||
if (parseConstructorName()) {
|
||||
node.kind = SyntaxKind.Constructor;
|
||||
fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node);
|
||||
node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None, Diagnostics.or_expected);
|
||||
return finishNode(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function parseMethodDeclaration(node: MethodDeclaration, asteriskToken: AsteriskToken, diagnosticMessage?: DiagnosticMessage): MethodDeclaration {
|
||||
@@ -5867,8 +5882,11 @@ namespace ts {
|
||||
return parseAccessorDeclaration(<AccessorDeclaration>node, SyntaxKind.SetAccessor);
|
||||
}
|
||||
|
||||
if (token() === SyntaxKind.ConstructorKeyword) {
|
||||
return parseConstructorDeclaration(<ConstructorDeclaration>node);
|
||||
if (token() === SyntaxKind.ConstructorKeyword || token() === SyntaxKind.StringLiteral) {
|
||||
const constructorDeclaration = tryParseConstructorDeclaration(<ConstructorDeclaration>node);
|
||||
if (constructorDeclaration) {
|
||||
return constructorDeclaration;
|
||||
}
|
||||
}
|
||||
|
||||
if (isIndexSignature()) {
|
||||
|
||||
@@ -197,6 +197,7 @@ namespace ts {
|
||||
"|=": SyntaxKind.BarEqualsToken,
|
||||
"^=": SyntaxKind.CaretEqualsToken,
|
||||
"@": SyntaxKind.AtToken,
|
||||
"`": SyntaxKind.BacktickToken
|
||||
});
|
||||
|
||||
/*
|
||||
@@ -298,7 +299,6 @@ namespace ts {
|
||||
}
|
||||
|
||||
const tokenStrings = makeReverseMap(textToToken);
|
||||
|
||||
export function tokenToString(t: SyntaxKind): string | undefined {
|
||||
return tokenStrings[t];
|
||||
}
|
||||
|
||||
@@ -775,10 +775,11 @@ namespace ts {
|
||||
priority: 5,
|
||||
text: `
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};`
|
||||
|
||||
@@ -229,14 +229,39 @@ namespace ts {
|
||||
if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
|
||||
// spread elements emit like so:
|
||||
// non-spread elements are chunked together into object literals, and then all are passed to __assign:
|
||||
// { a, ...o, b } => __assign({a}, o, {b});
|
||||
// { a, ...o, b } => __assign(__assign({a}, o), {b});
|
||||
// If the first element is a spread element, then the first argument to __assign is {}:
|
||||
// { ...o, a, b, ...o2 } => __assign({}, o, {a, b}, o2)
|
||||
// { ...o, a, b, ...o2 } => __assign(__assign(__assign({}, o), {a, b}), o2)
|
||||
//
|
||||
// We cannot call __assign with more than two elements, since any element could cause side effects. For
|
||||
// example:
|
||||
// var k = { a: 1, b: 2 };
|
||||
// var o = { a: 3, ...k, b: k.a++ };
|
||||
// // expected: { a: 1, b: 1 }
|
||||
// If we translate the above to `__assign({ a: 3 }, k, { b: k.a++ })`, the `k.a++` will evaluate before
|
||||
// `k` is spread and we end up with `{ a: 2, b: 1 }`.
|
||||
//
|
||||
// This also occurs for spread elements, not just property assignments:
|
||||
// var k = { a: 1, get b() { l = { z: 9 }; return 2; } };
|
||||
// var l = { c: 3 };
|
||||
// var o = { ...k, ...l };
|
||||
// // expected: { a: 1, b: 2, z: 9 }
|
||||
// If we translate the above to `__assign({}, k, l)`, the `l` will evaluate before `k` is spread and we
|
||||
// end up with `{ a: 1, b: 2, c: 3 }`
|
||||
const objects = chunkObjectLiteralElements(node.properties);
|
||||
if (objects.length && objects[0].kind !== SyntaxKind.ObjectLiteralExpression) {
|
||||
objects.unshift(createObjectLiteral());
|
||||
}
|
||||
return createAssignHelper(context, objects);
|
||||
let expression: Expression = objects[0];
|
||||
if (objects.length > 1) {
|
||||
for (let i = 1; i < objects.length; i++) {
|
||||
expression = createAssignHelper(context, [expression, objects[i]]);
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
else {
|
||||
return createAssignHelper(context, objects);
|
||||
}
|
||||
}
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
@@ -1882,6 +1882,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface JsxAttributes extends ObjectLiteralExpressionBase<JsxAttributeLike> {
|
||||
kind: SyntaxKind.JsxAttributes;
|
||||
parent: JsxOpeningLikeElement;
|
||||
}
|
||||
|
||||
@@ -4759,7 +4760,7 @@ namespace ts {
|
||||
UMD = 3,
|
||||
System = 4,
|
||||
ES2015 = 5,
|
||||
ESNext = 6
|
||||
ESNext = 99
|
||||
}
|
||||
|
||||
export const enum JsxEmit {
|
||||
@@ -4807,7 +4808,7 @@ namespace ts {
|
||||
ES2018 = 5,
|
||||
ES2019 = 6,
|
||||
ES2020 = 7,
|
||||
ESNext = 8,
|
||||
ESNext = 99,
|
||||
JSON = 100,
|
||||
Latest = ESNext,
|
||||
}
|
||||
|
||||
@@ -3150,6 +3150,23 @@ namespace ts {
|
||||
return s.replace(escapedCharsRegExp, getReplacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip off existed single quotes or double quotes from a given string
|
||||
*
|
||||
* @return non-quoted string
|
||||
*/
|
||||
export function stripQuotes(name: string) {
|
||||
const length = name.length;
|
||||
if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && startsWithQuote(name)) {
|
||||
return name.substring(1, length - 1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
export function startsWithQuote(name: string): boolean {
|
||||
return isSingleOrDoubleQuote(name.charCodeAt(0));
|
||||
}
|
||||
|
||||
function getReplacement(c: string, offset: number, input: string) {
|
||||
if (c.charCodeAt(0) === CharacterCodes.nullCharacter) {
|
||||
const lookAhead = input.charCodeAt(offset + c.length);
|
||||
@@ -7468,7 +7485,7 @@ namespace ts {
|
||||
export function getDirectoryPath(path: Path): Path;
|
||||
/**
|
||||
* Returns the path except for its basename. Semantics align with NodeJS's `path.dirname`
|
||||
* except that we support URLs as well.
|
||||
* except that we support URL's as well.
|
||||
*
|
||||
* ```ts
|
||||
* getDirectoryPath("/path/to/file.ext") === "/path/to"
|
||||
|
||||
+25
-65
@@ -797,8 +797,8 @@ namespace FourSlash {
|
||||
for (const include of toArray(options.includes)) {
|
||||
const name = typeof include === "string" ? include : include.name;
|
||||
const found = nameToEntries.get(name);
|
||||
if (!found) throw this.raiseError(`No completion ${name} found`);
|
||||
assert(found.length === 1, `Must use 'exact' for multiple completions with same name: '${name}'`);
|
||||
if (!found) throw this.raiseError(`Includes: completion '${name}' not found.`);
|
||||
assert(found.length === 1); // Must use 'exact' for multiple completions with same name
|
||||
this.verifyCompletionEntry(ts.first(found), include);
|
||||
}
|
||||
}
|
||||
@@ -806,7 +806,7 @@ namespace FourSlash {
|
||||
for (const exclude of toArray(options.excludes)) {
|
||||
assert(typeof exclude === "string");
|
||||
if (nameToEntries.has(exclude)) {
|
||||
this.raiseError(`Did not expect to get a completion named ${exclude}`);
|
||||
this.raiseError(`Excludes: unexpected completion '${exclude}' found.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -865,7 +865,7 @@ namespace FourSlash {
|
||||
ts.zipWith(actual, expected, (completion, expectedCompletion, index) => {
|
||||
const name = typeof expectedCompletion === "string" ? expectedCompletion : expectedCompletion.name;
|
||||
if (completion.name !== name) {
|
||||
this.raiseError(`${marker ? JSON.stringify(marker) : "" } Expected completion at index ${index} to be ${name}, got ${completion.name}`);
|
||||
this.raiseError(`${marker ? JSON.stringify(marker) : ""} Expected completion at index ${index} to be ${name}, got ${completion.name}`);
|
||||
}
|
||||
this.verifyCompletionEntry(completion, expectedCompletion);
|
||||
});
|
||||
@@ -948,7 +948,7 @@ namespace FourSlash {
|
||||
|
||||
const actual = checker.typeToString(type);
|
||||
if (actual !== expected) {
|
||||
this.raiseError(`Expected: '${expected}', actual: '${actual}'`);
|
||||
this.raiseError(displayExpectedAndActualString(expected, actual));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1024,9 +1024,7 @@ namespace FourSlash {
|
||||
private assertObjectsEqual<T>(fullActual: T, fullExpected: T, msgPrefix = ""): void {
|
||||
const recur = <U>(actual: U, expected: U, path: string) => {
|
||||
const fail = (msg: string) => {
|
||||
this.raiseError(`${msgPrefix} At ${path}: ${msg}
|
||||
Expected: ${stringify(fullExpected)}
|
||||
Actual: ${stringify(fullActual)}`);
|
||||
this.raiseError(`${msgPrefix} At ${path}: ${msg} ${displayExpectedAndActualString(stringify(fullExpected), stringify(fullActual))}`);
|
||||
};
|
||||
|
||||
if ((actual === undefined) !== (expected === undefined)) {
|
||||
@@ -1058,9 +1056,7 @@ Actual: ${stringify(fullActual)}`);
|
||||
if (fullActual === fullExpected) {
|
||||
return;
|
||||
}
|
||||
this.raiseError(`${msgPrefix}
|
||||
Expected: ${stringify(fullExpected)}
|
||||
Actual: ${stringify(fullActual)}`);
|
||||
this.raiseError(`${msgPrefix} ${displayExpectedAndActualString(stringify(fullExpected), stringify(fullActual))}`);
|
||||
}
|
||||
recur(fullActual, fullExpected, "");
|
||||
|
||||
@@ -2111,9 +2107,7 @@ Actual: ${stringify(fullActual)}`);
|
||||
public verifyCurrentLineContent(text: string) {
|
||||
const actual = this.getCurrentLineContent();
|
||||
if (actual !== text) {
|
||||
throw new Error("verifyCurrentLineContent\n" +
|
||||
"\tExpected: \"" + text + "\"\n" +
|
||||
"\t Actual: \"" + actual + "\"");
|
||||
throw new Error("verifyCurrentLineContent\n" + displayExpectedAndActualString(text, actual, /* quoted */ true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2139,25 +2133,19 @@ Actual: ${stringify(fullActual)}`);
|
||||
public verifyTextAtCaretIs(text: string) {
|
||||
const actual = this.getFileContent(this.activeFile.fileName).substring(this.currentCaretPosition, this.currentCaretPosition + text.length);
|
||||
if (actual !== text) {
|
||||
throw new Error("verifyTextAtCaretIs\n" +
|
||||
"\tExpected: \"" + text + "\"\n" +
|
||||
"\t Actual: \"" + actual + "\"");
|
||||
throw new Error("verifyTextAtCaretIs\n" + displayExpectedAndActualString(text, actual, /* quoted */ true));
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCurrentNameOrDottedNameSpanText(text: string) {
|
||||
const span = this.languageService.getNameOrDottedNameSpan(this.activeFile.fileName, this.currentCaretPosition, this.currentCaretPosition);
|
||||
if (!span) {
|
||||
return this.raiseError("verifyCurrentNameOrDottedNameSpanText\n" +
|
||||
"\tExpected: \"" + text + "\"\n" +
|
||||
"\t Actual: undefined");
|
||||
return this.raiseError("verifyCurrentNameOrDottedNameSpanText\n" + displayExpectedAndActualString("\"" + text + "\"", "undefined"));
|
||||
}
|
||||
|
||||
const actual = this.getFileContent(this.activeFile.fileName).substring(span.start, ts.textSpanEnd(span));
|
||||
if (actual !== text) {
|
||||
this.raiseError("verifyCurrentNameOrDottedNameSpanText\n" +
|
||||
"\tExpected: \"" + text + "\"\n" +
|
||||
"\t Actual: \"" + actual + "\"");
|
||||
this.raiseError("verifyCurrentNameOrDottedNameSpanText\n" + displayExpectedAndActualString(text, actual, /* quoted */ true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3690,7 +3678,7 @@ ${code}
|
||||
expected = makeWhitespaceVisible(expected);
|
||||
actual = makeWhitespaceVisible(actual);
|
||||
}
|
||||
return `Expected:\n${expected}\nActual:\n${actual}`;
|
||||
return displayExpectedAndActualString(expected, actual);
|
||||
}
|
||||
|
||||
function differOnlyByWhitespace(a: string, b: string) {
|
||||
@@ -3710,6 +3698,14 @@ ${code}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function displayExpectedAndActualString(expected: string, actual: string, quoted = false) {
|
||||
const expectMsg = "\x1b[1mExpected\x1b[0m\x1b[31m";
|
||||
const actualMsg = "\x1b[1mActual\x1b[0m\x1b[31m";
|
||||
const expectedString = quoted ? "\"" + expected + "\"" : expected;
|
||||
const actualString = quoted ? "\"" + actual + "\"" : actual;
|
||||
return `\n${expectMsg}:\n${expectedString}\n\n${actualMsg}:\n${actualString}`;
|
||||
}
|
||||
}
|
||||
|
||||
namespace FourSlashInterface {
|
||||
@@ -3759,7 +3755,7 @@ namespace FourSlashInterface {
|
||||
}
|
||||
|
||||
export class Plugins {
|
||||
constructor (private state: FourSlash.TestState) {
|
||||
constructor(private state: FourSlash.TestState) {
|
||||
}
|
||||
|
||||
public configurePlugin(pluginName: string, configuration: any): void {
|
||||
@@ -4582,7 +4578,7 @@ namespace FourSlashInterface {
|
||||
export const keywords: ReadonlyArray<ExpectedCompletionEntryObject> = keywordsWithUndefined.filter(k => k.name !== "undefined");
|
||||
|
||||
export const typeKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
|
||||
["false", "null", "true", "void", "any", "boolean", "keyof", "never", "number", "object", "string", "symbol", "undefined", "unique", "unknown", "bigint"].map(keywordEntry);
|
||||
["false", "null", "true", "void", "any", "boolean", "keyof", "never", "readonly", "number", "object", "string", "symbol", "undefined", "unique", "unknown", "bigint"].map(keywordEntry);
|
||||
|
||||
const globalTypeDecls: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
||||
interfaceEntry("Symbol"),
|
||||
@@ -4698,6 +4694,9 @@ namespace FourSlashInterface {
|
||||
];
|
||||
}
|
||||
|
||||
export const typeAssertionKeywords: ReadonlyArray<ExpectedCompletionEntry> =
|
||||
globalTypesPlus([keywordEntry("const")]);
|
||||
|
||||
function getInJsKeywords(keywords: ReadonlyArray<ExpectedCompletionEntryObject>): ReadonlyArray<ExpectedCompletionEntryObject> {
|
||||
return keywords.filter(keyword => {
|
||||
switch (keyword.name) {
|
||||
@@ -4828,40 +4827,23 @@ namespace FourSlashInterface {
|
||||
"interface",
|
||||
"let",
|
||||
"package",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"static",
|
||||
"yield",
|
||||
"abstract",
|
||||
"as",
|
||||
"any",
|
||||
"async",
|
||||
"await",
|
||||
"boolean",
|
||||
"constructor",
|
||||
"declare",
|
||||
"get",
|
||||
"infer",
|
||||
"is",
|
||||
"keyof",
|
||||
"module",
|
||||
"namespace",
|
||||
"never",
|
||||
"readonly",
|
||||
"require",
|
||||
"number",
|
||||
"object",
|
||||
"set",
|
||||
"string",
|
||||
"symbol",
|
||||
"type",
|
||||
"unique",
|
||||
"unknown",
|
||||
"from",
|
||||
"global",
|
||||
"bigint",
|
||||
"of",
|
||||
].map(keywordEntry);
|
||||
|
||||
export const statementKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = statementKeywordsWithTypes.filter(k => {
|
||||
@@ -5042,40 +5024,23 @@ namespace FourSlashInterface {
|
||||
"interface",
|
||||
"let",
|
||||
"package",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"static",
|
||||
"yield",
|
||||
"abstract",
|
||||
"as",
|
||||
"any",
|
||||
"async",
|
||||
"await",
|
||||
"boolean",
|
||||
"constructor",
|
||||
"declare",
|
||||
"get",
|
||||
"infer",
|
||||
"is",
|
||||
"keyof",
|
||||
"module",
|
||||
"namespace",
|
||||
"never",
|
||||
"readonly",
|
||||
"require",
|
||||
"number",
|
||||
"object",
|
||||
"set",
|
||||
"string",
|
||||
"symbol",
|
||||
"type",
|
||||
"unique",
|
||||
"unknown",
|
||||
"from",
|
||||
"global",
|
||||
"bigint",
|
||||
"of",
|
||||
].map(keywordEntry);
|
||||
|
||||
export const globalInJsKeywords = getInJsKeywords(globalKeywords);
|
||||
@@ -5128,11 +5093,6 @@ namespace FourSlashInterface {
|
||||
|
||||
export const insideMethodInJsKeywords = getInJsKeywords(insideMethodKeywords);
|
||||
|
||||
export const globalKeywordsPlusUndefined: ReadonlyArray<ExpectedCompletionEntryObject> = (() => {
|
||||
const i = ts.findIndex(globalKeywords, x => x.name === "unique");
|
||||
return [...globalKeywords.slice(0, i), keywordEntry("undefined"), ...globalKeywords.slice(i)];
|
||||
})();
|
||||
|
||||
export const globals: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
||||
globalThisEntry,
|
||||
...globalsVars,
|
||||
|
||||
@@ -30,7 +30,7 @@ interface Array<T> {}`
|
||||
return combinePaths(getDirectoryPath(libFile.path), "tsc.js");
|
||||
}
|
||||
|
||||
interface TestServerHostCreationParameters {
|
||||
export interface TestServerHostCreationParameters {
|
||||
useCaseSensitiveFileNames?: boolean;
|
||||
executingFilePath?: string;
|
||||
currentDirectory?: string;
|
||||
|
||||
+51
-24
@@ -289,9 +289,8 @@ namespace ts.JsTyping {
|
||||
|
||||
}
|
||||
|
||||
export const enum PackageNameValidationResult {
|
||||
export const enum NameValidationResult {
|
||||
Ok,
|
||||
ScopedPackagesNotSupported,
|
||||
EmptyName,
|
||||
NameTooLong,
|
||||
NameStartsWithDot,
|
||||
@@ -301,49 +300,77 @@ namespace ts.JsTyping {
|
||||
|
||||
const maxPackageNameLength = 214;
|
||||
|
||||
export interface ScopedPackageNameValidationResult {
|
||||
name: string;
|
||||
isScopeName: boolean;
|
||||
result: NameValidationResult;
|
||||
}
|
||||
export type PackageNameValidationResult = NameValidationResult | ScopedPackageNameValidationResult;
|
||||
|
||||
/**
|
||||
* Validates package name using rules defined at https://docs.npmjs.com/files/package.json
|
||||
*/
|
||||
export function validatePackageName(packageName: string): PackageNameValidationResult {
|
||||
return validatePackageNameWorker(packageName, /*supportScopedPackage*/ true);
|
||||
}
|
||||
|
||||
function validatePackageNameWorker(packageName: string, supportScopedPackage: false): NameValidationResult;
|
||||
function validatePackageNameWorker(packageName: string, supportScopedPackage: true): PackageNameValidationResult;
|
||||
function validatePackageNameWorker(packageName: string, supportScopedPackage: boolean): PackageNameValidationResult {
|
||||
if (!packageName) {
|
||||
return PackageNameValidationResult.EmptyName;
|
||||
return NameValidationResult.EmptyName;
|
||||
}
|
||||
if (packageName.length > maxPackageNameLength) {
|
||||
return PackageNameValidationResult.NameTooLong;
|
||||
return NameValidationResult.NameTooLong;
|
||||
}
|
||||
if (packageName.charCodeAt(0) === CharacterCodes.dot) {
|
||||
return PackageNameValidationResult.NameStartsWithDot;
|
||||
return NameValidationResult.NameStartsWithDot;
|
||||
}
|
||||
if (packageName.charCodeAt(0) === CharacterCodes._) {
|
||||
return PackageNameValidationResult.NameStartsWithUnderscore;
|
||||
return NameValidationResult.NameStartsWithUnderscore;
|
||||
}
|
||||
// check if name is scope package like: starts with @ and has one '/' in the middle
|
||||
// scoped packages are not currently supported
|
||||
// TODO: when support will be added we'll need to split and check both scope and package name
|
||||
if (/^@[^/]+\/[^/]+$/.test(packageName)) {
|
||||
return PackageNameValidationResult.ScopedPackagesNotSupported;
|
||||
if (supportScopedPackage) {
|
||||
const matches = /^@([^/]+)\/([^/]+)$/.exec(packageName);
|
||||
if (matches) {
|
||||
const scopeResult = validatePackageNameWorker(matches[1], /*supportScopedPackage*/ false);
|
||||
if (scopeResult !== NameValidationResult.Ok) {
|
||||
return { name: matches[1], isScopeName: true, result: scopeResult };
|
||||
}
|
||||
const packageResult = validatePackageNameWorker(matches[2], /*supportScopedPackage*/ false);
|
||||
if (packageResult !== NameValidationResult.Ok) {
|
||||
return { name: matches[2], isScopeName: false, result: packageResult };
|
||||
}
|
||||
return NameValidationResult.Ok;
|
||||
}
|
||||
}
|
||||
if (encodeURIComponent(packageName) !== packageName) {
|
||||
return PackageNameValidationResult.NameContainsNonURISafeCharacters;
|
||||
return NameValidationResult.NameContainsNonURISafeCharacters;
|
||||
}
|
||||
return PackageNameValidationResult.Ok;
|
||||
return NameValidationResult.Ok;
|
||||
}
|
||||
|
||||
export function renderPackageNameValidationFailure(result: PackageNameValidationResult, typing: string): string {
|
||||
return typeof result === "object" ?
|
||||
renderPackageNameValidationFailureWorker(typing, result.result, result.name, result.isScopeName) :
|
||||
renderPackageNameValidationFailureWorker(typing, result, typing, /*isScopeName*/ false);
|
||||
}
|
||||
|
||||
function renderPackageNameValidationFailureWorker(typing: string, result: NameValidationResult, name: string, isScopeName: boolean): string {
|
||||
const kind = isScopeName ? "Scope" : "Package";
|
||||
switch (result) {
|
||||
case PackageNameValidationResult.EmptyName:
|
||||
return `Package name '${typing}' cannot be empty`;
|
||||
case PackageNameValidationResult.NameTooLong:
|
||||
return `Package name '${typing}' should be less than ${maxPackageNameLength} characters`;
|
||||
case PackageNameValidationResult.NameStartsWithDot:
|
||||
return `Package name '${typing}' cannot start with '.'`;
|
||||
case PackageNameValidationResult.NameStartsWithUnderscore:
|
||||
return `Package name '${typing}' cannot start with '_'`;
|
||||
case PackageNameValidationResult.ScopedPackagesNotSupported:
|
||||
return `Package '${typing}' is scoped and currently is not supported`;
|
||||
case PackageNameValidationResult.NameContainsNonURISafeCharacters:
|
||||
return `Package name '${typing}' contains non URI safe characters`;
|
||||
case PackageNameValidationResult.Ok:
|
||||
case NameValidationResult.EmptyName:
|
||||
return `'${typing}':: ${kind} name '${name}' cannot be empty`;
|
||||
case NameValidationResult.NameTooLong:
|
||||
return `'${typing}':: ${kind} name '${name}' should be less than ${maxPackageNameLength} characters`;
|
||||
case NameValidationResult.NameStartsWithDot:
|
||||
return `'${typing}':: ${kind} name '${name}' cannot start with '.'`;
|
||||
case NameValidationResult.NameStartsWithUnderscore:
|
||||
return `'${typing}':: ${kind} name '${name}' cannot start with '_'`;
|
||||
case NameValidationResult.NameContainsNonURISafeCharacters:
|
||||
return `'${typing}':: ${kind} name '${name}' contains non URI safe characters`;
|
||||
case NameValidationResult.Ok:
|
||||
return Debug.fail(); // Shouldn't have called this.
|
||||
default:
|
||||
throw Debug.assertNever(result);
|
||||
|
||||
Vendored
+1
-1
@@ -12,7 +12,7 @@ declare var Infinity: number;
|
||||
declare function eval(x: string): any;
|
||||
|
||||
/**
|
||||
* Converts A string to an integer.
|
||||
* Converts a string to an integer.
|
||||
* @param s A string to convert into a number.
|
||||
* @param radix A value between 2 and 36 that specifies the base of the number in numString.
|
||||
* If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LCX SchemaVersion="6.0" Name="f:\ddSetup\sources\typescript\localization\compiler2.resx" PsrId="306" FileType="1" SrcCul="en-US" TgtCul="it-IT" xmlns="http://schemas.microsoft.com/locstudio/2006/6/lcx">
|
||||
<Props>
|
||||
<Str Name="CustomName1" Val="Custom 1" />
|
||||
@@ -3301,7 +3301,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Crea l'origine unitamente alle mappe di origine all'interno di un unico file. Richiede l'impostazione di '--inlineSourceMap' o '--sourceMap'.]]></Val>
|
||||
<Val><![CDATA[Crea l'origine unitamente ai mapping di origine all'interno di un unico file. Richiede l'impostazione di '--inlineSourceMap' o '--sourceMap'.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -4123,7 +4123,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Generates a sourcemap for each corresponding '.d.ts' file.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Genera un sourcemap per ogni file '.d.ts' corrispondente.]]></Val>
|
||||
<Val><![CDATA[Genera un mapping di origine per ogni file '.d.ts' corrispondente.]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LCX SchemaVersion="6.0" Name="f:\ddSetup\sources\typescript\localization\compiler2.resx" PsrId="306" FileType="1" SrcCul="en-US" TgtCul="ru-RU" xmlns="http://schemas.microsoft.com/locstudio/2006/6/lcx">
|
||||
<Props>
|
||||
<Str Name="CustomName1" Val="Custom 1" />
|
||||
@@ -3300,7 +3300,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Порождать источник вместе с sourcemap в одном файле (нужно задать параметр --inlineSourceMap или --sourceMap).]]></Val>
|
||||
<Val><![CDATA[Порождать источник вместе с сопоставителями с исходным кодом в одном файле (нужно задать параметр --inlineSourceMap или --sourceMap).]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
@@ -4122,7 +4122,7 @@
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Generates a sourcemap for each corresponding '.d.ts' file.]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Создает sourcemap для каждого соответствующего файла ".d.ts".]]></Val>
|
||||
<Val><![CDATA[Создает сопоставитель с исходным кодом для каждого соответствующего файла ".d.ts".]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
|
||||
@@ -1087,7 +1087,24 @@ namespace ts.server {
|
||||
|
||||
project.close();
|
||||
if (Debug.shouldAssert(AssertionLevel.Normal)) {
|
||||
this.filenameToScriptInfo.forEach(info => Debug.assert(!info.isAttached(project), "Found script Info still attached to project", () => `${project.projectName}: ScriptInfos still attached: ${JSON.stringify(mapDefined(arrayFrom(this.filenameToScriptInfo.values()), info => info.isAttached(project) ? info : undefined))}`));
|
||||
this.filenameToScriptInfo.forEach(info => Debug.assert(
|
||||
!info.isAttached(project),
|
||||
"Found script Info still attached to project",
|
||||
() => `${project.projectName}: ScriptInfos still attached: ${JSON.stringify(
|
||||
arrayFrom(
|
||||
mapDefinedIterator(
|
||||
this.filenameToScriptInfo.values(),
|
||||
info => info.isAttached(project) ?
|
||||
{
|
||||
fileName: info.fileName,
|
||||
projects: info.containingProjects.map(p => p.projectName),
|
||||
hasMixedContent: info.hasMixedContent
|
||||
} : undefined
|
||||
)
|
||||
),
|
||||
/*replacer*/ undefined,
|
||||
" "
|
||||
)}`));
|
||||
}
|
||||
// Remove the project from pending project updates
|
||||
this.pendingProjectUpdates.delete(project.getProjectName());
|
||||
|
||||
@@ -283,25 +283,13 @@ namespace ts.codefix {
|
||||
preferences: UserPreferences,
|
||||
): ReadonlyArray<FixAddNewImport | FixUseImportType> {
|
||||
const isJs = isSourceFileJS(sourceFile);
|
||||
const { allowsImporting } = createLazyPackageJsonDependencyReader(sourceFile, host);
|
||||
const choicesForEachExportingModule = flatMap(moduleSymbols, ({ moduleSymbol, importKind, exportedSymbolIsTypeOnly }) =>
|
||||
moduleSpecifiers.getModuleSpecifiers(moduleSymbol, program.getCompilerOptions(), sourceFile, host, program.getSourceFiles(), preferences, program.redirectTargetsMap)
|
||||
.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 }));
|
||||
|
||||
// Sort by presence in package.json, then shortest paths first
|
||||
return sort(choicesForEachExportingModule, (a, b) => {
|
||||
const allowsImportingA = allowsImporting(a.moduleSpecifier);
|
||||
const allowsImportingB = allowsImporting(b.moduleSpecifier);
|
||||
if (allowsImportingA && !allowsImportingB) {
|
||||
return -1;
|
||||
}
|
||||
if (allowsImportingB && !allowsImportingA) {
|
||||
return 1;
|
||||
}
|
||||
return a.moduleSpecifier.length - b.moduleSpecifier.length;
|
||||
});
|
||||
// Sort to keep the shortest paths first
|
||||
return sort(choicesForEachExportingModule, (a, b) => a.moduleSpecifier.length - b.moduleSpecifier.length);
|
||||
}
|
||||
|
||||
function getFixesForAddImport(
|
||||
@@ -392,8 +380,7 @@ namespace ts.codefix {
|
||||
// "default" is a keyword and not a legal identifier for the import, so we don't expect it here
|
||||
Debug.assert(symbolName !== InternalSymbolName.Default);
|
||||
|
||||
const exportInfos = getExportInfos(symbolName, getMeaningFromLocation(symbolToken), cancellationToken, sourceFile, checker, program, preferences, host);
|
||||
const fixes = arrayFrom(flatMapIterator(exportInfos.entries(), ([_, exportInfos]) =>
|
||||
const fixes = arrayFrom(flatMapIterator(getExportInfos(symbolName, getMeaningFromLocation(symbolToken), cancellationToken, sourceFile, checker, program).entries(), ([_, exportInfos]) =>
|
||||
getFixForImport(exportInfos, symbolName, symbolToken.getStart(sourceFile), program, sourceFile, host, preferences)));
|
||||
return { fixes, symbolName };
|
||||
}
|
||||
@@ -406,8 +393,6 @@ namespace ts.codefix {
|
||||
sourceFile: SourceFile,
|
||||
checker: TypeChecker,
|
||||
program: Program,
|
||||
preferences: UserPreferences,
|
||||
host: LanguageServiceHost
|
||||
): ReadonlyMap<ReadonlyArray<SymbolExportInfo>> {
|
||||
// For each original symbol, keep all re-exports of that symbol together so we can call `getCodeActionsForImport` on the whole group at once.
|
||||
// Maps symbol id to info for modules providing that symbol (original export + re-exports).
|
||||
@@ -415,7 +400,7 @@ namespace ts.codefix {
|
||||
function addSymbol(moduleSymbol: Symbol, exportedSymbol: Symbol, importKind: ImportKind): void {
|
||||
originalSymbolToExportInfos.add(getUniqueSymbolId(exportedSymbol, checker).toString(), { moduleSymbol, importKind, exportedSymbolIsTypeOnly: isTypeOnlySymbol(exportedSymbol, checker) });
|
||||
}
|
||||
forEachExternalModuleToImportFrom(checker, host, preferences, program.redirectTargetsMap, sourceFile, program.getSourceFiles(), moduleSymbol => {
|
||||
forEachExternalModuleToImportFrom(checker, sourceFile, program.getSourceFiles(), moduleSymbol => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, program.getCompilerOptions());
|
||||
@@ -576,44 +561,12 @@ namespace ts.codefix {
|
||||
return some(declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning));
|
||||
}
|
||||
|
||||
export function forEachExternalModuleToImportFrom(checker: TypeChecker, host: LanguageServiceHost, preferences: UserPreferences, redirectTargetsMap: RedirectTargetsMap, from: SourceFile, allSourceFiles: ReadonlyArray<SourceFile>, cb: (module: Symbol) => void) {
|
||||
const { allowsImporting } = createLazyPackageJsonDependencyReader(from, host);
|
||||
const compilerOptions = host.getCompilationSettings();
|
||||
const getCanonicalFileName = hostGetCanonicalFileName(host);
|
||||
export function forEachExternalModuleToImportFrom(checker: TypeChecker, from: SourceFile, allSourceFiles: ReadonlyArray<SourceFile>, cb: (module: Symbol) => void) {
|
||||
forEachExternalModule(checker, allSourceFiles, (module, sourceFile) => {
|
||||
if (sourceFile === undefined && allowsImporting(stripQuotes(module.getName()))) {
|
||||
if (sourceFile === undefined || sourceFile !== from && isImportablePath(from.fileName, sourceFile.fileName)) {
|
||||
cb(module);
|
||||
}
|
||||
else if (sourceFile && sourceFile !== from && isImportablePath(from.fileName, sourceFile.fileName)) {
|
||||
const moduleSpecifier = getNodeModulesPackageNameFromFileName(sourceFile.fileName);
|
||||
if (!moduleSpecifier || allowsImporting(moduleSpecifier)) {
|
||||
cb(module);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function getNodeModulesPackageNameFromFileName(importedFileName: string): string | undefined {
|
||||
const specifier = moduleSpecifiers.getModuleSpecifier(
|
||||
compilerOptions,
|
||||
from,
|
||||
toPath(from.fileName, /*basePath*/ undefined, getCanonicalFileName),
|
||||
importedFileName,
|
||||
host,
|
||||
allSourceFiles,
|
||||
preferences,
|
||||
redirectTargetsMap);
|
||||
|
||||
// Paths here are not node_modules, so we don’t care about them;
|
||||
// returning anything will trigger a lookup in package.json.
|
||||
if (!pathIsRelative(specifier) && !isRootedDiskPath(specifier)) {
|
||||
const components = getPathComponents(getPackageNameFromTypesPackageName(specifier)).slice(1);
|
||||
// Scoped packages
|
||||
if (startsWith(components[0], "@")) {
|
||||
return `${components[0]}/${components[1]}`;
|
||||
}
|
||||
return components[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function forEachExternalModule(checker: TypeChecker, allSourceFiles: ReadonlyArray<SourceFile>, cb: (module: Symbol, sourceFile: SourceFile | undefined) => void) {
|
||||
@@ -667,69 +620,4 @@ namespace ts.codefix {
|
||||
// Need `|| "_"` to ensure result isn't empty.
|
||||
return !isStringANonContextualKeyword(res) ? res || "_" : `_${res}`;
|
||||
}
|
||||
|
||||
function createLazyPackageJsonDependencyReader(fromFile: SourceFile, host: LanguageServiceHost) {
|
||||
const packageJsonPaths = findPackageJsons(getDirectoryPath(fromFile.fileName), host);
|
||||
const dependencyIterator = readPackageJsonDependencies(host, packageJsonPaths);
|
||||
let seenDeps: Map<true> | undefined;
|
||||
let usesNodeCoreModules: boolean | undefined;
|
||||
return { allowsImporting };
|
||||
|
||||
function containsDependency(dependency: string) {
|
||||
if ((seenDeps || (seenDeps = createMap())).has(dependency)) {
|
||||
return true;
|
||||
}
|
||||
let packageName: string | void;
|
||||
while (packageName = dependencyIterator.next().value) {
|
||||
seenDeps.set(packageName, true);
|
||||
if (packageName === dependency) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function allowsImporting(moduleSpecifier: string): boolean {
|
||||
if (!packageJsonPaths.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we’re in JavaScript, it can be difficult to tell whether the user wants to import
|
||||
// from Node core modules or not. We can start by seeing if the user is actually using
|
||||
// any node core modules, as opposed to simply having @types/node accidentally as a
|
||||
// dependency of a dependency.
|
||||
if (isSourceFileJS(fromFile) && JsTyping.nodeCoreModules.has(moduleSpecifier)) {
|
||||
if (usesNodeCoreModules === undefined) {
|
||||
usesNodeCoreModules = consumesNodeCoreModules(fromFile);
|
||||
}
|
||||
if (usesNodeCoreModules) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return containsDependency(moduleSpecifier)
|
||||
|| containsDependency(getTypesPackageName(moduleSpecifier));
|
||||
}
|
||||
}
|
||||
|
||||
function *readPackageJsonDependencies(host: LanguageServiceHost, packageJsonPaths: string[]) {
|
||||
type PackageJson = Record<typeof dependencyKeys[number], Record<string, string> | undefined>;
|
||||
const dependencyKeys = ["dependencies", "devDependencies", "optionalDependencies"] as const;
|
||||
for (const fileName of packageJsonPaths) {
|
||||
const content = readJson(fileName, { readFile: host.readFile ? host.readFile.bind(host) : sys.readFile }) as PackageJson;
|
||||
for (const key of dependencyKeys) {
|
||||
const dependencies = content[key];
|
||||
if (!dependencies) {
|
||||
continue;
|
||||
}
|
||||
for (const packageName in dependencies) {
|
||||
yield packageName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function consumesNodeCoreModules(sourceFile: SourceFile): boolean {
|
||||
return some(sourceFile.imports, ({ text }) => JsTyping.nodeCoreModules.has(text));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
const fixId = "removeUnnecessaryAwait";
|
||||
const errorCodes = [
|
||||
Diagnostics.await_has_no_effect_on_the_type_of_this_expression.code,
|
||||
];
|
||||
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions: (context) => {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span));
|
||||
if (changes.length > 0) {
|
||||
return [createCodeFixAction(fixId, changes, Diagnostics.Remove_unnecessary_await, fixId, Diagnostics.Remove_all_unnecessary_uses_of_await)];
|
||||
}
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => {
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file, diag));
|
||||
},
|
||||
});
|
||||
|
||||
function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan) {
|
||||
const awaitKeyword = tryCast(getTokenAtPosition(sourceFile, span.start), (node): node is AwaitKeywordToken => node.kind === SyntaxKind.AwaitKeyword);
|
||||
const awaitExpression = awaitKeyword && tryCast(awaitKeyword.parent, isAwaitExpression);
|
||||
if (!awaitExpression) {
|
||||
return;
|
||||
}
|
||||
|
||||
let expressionToReplace: Node = awaitExpression;
|
||||
const hasSurroundingParens = isParenthesizedExpression(awaitExpression.parent);
|
||||
if (hasSurroundingParens) {
|
||||
const leftMostExpression = getLeftmostExpression(awaitExpression.expression, /*stopAtCallExpressions*/ false);
|
||||
if (isIdentifier(leftMostExpression)) {
|
||||
const precedingToken = findPrecedingToken(awaitExpression.parent.pos, sourceFile);
|
||||
if (precedingToken && precedingToken.kind !== SyntaxKind.NewKeyword) {
|
||||
expressionToReplace = awaitExpression.parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changeTracker.replaceNode(sourceFile, expressionToReplace, awaitExpression.expression);
|
||||
}
|
||||
}
|
||||
+92
-147
@@ -38,6 +38,7 @@ namespace ts.Completions {
|
||||
InterfaceElementKeywords, // Keywords inside interface body
|
||||
ConstructorParameterKeywords, // Keywords at constructor parameter
|
||||
FunctionLikeBodyKeywords, // Keywords at function like body
|
||||
TypeAssertionKeywords,
|
||||
TypeKeywords,
|
||||
Last = TypeKeywords
|
||||
}
|
||||
@@ -63,7 +64,7 @@ namespace ts.Completions {
|
||||
return getLabelCompletionAtPosition(contextToken.parent);
|
||||
}
|
||||
|
||||
const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, preferences, /*detailsEntryId*/ undefined, host);
|
||||
const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, preferences, /*detailsEntryId*/ undefined);
|
||||
if (!completionData) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -406,10 +407,10 @@ namespace ts.Completions {
|
||||
previousToken: Node | undefined;
|
||||
readonly isJsxInitializer: IsJsxInitializer;
|
||||
}
|
||||
function getSymbolCompletionFromEntryId(program: Program, log: Log, sourceFile: SourceFile, position: number, entryId: CompletionEntryIdentifier, host: LanguageServiceHost
|
||||
function getSymbolCompletionFromEntryId(program: Program, log: Log, sourceFile: SourceFile, position: number, entryId: CompletionEntryIdentifier,
|
||||
): SymbolCompletion | { type: "request", request: Request } | { type: "literal", literal: string | number | PseudoBigInt } | { type: "none" } {
|
||||
const compilerOptions = program.getCompilerOptions();
|
||||
const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, { includeCompletionsForModuleExports: true, includeCompletionsWithInsertText: true }, entryId, host);
|
||||
const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, { includeCompletionsForModuleExports: true, includeCompletionsWithInsertText: true }, entryId);
|
||||
if (!completionData) {
|
||||
return { type: "none" };
|
||||
}
|
||||
@@ -441,7 +442,7 @@ namespace ts.Completions {
|
||||
(symbol.escapedName === InternalSymbolName.ExportEquals))
|
||||
// Name of "export default foo;" is "foo". Name of "export default 0" is the filename converted to camelCase.
|
||||
? firstDefined(symbol.declarations, d => isExportAssignment(d) && isIdentifier(d.expression) ? d.expression.text : undefined)
|
||||
|| codefix.moduleSymbolToValidIdentifier(origin.moduleSymbol, target)
|
||||
|| codefix.moduleSymbolToValidIdentifier(origin.moduleSymbol, target)
|
||||
: symbol.name;
|
||||
}
|
||||
|
||||
@@ -471,7 +472,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
// Compute all the completion symbols again.
|
||||
const symbolCompletion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host);
|
||||
const symbolCompletion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId);
|
||||
switch (symbolCompletion.type) {
|
||||
case "request": {
|
||||
const { request } = symbolCompletion;
|
||||
@@ -556,8 +557,8 @@ namespace ts.Completions {
|
||||
return { sourceDisplay: [textPart(moduleSpecifier)], codeActions: [codeAction] };
|
||||
}
|
||||
|
||||
export function getCompletionEntrySymbol(program: Program, log: Log, sourceFile: SourceFile, position: number, entryId: CompletionEntryIdentifier, host: LanguageServiceHost): Symbol | undefined {
|
||||
const completion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host);
|
||||
export function getCompletionEntrySymbol(program: Program, log: Log, sourceFile: SourceFile, position: number, entryId: CompletionEntryIdentifier): Symbol | undefined {
|
||||
const completion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId);
|
||||
return completion.type === "symbol" ? completion.symbol : undefined;
|
||||
}
|
||||
|
||||
@@ -632,9 +633,9 @@ namespace ts.Completions {
|
||||
// At `,`, treat this as the next argument after the comma.
|
||||
? checker.getContextualTypeForArgumentAtIndex(argInfo.invocation, argInfo.argumentIndex + (previousToken.kind === SyntaxKind.CommaToken ? 1 : 0))
|
||||
: isEqualityOperatorKind(previousToken.kind) && isBinaryExpression(parent) && isEqualityOperatorKind(parent.operatorToken.kind)
|
||||
// completion at `x ===/**/` should be for the right side
|
||||
? checker.getTypeAtLocation(parent.left)
|
||||
: checker.getContextualType(previousToken as Expression);
|
||||
// completion at `x ===/**/` should be for the right side
|
||||
? checker.getTypeAtLocation(parent.left)
|
||||
: checker.getContextualType(previousToken as Expression);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,7 +657,6 @@ namespace ts.Completions {
|
||||
position: number,
|
||||
preferences: Pick<UserPreferences, "includeCompletionsForModuleExports" | "includeCompletionsWithInsertText">,
|
||||
detailsEntryId: CompletionEntryIdentifier | undefined,
|
||||
host: LanguageServiceHost
|
||||
): CompletionData | Request | undefined {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
|
||||
@@ -947,11 +947,13 @@ namespace ts.Completions {
|
||||
// Right of dot member completion list
|
||||
completionKind = CompletionKind.PropertyAccess;
|
||||
|
||||
// Since this is qualified name check its a type node location
|
||||
// Since this is qualified name check it's a type node location
|
||||
const isImportType = isLiteralImportTypeNode(node);
|
||||
const isTypeLocation = insideJsDocTagTypeExpression || (isImportType && !(node as ImportTypeNode).isTypeOf) || isPartOfTypeNode(node.parent);
|
||||
const isTypeLocation = insideJsDocTagTypeExpression
|
||||
|| (isImportType && !(node as ImportTypeNode).isTypeOf)
|
||||
|| isPartOfTypeNode(node.parent)
|
||||
|| isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker);
|
||||
const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration(node);
|
||||
const allowTypeOrValue = isRhsOfImportDeclaration || (!isTypeLocation && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker));
|
||||
if (isEntityName(node) || isImportType) {
|
||||
const isNamespaceName = isModuleDeclaration(node.parent);
|
||||
if (isNamespaceName) isNewIdentifierLocation = true;
|
||||
@@ -968,7 +970,7 @@ namespace ts.Completions {
|
||||
isNamespaceName
|
||||
// At `namespace N.M/**/`, if this is the only declaration of `M`, don't include `M` as a completion.
|
||||
? symbol => !!(symbol.flags & SymbolFlags.Namespace) && !symbol.declarations.every(d => d.parent === node.parent)
|
||||
: allowTypeOrValue ?
|
||||
: isRhsOfImportDeclaration ?
|
||||
// Any kind is allowed when dotting off namespace in internal import equals declaration
|
||||
symbol => isValidTypeAccess(symbol) || isValidValueAccess(symbol) :
|
||||
isTypeLocation ? isValidTypeAccess : isValidValueAccess;
|
||||
@@ -1149,7 +1151,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
if (shouldOfferImportCompletions()) {
|
||||
getSymbolsFromOtherSourceFileExports(symbols, previousToken && isIdentifier(previousToken) ? previousToken.text : "", program.getCompilerOptions().target!, host);
|
||||
getSymbolsFromOtherSourceFileExports(symbols, previousToken && isIdentifier(previousToken) ? previousToken.text : "", program.getCompilerOptions().target!);
|
||||
}
|
||||
filterGlobalCompletion(symbols);
|
||||
}
|
||||
@@ -1181,8 +1183,11 @@ namespace ts.Completions {
|
||||
|
||||
function filterGlobalCompletion(symbols: Symbol[]): void {
|
||||
const isTypeOnly = isTypeOnlyCompletion();
|
||||
const allowTypes = isTypeOnly || !isContextTokenValueLocation(contextToken) && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker);
|
||||
if (isTypeOnly) keywordFilters = KeywordCompletionFilters.TypeKeywords;
|
||||
if (isTypeOnly) {
|
||||
keywordFilters = isTypeAssertion()
|
||||
? KeywordCompletionFilters.TypeAssertionKeywords
|
||||
: KeywordCompletionFilters.TypeKeywords;
|
||||
}
|
||||
|
||||
filterMutate(symbols, symbol => {
|
||||
if (!isSourceFile(location)) {
|
||||
@@ -1198,12 +1203,9 @@ namespace ts.Completions {
|
||||
return !!(symbol.flags & SymbolFlags.Namespace);
|
||||
}
|
||||
|
||||
if (allowTypes) {
|
||||
// Its a type, but you can reach it by namespace.type as well
|
||||
const symbolAllowedAsType = symbolCanBeReferencedAtTypeLocation(symbol);
|
||||
if (symbolAllowedAsType || isTypeOnly) {
|
||||
return symbolAllowedAsType;
|
||||
}
|
||||
if (isTypeOnly) {
|
||||
// It's a type, but you can reach it by namespace.type as well
|
||||
return symbolCanBeReferencedAtTypeLocation(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1212,8 +1214,16 @@ namespace ts.Completions {
|
||||
});
|
||||
}
|
||||
|
||||
function isTypeAssertion(): boolean {
|
||||
return isAssertionExpression(contextToken.parent);
|
||||
}
|
||||
|
||||
function isTypeOnlyCompletion(): boolean {
|
||||
return insideJsDocTagTypeExpression || !isContextTokenValueLocation(contextToken) && (isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken));
|
||||
return insideJsDocTagTypeExpression
|
||||
|| !isContextTokenValueLocation(contextToken) &&
|
||||
(isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker)
|
||||
|| isPartOfTypeNode(location)
|
||||
|| isContextTokenTypeLocation(contextToken));
|
||||
}
|
||||
|
||||
function isContextTokenValueLocation(contextToken: Node) {
|
||||
@@ -1239,6 +1249,10 @@ namespace ts.Completions {
|
||||
case SyntaxKind.AsKeyword:
|
||||
return parentKind === SyntaxKind.AsExpression;
|
||||
|
||||
case SyntaxKind.LessThanToken:
|
||||
return parentKind === SyntaxKind.TypeReference ||
|
||||
parentKind === SyntaxKind.TypeAssertionExpression;
|
||||
|
||||
case SyntaxKind.ExtendsKeyword:
|
||||
return parentKind === SyntaxKind.TypeParameter;
|
||||
}
|
||||
@@ -1255,64 +1269,12 @@ namespace ts.Completions {
|
||||
typeChecker.getExportsOfModule(sym).some(e => symbolCanBeReferencedAtTypeLocation(e, seenModules));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers symbols that can be imported from other files, deduplicating along the way. Symbols can be “duplicates”
|
||||
* if re-exported from another module, e.g. `export { foo } from "./a"`. That syntax creates a fresh symbol, but
|
||||
* it’s just an alias to the first, and both have the same name, so we generally want to filter those aliases out,
|
||||
* if and only if the the first can be imported (it may be excluded due to package.json filtering in
|
||||
* `codefix.forEachExternalModuleToImportFrom`).
|
||||
*
|
||||
* Example. Imagine a chain of node_modules re-exporting one original symbol:
|
||||
*
|
||||
* ```js
|
||||
* node_modules/x/index.js node_modules/y/index.js node_modules/z/index.js
|
||||
* +-----------------------+ +--------------------------+ +--------------------------+
|
||||
* | | | | | |
|
||||
* | export const foo = 0; | <--- | export { foo } from 'x'; | <--- | export { foo } from 'y'; |
|
||||
* | | | | | |
|
||||
* +-----------------------+ +--------------------------+ +--------------------------+
|
||||
* ```
|
||||
*
|
||||
* Also imagine three buckets, which we’ll reference soon:
|
||||
*
|
||||
* ```md
|
||||
* | | | | | |
|
||||
* | **Bucket A** | | **Bucket B** | | **Bucket C** |
|
||||
* | Symbols to | | Aliases to symbols | | Symbols to return |
|
||||
* | definitely | | in Buckets A or C | | if nothing better |
|
||||
* | return | | (don’t return these) | | comes along |
|
||||
* |__________________| |______________________| |___________________|
|
||||
* ```
|
||||
*
|
||||
* We _probably_ want to show `foo` from 'x', but not from 'y' or 'z'. However, if 'x' is not in a package.json, it
|
||||
* will not appear in a `forEachExternalModuleToImportFrom` iteration. Furthermore, the order of iterations is not
|
||||
* guaranteed, as it is host-dependent. Therefore, when presented with the symbol `foo` from module 'y' alone, we
|
||||
* may not be sure whether or not it should go in the list. So, we’ll take the following steps:
|
||||
*
|
||||
* 1. Resolve alias `foo` from 'y' to the export declaration in 'x', get the symbol there, and see if that symbol is
|
||||
* already in Bucket A (symbols we already know will be returned). If it is, put `foo` from 'y' in Bucket B
|
||||
* (symbols that are aliases to symbols in Bucket A). If it’s not, put it in Bucket C.
|
||||
* 2. Next, imagine we see `foo` from module 'z'. Again, we resolve the alias to the nearest export, which is in 'y'.
|
||||
* At this point, if that nearest export from 'y' is in _any_ of the three buckets, we know the symbol in 'z'
|
||||
* should never be returned in the final list, so put it in Bucket B.
|
||||
* 3. Next, imagine we see `foo` from module 'x', the original. Syntactically, it doesn’t look like a re-export, so
|
||||
* we can just check Bucket C to see if we put any aliases to the original in there. If they exist, throw them out.
|
||||
* Put this symbol in Bucket A.
|
||||
* 4. After we’ve iterated through every symbol of every module, any symbol left in Bucket C means that step 3 didn’t
|
||||
* occur for that symbol---that is, the original symbol is not in Bucket A, so we should include the alias. Move
|
||||
* everything from Bucket C to Bucket A.
|
||||
*
|
||||
* Note: Bucket A is passed in as the parameter `symbols` and mutated.
|
||||
*/
|
||||
function getSymbolsFromOtherSourceFileExports(/** Bucket A */ symbols: Symbol[], tokenText: string, target: ScriptTarget, host: LanguageServiceHost): void {
|
||||
function getSymbolsFromOtherSourceFileExports(symbols: Symbol[], tokenText: string, target: ScriptTarget): void {
|
||||
const tokenTextLowerCase = tokenText.toLowerCase();
|
||||
const seenResolvedModules = createMap<true>();
|
||||
/** Bucket B */
|
||||
const aliasesToAlreadyIncludedSymbols = createMap<true>();
|
||||
/** Bucket C */
|
||||
const aliasesToReturnIfOriginalsAreMissing = createMap<{ alias: Symbol, moduleSymbol: Symbol }>();
|
||||
|
||||
codefix.forEachExternalModuleToImportFrom(typeChecker, host, preferences, program.redirectTargetsMap, sourceFile, program.getSourceFiles(), moduleSymbol => {
|
||||
const seenResolvedModules = createMap<true>();
|
||||
|
||||
codefix.forEachExternalModuleToImportFrom(typeChecker, sourceFile, program.getSourceFiles(), moduleSymbol => {
|
||||
// Perf -- ignore other modules if this is a request for details
|
||||
if (detailsEntryId && detailsEntryId.source && stripQuotes(moduleSymbol.name) !== detailsEntryId.source) {
|
||||
return;
|
||||
@@ -1333,59 +1295,33 @@ namespace ts.Completions {
|
||||
symbolToOriginInfoMap[getSymbolId(resolvedModuleSymbol)] = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport: false };
|
||||
}
|
||||
|
||||
for (const symbol of typeChecker.getExportsOfModule(moduleSymbol)) {
|
||||
// If this is `export { _break as break };` (a keyword) -- skip this and prefer the keyword completion.
|
||||
if (some(symbol.declarations, d => isExportSpecifier(d) && !!d.propertyName && isIdentifierANonContextualKeyword(d.name))) {
|
||||
for (let symbol of typeChecker.getExportsOfModule(moduleSymbol)) {
|
||||
// Don't add a completion for a re-export, only for the original.
|
||||
// The actual import fix might end up coming from a re-export -- we don't compute that until getting completion details.
|
||||
// This is just to avoid adding duplicate completion entries.
|
||||
//
|
||||
// If `symbol.parent !== ...`, this is an `export * from "foo"` re-export. Those don't create new symbols.
|
||||
if (typeChecker.getMergedSymbol(symbol.parent!) !== resolvedModuleSymbol
|
||||
|| some(symbol.declarations, d =>
|
||||
// If `!!d.name.originalKeywordKind`, this is `export { _break as break };` -- skip this and prefer the keyword completion.
|
||||
// If `!!d.parent.parent.moduleSpecifier`, this is `export { foo } from "foo"` re-export, which creates a new symbol (thus isn't caught by the first check).
|
||||
isExportSpecifier(d) && (d.propertyName ? isIdentifierANonContextualKeyword(d.name) : !!d.parent.parent.moduleSpecifier))) {
|
||||
continue;
|
||||
}
|
||||
// If `symbol.parent !== moduleSymbol`, this is an `export * from "foo"` re-export. Those don't create new symbols.
|
||||
const isExportStarFromReExport = typeChecker.getMergedSymbol(symbol.parent!) !== resolvedModuleSymbol;
|
||||
// If `!!d.parent.parent.moduleSpecifier`, this is `export { foo } from "foo"` re-export, which creates a new symbol (thus isn't caught by the first check).
|
||||
if (isExportStarFromReExport || some(symbol.declarations, d => isExportSpecifier(d) && !d.propertyName && !!d.parent.parent.moduleSpecifier)) {
|
||||
// Walk the export chain back one module (step 1 or 2 in diagrammed example).
|
||||
// Or, in the case of `export * from "foo"`, `symbol` already points to the original export, so just use that.
|
||||
const nearestExportSymbolId = getSymbolId(isExportStarFromReExport ? symbol : Debug.assertDefined(getNearestExportSymbol(symbol)));
|
||||
const symbolHasBeenSeen = !!symbolToOriginInfoMap[nearestExportSymbolId] || aliasesToAlreadyIncludedSymbols.has(nearestExportSymbolId.toString());
|
||||
if (!symbolHasBeenSeen) {
|
||||
aliasesToReturnIfOriginalsAreMissing.set(nearestExportSymbolId.toString(), { alias: symbol, moduleSymbol });
|
||||
aliasesToAlreadyIncludedSymbols.set(getSymbolId(symbol).toString(), true);
|
||||
}
|
||||
else {
|
||||
// Perf - we know this symbol is an alias to one that’s already covered in `symbols`, so store it here
|
||||
// in case another symbol re-exports this one; that way we can short-circuit as soon as we see this symbol id.
|
||||
addToSeen(aliasesToAlreadyIncludedSymbols, getSymbolId(symbol));
|
||||
}
|
||||
|
||||
const isDefaultExport = symbol.escapedName === InternalSymbolName.Default;
|
||||
if (isDefaultExport) {
|
||||
symbol = getLocalSymbolForExportDefault(symbol) || symbol;
|
||||
}
|
||||
else {
|
||||
// This is not a re-export, so see if we have any aliases pending and remove them (step 3 in diagrammed example)
|
||||
aliasesToReturnIfOriginalsAreMissing.delete(getSymbolId(symbol).toString());
|
||||
pushSymbol(symbol, moduleSymbol);
|
||||
|
||||
const origin: SymbolOriginInfoExport = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport };
|
||||
if (detailsEntryId || stringContainsCharactersInOrder(getSymbolName(symbol, origin, target).toLowerCase(), tokenTextLowerCase)) {
|
||||
symbols.push(symbol);
|
||||
symbolToSortTextMap[getSymbolId(symbol)] = SortText.AutoImportSuggestions;
|
||||
symbolToOriginInfoMap[getSymbolId(symbol)] = origin;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// By this point, any potential duplicates that were actually duplicates have been
|
||||
// removed, so the rest need to be added. (Step 4 in diagrammed example)
|
||||
aliasesToReturnIfOriginalsAreMissing.forEach(({ alias, moduleSymbol }) => pushSymbol(alias, moduleSymbol));
|
||||
|
||||
function pushSymbol(symbol: Symbol, moduleSymbol: Symbol) {
|
||||
const isDefaultExport = symbol.escapedName === InternalSymbolName.Default;
|
||||
if (isDefaultExport) {
|
||||
symbol = getLocalSymbolForExportDefault(symbol) || symbol;
|
||||
}
|
||||
const origin: SymbolOriginInfoExport = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport };
|
||||
if (detailsEntryId || stringContainsCharactersInOrder(getSymbolName(symbol, origin, target).toLowerCase(), tokenTextLowerCase)) {
|
||||
symbols.push(symbol);
|
||||
symbolToSortTextMap[getSymbolId(symbol)] = SortText.AutoImportSuggestions;
|
||||
symbolToOriginInfoMap[getSymbolId(symbol)] = origin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getNearestExportSymbol(fromSymbol: Symbol) {
|
||||
return findAlias(typeChecker, fromSymbol, alias => {
|
||||
return some(alias.declarations, d => isExportSpecifier(d) || !!d.localSymbol);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1513,7 +1449,7 @@ namespace ts.Completions {
|
||||
// 3. at the end of a regular expression (due to trailing flags like '/foo/g').
|
||||
return (isRegularExpressionLiteral(contextToken) || isStringTextContainingNode(contextToken)) && (
|
||||
rangeContainsPositionExclusive(createTextRangeFromSpan(createTextSpanFromNode(contextToken)), position) ||
|
||||
position === contextToken.end && (!!contextToken.isUnterminated || isRegularExpressionLiteral(contextToken)));
|
||||
position === contextToken.end && (!!contextToken.isUnterminated || isRegularExpressionLiteral(contextToken)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1617,7 +1553,7 @@ namespace ts.Completions {
|
||||
* Relevant symbols are stored in the captured 'symbols' variable.
|
||||
*/
|
||||
function tryGetClassLikeCompletionSymbols(): GlobalsSearch {
|
||||
const decl = tryGetObjectTypeDeclarationCompletionContainer(sourceFile, contextToken, location);
|
||||
const decl = tryGetObjectTypeDeclarationCompletionContainer(sourceFile, contextToken, location, position);
|
||||
if (!decl) return GlobalsSearch.Continue;
|
||||
|
||||
// We're looking up possible property names from parent type.
|
||||
@@ -2023,8 +1959,8 @@ namespace ts.Completions {
|
||||
|
||||
return baseSymbols.filter(propertySymbol =>
|
||||
!existingMemberNames.has(propertySymbol.escapedName) &&
|
||||
!!propertySymbol.declarations &&
|
||||
!(getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.Private));
|
||||
!!propertySymbol.declarations &&
|
||||
!(getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.Private));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2126,16 +2062,20 @@ namespace ts.Completions {
|
||||
case KeywordCompletionFilters.None:
|
||||
return false;
|
||||
case KeywordCompletionFilters.All:
|
||||
return kind === SyntaxKind.AsyncKeyword || SyntaxKind.AwaitKeyword || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind) || kind === SyntaxKind.DeclareKeyword || kind === SyntaxKind.ModuleKeyword
|
||||
return isFunctionLikeBodyKeyword(kind)
|
||||
|| kind === SyntaxKind.DeclareKeyword
|
||||
|| kind === SyntaxKind.ModuleKeyword
|
||||
|| isTypeKeyword(kind) && kind !== SyntaxKind.UndefinedKeyword;
|
||||
case KeywordCompletionFilters.FunctionLikeBodyKeywords:
|
||||
return isFunctionLikeBodyKeyword(kind);
|
||||
case KeywordCompletionFilters.ClassElementKeywords:
|
||||
return isClassMemberCompletionKeyword(kind);
|
||||
case KeywordCompletionFilters.InterfaceElementKeywords:
|
||||
return isInterfaceOrTypeLiteralCompletionKeyword(kind);
|
||||
case KeywordCompletionFilters.ConstructorParameterKeywords:
|
||||
return isParameterPropertyModifier(kind);
|
||||
case KeywordCompletionFilters.FunctionLikeBodyKeywords:
|
||||
return isFunctionLikeBodyKeyword(kind);
|
||||
case KeywordCompletionFilters.TypeAssertionKeywords:
|
||||
return isTypeKeyword(kind) || kind === SyntaxKind.ConstKeyword;
|
||||
case KeywordCompletionFilters.TypeKeywords:
|
||||
return isTypeKeyword(kind);
|
||||
default:
|
||||
@@ -2196,7 +2136,9 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
function isFunctionLikeBodyKeyword(kind: SyntaxKind) {
|
||||
return kind === SyntaxKind.AsyncKeyword || kind === SyntaxKind.AwaitKeyword || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind);
|
||||
return kind === SyntaxKind.AsyncKeyword
|
||||
|| kind === SyntaxKind.AwaitKeyword
|
||||
|| !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind);
|
||||
}
|
||||
|
||||
function keywordForNode(node: Node): SyntaxKind {
|
||||
@@ -2234,7 +2176,7 @@ namespace ts.Completions {
|
||||
* Returns the immediate owning class declaration of a context token,
|
||||
* on the condition that one exists and that the context implies completion should be given.
|
||||
*/
|
||||
function tryGetObjectTypeDeclarationCompletionContainer(sourceFile: SourceFile, contextToken: Node | undefined, location: Node): ObjectTypeDeclaration | undefined {
|
||||
function tryGetObjectTypeDeclarationCompletionContainer(sourceFile: SourceFile, contextToken: Node | undefined, location: Node, position: number): ObjectTypeDeclaration | undefined {
|
||||
// class c { method() { } | method2() { } }
|
||||
switch (location.kind) {
|
||||
case SyntaxKind.SyntaxList:
|
||||
@@ -2244,9 +2186,15 @@ namespace ts.Completions {
|
||||
if (cls && !findChildOfKind(cls, SyntaxKind.CloseBraceToken, sourceFile)) {
|
||||
return cls;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.Identifier: // class c extends React.Component { a: () => 1\n compon| }
|
||||
if (isFromObjectTypeDeclaration(location)) {
|
||||
return findAncestor(location, isObjectTypeDeclaration);
|
||||
}
|
||||
}
|
||||
|
||||
if (!contextToken) return undefined;
|
||||
|
||||
switch (contextToken.kind) {
|
||||
case SyntaxKind.SemicolonToken: // class c {getValue(): number; | }
|
||||
case SyntaxKind.CloseBraceToken: // class c { method() { } | }
|
||||
@@ -2258,7 +2206,13 @@ namespace ts.Completions {
|
||||
case SyntaxKind.CommaToken: // class c {getValue(): number, | }
|
||||
return tryCast(contextToken.parent, isObjectTypeDeclaration);
|
||||
default:
|
||||
if (!isFromObjectTypeDeclaration(contextToken)) return undefined;
|
||||
if (!isFromObjectTypeDeclaration(contextToken)) {
|
||||
// class c extends React.Component { a: () => 1\n| }
|
||||
if (getLineAndCharacterOfPosition(sourceFile, contextToken.getEnd()).line !== getLineAndCharacterOfPosition(sourceFile, position).line && isObjectTypeDeclaration(location)) {
|
||||
return location;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
const isValidKeyword = isClassLike(contextToken.parent.parent) ? isClassMemberCompletionKeyword : isInterfaceOrTypeLiteralCompletionKeyword;
|
||||
return (isValidKeyword(contextToken.kind) || contextToken.kind === SyntaxKind.AsteriskToken || isIdentifier(contextToken) && isValidKeyword(stringToToken(contextToken.text)!)) // TODO: GH#18217
|
||||
? contextToken.parent.parent as ObjectTypeDeclaration : undefined;
|
||||
@@ -2295,13 +2249,4 @@ namespace ts.Completions {
|
||||
function binaryExpressionMayBeOpenTag({ left }: BinaryExpression): boolean {
|
||||
return nodeIsMissing(left);
|
||||
}
|
||||
|
||||
function findAlias(typeChecker: TypeChecker, symbol: Symbol, predicate: (symbol: Symbol) => boolean): Symbol | undefined {
|
||||
let currentAlias: Symbol | undefined = symbol;
|
||||
while (currentAlias.flags & SymbolFlags.Alias && (currentAlias = typeChecker.getImmediateAliasedSymbol(currentAlias))) {
|
||||
if (predicate(currentAlias)) {
|
||||
return currentAlias;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,6 +490,9 @@ namespace ts.formatting {
|
||||
else if (SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(parent, node, startLine, sourceFile)) {
|
||||
return { indentation: parentDynamicIndentation.getIndentation(), delta };
|
||||
}
|
||||
else if (SmartIndenter.argumentStartsOnSameLineAsPreviousArgument(parent, node, startLine, sourceFile)) {
|
||||
return { indentation: parentDynamicIndentation.getIndentation(), delta };
|
||||
}
|
||||
else {
|
||||
return { indentation: parentDynamicIndentation.getIndentation() + parentDynamicIndentation.getDelta(node), delta };
|
||||
}
|
||||
|
||||
@@ -322,6 +322,25 @@ namespace ts.formatting {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function argumentStartsOnSameLineAsPreviousArgument(parent: Node, child: TextRangeWithKind, childStartLine: number, sourceFile: SourceFileLike): boolean {
|
||||
if (isCallOrNewExpression(parent)) {
|
||||
if (!parent.arguments) return false;
|
||||
|
||||
const currentNode = Debug.assertDefined(find(parent.arguments, arg => arg.pos === child.pos));
|
||||
const currentIndex = parent.arguments.indexOf(currentNode);
|
||||
if (currentIndex === 0) return false; // Can't look at previous node if first
|
||||
|
||||
const previousNode = parent.arguments[currentIndex - 1];
|
||||
const lineOfPreviousNode = getLineAndCharacterOfPosition(sourceFile, previousNode.getEnd()).line;
|
||||
|
||||
if (childStartLine === lineOfPreviousNode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getContainingList(node: Node, sourceFile: SourceFile): NodeArray<Node> | undefined {
|
||||
return node.parent && getListByRange(node.getStart(sourceFile), node.getEnd(), node.parent, sourceFile);
|
||||
}
|
||||
|
||||
@@ -198,6 +198,8 @@ namespace ts.OutliningElementsCollector {
|
||||
return spanForObjectOrArrayLiteral(n, SyntaxKind.OpenBracketToken);
|
||||
case SyntaxKind.JsxElement:
|
||||
return spanForJSXElement(<JsxElement>n);
|
||||
case SyntaxKind.JsxFragment:
|
||||
return spanForJSXFragment(<JsxFragment>n);
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
case SyntaxKind.JsxOpeningElement:
|
||||
return spanForJSXAttributes((<JsxOpeningLikeElement>n).attributes);
|
||||
@@ -210,6 +212,12 @@ namespace ts.OutliningElementsCollector {
|
||||
return createOutliningSpan(textSpan, OutliningSpanKind.Code, textSpan, /*autoCollapse*/ false, bannerText);
|
||||
}
|
||||
|
||||
function spanForJSXFragment(node: JsxFragment): OutliningSpan | undefined {
|
||||
const textSpan = createTextSpanFromBounds(node.openingFragment.getStart(sourceFile), node.closingFragment.getEnd());
|
||||
const bannerText = "<>...</>";
|
||||
return createOutliningSpan(textSpan, OutliningSpanKind.Code, textSpan, /*autoCollapse*/ false, bannerText);
|
||||
}
|
||||
|
||||
function spanForJSXAttributes(node: JsxAttributes): OutliningSpan | undefined {
|
||||
if (node.properties.length === 0) {
|
||||
return undefined;
|
||||
|
||||
@@ -1453,7 +1453,7 @@ namespace ts {
|
||||
|
||||
function getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string): Symbol | undefined {
|
||||
synchronizeHostData();
|
||||
return Completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source }, host);
|
||||
return Completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source });
|
||||
}
|
||||
|
||||
function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo | undefined {
|
||||
|
||||
@@ -627,6 +627,30 @@ namespace ts.Completions.StringCompletions {
|
||||
}
|
||||
}
|
||||
|
||||
function findPackageJsons(directory: string, host: LanguageServiceHost): string[] {
|
||||
const paths: string[] = [];
|
||||
forEachAncestorDirectory(directory, ancestor => {
|
||||
const currentConfigPath = findConfigFile(ancestor, (f) => tryFileExists(host, f), "package.json");
|
||||
if (!currentConfigPath) {
|
||||
return true; // break out
|
||||
}
|
||||
paths.push(currentConfigPath);
|
||||
});
|
||||
return paths;
|
||||
}
|
||||
|
||||
function findPackageJson(directory: string, host: LanguageServiceHost): string | undefined {
|
||||
let packageJson: string | undefined;
|
||||
forEachAncestorDirectory(directory, ancestor => {
|
||||
if (ancestor === "node_modules") return true;
|
||||
packageJson = findConfigFile(ancestor, (f) => tryFileExists(host, f), "package.json");
|
||||
if (packageJson) {
|
||||
return true; // break out
|
||||
}
|
||||
});
|
||||
return packageJson;
|
||||
}
|
||||
|
||||
function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string): ReadonlyArray<string> {
|
||||
if (!host.readFile || !host.fileExists) return emptyArray;
|
||||
|
||||
@@ -682,6 +706,31 @@ namespace ts.Completions.StringCompletions {
|
||||
|
||||
const nodeModulesDependencyKeys: ReadonlyArray<string> = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
|
||||
|
||||
function tryGetDirectories(host: LanguageServiceHost, directoryName: string): string[] {
|
||||
return tryIOAndConsumeErrors(host, host.getDirectories, directoryName) || [];
|
||||
}
|
||||
|
||||
function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>): ReadonlyArray<string> {
|
||||
return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include) || emptyArray;
|
||||
}
|
||||
|
||||
function tryFileExists(host: LanguageServiceHost, path: string): boolean {
|
||||
return tryIOAndConsumeErrors(host, host.fileExists, path);
|
||||
}
|
||||
|
||||
function tryDirectoryExists(host: LanguageServiceHost, path: string): boolean {
|
||||
return tryAndIgnoreErrors(() => directoryProbablyExists(path, host)) || false;
|
||||
}
|
||||
|
||||
function tryIOAndConsumeErrors<T>(host: LanguageServiceHost, toApply: ((...a: any[]) => T) | undefined, ...args: any[]) {
|
||||
return tryAndIgnoreErrors(() => toApply && toApply.apply(host, args));
|
||||
}
|
||||
|
||||
function tryAndIgnoreErrors<T>(cb: () => T): T | undefined {
|
||||
try { return cb(); }
|
||||
catch { return undefined; }
|
||||
}
|
||||
|
||||
function containsSlash(fragment: string) {
|
||||
return stringContains(fragment, directorySeparator);
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
"codefixes/useDefaultImport.ts",
|
||||
"codefixes/fixAddModuleReferTypeMissingTypeof.ts",
|
||||
"codefixes/convertToMappedObjectType.ts",
|
||||
"codefixes/removeUnnecessaryAwait.ts",
|
||||
"refactors/convertExport.ts",
|
||||
"refactors/convertImport.ts",
|
||||
"refactors/extractSymbol.ts",
|
||||
|
||||
@@ -892,7 +892,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface CompletionInfo {
|
||||
/** Not true for all glboal completions. This will be true if the enclosing scope matches a few syntax kinds. See `isSnippetScope`. */
|
||||
/** Not true for all global completions. This will be true if the enclosing scope matches a few syntax kinds. See `isSnippetScope`. */
|
||||
isGlobalCompletion: boolean;
|
||||
isMemberCompletion: boolean;
|
||||
|
||||
|
||||
@@ -1224,6 +1224,7 @@ namespace ts {
|
||||
SyntaxKind.NullKeyword,
|
||||
SyntaxKind.NumberKeyword,
|
||||
SyntaxKind.ObjectKeyword,
|
||||
SyntaxKind.ReadonlyKeyword,
|
||||
SyntaxKind.StringKeyword,
|
||||
SyntaxKind.SymbolKeyword,
|
||||
SyntaxKind.TrueKeyword,
|
||||
@@ -1636,23 +1637,6 @@ namespace ts {
|
||||
return !!location.parent && isImportOrExportSpecifier(location.parent) && location.parent.propertyName === location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip off existed single quotes or double quotes from a given string
|
||||
*
|
||||
* @return non-quoted string
|
||||
*/
|
||||
export function stripQuotes(name: string) {
|
||||
const length = name.length;
|
||||
if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && startsWithQuote(name)) {
|
||||
return name.substring(1, length - 1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
export function startsWithQuote(name: string): boolean {
|
||||
return isSingleOrDoubleQuote(name.charCodeAt(0));
|
||||
}
|
||||
|
||||
export function scriptKindIs(fileName: string, host: LanguageServiceHost, ...scriptKinds: ScriptKind[]): boolean {
|
||||
const scriptKind = getScriptKind(fileName, host);
|
||||
return some(scriptKinds, k => k === scriptKind);
|
||||
@@ -1751,8 +1735,8 @@ namespace ts {
|
||||
|
||||
function getSynthesizedDeepCloneWorker<T extends Node>(node: T, renameMap?: Map<Identifier>, checker?: TypeChecker, callback?: (originalNode: Node, clone: Node) => any): T {
|
||||
const visited = (renameMap || checker || callback) ?
|
||||
visitEachChild(node, wrapper, nullTransformationContext) :
|
||||
visitEachChild(node, getSynthesizedDeepClone, nullTransformationContext);
|
||||
visitEachChild(node, wrapper, nullTransformationContext) :
|
||||
visitEachChild(node, getSynthesizedDeepClone, nullTransformationContext);
|
||||
|
||||
if (visited === node) {
|
||||
// This only happens for leaf nodes - internal nodes always see their children change.
|
||||
@@ -2038,53 +2022,4 @@ namespace ts {
|
||||
// If even 2/5 places have a semicolon, the user probably wants semicolons
|
||||
return withSemicolon / withoutSemicolon > 1 / nStatementsToObserve;
|
||||
}
|
||||
|
||||
export function tryGetDirectories(host: LanguageServiceHost, directoryName: string): string[] {
|
||||
return tryIOAndConsumeErrors(host, host.getDirectories, directoryName) || [];
|
||||
}
|
||||
|
||||
export function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>): ReadonlyArray<string> {
|
||||
return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include) || emptyArray;
|
||||
}
|
||||
|
||||
export function tryFileExists(host: LanguageServiceHost, path: string): boolean {
|
||||
return tryIOAndConsumeErrors(host, host.fileExists, path);
|
||||
}
|
||||
|
||||
export function tryDirectoryExists(host: LanguageServiceHost, path: string): boolean {
|
||||
return tryAndIgnoreErrors(() => directoryProbablyExists(path, host)) || false;
|
||||
}
|
||||
|
||||
export function tryAndIgnoreErrors<T>(cb: () => T): T | undefined {
|
||||
try { return cb(); }
|
||||
catch { return undefined; }
|
||||
}
|
||||
|
||||
export function tryIOAndConsumeErrors<T>(host: LanguageServiceHost, toApply: ((...a: any[]) => T) | undefined, ...args: any[]) {
|
||||
return tryAndIgnoreErrors(() => toApply && toApply.apply(host, args));
|
||||
}
|
||||
|
||||
export function findPackageJsons(directory: string, host: LanguageServiceHost): string[] {
|
||||
const paths: string[] = [];
|
||||
forEachAncestorDirectory(directory, ancestor => {
|
||||
const currentConfigPath = findConfigFile(ancestor, (f) => tryFileExists(host, f), "package.json");
|
||||
if (!currentConfigPath) {
|
||||
return true; // break out
|
||||
}
|
||||
paths.push(currentConfigPath);
|
||||
});
|
||||
return paths;
|
||||
}
|
||||
|
||||
export function findPackageJson(directory: string, host: LanguageServiceHost): string | undefined {
|
||||
let packageJson: string | undefined;
|
||||
forEachAncestorDirectory(directory, ancestor => {
|
||||
if (ancestor === "node_modules") return true;
|
||||
packageJson = findConfigFile(ancestor, (f) => tryFileExists(host, f), "package.json");
|
||||
if (packageJson) {
|
||||
return true; // break out
|
||||
}
|
||||
});
|
||||
return packageJson;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,9 @@
|
||||
"unittests/config/tsconfigParsing.ts",
|
||||
"unittests/evaluation/asyncArrow.ts",
|
||||
"unittests/evaluation/asyncGenerator.ts",
|
||||
"unittests/evaluation/awaiter.ts",
|
||||
"unittests/evaluation/forAwaitOf.ts",
|
||||
"unittests/evaluation/objectRest.ts",
|
||||
"unittests/services/cancellableLanguageServiceOperations.ts",
|
||||
"unittests/services/colorization.ts",
|
||||
"unittests/services/convertToAsyncFunction.ts",
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
describe("unittests:: evaluation:: awaiter", () => {
|
||||
// NOTE: This could break if the ECMAScript spec ever changes the timing behavior for Promises (again)
|
||||
it("await (es5)", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
async function a(msg: string) {
|
||||
await Promise.resolve();
|
||||
output.push(msg);
|
||||
}
|
||||
function b(msg: string) {
|
||||
return Promise.resolve().then(() => {
|
||||
output.push(msg);
|
||||
});
|
||||
}
|
||||
export const output: string[] = [];
|
||||
export async function main() {
|
||||
const p1 = a('1');
|
||||
const p2 = b('2');
|
||||
await Promise.all([p1, p2]);
|
||||
}
|
||||
`);
|
||||
await result.main();
|
||||
assert.deepEqual(result.output, ["1", "2"]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
describe("unittests:: evaluation:: objectRest", () => {
|
||||
// https://github.com/microsoft/TypeScript/issues/31469
|
||||
it("side effects in property assignment", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
const k = { a: 1, b: 2 };
|
||||
const o = { a: 3, ...k, b: k.a++ };
|
||||
export const output = o;
|
||||
`);
|
||||
assert.deepEqual(result.output, { a: 1, b: 1 });
|
||||
});
|
||||
it("side effects in during spread", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
const k = { a: 1, get b() { l = { c: 9 }; return 2; } };
|
||||
let l = { c: 3 };
|
||||
const o = { ...k, ...l };
|
||||
export const output = o;
|
||||
`);
|
||||
assert.deepEqual(result.output, { a: 1, b: 2, c: 9 });
|
||||
});
|
||||
it("trailing literal-valued object-literal", async () => {
|
||||
const result = evaluator.evaluateTypeScript(`
|
||||
const k = { a: 1 }
|
||||
const o = { ...k, ...{ b: 2 } };
|
||||
export const output = o;
|
||||
`);
|
||||
assert.deepEqual(result.output, { a: 1, b: 2 });
|
||||
});
|
||||
});
|
||||
@@ -31,3 +31,18 @@ describe("Public APIs", () => {
|
||||
verifyApi("tsserverlibrary.d.ts");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Public APIs:: token to string", () => {
|
||||
function assertDefinedTokenToString(initial: ts.SyntaxKind, last: ts.SyntaxKind) {
|
||||
for (let t = initial; t <= last; t++) {
|
||||
assert.isDefined(ts.tokenToString(t), `Expected tokenToString defined for ${ts.Debug.formatSyntaxKind(t)}`);
|
||||
}
|
||||
}
|
||||
|
||||
it("for punctuations", () => {
|
||||
assertDefinedTokenToString(ts.SyntaxKind.FirstPunctuation, ts.SyntaxKind.LastPunctuation);
|
||||
});
|
||||
it("for keywords", () => {
|
||||
assertDefinedTokenToString(ts.SyntaxKind.FirstKeyword, ts.SyntaxKind.LastKeyword);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1467,5 +1467,22 @@ var x = 10;`
|
||||
openFilesForSession([{ file, projectRootPath }], session);
|
||||
}
|
||||
});
|
||||
|
||||
it("assert when removing project", () => {
|
||||
const host = createServerHost([commonFile1, commonFile2, libFile]);
|
||||
const service = createProjectService(host);
|
||||
service.openClientFile(commonFile1.path);
|
||||
const project = service.inferredProjects[0];
|
||||
checkProjectActualFiles(project, [commonFile1.path, libFile.path]);
|
||||
// Intentionally create scriptinfo and attach it to project
|
||||
const info = service.getOrCreateScriptInfoForNormalizedPath(commonFile2.path as server.NormalizedPath, /*openedByClient*/ false)!;
|
||||
info.attachToProject(project);
|
||||
try {
|
||||
service.applyChangesInOpenFiles(/*openFiles*/ undefined, /*changedFiles*/ undefined, [commonFile1.path]);
|
||||
}
|
||||
catch (e) {
|
||||
assert.isTrue(e.message.indexOf("Debug Failure. False expression: Found script Info still attached to project") === 0);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace ts.projectSystem {
|
||||
import validatePackageName = JsTyping.validatePackageName;
|
||||
import PackageNameValidationResult = JsTyping.PackageNameValidationResult;
|
||||
import NameValidationResult = JsTyping.NameValidationResult;
|
||||
|
||||
interface InstallerParams {
|
||||
globalTypingsCacheLocation?: string;
|
||||
@@ -948,7 +948,8 @@ namespace ts.projectSystem {
|
||||
path: "/a/b/app.js",
|
||||
content: `
|
||||
import * as fs from "fs";
|
||||
import * as commander from "commander";`
|
||||
import * as commander from "commander";
|
||||
import * as component from "@ember/component";`
|
||||
};
|
||||
const cachePath = "/a/cache";
|
||||
const node = {
|
||||
@@ -959,14 +960,19 @@ namespace ts.projectSystem {
|
||||
path: cachePath + "/node_modules/@types/commander/index.d.ts",
|
||||
content: "export let y: string"
|
||||
};
|
||||
const emberComponentDirectory = "ember__component";
|
||||
const emberComponent = {
|
||||
path: `${cachePath}/node_modules/@types/${emberComponentDirectory}/index.d.ts`,
|
||||
content: "export let x: number"
|
||||
};
|
||||
const host = createServerHost([file]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("node", "commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/node", "@types/commander"];
|
||||
const typingFiles = [node, commander];
|
||||
const installedTypings = ["@types/node", "@types/commander", `@types/${emberComponentDirectory}`];
|
||||
const typingFiles = [node, commander, emberComponent];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
})();
|
||||
@@ -980,9 +986,10 @@ namespace ts.projectSystem {
|
||||
|
||||
assert.isTrue(host.fileExists(node.path), "typings for 'node' should be created");
|
||||
assert.isTrue(host.fileExists(commander.path), "typings for 'commander' should be created");
|
||||
assert.isTrue(host.fileExists(emberComponent.path), "typings for 'commander' should be created");
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkProjectActualFiles(service.inferredProjects[0], [file.path, node.path, commander.path]);
|
||||
checkProjectActualFiles(service.inferredProjects[0], [file.path, node.path, commander.path, emberComponent.path]);
|
||||
});
|
||||
|
||||
it("should redo resolution that resolved to '.js' file after typings are installed", () => {
|
||||
@@ -1263,21 +1270,44 @@ namespace ts.projectSystem {
|
||||
for (let i = 0; i < 8; i++) {
|
||||
packageName += packageName;
|
||||
}
|
||||
assert.equal(validatePackageName(packageName), PackageNameValidationResult.NameTooLong);
|
||||
assert.equal(validatePackageName(packageName), NameValidationResult.NameTooLong);
|
||||
});
|
||||
it("name cannot start with dot", () => {
|
||||
assert.equal(validatePackageName(".foo"), PackageNameValidationResult.NameStartsWithDot);
|
||||
it("package name cannot start with dot", () => {
|
||||
assert.equal(validatePackageName(".foo"), NameValidationResult.NameStartsWithDot);
|
||||
});
|
||||
it("name cannot start with underscore", () => {
|
||||
assert.equal(validatePackageName("_foo"), PackageNameValidationResult.NameStartsWithUnderscore);
|
||||
it("package name cannot start with underscore", () => {
|
||||
assert.equal(validatePackageName("_foo"), NameValidationResult.NameStartsWithUnderscore);
|
||||
});
|
||||
it("scoped packages not supported", () => {
|
||||
assert.equal(validatePackageName("@scope/bar"), PackageNameValidationResult.ScopedPackagesNotSupported);
|
||||
it("package non URI safe characters are not supported", () => {
|
||||
assert.equal(validatePackageName(" scope "), NameValidationResult.NameContainsNonURISafeCharacters);
|
||||
assert.equal(validatePackageName("; say ‘Hello from TypeScript!’ #"), NameValidationResult.NameContainsNonURISafeCharacters);
|
||||
assert.equal(validatePackageName("a/b/c"), NameValidationResult.NameContainsNonURISafeCharacters);
|
||||
});
|
||||
it("non URI safe characters are not supported", () => {
|
||||
assert.equal(validatePackageName(" scope "), PackageNameValidationResult.NameContainsNonURISafeCharacters);
|
||||
assert.equal(validatePackageName("; say ‘Hello from TypeScript!’ #"), PackageNameValidationResult.NameContainsNonURISafeCharacters);
|
||||
assert.equal(validatePackageName("a/b/c"), PackageNameValidationResult.NameContainsNonURISafeCharacters);
|
||||
it("scoped package name is supported", () => {
|
||||
assert.equal(validatePackageName("@scope/bar"), NameValidationResult.Ok);
|
||||
});
|
||||
it("scoped name in scoped package name cannot start with dot", () => {
|
||||
assert.deepEqual(validatePackageName("@.scope/bar"), { name: ".scope", isScopeName: true, result: NameValidationResult.NameStartsWithDot });
|
||||
assert.deepEqual(validatePackageName("@.scope/.bar"), { name: ".scope", isScopeName: true, result: NameValidationResult.NameStartsWithDot });
|
||||
});
|
||||
it("scope name in scoped package name cannot start with underscore", () => {
|
||||
assert.deepEqual(validatePackageName("@_scope/bar"), { name: "_scope", isScopeName: true, result: NameValidationResult.NameStartsWithUnderscore });
|
||||
assert.deepEqual(validatePackageName("@_scope/_bar"), { name: "_scope", isScopeName: true, result: NameValidationResult.NameStartsWithUnderscore });
|
||||
});
|
||||
it("scope name in scoped package name with non URI safe characters are not supported", () => {
|
||||
assert.deepEqual(validatePackageName("@ scope /bar"), { name: " scope ", isScopeName: true, result: NameValidationResult.NameContainsNonURISafeCharacters });
|
||||
assert.deepEqual(validatePackageName("@; say ‘Hello from TypeScript!’ #/bar"), { name: "; say ‘Hello from TypeScript!’ #", isScopeName: true, result: NameValidationResult.NameContainsNonURISafeCharacters });
|
||||
assert.deepEqual(validatePackageName("@ scope / bar "), { name: " scope ", isScopeName: true, result: NameValidationResult.NameContainsNonURISafeCharacters });
|
||||
});
|
||||
it("package name in scoped package name cannot start with dot", () => {
|
||||
assert.deepEqual(validatePackageName("@scope/.bar"), { name: ".bar", isScopeName: false, result: NameValidationResult.NameStartsWithDot });
|
||||
});
|
||||
it("package name in scoped package name cannot start with underscore", () => {
|
||||
assert.deepEqual(validatePackageName("@scope/_bar"), { name: "_bar", isScopeName: false, result: NameValidationResult.NameStartsWithUnderscore });
|
||||
});
|
||||
it("package name in scoped package name with non URI safe characters are not supported", () => {
|
||||
assert.deepEqual(validatePackageName("@scope/ bar "), { name: " bar ", isScopeName: false, result: NameValidationResult.NameContainsNonURISafeCharacters });
|
||||
assert.deepEqual(validatePackageName("@scope/; say ‘Hello from TypeScript!’ #"), { name: "; say ‘Hello from TypeScript!’ #", isScopeName: false, result: NameValidationResult.NameContainsNonURISafeCharacters });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1309,7 +1339,7 @@ namespace ts.projectSystem {
|
||||
projectService.openClientFile(f1.path);
|
||||
|
||||
installer.checkPendingCommands(/*expectedCount*/ 0);
|
||||
assert.isTrue(messages.indexOf("Package name '; say ‘Hello from TypeScript!’ #' contains non URI safe characters") > 0, "should find package with invalid name");
|
||||
assert.isTrue(messages.indexOf("'; say ‘Hello from TypeScript!’ #':: Package name '; say ‘Hello from TypeScript!’ #' contains non URI safe characters") > 0, "should find package with invalid name");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ namespace ts.server {
|
||||
isKnownTypesPackageName(name: string): boolean {
|
||||
// We want to avoid looking this up in the registry as that is expensive. So first check that it's actually an NPM package.
|
||||
const validationResult = JsTyping.validatePackageName(name);
|
||||
if (validationResult !== JsTyping.PackageNameValidationResult.Ok) {
|
||||
if (validationResult !== JsTyping.NameValidationResult.Ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -268,27 +268,28 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
|
||||
private filterTypings(typingsToInstall: ReadonlyArray<string>): ReadonlyArray<string> {
|
||||
return typingsToInstall.filter(typing => {
|
||||
if (this.missingTypingsSet.get(typing)) {
|
||||
if (this.log.isEnabled()) this.log.writeLine(`'${typing}' is in missingTypingsSet - skipping...`);
|
||||
return false;
|
||||
return mapDefined(typingsToInstall, typing => {
|
||||
const typingKey = mangleScopedPackageName(typing);
|
||||
if (this.missingTypingsSet.get(typingKey)) {
|
||||
if (this.log.isEnabled()) this.log.writeLine(`'${typing}':: '${typingKey}' is in missingTypingsSet - skipping...`);
|
||||
return undefined;
|
||||
}
|
||||
const validationResult = JsTyping.validatePackageName(typing);
|
||||
if (validationResult !== JsTyping.PackageNameValidationResult.Ok) {
|
||||
if (validationResult !== JsTyping.NameValidationResult.Ok) {
|
||||
// add typing name to missing set so we won't process it again
|
||||
this.missingTypingsSet.set(typing, true);
|
||||
this.missingTypingsSet.set(typingKey, true);
|
||||
if (this.log.isEnabled()) this.log.writeLine(JsTyping.renderPackageNameValidationFailure(validationResult, typing));
|
||||
return false;
|
||||
return undefined;
|
||||
}
|
||||
if (!this.typesRegistry.has(typing)) {
|
||||
if (this.log.isEnabled()) this.log.writeLine(`Entry for package '${typing}' does not exist in local types registry - skipping...`);
|
||||
return false;
|
||||
if (!this.typesRegistry.has(typingKey)) {
|
||||
if (this.log.isEnabled()) this.log.writeLine(`'${typing}':: Entry for package '${typingKey}' does not exist in local types registry - skipping...`);
|
||||
return undefined;
|
||||
}
|
||||
if (this.packageNameToTypingLocation.get(typing) && JsTyping.isTypingUpToDate(this.packageNameToTypingLocation.get(typing)!, this.typesRegistry.get(typing)!)) {
|
||||
if (this.log.isEnabled()) this.log.writeLine(`'${typing}' already has an up-to-date typing - skipping...`);
|
||||
return false;
|
||||
if (this.packageNameToTypingLocation.get(typingKey) && JsTyping.isTypingUpToDate(this.packageNameToTypingLocation.get(typingKey)!, this.typesRegistry.get(typingKey)!)) {
|
||||
if (this.log.isEnabled()) this.log.writeLine(`'${typing}':: '${typingKey}' already has an up-to-date typing - skipping...`);
|
||||
return undefined;
|
||||
}
|
||||
return true;
|
||||
return typingKey;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user