Merge branch 'master' into isInMultiLineComment

This commit is contained in:
Arthur Ozga
2017-08-16 17:57:04 -07:00
3937 changed files with 15008 additions and 8816 deletions
+7
View File
@@ -18,6 +18,13 @@
"problemMatcher": [
"$tsc"
]
},
{
"taskName": "tests",
"showOutput": "silent",
"problemMatcher": [
"$tsc"
]
}
]
}
+6 -21
View File
@@ -133,6 +133,7 @@ var harnessSources = harnessCoreSources.concat([
"projectErrors.ts",
"matchFiles.ts",
"initializeTSConfig.ts",
"extractMethods.ts",
"printer.ts",
"textChanges.ts",
"telemetry.ts",
@@ -533,7 +534,6 @@ var tscFile = path.join(builtLocalDirectory, compilerFilename);
compileFile(tscFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false);
var servicesFile = path.join(builtLocalDirectory, "typescriptServices.js");
var servicesFileInBrowserTest = path.join(builtLocalDirectory, "typescriptServicesInBrowserTest.js");
var standaloneDefinitionsFile = path.join(builtLocalDirectory, "typescriptServices.d.ts");
var nodePackageFile = path.join(builtLocalDirectory, "typescript.js");
var nodeDefinitionsFile = path.join(builtLocalDirectory, "typescript.d.ts");
@@ -572,22 +572,6 @@ compileFile(servicesFile, servicesSources, [builtLocalDirectory, copyright].conc
fs.writeFileSync(nodeStandaloneDefinitionsFile, nodeStandaloneDefinitionsFileContents);
});
compileFile(
servicesFileInBrowserTest,
servicesSources,
[builtLocalDirectory, copyright].concat(servicesSources),
/*prefixes*/[copyright],
/*useBuiltCompiler*/ true,
{
noOutFile: false,
generateDeclarations: true,
preserveConstEnums: true,
keepComments: true,
noResolve: false,
stripInternal: true,
inlineSourceMap: true
});
file(typescriptServicesDts, [servicesFile]);
var cancellationTokenFile = path.join(builtLocalDirectory, "cancellationToken.js");
@@ -725,7 +709,7 @@ compileFile(
/*prereqs*/[builtLocalDirectory, tscFile].concat(libraryTargets).concat(servicesSources).concat(harnessSources),
/*prefixes*/[],
/*useBuiltCompiler:*/ true,
/*opts*/ { inlineSourceMap: true, types: ["node", "mocha", "chai"], lib: "es6" });
/*opts*/ { types: ["node", "mocha", "chai"], lib: "es6" });
var internalTests = "internal/";
@@ -961,13 +945,14 @@ var nodeServerInFile = "tests/webTestServer.ts";
compileFile(nodeServerOutFile, [nodeServerInFile], [builtLocalDirectory, tscFile], [], /*useBuiltCompiler:*/ true, { noOutFile: true, lib: "es6" });
desc("Runs browserify on run.js to produce a file suitable for running tests in the browser");
task("browserify", ["tests", run, builtLocalDirectory, nodeServerOutFile], function() {
var cmd = 'browserify built/local/run.js -t ./scripts/browserify-optional -d -o built/local/bundle.js';
task("browserify", [], function() {
// Shell out to `gulp`, since we do the work to handle sourcemaps correctly w/o inline maps there
var cmd = 'gulp browserify --silent';
exec(cmd);
}, { async: true });
desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is jake runtests-browser. Additional optional parameters tests=[regex], browser=[chrome|IE]");
task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFileInBrowserTest], function () {
task("runtests-browser", ["browserify", nodeServerOutFile], function () {
cleanTestDirs();
host = "node";
browser = process.env.browser || process.env.b || (os.platform() === "linux" ? "chrome" : "IE");
-1
View File
@@ -90,7 +90,6 @@
"setup-hooks": "node scripts/link-hooks.js"
},
"browser": {
"buffer": false,
"fs": false,
"os": false,
"path": false
+8 -28
View File
@@ -806,11 +806,7 @@ namespace ts {
return antecedent;
}
setFlowNodeReferenced(antecedent);
return <FlowCondition>{
flags,
expression,
antecedent
};
return { flags, expression, antecedent };
}
function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
@@ -818,31 +814,18 @@ namespace ts {
return antecedent;
}
setFlowNodeReferenced(antecedent);
return <FlowSwitchClause>{
flags: FlowFlags.SwitchClause,
switchStatement,
clauseStart,
clauseEnd,
antecedent
};
return { flags: FlowFlags.SwitchClause, switchStatement, clauseStart, clauseEnd, antecedent };
}
function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode {
setFlowNodeReferenced(antecedent);
return <FlowAssignment>{
flags: FlowFlags.Assignment,
antecedent,
node
};
return { flags: FlowFlags.Assignment, antecedent, node };
}
function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode {
setFlowNodeReferenced(antecedent);
return <FlowArrayMutation>{
flags: FlowFlags.ArrayMutation,
antecedent,
node
};
const res: FlowArrayMutation = { flags: FlowFlags.ArrayMutation, antecedent, node };
return res;
}
function finishFlowLabel(flow: FlowLabel): FlowNode {
@@ -2784,7 +2767,6 @@ namespace ts {
function computeParameter(node: ParameterDeclaration, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const modifierFlags = getModifierFlags(node);
const name = node.name;
const initializer = node.initializer;
const dotDotDotToken = node.dotDotDotToken;
@@ -2799,7 +2781,7 @@ namespace ts {
}
// If a parameter has an accessibility modifier, then it is TypeScript syntax.
if (modifierFlags & ModifierFlags.ParameterPropertyModifier) {
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsParameterPropertyAssignments;
}
@@ -2844,9 +2826,8 @@ namespace ts {
function computeClassDeclaration(node: ClassDeclaration, subtreeFlags: TransformFlags) {
let transformFlags: TransformFlags;
const modifierFlags = getModifierFlags(node);
if (modifierFlags & ModifierFlags.Ambient) {
if (hasModifier(node, ModifierFlags.Ambient)) {
// An ambient declaration is TypeScript syntax.
transformFlags = TransformFlags.AssertTypeScript;
}
@@ -3190,11 +3171,10 @@ namespace ts {
function computeVariableStatement(node: VariableStatement, subtreeFlags: TransformFlags) {
let transformFlags: TransformFlags;
const modifierFlags = getModifierFlags(node);
const declarationListTransformFlags = node.declarationList.transformFlags;
// An ambient declaration is TypeScript syntax.
if (modifierFlags & ModifierFlags.Ambient) {
if (hasModifier(node, ModifierFlags.Ambient)) {
transformFlags = TransformFlags.AssertTypeScript;
}
else {
+186 -163
View File
@@ -138,6 +138,9 @@ namespace ts {
node = getParseTreeNode(node, isExportSpecifier);
return node ? getExportSpecifierLocalTargetSymbol(node) : undefined;
},
getExportSymbolOfSymbol(symbol) {
return getMergedSymbol(symbol.exportSymbol || symbol);
},
getTypeAtLocation: node => {
node = getParseTreeNode(node);
return node ? getTypeOfNode(node) : unknownType;
@@ -223,11 +226,10 @@ namespace ts {
getSuggestionForNonexistentProperty: (node, type) => unescapeLeadingUnderscores(getSuggestionForNonexistentProperty(node, type)),
getSuggestionForNonexistentSymbol: (location, name, meaning) => unescapeLeadingUnderscores(getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning)),
getBaseConstraintOfType,
getJsxNamespace: () => unescapeLeadingUnderscores(getJsxNamespace()),
resolveNameAtLocation(location: Node, name: string, meaning: SymbolFlags): Symbol | undefined {
location = getParseTreeNode(location);
return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, escapeLeadingUnderscores(name));
resolveName(name, location, meaning) {
return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
},
getJsxNamespace: () => unescapeLeadingUnderscores(getJsxNamespace()),
};
const tupleTypes: GenericType[] = [];
@@ -835,13 +837,13 @@ namespace ts {
(<PropertyDeclaration>current.parent).initializer === current;
if (initializerOfProperty) {
if (getModifierFlags(current.parent) & ModifierFlags.Static) {
if (hasModifier(current.parent, ModifierFlags.Static)) {
if (declaration.kind === SyntaxKind.MethodDeclaration) {
return true;
}
}
else {
const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !(getModifierFlags(declaration) & ModifierFlags.Static);
const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !hasModifier(declaration, ModifierFlags.Static);
if (!isDeclarationInstanceProperty || getContainingClass(usage) !== getContainingClass(declaration)) {
return true;
}
@@ -978,7 +980,7 @@ namespace ts {
// local variables of the constructor. This effectively means that entities from outer scopes
// by the same name as a constructor parameter or local variable are inaccessible
// in initializer expressions for instance member variables.
if (isClassLike(location.parent) && !(getModifierFlags(location) & ModifierFlags.Static)) {
if (isClassLike(location.parent) && !hasModifier(location, ModifierFlags.Static)) {
const ctor = findConstructorDeclaration(<ClassLikeDeclaration>location.parent);
if (ctor && ctor.locals) {
if (lookup(ctor.locals, name, meaning & SymbolFlags.Value)) {
@@ -997,7 +999,7 @@ namespace ts {
result = undefined;
break;
}
if (lastLocation && getModifierFlags(lastLocation) & ModifierFlags.Static) {
if (lastLocation && hasModifier(lastLocation, ModifierFlags.Static)) {
// TypeScript 1.0 spec (April 2014): 3.4.1
// The scope of a type parameter extends over the entire declaration with which the type
// parameter list is associated, with the exception of static member declarations in classes.
@@ -1200,7 +1202,7 @@ namespace ts {
// No static member is present.
// Check if we're in an instance method and look for a relevant instance member.
if (location === container && !(getModifierFlags(location) & ModifierFlags.Static)) {
if (location === container && !hasModifier(location, ModifierFlags.Static)) {
const instanceType = (<InterfaceType>getDeclaredTypeOfSymbol(classSymbol)).thisType;
if (getPropertyOfType(instanceType, name)) {
error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0, diagnosticName(nameArg));
@@ -2154,6 +2156,11 @@ namespace ts {
return false;
}
function isTypeSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node): boolean {
const access = isSymbolAccessible(typeSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false);
return access.accessibility === SymbolAccessibility.Accessible;
}
/**
* Check if the given symbol in given enclosing declaration is accessible and mark all associated alias to be visible if requested
*
@@ -2172,7 +2179,7 @@ namespace ts {
if (accessibleSymbolChain) {
const hasAccessibleDeclarations = hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible);
if (!hasAccessibleDeclarations) {
return <SymbolAccessibilityResult>{
return {
accessibility: SymbolAccessibility.NotAccessible,
errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning),
errorModuleName: symbol !== initialSymbol ? symbolToString(symbol, enclosingDeclaration, SymbolFlags.Namespace) : undefined,
@@ -2245,7 +2252,7 @@ namespace ts {
const anyImportSyntax = getAnyImportSyntax(declaration);
if (anyImportSyntax &&
!(getModifierFlags(anyImportSyntax) & ModifierFlags.Export) && // import clause without export
!hasModifier(anyImportSyntax, ModifierFlags.Export) && // import clause without export
isDeclarationVisible(<Declaration>anyImportSyntax.parent)) {
// In function "buildTypeDisplay" where we decide whether to write type-alias or serialize types,
// we want to just check if type- alias is accessible or not but we don't care about emitting those alias at that time
@@ -2294,7 +2301,7 @@ namespace ts {
const symbol = resolveName(enclosingDeclaration, firstIdentifier.escapedText, meaning, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
// Verify if the symbol is accessible
return (symbol && hasVisibleDeclarations(symbol, /*shouldComputeAliasToMakeVisible*/ true)) || <SymbolVisibilityResult>{
return (symbol && hasVisibleDeclarations(symbol, /*shouldComputeAliasToMakeVisible*/ true)) || {
accessibility: SymbolAccessibility.NotAccessible,
errorSymbolName: getTextOfNode(firstIdentifier),
errorNode: firstIdentifier
@@ -2484,8 +2491,7 @@ namespace ts {
// Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter.
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (!inTypeAlias && type.aliasSymbol &&
isSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) {
if (!inTypeAlias && type.aliasSymbol && isTypeSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration)) {
const name = symbolToTypeReferenceName(type.aliasSymbol);
const typeArgumentNodes = mapToTypeNodes(type.aliasTypeArguments, context);
return createTypeReferenceNode(name, typeArgumentNodes);
@@ -2572,8 +2578,8 @@ namespace ts {
}
function shouldWriteTypeOfFunctionSymbol() {
const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method && // typeof static method
forEach(symbol.declarations, declaration => getModifierFlags(declaration) & ModifierFlags.Static));
const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method) && // typeof static method
some(symbol.declarations, declaration => hasModifier(declaration, ModifierFlags.Static));
const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) &&
(symbol.parent || // is exported function symbol
forEach(symbol.declarations, declaration =>
@@ -3252,7 +3258,7 @@ namespace ts {
buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags);
}
else if (!(flags & TypeFormatFlags.InTypeAlias) && type.aliasSymbol &&
isSymbolAccessible(type.aliasSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) {
((flags & TypeFormatFlags.UseAliasDefinedOutsideCurrentScope) || isTypeSymbolAccessible(type.aliasSymbol, enclosingDeclaration))) {
const typeArguments = type.aliasTypeArguments;
writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, length(typeArguments), nextFlags);
}
@@ -3437,16 +3443,16 @@ namespace ts {
}
function shouldWriteTypeOfFunctionSymbol() {
const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method && // typeof static method
forEach(symbol.declarations, declaration => getModifierFlags(declaration) & ModifierFlags.Static));
const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method) && // typeof static method
some(symbol.declarations, declaration => hasModifier(declaration, ModifierFlags.Static));
const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) &&
(symbol.parent || // is exported function symbol
forEach(symbol.declarations, declaration =>
some(symbol.declarations, declaration =>
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
// typeof is allowed only for static/non local functions
return !!(flags & TypeFormatFlags.UseTypeOfFunction) || // use typeof if format flags specify it
(contains(symbolStack, symbol)); // it is type of the symbol uses itself recursively
contains(symbolStack, symbol); // it is type of the symbol uses itself recursively
}
}
}
@@ -3892,7 +3898,7 @@ namespace ts {
case SyntaxKind.SetAccessor:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (getModifierFlags(node) & (ModifierFlags.Private | ModifierFlags.Protected)) {
if (hasModifier(node, ModifierFlags.Private | ModifierFlags.Protected)) {
// Private/protected properties/methods are not visible
return false;
}
@@ -5096,8 +5102,8 @@ namespace ts {
return unknownType;
}
const declaration = <JSDocTypedefTag | TypeAliasDeclaration>findDeclaration(
symbol, d => d.kind === SyntaxKind.JSDocTypedefTag || d.kind === SyntaxKind.TypeAliasDeclaration);
const declaration = <JSDocTypedefTag | TypeAliasDeclaration>find(symbol.declarations, d =>
d.kind === SyntaxKind.JSDocTypedefTag || d.kind === SyntaxKind.TypeAliasDeclaration);
let type = getTypeFromTypeNode(declaration.kind === SyntaxKind.JSDocTypedefTag ? declaration.typeExpression : declaration.type);
if (popTypeResolution()) {
@@ -6300,7 +6306,7 @@ namespace ts {
return {
kind: TypePredicateKind.This,
type: getTypeFromTypeNode(node.type)
} as ThisTypePredicate;
};
}
}
@@ -6576,6 +6582,10 @@ namespace ts {
return signature.resolvedReturnType;
}
function isResolvingReturnTypeOfSignature(signature: Signature) {
return !signature.resolvedReturnType && findResolutionCycleStartIndex(signature, TypeSystemPropertyName.ResolvedReturnType) >= 0;
}
function getRestTypeOfSignature(signature: Signature): Type {
if (signature.hasRestParameter) {
const type = getTypeOfSymbol(lastOrUndefined(signature.parameters));
@@ -6657,7 +6667,7 @@ namespace ts {
const declaration = getIndexDeclarationOfSymbol(symbol, kind);
if (declaration) {
return createIndexInfo(declaration.type ? getTypeFromTypeNode(declaration.type) : anyType,
(getModifierFlags(declaration) & ModifierFlags.Readonly) !== 0, declaration);
hasModifier(declaration, ModifierFlags.Readonly), declaration);
}
return undefined;
}
@@ -7566,7 +7576,6 @@ namespace ts {
if (indexInfo) {
if (accessExpression && indexInfo.isReadonly && (isAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression))) {
error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType));
return unknownType;
}
return indexInfo.type;
}
@@ -7607,7 +7616,6 @@ namespace ts {
}
if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) && type.declaration.readonlyToken) {
error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(type));
return unknownType;
}
}
const mapper = createTypeMapper([getTypeParameterFromMappedType(type)], [indexType]);
@@ -7909,7 +7917,7 @@ namespace ts {
const container = getThisContainer(node, /*includeArrowFunctions*/ false);
const parent = container && container.parent;
if (parent && (isClassLike(parent) || parent.kind === SyntaxKind.InterfaceDeclaration)) {
if (!(getModifierFlags(container) & ModifierFlags.Static) &&
if (!hasModifier(container, ModifierFlags.Static) &&
(container.kind !== SyntaxKind.Constructor || isNodeDescendantOf(node, (<ConstructorDeclaration>container).body))) {
return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent)).thisType;
}
@@ -8109,13 +8117,13 @@ namespace ts {
parameterName: predicate.parameterName,
parameterIndex: predicate.parameterIndex,
type: instantiateType(predicate.type, mapper)
} as IdentifierTypePredicate;
};
}
else {
return {
kind: TypePredicateKind.This,
type: instantiateType(predicate.type, mapper)
} as ThisTypePredicate;
};
}
}
@@ -8405,16 +8413,17 @@ namespace ts {
if (forEach(node.parameters, p => !getEffectiveTypeAnnotationNode(p))) {
return true;
}
// For arrow functions we now know we're not context sensitive.
if (node.kind === SyntaxKind.ArrowFunction) {
return false;
if (node.kind !== SyntaxKind.ArrowFunction) {
// If the first parameter is not an explicit 'this' parameter, then the function has
// an implicit 'this' parameter which is subject to contextual typing.
const parameter = firstOrUndefined(node.parameters);
if (!(parameter && parameterIsThisKeyword(parameter))) {
return true;
}
}
// If the first parameter is not an explicit 'this' parameter, then the function has
// an implicit 'this' parameter which is subject to contextual typing. Otherwise we
// know that all parameters (including 'this') have type annotations and nothing is
// subject to contextual typing.
const parameter = firstOrUndefined(node.parameters);
return !(parameter && parameterIsThisKeyword(parameter));
// TODO(anhans): A block should be context-sensitive if it has a context-sensitive return value.
return node.body.kind === SyntaxKind.Block ? false : isContextSensitive(node.body);
}
function isContextSensitiveFunctionOrObjectLiteralMethod(func: Node): func is FunctionExpression | ArrowFunction | MethodDeclaration {
@@ -8592,7 +8601,7 @@ namespace ts {
// The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions
if (target.typePredicate) {
if (source.typePredicate) {
result &= compareTypePredicateRelatedTo(source.typePredicate, target.typePredicate, reportErrors, errorReporter, compareTypes);
result &= compareTypePredicateRelatedTo(source.typePredicate, target.typePredicate, source.declaration, target.declaration, reportErrors, errorReporter, compareTypes);
}
else if (isIdentifierTypePredicate(target.typePredicate)) {
if (reportErrors) {
@@ -8614,8 +8623,11 @@ namespace ts {
return result;
}
function compareTypePredicateRelatedTo(source: TypePredicate,
function compareTypePredicateRelatedTo(
source: TypePredicate,
target: TypePredicate,
sourceDeclaration: SignatureDeclaration,
targetDeclaration: SignatureDeclaration,
reportErrors: boolean,
errorReporter: ErrorReporter,
compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary {
@@ -8628,11 +8640,13 @@ namespace ts {
}
if (source.kind === TypePredicateKind.Identifier) {
const sourceIdentifierPredicate = source as IdentifierTypePredicate;
const targetIdentifierPredicate = target as IdentifierTypePredicate;
if (sourceIdentifierPredicate.parameterIndex !== targetIdentifierPredicate.parameterIndex) {
const sourcePredicate = source as IdentifierTypePredicate;
const targetPredicate = target as IdentifierTypePredicate;
const sourceIndex = sourcePredicate.parameterIndex - (getThisParameter(sourceDeclaration) ? 1 : 0);
const targetIndex = targetPredicate.parameterIndex - (getThisParameter(targetDeclaration) ? 1 : 0);
if (sourceIndex !== targetIndex) {
if (reportErrors) {
errorReporter(Diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, sourceIdentifierPredicate.parameterName, targetIdentifierPredicate.parameterName);
errorReporter(Diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, sourcePredicate.parameterName, targetPredicate.parameterName);
errorReporter(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typePredicateToString(source), typePredicateToString(target));
}
return Ternary.False;
@@ -9723,8 +9737,8 @@ namespace ts {
return true;
}
const sourceAccessibility = getModifierFlags(sourceSignature.declaration) & ModifierFlags.NonPublicAccessibilityModifier;
const targetAccessibility = getModifierFlags(targetSignature.declaration) & ModifierFlags.NonPublicAccessibilityModifier;
const sourceAccessibility = getSelectedModifierFlags(sourceSignature.declaration, ModifierFlags.NonPublicAccessibilityModifier);
const targetAccessibility = getSelectedModifierFlags(targetSignature.declaration, ModifierFlags.NonPublicAccessibilityModifier);
// A public, protected and private signature is assignable to a private signature.
if (targetAccessibility === ModifierFlags.Private) {
@@ -9798,7 +9812,7 @@ namespace ts {
const symbol = type.symbol;
if (symbol && symbol.flags & SymbolFlags.Class) {
const declaration = getClassLikeDeclarationOfSymbol(symbol);
if (declaration && getModifierFlags(declaration) & ModifierFlags.Abstract) {
if (declaration && hasModifier(declaration, ModifierFlags.Abstract)) {
return true;
}
}
@@ -12465,7 +12479,7 @@ namespace ts {
break;
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
if (getModifierFlags(container) & ModifierFlags.Static) {
if (hasModifier(container, ModifierFlags.Static)) {
error(node, Diagnostics.this_cannot_be_referenced_in_a_static_property_initializer);
// do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks
}
@@ -12583,7 +12597,7 @@ namespace ts {
checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class);
}
if ((getModifierFlags(container) & ModifierFlags.Static) || isCallExpression) {
if (hasModifier(container, ModifierFlags.Static) || isCallExpression) {
nodeCheckFlag = NodeCheckFlags.SuperStatic;
}
else {
@@ -12648,7 +12662,7 @@ namespace ts {
// This helper creates an object with a "value" property that wraps the `super` property or indexed access for both get and set.
// This is required for destructuring assignments, as a call expression cannot be used as the target of a destructuring assignment
// while a property access can.
if (container.kind === SyntaxKind.MethodDeclaration && getModifierFlags(container) & ModifierFlags.Async) {
if (container.kind === SyntaxKind.MethodDeclaration && hasModifier(container, ModifierFlags.Async)) {
if (isSuperProperty(node.parent) && isAssignmentTarget(node.parent)) {
getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuperBinding;
}
@@ -12714,7 +12728,7 @@ namespace ts {
// topmost container must be something that is directly nested in the class declaration\object literal expression
if (isClassLike(container.parent) || container.parent.kind === SyntaxKind.ObjectLiteralExpression) {
if (getModifierFlags(container) & ModifierFlags.Static) {
if (hasModifier(container, ModifierFlags.Static)) {
return container.kind === SyntaxKind.MethodDeclaration ||
container.kind === SyntaxKind.MethodSignature ||
container.kind === SyntaxKind.GetAccessor ||
@@ -12943,7 +12957,7 @@ namespace ts {
// Otherwise, if the containing function is contextually typed by a function type with exactly one call signature
// and that call signature is non-generic, return statements are contextually typed by the return type of the signature
const signature = getContextualSignatureForFunctionLikeDeclaration(<FunctionExpression>functionDecl);
if (signature) {
if (signature && !isResolvingReturnTypeOfSignature(signature)) {
return getReturnTypeOfSignature(signature);
}
@@ -14599,9 +14613,12 @@ namespace ts {
}
const prop = getPropertyOfType(apparentType, right.escapedText);
if (!prop) {
const stringIndexType = getIndexTypeOfType(apparentType, IndexKind.String);
if (stringIndexType) {
return stringIndexType;
const indexInfo = getIndexInfoOfType(apparentType, IndexKind.String);
if (indexInfo && indexInfo.type) {
if (indexInfo.isReadonly && (isAssignmentTarget(node) || isDeleteTarget(node))) {
error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType));
}
return indexInfo.type;
}
if (right.escapedText && !checkAndReportErrorForExtendingInterface(node)) {
reportNonexistentProperty(right, type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType ? apparentType : type);
@@ -14759,7 +14776,7 @@ namespace ts {
if (prop &&
noUnusedIdentifiers &&
(prop.flags & SymbolFlags.ClassMember) &&
prop.valueDeclaration && (getModifierFlags(prop.valueDeclaration) & ModifierFlags.Private)) {
prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Private)) {
if (getCheckFlags(prop) & CheckFlags.Instantiated) {
getSymbolLinks(prop).target.isReferenced = true;
}
@@ -15705,9 +15722,10 @@ namespace ts {
//
// For a decorator, no arguments are susceptible to contextual typing due to the fact
// decorators are applied to a declaration by the emitter, and not to an expression.
const isSingleNonGenericCandidate = candidates.length === 1 && !candidates[0].typeParameters;
let excludeArgument: boolean[];
let excludeCount = 0;
if (!isDecorator) {
if (!isDecorator && !isSingleNonGenericCandidate) {
// We do not need to call `getEffectiveArgumentCount` here as it only
// applies when calculating the number of arguments for a decorator.
for (let i = isTaggedTemplate ? 1 : 0; i < args.length; i++) {
@@ -15860,6 +15878,19 @@ namespace ts {
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false) {
candidateForArgumentError = undefined;
candidateForTypeArgumentError = undefined;
if (isSingleNonGenericCandidate) {
const candidate = candidates[0];
if (!hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) {
return undefined;
}
if (!checkApplicableSignature(node, args, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
candidateForArgumentError = candidate;
return undefined;
}
return candidate;
}
for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
const originalCandidate = candidates[candidateIndex];
if (!hasCorrectArity(node, args, originalCandidate, signatureHelpTrailingComma)) {
@@ -15907,7 +15938,6 @@ namespace ts {
return undefined;
}
}
function getLongestCandidateIndex(candidates: Signature[], argsCount: number): number {
@@ -16039,7 +16069,7 @@ namespace ts {
// In the case of a merged class-module or class-interface declaration,
// only the class declaration node will have the Abstract flag set.
const valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol);
if (valueDecl && getModifierFlags(valueDecl) & ModifierFlags.Abstract) {
if (valueDecl && hasModifier(valueDecl, ModifierFlags.Abstract)) {
error(node, Diagnostics.Cannot_create_an_instance_of_the_abstract_class_0, declarationNameToString(getNameOfDeclaration(valueDecl)));
return resolveErrorCall(node);
}
@@ -16092,10 +16122,10 @@ namespace ts {
}
const declaration = signature.declaration;
const modifiers = getModifierFlags(declaration);
const modifiers = getSelectedModifierFlags(declaration, ModifierFlags.NonPublicAccessibilityModifier);
// Public constructor is accessible.
if (!(modifiers & ModifierFlags.NonPublicAccessibilityModifier)) {
if (!modifiers) {
return true;
}
@@ -16333,7 +16363,7 @@ namespace ts {
*/
function checkCallExpression(node: CallExpression | NewExpression): Type {
// Grammar checking; stop grammar-checking if checkGrammarTypeArguments return true
checkGrammarTypeArguments(node, node.typeArguments) || checkGrammarArguments(node, node.arguments);
checkGrammarTypeArguments(node, node.typeArguments) || checkGrammarArguments(node.arguments);
const signature = getResolvedSignature(node);
@@ -16381,7 +16411,7 @@ namespace ts {
function checkImportCallExpression(node: ImportCall): Type {
// Check grammar of dynamic import
checkGrammarArguments(node, node.arguments) || checkGrammarImportCallExpression(node);
checkGrammarArguments(node.arguments) || checkGrammarImportCallExpression(node);
if (node.arguments.length === 0) {
return createPromiseReturnType(node, anyType);
@@ -16568,14 +16598,14 @@ namespace ts {
// When contextual typing assigns a type to a parameter that contains a binding pattern, we also need to push
// the destructured type into the contained binding elements.
function assignBindingElementTypes(node: VariableLikeDeclaration) {
if (isBindingPattern(node.name)) {
for (const element of node.name.elements) {
if (!isOmittedExpression(element)) {
if (element.name.kind === SyntaxKind.Identifier) {
getSymbolLinks(getSymbolOfNode(element)).type = getTypeForBindingElement(element);
}
assignBindingElementTypes(element);
function assignBindingElementTypes(pattern: BindingPattern) {
for (const element of pattern.elements) {
if (!isOmittedExpression(element)) {
if (element.name.kind === SyntaxKind.Identifier) {
getSymbolLinks(getSymbolOfNode(element)).type = getTypeForBindingElement(element);
}
else {
assignBindingElementTypes(element.name);
}
}
}
@@ -16585,13 +16615,14 @@ namespace ts {
const links = getSymbolLinks(parameter);
if (!links.type) {
links.type = contextualType;
const name = getNameOfDeclaration(parameter.valueDeclaration);
// if inference didn't come up with anything but {}, fall back to the binding pattern if present.
if (links.type === emptyObjectType &&
(name.kind === SyntaxKind.ObjectBindingPattern || name.kind === SyntaxKind.ArrayBindingPattern)) {
links.type = getTypeFromBindingPattern(<BindingPattern>name);
const decl = parameter.valueDeclaration as ParameterDeclaration;
if (decl.name.kind !== SyntaxKind.Identifier) {
// if inference didn't come up with anything but {}, fall back to the binding pattern if present.
if (links.type === emptyObjectType) {
links.type = getTypeFromBindingPattern(decl.name);
}
assignBindingElementTypes(decl.name);
}
assignBindingElementTypes(<ParameterDeclaration>parameter.valueDeclaration);
}
}
@@ -16851,18 +16882,18 @@ namespace ts {
function checkFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | MethodDeclaration, checkMode?: CheckMode): Type {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
// Grammar checking
const hasGrammarError = checkGrammarFunctionLikeDeclaration(node);
if (!hasGrammarError && node.kind === SyntaxKind.FunctionExpression) {
checkGrammarForGenerator(node);
}
// The identityMapper object is used to indicate that function expressions are wildcards
if (checkMode === CheckMode.SkipContextSensitive && isContextSensitive(node)) {
checkNodeDeferred(node);
return anyFunctionType;
}
// Grammar checking
const hasGrammarError = checkGrammarFunctionLikeDeclaration(node);
if (!hasGrammarError && node.kind === SyntaxKind.FunctionExpression) {
checkGrammarForGenerator(node);
}
const links = getNodeLinks(node);
const type = getTypeOfSymbol(node.symbol);
@@ -17108,7 +17139,9 @@ namespace ts {
if (operandType === silentNeverType) {
return silentNeverType;
}
const ok = checkArithmeticOperandType(node.operand, checkNonNullType(operandType, node.operand),
const ok = checkArithmeticOperandType(
node.operand,
checkNonNullType(operandType, node.operand),
Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type);
if (ok) {
// run check only if former checks succeeded to avoid reporting cascading errors
@@ -17738,15 +17771,14 @@ namespace ts {
return getBestChoiceType(type1, type2);
}
function checkLiteralExpression(node: Expression): Type {
if (node.kind === SyntaxKind.NumericLiteral) {
checkGrammarNumericLiteral(<NumericLiteral>node);
}
function checkLiteralExpression(node: LiteralExpression | Token<SyntaxKind.TrueKeyword | SyntaxKind.FalseKeyword>): Type {
switch (node.kind) {
case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.StringLiteral:
return getFreshTypeOfLiteralType(getLiteralType((<LiteralExpression>node).text));
return getFreshTypeOfLiteralType(getLiteralType(node.text));
case SyntaxKind.NumericLiteral:
return getFreshTypeOfLiteralType(getLiteralType(+(<LiteralExpression>node).text));
checkGrammarNumericLiteral(<NumericLiteral>node);
return getFreshTypeOfLiteralType(getLiteralType(+node.text));
case SyntaxKind.TrueKeyword:
return trueType;
case SyntaxKind.FalseKeyword:
@@ -17964,15 +17996,14 @@ namespace ts {
return checkSuperExpression(node);
case SyntaxKind.NullKeyword:
return nullWideningType;
case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return checkLiteralExpression(node);
return checkLiteralExpression(node as LiteralExpression);
case SyntaxKind.TemplateExpression:
return checkTemplateExpression(<TemplateExpression>node);
case SyntaxKind.NoSubstitutionTemplateLiteral:
return stringType;
case SyntaxKind.RegularExpressionLiteral:
return globalRegExpType;
case SyntaxKind.ArrayLiteralExpression:
@@ -18077,7 +18108,7 @@ namespace ts {
checkVariableLikeDeclaration(node);
let func = getContainingFunction(node);
if (getModifierFlags(node) & ModifierFlags.ParameterPropertyModifier) {
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
func = getContainingFunction(node);
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
error(node, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation);
@@ -18312,7 +18343,7 @@ namespace ts {
}
}
else {
const isStatic = getModifierFlags(member) & ModifierFlags.Static;
const isStatic = hasModifier(member, ModifierFlags.Static);
const names = isStatic ? staticNames : instanceNames;
const memberName = member.name && getPropertyNameForPropertyNameNode(member.name);
@@ -18373,7 +18404,7 @@ namespace ts {
function checkClassForStaticPropertyNameConflicts(node: ClassLikeDeclaration) {
for (const member of node.members) {
const memberNameNode = member.name;
const isStatic = getModifierFlags(member) & ModifierFlags.Static;
const isStatic = hasModifier(member, ModifierFlags.Static);
if (isStatic && memberNameNode) {
const memberName = getPropertyNameForPropertyNameNode(memberNameNode);
switch (memberName) {
@@ -18478,7 +18509,7 @@ namespace ts {
// Abstract methods cannot have an implementation.
// Extra checks are to avoid reporting multiple errors relating to the "abstractness" of the node.
if (getModifierFlags(node) & ModifierFlags.Abstract && node.body) {
if (hasModifier(node, ModifierFlags.Abstract) && node.body) {
error(node, Diagnostics.Method_0_cannot_have_an_implementation_because_it_is_marked_abstract, declarationNameToString(node.name));
}
}
@@ -18529,7 +18560,7 @@ namespace ts {
function isInstancePropertyWithInitializer(n: Node): boolean {
return n.kind === SyntaxKind.PropertyDeclaration &&
!(getModifierFlags(n) & ModifierFlags.Static) &&
!hasModifier(n, ModifierFlags.Static) &&
!!(<PropertyDeclaration>n).initializer;
}
@@ -18552,8 +18583,8 @@ namespace ts {
// - The constructor declares parameter properties
// or the containing class declares instance member variables with initializers.
const superCallShouldBeFirst =
forEach((<ClassDeclaration>node.parent).members, isInstancePropertyWithInitializer) ||
forEach(node.parameters, p => getModifierFlags(p) & ModifierFlags.ParameterPropertyModifier);
some((<ClassDeclaration>node.parent).members, isInstancePropertyWithInitializer) ||
some(node.parameters, p => hasModifier(p, ModifierFlags.ParameterPropertyModifier));
// Skip past any prologue directives to find the first statement
// to ensure that it was a super call.
@@ -18607,10 +18638,12 @@ namespace ts {
const otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor;
const otherAccessor = getDeclarationOfKind<AccessorDeclaration>(node.symbol, otherKind);
if (otherAccessor) {
if ((getModifierFlags(node) & ModifierFlags.AccessibilityModifier) !== (getModifierFlags(otherAccessor) & ModifierFlags.AccessibilityModifier)) {
const nodeFlags = getModifierFlags(node);
const otherFlags = getModifierFlags(otherAccessor);
if ((nodeFlags & ModifierFlags.AccessibilityModifier) !== (otherFlags & ModifierFlags.AccessibilityModifier)) {
error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility);
}
if (hasModifier(node, ModifierFlags.Abstract) !== hasModifier(otherAccessor, ModifierFlags.Abstract)) {
if ((nodeFlags & ModifierFlags.Abstract) !== (otherFlags & ModifierFlags.Abstract)) {
error(node.name, Diagnostics.Accessors_must_both_be_abstract_or_non_abstract);
}
@@ -18667,7 +18700,7 @@ namespace ts {
function checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) {
checkGrammarTypeArguments(node, node.typeArguments);
if (node.kind === SyntaxKind.TypeReference && node.typeName.jsdocDotPos !== undefined && !isInJavaScriptFile(node) && !isInJSDoc(node)) {
grammarErrorAtPos(getSourceFileOfNode(node), node.typeName.jsdocDotPos, 1, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments);
grammarErrorAtPos(node, node.typeName.jsdocDotPos, 1, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments);
}
const type = getTypeFromTypeReference(node);
@@ -18739,13 +18772,10 @@ namespace ts {
if (isTypeAssignableTo(indexType, getIndexType(objectType))) {
return type;
}
// Check if we're indexing with a numeric type and the object type is a generic
// type with a constraint that has a numeric index signature.
if (maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) {
const constraint = getBaseConstraintOfType(objectType);
if (constraint && getIndexInfoOfType(constraint, IndexKind.Number)) {
return type;
}
// Check if we're indexing with a numeric type and if either object or index types
// is a generic type with a constraint that has a numeric index signature.
if (getIndexInfoOfType(getApparentType(objectType), IndexKind.Number) && isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) {
return type;
}
error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
return type;
@@ -18766,7 +18796,7 @@ namespace ts {
}
function isPrivateWithinAmbient(node: Node): boolean {
return (getModifierFlags(node) & ModifierFlags.Private) && isInAmbientContext(node);
return hasModifier(node, ModifierFlags.Private) && isInAmbientContext(node);
}
function getEffectiveDeclarationFlags(n: Node, flagsToCheck: ModifierFlags): ModifierFlags {
@@ -18879,13 +18909,13 @@ namespace ts {
!isComputedPropertyName(node.name) && !isComputedPropertyName(subsequentName) && getEscapedTextOfIdentifierOrLiteral(node.name) === getEscapedTextOfIdentifierOrLiteral(subsequentName))) {
const reportError =
(node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) &&
(getModifierFlags(node) & ModifierFlags.Static) !== (getModifierFlags(subsequentNode) & ModifierFlags.Static);
hasModifier(node, ModifierFlags.Static) !== hasModifier(subsequentNode, ModifierFlags.Static);
// we can get here in two cases
// 1. mixed static and instance class members
// 2. something with the same name was defined before the set of overloads that prevents them from merging
// here we'll report error only for the first case since for second we should already report error in binder
if (reportError) {
const diagnostic = getModifierFlags(node) & ModifierFlags.Static ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static;
const diagnostic = hasModifier(node, ModifierFlags.Static) ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static;
error(errorNode, diagnostic);
}
return;
@@ -18903,7 +18933,7 @@ namespace ts {
else {
// Report different errors regarding non-consecutive blocks of declarations depending on whether
// the node in question is abstract.
if (getModifierFlags(node) & ModifierFlags.Abstract) {
if (hasModifier(node, ModifierFlags.Abstract)) {
error(errorNode, Diagnostics.All_declarations_of_an_abstract_method_must_be_consecutive);
}
else {
@@ -18979,7 +19009,7 @@ namespace ts {
// Abstract methods can't have an implementation -- in particular, they don't need one.
if (lastSeenNonAmbientDeclaration && !lastSeenNonAmbientDeclaration.body &&
!(getModifierFlags(lastSeenNonAmbientDeclaration) & ModifierFlags.Abstract) && !lastSeenNonAmbientDeclaration.questionToken) {
!hasModifier(lastSeenNonAmbientDeclaration, ModifierFlags.Abstract) && !lastSeenNonAmbientDeclaration.questionToken) {
reportImplementationExpectedError(lastSeenNonAmbientDeclaration);
}
@@ -19625,7 +19655,7 @@ namespace ts {
// checkFunctionOrConstructorSymbol wouldn't be called if we didnt ignore javascript function.
const firstDeclaration = find(localSymbol.declarations,
// Get first non javascript function declaration
declaration => declaration.kind === node.kind && !isSourceFileJavaScript(getSourceFileOfNode(declaration)));
declaration => declaration.kind === node.kind && !(declaration.flags & NodeFlags.JavaScriptFile));
// Only type check the symbol once
if (node === firstDeclaration) {
@@ -19782,13 +19812,13 @@ namespace ts {
if (node.members) {
for (const member of node.members) {
if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.PropertyDeclaration) {
if (!member.symbol.isReferenced && getModifierFlags(member) & ModifierFlags.Private) {
if (!member.symbol.isReferenced && hasModifier(member, ModifierFlags.Private)) {
error(member.name, Diagnostics._0_is_declared_but_never_used, unescapeLeadingUnderscores(member.symbol.escapedName));
}
}
else if (member.kind === SyntaxKind.Constructor) {
for (const parameter of (<ConstructorDeclaration>member).parameters) {
if (!parameter.symbol.isReferenced && getModifierFlags(parameter) & ModifierFlags.Private) {
if (!parameter.symbol.isReferenced && hasModifier(parameter, ModifierFlags.Private)) {
error(parameter.name, Diagnostics.Property_0_is_declared_but_never_used, unescapeLeadingUnderscores(parameter.symbol.escapedName));
}
}
@@ -20258,7 +20288,7 @@ namespace ts {
ModifierFlags.Readonly |
ModifierFlags.Static;
return (getModifierFlags(left) & interestingFlags) === (getModifierFlags(right) & interestingFlags);
return getSelectedModifierFlags(left, interestingFlags) === getSelectedModifierFlags(right, interestingFlags);
}
function checkVariableDeclaration(node: VariableDeclaration) {
@@ -21011,7 +21041,7 @@ namespace ts {
// Only process instance properties with computed names here.
// Static properties cannot be in conflict with indexers,
// and properties with literal names were already checked.
if (!(getModifierFlags(member) & ModifierFlags.Static) && hasDynamicName(member)) {
if (!hasModifier(member, ModifierFlags.Static) && hasDynamicName(member)) {
const propType = getTypeOfSymbol(member.symbol);
checkIndexConstraintForProperty(member.symbol, propType, type, declaredStringIndexer, stringIndexType, IndexKind.String);
checkIndexConstraintForProperty(member.symbol, propType, type, declaredNumberIndexer, numberIndexType, IndexKind.Number);
@@ -21206,7 +21236,7 @@ namespace ts {
}
function checkClassDeclaration(node: ClassDeclaration) {
if (!node.name && !(getModifierFlags(node) & ModifierFlags.Default)) {
if (!node.name && !hasModifier(node, ModifierFlags.Default)) {
grammarErrorOnFirstToken(node, Diagnostics.A_class_declaration_without_the_default_modifier_must_have_a_name);
}
checkClassLikeDeclaration(node);
@@ -21312,7 +21342,7 @@ namespace ts {
const signatures = getSignaturesOfType(type, SignatureKind.Construct);
if (signatures.length) {
const declaration = signatures[0].declaration;
if (declaration && getModifierFlags(declaration) & ModifierFlags.Private) {
if (declaration && hasModifier(declaration, ModifierFlags.Private)) {
const typeClassDeclaration = <ClassLikeDeclaration>getClassLikeDeclarationOfSymbol(type.symbol);
if (!isNodeWithinClass(node, typeClassDeclaration)) {
error(node, Diagnostics.Cannot_extend_a_class_0_Class_constructor_is_marked_as_private, getFullyQualifiedName(type.symbol));
@@ -21378,7 +21408,7 @@ namespace ts {
// It is an error to inherit an abstract member without implementing it or being declared abstract.
// If there is no declaration for the derived class (as in the case of class expressions),
// then the class cannot be declared abstract.
if (baseDeclarationFlags & ModifierFlags.Abstract && (!derivedClassDecl || !(getModifierFlags(derivedClassDecl) & ModifierFlags.Abstract))) {
if (baseDeclarationFlags & ModifierFlags.Abstract && (!derivedClassDecl || !hasModifier(derivedClassDecl, ModifierFlags.Abstract))) {
if (derivedClassDecl.kind === SyntaxKind.ClassExpression) {
error(derivedClassDecl, Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1,
symbolToString(baseProperty), typeToString(baseType));
@@ -22001,7 +22031,7 @@ namespace ts {
// If we hit an import declaration in an illegal context, just bail out to avoid cascading errors.
return;
}
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && getModifierFlags(node) !== 0) {
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && hasModifiers(node)) {
grammarErrorOnFirstToken(node, Diagnostics.An_import_declaration_cannot_have_modifiers);
}
if (checkExternalImportOrExportDeclaration(node)) {
@@ -22031,7 +22061,7 @@ namespace ts {
checkGrammarDecorators(node) || checkGrammarModifiers(node);
if (isInternalModuleImportEqualsDeclaration(node) || checkExternalImportOrExportDeclaration(node)) {
checkImportBinding(node);
if (getModifierFlags(node) & ModifierFlags.Export) {
if (hasModifier(node, ModifierFlags.Export)) {
markExportAsReferenced(node);
}
if (isInternalModuleImportEqualsDeclaration(node)) {
@@ -22064,7 +22094,7 @@ namespace ts {
return;
}
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && getModifierFlags(node) !== 0) {
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && hasModifiers(node)) {
grammarErrorOnFirstToken(node, Diagnostics.An_export_declaration_cannot_have_modifiers);
}
@@ -22137,7 +22167,7 @@ namespace ts {
return;
}
// Grammar checking
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && getModifierFlags(node) !== 0) {
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && hasModifiers(node)) {
grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers);
}
if (node.expression.kind === SyntaxKind.Identifier) {
@@ -22535,7 +22565,7 @@ namespace ts {
}
const symbols = createSymbolTable();
let memberFlags: ModifierFlags = ModifierFlags.None;
let isStatic = false;
populateSymbols();
@@ -22568,7 +22598,7 @@ namespace ts {
// add the type parameters into the symbol table
// (type parameters of classDeclaration/classExpression and interface are in member property of the symbol.
// Note: that the memberFlags come from previous iteration.
if (!(memberFlags & ModifierFlags.Static)) {
if (!isStatic) {
copySymbols(getSymbolOfNode(location).members, meaning & SymbolFlags.Type);
}
break;
@@ -22584,7 +22614,7 @@ namespace ts {
copySymbol(argumentsSymbol, meaning);
}
memberFlags = getModifierFlags(location);
isStatic = hasModifier(location, ModifierFlags.Static);
location = location.parent;
}
@@ -22891,10 +22921,7 @@ namespace ts {
// index access
if (node.parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>node.parent).argumentExpression === node) {
const objectType = getTypeOfExpression((<ElementAccessExpression>node.parent).expression);
if (objectType === unknownType) return undefined;
const apparentType = getApparentType(objectType);
if (apparentType === unknownType) return undefined;
return getPropertyOfType(apparentType, (<NumericLiteral>node).text as __String);
return getPropertyOfType(objectType, (<NumericLiteral>node).text as __String);
}
break;
}
@@ -23045,7 +23072,7 @@ namespace ts {
*/
function getParentTypeOfClassElement(node: ClassElement) {
const classSymbol = getSymbolOfNode(node.parent);
return getModifierFlags(node) & ModifierFlags.Static
return hasModifier(node, ModifierFlags.Static)
? getTypeOfSymbol(classSymbol)
: getDeclaredTypeOfSymbol(classSymbol);
}
@@ -23357,14 +23384,14 @@ namespace ts {
return strictNullChecks &&
!isOptionalParameter(parameter) &&
parameter.initializer &&
!(getModifierFlags(parameter) & ModifierFlags.ParameterPropertyModifier);
!hasModifier(parameter, ModifierFlags.ParameterPropertyModifier);
}
function isOptionalUninitializedParameterProperty(parameter: ParameterDeclaration) {
return strictNullChecks &&
isOptionalParameter(parameter) &&
!parameter.initializer &&
!!(getModifierFlags(parameter) & ModifierFlags.ParameterPropertyModifier);
hasModifier(parameter, ModifierFlags.ParameterPropertyModifier);
}
function getNodeCheckFlags(node: Node): NodeCheckFlags {
@@ -23979,7 +24006,7 @@ namespace ts {
node.kind !== SyntaxKind.SetAccessor) {
return grammarErrorOnNode(modifier, Diagnostics.abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration);
}
if (!(node.parent.kind === SyntaxKind.ClassDeclaration && getModifierFlags(node.parent) & ModifierFlags.Abstract)) {
if (!(node.parent.kind === SyntaxKind.ClassDeclaration && hasModifier(node.parent, ModifierFlags.Abstract))) {
return grammarErrorOnNode(modifier, Diagnostics.Abstract_methods_can_only_appear_within_an_abstract_class);
}
if (flags & ModifierFlags.Static) {
@@ -24109,8 +24136,7 @@ namespace ts {
if (list && list.hasTrailingComma) {
const start = list.end - ",".length;
const end = list.end;
const sourceFile = getSourceFileOfNode(list[0]);
return grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.Trailing_comma_not_allowed);
return grammarErrorAtPos(list[0], start, end - start, Diagnostics.Trailing_comma_not_allowed);
}
}
@@ -24199,7 +24225,7 @@ namespace ts {
if (parameter.dotDotDotToken) {
return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.An_index_signature_cannot_have_a_rest_parameter);
}
if (getModifierFlags(parameter) !== 0) {
if (hasModifiers(parameter)) {
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_accessibility_modifier);
}
if (parameter.questionToken) {
@@ -24238,19 +24264,18 @@ namespace ts {
checkGrammarForAtLeastOneTypeArgument(node, typeArguments);
}
function checkGrammarForOmittedArgument(node: CallExpression | NewExpression, args: NodeArray<Expression>): boolean {
function checkGrammarForOmittedArgument(args: NodeArray<Expression>): boolean {
if (args) {
const sourceFile = getSourceFileOfNode(node);
for (const arg of args) {
if (arg.kind === SyntaxKind.OmittedExpression) {
return grammarErrorAtPos(sourceFile, arg.pos, 0, Diagnostics.Argument_expression_expected);
return grammarErrorAtPos(arg, arg.pos, 0, Diagnostics.Argument_expression_expected);
}
}
}
}
function checkGrammarArguments(node: CallExpression | NewExpression, args: NodeArray<Expression>): boolean {
return checkGrammarForOmittedArgument(node, args);
function checkGrammarArguments(args: NodeArray<Expression>): boolean {
return checkGrammarForOmittedArgument(args);
}
function checkGrammarHeritageClause(node: HeritageClause): boolean {
@@ -24260,8 +24285,7 @@ namespace ts {
}
if (types && types.length === 0) {
const listType = tokenToString(node.token);
const sourceFile = getSourceFileOfNode(node);
return grammarErrorAtPos(sourceFile, types.pos, 0, Diagnostics._0_list_cannot_be_empty, listType);
return grammarErrorAtPos(node, types.pos, 0, Diagnostics._0_list_cannot_be_empty, listType);
}
return forEach(types, checkGrammarExpressionWithTypeArguments);
}
@@ -24538,10 +24562,10 @@ namespace ts {
else if (isInAmbientContext(accessor)) {
return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_be_declared_in_an_ambient_context);
}
else if (accessor.body === undefined && !(getModifierFlags(accessor) & ModifierFlags.Abstract)) {
return grammarErrorAtPos(getSourceFileOfNode(accessor), accessor.end - 1, ";".length, Diagnostics._0_expected, "{");
else if (accessor.body === undefined && !hasModifier(accessor, ModifierFlags.Abstract)) {
return grammarErrorAtPos(accessor, accessor.end - 1, ";".length, Diagnostics._0_expected, "{");
}
else if (accessor.body && getModifierFlags(accessor) & ModifierFlags.Abstract) {
else if (accessor.body && hasModifier(accessor, ModifierFlags.Abstract)) {
return grammarErrorOnNode(accessor, Diagnostics.An_abstract_accessor_cannot_have_an_implementation);
}
else if (accessor.typeParameters) {
@@ -24604,7 +24628,7 @@ namespace ts {
return true;
}
else if (node.body === undefined) {
return grammarErrorAtPos(getSourceFileOfNode(node), node.end - 1, ";".length, Diagnostics._0_expected, "{");
return grammarErrorAtPos(node, node.end - 1, ";".length, Diagnostics._0_expected, "{");
}
}
@@ -24696,7 +24720,7 @@ namespace ts {
if (node.initializer) {
// Error on equals token which immediately precedes the initializer
return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - 1, 1, Diagnostics.A_rest_element_cannot_have_an_initializer);
return grammarErrorAtPos(node, node.initializer.pos - 1, 1, Diagnostics.A_rest_element_cannot_have_an_initializer);
}
}
}
@@ -24719,15 +24743,13 @@ namespace ts {
else {
// Error on equals token which immediate precedes the initializer
const equalsTokenLength = "=".length;
return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - equalsTokenLength,
equalsTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
return grammarErrorAtPos(node, node.initializer.pos - equalsTokenLength, equalsTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
}
}
if (node.initializer && !(isConst(node) && isStringOrNumberLiteralExpression(node.initializer))) {
// Error on equals token which immediate precedes the initializer
const equalsTokenLength = "=".length;
return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - equalsTokenLength,
equalsTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
return grammarErrorAtPos(node, node.initializer.pos - equalsTokenLength, equalsTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
}
}
else if (!node.initializer) {
@@ -24796,7 +24818,7 @@ namespace ts {
}
if (!declarationList.declarations.length) {
return grammarErrorAtPos(getSourceFileOfNode(declarationList), declarations.pos, declarations.end - declarations.pos, Diagnostics.Variable_declaration_list_cannot_be_empty);
return grammarErrorAtPos(declarationList, declarations.pos, declarations.end - declarations.pos, Diagnostics.Variable_declaration_list_cannot_be_empty);
}
}
@@ -24849,7 +24871,8 @@ namespace ts {
}
}
function grammarErrorAtPos(sourceFile: SourceFile, start: number, length: number, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean {
function grammarErrorAtPos(nodeForSourceFile: Node, start: number, length: number, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean {
const sourceFile = getSourceFileOfNode(nodeForSourceFile);
if (!hasParseDiagnostics(sourceFile)) {
diagnostics.add(createFileDiagnostic(sourceFile, start, length, message, arg0, arg1, arg2));
return true;
@@ -24866,7 +24889,7 @@ namespace ts {
function checkGrammarConstructorTypeParameters(node: ConstructorDeclaration) {
if (node.typeParameters) {
return grammarErrorAtPos(getSourceFileOfNode(node), node.typeParameters.pos, node.typeParameters.end - node.typeParameters.pos, Diagnostics.Type_parameters_cannot_appear_on_a_constructor_declaration);
return grammarErrorAtPos(node, node.typeParameters.pos, node.typeParameters.end - node.typeParameters.pos, Diagnostics.Type_parameters_cannot_appear_on_a_constructor_declaration);
}
}
@@ -24924,7 +24947,7 @@ namespace ts {
node.kind === SyntaxKind.ExportDeclaration ||
node.kind === SyntaxKind.ExportAssignment ||
node.kind === SyntaxKind.NamespaceExportDeclaration ||
getModifierFlags(node) & (ModifierFlags.Ambient | ModifierFlags.Export | ModifierFlags.Default)) {
hasModifier(node, ModifierFlags.Ambient | ModifierFlags.Export | ModifierFlags.Default)) {
return false;
}
+8 -3
View File
@@ -340,7 +340,6 @@ namespace ts {
isTSConfigOnly: true,
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.A_series_of_entries_which_re_map_imports_to_lookup_locations_relative_to_the_baseUrl
},
{
// this option can only be specified in tsconfig.json
@@ -384,6 +383,12 @@ namespace ts {
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.Allow_default_imports_from_modules_with_no_default_export_This_does_not_affect_code_emit_just_typechecking
},
{
name: "preserveSymlinks",
type: "boolean",
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.Do_not_resolve_the_real_path_of_symlinks,
},
// Source Maps
{
@@ -1111,7 +1116,7 @@ namespace ts {
if (option && typeof option.type !== "string") {
const customOption = <CommandLineOptionOfCustomType>option;
// Validate custom option type
if (!customOption.type.has(text)) {
if (!customOption.type.has(text.toLowerCase())) {
errors.push(
createDiagnosticForInvalidCustomType(
customOption,
@@ -1806,7 +1811,7 @@ namespace ts {
return value;
}
else if (typeof option.type !== "string") {
return option.type.get(value);
return option.type.get(typeof value === "string" ? value.toLowerCase() : value);
}
return normalizeNonListOptionValue(option, basePath, value);
}
+1 -11
View File
@@ -415,17 +415,7 @@ namespace ts {
* @return true if the comment is a triple-slash comment else false
*/
function isTripleSlashComment(commentPos: number, commentEnd: number) {
// Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
// so that we don't end up computing comment string and doing match for all // comments
if (currentText.charCodeAt(commentPos + 1) === CharacterCodes.slash &&
commentPos + 2 < commentEnd &&
currentText.charCodeAt(commentPos + 2) === CharacterCodes.slash) {
const textSubStr = currentText.substring(commentPos, commentEnd);
return textSubStr.match(fullTripleSlashReferencePathRegEx) ||
textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ?
true : false;
}
return false;
return isRecognizedTripleSlashComment(currentText, commentPos, commentEnd);
}
}
}
+15 -14
View File
@@ -501,13 +501,15 @@ namespace ts {
return result || array;
}
export function mapDefined<T, U>(array: ReadonlyArray<T>, mapFn: (x: T, i: number) => U | undefined): U[] {
export function mapDefined<T, U>(array: ReadonlyArray<T> | undefined, mapFn: (x: T, i: number) => U | undefined): U[] {
const result: U[] = [];
for (let i = 0; i < array.length; i++) {
const item = array[i];
const mapped = mapFn(item, i);
if (mapped !== undefined) {
result.push(mapped);
if (array) {
for (let i = 0; i < array.length; i++) {
const item = array[i];
const mapped = mapFn(item, i);
if (mapped !== undefined) {
result.push(mapped);
}
}
}
return result;
@@ -2208,20 +2210,14 @@ namespace ts {
/** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */
export const supportedTypescriptExtensionsForExtractExtension: ReadonlyArray<Extension> = [Extension.Dts, Extension.Ts, Extension.Tsx];
export const supportedJavascriptExtensions: ReadonlyArray<Extension> = [Extension.Js, Extension.Jsx];
const allSupportedExtensions = [...supportedTypeScriptExtensions, ...supportedJavascriptExtensions];
const allSupportedExtensions: ReadonlyArray<Extension> = [...supportedTypeScriptExtensions, ...supportedJavascriptExtensions];
export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: ReadonlyArray<JsFileExtensionInfo>): ReadonlyArray<string> {
const needAllExtensions = options && options.allowJs;
if (!extraFileExtensions || extraFileExtensions.length === 0 || !needAllExtensions) {
return needAllExtensions ? allSupportedExtensions : supportedTypeScriptExtensions;
}
const extensions: string[] = allSupportedExtensions.slice(0);
for (const extInfo of extraFileExtensions) {
if (extensions.indexOf(extInfo.extension) === -1) {
extensions.push(extInfo.extension);
}
}
return extensions;
return deduplicate([...allSupportedExtensions, ...extraFileExtensions.map(e => e.extension)]);
}
export function hasJavaScriptFileExtension(fileName: string) {
@@ -2588,6 +2584,11 @@ namespace ts {
}
Debug.fail(`File ${path} has unknown extension.`);
}
export function isAnySupportedFileExtension(path: string): boolean {
return tryGetExtensionFromPath(path) !== undefined;
}
export function tryGetExtensionFromPath(path: string): Extension | undefined {
return find<Extension>(supportedTypescriptExtensionsForExtractExtension, e => fileExtensionIs(path, e)) || find(supportedJavascriptExtensions, e => fileExtensionIs(path, e));
}
+6 -3
View File
@@ -349,7 +349,6 @@ namespace ts {
errorNameNode = declaration.name;
const format = TypeFormatFlags.UseTypeOfFunction |
TypeFormatFlags.WriteClassExpressionAsTypeLiteral |
TypeFormatFlags.UseTypeAliasValue |
(shouldUseResolverType ? TypeFormatFlags.AddUndefined : 0);
resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, format, writer);
errorNameNode = undefined;
@@ -368,7 +367,7 @@ namespace ts {
resolver.writeReturnTypeOfSignatureDeclaration(
signature,
enclosingDeclaration,
TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue | TypeFormatFlags.WriteClassExpressionAsTypeLiteral,
TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.WriteClassExpressionAsTypeLiteral,
writer);
errorNameNode = undefined;
}
@@ -633,7 +632,7 @@ namespace ts {
resolver.writeTypeOfExpression(
expr,
enclosingDeclaration,
TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue | TypeFormatFlags.WriteClassExpressionAsTypeLiteral,
TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.WriteClassExpressionAsTypeLiteral,
writer);
write(";");
writeLine();
@@ -1367,6 +1366,10 @@ namespace ts {
}
function writeVariableStatement(node: VariableStatement) {
// If binding pattern doesn't have name, then there is nothing to be emitted for declaration file i.e. const [,] = [1,2].
if (every(node.declarationList && node.declarationList.declarations, decl => decl.name && isEmptyBindingPattern(decl.name))) {
return;
}
emitJsDocComments(node);
emitModuleElementDeclarationFlags(node);
if (isLet(node.declarationList)) {
+14
View File
@@ -2662,6 +2662,10 @@
"category": "Message",
"code": 6012
},
"Do not resolve the real path of symlinks.": {
"category": "Message",
"code": 6013
},
"Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'.": {
"category": "Message",
"code": 6015
@@ -3677,5 +3681,15 @@
"Convert function '{0}' to class": {
"category": "Message",
"code": 95002
},
"Extract function": {
"category": "Message",
"code": 95003
},
"Extract function into '{0}'": {
"category": "Message",
"code": 95004
}
}
+4 -2
View File
@@ -1195,7 +1195,9 @@ namespace ts {
if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) {
const dotRangeStart = node.expression.end;
const dotRangeEnd = skipTrivia(currentSourceFile.text, node.expression.end) + 1;
const dotToken = <Node>{ kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd };
const dotToken = createToken(SyntaxKind.DotToken);
dotToken.pos = dotRangeStart;
dotToken.end = dotRangeEnd;
indentBeforeDot = needsIndentation(node, node.expression, dotToken);
indentAfterDot = needsIndentation(node, dotToken, node.name);
}
@@ -1348,7 +1350,7 @@ namespace ts {
increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined);
emitLeadingCommentsOfPosition(node.operatorToken.pos);
writeTokenNode(node.operatorToken);
emitTrailingCommentsOfPosition(node.operatorToken.end);
emitTrailingCommentsOfPosition(node.operatorToken.end, /*prefixSpace*/ true); // Binary operators should have a space before the comment starts
increaseIndentIf(indentAfterOperator, " ");
emitExpression(node.right);
decreaseIndentIf(indentBeforeOperator, indentAfterOperator);
+2 -2
View File
@@ -2586,7 +2586,7 @@ namespace ts {
}
export function addSyntheticLeadingComment<T extends Node>(node: T, kind: SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia, text: string, hasTrailingNewLine?: boolean) {
return setSyntheticLeadingComments(node, append(getSyntheticLeadingComments(node), <SynthesizedComment>{ kind, pos: -1, end: -1, hasTrailingNewLine, text }));
return setSyntheticLeadingComments(node, append<SynthesizedComment>(getSyntheticLeadingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text }));
}
export function getSyntheticTrailingComments(node: Node): SynthesizedComment[] | undefined {
@@ -2600,7 +2600,7 @@ namespace ts {
}
export function addSyntheticTrailingComment<T extends Node>(node: T, kind: SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia, text: string, hasTrailingNewLine?: boolean) {
return setSyntheticTrailingComments(node, append(getSyntheticTrailingComments(node), <SynthesizedComment>{ kind, pos: -1, end: -1, hasTrailingNewLine, text }));
return setSyntheticTrailingComments(node, append<SynthesizedComment>(getSyntheticTrailingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text }));
}
/**
+87 -36
View File
@@ -13,13 +13,32 @@ namespace ts {
return compilerOptions.traceResolution && host.trace !== undefined;
}
/**
* Result of trying to resolve a module.
* At least one of `ts` and `js` should be defined, or the whole thing should be `undefined`.
*/
/** Array that is only intended to be pushed to, never read. */
/* @internal */
export interface Push<T> {
push(value: T): void;
}
function withPackageId(packageId: PackageId | undefined, r: PathAndExtension | undefined): Resolved {
return r && { path: r.path, extension: r.ext, packageId };
}
function noPackageId(r: PathAndExtension | undefined): Resolved {
return withPackageId(/*packageId*/ undefined, r);
}
/** Result of trying to resolve a module. */
interface Resolved {
path: string;
extension: Extension;
packageId: PackageId | undefined;
}
/** Result of trying to resolve a module at a file. Needs to have 'packageId' added later. */
interface PathAndExtension {
path: string;
// (Use a different name than `extension` to make sure Resolved isn't assignable to PathAndExtension.)
ext: Extension;
}
/**
@@ -43,7 +62,7 @@ namespace ts {
function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
return {
resolvedModule: resolved && { resolvedFileName: resolved.path, extension: resolved.extension, isExternalLibraryImport },
resolvedModule: resolved && { resolvedFileName: resolved.path, extension: resolved.extension, isExternalLibraryImport, packageId: resolved.packageId },
failedLookupLocations
};
}
@@ -54,9 +73,16 @@ namespace ts {
traceEnabled: boolean;
}
interface PackageJson {
name?: string;
version?: string;
typings?: string;
types?: string;
main?: string;
}
/** Reads from "main" or "types"/"typings" depending on `extensions`. */
function tryReadPackageJsonFields(readTypes: boolean, packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string | undefined {
const jsonContent = readJson(packageJsonPath, state.host);
function tryReadPackageJsonFields(readTypes: boolean, jsonContent: PackageJson, baseDirectory: string, state: ModuleResolutionState): string | undefined {
return readTypes ? tryReadFromField("typings") || tryReadFromField("types") : tryReadFromField("main");
function tryReadFromField(fieldName: "typings" | "types" | "main"): string | undefined {
@@ -83,7 +109,7 @@ namespace ts {
}
}
function readJson(path: string, host: ModuleResolutionHost): { typings?: string, types?: string, main?: string } {
function readJson(path: string, host: ModuleResolutionHost): PackageJson {
try {
const jsonText = host.readFile(path);
return jsonText ? JSON.parse(jsonText) : {};
@@ -174,7 +200,10 @@ namespace ts {
let resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined;
if (resolved) {
resolved = realpath(resolved, host, traceEnabled);
if (!options.preserveSymlinks) {
resolved = realPath(resolved, host, traceEnabled);
}
if (traceEnabled) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolved, primary);
}
@@ -646,7 +675,7 @@ namespace ts {
if (extension !== undefined) {
const path = tryFile(candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state);
if (path !== undefined) {
return { path, extension };
return { path, extension, packageId: undefined };
}
}
@@ -708,8 +737,14 @@ namespace ts {
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder_target_file_type_1, moduleName, Extensions[extensions]);
}
const resolved = loadModuleFromNodeModules(extensions, moduleName, containingDirectory, failedLookupLocations, state, cache);
if (!resolved) return undefined;
let resolvedValue = resolved.value;
if (!compilerOptions.preserveSymlinks) {
resolvedValue = resolvedValue && { ...resolved.value, path: realPath(resolved.value.path, host, traceEnabled), extension: resolved.value.extension };
}
// For node_modules lookups, get the real path so that multiple accesses to an `npm link`-ed module do not create duplicate files.
return resolved && { value: resolved.value && { resolved: { path: realpath(resolved.value.path, host, traceEnabled), extension: resolved.value.extension }, isExternalLibraryImport: true } };
return { value: resolvedValue && { resolved: resolvedValue, isExternalLibraryImport: true } };
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
@@ -719,7 +754,7 @@ namespace ts {
}
}
function realpath(path: string, host: ModuleResolutionHost, traceEnabled: boolean): string {
function realPath(path: string, host: ModuleResolutionHost, traceEnabled: boolean): string {
if (!host.realpath) {
return path;
}
@@ -747,7 +782,7 @@ namespace ts {
}
const resolvedFromFile = loadModuleFromFile(extensions, candidate, failedLookupLocations, onlyRecordFailures, state);
if (resolvedFromFile) {
return resolvedFromFile;
return noPackageId(resolvedFromFile);
}
}
if (!onlyRecordFailures) {
@@ -768,11 +803,15 @@ namespace ts {
return !host.directoryExists || host.directoryExists(directoryName);
}
function loadModuleFromFileNoPackageId(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved {
return noPackageId(loadModuleFromFile(extensions, candidate, failedLookupLocations, onlyRecordFailures, state));
}
/**
* @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary
* in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations.
*/
function loadModuleFromFile(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
function loadModuleFromFile(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
// First, try adding an extension. An import of "foo" could be matched by a file "foo.ts", or "foo.js" by "foo.js.ts"
const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, failedLookupLocations, onlyRecordFailures, state);
if (resolvedByAddingExtension) {
@@ -792,7 +831,7 @@ namespace ts {
}
/** Try to return an existing file that adds one of the `extensions` to `candidate`. */
function tryAddingExtensions(candidate: string, extensions: Extensions, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
function tryAddingExtensions(candidate: string, extensions: Extensions, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
if (!onlyRecordFailures) {
// check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
const directory = getDirectoryPath(candidate);
@@ -810,9 +849,9 @@ namespace ts {
return tryExtension(Extension.Js) || tryExtension(Extension.Jsx);
}
function tryExtension(extension: Extension): Resolved | undefined {
const path = tryFile(candidate + extension, failedLookupLocations, onlyRecordFailures, state);
return path && { path, extension };
function tryExtension(ext: Extension): PathAndExtension | undefined {
const path = tryFile(candidate + ext, failedLookupLocations, onlyRecordFailures, state);
return path && { path, ext };
}
}
@@ -838,12 +877,23 @@ namespace ts {
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true): Resolved | undefined {
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
let packageId: PackageId | undefined;
if (considerPackageJson) {
const packageJsonPath = pathToPackageJson(candidate);
if (directoryExists && state.host.fileExists(packageJsonPath)) {
const fromPackageJson = loadModuleFromPackageJson(packageJsonPath, extensions, candidate, failedLookupLocations, state);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
const jsonContent = readJson(packageJsonPath, state.host);
if (typeof jsonContent.name === "string" && typeof jsonContent.version === "string") {
packageId = { name: jsonContent.name, version: jsonContent.version };
}
const fromPackageJson = loadModuleFromPackageJson(jsonContent, extensions, candidate, failedLookupLocations, state);
if (fromPackageJson) {
return fromPackageJson;
return withPackageId(packageId, fromPackageJson);
}
}
else {
@@ -855,15 +905,11 @@ namespace ts {
}
}
return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state);
return withPackageId(packageId, loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state));
}
function loadModuleFromPackageJson(packageJsonPath: string, extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): Resolved | undefined {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
const file = tryReadPackageJsonFields(extensions !== Extensions.JavaScript, packageJsonPath, candidate, state);
function loadModuleFromPackageJson(jsonContent: PackageJson, extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): PathAndExtension | undefined {
const file = tryReadPackageJsonFields(extensions !== Extensions.JavaScript, jsonContent, candidate, state);
if (!file) {
return undefined;
}
@@ -883,13 +929,18 @@ namespace ts {
// Even if extensions is DtsOnly, we can still look up a .ts file as a result of package.json "types"
const nextExtensions = extensions === Extensions.DtsOnly ? Extensions.TypeScript : extensions;
// Don't do package.json lookup recursively, because Node.js' package lookup doesn't.
return nodeLoadModuleByRelativeName(nextExtensions, file, failedLookupLocations, onlyRecordFailures, state, /*considerPackageJson*/ false);
const result = nodeLoadModuleByRelativeName(nextExtensions, file, failedLookupLocations, onlyRecordFailures, state, /*considerPackageJson*/ false);
if (result) {
// It won't have a `packageId` set, because we disabled `considerPackageJson`.
Debug.assert(result.packageId === undefined);
return { path: result.path, ext: result.extension };
}
}
/** Resolve from an arbitrarily specified file. Return `undefined` if it has an unsupported extension. */
function resolvedIfExtensionMatches(extensions: Extensions, path: string): Resolved | undefined {
const extension = tryGetExtensionFromPath(path);
return extension !== undefined && extensionIsOk(extensions, extension) ? { path, extension } : undefined;
function resolvedIfExtensionMatches(extensions: Extensions, path: string): PathAndExtension | undefined {
const ext = tryGetExtensionFromPath(path);
return ext !== undefined && extensionIsOk(extensions, ext) ? { path, ext } : undefined;
}
/** True if `extension` is one of the supported `extensions`. */
@@ -911,7 +962,7 @@ namespace ts {
function loadModuleFromNodeModulesFolder(extensions: Extensions, moduleName: string, nodeModulesFolder: string, nodeModulesFolderExists: boolean, failedLookupLocations: Push<string>, state: ModuleResolutionState): Resolved | undefined {
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
return loadModuleFromFile(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
return loadModuleFromFileNoPackageId(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
loadNodeModuleFromDirectory(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
}
@@ -996,7 +1047,7 @@ namespace ts {
if (traceEnabled) {
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache, moduleName);
}
return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, extension: result.resolvedModule.extension } };
return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, extension: result.resolvedModule.extension, packageId: result.resolvedModule.packageId } };
}
}
@@ -1010,7 +1061,7 @@ namespace ts {
return createResolvedModuleWithFailedLookupLocations(resolved && resolved.value, /*isExternalLibraryImport*/ false, failedLookupLocations);
function tryResolve(extensions: Extensions): SearchResult<Resolved> {
const resolvedUsingSettings = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loadModuleFromFile, failedLookupLocations, state);
const resolvedUsingSettings = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loadModuleFromFileNoPackageId, failedLookupLocations, state);
if (resolvedUsingSettings) {
return { value: resolvedUsingSettings };
}
@@ -1024,7 +1075,7 @@ namespace ts {
return resolutionFromCache;
}
const searchName = normalizePath(combinePaths(directory, moduleName));
return toSearchResult(loadModuleFromFile(extensions, searchName, failedLookupLocations, /*onlyRecordFailures*/ false, state));
return toSearchResult(loadModuleFromFileNoPackageId(extensions, searchName, failedLookupLocations, /*onlyRecordFailures*/ false, state));
});
if (resolved) {
return resolved;
@@ -1036,7 +1087,7 @@ namespace ts {
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
return toSearchResult(loadModuleFromFile(extensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state));
return toSearchResult(loadModuleFromFileNoPackageId(extensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state));
}
}
}
+4 -4
View File
@@ -3202,7 +3202,7 @@ namespace ts {
return undefined;
}
const isAsync = !!(getModifierFlags(arrowFunction) & ModifierFlags.Async);
const isAsync = hasModifier(arrowFunction, ModifierFlags.Async);
// If we have an arrow, then try to parse the body. Even if not, try to parse if we
// have an opening brace, just in case we're in an error state.
@@ -3382,7 +3382,7 @@ namespace ts {
function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction {
const node = <ArrowFunction>createNode(SyntaxKind.ArrowFunction);
node.modifiers = parseModifiersForArrowFunction();
const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
// Arrow functions are never generators.
//
@@ -4515,7 +4515,7 @@ namespace ts {
node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken);
const isGenerator = node.asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
node.name =
isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) :
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
@@ -6169,7 +6169,7 @@ namespace ts {
export function parseIsolatedJSDocComment(content: string, start: number, length: number): { jsDoc: JSDoc, diagnostics: Diagnostic[] } | undefined {
initializeState(content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS);
sourceFile = <SourceFile>{ languageVariant: LanguageVariant.Standard, text: content };
sourceFile = <SourceFile>{ languageVariant: LanguageVariant.Standard, text: content }; // tslint:disable-line no-object-literal-type-assertion
const jsDoc = parseJSDocCommentWorker(start, length);
const diagnostics = parseDiagnostics;
clearState();
+98 -6
View File
@@ -472,6 +472,15 @@ namespace ts {
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(checkAllDefined(typeReferenceDirectiveNames), containingFile, loader);
}
// Map from a stringified PackageId to the source file with that id.
// Only one source file may have a given packageId. Others become redirects (see createRedirectSourceFile).
// `packageIdToSourceFile` is only used while building the program, while `sourceFileToPackageName` and `isSourceFileTargetOfRedirect` are kept around.
const packageIdToSourceFile = createMap<SourceFile>();
// Maps from a SourceFile's `.path` to the name of the package it was imported with.
let sourceFileToPackageName = createMap<string>();
// See `sourceFileIsRedirectedTo`.
let redirectTargetsSet = createMap<true>();
const filesByName = createMap<SourceFile | undefined>();
// stores 'filename -> file association' ignoring case
// used to track cases when two file names differ only in casing
@@ -548,6 +557,8 @@ namespace ts {
isSourceFileFromExternalLibrary,
dropDiagnosticsProducingTypeChecker,
getSourceFileFromReference,
sourceFileToPackageName,
redirectTargetsSet,
};
verifyCompilerOptions();
@@ -773,8 +784,12 @@ namespace ts {
const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
oldProgram.structureIsReused = StructureIsReused.Completely;
for (const oldSourceFile of oldProgram.getSourceFiles()) {
const newSourceFile = host.getSourceFileByPath
const oldSourceFiles = oldProgram.getSourceFiles();
const enum SeenPackageName { Exists, Modified }
const seenPackageNames = createMap<SeenPackageName>();
for (const oldSourceFile of oldSourceFiles) {
let newSourceFile = host.getSourceFileByPath
? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target)
: host.getSourceFile(oldSourceFile.fileName, options.target);
@@ -782,10 +797,46 @@ namespace ts {
return oldProgram.structureIsReused = StructureIsReused.Not;
}
Debug.assert(!newSourceFile.redirectInfo, "Host should not return a redirect source file from `getSourceFile`");
let fileChanged: boolean;
if (oldSourceFile.redirectInfo) {
// We got `newSourceFile` by path, so it is actually for the unredirected file.
// This lets us know if the unredirected file has changed. If it has we should break the redirect.
if (newSourceFile !== oldSourceFile.redirectInfo.unredirected) {
// Underlying file has changed. Might not redirect anymore. Must rebuild program.
return oldProgram.structureIsReused = StructureIsReused.Not;
}
fileChanged = false;
newSourceFile = oldSourceFile; // Use the redirect.
}
else if (oldProgram.redirectTargetsSet.has(oldSourceFile.path)) {
// If a redirected-to source file changes, the redirect may be broken.
if (newSourceFile !== oldSourceFile) {
return oldProgram.structureIsReused = StructureIsReused.Not;
}
fileChanged = false;
}
else {
fileChanged = newSourceFile !== oldSourceFile;
}
newSourceFile.path = oldSourceFile.path;
filePaths.push(newSourceFile.path);
if (oldSourceFile !== newSourceFile) {
const packageName = oldProgram.sourceFileToPackageName.get(oldSourceFile.path);
if (packageName !== undefined) {
// If there are 2 different source files for the same package name and at least one of them changes,
// they might become redirects. So we must rebuild the program.
const prevKind = seenPackageNames.get(packageName);
const newKind = fileChanged ? SeenPackageName.Modified : SeenPackageName.Exists;
if ((prevKind !== undefined && newKind === SeenPackageName.Modified) || prevKind === SeenPackageName.Modified) {
return oldProgram.structureIsReused = StructureIsReused.Not;
}
seenPackageNames.set(packageName, newKind);
}
if (fileChanged) {
// The `newSourceFile` object was created for the new program.
if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
@@ -897,6 +948,9 @@ namespace ts {
}
resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
sourceFileToPackageName = oldProgram.sourceFileToPackageName;
redirectTargetsSet = oldProgram.redirectTargetsSet;
return oldProgram.structureIsReused = StructureIsReused.Completely;
}
@@ -1537,7 +1591,7 @@ namespace ts {
/** This has side effects through `findSourceFile`. */
function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): void {
getSourceFileFromReferenceWorker(fileName,
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd),
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd, /*packageId*/ undefined),
(diagnostic, ...args) => {
fileProcessingDiagnostics.add(refFile !== undefined && refEnd !== undefined && refPos !== undefined
? createFileDiagnostic(refFile, refPos, refEnd - refPos, diagnostic, ...args)
@@ -1556,8 +1610,26 @@ namespace ts {
}
}
function createRedirectSourceFile(redirectTarget: SourceFile, unredirected: SourceFile, fileName: string, path: Path): SourceFile {
const redirect: SourceFile = Object.create(redirectTarget);
redirect.fileName = fileName;
redirect.path = path;
redirect.redirectInfo = { redirectTarget, unredirected };
Object.defineProperties(redirect, {
id: {
get(this: SourceFile) { return this.redirectInfo.redirectTarget.id; },
set(this: SourceFile, value: SourceFile["id"]) { this.redirectInfo.redirectTarget.id = value; },
},
symbol: {
get(this: SourceFile) { return this.redirectInfo.redirectTarget.symbol; },
set(this: SourceFile, value: SourceFile["symbol"]) { this.redirectInfo.redirectTarget.symbol = value; },
},
});
return redirect;
}
// Get source file from normalized fileName
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile {
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, refFile: SourceFile, refPos: number, refEnd: number, packageId: PackageId | undefined): SourceFile | undefined {
if (filesByName.has(path)) {
const file = filesByName.get(path);
// try to check if we've already seen this file but with a different casing in path
@@ -1600,6 +1672,26 @@ namespace ts {
}
});
if (packageId) {
const packageIdKey = `${packageId.name}@${packageId.version}`;
const fileFromPackageId = packageIdToSourceFile.get(packageIdKey);
if (fileFromPackageId) {
// Some other SourceFile already exists with this package name and version.
// Instead of creating a duplicate, just redirect to the existing one.
const dupFile = createRedirectSourceFile(fileFromPackageId, file, fileName, path);
redirectTargetsSet.set(fileFromPackageId.path, true);
filesByName.set(path, dupFile);
sourceFileToPackageName.set(path, packageId.name);
files.push(dupFile);
return dupFile;
}
else if (file) {
// This is the first source file to have this packageId.
packageIdToSourceFile.set(packageIdKey, file);
sourceFileToPackageName.set(path, packageId.name);
}
}
filesByName.set(path, file);
if (file) {
sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
@@ -1762,7 +1854,7 @@ namespace ts {
else if (shouldAddFile) {
const path = toPath(resolvedFileName);
const pos = skipTrivia(file.text, file.imports[i].pos);
findSourceFile(resolvedFileName, path, /*isDefaultLib*/ false, file, pos, file.imports[i].end);
findSourceFile(resolvedFileName, path, /*isDefaultLib*/ false, file, pos, file.imports[i].end, resolution.packageId);
}
if (isFromNodeModulesSearch) {
+4 -1
View File
@@ -331,11 +331,14 @@ namespace ts {
location
);
}
else if (numElements !== 1 && (flattenContext.level < FlattenLevel.ObjectRest || numElements === 0)) {
else if (numElements !== 1 && (flattenContext.level < FlattenLevel.ObjectRest || numElements === 0)
|| every(elements, isOmittedExpression)) {
// For anything other than a single-element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once. Additionally, if we have zero elements
// we need to emit *something* to ensure that in case a 'var' keyword was already emitted,
// so in that case, we'll intentionally create that temporary.
// Or all the elements of the binding pattern are omitted expression such as "var [,] = [1,2]",
// then we will create temporary variable.
const reuseIdentifierExpressions = !isDeclarationBindingElement(parent) || numElements !== 0;
value = ensureIdentifier(flattenContext, value, reuseIdentifierExpressions, location);
}
+9 -6
View File
@@ -394,7 +394,7 @@ namespace ts {
function shouldVisitNode(node: Node): boolean {
return (node.transformFlags & TransformFlags.ContainsES2015) !== 0
|| convertedLoopState !== undefined
|| (hierarchyFacts & HierarchyFacts.ConstructorWithCapturedSuper && isStatement(node))
|| (hierarchyFacts & HierarchyFacts.ConstructorWithCapturedSuper && (isStatement(node) || (node.kind === SyntaxKind.Block)))
|| (isIterationStatement(node, /*lookInLabeledStatements*/ false) && shouldConvertIterationStatementBody(node))
|| isTypeScriptClassWrapper(node);
}
@@ -840,7 +840,7 @@ namespace ts {
outer.end = skipTrivia(currentText, node.pos);
setEmitFlags(outer, EmitFlags.NoComments);
return createParen(
const result = createParen(
createCall(
outer,
/*typeArguments*/ undefined,
@@ -849,6 +849,8 @@ namespace ts {
: []
)
);
addSyntheticLeadingComment(result, SyntaxKind.MultiLineCommentTrivia, "* @class ");
return result;
}
/**
@@ -2105,13 +2107,14 @@ namespace ts {
setCommentRange(declarationList, node);
if (node.transformFlags & TransformFlags.ContainsBindingPattern
&& (isBindingPattern(node.declarations[0].name)
|| isBindingPattern(lastOrUndefined(node.declarations).name))) {
&& (isBindingPattern(node.declarations[0].name) || isBindingPattern(lastOrUndefined(node.declarations).name))) {
// If the first or last declaration is a binding pattern, we need to modify
// the source map range for the declaration list.
const firstDeclaration = firstOrUndefined(declarations);
const lastDeclaration = lastOrUndefined(declarations);
setSourceMapRange(declarationList, createRange(firstDeclaration.pos, lastDeclaration.end));
if (firstDeclaration) {
const lastDeclaration = lastOrUndefined(declarations);
setSourceMapRange(declarationList, createRange(firstDeclaration.pos, lastDeclaration.end));
}
}
return declarationList;
+46 -48
View File
@@ -164,12 +164,11 @@ namespace ts {
}
// A generated code block
interface CodeBlock {
kind: CodeBlockKind;
}
type CodeBlock = | ExceptionBlock | LabeledBlock | SwitchBlock | LoopBlock | WithBlock;
// a generated exception block, used for 'try' statements
interface ExceptionBlock extends CodeBlock {
interface ExceptionBlock {
kind: CodeBlockKind.Exception;
state: ExceptionBlockState;
startLabel: Label;
catchVariable?: Identifier;
@@ -179,27 +178,31 @@ namespace ts {
}
// A generated code that tracks the target for 'break' statements in a LabeledStatement.
interface LabeledBlock extends CodeBlock {
interface LabeledBlock {
kind: CodeBlockKind.Labeled;
labelText: string;
isScript: boolean;
breakLabel: Label;
}
// a generated block that tracks the target for 'break' statements in a 'switch' statement
interface SwitchBlock extends CodeBlock {
interface SwitchBlock {
kind: CodeBlockKind.Switch;
isScript: boolean;
breakLabel: Label;
}
// a generated block that tracks the targets for 'break' and 'continue' statements, used for iteration statements
interface LoopBlock extends CodeBlock {
interface LoopBlock {
kind: CodeBlockKind.Loop;
continueLabel: Label;
isScript: boolean;
breakLabel: Label;
}
// a generated block associated with a 'with' statement
interface WithBlock extends CodeBlock {
interface WithBlock {
kind: CodeBlockKind.With;
expression: Identifier;
startLabel: Label;
endLabel: Label;
@@ -2070,7 +2073,7 @@ namespace ts {
const startLabel = defineLabel();
const endLabel = defineLabel();
markLabel(startLabel);
beginBlock(<WithBlock>{
beginBlock({
kind: CodeBlockKind.With,
expression,
startLabel,
@@ -2087,10 +2090,6 @@ namespace ts {
markLabel(block.endLabel);
}
function isWithBlock(block: CodeBlock): block is WithBlock {
return block.kind === CodeBlockKind.With;
}
/**
* Begins a code block for a generated `try` statement.
*/
@@ -2098,7 +2097,7 @@ namespace ts {
const startLabel = defineLabel();
const endLabel = defineLabel();
markLabel(startLabel);
beginBlock(<ExceptionBlock>{
beginBlock({
kind: CodeBlockKind.Exception,
state: ExceptionBlockState.Try,
startLabel,
@@ -2188,10 +2187,6 @@ namespace ts {
exception.state = ExceptionBlockState.Done;
}
function isExceptionBlock(block: CodeBlock): block is ExceptionBlock {
return block.kind === CodeBlockKind.Exception;
}
/**
* Begins a code block that supports `break` or `continue` statements that are defined in
* the source tree and not from generated code.
@@ -2199,7 +2194,7 @@ namespace ts {
* @param labelText Names from containing labeled statements.
*/
function beginScriptLoopBlock(): void {
beginBlock(<LoopBlock>{
beginBlock({
kind: CodeBlockKind.Loop,
isScript: true,
breakLabel: -1,
@@ -2217,7 +2212,7 @@ namespace ts {
*/
function beginLoopBlock(continueLabel: Label): Label {
const breakLabel = defineLabel();
beginBlock(<LoopBlock>{
beginBlock({
kind: CodeBlockKind.Loop,
isScript: false,
breakLabel,
@@ -2245,7 +2240,7 @@ namespace ts {
*
*/
function beginScriptSwitchBlock(): void {
beginBlock(<SwitchBlock>{
beginBlock({
kind: CodeBlockKind.Switch,
isScript: true,
breakLabel: -1
@@ -2259,7 +2254,7 @@ namespace ts {
*/
function beginSwitchBlock(): Label {
const breakLabel = defineLabel();
beginBlock(<SwitchBlock>{
beginBlock({
kind: CodeBlockKind.Switch,
isScript: false,
breakLabel,
@@ -2280,7 +2275,7 @@ namespace ts {
}
function beginScriptLabeledBlock(labelText: string) {
beginBlock(<LabeledBlock>{
beginBlock({
kind: CodeBlockKind.Labeled,
isScript: true,
labelText,
@@ -2290,7 +2285,7 @@ namespace ts {
function beginLabeledBlock(labelText: string) {
const breakLabel = defineLabel();
beginBlock(<LabeledBlock>{
beginBlock({
kind: CodeBlockKind.Labeled,
isScript: false,
labelText,
@@ -2878,34 +2873,37 @@ namespace ts {
for (; blockIndex < blockActions.length && blockOffsets[blockIndex] <= operationIndex; blockIndex++) {
const block = blocks[blockIndex];
const blockAction = blockActions[blockIndex];
if (isExceptionBlock(block)) {
if (blockAction === BlockAction.Open) {
if (!exceptionBlockStack) {
exceptionBlockStack = [];
}
switch (block.kind) {
case CodeBlockKind.Exception:
if (blockAction === BlockAction.Open) {
if (!exceptionBlockStack) {
exceptionBlockStack = [];
}
if (!statements) {
statements = [];
}
if (!statements) {
statements = [];
}
exceptionBlockStack.push(currentExceptionBlock);
currentExceptionBlock = block;
}
else if (blockAction === BlockAction.Close) {
currentExceptionBlock = exceptionBlockStack.pop();
}
}
else if (isWithBlock(block)) {
if (blockAction === BlockAction.Open) {
if (!withBlockStack) {
withBlockStack = [];
exceptionBlockStack.push(currentExceptionBlock);
currentExceptionBlock = block;
}
else if (blockAction === BlockAction.Close) {
currentExceptionBlock = exceptionBlockStack.pop();
}
break;
case CodeBlockKind.With:
if (blockAction === BlockAction.Open) {
if (!withBlockStack) {
withBlockStack = [];
}
withBlockStack.push(block);
}
else if (blockAction === BlockAction.Close) {
withBlockStack.pop();
}
withBlockStack.push(block);
}
else if (blockAction === BlockAction.Close) {
withBlockStack.pop();
}
break;
// default: do nothing
}
}
}
+1 -1
View File
@@ -114,7 +114,7 @@ namespace ts {
compilerOptions.reactNamespace,
tagName,
objectProperties,
filter(map(children, transformJsxChildToExpression), isDefined),
mapDefined(children, transformJsxChildToExpression),
node,
location
);
+70 -13
View File
@@ -999,6 +999,7 @@ namespace ts {
export interface StringLiteral extends LiteralExpression {
kind: SyntaxKind.StringLiteral;
/* @internal */ textSourceNode?: Identifier | StringLiteral | NumericLiteral; // Allows a StringLiteral to get its text from another node (used by transforms).
/* @internal */ singleQuote?: boolean;
}
// Note: 'brands' in our syntax nodes serve to give us a small amount of nominal typing.
@@ -2176,16 +2177,18 @@ namespace ts {
locked?: boolean;
}
export interface AfterFinallyFlow extends FlowNode, FlowLock {
export interface AfterFinallyFlow extends FlowNodeBase, FlowLock {
antecedent: FlowNode;
}
export interface PreFinallyFlow extends FlowNode {
export interface PreFinallyFlow extends FlowNodeBase {
antecedent: FlowNode;
lock: FlowLock;
}
export interface FlowNode {
export type FlowNode =
| AfterFinallyFlow | PreFinallyFlow | FlowStart | FlowLabel | FlowAssignment | FlowCondition | FlowSwitchClause | FlowArrayMutation;
export interface FlowNodeBase {
flags: FlowFlags;
id?: number; // Node id used by flow type cache in checker
}
@@ -2193,30 +2196,30 @@ namespace ts {
// FlowStart represents the start of a control flow. For a function expression or arrow
// function, the container property references the function (which in turn has a flowNode
// property for the containing control flow).
export interface FlowStart extends FlowNode {
export interface FlowStart extends FlowNodeBase {
container?: FunctionExpression | ArrowFunction | MethodDeclaration;
}
// FlowLabel represents a junction with multiple possible preceding control flows.
export interface FlowLabel extends FlowNode {
export interface FlowLabel extends FlowNodeBase {
antecedents: FlowNode[];
}
// FlowAssignment represents a node that assigns a value to a narrowable reference,
// i.e. an identifier or a dotted name that starts with an identifier or 'this'.
export interface FlowAssignment extends FlowNode {
export interface FlowAssignment extends FlowNodeBase {
node: Expression | VariableDeclaration | BindingElement;
antecedent: FlowNode;
}
// FlowCondition represents a condition that is known to be true or false at the
// node's location in the control flow.
export interface FlowCondition extends FlowNode {
export interface FlowCondition extends FlowNodeBase {
expression: Expression;
antecedent: FlowNode;
}
export interface FlowSwitchClause extends FlowNode {
export interface FlowSwitchClause extends FlowNodeBase {
switchStatement: SwitchStatement;
clauseStart: number; // Start index of case/default clause range
clauseEnd: number; // End index of case/default clause range
@@ -2225,7 +2228,7 @@ namespace ts {
// FlowArrayMutation represents a node potentially mutates an array, i.e. an
// operation of the form 'x.push(value)', 'x.unshift(value)' or 'x[n] = value'.
export interface FlowArrayMutation extends FlowNode {
export interface FlowArrayMutation extends FlowNodeBase {
node: CallExpression | BinaryExpression;
antecedent: FlowNode;
}
@@ -2255,6 +2258,17 @@ namespace ts {
}
/* @internal */
export interface RedirectInfo {
/** Source file this redirects to. */
readonly redirectTarget: SourceFile;
/**
* Source file for the duplicate package. This will not be used by the Program,
* but we need to keep this around so we can watch for changes in underlying.
*/
readonly unredirected: SourceFile;
}
// Source files are declarations when they are external modules.
export interface SourceFile extends Declaration {
kind: SyntaxKind.SourceFile;
@@ -2265,6 +2279,13 @@ namespace ts {
/* @internal */ path: Path;
text: string;
/**
* If two source files are for the same version of the same package, one will redirect to the other.
* (See `createRedirectSourceFile` in program.ts.)
* The redirect will have this set. The other will not have anything set, but see Program#sourceFileIsRedirectedTo.
*/
/* @internal */ redirectInfo?: RedirectInfo | undefined;
amdDependencies: AmdDependency[];
moduleName: string;
referencedFiles: FileReference[];
@@ -2435,6 +2456,11 @@ namespace ts {
/* @internal */ structureIsReused?: StructureIsReused;
/* @internal */ getSourceFileFromReference(referencingFile: SourceFile, ref: FileReference): SourceFile | undefined;
/** Given a source file, get the name of the package it was imported from. */
/* @internal */ sourceFileToPackageName: Map<string>;
/** Set of all source files that some other source file redirects to. */
/* @internal */ redirectTargetsSet: Map<true>;
}
/* @internal */
@@ -2542,6 +2568,15 @@ namespace ts {
getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): Symbol[];
getShorthandAssignmentValueSymbol(location: Node): Symbol | undefined;
getExportSpecifierLocalTargetSymbol(location: ExportSpecifier): Symbol | undefined;
/**
* If a symbol is a local symbol with an associated exported symbol, returns the exported symbol.
* Otherwise returns its input.
* For example, at `export type T = number;`:
* - `getSymbolAtLocation` at the location `T` will return the exported symbol for `T`.
* - But the result of `getSymbolsInScope` will contain the *local* symbol for `T`, not the exported symbol.
* - Calling `getExportSymbolOfSymbol` on that local symbol will return the exported symbol.
*/
getExportSymbolOfSymbol(symbol: Symbol): Symbol;
getPropertySymbolOfDestructuringAssignment(location: Identifier): Symbol | undefined;
getTypeAtLocation(node: Node): Type;
getTypeFromTypeNode(node: TypeNode): Type;
@@ -2606,9 +2641,8 @@ namespace ts {
* Does not include properties of primitive types.
*/
/* @internal */ getAllPossiblePropertiesOfType(type: Type): Symbol[];
/* @internal */ resolveName(name: string, location: Node, meaning: SymbolFlags): Symbol | undefined;
/* @internal */ getJsxNamespace(): string;
/* @internal */ resolveNameAtLocation(location: Node, name: string, meaning: SymbolFlags): Symbol | undefined;
}
export enum NodeBuilderFlags {
@@ -2683,11 +2717,12 @@ namespace ts {
UseFullyQualifiedType = 1 << 8, // Write out the fully qualified type name (eg. Module.Type, instead of Type)
InFirstTypeArgument = 1 << 9, // Writing first type argument of the instantiated type
InTypeAlias = 1 << 10, // Writing type in type alias declaration
UseTypeAliasValue = 1 << 11, // Serialize the type instead of using type-alias. This is needed when we emit declaration file.
SuppressAnyReturnType = 1 << 12, // If the return type is any-like, don't offer a return type.
AddUndefined = 1 << 13, // Add undefined to types of initialized, non-optional parameters
WriteClassExpressionAsTypeLiteral = 1 << 14, // Write a type literal instead of (Anonymous class)
InArrayType = 1 << 15, // Writing an array element type
UseAliasDefinedOutsideCurrentScope = 1 << 16, // For a `type T = ... ` defined in a different file, write `T` instead of its value,
// even though `T` can't be accessed in the current scope.
}
export const enum SymbolFormatFlags {
@@ -3101,7 +3136,7 @@ namespace ts {
/* @internal */
ContainsObjectLiteral = 1 << 22, // Type is or contains object literal type
/* @internal */
ContainsAnyFunctionType = 1 << 23, // Type is or contains object literal type
ContainsAnyFunctionType = 1 << 23, // Type is or contains the anyFunctionType
NonPrimitive = 1 << 24, // intrinsic object type
/* @internal */
JsxAttributes = 1 << 25, // Jsx attributes type
@@ -3583,6 +3618,7 @@ namespace ts {
paths?: MapLike<string[]>;
/*@internal*/ plugins?: PluginImport[];
preserveConstEnums?: boolean;
preserveSymlinks?: boolean;
project?: string;
/* @internal */ pretty?: DiagnosticStyle;
reactNamespace?: string;
@@ -3898,6 +3934,10 @@ namespace ts {
readFile(fileName: string): string | undefined;
trace?(s: string): void;
directoryExists?(directoryName: string): boolean;
/**
* Resolve a symbolic link.
* @see https://nodejs.org/api/fs.html#fs_fs_realpathsync_path_options
*/
realpath?(path: string): string;
getCurrentDirectory?(): string;
getDirectories?(path: string): string[];
@@ -3925,6 +3965,7 @@ namespace ts {
/**
* ResolvedModule with an explicitly provided `extension` property.
* Prefer this over `ResolvedModule`.
* If changing this, remember to change `moduleResolutionIsEqualTo`.
*/
export interface ResolvedModuleFull extends ResolvedModule {
/**
@@ -3932,6 +3973,22 @@ namespace ts {
* This is optional for backwards-compatibility, but will be added if not provided.
*/
extension: Extension;
packageId?: PackageId;
}
/**
* Unique identifier with a package name and version.
* If changing this, remember to change `packageIdIsEqual`.
*/
export interface PackageId {
/**
* Name of the package.
* Should not include `@types`.
* If accessing a non-index file, this should include its name e.g. "foo/bar".
*/
name: string;
/** Version of the package, e.g. "1.2.3" */
version: string;
}
export const enum Extension {
+111 -215
View File
@@ -27,20 +27,6 @@ namespace ts {
return undefined;
}
export function findDeclaration<T extends Declaration>(symbol: Symbol, predicate: (node: Declaration) => node is T): T | undefined;
export function findDeclaration(symbol: Symbol, predicate: (node: Declaration) => boolean): Declaration | undefined;
export function findDeclaration(symbol: Symbol, predicate: (node: Declaration) => boolean): Declaration | undefined {
const declarations = symbol.declarations;
if (declarations) {
for (const declaration of declarations) {
if (predicate(declaration)) {
return declaration;
}
}
}
return undefined;
}
export interface StringSymbolWriter extends SymbolWriter {
string(): string;
}
@@ -112,19 +98,21 @@ namespace ts {
sourceFile.resolvedTypeReferenceDirectiveNames.set(typeReferenceDirectiveName, resolvedTypeReferenceDirective);
}
/* @internal */
export function moduleResolutionIsEqualTo(oldResolution: ResolvedModuleFull, newResolution: ResolvedModuleFull): boolean {
return oldResolution.isExternalLibraryImport === newResolution.isExternalLibraryImport &&
oldResolution.extension === newResolution.extension &&
oldResolution.resolvedFileName === newResolution.resolvedFileName;
oldResolution.resolvedFileName === newResolution.resolvedFileName &&
packageIdIsEqual(oldResolution.packageId, newResolution.packageId);
}
function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean {
return a === b || a && b && a.name === b.name && a.version === b.version;
}
/* @internal */
export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean {
return oldResolution.resolvedFileName === newResolution.resolvedFileName && oldResolution.primary === newResolution.primary;
}
/* @internal */
export function hasChangesInResolutions<T>(
names: ReadonlyArray<string>,
newResolutions: ReadonlyArray<T>,
@@ -203,14 +191,6 @@ namespace ts {
return `${file.fileName}(${loc.line + 1},${loc.character + 1})`;
}
export function getStartPosOfNode(node: Node): number {
return node.pos;
}
export function isDefined(value: any): boolean {
return value !== undefined;
}
export function getEndLinePosition(line: number, sourceFile: SourceFileLike): number {
Debug.assert(line >= 0);
const lineStarts = getLineStarts(sourceFile);
@@ -262,6 +242,32 @@ namespace ts {
return !nodeIsMissing(node);
}
/**
* Determine if the given comment is a triple-slash
*
* @return true if the comment is a triple-slash comment else false
*/
export function isRecognizedTripleSlashComment(text: string, commentPos: number, commentEnd: number) {
// Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
// so that we don't end up computing comment string and doing match for all // comments
if (text.charCodeAt(commentPos + 1) === CharacterCodes.slash &&
commentPos + 2 < commentEnd &&
text.charCodeAt(commentPos + 2) === CharacterCodes.slash) {
const textSubStr = text.substring(commentPos, commentEnd);
return textSubStr.match(fullTripleSlashReferencePathRegEx) ||
textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ||
textSubStr.match(fullTripleSlashReferenceTypeReferenceDirectiveRegEx) ||
textSubStr.match(defaultLibReferenceRegEx) ?
true : false;
}
return false;
}
export function isPinnedComment(text: string, comment: CommentRange) {
return text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk &&
text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation;
}
export function getTokenPosOfNode(node: Node, sourceFile?: SourceFileLike, includeJsDoc?: boolean): number {
// With nodes that have no width (i.e. 'Missing' nodes), we actually *don't*
// want to skip trivia because this will launch us forward to the next token.
@@ -338,15 +344,20 @@ namespace ts {
// or a (possibly escaped) quoted form of the original text if it's string-like.
switch (node.kind) {
case SyntaxKind.StringLiteral:
return '"' + escapeText(node.text) + '"';
if ((<StringLiteral>node).singleQuote) {
return "'" + escapeText(node.text, CharacterCodes.singleQuote) + "'";
}
else {
return '"' + escapeText(node.text, CharacterCodes.doubleQuote) + '"';
}
case SyntaxKind.NoSubstitutionTemplateLiteral:
return "`" + escapeText(node.text) + "`";
return "`" + escapeText(node.text, CharacterCodes.backtick) + "`";
case SyntaxKind.TemplateHead:
return "`" + escapeText(node.text) + "${";
return "`" + escapeText(node.text, CharacterCodes.backtick) + "${";
case SyntaxKind.TemplateMiddle:
return "}" + escapeText(node.text) + "${";
return "}" + escapeText(node.text, CharacterCodes.backtick) + "${";
case SyntaxKind.TemplateTail:
return "}" + escapeText(node.text) + "`";
return "}" + escapeText(node.text, CharacterCodes.backtick) + "`";
case SyntaxKind.NumericLiteral:
return node.text;
}
@@ -437,6 +448,7 @@ namespace ts {
return isExternalModule(node) || compilerOptions.isolatedModules;
}
/* @internal */
export function isBlockScope(node: Node, parentNode: Node) {
switch (node.kind) {
case SyntaxKind.SourceFile:
@@ -638,10 +650,6 @@ namespace ts {
return node.kind !== SyntaxKind.JsxText ? getLeadingCommentRanges(sourceFileOfNode.text, node.pos) : undefined;
}
export function getLeadingCommentRangesOfNodeFromText(node: Node, text: string) {
return getLeadingCommentRanges(text, node.pos);
}
export function getJSDocCommentRanges(node: Node, text: string) {
const commentRanges = (node.kind === SyntaxKind.Parameter ||
node.kind === SyntaxKind.TypeParameter ||
@@ -649,7 +657,7 @@ namespace ts {
node.kind === SyntaxKind.ArrowFunction ||
node.kind === SyntaxKind.ParenthesizedExpression) ?
concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) :
getLeadingCommentRangesOfNodeFromText(node, text);
getLeadingCommentRanges(text, node.pos);
// True if the comment starts with '/**' but not if it is '/**/'
return filter(commentRanges, comment =>
text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk &&
@@ -657,9 +665,10 @@ namespace ts {
text.charCodeAt(comment.pos + 3) !== CharacterCodes.slash);
}
export let fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*<reference\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
export let fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*<reference\s+types\s*=\s*)('|")(.+?)\2.*?\/>/;
export let fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*<amd-dependency\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
export const fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*<reference\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
const fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*<reference\s+types\s*=\s*)('|")(.+?)\2.*?\/>/;
export const fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*<amd-dependency\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
const defaultLibReferenceRegEx = /^(\/\/\/\s*<reference\s+no-default-lib\s*=\s*)('|")(.+?)\2\s*\/>/;
export function isPartOfTypeNode(node: Node): boolean {
if (SyntaxKind.FirstTypeNode <= node.kind && node.kind <= SyntaxKind.LastTypeNode) {
@@ -1499,8 +1508,8 @@ namespace ts {
parent.parent.kind === SyntaxKind.VariableStatement;
const variableStatementNode =
isInitializerOfVariableDeclarationInStatement ? parent.parent.parent :
isVariableOfVariableDeclarationStatement ? parent.parent :
undefined;
isVariableOfVariableDeclarationStatement ? parent.parent :
undefined;
if (variableStatementNode) {
getJSDocCommentsAndTagsWorker(variableStatementNode);
}
@@ -1614,7 +1623,7 @@ namespace ts {
if (isInJavaScriptFile(node)) {
if (node.type && node.type.kind === SyntaxKind.JSDocVariadicType ||
forEach(getJSDocParameterTags(node),
t => t.typeExpression && t.typeExpression.type.kind === SyntaxKind.JSDocVariadicType)) {
t => t.typeExpression && t.typeExpression.type.kind === SyntaxKind.JSDocVariadicType)) {
return true;
}
}
@@ -1846,7 +1855,7 @@ namespace ts {
export function getFileReferenceFromReferencePath(comment: string, commentRange: CommentRange): ReferencePathMatchResult {
const simpleReferenceRegEx = /^\/\/\/\s*<reference\s+/gim;
const isNoDefaultLibRegEx = /^(\/\/\/\s*<reference\s+no-default-lib\s*=\s*)('|")(.+?)\2\s*\/>/gim;
const isNoDefaultLibRegEx = new RegExp(defaultLibReferenceRegEx.source, "gim");
if (simpleReferenceRegEx.test(comment)) {
if (isNoDefaultLibRegEx.test(comment)) {
return { isNoDefaultLib: true };
@@ -2068,10 +2077,6 @@ namespace ts {
return getParseTreeNode(sourceFile, isSourceFile) || sourceFile;
}
export function getOriginalSourceFiles(sourceFiles: ReadonlyArray<SourceFile>) {
return sameMap(sourceFiles, getOriginalSourceFile);
}
export const enum Associativity {
Left,
Right
@@ -2356,7 +2361,9 @@ namespace ts {
// the language service. These characters should be escaped when printing, and if any characters are added,
// the map below must be updated. Note that this regexp *does not* include the 'delete' character.
// There is no reason for this other than that JSON.stringify does not handle it either.
const escapedCharsRegExp = /[\\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
const doubleQuoteEscapedCharsRegExp = /[\\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
const singleQuoteEscapedCharsRegExp = /[\\\'\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
const backtickQuoteEscapedCharsRegExp = /[\\\`\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
const escapedCharsMap = createMapFromTemplate({
"\0": "\\0",
"\t": "\\t",
@@ -2367,18 +2374,23 @@ namespace ts {
"\n": "\\n",
"\\": "\\\\",
"\"": "\\\"",
"\'": "\\\'",
"\`": "\\\`",
"\u2028": "\\u2028", // lineSeparator
"\u2029": "\\u2029", // paragraphSeparator
"\u0085": "\\u0085" // nextLine
});
/**
* Based heavily on the abstract 'Quote'/'QuoteJSONString' operation from ECMA-262 (24.3.2.2),
* but augmented for a few select characters (e.g. lineSeparator, paragraphSeparator, nextLine)
* Note that this doesn't actually wrap the input in double quotes.
*/
export function escapeString(s: string): string {
export function escapeString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote | CharacterCodes.backtick): string {
const escapedCharsRegExp =
quoteChar === CharacterCodes.backtick ? backtickQuoteEscapedCharsRegExp :
quoteChar === CharacterCodes.singleQuote ? singleQuoteEscapedCharsRegExp :
doubleQuoteEscapedCharsRegExp;
return s.replace(escapedCharsRegExp, getReplacement);
}
@@ -2400,8 +2412,8 @@ namespace ts {
}
const nonAsciiCharacters = /[^\u0000-\u007F]/g;
export function escapeNonAsciiString(s: string): string {
s = escapeString(s);
export function escapeNonAsciiString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote | CharacterCodes.backtick): string {
s = escapeString(s, quoteChar);
// Replace non-ASCII characters with '\uNNNN' escapes if any exist.
// Otherwise just return the original string.
return nonAsciiCharacters.test(s) ?
@@ -2823,7 +2835,7 @@ namespace ts {
//
// var x = 10;
if (node.pos === 0) {
leadingComments = filter(getLeadingCommentRanges(text, node.pos), isPinnedComment);
leadingComments = filter(getLeadingCommentRanges(text, node.pos), isPinnedCommentLocal);
}
}
else {
@@ -2869,9 +2881,8 @@ namespace ts {
return currentDetachedCommentInfo;
function isPinnedComment(comment: CommentRange) {
return text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk &&
text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation;
function isPinnedCommentLocal(comment: CommentRange) {
return isPinnedComment(text, comment);
}
}
@@ -2977,8 +2988,12 @@ namespace ts {
return getModifierFlags(node) !== ModifierFlags.None;
}
export function hasModifier(node: Node, flags: ModifierFlags) {
return (getModifierFlags(node) & flags) !== 0;
export function hasModifier(node: Node, flags: ModifierFlags): boolean {
return !!getSelectedModifierFlags(node, flags);
}
export function getSelectedModifierFlags(node: Node, flags: ModifierFlags): ModifierFlags {
return getModifierFlags(node) & flags;
}
export function getModifierFlags(node: Node): ModifierFlags {
@@ -3064,24 +3079,6 @@ namespace ts {
return false;
}
// Returns false if this heritage clause element's expression contains something unsupported
// (i.e. not a name or dotted name).
export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): boolean {
return isSupportedExpressionWithTypeArgumentsRest(node.expression);
}
function isSupportedExpressionWithTypeArgumentsRest(node: Expression): boolean {
if (node.kind === SyntaxKind.Identifier) {
return true;
}
else if (isPropertyAccessExpression(node)) {
return isSupportedExpressionWithTypeArgumentsRest(node.expression);
}
else {
return false;
}
}
export function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node): boolean {
return tryGetClassExtendingExpressionWithTypeArguments(node) !== undefined;
}
@@ -3218,81 +3215,6 @@ namespace ts {
return carriageReturnLineFeed;
}
/**
* Tests whether a node and its subtree is simple enough to have its position
* information ignored when emitting source maps in a destructuring assignment.
*
* @param node The expression to test.
*/
export function isSimpleExpression(node: Expression): boolean {
return isSimpleExpressionWorker(node, 0);
}
function isSimpleExpressionWorker(node: Expression, depth: number): boolean {
if (depth <= 5) {
const kind = node.kind;
if (kind === SyntaxKind.StringLiteral
|| kind === SyntaxKind.NumericLiteral
|| kind === SyntaxKind.RegularExpressionLiteral
|| kind === SyntaxKind.NoSubstitutionTemplateLiteral
|| kind === SyntaxKind.Identifier
|| kind === SyntaxKind.ThisKeyword
|| kind === SyntaxKind.SuperKeyword
|| kind === SyntaxKind.TrueKeyword
|| kind === SyntaxKind.FalseKeyword
|| kind === SyntaxKind.NullKeyword) {
return true;
}
else if (kind === SyntaxKind.PropertyAccessExpression) {
return isSimpleExpressionWorker((<PropertyAccessExpression>node).expression, depth + 1);
}
else if (kind === SyntaxKind.ElementAccessExpression) {
return isSimpleExpressionWorker((<ElementAccessExpression>node).expression, depth + 1)
&& isSimpleExpressionWorker((<ElementAccessExpression>node).argumentExpression, depth + 1);
}
else if (kind === SyntaxKind.PrefixUnaryExpression
|| kind === SyntaxKind.PostfixUnaryExpression) {
return isSimpleExpressionWorker((<PrefixUnaryExpression | PostfixUnaryExpression>node).operand, depth + 1);
}
else if (kind === SyntaxKind.BinaryExpression) {
return (<BinaryExpression>node).operatorToken.kind !== SyntaxKind.AsteriskAsteriskToken
&& isSimpleExpressionWorker((<BinaryExpression>node).left, depth + 1)
&& isSimpleExpressionWorker((<BinaryExpression>node).right, depth + 1);
}
else if (kind === SyntaxKind.ConditionalExpression) {
return isSimpleExpressionWorker((<ConditionalExpression>node).condition, depth + 1)
&& isSimpleExpressionWorker((<ConditionalExpression>node).whenTrue, depth + 1)
&& isSimpleExpressionWorker((<ConditionalExpression>node).whenFalse, depth + 1);
}
else if (kind === SyntaxKind.VoidExpression
|| kind === SyntaxKind.TypeOfExpression
|| kind === SyntaxKind.DeleteExpression) {
return isSimpleExpressionWorker((<VoidExpression | TypeOfExpression | DeleteExpression>node).expression, depth + 1);
}
else if (kind === SyntaxKind.ArrayLiteralExpression) {
return (<ArrayLiteralExpression>node).elements.length === 0;
}
else if (kind === SyntaxKind.ObjectLiteralExpression) {
return (<ObjectLiteralExpression>node).properties.length === 0;
}
else if (kind === SyntaxKind.CallExpression) {
if (!isSimpleExpressionWorker((<CallExpression>node).expression, depth + 1)) {
return false;
}
for (const argument of (<CallExpression>node).arguments) {
if (!isSimpleExpressionWorker(argument, depth + 1)) {
return false;
}
}
return true;
}
}
return false;
}
/**
* Formats an enum value as a string for debugging and debug assertions.
*/
@@ -3365,24 +3287,6 @@ namespace ts {
return formatEnum(flags, (<any>ts).ObjectFlags, /*isFlags*/ true);
}
export function getRangePos(range: TextRange | undefined) {
return range ? range.pos : -1;
}
export function getRangeEnd(range: TextRange | undefined) {
return range ? range.end : -1;
}
/**
* Increases (or decreases) a position by the provided amount.
*
* @param pos The position.
* @param value The delta.
*/
export function movePos(pos: number, value: number) {
return positionIsSynthesized(pos) ? -1 : pos + value;
}
/**
* Creates a new TextRange from the provided pos and end.
*
@@ -3440,26 +3344,6 @@ namespace ts {
return range.pos === range.end;
}
/**
* Creates a new TextRange from a provided range with its end position collapsed to its
* start position.
*
* @param range A TextRange.
*/
export function collapseRangeToStart(range: TextRange): TextRange {
return isCollapsedRange(range) ? range : moveRangeEnd(range, range.pos);
}
/**
* Creates a new TextRange from a provided range with its start position collapsed to its
* end position.
*
* @param range A TextRange.
*/
export function collapseRangeToEnd(range: TextRange): TextRange {
return isCollapsedRange(range) ? range : moveRangePos(range, range.end);
}
/**
* Creates a new TextRange for a token at the provides start position.
*
@@ -3523,31 +3407,6 @@ namespace ts {
return node.initializer !== undefined;
}
/**
* Gets a value indicating whether a node is merged with a class declaration in the same scope.
*/
export function isMergedWithClass(node: Node) {
if (node.symbol) {
for (const declaration of node.symbol.declarations) {
if (declaration.kind === SyntaxKind.ClassDeclaration && declaration !== node) {
return true;
}
}
}
return false;
}
/**
* Gets a value indicating whether a node is the first declaration of its kind.
*
* @param node A Declaration node.
* @param kind The SyntaxKind to find among related declarations.
*/
export function isFirstDeclarationOfKind(node: Node, kind: SyntaxKind) {
return node.symbol && getDeclarationOfKind(node.symbol, kind) === node;
}
export function isWatchSet(options: CompilerOptions) {
// Firefox has Object.prototype.watch
return options.watch && options.hasOwnProperty("watch");
@@ -3856,6 +3715,20 @@ namespace ts {
return hasModifier(node, ModifierFlags.ParameterPropertyModifier) && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent);
}
export function isEmptyBindingPattern(node: BindingName): node is BindingPattern {
if (isBindingPattern(node)) {
return every(node.elements, isEmptyBindingElement);
}
return false;
}
export function isEmptyBindingElement(node: BindingElement): boolean {
if (isOmittedExpression(node)) {
return true;
}
return isEmptyBindingPattern(node.name);
}
function walkUpBindingElementsAndPatterns(node: Node): Node {
while (node && (node.kind === SyntaxKind.BindingElement || isBindingPattern(node))) {
node = node.parent;
@@ -5123,6 +4996,19 @@ namespace ts {
return isUnaryExpressionKind(skipPartiallyEmittedExpressions(node).kind);
}
/* @internal */
export function isUnaryExpressionWithWrite(expr: Node): expr is PrefixUnaryExpression | PostfixUnaryExpression {
switch (expr.kind) {
case SyntaxKind.PostfixUnaryExpression:
return true;
case SyntaxKind.PrefixUnaryExpression:
return (<PrefixUnaryExpression>expr).operator === SyntaxKind.PlusPlusToken ||
(<PrefixUnaryExpression>expr).operator === SyntaxKind.MinusMinusToken;
default:
return false;
}
}
function isExpressionKind(kind: SyntaxKind) {
return kind === SyntaxKind.ConditionalExpression
|| kind === SyntaxKind.YieldExpression
@@ -5337,7 +5223,17 @@ namespace ts {
const kind = node.kind;
return isStatementKindButNotDeclarationKind(kind)
|| isDeclarationStatementKind(kind)
|| kind === SyntaxKind.Block;
|| isBlockStatement(node);
}
function isBlockStatement(node: Node): node is Block {
if (node.kind !== SyntaxKind.Block) return false;
if (node.parent !== undefined) {
if (node.parent.kind === SyntaxKind.TryStatement || node.parent.kind === SyntaxKind.CatchClause) {
return false;
}
}
return !isFunctionBlock(node);
}
// Module references
+93 -43
View File
@@ -187,6 +187,9 @@ namespace FourSlash {
// The current caret position in the active file
public currentCaretPosition = 0;
// The position of the end of the current selection, or -1 if nothing is selected
public selectionEnd = -1;
public lastKnownMarker = "";
// The file that's currently 'opened'
@@ -433,11 +436,19 @@ namespace FourSlash {
public goToPosition(pos: number) {
this.currentCaretPosition = pos;
this.selectionEnd = -1;
}
public select(startMarker: string, endMarker: string) {
const start = this.getMarkerByName(startMarker), end = this.getMarkerByName(endMarker);
this.goToPosition(start.position);
this.selectionEnd = end.position;
}
public moveCaretRight(count = 1) {
this.currentCaretPosition += count;
this.currentCaretPosition = Math.min(this.currentCaretPosition, this.getFileContent(this.activeFile.fileName).length);
this.selectionEnd = -1;
}
// Opens a file given its 0-based index or fileName
@@ -451,7 +462,7 @@ namespace FourSlash {
this.languageServiceAdapterHost.openFile(fileToOpen.fileName, content, scriptKindName);
}
public verifyErrorExistsBetweenMarkers(startMarkerName: string, endMarkerName: string, negative: boolean) {
public verifyErrorExistsBetweenMarkers(startMarkerName: string, endMarkerName: string, shouldExist: boolean) {
const startMarker = this.getMarkerByName(startMarkerName);
const endMarker = this.getMarkerByName(endMarkerName);
const predicate = (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) =>
@@ -459,9 +470,9 @@ namespace FourSlash {
const exists = this.anyErrorInRange(predicate, startMarker, endMarker);
if (exists !== negative) {
this.printErrorLog(negative, this.getAllDiagnostics());
throw new Error(`Failure between markers: '${startMarkerName}', '${endMarkerName}'`);
if (exists !== shouldExist) {
this.printErrorLog(shouldExist, this.getAllDiagnostics());
throw new Error(`${shouldExist ? "Expected" : "Did not expect"} failure between markers: '${startMarkerName}', '${endMarkerName}'`);
}
}
@@ -483,10 +494,11 @@ namespace FourSlash {
}
private getAllDiagnostics(): ts.Diagnostic[] {
return ts.flatMap(this.languageServiceAdapterHost.getFilenames(), fileName => this.getDiagnostics(fileName));
return ts.flatMap(this.languageServiceAdapterHost.getFilenames(), fileName =>
ts.isAnySupportedFileExtension(fileName) ? this.getDiagnostics(fileName) : []);
}
public verifyErrorExistsAfterMarker(markerName: string, negative: boolean, after: boolean) {
public verifyErrorExistsAfterMarker(markerName: string, shouldExist: boolean, after: boolean) {
const marker: Marker = this.getMarkerByName(markerName);
let predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean;
@@ -502,30 +514,15 @@ namespace FourSlash {
const exists = this.anyErrorInRange(predicate, marker);
const diagnostics = this.getAllDiagnostics();
if (exists !== negative) {
this.printErrorLog(negative, diagnostics);
throw new Error("Failure at marker: " + markerName);
if (exists !== shouldExist) {
this.printErrorLog(shouldExist, diagnostics);
throw new Error(`${shouldExist ? "Expected" : "Did not expect"} failure at marker '${markerName}'`);
}
}
private anyErrorInRange(predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean, startMarker: Marker, endMarker?: Marker) {
const errors = this.getDiagnostics(startMarker.fileName);
let exists = false;
const startPos = startMarker.position;
let endPos: number = undefined;
if (endMarker !== undefined) {
endPos = endMarker.position;
}
errors.forEach(function (error: ts.Diagnostic) {
if (predicate(error.start, error.start + error.length, startPos, endPos)) {
exists = true;
}
});
return exists;
private anyErrorInRange(predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean, startMarker: Marker, endMarker?: Marker): boolean {
return this.getDiagnostics(startMarker.fileName).some(({ start, length }) =>
predicate(start, start + length, startMarker.position, endMarker === undefined ? undefined : endMarker.position));
}
private printErrorLog(expectErrors: boolean, errors: ts.Diagnostic[]) {
@@ -550,6 +547,7 @@ namespace FourSlash {
public verifyNoErrors() {
ts.forEachKey(this.inputFiles, fileName => {
if (!ts.isAnySupportedFileExtension(fileName)) return;
const errors = this.getDiagnostics(fileName);
if (errors.length) {
this.printErrorLog(/*expectErrors*/ false, errors);
@@ -980,9 +978,9 @@ namespace FourSlash {
}
for (const reference of expectedReferences) {
const {fileName, start, end} = reference;
const { fileName, start, end } = reference;
if (reference.marker && reference.marker.data) {
const {isWriteAccess, isDefinition} = reference.marker.data;
const { isWriteAccess, isDefinition } = reference.marker.data;
this.verifyReferencesWorker(actualReferences, fileName, start, end, isWriteAccess, isDefinition);
}
else {
@@ -1193,7 +1191,7 @@ namespace FourSlash {
displayParts: ts.SymbolDisplayPart[],
documentation: ts.SymbolDisplayPart[],
tags: ts.JSDocTagInfo[]
) {
) {
const actualQuickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition);
assert.equal(actualQuickInfo.kind, kind, this.messageAtLastKnownMarker("QuickInfo kind"));
@@ -1789,19 +1787,16 @@ namespace FourSlash {
// We get back a set of edits, but langSvc.editScript only accepts one at a time. Use this to keep track
// of the incremental offset from each edit to the next. We assume these edit ranges don't overlap
edits = edits.sort((a, b) => a.span.start - b.span.start);
for (let i = 0; i < edits.length - 1; i++) {
const firstEditSpan = edits[i].span;
const firstEditEnd = firstEditSpan.start + firstEditSpan.length;
assert.isTrue(firstEditEnd <= edits[i + 1].span.start);
}
// Copy this so we don't ruin someone else's copy
edits = JSON.parse(JSON.stringify(edits));
// Get a snapshot of the content of the file so we can make sure any formatting edits didn't destroy non-whitespace characters
const oldContent = this.getFileContent(fileName);
let runningOffset = 0;
for (const edit of edits) {
const offsetStart = edit.span.start + runningOffset;
for (let i = 0; i < edits.length; i++) {
const edit = edits[i];
const offsetStart = edit.span.start;
const offsetEnd = offsetStart + edit.span.length;
this.editScriptAndUpdateMarkers(fileName, offsetStart, offsetEnd, edit.newText);
const editDelta = edit.newText.length - edit.span.length;
@@ -1816,8 +1811,13 @@ namespace FourSlash {
}
}
runningOffset += editDelta;
// TODO: Consider doing this at least some of the time for higher fidelity. Currently causes a failure (bug 707150)
// this.languageService.getScriptLexicalStructure(fileName);
// Update positions of any future edits affected by this change
for (let j = i + 1; j < edits.length; j++) {
if (edits[j].span.start >= edits[i].span.start) {
edits[j].span.start += editDelta;
}
}
}
if (isFormattingEdit) {
@@ -1901,7 +1901,7 @@ namespace FourSlash {
this.goToPosition(len);
}
public goToRangeStart({fileName, start}: Range) {
public goToRangeStart({ fileName, start }: Range) {
this.openFile(fileName);
this.goToPosition(start);
}
@@ -2075,7 +2075,7 @@ namespace FourSlash {
return result;
}
private rangeText({fileName, start, end}: Range): string {
private rangeText({ fileName, start, end }: Range): string {
return this.getFileContent(fileName).slice(start, end);
}
@@ -2361,7 +2361,7 @@ namespace FourSlash {
private applyCodeActions(actions: ts.CodeAction[], index?: number): void {
if (index === undefined) {
if (!(actions && actions.length === 1)) {
this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `${Harness.IO.newLine()} "${a.description}"`) : "" }`);
this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `${Harness.IO.newLine()} "${a.description}"`) : ""}`);
}
index = 0;
}
@@ -2750,6 +2750,30 @@ namespace FourSlash {
}
}
private getSelection() {
return ({
pos: this.currentCaretPosition,
end: this.selectionEnd === -1 ? this.currentCaretPosition : this.selectionEnd
});
}
public verifyRefactorAvailable(negative: boolean, name?: string, subName?: string) {
const selection = this.getSelection();
let refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, selection) || [];
if (name) {
refactors = refactors.filter(r => r.name === name && (subName === undefined || r.actions.some(a => a.name === subName)));
}
const isAvailable = refactors.length > 0;
if (negative && isAvailable) {
this.raiseError(`verifyApplicableRefactorAvailableForRange failed - expected no refactor but found some: ${refactors.map(r => r.name).join(", ")}`);
}
else if (!negative && !isAvailable) {
this.raiseError(`verifyApplicableRefactorAvailableForRange failed - expected a refactor but found none.`);
}
}
public verifyApplicableRefactorAvailableForRange(negative: boolean) {
const ranges = this.getRanges();
if (!(ranges && ranges.length === 1)) {
@@ -2766,6 +2790,20 @@ namespace FourSlash {
}
}
public applyRefactor(refactorName: string, actionName: string) {
const range = this.getSelection();
const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range);
const refactor = ts.find(refactors, r => r.name === refactorName);
if (!refactor) {
this.raiseError(`The expected refactor: ${refactorName} is not available at the marker location.`);
}
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName);
for (const edit of editInfo.edits) {
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
}
}
public verifyFileAfterApplyingRefactorAtMarker(
markerName: string,
expectedContent: string,
@@ -3510,6 +3548,10 @@ namespace FourSlashInterface {
public file(indexOrName: any, content?: string, scriptKindName?: string): void {
this.state.openFile(indexOrName, content, scriptKindName);
}
public select(startMarker: string, endMarker: string) {
this.state.select(startMarker, endMarker);
}
}
export class VerifyNegatable {
@@ -3635,6 +3677,10 @@ namespace FourSlashInterface {
public applicableRefactorAvailableForRange() {
this.state.verifyApplicableRefactorAvailableForRange(this.negative);
}
public refactorAvailable(name?: string, subName?: string) {
this.state.verifyRefactorAvailable(this.negative, name, subName);
}
}
export class Verify extends VerifyNegatable {
@@ -4030,6 +4076,10 @@ namespace FourSlashInterface {
public disableFormatting() {
this.state.enableFormatting = false;
}
public applyRefactor(refactorName: string, actionName: string) {
this.state.applyRefactor(refactorName, actionName);
}
}
export class Debug {
+62 -35
View File
@@ -67,17 +67,16 @@ namespace Utils {
export let currentExecutionEnvironment = getExecutionEnvironment();
const Buffer: typeof global.Buffer = currentExecutionEnvironment !== ExecutionEnvironment.Browser
? require("buffer").Buffer
: undefined;
// Thanks to browserify, Buffer is always available nowadays
const Buffer: typeof global.Buffer = require("buffer").Buffer;
export function encodeString(s: string): string {
return Buffer ? (new Buffer(s)).toString("utf8") : s;
return Buffer.from(s).toString("utf8");
}
export function byteLength(s: string, encoding?: string): number {
// stub implementation if Buffer is not available (in-browser case)
return Buffer ? Buffer.byteLength(s, encoding) : s.length;
return Buffer.byteLength(s, encoding);
}
export function evalFile(fileContents: string, fileName: string, nodeContext?: any) {
@@ -133,17 +132,18 @@ namespace Utils {
return content;
}
export function memoize<T extends Function>(f: T): T {
const cache: { [idx: string]: any } = {};
export function memoize<T extends Function>(f: T, memoKey: (...anything: any[]) => string): T {
const cache = ts.createMap<any>();
return <any>(function(this: any) {
const key = Array.prototype.join.call(arguments);
const cachedResult = cache[key];
if (cachedResult) {
return cachedResult;
return <any>(function(this: any, ...args: any[]) {
const key = memoKey(...args);
if (cache.has(key)) {
return cache.get(key);
}
else {
return cache[key] = f.apply(this, arguments);
const value = f.apply(this, args);
cache.set(key, value);
return value;
}
});
}
@@ -564,7 +564,7 @@ namespace Harness {
}
export let listFiles: typeof IO.listFiles = (path, spec?, options?) => {
options = options || <{ recursive?: boolean; }>{};
options = options || {};
function filesInFolder(folder: string): string[] {
let paths: string[] = [];
@@ -686,7 +686,7 @@ namespace Harness {
return dirPath;
}
export let directoryName: typeof IO.directoryName = Utils.memoize(directoryNameImpl);
export let directoryName: typeof IO.directoryName = Utils.memoize(directoryNameImpl, path => path);
export function resolvePath(path: string) {
const response = Http.getFileFromServerSync(serverRoot + path + "?resolve=true");
@@ -703,21 +703,22 @@ namespace Harness {
return response.status === 200;
}
export let listFiles = Utils.memoize((path: string, spec?: RegExp): string[] => {
export const listFiles = Utils.memoize((path: string, spec?: RegExp, options?: { recursive?: boolean }): string[] => {
const response = Http.getFileFromServerSync(serverRoot + path);
if (response.status === 200) {
const results = response.responseText.split(",");
let results = response.responseText.split(",");
if (spec) {
return results.filter(file => spec.test(file));
results = results.filter(file => spec.test(file));
}
else {
return results;
if (options && !options.recursive) {
results = results.filter(file => (ts.getDirectoryPath(ts.normalizeSlashes(file)) === path));
}
return results;
}
else {
return [""];
}
});
}, (path: string, spec?: RegExp, options?: { recursive?: boolean }) => `${path}|${spec}|${options ? options.recursive : undefined}`);
export function readFile(file: string): string | undefined {
const response = Http.getFileFromServerSync(serverRoot + file);
@@ -1194,13 +1195,21 @@ namespace Harness {
return { result, options };
}
export function compileDeclarationFiles(inputFiles: TestFile[],
export interface DeclarationCompilationContext {
declInputFiles: TestFile[];
declOtherFiles: TestFile[];
harnessSettings: TestCaseParser.CompilerSettings & HarnessOptions;
options: ts.CompilerOptions;
currentDirectory: string;
}
export function prepareDeclarationCompilationContext(inputFiles: TestFile[],
otherFiles: TestFile[],
result: CompilerResult,
harnessSettings: TestCaseParser.CompilerSettings & HarnessOptions,
options: ts.CompilerOptions,
// Current directory is needed for rwcRunner to be able to use currentDirectory defined in json file
currentDirectory: string) {
currentDirectory: string): DeclarationCompilationContext | undefined {
if (options.declaration && result.errors.length === 0 && result.declFilesCode.length !== result.files.length) {
throw new Error("There were no errors and declFiles generated did not match number of js files generated");
}
@@ -1212,8 +1221,7 @@ namespace Harness {
if (options.declaration && result.errors.length === 0 && result.declFilesCode.length > 0) {
ts.forEach(inputFiles, file => addDtsFile(file, declInputFiles));
ts.forEach(otherFiles, file => addDtsFile(file, declOtherFiles));
const output = compileFiles(declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory || harnessSettings["currentDirectory"]);
return { declInputFiles, declOtherFiles, declResult: output.result };
return { declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory: currentDirectory || harnessSettings["currentDirectory"] };
}
function addDtsFile(file: TestFile, dtsFiles: TestFile[]) {
@@ -1259,6 +1267,15 @@ namespace Harness {
}
}
export function compileDeclarationFiles(context: DeclarationCompilationContext | undefined) {
if (!context) {
return;
}
const { declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory } = context;
const output = compileFiles(declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory);
return { declInputFiles, declOtherFiles, declResult: output.result };
}
function normalizeLineEndings(text: string, lineEnding: string): string {
let normalized = text.replace(/\r\n?/g, "\n");
if (lineEnding !== "\n") {
@@ -1273,10 +1290,19 @@ namespace Harness {
export function getErrorBaseline(inputFiles: TestFile[], diagnostics: ts.Diagnostic[]) {
diagnostics.sort(ts.compareDiagnostics);
const outputLines: string[] = [];
let outputLines = "";
// Count up all errors that were found in files other than lib.d.ts so we don't miss any
let totalErrorsReportedInNonLibraryFiles = 0;
let firstLine = true;
function newLine() {
if (firstLine) {
firstLine = false;
return "";
}
return "\r\n";
}
function outputErrorText(error: ts.Diagnostic) {
const message = ts.flattenDiagnosticMessageText(error.messageText, Harness.IO.newLine());
@@ -1285,7 +1311,7 @@ namespace Harness {
.map(s => s.length > 0 && s.charAt(s.length - 1) === "\r" ? s.substr(0, s.length - 1) : s)
.filter(s => s.length > 0)
.map(s => "!!! " + ts.DiagnosticCategory[error.category].toLowerCase() + " TS" + error.code + ": " + s);
errLines.forEach(e => outputLines.push(e));
errLines.forEach(e => outputLines += (newLine() + e));
// do not count errors from lib.d.ts here, they are computed separately as numLibraryDiagnostics
// if lib.d.ts is explicitly included in input files and there are some errors in it (i.e. because of duplicate identifiers)
@@ -1311,7 +1337,7 @@ namespace Harness {
// Header
outputLines.push("==== " + inputFile.unitName + " (" + fileErrors.length + " errors) ====");
outputLines += (newLine() + "==== " + inputFile.unitName + " (" + fileErrors.length + " errors) ====");
// Make sure we emit something for every error
let markedErrorCount = 0;
@@ -1340,7 +1366,7 @@ namespace Harness {
nextLineStart = lineStarts[lineIndex + 1];
}
// Emit this line from the original file
outputLines.push(" " + line);
outputLines += (newLine() + " " + line);
fileErrors.forEach(err => {
// Does any error start or continue on to this line? Emit squiggles
const end = ts.textSpanEnd(err);
@@ -1352,7 +1378,7 @@ namespace Harness {
// Calculate the start of the squiggle
const squiggleStart = Math.max(0, relativeOffset);
// TODO/REVIEW: this doesn't work quite right in the browser if a multi file test has files whose names are just the right length relative to one another
outputLines.push(" " + line.substr(0, squiggleStart).replace(/[^\s]/g, " ") + new Array(Math.min(length, line.length - squiggleStart) + 1).join("~"));
outputLines += (newLine() + " " + line.substr(0, squiggleStart).replace(/[^\s]/g, " ") + new Array(Math.min(length, line.length - squiggleStart) + 1).join("~"));
// If the error ended here, or we're at the end of the file, emit its message
if ((lineIndex === lines.length - 1) || nextLineStart > end) {
@@ -1383,7 +1409,7 @@ namespace Harness {
assert.equal(totalErrorsReportedInNonLibraryFiles + numLibraryDiagnostics + numTest262HarnessDiagnostics, diagnostics.length, "total number of errors");
return minimalDiagnosticsToString(diagnostics) +
Harness.IO.newLine() + Harness.IO.newLine() + outputLines.join("\r\n");
Harness.IO.newLine() + Harness.IO.newLine() + outputLines;
}
export function doErrorBaseline(baselinePath: string, inputFiles: TestFile[], errors: ts.Diagnostic[]) {
@@ -1586,9 +1612,10 @@ namespace Harness {
}
}
const declFileCompilationResult =
Harness.Compiler.compileDeclarationFiles(
toBeCompiled, otherFiles, result, harnessSettings, options, /*currentDirectory*/ undefined);
const declFileContext = Harness.Compiler.prepareDeclarationCompilationContext(
toBeCompiled, otherFiles, result, harnessSettings, options, /*currentDirectory*/ undefined
);
const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(declFileContext);
if (declFileCompilationResult && declFileCompilationResult.declResult.errors.length) {
jsCode += "\r\n\r\n//// [DtsFileErrors]\r\n";
@@ -1963,7 +1990,7 @@ namespace Harness {
IO.writeFile(actualFileName + ".delete", "");
}
else {
IO.writeFile(actualFileName, actual);
IO.writeFile(actualFileName, encoded_actual);
}
throw new Error(`The baseline file ${relativeFileName} has changed.`);
}
+4 -1
View File
@@ -193,7 +193,9 @@ namespace Harness.LanguageService {
}
getCurrentDirectory(): string { return virtualFileSystemRoot; }
getDefaultLibFileName(): string { return Harness.Compiler.defaultLibFileName; }
getScriptFileNames(): string[] { return this.getFilenames(); }
getScriptFileNames(): string[] {
return this.getFilenames().filter(ts.isAnySupportedFileExtension);
}
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
const script = this.getScriptInfo(fileName);
return script ? new ScriptSnapshot(script) : undefined;
@@ -826,6 +828,7 @@ namespace Harness.LanguageService {
host: serverHost,
cancellationToken: ts.server.nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
+2 -2
View File
@@ -426,12 +426,12 @@ class ProjectRunner extends RunnerBase {
compilerResult.program ?
ts.filter(compilerResult.program.getSourceFiles(), sourceFile => !Harness.isDefaultLibraryFile(sourceFile.fileName)) :
[]),
sourceFile => <Harness.Compiler.TestFile>{
(sourceFile): Harness.Compiler.TestFile => ({
unitName: ts.isRootedDiskPath(sourceFile.fileName) ?
RunnerBase.removeFullPaths(sourceFile.fileName) :
sourceFile.fileName,
content: sourceFile.text
});
}));
return Harness.Compiler.getErrorBaseline(inputFiles, compilerResult.errors);
}
+14 -10
View File
@@ -208,6 +208,14 @@ namespace RWC {
}, baselineOpts);
});
it("has the expected types", () => {
// We don't need to pass the extension here because "doTypeAndSymbolBaseline" will append appropriate extension of ".types" or ".symbols"
Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult, inputFiles
.concat(otherFiles)
.filter(file => !!compilerResult.program.getSourceFile(file.unitName))
.filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts);
});
// Ideally, a generated declaration file will have no errors. But we allow generated
// declaration file errors as part of the baseline.
it("has the expected errors in generated declaration files", () => {
@@ -217,8 +225,12 @@ namespace RWC {
return null;
}
const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(
inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined, compilerOptions, currentDirectory);
const declContext = Harness.Compiler.prepareDeclarationCompilationContext(
inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined, compilerOptions, currentDirectory
);
// Reset compilerResult before calling into `compileDeclarationFiles` so the memory from the original compilation can be freed
compilerResult = undefined;
const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(declContext);
return Harness.Compiler.minimalDiagnosticsToString(declFileCompilationResult.declResult.errors) +
Harness.IO.newLine() + Harness.IO.newLine() +
@@ -226,14 +238,6 @@ namespace RWC {
}, baselineOpts);
}
});
it("has the expected types", () => {
// We don't need to pass the extension here because "doTypeAndSymbolBaseline" will append appropriate extension of ".types" or ".symbols"
Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult, inputFiles
.concat(otherFiles)
.filter(file => !!compilerResult.program.getSourceFile(file.unitName))
.filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts);
});
});
}
}
@@ -57,6 +57,7 @@ namespace ts {
logger: projectSystem.nullLogger,
cancellationToken: { isCancellationRequested: () => false },
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined
};
const projectService = new server.ProjectService(svcOpts);
+10 -7
View File
@@ -36,6 +36,7 @@ namespace ts.projectSystem {
host,
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: typingsInstaller || server.nullTypingsInstaller,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
@@ -518,18 +519,20 @@ namespace ts.projectSystem {
};
const host = createServerHost([f], { newLine });
const session = createSession(host);
session.executeCommand(<server.protocol.OpenRequest>{
const openRequest: server.protocol.OpenRequest = {
seq: 1,
type: "request",
command: "open",
command: server.protocol.CommandTypes.Open,
arguments: { file: f.path }
});
session.executeCommand(<server.protocol.CompileOnSaveEmitFileRequest>{
};
session.executeCommand(openRequest);
const emitFileRequest: server.protocol.CompileOnSaveEmitFileRequest = {
seq: 2,
type: "request",
command: "compileOnSaveEmitFile",
command: server.protocol.CommandTypes.CompileOnSaveEmitFile,
arguments: { file: f.path }
});
};
session.executeCommand(emitFileRequest);
const emitOutput = host.readFile(path + ts.Extension.Js);
assert.equal(emitOutput, f.content + newLine, "content of emit output should be identical with the input + newline");
}
@@ -550,7 +553,7 @@ namespace ts.projectSystem {
};
const host = createServerHost([file1, file2, configFile, libFile], { newLine: "\r\n" });
const typingsInstaller = createTestTypingsInstaller(host);
const session = createSession(host, typingsInstaller);
const session = createSession(host, { typingsInstaller });
openFilesForSession([file1, file2], session);
const compileFileRequest = makeSessionRequest<server.protocol.CompileOnSaveEmitFileRequestArgs>(CommandNames.CompileOnSaveEmitFile, { file: file1.path, projectFileName: configFile.path });
@@ -67,14 +67,14 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"]
},
errors: <Diagnostic[]>[]
errors: []
}
);
});
@@ -92,7 +92,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@@ -100,7 +100,7 @@ namespace ts {
allowJs: false,
lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"]
},
errors: <Diagnostic[]>[]
errors: []
}
);
});
@@ -117,7 +117,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@@ -146,7 +146,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
target: ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
@@ -174,7 +174,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
target: ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
@@ -201,7 +201,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
noImplicitAny: false,
sourceMap: false,
},
@@ -227,7 +227,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
noImplicitAny: false,
sourceMap: false,
},
@@ -255,7 +255,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@@ -286,7 +286,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@@ -317,7 +317,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@@ -348,7 +348,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@@ -379,7 +379,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@@ -415,8 +415,8 @@ namespace ts {
it("Convert default tsconfig.json to compiler-options ", () => {
assertCompilerOptions({}, "tsconfig.json",
{
compilerOptions: {} as CompilerOptions,
errors: <Diagnostic[]>[]
compilerOptions: {},
errors: []
}
);
});
@@ -434,7 +434,7 @@ namespace ts {
}
}, "jsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
allowJs: true,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
@@ -445,7 +445,7 @@ namespace ts {
sourceMap: false,
lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"]
},
errors: <Diagnostic[]>[]
errors: []
}
);
});
@@ -463,7 +463,7 @@ namespace ts {
}
}, "jsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
allowJs: false,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
@@ -474,7 +474,7 @@ namespace ts {
sourceMap: false,
lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"]
},
errors: <Diagnostic[]>[]
errors: []
}
);
});
@@ -516,7 +516,7 @@ namespace ts {
allowSyntheticDefaultImports: true,
skipLibCheck: true
},
errors: <Diagnostic[]>[]
errors: []
}
);
});
+591
View File
@@ -0,0 +1,591 @@
/// <reference path="..\harness.ts" />
/// <reference path="tsserverProjectSystem.ts" />
namespace ts {
interface Range {
start: number;
end: number;
name: string;
}
interface Test {
source: string;
ranges: Map<Range>;
}
function extractTest(source: string): Test {
const activeRanges: Range[] = [];
let text = "";
let lastPos = 0;
let pos = 0;
const ranges = createMap<Range>();
while (pos < source.length) {
if (source.charCodeAt(pos) === CharacterCodes.openBracket &&
(source.charCodeAt(pos + 1) === CharacterCodes.hash || source.charCodeAt(pos + 1) === CharacterCodes.$)) {
const saved = pos;
pos += 2;
const s = pos;
consumeIdentifier();
const e = pos;
if (source.charCodeAt(pos) === CharacterCodes.bar) {
pos++;
text += source.substring(lastPos, saved);
const name = s === e
? source.charCodeAt(saved + 1) === CharacterCodes.hash ? "selection" : "extracted"
: source.substring(s, e);
activeRanges.push({ name, start: text.length, end: undefined });
lastPos = pos;
continue;
}
else {
pos = saved;
}
}
else if (source.charCodeAt(pos) === CharacterCodes.bar && source.charCodeAt(pos + 1) === CharacterCodes.closeBracket) {
text += source.substring(lastPos, pos);
activeRanges[activeRanges.length - 1].end = text.length;
const range = activeRanges.pop();
if (range.name in ranges) {
throw new Error(`Duplicate name of range ${range.name}`);
}
ranges.set(range.name, range);
pos += 2;
lastPos = pos;
continue;
}
pos++;
}
text += source.substring(lastPos, pos);
function consumeIdentifier() {
while (isIdentifierPart(source.charCodeAt(pos), ScriptTarget.Latest)) {
pos++;
}
}
return { source: text, ranges };
}
const newLineCharacter = "\n";
function getRuleProvider(action?: (opts: FormatCodeSettings) => void) {
const options = {
indentSize: 4,
tabSize: 4,
newLineCharacter,
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Smart,
insertSpaceAfterConstructor: false,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
insertSpaceBeforeAndAfterBinaryOperators: true,
insertSpaceAfterKeywordsInControlFlowStatements: true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
insertSpaceBeforeFunctionParenthesis: false,
placeOpenBraceOnNewLineForFunctions: false,
placeOpenBraceOnNewLineForControlBlocks: false,
};
if (action) {
action(options);
}
const rulesProvider = new formatting.RulesProvider();
rulesProvider.ensureUpToDate(options);
return rulesProvider;
}
function testExtractRangeFailed(caption: string, s: string, expectedErrors: string[]) {
return it(caption, () => {
const t = extractTest(s);
const file = createSourceFile("a.ts", t.source, ScriptTarget.Latest, /*setParentNodes*/ true);
const selectionRange = t.ranges.get("selection");
if (!selectionRange) {
throw new Error(`Test ${s} does not specify selection range`);
}
const result = refactor.extractMethod.getRangeToExtract(file, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
assert(result.targetRange === undefined, "failure expected");
const sortedErrors = result.errors.map(e => <string>e.messageText).sort();
assert.deepEqual(sortedErrors, expectedErrors.sort(), "unexpected errors");
});
}
function testExtractRange(s: string): void {
const t = extractTest(s);
const f = createSourceFile("a.ts", t.source, ScriptTarget.Latest, /*setParentNodes*/ true);
const selectionRange = t.ranges.get("selection");
if (!selectionRange) {
throw new Error(`Test ${s} does not specify selection range`);
}
const result = refactor.extractMethod.getRangeToExtract(f, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
const expectedRange = t.ranges.get("extracted");
if (expectedRange) {
let start: number, end: number;
if (ts.isArray(result.targetRange.range)) {
start = result.targetRange.range[0].getStart(f);
end = ts.lastOrUndefined(result.targetRange.range).getEnd();
}
else {
start = result.targetRange.range.getStart(f);
end = result.targetRange.range.getEnd();
}
assert.equal(start, expectedRange.start, "incorrect start of range");
assert.equal(end, expectedRange.end, "incorrect end of range");
}
else {
assert.isTrue(!result.targetRange, `expected range to extract to be undefined`);
}
}
describe("extractMethods", () => {
it("get extract range from selection", () => {
testExtractRange(`
[#|
[$|var x = 1;
var y = 2;|]|]
`);
testExtractRange(`
[#|
var x = 1;
var y = 2|];
`);
testExtractRange(`
[#|var x = 1|];
var y = 2;
`);
testExtractRange(`
if ([#|[#extracted|a && b && c && d|]|]) {
}
`);
testExtractRange(`
if [#|(a && b && c && d|]) {
}
`);
testExtractRange(`
if (a && b && c && d) {
[#| [$|var x = 1;
console.log(x);|] |]
}
`);
testExtractRange(`
[#|
if (a) {
return 100;
} |]
`);
testExtractRange(`
function foo() {
[#| [$|if (a) {
}
return 100|] |]
}
`);
testExtractRange(`
[#|
[$|l1:
if (x) {
break l1;
}|]|]
`);
testExtractRange(`
[#|
[$|l2:
{
if (x) {
}
break l2;
}|]|]
`);
testExtractRange(`
while (true) {
[#| if(x) {
}
break; |]
}
`);
testExtractRange(`
while (true) {
[#| if(x) {
}
continue; |]
}
`);
testExtractRange(`
l3:
{
[#|
if (x) {
}
break l3; |]
}
`);
testExtractRange(`
function f() {
while (true) {
[#|
if (x) {
return;
} |]
}
}
`);
testExtractRange(`
function f() {
while (true) {
[#|
[$|if (x) {
}
return;|]
|]
}
}
`);
testExtractRange(`
function f() {
return [#| [$|1 + 2|] |]+ 3;
}
}
`);
testExtractRange(`
function f() {
return [$|1 + [#|2 + 3|]|];
}
}
`);
testExtractRange(`
function f() {
return [$|1 + 2 + [#|3 + 4|]|];
}
}
`);
});
testExtractRangeFailed("extractRangeFailed1",
`
namespace A {
function f() {
[#|
let x = 1
if (x) {
return 10;
}
|]
}
}
`,
[
"Cannot extract range containing conditional return statement."
]);
testExtractRangeFailed("extractRangeFailed2",
`
namespace A {
function f() {
while (true) {
[#|
let x = 1
if (x) {
break;
}
|]
}
}
}
`,
[
"Cannot extract range containing conditional break or continue statements."
]);
testExtractRangeFailed("extractRangeFailed3",
`
namespace A {
function f() {
while (true) {
[#|
let x = 1
if (x) {
continue;
}
|]
}
}
}
`,
[
"Cannot extract range containing conditional break or continue statements."
]);
testExtractRangeFailed("extractRangeFailed4",
`
namespace A {
function f() {
l1: {
[#|
let x = 1
if (x) {
break l1;
}
|]
}
}
}
`,
[
"Cannot extract range containing labeled break or continue with target outside of the range."
]);
testExtractRangeFailed("extractRangeFailed5",
`
namespace A {
function f() {
[#|
try {
f2()
return 10;
}
catch (e) {
}
|]
}
function f2() {
}
}
`,
[
"Cannot extract range containing conditional return statement."
]);
testExtractRangeFailed("extractRangeFailed6",
`
namespace A {
function f() {
[#|
try {
f2()
}
catch (e) {
return 10;
}
|]
}
function f2() {
}
}
`,
[
"Cannot extract range containing conditional return statement."
]);
testExtractMethod("extractMethod1",
`namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
let a = 1;
[#|
let y = 5;
let z = x;
a = y;
foo();|]
}
}
}`);
testExtractMethod("extractMethod2",
`namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
[#|
let y = 5;
let z = x;
return foo();|]
}
}
}`);
testExtractMethod("extractMethod3",
`namespace A {
function foo() {
}
namespace B {
function* a(z: number) {
[#|
let y = 5;
yield z;
return foo();|]
}
}
}`);
testExtractMethod("extractMethod4",
`namespace A {
function foo() {
}
namespace B {
async function a(z: number, z1: any) {
[#|
let y = 5;
if (z) {
await z1;
}
return foo();|]
}
}
}`);
testExtractMethod("extractMethod5",
`namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
[#|
let y = 5;
let z = x;
a = y;
foo();|]
}
}
}`);
testExtractMethod("extractMethod6",
`namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
[#|
let y = 5;
let z = x;
a = y;
return foo();|]
}
}
}`);
testExtractMethod("extractMethod7",
`namespace A {
let x = 1;
export namespace C {
export function foo() {
}
}
namespace B {
function a() {
let a = 1;
[#|
let y = 5;
let z = x;
a = y;
return C.foo();|]
}
}
}`);
testExtractMethod("extractMethod8",
`namespace A {
let x = 1;
namespace B {
function a() {
let a1 = 1;
return 1 + [#|a1 + x|] + 100;
}
}
}`);
testExtractMethod("extractMethod9",
`namespace A {
export interface I { x: number };
namespace B {
function a() {
[#|let a1: I = { x: 1 };
return a1.x + 10;|]
}
}
}`);
testExtractMethod("extractMethod10",
`namespace A {
export interface I { x: number };
class C {
a() {
let z = 1;
[#|let a1: I = { x: 1 };
return a1.x + 10;|]
}
}
}`);
testExtractMethod("extractMethod11",
`namespace A {
let y = 1;
class C {
a() {
let z = 1;
[#|let a1 = { x: 1 };
y = 10;
z = 42;
return a1.x + 10;|]
}
}
}`);
testExtractMethod("extractMethod12",
`namespace A {
let y = 1;
class C {
b() {}
a() {
let z = 1;
[#|let a1 = { x: 1 };
y = 10;
z = 42;
this.b();
return a1.x + 10;|]
}
}
}`);
});
function testExtractMethod(caption: string, text: string) {
it(caption, () => {
Harness.Baseline.runBaseline(`extractMethod/${caption}.js`, () => {
const t = extractTest(text);
const selectionRange = t.ranges.get("selection");
if (!selectionRange) {
throw new Error(`Test ${caption} does not specify selection range`);
}
const f = {
path: "/a.ts",
content: t.source
};
const host = projectSystem.createServerHost([f]);
const projectService = projectSystem.createProjectService(host);
projectService.openClientFile(f.path);
const program = projectService.inferredProjects[0].getLanguageService().getProgram();
const sourceFile = program.getSourceFile(f.path);
const context: RefactorContext = {
cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } },
newLineCharacter,
program,
file: sourceFile,
startPosition: -1,
rulesProvider: getRuleProvider()
};
const result = refactor.extractMethod.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
assert.equal(result.errors, undefined, "expect no errors");
const results = refactor.extractMethod.getPossibleExtractions(result.targetRange, context);
const data: string[] = [];
data.push(`==ORIGINAL==`);
data.push(sourceFile.text);
for (const r of results) {
const changes = refactor.extractMethod.getPossibleExtractions(result.targetRange, context, results.indexOf(r))[0].changes;
data.push(`==SCOPE::${r.scopeDescription}==`);
data.push(textChanges.applyChanges(sourceFile.text, changes[0].textChanges));
}
return data.join(newLineCharacter);
});
});
}
}
+9 -11
View File
@@ -110,23 +110,21 @@ namespace ts {
}
{
const actual = ts.parseJsonConfigFileContent(json, host, basePath, existingOptions, configFileName, resolutionStack);
expected.errors = map(expected.errors, error => {
return <Diagnostic>{
category: error.category,
code: error.code,
file: undefined,
length: undefined,
messageText: error.messageText,
start: undefined,
};
});
expected.errors = expected.errors.map<Diagnostic>(error => ({
category: error.category,
code: error.code,
file: undefined,
length: undefined,
messageText: error.messageText,
start: undefined,
}));
assertParsed(actual, expected);
}
}
function createDiagnosticForConfigFile(json: any, start: number, length: number, diagnosticMessage: DiagnosticMessage, arg0: string) {
const text = JSON.stringify(json);
const file = <SourceFile>{
const file = <SourceFile>{ // tslint:disable-line no-object-literal-type-assertion
fileName: caseInsensitiveTsconfigPath,
kind: SyntaxKind.SourceFile,
text
+30 -4
View File
@@ -26,10 +26,19 @@ namespace ts {
interface File {
name: string;
content?: string;
symlinks?: string[];
}
function createModuleResolutionHost(hasDirectoryExists: boolean, ...files: File[]): ModuleResolutionHost {
const map = arrayToMap(files, f => f.name);
const map = createMap<File>();
for (const file of files) {
map.set(file.name, file);
if (file.symlinks) {
for (const symlink of file.symlinks) {
map.set(symlink, file);
}
}
}
if (hasDirectoryExists) {
const directories = createMap<string>();
@@ -46,6 +55,7 @@ namespace ts {
}
return {
readFile,
realpath,
directoryExists: path => directories.has(path),
fileExists: path => {
assert.isTrue(directories.has(getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`);
@@ -54,12 +64,15 @@ namespace ts {
};
}
else {
return { readFile, fileExists: path => map.has(path) };
return { readFile, realpath, fileExists: path => map.has(path) };
}
function readFile(path: string): string | undefined {
const file = map.get(path);
return file && file.content;
}
function realpath(path: string): string {
return map.get(path).name;
}
}
describe("Node module resolution - relative paths", () => {
@@ -233,8 +246,8 @@ namespace ts {
test(/*hasDirectoryExists*/ true);
function test(hasDirectoryExists: boolean) {
const containingFile = { name: "/a/node_modules/b/c/node_modules/d/e.ts" };
const moduleFile = { name: "/a/node_modules/foo/index.d.ts" };
const containingFile: File = { name: "/a/node_modules/b/c/node_modules/d/e.ts" };
const moduleFile: File = { name: "/a/node_modules/foo/index.d.ts" };
const resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
checkResolvedModuleWithFailedLookupLocations(resolution, createResolvedModule(moduleFile.name, /*isExternalLibraryImport*/ true), [
"/a/node_modules/b/c/node_modules/d/node_modules/foo.ts",
@@ -289,6 +302,19 @@ namespace ts {
]);
}
});
testPreserveSymlinks(/*preserveSymlinks*/ false);
testPreserveSymlinks(/*preserveSymlinks*/ true);
function testPreserveSymlinks(preserveSymlinks: boolean) {
it(`preserveSymlinks: ${preserveSymlinks}`, () => {
const realFileName = "/linked/index.d.ts";
const symlinkFileName = "/app/node_modulex/linked/index.d.ts";
const host = createModuleResolutionHost(/*hasDirectoryExists*/ true, { name: realFileName, symlinks: [symlinkFileName] });
const resolution = nodeModuleNameResolver("linked", "/app/app.ts", { preserveSymlinks }, host);
const resolvedFileName = preserveSymlinks ? symlinkFileName : realFileName;
checkResolvedModule(resolution.resolvedModule, { resolvedFileName, isExternalLibraryImport: true, extension: Extension.Dts });
});
}
});
describe("Module resolution - relative imports", () => {
+2 -2
View File
@@ -4,12 +4,12 @@
namespace ts.projectSystem {
describe("Project errors", () => {
function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: string[]) {
function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: ReadonlyArray<string>): void {
assert.isTrue(projectFiles !== undefined, "missing project files");
checkProjectErrorsWorker(projectFiles.projectErrors, expectedErrors);
}
function checkProjectErrorsWorker(errors: Diagnostic[], expectedErrors: string[]) {
function checkProjectErrorsWorker(errors: ReadonlyArray<Diagnostic>, expectedErrors: ReadonlyArray<string>): void {
assert.equal(errors ? errors.length : 0, expectedErrors.length, `expected ${expectedErrors.length} error in the list`);
if (expectedErrors.length) {
for (let i = 0; i < errors.length; i++) {
+129 -29
View File
@@ -109,7 +109,10 @@ namespace ts {
function createTestCompilerHost(texts: NamedSourceText[], target: ScriptTarget, oldProgram?: ProgramWithSourceTexts): TestCompilerHost {
const files = arrayToMap(texts, t => t.name, t => {
if (oldProgram) {
const oldFile = <SourceFileWithText>oldProgram.getSourceFile(t.name);
let oldFile = <SourceFileWithText>oldProgram.getSourceFile(t.name);
if (oldFile && oldFile.redirectInfo) {
oldFile = oldFile.redirectInfo.unredirected;
}
if (oldFile && oldFile.sourceText.getVersion() === t.text.getVersion()) {
return oldFile;
}
@@ -171,11 +174,16 @@ namespace ts {
return program;
}
function updateProgramText(files: ReadonlyArray<NamedSourceText>, fileName: string, newProgramText: string) {
const file = find(files, f => f.name === fileName)!;
file.text = file.text.updateProgram(newProgramText);
}
function checkResolvedTypeDirective(expected: ResolvedTypeReferenceDirective, actual: ResolvedTypeReferenceDirective): boolean {
if (!expected === !actual) {
if (expected) {
assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`);
assert.isTrue(expected.primary === actual.primary, `'primary': expected '${expected.primary}' to be equal to '${actual.primary}'`);
assert.equal(expected.resolvedFileName, actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`);
assert.equal(expected.primary, actual.primary, `'primary': expected '${expected.primary}' to be equal to '${actual.primary}'`);
}
return true;
}
@@ -238,7 +246,7 @@ namespace ts {
const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
files[0].text = files[0].text.updateProgram("var x = 100");
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
const program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
@@ -249,7 +257,7 @@ namespace ts {
const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
files[0].text = files[0].text.updateProgram("var x = 100");
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
const program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
@@ -263,19 +271,19 @@ namespace ts {
`;
files[0].text = files[0].text.updateReferences(newReferences);
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_1.structureIsReused, StructureIsReused.SafeModules);
});
it("fails if change affects type references", () => {
const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
updateProgram(program_1, ["a.ts"], { types: ["b"] }, noop);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
});
it("succeeds if change doesn't affect type references", () => {
const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
updateProgram(program_1, ["a.ts"], { types: ["a"] }, noop);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
});
it("fails if change affects imports", () => {
@@ -283,7 +291,7 @@ namespace ts {
updateProgram(program_1, ["a.ts"], { target }, files => {
files[2].text = files[2].text.updateImportsAndExports("import x from 'b'");
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_1.structureIsReused, StructureIsReused.SafeModules);
});
it("fails if change affects type directives", () => {
@@ -295,25 +303,25 @@ namespace ts {
/// <reference types="typerefs1" />`;
files[0].text = files[0].text.updateReferences(newReferences);
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_1.structureIsReused, StructureIsReused.SafeModules);
});
it("fails if module kind changes", () => {
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS });
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.AMD }, noop);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
});
it("fails if rootdir changes", () => {
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/b" });
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/c" }, noop);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
});
it("fails if config path changes", () => {
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/b/tsconfig.json" });
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/c/tsconfig.json" }, noop);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
});
it("succeeds if missing files remain missing", () => {
@@ -357,7 +365,7 @@ namespace ts {
const program_2 = updateProgram(program_1, ["a.ts"], options, files => {
files[0].text = files[0].text.updateProgram("var x = 2");
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
// content of resolution cache should not change
checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") }));
@@ -367,7 +375,7 @@ namespace ts {
const program_3 = updateProgram(program_2, ["a.ts"], options, files => {
files[0].text = files[0].text.updateImportsAndExports("");
});
assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_2.structureIsReused, StructureIsReused.SafeModules);
checkResolvedModulesCache(program_3, "a.ts", /*expectedContent*/ undefined);
const program_4 = updateProgram(program_3, ["a.ts"], options, files => {
@@ -376,7 +384,7 @@ namespace ts {
`;
files[0].text = files[0].text.updateImportsAndExports(newImports);
});
assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_3.structureIsReused, StructureIsReused.SafeModules);
checkResolvedModulesCache(program_4, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts"), "c": undefined }));
});
@@ -394,7 +402,7 @@ namespace ts {
const program_2 = updateProgram(program_1, ["/a.ts"], options, files => {
files[0].text = files[0].text.updateProgram("var x = 2");
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
// content of resolution cache should not change
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
@@ -405,7 +413,7 @@ namespace ts {
files[0].text = files[0].text.updateReferences("");
});
assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_2.structureIsReused, StructureIsReused.SafeModules);
checkResolvedTypeDirectivesCache(program_3, "/a.ts", /*expectedContent*/ undefined);
updateProgram(program_3, ["/a.ts"], options, files => {
@@ -414,7 +422,7 @@ namespace ts {
`;
files[0].text = files[0].text.updateReferences(newReferences);
});
assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_3.structureIsReused, StructureIsReused.SafeModules);
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
});
@@ -454,7 +462,7 @@ namespace ts {
"initialProgram: execute module resolution normally.");
const initialProgramDiagnostics = initialProgram.getSemanticDiagnostics(initialProgram.getSourceFile("file1.ts"));
assert(initialProgramDiagnostics.length === 1, `initialProgram: import should fail.`);
assert.lengthOf(initialProgramDiagnostics, 1, `initialProgram: import should fail.`);
}
const afterNpmInstallProgram = updateProgram(initialProgram, rootFiles.map(f => f.name), options, f => {
@@ -478,7 +486,7 @@ namespace ts {
"afterNpmInstallProgram: execute module resolution normally.");
const afterNpmInstallProgramDiagnostics = afterNpmInstallProgram.getSemanticDiagnostics(afterNpmInstallProgram.getSourceFile("file1.ts"));
assert(afterNpmInstallProgramDiagnostics.length === 0, `afterNpmInstallProgram: program is well-formed with import.`);
assert.lengthOf(afterNpmInstallProgramDiagnostics, 0, `afterNpmInstallProgram: program is well-formed with import.`);
}
});
@@ -617,10 +625,10 @@ namespace ts {
"File 'f1.ts' exist - use it as a name resolution result.",
"======== Module name './f1' was successfully resolved to 'f1.ts'. ========"
],
"program_1: execute module reoslution normally.");
"program_1: execute module resolution normally.");
const program_1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("f2.ts"));
assert(program_1Diagnostics.length === expectedErrors, `initial program should be well-formed`);
assert.lengthOf(program_1Diagnostics, expectedErrors, `initial program should be well-formed`);
}
const indexOfF1 = 6;
const program_2 = updateProgram(program_1, program_1.getRootFileNames(), options, f => {
@@ -630,7 +638,7 @@ namespace ts {
{
const program_2Diagnostics = program_2.getSemanticDiagnostics(program_2.getSourceFile("f2.ts"));
assert(program_2Diagnostics.length === expectedErrors, `removing no-default-lib shouldn't affect any types used.`);
assert.lengthOf(program_2Diagnostics, expectedErrors, `removing no-default-lib shouldn't affect any types used.`);
assert.deepEqual(program_2.host.getTrace(), [
"======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========",
@@ -659,7 +667,7 @@ namespace ts {
{
const program_3Diagnostics = program_3.getSemanticDiagnostics(program_3.getSourceFile("f2.ts"));
assert(program_3Diagnostics.length === expectedErrors, `typerefs2 was unused, so diagnostics should be unaffected.`);
assert.lengthOf(program_3Diagnostics, expectedErrors, `typerefs2 was unused, so diagnostics should be unaffected.`);
assert.deepEqual(program_3.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
@@ -684,7 +692,7 @@ namespace ts {
{
const program_4Diagnostics = program_4.getSemanticDiagnostics(program_4.getSourceFile("f2.ts"));
assert(program_4Diagnostics.length === expectedErrors, `a1.ts was unused, so diagnostics should be unaffected.`);
assert.lengthOf(program_4Diagnostics, expectedErrors, `a1.ts was unused, so diagnostics should be unaffected.`);
assert.deepEqual(program_4.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
@@ -708,7 +716,7 @@ namespace ts {
{
const program_5Diagnostics = program_5.getSemanticDiagnostics(program_5.getSourceFile("f2.ts"));
assert(program_5Diagnostics.length === ++expectedErrors, `import of BB in f1 fails. BB is of type any. Add one error`);
assert.lengthOf(program_5Diagnostics, ++expectedErrors, `import of BB in f1 fails. BB is of type any. Add one error`);
assert.deepEqual(program_5.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
@@ -725,7 +733,7 @@ namespace ts {
{
const program_6Diagnostics = program_6.getSemanticDiagnostics(program_6.getSourceFile("f2.ts"));
assert(program_6Diagnostics.length === expectedErrors, `import of BB in f1 fails.`);
assert.lengthOf(program_6Diagnostics, expectedErrors, `import of BB in f1 fails.`);
assert.deepEqual(program_6.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
@@ -749,7 +757,7 @@ namespace ts {
{
const program_7Diagnostics = program_7.getSemanticDiagnostics(program_7.getSourceFile("f2.ts"));
assert(program_7Diagnostics.length === expectedErrors, `removing import is noop with respect to program, so no change in diagnostics.`);
assert.lengthOf(program_7Diagnostics, expectedErrors, `removing import is noop with respect to program, so no change in diagnostics.`);
assert.deepEqual(program_7.host.getTrace(), [
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
@@ -762,6 +770,98 @@ namespace ts {
], "program_7 should reuse module resolutions in f2 since it is unchanged");
}
});
describe("redirects", () => {
const axIndex = "/node_modules/a/node_modules/x/index.d.ts";
const axPackage = "/node_modules/a/node_modules/x/package.json";
const bxIndex = "/node_modules/b/node_modules/x/index.d.ts";
const bxPackage = "/node_modules/b/node_modules/x/package.json";
const root = "/a.ts";
const compilerOptions = { target, moduleResolution: ModuleResolutionKind.NodeJs };
function createRedirectProgram(options?: { bText: string, bVersion: string }): ProgramWithSourceTexts {
const files: NamedSourceText[] = [
{
name: "/node_modules/a/index.d.ts",
text: SourceText.New("", 'import X from "x";', "export function a(x: X): void;"),
},
{
name: axIndex,
text: SourceText.New("", "", "export default class X { private x: number; }"),
},
{
name: axPackage,
text: SourceText.New("", "", JSON.stringify({ name: "x", version: "1.2.3" })),
},
{
name: "/node_modules/b/index.d.ts",
text: SourceText.New("", 'import X from "x";', "export const b: X;"),
},
{
name: bxIndex,
text: SourceText.New("", "", options ? options.bText : "export default class X { private x: number; }"),
},
{
name: bxPackage,
text: SourceText.New("", "", JSON.stringify({ name: "x", version: options ? options.bVersion : "1.2.3" })),
},
{
name: root,
text: SourceText.New("", 'import { a } from "a"; import { b } from "b";', "a(b)"),
},
];
return newProgram(files, [root], compilerOptions);
}
function updateRedirectProgram(program: ProgramWithSourceTexts, updater: (files: NamedSourceText[]) => void): ProgramWithSourceTexts {
return updateProgram(program, [root], compilerOptions, updater);
}
it("No changes -> redirect not broken", () => {
const program_1 = createRedirectProgram();
const program_2 = updateRedirectProgram(program_1, files => {
updateProgramText(files, root, "const x = 1;");
});
assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
assert.deepEqual(program_2.getSemanticDiagnostics(), emptyArray);
});
it("Target changes -> redirect broken", () => {
const program_1 = createRedirectProgram();
assert.deepEqual(program_1.getSemanticDiagnostics(), emptyArray);
const program_2 = updateRedirectProgram(program_1, files => {
updateProgramText(files, axIndex, "export default class X { private x: number; private y: number; }");
updateProgramText(files, axPackage, JSON.stringify('{ name: "x", version: "1.2.4" }'));
});
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
assert.lengthOf(program_2.getSemanticDiagnostics(), 1);
});
it("Underlying changes -> redirect broken", () => {
const program_1 = createRedirectProgram();
const program_2 = updateRedirectProgram(program_1, files => {
updateProgramText(files, bxIndex, "export default class X { private x: number; private y: number; }");
updateProgramText(files, bxPackage, JSON.stringify({ name: "x", version: "1.2.4" }));
});
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
assert.lengthOf(program_2.getSemanticDiagnostics(), 1);
});
it("Previously duplicate packages -> program structure not reused", () => {
const program_1 = createRedirectProgram({ bVersion: "1.2.4", bText: "export = class X { private x: number; }" });
const program_2 = updateRedirectProgram(program_1, files => {
updateProgramText(files, bxIndex, "export default class X { private x: number; }");
updateProgramText(files, bxPackage, JSON.stringify({ name: "x", version: "1.2.3" }));
});
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
assert.deepEqual(program_2.getSemanticDiagnostics(), []);
});
});
});
describe("host is optional", () => {
+6 -2
View File
@@ -43,6 +43,7 @@ namespace ts.server {
host: mockHost,
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
@@ -81,14 +82,15 @@ namespace ts.server {
session.executeCommand(req);
expect(lastSent).to.deep.equal(<protocol.Response>{
const expected: protocol.Response = {
command: CommandNames.Unknown,
type: "response",
seq: 0,
message: "Unrecognized JSON command: foobar",
request_seq: 0,
success: false
});
};
expect(lastSent).to.deep.equal(expected);
});
it("should return a tuple containing the response and if a response is required on success", () => {
const req: protocol.ConfigureRequest = {
@@ -393,6 +395,7 @@ namespace ts.server {
host: mockHost,
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
@@ -460,6 +463,7 @@ namespace ts.server {
host: mockHost,
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
+153 -30
View File
@@ -185,23 +185,28 @@ namespace ts.projectSystem {
}
}
export function createSession(host: server.ServerHost, typingsInstaller?: server.ITypingsInstaller, projectServiceEventHandler?: server.ProjectServiceEventHandler, cancellationToken?: server.ServerCancellationToken, throttleWaitMilliseconds?: number) {
if (typingsInstaller === undefined) {
typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host);
export function createSession(host: server.ServerHost, opts: Partial<server.SessionOptions> = {}) {
if (opts.typingsInstaller === undefined) {
opts.typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/ 5, host);
}
const opts: server.SessionOptions = {
if (opts.eventHandler !== undefined) {
opts.canUseEvents = true;
}
const sessionOptions: server.SessionOptions = {
host,
cancellationToken: cancellationToken || server.nullCancellationToken,
cancellationToken: server.nullCancellationToken,
useSingleInferredProject: false,
typingsInstaller,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: nullLogger,
canUseEvents: projectServiceEventHandler !== undefined,
eventHandler: projectServiceEventHandler,
throttleWaitMilliseconds
canUseEvents: false
};
return new TestSession(opts);
return new TestSession({ ...sessionOptions, ...opts });
}
interface CreateProjectServiceParameters {
@@ -215,9 +220,16 @@ namespace ts.projectSystem {
export class TestProjectService extends server.ProjectService {
constructor(host: server.ServerHost, logger: server.Logger, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean,
typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler) {
typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler, opts: Partial<server.ProjectServiceOptions> = {}) {
super({
host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, eventHandler
host,
logger,
cancellationToken,
useSingleInferredProject,
useInferredProjectPerProjectRoot: false,
typingsInstaller,
eventHandler,
...opts
});
}
@@ -631,7 +643,7 @@ namespace ts.projectSystem {
}
}
describe("tsserver-project-system", () => {
describe("tsserverProjectSystem", () => {
const commonFile1: FileOrFolder = {
path: "/a/b/commonFile1.ts",
content: "let x = 1"
@@ -2230,13 +2242,16 @@ namespace ts.projectSystem {
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
let lastEvent: server.ProjectLanguageServiceStateEvent;
const session = createSession(host, /*typingsInstaller*/ undefined, e => {
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ContextEvent || e.eventName === server.ProjectInfoTelemetryEvent) {
return;
const session = createSession(host, {
canUseEvents: true,
eventHandler: e => {
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ContextEvent || e.eventName === server.ProjectInfoTelemetryEvent) {
return;
}
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
assert.equal(e.data.project.getProjectName(), config.path, "project name");
lastEvent = <server.ProjectLanguageServiceStateEvent>e;
}
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
assert.equal(e.data.project.getProjectName(), config.path, "project name");
lastEvent = <server.ProjectLanguageServiceStateEvent>e;
});
session.executeCommand(<protocol.OpenRequest>{
seq: 0,
@@ -2280,12 +2295,15 @@ namespace ts.projectSystem {
host.getFileSize = (filePath: string) =>
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
let lastEvent: server.ProjectLanguageServiceStateEvent;
const session = createSession(host, /*typingsInstaller*/ undefined, e => {
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectInfoTelemetryEvent) {
return;
const session = createSession(host, {
canUseEvents: true,
eventHandler: e => {
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectInfoTelemetryEvent) {
return;
}
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
lastEvent = <server.ProjectLanguageServiceStateEvent>e;
}
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
lastEvent = <server.ProjectLanguageServiceStateEvent>e;
});
session.executeCommand(<protocol.OpenRequest>{
seq: 0,
@@ -3069,7 +3087,10 @@ namespace ts.projectSystem {
};
const host = createServerHost([file, configFile]);
const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler);
const session = createSession(host, {
canUseEvents: true,
eventHandler: serverEventManager.handler
});
openFilesForSession([file], session);
serverEventManager.checkEventCountOfType("configFileDiag", 1);
@@ -3096,7 +3117,10 @@ namespace ts.projectSystem {
};
const host = createServerHost([file, configFile]);
const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler);
const session = createSession(host, {
canUseEvents: true,
eventHandler: serverEventManager.handler
});
openFilesForSession([file], session);
serverEventManager.checkEventCountOfType("configFileDiag", 1);
});
@@ -3115,7 +3139,10 @@ namespace ts.projectSystem {
};
const host = createServerHost([file, configFile]);
const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler);
const session = createSession(host, {
canUseEvents: true,
eventHandler: serverEventManager.handler
});
openFilesForSession([file], session);
serverEventManager.checkEventCountOfType("configFileDiag", 1);
@@ -3504,6 +3531,93 @@ namespace ts.projectSystem {
checkNumberOfProjects(projectService, { inferredProjects: 1 });
checkProjectActualFiles(projectService.inferredProjects[0], [f.path]);
});
it("inferred projects per project root", () => {
const file1 = { path: "/a/file1.ts", content: "let x = 1;", projectRootPath: "/a" };
const file2 = { path: "/a/file2.ts", content: "let y = 2;", projectRootPath: "/a" };
const file3 = { path: "/b/file2.ts", content: "let x = 3;", projectRootPath: "/b" };
const file4 = { path: "/c/file3.ts", content: "let z = 4;" };
const host = createServerHost([file1, file2, file3, file4]);
const session = createSession(host, {
useSingleInferredProject: true,
useInferredProjectPerProjectRoot: true
});
session.executeCommand(<server.protocol.SetCompilerOptionsForInferredProjectsRequest>{
seq: 1,
type: "request",
command: CommandNames.CompilerOptionsForInferredProjects,
arguments: {
options: {
allowJs: true,
target: ScriptTarget.ESNext
}
}
});
session.executeCommand(<server.protocol.SetCompilerOptionsForInferredProjectsRequest>{
seq: 2,
type: "request",
command: CommandNames.CompilerOptionsForInferredProjects,
arguments: {
options: {
allowJs: true,
target: ScriptTarget.ES2015
},
projectRootPath: "/b"
}
});
session.executeCommand(<server.protocol.OpenRequest>{
seq: 3,
type: "request",
command: CommandNames.Open,
arguments: {
file: file1.path,
fileContent: file1.content,
scriptKindName: "JS",
projectRootPath: file1.projectRootPath
}
});
session.executeCommand(<server.protocol.OpenRequest>{
seq: 4,
type: "request",
command: CommandNames.Open,
arguments: {
file: file2.path,
fileContent: file2.content,
scriptKindName: "JS",
projectRootPath: file2.projectRootPath
}
});
session.executeCommand(<server.protocol.OpenRequest>{
seq: 5,
type: "request",
command: CommandNames.Open,
arguments: {
file: file3.path,
fileContent: file3.content,
scriptKindName: "JS",
projectRootPath: file3.projectRootPath
}
});
session.executeCommand(<server.protocol.OpenRequest>{
seq: 6,
type: "request",
command: CommandNames.Open,
arguments: {
file: file4.path,
fileContent: file4.content,
scriptKindName: "JS"
}
});
const projectService = session.getProjectService();
checkNumberOfProjects(projectService, { inferredProjects: 3 });
checkProjectActualFiles(projectService.inferredProjects[0], [file4.path]);
checkProjectActualFiles(projectService.inferredProjects[1], [file1.path, file2.path]);
checkProjectActualFiles(projectService.inferredProjects[2], [file3.path]);
assert.equal(projectService.inferredProjects[0].getCompilerOptions().target, ScriptTarget.ESNext);
assert.equal(projectService.inferredProjects[1].getCompilerOptions().target, ScriptTarget.ESNext);
assert.equal(projectService.inferredProjects[2].getCompilerOptions().target, ScriptTarget.ES2015);
});
});
describe("No overwrite emit error", () => {
@@ -3697,7 +3811,7 @@ namespace ts.projectSystem {
resetRequest: noop
};
const session = createSession(host, /*typingsInstaller*/ undefined, /*projectServiceEventHandler*/ undefined, cancellationToken);
const session = createSession(host, { cancellationToken });
expectedRequestId = session.getNextSeq();
session.executeCommandSeq(<server.protocol.OpenRequest>{
@@ -3737,7 +3851,11 @@ namespace ts.projectSystem {
const cancellationToken = new TestServerCancellationToken();
const host = createServerHost([f1, config]);
const session = createSession(host, /*typingsInstaller*/ undefined, () => { }, cancellationToken);
const session = createSession(host, {
canUseEvents: true,
eventHandler: () => { },
cancellationToken
});
{
session.executeCommandSeq(<protocol.OpenRequest>{
command: "open",
@@ -3870,7 +3988,12 @@ namespace ts.projectSystem {
};
const cancellationToken = new TestServerCancellationToken(/*cancelAfterRequest*/ 3);
const host = createServerHost([f1, config]);
const session = createSession(host, /*typingsInstaller*/ undefined, () => { }, cancellationToken, /*throttleWaitMilliseconds*/ 0);
const session = createSession(host, {
canUseEvents: true,
eventHandler: () => { },
cancellationToken,
throttleWaitMilliseconds: 0
});
{
session.executeCommandSeq(<protocol.OpenRequest>{
command: "open",
+8 -6
View File
@@ -935,25 +935,26 @@ namespace ts.projectSystem {
import * as cmd from "commander
`
};
session.executeCommand(<server.protocol.OpenRequest>{
const openRequest: server.protocol.OpenRequest = {
seq: 1,
type: "request",
command: "open",
command: server.protocol.CommandTypes.Open,
arguments: {
file: f.path,
fileContent: f.content
}
});
};
session.executeCommand(openRequest);
const projectService = session.getProjectService();
checkNumberOfProjects(projectService, { inferredProjects: 1 });
const proj = projectService.inferredProjects[0];
const version1 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion();
// make a change that should not affect the structure of the program
session.executeCommand(<server.protocol.ChangeRequest>{
const changeRequest: server.protocol.ChangeRequest = {
seq: 2,
type: "request",
command: "change",
command: server.protocol.CommandTypes.Change,
arguments: {
file: f.path,
insertString: "\nlet x = 1;",
@@ -962,7 +963,8 @@ namespace ts.projectSystem {
endLine: 2,
endOffset: 0
}
});
};
session.executeCommand(changeRequest);
host.checkTimeoutQueueLength(1);
host.runQueuedTimeoutCallbacks();
const version2 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion();
+6
View File
@@ -49,6 +49,12 @@ var q:Point=<Point>p;`;
validateEditAtLineCharIndex = undefined;
});
it("handles empty lines array", () => {
const lineIndex = new server.LineIndex();
lineIndex.load([]);
assert.deepEqual(lineIndex.positionToLineOffset(0), { line: 1, offset: 1 });
});
it(`change 9 1 0 1 {"y"}`, () => {
validateEditAtLineCharIndex(9, 1, 0, "y");
});
+5 -5
View File
@@ -216,7 +216,7 @@ interface ObjectConstructor {
* Returns the names of the enumerable properties and methods of an object.
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
*/
keys(o: any): string[];
keys(o: {}): string[];
}
/**
@@ -980,12 +980,12 @@ interface ReadonlyArray<T> {
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: T[][]): T[];
concat(...items: ReadonlyArray<T>[]): T[];
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: (T | T[])[]): T[];
concat(...items: (T | ReadonlyArray<T>)[]): T[];
/**
* Adds all the elements of an array separated by the specified separator string.
* @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma.
@@ -1099,12 +1099,12 @@ interface Array<T> {
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: T[][]): T[];
concat(...items: ReadonlyArray<T>[]): T[];
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: (T | T[])[]): T[];
concat(...items: (T | ReadonlyArray<T>)[]): T[];
/**
* Adds all the elements of an array separated by the specified separator string.
* @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma.
+139 -48
View File
@@ -22,7 +22,7 @@ namespace ts.server {
export interface ConfigFileDiagEvent {
eventName: typeof ConfigFileDiagEvent;
data: { triggerFile: string, configFileName: string, diagnostics: Diagnostic[] };
data: { triggerFile: string, configFileName: string, diagnostics: ReadonlyArray<Diagnostic> };
}
export interface ProjectLanguageServiceStateEvent {
@@ -200,7 +200,7 @@ namespace ts.server {
/**
* This helper function processes a list of projects and return the concatenated, sortd and deduplicated output of processing each project.
*/
export function combineProjectOutput<T>(projects: Project[], action: (project: Project) => T[], comparer?: (a: T, b: T) => number, areEqual?: (a: T, b: T) => boolean) {
export function combineProjectOutput<T>(projects: ReadonlyArray<Project>, action: (project: Project) => ReadonlyArray<T>, comparer?: (a: T, b: T) => number, areEqual?: (a: T, b: T) => boolean) {
const result = flatMap(projects, action).sort(comparer);
return projects.length > 1 ? deduplicate(result, areEqual) : result;
}
@@ -220,14 +220,14 @@ namespace ts.server {
interface OpenConfigFileResult {
success: boolean;
errors?: Diagnostic[];
errors?: ReadonlyArray<Diagnostic>;
project?: ConfiguredProject;
}
export interface OpenConfiguredProjectResult {
configFileName?: NormalizedPath;
configFileErrors?: Diagnostic[];
configFileErrors?: ReadonlyArray<Diagnostic>;
}
interface FilePropertyReader<T> {
@@ -323,6 +323,7 @@ namespace ts.server {
logger: Logger;
cancellationToken: HostCancellationToken;
useSingleInferredProject: boolean;
useInferredProjectPerProjectRoot: boolean;
typingsInstaller: ITypingsInstaller;
eventHandler?: ProjectServiceEventHandler;
throttleWaitMilliseconds?: number;
@@ -364,7 +365,7 @@ namespace ts.server {
readonly openFiles: ScriptInfo[] = [];
private compilerOptionsForInferredProjects: CompilerOptions;
private compileOnSaveForInferredProjects: boolean;
private compilerOptionsForInferredProjectsPerProjectRoot = createMap<CompilerOptions>();
private readonly projectToSizeMap: Map<number> = createMap<number>();
private readonly directoryWatchers: DirectoryWatchers;
private readonly throttledOperations: ThrottledOperations;
@@ -382,6 +383,7 @@ namespace ts.server {
public readonly logger: Logger;
public readonly cancellationToken: HostCancellationToken;
public readonly useSingleInferredProject: boolean;
public readonly useInferredProjectPerProjectRoot: boolean;
public readonly typingsInstaller: ITypingsInstaller;
public readonly throttleWaitMilliseconds?: number;
private readonly eventHandler?: ProjectServiceEventHandler;
@@ -398,6 +400,7 @@ namespace ts.server {
this.logger = opts.logger;
this.cancellationToken = opts.cancellationToken;
this.useSingleInferredProject = opts.useSingleInferredProject;
this.useInferredProjectPerProjectRoot = opts.useInferredProjectPerProjectRoot;
this.typingsInstaller = opts.typingsInstaller || nullTypingsInstaller;
this.throttleWaitMilliseconds = opts.throttleWaitMilliseconds;
this.eventHandler = opts.eventHandler;
@@ -441,10 +444,11 @@ namespace ts.server {
if (!this.eventHandler) {
return;
}
this.eventHandler(<ProjectLanguageServiceStateEvent>{
const event: ProjectLanguageServiceStateEvent = {
eventName: ProjectLanguageServiceStateEvent,
data: { project, languageServiceEnabled }
});
};
this.eventHandler(event);
}
updateTypingsForProject(response: SetTypings | InvalidateCachedTypings): void {
@@ -463,17 +467,42 @@ namespace ts.server {
project.updateGraph();
}
setCompilerOptionsForInferredProjects(projectCompilerOptions: protocol.ExternalProjectCompilerOptions): void {
this.compilerOptionsForInferredProjects = convertCompilerOptions(projectCompilerOptions);
setCompilerOptionsForInferredProjects(projectCompilerOptions: protocol.ExternalProjectCompilerOptions, projectRootPath?: string): void {
Debug.assert(projectRootPath === undefined || this.useInferredProjectPerProjectRoot, "Setting compiler options per project root path is only supported when useInferredProjectPerProjectRoot is enabled");
const compilerOptions = convertCompilerOptions(projectCompilerOptions);
// always set 'allowNonTsExtensions' for inferred projects since user cannot configure it from the outside
// previously we did not expose a way for user to change these settings and this option was enabled by default
this.compilerOptionsForInferredProjects.allowNonTsExtensions = true;
this.compileOnSaveForInferredProjects = projectCompilerOptions.compileOnSave;
for (const proj of this.inferredProjects) {
proj.setCompilerOptions(this.compilerOptionsForInferredProjects);
proj.compileOnSaveEnabled = projectCompilerOptions.compileOnSave;
compilerOptions.allowNonTsExtensions = true;
if (projectRootPath) {
this.compilerOptionsForInferredProjectsPerProjectRoot.set(projectRootPath, compilerOptions);
}
this.updateProjectGraphs(this.inferredProjects);
else {
this.compilerOptionsForInferredProjects = compilerOptions;
}
const updatedProjects: Project[] = [];
for (const project of this.inferredProjects) {
// Only update compiler options in the following cases:
// - Inferred projects without a projectRootPath, if the new options do not apply to
// a workspace root
// - Inferred projects with a projectRootPath, if the new options do not apply to a
// workspace root and there is no more specific set of options for that project's
// root path
// - Inferred projects with a projectRootPath, if the new options apply to that
// project root path.
if (projectRootPath ?
project.projectRootPath === projectRootPath :
!project.projectRootPath || !this.compilerOptionsForInferredProjectsPerProjectRoot.has(project.projectRootPath)) {
project.setCompilerOptions(compilerOptions);
project.compileOnSaveEnabled = compilerOptions.compileOnSave;
updatedProjects.push(project);
}
}
this.updateProjectGraphs(updatedProjects);
}
stopWatchingDirectory(directory: string) {
@@ -602,10 +631,11 @@ namespace ts.server {
}
for (const openFile of this.openFiles) {
this.eventHandler(<ContextEvent>{
const event: ContextEvent = {
eventName: ContextEvent,
data: { project: openFile.getDefaultProject(), fileName: openFile.fileName }
});
};
this.eventHandler(event);
}
}
@@ -713,7 +743,7 @@ namespace ts.server {
}
}
private assignScriptInfoToInferredProjectIfNecessary(info: ScriptInfo, addToListOfOpenFiles: boolean): void {
private assignScriptInfoToInferredProjectIfNecessary(info: ScriptInfo, addToListOfOpenFiles: boolean, projectRootPath?: string): void {
const externalProject = this.findContainingExternalProject(info.fileName);
if (externalProject) {
// file is already included in some external project - do nothing
@@ -741,30 +771,30 @@ namespace ts.server {
}
if (info.containingProjects.length === 0) {
// create new inferred project p with the newly opened file as root
// or add root to existing inferred project if 'useOneInferredProject' is true
const inferredProject = this.createInferredProjectWithRootFileIfNecessary(info);
if (!this.useSingleInferredProject) {
// if useOneInferredProject is not set then try to fixup ownership of open files
// check 'defaultProject !== inferredProject' is necessary to handle cases
// when creation inferred project for some file has added other open files into this project (i.e. as referenced files)
// we definitely don't want to delete the project that was just created
// get (or create) an inferred project using the newly opened file as a root.
const inferredProject = this.createInferredProjectWithRootFileIfNecessary(info, projectRootPath);
if (!this.useSingleInferredProject && !inferredProject.projectRootPath) {
// if useSingleInferredProject is false and the inferred project is not associated
// with a project root, then try to repair the ownership of open files.
for (const f of this.openFiles) {
if (f.containingProjects.length === 0 || !inferredProject.containsScriptInfo(f)) {
// this is orphaned file that we have not processed yet - skip it
continue;
}
for (const fContainingProject of f.containingProjects) {
if (fContainingProject.projectKind === ProjectKind.Inferred &&
fContainingProject.isRoot(f) &&
fContainingProject !== inferredProject) {
for (const containingProject of f.containingProjects) {
// We verify 'containingProject !== inferredProject' to handle cases
// where the inferred project for some file has added other open files
// into this project (i.e. as referenced files) as we don't want to
// delete the project that was just created
if (containingProject.projectKind === ProjectKind.Inferred &&
containingProject !== inferredProject &&
containingProject.isRoot(f)) {
// open file used to be root in inferred project,
// this inferred project is different from the one we've just created for current file
// and new inferred project references this open file.
// We should delete old inferred project and attach open file to the new one
this.removeProject(fContainingProject);
this.removeProject(containingProject);
f.attachToProject(inferredProject);
}
}
@@ -1100,18 +1130,19 @@ namespace ts.server {
}
}
private reportConfigFileDiagnostics(configFileName: string, diagnostics: Diagnostic[], triggerFile: string) {
private reportConfigFileDiagnostics(configFileName: string, diagnostics: ReadonlyArray<Diagnostic>, triggerFile: string) {
if (!this.eventHandler) {
return;
}
this.eventHandler(<ConfigFileDiagEvent>{
const event: ConfigFileDiagEvent = {
eventName: ConfigFileDiagEvent,
data: { configFileName, diagnostics: diagnostics || [], triggerFile }
});
data: { configFileName, diagnostics: diagnostics || emptyArray, triggerFile }
};
this.eventHandler(event);
}
private createAndAddConfiguredProject(configFileName: NormalizedPath, projectOptions: ProjectOptions, configFileErrors: Diagnostic[], clientFileName?: string) {
private createAndAddConfiguredProject(configFileName: NormalizedPath, projectOptions: ProjectOptions, configFileErrors: ReadonlyArray<Diagnostic>, clientFileName?: string) {
const sizeLimitExceeded = this.exceededTotalSizeLimitForNonTsFiles(configFileName, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader);
const project = new ConfiguredProject(
configFileName,
@@ -1143,7 +1174,7 @@ namespace ts.server {
}
}
private addFilesToProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: Diagnostic[]): void {
private addFilesToProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: ReadonlyArray<Diagnostic>): void {
let errors: Diagnostic[];
for (const f of files) {
const rootFilename = propertyReader.getFileName(f);
@@ -1283,11 +1314,74 @@ namespace ts.server {
return configFileErrors;
}
createInferredProjectWithRootFileIfNecessary(root: ScriptInfo) {
const useExistingProject = this.useSingleInferredProject && this.inferredProjects.length;
const project = useExistingProject
? this.inferredProjects[0]
: new InferredProject(this, this.documentRegistry, this.compilerOptionsForInferredProjects);
private getOrCreateInferredProjectForProjectRootPathIfEnabled(root: ScriptInfo, projectRootPath: string | undefined): InferredProject | undefined {
if (!this.useInferredProjectPerProjectRoot) {
return undefined;
}
if (projectRootPath) {
// if we have an explicit project root path, find (or create) the matching inferred project.
for (const project of this.inferredProjects) {
if (project.projectRootPath === projectRootPath) {
return project;
}
}
return this.createInferredProject(/*isSingleInferredProject*/ false, projectRootPath);
}
// we don't have an explicit root path, so we should try to find an inferred project
// that more closely contains the file.
let bestMatch: InferredProject;
for (const project of this.inferredProjects) {
// ignore single inferred projects (handled elsewhere)
if (!project.projectRootPath) continue;
// ignore inferred projects that don't contain the root's path
if (!containsPath(project.projectRootPath, root.path, this.host.getCurrentDirectory(), !this.host.useCaseSensitiveFileNames)) continue;
// ignore inferred projects that are higher up in the project root.
// TODO(rbuckton): Should we add the file as a root to these as well?
if (bestMatch && bestMatch.projectRootPath.length > project.projectRootPath.length) continue;
bestMatch = project;
}
return bestMatch;
}
private getOrCreateSingleInferredProjectIfEnabled(): InferredProject | undefined {
if (!this.useSingleInferredProject) {
return undefined;
}
// If `useInferredProjectPerProjectRoot` is not enabled, then there will only be one
// inferred project for all files. If `useInferredProjectPerProjectRoot` is enabled
// then we want to put all files that are not opened with a `projectRootPath` into
// the same inferred project.
//
// To avoid the cost of searching through the array and to optimize for the case where
// `useInferredProjectPerProjectRoot` is not enabled, we will always put the inferred
// project for non-rooted files at the front of the array.
if (this.inferredProjects.length > 0 && this.inferredProjects[0].projectRootPath === undefined) {
return this.inferredProjects[0];
}
return this.createInferredProject(/*isSingleInferredProject*/ true);
}
private createInferredProject(isSingleInferredProject?: boolean, projectRootPath?: string): InferredProject {
const compilerOptions = projectRootPath && this.compilerOptionsForInferredProjectsPerProjectRoot.get(projectRootPath) || this.compilerOptionsForInferredProjects;
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath);
if (isSingleInferredProject) {
this.inferredProjects.unshift(project);
}
else {
this.inferredProjects.push(project);
}
return project;
}
createInferredProjectWithRootFileIfNecessary(root: ScriptInfo, projectRootPath?: string) {
const project = this.getOrCreateInferredProjectForProjectRootPathIfEnabled(root, projectRootPath) ||
this.getOrCreateSingleInferredProjectIfEnabled() ||
this.createInferredProject();
project.addRoot(root);
@@ -1298,9 +1392,6 @@ namespace ts.server {
project.updateGraph();
if (!useExistingProject) {
this.inferredProjects.push(project);
}
return project;
}
@@ -1456,7 +1547,7 @@ namespace ts.server {
openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult {
let configFileName: NormalizedPath;
let configFileErrors: Diagnostic[];
let configFileErrors: ReadonlyArray<Diagnostic>;
let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName);
if (!project) {
@@ -1474,7 +1565,7 @@ namespace ts.server {
// at this point if file is the part of some configured/external project then this project should be created
const info = this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent);
this.assignScriptInfoToInferredProjectIfNecessary(info, /*addToListOfOpenFiles*/ true);
this.assignScriptInfoToInferredProjectIfNecessary(info, /*addToListOfOpenFiles*/ true, projectRootPath);
// Delete the orphan files here because there might be orphan script infos (which are not part of project)
// when some file/s were closed which resulted in project removal.
// It was then postponed to cleanup these script infos so that they can be reused if
+7 -5
View File
@@ -54,7 +54,7 @@ namespace ts.server {
/* @internal */
export interface ProjectFilesWithTSDiagnostics extends protocol.ProjectFiles {
projectErrors: Diagnostic[];
projectErrors: ReadonlyArray<Diagnostic>;
}
export class UnresolvedImportsMap {
@@ -147,7 +147,7 @@ namespace ts.server {
private typingFiles: SortedReadonlyArray<string>;
protected projectErrors: Diagnostic[];
protected projectErrors: ReadonlyArray<Diagnostic>;
public typesVersion = 0;
@@ -837,6 +837,7 @@ namespace ts.server {
* the file and its imports/references are put into an InferredProject.
*/
export class InferredProject extends Project {
public readonly projectRootPath: string | undefined;
private static readonly newName = (() => {
let nextId = 1;
@@ -876,7 +877,7 @@ namespace ts.server {
// Used to keep track of what directories are watched for this project
directoriesWatchedForTsconfig: string[] = [];
constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions) {
constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions, projectRootPath?: string) {
super(InferredProject.newName(),
ProjectKind.Inferred,
projectService,
@@ -885,6 +886,7 @@ namespace ts.server {
/*languageServiceEnabled*/ true,
compilerOptions,
/*compileOnSaveEnabled*/ false);
this.projectRootPath = projectRootPath;
}
addRoot(info: ScriptInfo) {
@@ -1045,7 +1047,7 @@ namespace ts.server {
return getDirectoryPath(this.getConfigFilePath());
}
setProjectErrors(projectErrors: Diagnostic[]) {
setProjectErrors(projectErrors: ReadonlyArray<Diagnostic>) {
this.projectErrors = projectErrors;
}
@@ -1189,7 +1191,7 @@ namespace ts.server {
return this.typeAcquisition;
}
setProjectErrors(projectErrors: Diagnostic[]) {
setProjectErrors(projectErrors: ReadonlyArray<Diagnostic>) {
this.projectErrors = projectErrors;
}
+9 -1
View File
@@ -930,7 +930,7 @@ namespace ts.server.protocol {
/**
* An array of span groups (one per file) that refer to the item to be renamed.
*/
locs: SpanGroup[];
locs: ReadonlyArray<SpanGroup>;
}
/**
@@ -1320,6 +1320,13 @@ namespace ts.server.protocol {
* Compiler options to be used with inferred projects.
*/
options: ExternalProjectCompilerOptions;
/**
* Specifies the project root path used to scope compiler options.
* It is an error to provide this property if the server has not been started with
* `useInferredProjectPerProjectRoot` enabled.
*/
projectRootPath?: string;
}
/**
@@ -2445,6 +2452,7 @@ namespace ts.server.protocol {
paths?: MapLike<string[]>;
plugins?: PluginImport[];
preserveConstEnums?: boolean;
preserveSymlinks?: boolean;
project?: string;
reactNamespace?: string;
removeComments?: boolean;
+6 -9
View File
@@ -16,7 +16,7 @@ namespace ts.server {
public getVersion() {
return this.svc
? `SVC-${this.svcVersion}-${this.svc.getSnapshot().version}`
? `SVC-${this.svcVersion}-${this.svc.getSnapshotVersion()}`
: `Text-${this.textVersion}`;
}
@@ -62,22 +62,19 @@ namespace ts.server {
}
public getLineInfo(line: number): AbsolutePositionAndLineText {
return this.switchToScriptVersionCache().getSnapshot().index.lineNumberToInfo(line);
return this.switchToScriptVersionCache().getLineInfo(line);
}
/**
* @param line 0 based index
*/
lineToTextSpan(line: number) {
lineToTextSpan(line: number): TextSpan {
if (!this.svc) {
const lineMap = this.getLineMap();
const start = lineMap[line]; // -1 since line is 1-based
const end = line + 1 < lineMap.length ? lineMap[line + 1] : this.text.length;
return createTextSpanFromBounds(start, end);
}
const index = this.svc.getSnapshot().index;
const { lineText, absolutePosition } = index.lineNumberToInfo(line + 1);
const len = lineText !== undefined ? lineText.length : index.absolutePositionOfStartOfLine(line + 2) - absolutePosition;
return createTextSpan(absolutePosition, len);
return this.svc.lineToTextSpan(line);
}
/**
@@ -90,7 +87,7 @@ namespace ts.server {
}
// TODO: assert this offset is actually on the line
return this.svc.getSnapshot().index.absolutePositionOfStartOfLine(line) + (offset - 1);
return this.svc.lineOffsetToPosition(line, offset);
}
positionToLineOffset(position: number): protocol.Location {
@@ -98,7 +95,7 @@ namespace ts.server {
const { line, character } = computeLineAndCharacterOfPosition(this.getLineMap(), position);
return { line: line + 1, offset: character + 1 };
}
return this.svc.getSnapshot().index.positionToLineOffset(position);
return this.svc.positionToLineOffset(position);
}
private getFileText(tempFileName?: string) {
+55 -76
View File
@@ -5,7 +5,7 @@
namespace ts.server {
const lineCollectionCapacity = 4;
export interface LineCollection {
interface LineCollection {
charCount(): number;
lineCount(): number;
isLeaf(): this is LineLeaf;
@@ -17,7 +17,7 @@ namespace ts.server {
lineText: string | undefined;
}
export enum CharRangeSection {
const enum CharRangeSection {
PreStart,
Start,
Entire,
@@ -26,7 +26,7 @@ namespace ts.server {
PostEnd
}
export interface ILineIndexWalker {
interface ILineIndexWalker {
goSubtree: boolean;
done: boolean;
leaf(relativeStart: number, relativeLength: number, lineCollection: LineLeaf): void;
@@ -243,7 +243,7 @@ namespace ts.server {
}
// text change information
export class TextChange {
class TextChange {
constructor(public pos: number, public deleteLen: number, public insertedText?: string) {
}
@@ -285,17 +285,6 @@ namespace ts.server {
}
}
latest() {
return this.versions[this.currentVersionToIndex()];
}
latestVersion() {
if (this.changes.length > 0) {
this.getSnapshot();
}
return this.currentVersion;
}
// reload whole script, leaving no change history behind reload
reload(script: string) {
this.currentVersion++;
@@ -314,7 +303,9 @@ namespace ts.server {
this.minVersion = this.currentVersion;
}
getSnapshot() {
getSnapshot(): IScriptSnapshot { return this._getSnapshot(); }
private _getSnapshot(): LineIndexSnapshot {
let snap = this.versions[this.currentVersionToIndex()];
if (this.changes.length > 0) {
let snapIndex = snap.index;
@@ -334,6 +325,29 @@ namespace ts.server {
return snap;
}
getSnapshotVersion(): number {
return this._getSnapshot().version;
}
getLineInfo(line: number): AbsolutePositionAndLineText {
return this._getSnapshot().index.lineNumberToInfo(line);
}
lineOffsetToPosition(line: number, column: number): number {
return this._getSnapshot().index.absolutePositionOfStartOfLine(line) + (column - 1);
}
positionToLineOffset(position: number): protocol.Location {
return this._getSnapshot().index.positionToLineOffset(position);
}
lineToTextSpan(line: number): TextSpan {
const index = this._getSnapshot().index;
const { lineText, absolutePosition } = index.lineNumberToInfo(line + 1);
const len = lineText !== undefined ? lineText.length : index.absolutePositionOfStartOfLine(line + 2) - absolutePosition;
return createTextSpan(absolutePosition, len);
}
getTextChangesBetweenVersions(oldVersion: number, newVersion: number) {
if (oldVersion < newVersion) {
if (oldVersion >= this.minVersion) {
@@ -365,7 +379,7 @@ namespace ts.server {
}
}
export class LineIndexSnapshot implements IScriptSnapshot {
class LineIndexSnapshot implements IScriptSnapshot {
constructor(readonly version: number, readonly cache: ScriptVersionCache, readonly index: LineIndex, readonly changesSincePreviousVersion: ReadonlyArray<TextChange> = emptyArray) {
}
@@ -389,6 +403,7 @@ namespace ts.server {
}
}
/* @internal */
export class LineIndex {
root: LineNode;
// set this to true to check each edit for accuracy
@@ -561,7 +576,7 @@ namespace ts.server {
}
}
export class LineNode implements LineCollection {
class LineNode implements LineCollection {
totalChars = 0;
totalLines = 0;
@@ -660,45 +675,29 @@ namespace ts.server {
// Input position is relative to the start of this node.
// Output line number is absolute.
charOffsetToLineInfo(lineNumberAccumulator: number, relativePosition: number): { oneBasedLine: number, zeroBasedColumn: number, lineText: string | undefined } {
const childInfo = this.childFromCharOffset(lineNumberAccumulator, relativePosition);
if (!childInfo.child) {
return {
oneBasedLine: lineNumberAccumulator,
zeroBasedColumn: relativePosition,
lineText: undefined,
};
if (this.children.length === 0) {
// Root node might have no children if this is an empty document.
return { oneBasedLine: lineNumberAccumulator, zeroBasedColumn: relativePosition, lineText: undefined };
}
else if (childInfo.childIndex < this.children.length) {
if (childInfo.child.isLeaf()) {
return {
oneBasedLine: childInfo.lineNumberAccumulator,
zeroBasedColumn: childInfo.relativePosition,
lineText: childInfo.child.text,
};
for (const child of this.children) {
if (child.charCount() > relativePosition) {
if (child.isLeaf()) {
return { oneBasedLine: lineNumberAccumulator, zeroBasedColumn: relativePosition, lineText: child.text };
}
else {
return (<LineNode>child).charOffsetToLineInfo(lineNumberAccumulator, relativePosition);
}
}
else {
const lineNode = <LineNode>(childInfo.child);
return lineNode.charOffsetToLineInfo(childInfo.lineNumberAccumulator, childInfo.relativePosition);
relativePosition -= child.charCount();
lineNumberAccumulator += child.lineCount();
}
}
else {
const lineInfo = this.lineNumberToInfo(this.lineCount(), 0);
return { oneBasedLine: this.lineCount(), zeroBasedColumn: lineInfo.leaf.charCount(), lineText: undefined };
}
}
lineNumberToInfo(relativeOneBasedLine: number, positionAccumulator: number): { position: number, leaf: LineLeaf | undefined } {
const childInfo = this.childFromLineNumber(relativeOneBasedLine, positionAccumulator);
if (!childInfo.child) {
return { position: positionAccumulator, leaf: undefined };
}
else if (childInfo.child.isLeaf()) {
return { position: childInfo.positionAccumulator, leaf: childInfo.child };
}
else {
const lineNode = <LineNode>(childInfo.child);
return lineNode.lineNumberToInfo(childInfo.relativeOneBasedLine, childInfo.positionAccumulator);
}
// Skipped all children
const { leaf } = this.lineNumberToInfo(this.lineCount(), 0);
return { oneBasedLine: this.lineCount(), zeroBasedColumn: leaf.charCount(), lineText: undefined };
}
/**
@@ -706,39 +705,19 @@ namespace ts.server {
* Output line number is relative to the child.
* positionAccumulator will be an absolute position once relativeLineNumber reaches 0.
*/
private childFromLineNumber(relativeOneBasedLine: number, positionAccumulator: number): { child: LineCollection, relativeOneBasedLine: number, positionAccumulator: number } {
let child: LineCollection;
let i: number;
for (i = 0; i < this.children.length; i++) {
child = this.children[i];
lineNumberToInfo(relativeOneBasedLine: number, positionAccumulator: number): { position: number, leaf: LineLeaf | undefined } {
for (const child of this.children) {
const childLineCount = child.lineCount();
if (childLineCount >= relativeOneBasedLine) {
break;
return child.isLeaf() ? { position: positionAccumulator, leaf: child } : (<LineNode>child).lineNumberToInfo(relativeOneBasedLine, positionAccumulator);
}
else {
relativeOneBasedLine -= childLineCount;
positionAccumulator += child.charCount();
}
}
return { child, relativeOneBasedLine, positionAccumulator };
}
private childFromCharOffset(lineNumberAccumulator: number, relativePosition: number
): { child: LineCollection, childIndex: number, relativePosition: number, lineNumberAccumulator: number } {
let child: LineCollection;
let i: number;
let len: number;
for (i = 0, len = this.children.length; i < len; i++) {
child = this.children[i];
if (child.charCount() > relativePosition) {
break;
}
else {
relativePosition -= child.charCount();
lineNumberAccumulator += child.lineCount();
}
}
return { child, childIndex: i, relativePosition, lineNumberAccumulator };
return { position: positionAccumulator, leaf: undefined };
}
private splitAfter(childIndex: number) {
@@ -844,7 +823,7 @@ namespace ts.server {
}
}
export class LineLeaf implements LineCollection {
class LineLeaf implements LineCollection {
constructor(public text: string) {
}
+4
View File
@@ -9,6 +9,7 @@ namespace ts.server {
canUseEvents: boolean;
installerEventPort: number;
useSingleInferredProject: boolean;
useInferredProjectPerProjectRoot: boolean;
disableAutomaticTypingAcquisition: boolean;
globalTypingsCacheLocation: string;
logger: Logger;
@@ -414,6 +415,7 @@ namespace ts.server {
host,
cancellationToken,
useSingleInferredProject,
useInferredProjectPerProjectRoot,
typingsInstaller: typingsInstaller || nullTypingsInstaller,
byteLength: Buffer.byteLength,
hrtime: process.hrtime,
@@ -779,6 +781,7 @@ namespace ts.server {
const allowLocalPluginLoads = hasArgument("--allowLocalPluginLoads");
const useSingleInferredProject = hasArgument("--useSingleInferredProject");
const useInferredProjectPerProjectRoot = hasArgument("--useInferredProjectPerProjectRoot");
const disableAutomaticTypingAcquisition = hasArgument("--disableAutomaticTypingAcquisition");
const telemetryEnabled = hasArgument(Arguments.EnableTelemetry);
@@ -788,6 +791,7 @@ namespace ts.server {
installerEventPort: eventPort,
canUseEvents: eventPort === undefined,
useSingleInferredProject,
useInferredProjectPerProjectRoot,
disableAutomaticTypingAcquisition,
globalTypingsCacheLocation: getGlobalTypingsCacheLocation(),
typingSafeListLocation,
+39 -38
View File
@@ -247,6 +247,7 @@ namespace ts.server {
host: ServerHost;
cancellationToken: ServerCancellationToken;
useSingleInferredProject: boolean;
useInferredProjectPerProjectRoot: boolean;
typingsInstaller: ITypingsInstaller;
byteLength: (buf: string, encoding?: string) => number;
hrtime: (start?: number[]) => number[];
@@ -307,6 +308,7 @@ namespace ts.server {
logger: this.logger,
cancellationToken: this.cancellationToken,
useSingleInferredProject: opts.useSingleInferredProject,
useInferredProjectPerProjectRoot: opts.useInferredProjectPerProjectRoot,
typingsInstaller: this.typingsInstaller,
throttleWaitMilliseconds,
eventHandler: this.eventHandler,
@@ -379,7 +381,7 @@ namespace ts.server {
this.host.write(formatMessage(msg, this.logger, this.byteLength, this.host.newLine));
}
public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: Diagnostic[]) {
public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: ReadonlyArray<Diagnostic>) {
const bakedDiags = map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true));
const ev: protocol.ConfigFileDiagnosticEvent = {
seq: 0,
@@ -539,8 +541,8 @@ namespace ts.server {
);
}
private convertToDiagnosticsWithLinePositionFromDiagnosticFile(diagnostics: Diagnostic[]) {
return diagnostics.map(d => <protocol.DiagnosticWithLinePosition>{
private convertToDiagnosticsWithLinePositionFromDiagnosticFile(diagnostics: ReadonlyArray<Diagnostic>): protocol.DiagnosticWithLinePosition[] {
return diagnostics.map<protocol.DiagnosticWithLinePosition>(d => ({
message: flattenDiagnosticMessageText(d.messageText, this.host.newLine),
start: d.start,
length: d.length,
@@ -548,7 +550,7 @@ namespace ts.server {
code: d.code,
startLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start)),
endLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start + d.length))
});
}));
}
private getCompilerOptionsDiagnostics(args: protocol.CompilerOptionsDiagnosticsRequestArgs) {
@@ -565,7 +567,7 @@ namespace ts.server {
);
}
private convertToDiagnosticsWithLinePosition(diagnostics: Diagnostic[], scriptInfo: ScriptInfo) {
private convertToDiagnosticsWithLinePosition(diagnostics: ReadonlyArray<Diagnostic>, scriptInfo: ScriptInfo): protocol.DiagnosticWithLinePosition[] {
return diagnostics.map(d => <protocol.DiagnosticWithLinePosition>{
message: flattenDiagnosticMessageText(d.messageText, this.host.newLine),
start: d.start,
@@ -578,10 +580,12 @@ namespace ts.server {
});
}
private getDiagnosticsWorker(args: protocol.FileRequestArgs, isSemantic: boolean, selector: (project: Project, file: string) => Diagnostic[], includeLinePosition: boolean) {
private getDiagnosticsWorker(
args: protocol.FileRequestArgs, isSemantic: boolean, selector: (project: Project, file: string) => ReadonlyArray<Diagnostic>, includeLinePosition: boolean
): ReadonlyArray<protocol.DiagnosticWithLinePosition> | ReadonlyArray<protocol.Diagnostic> {
const { project, file } = this.getFileAndProject(args);
if (isSemantic && isDeclarationFileInJSOnlyNonConfiguredProject(project, file)) {
return [];
return emptyArray;
}
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const diagnostics = selector(project, file);
@@ -590,14 +594,14 @@ namespace ts.server {
: diagnostics.map(d => formatDiag(file, project, d));
}
private getDefinition(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.FileSpan[] | DefinitionInfo[] {
private getDefinition(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.FileSpan> | ReadonlyArray<DefinitionInfo> {
const { file, project } = this.getFileAndProject(args);
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, scriptInfo);
const definitions = project.getLanguageService().getDefinitionAtPosition(file, position);
if (!definitions) {
return undefined;
return emptyArray;
}
if (simplifiedResult) {
@@ -615,14 +619,14 @@ namespace ts.server {
}
}
private getTypeDefinition(args: protocol.FileLocationRequestArgs): protocol.FileSpan[] {
private getTypeDefinition(args: protocol.FileLocationRequestArgs): ReadonlyArray<protocol.FileSpan> {
const { file, project } = this.getFileAndProject(args);
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, scriptInfo);
const definitions = project.getLanguageService().getTypeDefinitionAtPosition(file, position);
if (!definitions) {
return undefined;
return emptyArray;
}
return definitions.map(def => {
@@ -635,12 +639,12 @@ namespace ts.server {
});
}
private getImplementation(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.FileSpan[] | ImplementationLocation[] {
private getImplementation(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.FileSpan> | ReadonlyArray<ImplementationLocation> {
const { file, project } = this.getFileAndProject(args);
const position = this.getPosition(args, project.getScriptInfoForNormalizedPath(file));
const implementations = project.getLanguageService().getImplementationAtPosition(file, position);
if (!implementations) {
return [];
return emptyArray;
}
if (simplifiedResult) {
return implementations.map(({ fileName, textSpan }) => {
@@ -657,7 +661,7 @@ namespace ts.server {
}
}
private getOccurrences(args: protocol.FileLocationRequestArgs): protocol.OccurrencesResponseItem[] {
private getOccurrences(args: protocol.FileLocationRequestArgs): ReadonlyArray<protocol.OccurrencesResponseItem> {
const { file, project } = this.getFileAndProject(args);
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, scriptInfo);
@@ -665,7 +669,7 @@ namespace ts.server {
const occurrences = project.getLanguageService().getOccurrencesAtPosition(file, position);
if (!occurrences) {
return undefined;
return emptyArray;
}
return occurrences.map(occurrence => {
@@ -687,17 +691,17 @@ namespace ts.server {
});
}
private getSyntacticDiagnosticsSync(args: protocol.SyntacticDiagnosticsSyncRequestArgs): protocol.Diagnostic[] | protocol.DiagnosticWithLinePosition[] {
private getSyntacticDiagnosticsSync(args: protocol.SyntacticDiagnosticsSyncRequestArgs): ReadonlyArray<protocol.Diagnostic> | ReadonlyArray<protocol.DiagnosticWithLinePosition> {
const { configFile } = this.getConfigFileAndProject(args);
if (configFile) {
// all the config file errors are reported as part of semantic check so nothing to report here
return [];
return emptyArray;
}
return this.getDiagnosticsWorker(args, /*isSemantic*/ false, (project, file) => project.getLanguageService().getSyntacticDiagnostics(file), args.includeLinePosition);
}
private getSemanticDiagnosticsSync(args: protocol.SemanticDiagnosticsSyncRequestArgs): protocol.Diagnostic[] | protocol.DiagnosticWithLinePosition[] {
private getSemanticDiagnosticsSync(args: protocol.SemanticDiagnosticsSyncRequestArgs): ReadonlyArray<protocol.Diagnostic> | ReadonlyArray<protocol.DiagnosticWithLinePosition> {
const { configFile, project } = this.getConfigFileAndProject(args);
if (configFile) {
return this.getConfigFileDiagnostics(configFile, project, args.includeLinePosition);
@@ -705,14 +709,14 @@ namespace ts.server {
return this.getDiagnosticsWorker(args, /*isSemantic*/ true, (project, file) => project.getLanguageService().getSemanticDiagnostics(file), args.includeLinePosition);
}
private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): protocol.DocumentHighlightsItem[] | DocumentHighlights[] {
private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.DocumentHighlightsItem> | ReadonlyArray<DocumentHighlights> {
const { file, project } = this.getFileAndProject(args);
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, scriptInfo);
const documentHighlights = project.getLanguageService().getDocumentHighlights(file, position, args.filesToSearch);
if (!documentHighlights) {
return undefined;
return emptyArray;
}
if (simplifiedResult) {
@@ -741,7 +745,7 @@ namespace ts.server {
}
private setCompilerOptionsForInferredProjects(args: protocol.SetCompilerOptionsForInferredProjectsArgs): void {
this.projectService.setCompilerOptionsForInferredProjects(args.options);
this.projectService.setCompilerOptionsForInferredProjects(args.options, args.projectRootPath);
}
private getProjectInfo(args: protocol.ProjectInfoRequestArgs): protocol.ProjectInfo {
@@ -796,7 +800,7 @@ namespace ts.server {
return info.getDefaultProject();
}
private getRenameLocations(args: protocol.RenameRequestArgs, simplifiedResult: boolean): protocol.RenameResponseBody | RenameLocation[] {
private getRenameLocations(args: protocol.RenameRequestArgs, simplifiedResult: boolean): protocol.RenameResponseBody | ReadonlyArray<RenameLocation> {
const file = toNormalizedPath(args.file);
const info = this.projectService.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, info);
@@ -813,7 +817,7 @@ namespace ts.server {
if (!renameInfo.canRename) {
return {
info: renameInfo,
locs: []
locs: emptyArray
};
}
@@ -822,12 +826,12 @@ namespace ts.server {
(project: Project) => {
const renameLocations = project.getLanguageService().findRenameLocations(file, position, args.findInStrings, args.findInComments);
if (!renameLocations) {
return [];
return emptyArray;
}
return renameLocations.map(location => {
const locationScriptInfo = project.getScriptInfo(location.fileName);
return <protocol.FileSpan>{
return {
file: location.fileName,
start: locationScriptInfo.positionToLineOffset(location.textSpan.start),
end: locationScriptInfo.positionToLineOffset(textSpanEnd(location.textSpan)),
@@ -899,7 +903,7 @@ namespace ts.server {
}
}
private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | ReferencedSymbol[] {
private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | undefined | ReadonlyArray<ReferencedSymbol> {
const file = toNormalizedPath(args.file);
const projects = this.getProjects(args);
@@ -921,7 +925,7 @@ namespace ts.server {
(project: Project) => {
const references = project.getLanguageService().getReferencesAtPosition(file, position);
if (!references) {
return [];
return emptyArray;
}
return references.map(ref => {
@@ -978,7 +982,7 @@ namespace ts.server {
if (this.eventHandler) {
this.eventHandler({
eventName: "configFileDiag",
data: { triggerFile: fileName, configFileName, diagnostics: configFileErrors || [] }
data: { triggerFile: fileName, configFileName, diagnostics: configFileErrors || emptyArray }
});
}
}
@@ -1171,7 +1175,7 @@ namespace ts.server {
});
}
private getCompletions(args: protocol.CompletionsRequestArgs, simplifiedResult: boolean): protocol.CompletionEntry[] | CompletionInfo {
private getCompletions(args: protocol.CompletionsRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.CompletionEntry> | CompletionInfo | undefined {
const prefix = args.prefix || "";
const { file, project } = this.getFileAndProject(args);
@@ -1179,11 +1183,8 @@ namespace ts.server {
const position = this.getPosition(args, scriptInfo);
const completions = project.getLanguageService().getCompletionsAtPosition(file, position);
if (!completions) {
return undefined;
}
if (simplifiedResult) {
return mapDefined(completions.entries, entry => {
return mapDefined(completions && completions.entries, entry => {
if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) {
const { name, kind, kindModifiers, sortText, replacementSpan } = entry;
const convertedSpan = replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined;
@@ -1196,7 +1197,7 @@ namespace ts.server {
}
}
private getCompletionEntryDetails(args: protocol.CompletionDetailsRequestArgs): protocol.CompletionEntryDetails[] {
private getCompletionEntryDetails(args: protocol.CompletionDetailsRequestArgs): ReadonlyArray<protocol.CompletionEntryDetails> {
const { file, project } = this.getFileAndProject(args);
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, scriptInfo);
@@ -1205,12 +1206,12 @@ namespace ts.server {
project.getLanguageService().getCompletionEntryDetails(file, position, entryName));
}
private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): protocol.CompileOnSaveAffectedFileListSingleProject[] {
private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): ReadonlyArray<protocol.CompileOnSaveAffectedFileListSingleProject> {
const info = this.projectService.getScriptInfo(args.file);
const result: protocol.CompileOnSaveAffectedFileListSingleProject[] = [];
if (!info) {
return [];
return emptyArray;
}
// if specified a project, we only return affected file list in this project
@@ -1368,7 +1369,7 @@ namespace ts.server {
: tree;
}
private getNavigateToItems(args: protocol.NavtoRequestArgs, simplifiedResult: boolean): protocol.NavtoItem[] | NavigateToItem[] {
private getNavigateToItems(args: protocol.NavtoRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.NavtoItem> | ReadonlyArray<NavigateToItem> {
const projects = this.getProjects(args);
const fileName = args.currentFileOnly ? args.file && normalizeSlashes(args.file) : undefined;
@@ -1378,7 +1379,7 @@ namespace ts.server {
project => {
const navItems = project.getLanguageService().getNavigateToItems(args.searchValue, args.maxResultCount, fileName, /*excludeDts*/ project.isNonTsProject());
if (!navItems) {
return [];
return emptyArray;
}
return navItems.map((navItem) => {
@@ -356,14 +356,15 @@ namespace ts.server.typingsInstaller {
this.sendResponse(this.createSetTypings(req, currentlyCachedTypings.concat(installedTypingFiles)));
}
finally {
this.sendResponse(<EndInstallTypes>{
const response: EndInstallTypes = {
kind: EventEndInstallTypes,
eventId: requestId,
projectName: req.projectName,
packagesToInstall: scopedTypings,
installSuccess: ok,
typingsInstallerVersion: ts.version // qualified explicitly to prevent occasional shadowing
});
};
this.sendResponse(response);
}
});
}
+45 -3
View File
@@ -147,7 +147,7 @@ namespace ts.codefix {
}
else if (isJsxOpeningLikeElement(token.parent) && token.parent.tagName === token) {
// The error wasn't for the symbolAtLocation, it was for the JSX tag itself, which needs access to e.g. `React`.
symbol = checker.getAliasedSymbol(checker.resolveNameAtLocation(token, checker.getJsxNamespace(), SymbolFlags.Value));
symbol = checker.getAliasedSymbol(checker.resolveName(checker.getJsxNamespace(), token.parent.tagName, SymbolFlags.Value));
symbolName = symbol.name;
}
else {
@@ -394,9 +394,11 @@ namespace ts.codefix {
: isNamespaceImport
? createImportClause(/*name*/ undefined, createNamespaceImport(createIdentifier(symbolName)))
: createImportClause(/*name*/ undefined, createNamedImports([createImportSpecifier(/*propertyName*/ undefined, createIdentifier(symbolName))]));
const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, importClause, createLiteral(moduleSpecifierWithoutQuotes));
const moduleSpecifierLiteral = createLiteral(moduleSpecifierWithoutQuotes);
moduleSpecifierLiteral.singleQuote = getSingleQuoteStyleFromExistingImports();
const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, importClause, moduleSpecifierLiteral);
if (!lastImportDeclaration) {
changeTracker.insertNodeAt(sourceFile, sourceFile.getStart(), importDecl, { suffix: `${context.newLineCharacter}${context.newLineCharacter}` });
changeTracker.insertNodeAt(sourceFile, getSourceFileImportLocation(sourceFile), importDecl, { suffix: `${context.newLineCharacter}${context.newLineCharacter}` });
}
else {
changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl, { suffix: context.newLineCharacter });
@@ -413,6 +415,46 @@ namespace ts.codefix {
moduleSpecifierWithoutQuotes
);
function getSourceFileImportLocation(node: SourceFile) {
// For a source file, it is possible there are detached comments we should not skip
const text = node.text;
let ranges = getLeadingCommentRanges(text, 0);
if (!ranges) return 0;
let position = 0;
// However we should still skip a pinned comment at the top
if (ranges.length && ranges[0].kind === SyntaxKind.MultiLineCommentTrivia && isPinnedComment(text, ranges[0])) {
position = ranges[0].end + 1;
ranges = ranges.slice(1);
}
// As well as any triple slash references
for (const range of ranges) {
if (range.kind === SyntaxKind.SingleLineCommentTrivia && isRecognizedTripleSlashComment(node.text, range.pos, range.end)) {
position = range.end + 1;
continue;
}
break;
}
return position;
}
function getSingleQuoteStyleFromExistingImports() {
const firstModuleSpecifier = forEach(sourceFile.statements, node => {
if (isImportDeclaration(node) || isExportDeclaration(node)) {
if (node.moduleSpecifier && isStringLiteral(node.moduleSpecifier)) {
return node.moduleSpecifier;
}
}
else if (isImportEqualsDeclaration(node)) {
if (isExternalModuleReference(node.moduleReference) && isStringLiteral(node.moduleReference.expression)) {
return node.moduleReference.expression;
}
}
});
if (firstModuleSpecifier) {
return sourceFile.text.charCodeAt(firstModuleSpecifier.getStart()) === CharacterCodes.singleQuote;
}
}
function getModuleSpecifierForNewImport() {
const fileName = sourceFile.fileName;
const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().fileName;
+10 -5
View File
@@ -652,10 +652,15 @@ namespace ts.FindAllReferences.Core {
return undefined;
}
// If the symbol has a parent, it's globally visible.
// Unless that parent is an external module, then we should only search in the module (and recurse on the export later).
// But if the parent is a module that has `export as namespace`, then the symbol *is* globally visible.
if (parent && !((parent.flags & SymbolFlags.Module) && isExternalModuleSymbol(parent) && !parent.globalExports)) {
/*
If the symbol has a parent, it's globally visible unless:
- It's a private property (handled above).
- It's a type parameter.
- The parent is an external module: then we should only search in the module (and recurse on the export later).
- But if the parent has `export as namespace`, the symbol is globally visible through that namespace.
*/
const exposedByParent = parent && !(symbol.flags & SymbolFlags.TypeParameter);
if (exposedByParent && !((parent.flags & SymbolFlags.Module) && isExternalModuleSymbol(parent) && !parent.globalExports)) {
return undefined;
}
@@ -682,7 +687,7 @@ namespace ts.FindAllReferences.Core {
// declare module "a" { export type T = number; }
// declare module "b" { import { T } from "a"; export const x: T; }
// So we must search the whole source file. (Because we will mark the source file as seen, we we won't return to it when searching for imports.)
return parent ? scope.getSourceFile() : scope;
return exposedByParent ? scope.getSourceFile() : scope;
}
function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): number[] {
+22
View File
@@ -76,6 +76,28 @@ namespace ts.GoToDefinition {
declaration => createDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName));
}
// If the node is the name of a BindingElement within an ObjectBindingPattern instead of just returning the
// declaration the symbol (which is itself), we should try to get to the original type of the ObjectBindingPattern
// and return the property declaration for the referenced property.
// For example:
// import('./foo').then(({ b/*goto*/ar }) => undefined); => should get use to the declaration in file "./foo"
//
// function bar<T>(onfulfilled: (value: T) => void) { //....}
// interface Test {
// pr/*destination*/op1: number
// }
// bar<Test>(({pr/*goto*/op1})=>{});
if (isPropertyName(node) && isBindingElement(node.parent) && isObjectBindingPattern(node.parent.parent) &&
(node === (node.parent.propertyName || node.parent.name))) {
const type = typeChecker.getTypeAtLocation(node.parent.parent);
if (type) {
const propSymbols = getPropertySymbolsFromType(type, node);
if (propSymbols) {
return flatMap(propSymbols, propSymbol => getDefinitionFromSymbol(typeChecker, propSymbol, node));
}
}
}
// If the current location we want to find its definition is in an object literal, try to get the contextual type for the
// object literal, lookup the property symbol in the contextual type, and use this for goto-definition.
// For example
@@ -1,6 +1,6 @@
/* @internal */
namespace ts.refactor {
namespace ts.refactor.convertFunctionToES6Class {
const actionName = "convert";
const convertFunctionToES6Class: Refactor = {
File diff suppressed because it is too large Load Diff
+1
View File
@@ -1 +1,2 @@
/// <reference path="convertFunctionToEs6Class.ts" />
/// <reference path="extractMethod.ts" />
+10 -5
View File
@@ -2150,12 +2150,17 @@ namespace ts {
export function getPropertySymbolsFromContextualType(typeChecker: TypeChecker, node: ObjectLiteralElement): Symbol[] {
const objectLiteral = <ObjectLiteralExpression | JsxAttributes>node.parent;
const contextualType = typeChecker.getContextualType(objectLiteral);
const name = unescapeLeadingUnderscores(getTextOfPropertyName(node.name));
if (name && contextualType) {
return getPropertySymbolsFromType(contextualType, node.name);
}
/* @internal */
export function getPropertySymbolsFromType(type: Type, propName: PropertyName) {
const name = unescapeLeadingUnderscores(getTextOfPropertyName(propName));
if (name && type) {
const result: Symbol[] = [];
const symbol = contextualType.getProperty(name);
if (contextualType.flags & TypeFlags.Union) {
forEach((<UnionType>contextualType).types, t => {
const symbol = type.getProperty(name);
if (type.flags & TypeFlags.Union) {
forEach((<UnionType>type).types, t => {
const symbol = t.getProperty(name);
if (symbol) {
result.push(symbol);
+1 -1
View File
@@ -203,7 +203,7 @@ namespace ts.SymbolDisplay {
// get the signature from the declaration and write it
const functionDeclaration = <FunctionLike>location.parent;
// Use function declaration to write the signatures only if the symbol corresponding to this declaration
const locationIsSymbolDeclaration = findDeclaration(symbol, declaration =>
const locationIsSymbolDeclaration = find(symbol.declarations, declaration =>
declaration === (location.kind === SyntaxKind.ConstructorKeyword ? functionDeclaration.parent : functionDeclaration));
if (locationIsSymbolDeclaration) {
+129 -43
View File
@@ -83,16 +83,43 @@ namespace ts.textChanges {
delta?: number;
}
export type ChangeNodeOptions = ConfigurableStartEnd & InsertNodeOptions;
enum ChangeKind {
Remove,
ReplaceWithSingleNode,
ReplaceWithMultipleNodes
}
interface Change {
type Change = ReplaceWithSingleNode | ReplaceWithMultipleNodes | RemoveNode;
interface BaseChange {
readonly sourceFile: SourceFile;
readonly range: TextRange;
}
export interface ChangeNodeOptions extends ConfigurableStartEnd, InsertNodeOptions {
readonly useIndentationFromFile?: boolean;
readonly node?: Node;
}
interface ReplaceWithSingleNode extends BaseChange {
readonly kind: ChangeKind.ReplaceWithSingleNode;
readonly node: Node;
readonly options?: ChangeNodeOptions;
}
interface RemoveNode extends BaseChange {
readonly kind: ChangeKind.Remove;
readonly node?: never;
readonly options?: never;
}
export interface ChangeMultipleNodesOptions extends ChangeNodeOptions {
nodeSeparator: string;
}
interface ReplaceWithMultipleNodes extends BaseChange {
readonly kind: ChangeKind.ReplaceWithMultipleNodes;
readonly nodes: ReadonlyArray<Node>;
readonly options?: ChangeMultipleNodesOptions;
}
export function getSeparatorCharacter(separator: Token<SyntaxKind.CommaToken | SyntaxKind.SemicolonToken>) {
return tokenToString(separator.kind);
}
@@ -126,13 +153,11 @@ namespace ts.textChanges {
}
export function getAdjustedEndPosition(sourceFile: SourceFile, node: Node, options: ConfigurableEnd) {
if (options.useNonAdjustedEndPosition) {
if (options.useNonAdjustedEndPosition || isExpression(node)) {
return node.getEnd();
}
const end = node.getEnd();
const newEnd = skipTrivia(sourceFile.text, end, /*stopAfterLineBreak*/ true);
// check if last character before newPos is linebreak
// if yes - considered all skipped trivia to be trailing trivia of the node
return newEnd !== end && isLineBreak(sourceFile.text.charCodeAt(newEnd - 1))
? newEnd
: end;
@@ -153,12 +178,16 @@ namespace ts.textChanges {
return s;
}
function getNewlineKind(context: { newLineCharacter: string }) {
return context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed;
}
export class ChangeTracker {
private changes: Change[] = [];
private readonly newLineCharacter: string;
public static fromCodeFixContext(context: { newLineCharacter: string, rulesProvider: formatting.RulesProvider }) {
return new ChangeTracker(context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.rulesProvider);
public static fromCodeFixContext(context: { newLineCharacter: string, rulesProvider?: formatting.RulesProvider }) {
return new ChangeTracker(getNewlineKind(context), context.rulesProvider);
}
constructor(
@@ -168,22 +197,22 @@ namespace ts.textChanges {
this.newLineCharacter = getNewLineCharacter({ newLine });
}
public deleteNode(sourceFile: SourceFile, node: Node, options: ConfigurableStartEnd = {}) {
const startPosition = getAdjustedStartPosition(sourceFile, node, options, Position.FullStart);
const endPosition = getAdjustedEndPosition(sourceFile, node, options);
this.changes.push({ sourceFile, options, range: { pos: startPosition, end: endPosition } });
public deleteRange(sourceFile: SourceFile, range: TextRange) {
this.changes.push({ kind: ChangeKind.Remove, sourceFile, range });
return this;
}
public deleteRange(sourceFile: SourceFile, range: TextRange) {
this.changes.push({ sourceFile, range });
public deleteNode(sourceFile: SourceFile, node: Node, options: ConfigurableStartEnd = {}) {
const startPosition = getAdjustedStartPosition(sourceFile, node, options, Position.FullStart);
const endPosition = getAdjustedEndPosition(sourceFile, node, options);
this.changes.push({ kind: ChangeKind.Remove, sourceFile, range: { pos: startPosition, end: endPosition } });
return this;
}
public deleteNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd = {}) {
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.FullStart);
const endPosition = getAdjustedEndPosition(sourceFile, endNode, options);
this.changes.push({ sourceFile, options, range: { pos: startPosition, end: endPosition } });
this.changes.push({ kind: ChangeKind.Remove, sourceFile, range: { pos: startPosition, end: endPosition } });
return this;
}
@@ -223,33 +252,74 @@ namespace ts.textChanges {
}
public replaceRange(sourceFile: SourceFile, range: TextRange, newNode: Node, options: InsertNodeOptions = {}) {
this.changes.push({ sourceFile, range, options, node: newNode });
this.changes.push({ kind: ChangeKind.ReplaceWithSingleNode, sourceFile, range, options, node: newNode });
return this;
}
public replaceNode(sourceFile: SourceFile, oldNode: Node, newNode: Node, options: ChangeNodeOptions = {}) {
const startPosition = getAdjustedStartPosition(sourceFile, oldNode, options, Position.Start);
const endPosition = getAdjustedEndPosition(sourceFile, oldNode, options);
this.changes.push({ sourceFile, options, useIndentationFromFile: true, node: newNode, range: { pos: startPosition, end: endPosition } });
return this;
return this.replaceWithSingle(sourceFile, startPosition, endPosition, newNode, options);
}
public replaceNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, newNode: Node, options: ChangeNodeOptions = {}) {
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.Start);
const endPosition = getAdjustedEndPosition(sourceFile, endNode, options);
this.changes.push({ sourceFile, options, useIndentationFromFile: true, node: newNode, range: { pos: startPosition, end: endPosition } });
return this.replaceWithSingle(sourceFile, startPosition, endPosition, newNode, options);
}
private replaceWithSingle(sourceFile: SourceFile, startPosition: number, endPosition: number, newNode: Node, options: ChangeNodeOptions): this {
this.changes.push({
kind: ChangeKind.ReplaceWithSingleNode,
sourceFile,
options,
node: newNode,
range: { pos: startPosition, end: endPosition }
});
return this;
}
private replaceWithMultiple(sourceFile: SourceFile, startPosition: number, endPosition: number, newNodes: ReadonlyArray<Node>, options: ChangeMultipleNodesOptions): this {
this.changes.push({
kind: ChangeKind.ReplaceWithMultipleNodes,
sourceFile,
options,
nodes: newNodes,
range: { pos: startPosition, end: endPosition }
});
return this;
}
public replaceNodeWithNodes(sourceFile: SourceFile, oldNode: Node, newNodes: ReadonlyArray<Node>, options: ChangeMultipleNodesOptions) {
const startPosition = getAdjustedStartPosition(sourceFile, oldNode, options, Position.Start);
const endPosition = getAdjustedEndPosition(sourceFile, oldNode, options);
return this.replaceWithMultiple(sourceFile, startPosition, endPosition, newNodes, options);
}
public replaceNodesWithNodes(sourceFile: SourceFile, oldNodes: ReadonlyArray<Node>, newNodes: ReadonlyArray<Node>, options: ChangeMultipleNodesOptions) {
const startPosition = getAdjustedStartPosition(sourceFile, oldNodes[0], options, Position.Start);
const endPosition = getAdjustedEndPosition(sourceFile, lastOrUndefined(oldNodes), options);
return this.replaceWithMultiple(sourceFile, startPosition, endPosition, newNodes, options);
}
public replaceRangeWithNodes(sourceFile: SourceFile, range: TextRange, newNodes: ReadonlyArray<Node>, options: ChangeMultipleNodesOptions) {
return this.replaceWithMultiple(sourceFile, range.pos, range.end, newNodes, options);
}
public replaceNodeRangeWithNodes(sourceFile: SourceFile, startNode: Node, endNode: Node, newNodes: ReadonlyArray<Node>, options: ChangeMultipleNodesOptions) {
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.Start);
const endPosition = getAdjustedEndPosition(sourceFile, endNode, options);
return this.replaceWithMultiple(sourceFile, startPosition, endPosition, newNodes, options);
}
public insertNodeAt(sourceFile: SourceFile, pos: number, newNode: Node, options: InsertNodeOptions = {}) {
this.changes.push({ sourceFile, options, node: newNode, range: { pos, end: pos } });
this.changes.push({ kind: ChangeKind.ReplaceWithSingleNode, sourceFile, options, node: newNode, range: { pos, end: pos } });
return this;
}
public insertNodeBefore(sourceFile: SourceFile, before: Node, newNode: Node, options: InsertNodeOptions & ConfigurableStart = {}) {
const startPosition = getAdjustedStartPosition(sourceFile, before, options, Position.Start);
this.changes.push({ sourceFile, options, useIndentationFromFile: true, node: newNode, range: { pos: startPosition, end: startPosition } });
return this;
return this.replaceWithSingle(sourceFile, startPosition, startPosition, newNode, options);
}
public insertNodeAfter(sourceFile: SourceFile, after: Node, newNode: Node, options: InsertNodeOptions & ConfigurableEnd = {}) {
@@ -261,6 +331,7 @@ namespace ts.textChanges {
// if not - insert semicolon to preserve the code from changing the meaning due to ASI
if (sourceFile.text.charCodeAt(after.end - 1) !== CharacterCodes.semicolon) {
this.changes.push({
kind: ChangeKind.ReplaceWithSingleNode,
sourceFile,
options: {},
range: { pos: after.end, end: after.end },
@@ -269,8 +340,7 @@ namespace ts.textChanges {
}
}
const endPosition = getAdjustedEndPosition(sourceFile, after, options);
this.changes.push({ sourceFile, options, useIndentationFromFile: true, node: newNode, range: { pos: endPosition, end: endPosition } });
return this;
return this.replaceWithSingle(sourceFile, endPosition, endPosition, newNode, options);
}
/**
@@ -339,10 +409,10 @@ namespace ts.textChanges {
}
this.changes.push({
kind: ChangeKind.ReplaceWithSingleNode,
sourceFile,
range: { pos: startPos, end: containingList[index + 1].getStart(sourceFile) },
node: newNode,
useIndentationFromFile: true,
options: {
prefix,
// write separator and leading trivia of the next element as suffix
@@ -383,6 +453,7 @@ namespace ts.textChanges {
if (multilineList) {
// insert separator immediately following the 'after' node to preserve comments in trailing trivia
this.changes.push({
kind: ChangeKind.ReplaceWithSingleNode,
sourceFile,
range: { pos: end, end },
node: createToken(separator),
@@ -396,6 +467,7 @@ namespace ts.textChanges {
insertPos--;
}
this.changes.push({
kind: ChangeKind.ReplaceWithSingleNode,
sourceFile,
range: { pos: insertPos, end: insertPos },
node: newNode,
@@ -404,6 +476,7 @@ namespace ts.textChanges {
}
else {
this.changes.push({
kind: ChangeKind.ReplaceWithSingleNode,
sourceFile,
range: { pos: end, end },
node: newNode,
@@ -446,38 +519,51 @@ namespace ts.textChanges {
}
private computeNewText(change: Change, sourceFile: SourceFile): string {
if (!change.node) {
if (change.kind === ChangeKind.Remove) {
// deletion case
return "";
}
const options = change.options || {};
const nonFormattedText = getNonformattedText(change.node, sourceFile, this.newLine);
let text: string;
const pos = change.range.pos;
const posStartsLine = getLineStartPositionForPosition(pos, sourceFile) === pos;
if (change.kind === ChangeKind.ReplaceWithMultipleNodes) {
const parts = change.nodes.map(n => this.getFormattedTextOfNode(n, sourceFile, pos, options));
text = parts.join(change.options.nodeSeparator);
}
else {
Debug.assert(change.kind === ChangeKind.ReplaceWithSingleNode, "change.kind === ReplaceWithSingleNode");
text = this.getFormattedTextOfNode(change.node, sourceFile, pos, options);
}
// strip initial indentation (spaces or tabs) if text will be inserted in the middle of the line
text = (posStartsLine || options.indentation !== undefined) ? text : text.replace(/^\s+/, "");
return (options.prefix || "") + text + (options.suffix || "");
}
private getFormattedTextOfNode(node: Node, sourceFile: SourceFile, pos: number, options: ChangeNodeOptions): string {
const nonformattedText = getNonformattedText(node, sourceFile, this.newLine);
if (this.validator) {
this.validator(nonFormattedText);
this.validator(nonformattedText);
}
const formatOptions = this.rulesProvider.getFormatOptions();
const pos = change.range.pos;
const posStartsLine = getLineStartPositionForPosition(pos, sourceFile) === pos;
const initialIndentation =
change.options.indentation !== undefined
? change.options.indentation
: change.useIndentationFromFile
? formatting.SmartIndenter.getIndentation(change.range.pos, sourceFile, formatOptions, posStartsLine || (change.options.prefix === this.newLineCharacter))
options.indentation !== undefined
? options.indentation
: (options.useIndentationFromFile !== false)
? formatting.SmartIndenter.getIndentation(pos, sourceFile, formatOptions, posStartsLine || (options.prefix === this.newLineCharacter))
: 0;
const delta =
change.options.delta !== undefined
? change.options.delta
: formatting.SmartIndenter.shouldIndentChildNode(change.node)
? formatOptions.indentSize
options.delta !== undefined
? options.delta
: formatting.SmartIndenter.shouldIndentChildNode(node)
? (formatOptions.indentSize || 0)
: 0;
let text = applyFormatting(nonFormattedText, sourceFile, initialIndentation, delta, this.rulesProvider);
// strip initial indentation (spaces or tabs) if text will be inserted in the middle of the line
// however keep indentation if it is was forced
text = posStartsLine || change.options.indentation !== undefined ? text : text.replace(/^\s+/, "");
return (options.prefix || "") + text + (options.suffix || "");
return applyFormatting(nonformattedText, sourceFile, initialIndentation, delta, this.rulesProvider);
}
private static normalize(changes: Change[]): Change[] {
@@ -654,4 +740,4 @@ namespace ts.textChanges {
this.lastNonTriviaPosition = 0;
}
}
}
}
-1
View File
@@ -273,7 +273,6 @@ namespace ts {
getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan;
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: FormatCodeSettings): CodeAction[];
getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[];
getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined;
+1
View File
@@ -1247,6 +1247,7 @@ namespace ts {
}
export function signatureToDisplayParts(typechecker: TypeChecker, signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[] {
flags |= TypeFormatFlags.UseAliasDefinedOutsideCurrentScope;
return mapToDisplayParts(writer => {
typechecker.getSymbolDisplayBuilder().buildSignatureDisplay(signature, writer, enclosingDeclaration, flags);
});
@@ -1158,3 +1158,4 @@ MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/// <reference no-default-lib="true"/>
+3 -3
View File
@@ -16,17 +16,17 @@ class Board {
}
//// [2dArrays.js]
var Cell = (function () {
var Cell = /** @class */ (function () {
function Cell() {
}
return Cell;
}());
var Ship = (function () {
var Ship = /** @class */ (function () {
function Ship() {
}
return Ship;
}());
var Board = (function () {
var Board = /** @class */ (function () {
function Board() {
}
Board.prototype.allShipsSunk = function () {
@@ -25,7 +25,7 @@ var p = new A.Point(0, 0); // unexpected error here, bug 840000
//// [classPoint.js]
var A;
(function (A) {
var Point = (function () {
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
@@ -51,7 +51,7 @@ module clodule4 {
//// [ClassAndModuleThatMergeWithModuleMemberThatUsesClassTypeParameter.js]
// all expected to be errors
var clodule1 = (function () {
var clodule1 = /** @class */ (function () {
function clodule1() {
}
return clodule1;
@@ -59,20 +59,20 @@ var clodule1 = (function () {
(function (clodule1) {
function f(x) { }
})(clodule1 || (clodule1 = {}));
var clodule2 = (function () {
var clodule2 = /** @class */ (function () {
function clodule2() {
}
return clodule2;
}());
(function (clodule2) {
var x;
var D = (function () {
var D = /** @class */ (function () {
function D() {
}
return D;
}());
})(clodule2 || (clodule2 = {}));
var clodule3 = (function () {
var clodule3 = /** @class */ (function () {
function clodule3() {
}
return clodule3;
@@ -80,13 +80,13 @@ var clodule3 = (function () {
(function (clodule3) {
clodule3.y = { id: T };
})(clodule3 || (clodule3 = {}));
var clodule4 = (function () {
var clodule4 = /** @class */ (function () {
function clodule4() {
}
return clodule4;
}());
(function (clodule4) {
var D = (function () {
var D = /** @class */ (function () {
function D() {
}
return D;
@@ -16,7 +16,7 @@ module clodule {
//// [ClassAndModuleThatMergeWithModulesExportedGenericFunctionAndGenericClassStaticFunctionOfTheSameName.js]
var clodule = (function () {
var clodule = /** @class */ (function () {
function clodule() {
}
clodule.fn = function (id) { };
@@ -16,7 +16,7 @@ module clodule {
//// [ClassAndModuleThatMergeWithModulesExportedGenericFunctionAndNonGenericClassStaticFunctionOfTheSameName.js]
var clodule = (function () {
var clodule = /** @class */ (function () {
function clodule() {
}
clodule.fn = function (id) { };
@@ -16,7 +16,7 @@ module clodule {
//// [ClassAndModuleThatMergeWithModulesExportedStaticFunctionUsingClassPrivateStatics.js]
var clodule = (function () {
var clodule = /** @class */ (function () {
function clodule() {
}
clodule.sfn = function (id) { return 42; };
@@ -23,7 +23,7 @@ module A {
}
//// [ClassAndModuleThatMergeWithStaticFunctionAndExportedFunctionThatShareAName.js]
var Point = (function () {
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
@@ -37,7 +37,7 @@ var Point = (function () {
})(Point || (Point = {}));
var A;
(function (A) {
var Point = (function () {
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
@@ -23,7 +23,7 @@ module A {
}
//// [ClassAndModuleThatMergeWithStaticFunctionAndNonExportedFunctionThatShareAName.js]
var Point = (function () {
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
@@ -36,7 +36,7 @@ var Point = (function () {
})(Point || (Point = {}));
var A;
(function (A) {
var Point = (function () {
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
@@ -23,7 +23,7 @@ module A {
}
//// [ClassAndModuleThatMergeWithStaticVariableAndExportedVarThatShareAName.js]
var Point = (function () {
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
@@ -36,7 +36,7 @@ var Point = (function () {
})(Point || (Point = {}));
var A;
(function (A) {
var Point = (function () {
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
@@ -23,7 +23,7 @@ module A {
}
//// [ClassAndModuleThatMergeWithStaticVariableAndNonExportedVarThatShareAName.js]
var Point = (function () {
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
@@ -36,7 +36,7 @@ var Point = (function () {
})(Point || (Point = {}));
var A;
(function (A) {
var Point = (function () {
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
@@ -45,7 +45,7 @@ var X;
(function (X) {
var Y;
(function (Y) {
var Point = (function () {
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
@@ -71,7 +71,7 @@ var X;
var cl = new X.Y.Point(1, 1);
var cl = X.Y.Point.Origin; // error not expected here same as bug 83996 ?
//// [simple.js]
var A = (function () {
var A = /** @class */ (function () {
function A() {
}
return A;
@@ -5,7 +5,7 @@ class C {
}
//// [ClassDeclaration10.js]
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
return C;
@@ -5,7 +5,7 @@ class C {
}
//// [ClassDeclaration11.js]
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
C.prototype.foo = function () { };
@@ -5,7 +5,7 @@ class C {
}
//// [ClassDeclaration13.js]
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
C.prototype.bar = function () { };
@@ -5,7 +5,7 @@ class C {
}
//// [ClassDeclaration14.js]
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
return C;
@@ -5,7 +5,7 @@ class C {
}
//// [ClassDeclaration15.js]
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
return C;
@@ -5,7 +5,7 @@ class C {
}
//// [ClassDeclaration21.js]
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
C.prototype[1] = function () { };
@@ -5,7 +5,7 @@ class C {
}
//// [ClassDeclaration22.js]
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
C.prototype["bar"] = function () { };
@@ -3,7 +3,7 @@ class any {
}
//// [ClassDeclaration24.js]
var any = (function () {
var any = /** @class */ (function () {
function any() {
}
return any;
@@ -10,7 +10,7 @@ class List<U> implements IList<U> {
//// [ClassDeclaration25.js]
var List = (function () {
var List = /** @class */ (function () {
function List() {
}
return List;
@@ -6,7 +6,7 @@ class C {
}
//// [ClassDeclaration26.js]
var C = (function () {
var C = /** @class */ (function () {
function C() {
this.foo = 10;
}
@@ -4,7 +4,7 @@ class C {
}
//// [ClassDeclaration8.js]
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
return C;
@@ -4,7 +4,7 @@ class C {
}
//// [ClassDeclaration9.js]
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
return C;
@@ -4,7 +4,7 @@ class AtomicNumbers {
}
//// [ClassDeclarationWithInvalidConstOnPropertyDeclaration.js]
var AtomicNumbers = (function () {
var AtomicNumbers = /** @class */ (function () {
function AtomicNumbers() {
}
AtomicNumbers.H = 1;
@@ -5,7 +5,7 @@ class C {
}
//// [ClassDeclarationWithInvalidConstOnPropertyDeclaration2.js]
var C = (function () {
var C = /** @class */ (function () {
function C() {
this.x = 10;
}
@@ -16,7 +16,7 @@ for (var v of new StringIterator) { }
//// [ES5For-ofTypeCheck10.js]
// In ES3/5, you cannot for...of over an arbitrary iterable.
var StringIterator = (function () {
var StringIterator = /** @class */ (function () {
function StringIterator() {
}
StringIterator.prototype.next = function () {
@@ -14,7 +14,7 @@ module M {
var M;
(function (M) {
var Symbol;
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
C.prototype[Symbol.iterator] = function () { };
@@ -9,7 +9,7 @@ class C {
//// [ES5SymbolProperty3.js]
var Symbol;
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
C.prototype[Symbol.iterator] = function () { };
@@ -9,7 +9,7 @@ class C {
//// [ES5SymbolProperty4.js]
var Symbol;
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
C.prototype[Symbol.iterator] = function () { };
@@ -9,7 +9,7 @@ class C {
//// [ES5SymbolProperty5.js]
var Symbol;
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
C.prototype[Symbol.iterator] = function () { };
@@ -6,7 +6,7 @@ class C {
(new C)[Symbol.iterator]
//// [ES5SymbolProperty6.js]
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
C.prototype[Symbol.iterator] = function () { };
@@ -9,7 +9,7 @@ class C {
//// [ES5SymbolProperty7.js]
var Symbol;
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
C.prototype[Symbol.iterator] = function () { };
@@ -23,7 +23,7 @@ var enumdule;
enumdule[enumdule["Blue"] = 1] = "Blue";
})(enumdule || (enumdule = {}));
(function (enumdule) {
var Point = (function () {
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
@@ -6,7 +6,7 @@ export = B;
//// [ExportAssignment7.js]
"use strict";
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
return C;
@@ -6,7 +6,7 @@ export class C {
//// [ExportAssignment8.js]
"use strict";
var C = (function () {
var C = /** @class */ (function () {
function C() {
}
return C;
@@ -22,7 +22,7 @@ module A {
//// [ExportClassWhichExtendsInterfaceWithInaccessibleType.js]
var A;
(function (A) {
var Point2d = (function () {
var Point2d = /** @class */ (function () {
function Point2d(x, y) {
this.x = x;
this.y = y;
@@ -33,14 +33,14 @@ var __extends = (this && this.__extends) || (function () {
})();
var A;
(function (A) {
var Point = (function () {
var Point = /** @class */ (function () {
function Point() {
}
return Point;
}());
A.Point = Point;
A.Origin = { x: 0, y: 0 };
var Point3d = (function (_super) {
var Point3d = /** @class */ (function (_super) {
__extends(Point3d, _super);
function Point3d() {
return _super !== null && _super.apply(this, arguments) || this;
@@ -49,7 +49,7 @@ var A;
}(Point));
A.Point3d = Point3d;
A.Origin3d = { x: 0, y: 0, z: 0 };
var Line = (function () {
var Line = /** @class */ (function () {
function Line(start, end) {
this.start = start;
this.end = end;
@@ -18,12 +18,12 @@ module A {
//// [ExportClassWithInaccessibleTypeInIndexerTypeAnnotations.js]
var A;
(function (A) {
var Point = (function () {
var Point = /** @class */ (function () {
function Point() {
}
return Point;
}());
var points = (function () {
var points = /** @class */ (function () {
function points() {
}
return points;
@@ -37,13 +37,13 @@ var __extends = (this && this.__extends) || (function () {
})();
var A;
(function (A) {
var Point = (function () {
var Point = /** @class */ (function () {
function Point() {
}
return Point;
}());
A.Origin = { x: 0, y: 0 };
var Point3d = (function (_super) {
var Point3d = /** @class */ (function (_super) {
__extends(Point3d, _super);
function Point3d() {
return _super !== null && _super.apply(this, arguments) || this;
@@ -52,7 +52,7 @@ var A;
}(Point));
A.Point3d = Point3d;
A.Origin3d = { x: 0, y: 0, z: 0 };
var Line = (function () {
var Line = /** @class */ (function () {
function Line(start, end) {
this.start = start;
this.end = end;
@@ -18,13 +18,13 @@ module A {
//// [ExportFunctionWithAccessibleTypesInParameterAndReturnTypeAnnotation.js]
var A;
(function (A) {
var Point = (function () {
var Point = /** @class */ (function () {
function Point() {
}
return Point;
}());
A.Point = Point;
var Line = (function () {
var Line = /** @class */ (function () {
function Line(start, end) {
this.start = start;
this.end = end;
@@ -18,12 +18,12 @@ module A {
//// [ExportFunctionWithInaccessibleTypesInParameterTypeAnnotation.js]
var A;
(function (A) {
var Point = (function () {
var Point = /** @class */ (function () {
function Point() {
}
return Point;
}());
var Line = (function () {
var Line = /** @class */ (function () {
function Line(start, end) {
this.start = start;
this.end = end;

Some files were not shown because too many files have changed in this diff Show More