Merge branch 'master' into literalEnumTypes

# Conflicts:
#	src/compiler/checker.ts
#	src/compiler/diagnosticMessages.json
#	src/compiler/types.ts
#	src/compiler/utilities.ts
#	src/services/completions.ts
This commit is contained in:
Anders Hejlsberg committed 2017-05-17 11:59:12 -07:00
commit 947abf5d6b
527 files changed
+143798 -67084

No files matched your search

+29 -28
View File
@@ -149,7 +149,7 @@ namespace ts {
inStrictMode = bindInStrictMode(file, opts);
classifiableNames = createMap<string>();
symbolCount = 0;
skipTransformFlagAggregation = isDeclarationFile(file);
skipTransformFlagAggregation = file.isDeclarationFile;
Symbol = objectAllocator.getSymbolConstructor();
@@ -182,7 +182,7 @@ namespace ts {
return bindSourceFile;
function bindInStrictMode(file: SourceFile, opts: CompilerOptions): boolean {
if ((opts.alwaysStrict === undefined ? opts.strict : opts.alwaysStrict) && !isDeclarationFile(file)) {
if ((opts.alwaysStrict === undefined ? opts.strict : opts.alwaysStrict) && !file.isDeclarationFile) {
// bind in strict mode source files with alwaysStrict option
return true;
}
@@ -221,18 +221,19 @@ namespace ts {
// other kinds of value declarations take precedence over modules
symbol.valueDeclaration = node;
}
}
}
}
// Should not be called on a declaration with a computed property name,
// unless it is a well known Symbol.
function getDeclarationName(node: Declaration): string {
if (node.name) {
const name = getNameOfDeclaration(node);
if (name) {
if (isAmbientModule(node)) {
return isGlobalScopeAugmentation(<ModuleDeclaration>node) ? "__global" : `"${(<LiteralExpression>node.name).text}"`;
return isGlobalScopeAugmentation(<ModuleDeclaration>node) ? "__global" : `"${(<LiteralExpression>name).text}"`;
}
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
const nameExpression = (<ComputedPropertyName>node.name).expression;
if (name.kind === SyntaxKind.ComputedPropertyName) {
const nameExpression = (<ComputedPropertyName>name).expression;
// treat computed property names where expression is string/numeric literal as just string/numeric literal
if (isStringOrNumericLiteral(nameExpression)) {
return nameExpression.text;
@@ -241,7 +242,7 @@ namespace ts {
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
return getPropertyNameForKnownSymbolName((<PropertyAccessExpression>nameExpression).name.text);
}
return (<Identifier | LiteralExpression>node.name).text;
return (<Identifier | LiteralExpression>name).text;
}
switch (node.kind) {
case SyntaxKind.Constructor:
@@ -303,7 +304,7 @@ namespace ts {
}
function getDisplayName(node: Declaration): string {
return node.name ? declarationNameToString(node.name) : getDeclarationName(node);
return (node as NamedDeclaration).name ? declarationNameToString((node as NamedDeclaration).name) : getDeclarationName(node);
}
/**
@@ -366,8 +367,8 @@ namespace ts {
symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name));
}
else {
if (node.name) {
node.name.parent = node;
if ((node as NamedDeclaration).name) {
(node as NamedDeclaration).name.parent = node;
}
// Report errors every position with duplicate declaration
@@ -389,16 +390,16 @@ namespace ts {
// 1. multiple export default of class declaration or function declaration by checking NodeFlags.Default
// 2. multiple export default of export assignment. This one doesn't have NodeFlags.Default on (as export default doesn't considered as modifiers)
if (symbol.declarations && symbol.declarations.length &&
(isDefaultExport || (node.kind === SyntaxKind.ExportAssignment && !(<ExportAssignment>node).isExportEquals))) {
message = Diagnostics.A_module_cannot_have_multiple_default_exports;
}
(isDefaultExport || (node.kind === SyntaxKind.ExportAssignment && !(<ExportAssignment>node).isExportEquals))) {
message = Diagnostics.A_module_cannot_have_multiple_default_exports;
}
}
}
forEach(symbol.declarations, declaration => {
file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration)));
file.bindDiagnostics.push(createDiagnosticForNode(getNameOfDeclaration(declaration) || declaration, message, getDisplayName(declaration)));
});
file.bindDiagnostics.push(createDiagnosticForNode(node.name || node, message, getDisplayName(node)));
file.bindDiagnostics.push(createDiagnosticForNode(getNameOfDeclaration(node) || node, message, getDisplayName(node)));
symbol = createSymbol(SymbolFlags.None, name);
}
@@ -439,9 +440,9 @@ namespace ts {
// and this case is specially handled. Module augmentations should only be merged with original module definition
// and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed.
const isJSDocTypedefInJSDocNamespace = node.kind === SyntaxKind.JSDocTypedefTag &&
node.name &&
node.name.kind === SyntaxKind.Identifier &&
(<Identifier>node.name).isInJSDocNamespace;
(node as JSDocTypedefTag).name &&
(node as JSDocTypedefTag).name.kind === SyntaxKind.Identifier &&
((node as JSDocTypedefTag).name as Identifier).isInJSDocNamespace;
if ((!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) || isJSDocTypedefInJSDocNamespace) {
const exportKind =
(symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0) |
@@ -1414,7 +1415,7 @@ namespace ts {
if (isObjectLiteralOrClassExpressionMethod(node)) {
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsObjectLiteralOrClassExpressionMethod;
}
// falls through
// falls through
case SyntaxKind.Constructor:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodSignature:
@@ -1716,7 +1717,7 @@ namespace ts {
declareModuleMember(node, symbolFlags, symbolExcludes);
break;
}
// falls through
// falls through
default:
if (!blockScopeContainer.locals) {
blockScopeContainer.locals = createMap<Symbol>();
@@ -2010,7 +2011,7 @@ namespace ts {
bindBlockScopedDeclaration(<Declaration>parentNode, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
break;
}
// falls through
// falls through
case SyntaxKind.ThisKeyword:
if (currentFlow && (isExpression(node) || parent.kind === SyntaxKind.ShorthandPropertyAssignment)) {
node.flowNode = currentFlow;
@@ -2186,7 +2187,7 @@ namespace ts {
if (!isFunctionLike(node.parent)) {
return;
}
// falls through
// falls through
case SyntaxKind.ModuleBlock:
return updateStrictModeStatementList((<Block | ModuleBlock>node).statements);
}
@@ -2211,7 +2212,7 @@ namespace ts {
}
function bindSourceFileAsExternalModule() {
bindAnonymousDeclaration(file, SymbolFlags.ValueModule, `"${removeFileExtension(file.fileName) }"`);
bindAnonymousDeclaration(file, SymbolFlags.ValueModule, `"${removeFileExtension(file.fileName)}"`);
}
function bindExportAssignment(node: ExportAssignment | BinaryExpression) {
@@ -2504,7 +2505,7 @@ namespace ts {
}
function bindParameter(node: ParameterDeclaration) {
if (inStrictMode) {
if (inStrictMode && !isInAmbientContext(node)) {
// It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a
// strict mode FunctionLikeDeclaration or FunctionExpression(13.1)
checkStrictModeEvalOrArguments(node, node.name);
@@ -2526,7 +2527,7 @@ namespace ts {
}
function bindFunctionDeclaration(node: FunctionDeclaration) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (!file.isDeclarationFile && !isInAmbientContext(node)) {
if (isAsyncFunction(node)) {
emitFlags |= NodeFlags.HasAsyncFunctions;
}
@@ -2543,7 +2544,7 @@ namespace ts {
}
function bindFunctionExpression(node: FunctionExpression) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (!file.isDeclarationFile && !isInAmbientContext(node)) {
if (isAsyncFunction(node)) {
emitFlags |= NodeFlags.HasAsyncFunctions;
}
@@ -2557,7 +2558,7 @@ namespace ts {
}
function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (!file.isDeclarationFile && !isInAmbientContext(node)) {
if (isAsyncFunction(node)) {
emitFlags |= NodeFlags.HasAsyncFunctions;
}
+904 -506
View File
File diff suppressed because it is too large. Load diff
+125 -82
View File
@@ -1088,58 +1088,38 @@ namespace ts {
* @param host Instance of ParseConfigHost used to enumerate files in folder.
* @param basePath A root directory to resolve relative path entries in the config
* file to. e.g. outDir
* @param resolutionStack Only present for backwards-compatibility. Should be empty.
*/
export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string, resolutionStack: Path[] = [], extraFileExtensions: JsFileExtensionInfo[] = []): ParsedCommandLine {
export function parseJsonConfigFileContent(
json: any,
host: ParseConfigHost,
basePath: string,
existingOptions: CompilerOptions = {},
configFileName?: string,
resolutionStack: Path[] = [],
extraFileExtensions: JsFileExtensionInfo[] = [],
): ParsedCommandLine {
const errors: Diagnostic[] = [];
basePath = normalizeSlashes(basePath);
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
const resolvedPath = toPath(configFileName || "", basePath, getCanonicalFileName);
if (resolutionStack.indexOf(resolvedPath) >= 0) {
return {
options: {},
fileNames: [],
typeAcquisition: {},
raw: json,
errors: [createCompilerDiagnostic(Diagnostics.Circularity_detected_while_resolving_configuration_Colon_0, [...resolutionStack, resolvedPath].join(" -> "))],
wildcardDirectories: {}
};
}
let options: CompilerOptions = convertCompilerOptionsFromJsonWorker(json["compilerOptions"], basePath, errors, configFileName);
let options = (() => {
const { include, exclude, files, options, compileOnSave } = parseConfig(json, host, basePath, configFileName, resolutionStack, errors);
if (include) { json.include = include; }
if (exclude) { json.exclude = exclude; }
if (files) { json.files = files; }
if (compileOnSave !== undefined) { json.compileOnSave = compileOnSave; }
return options;
})();
options = extend(existingOptions, options);
options.configFilePath = configFileName;
// typingOptions has been deprecated and is only supported for backward compatibility purposes.
// It should be removed in future releases - use typeAcquisition instead.
const jsonOptions = json["typeAcquisition"] || json["typingOptions"];
const typeAcquisition: TypeAcquisition = convertTypeAcquisitionFromJsonWorker(jsonOptions, basePath, errors, configFileName);
let baseCompileOnSave: boolean;
if (json["extends"]) {
let [include, exclude, files, baseOptions]: [string[], string[], string[], CompilerOptions] = [undefined, undefined, undefined, {}];
if (typeof json["extends"] === "string") {
[include, exclude, files, baseCompileOnSave, baseOptions] = (tryExtendsName(json["extends"]) || [include, exclude, files, baseCompileOnSave, baseOptions]);
}
else {
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", "string"));
}
if (include && !json["include"]) {
json["include"] = include;
}
if (exclude && !json["exclude"]) {
json["exclude"] = exclude;
}
if (files && !json["files"]) {
json["files"] = files;
}
options = assign({}, baseOptions, options);
}
options = extend(existingOptions, options);
options.configFilePath = configFileName;
const { fileNames, wildcardDirectories } = getFileNames(errors);
let compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors);
if (baseCompileOnSave && json[compileOnSaveCommandLineOption.name] === undefined) {
compileOnSave = baseCompileOnSave;
}
const { fileNames, wildcardDirectories } = getFileNames();
const compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors);
return {
options,
@@ -1151,40 +1131,7 @@ namespace ts {
compileOnSave
};
function tryExtendsName(extendedConfig: string): [string[], string[], string[], boolean, CompilerOptions] {
// If the path isn't a rooted or relative path, don't try to resolve it (we reserve the right to special case module-id like paths in the future)
if (!(isRootedDiskPath(extendedConfig) || startsWith(normalizeSlashes(extendedConfig), "./") || startsWith(normalizeSlashes(extendedConfig), "../"))) {
errors.push(createCompilerDiagnostic(Diagnostics.A_path_in_an_extends_option_must_be_relative_or_rooted_but_0_is_not, extendedConfig));
return;
}
let extendedConfigPath = toPath(extendedConfig, basePath, getCanonicalFileName);
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, ".json")) {
extendedConfigPath = `${extendedConfigPath}.json` as Path;
if (!host.fileExists(extendedConfigPath)) {
errors.push(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
return;
}
}
const extendedResult = readConfigFile(extendedConfigPath, path => host.readFile(path));
if (extendedResult.error) {
errors.push(extendedResult.error);
return;
}
const extendedDirname = getDirectoryPath(extendedConfigPath);
const relativeDifference = convertToRelativePath(extendedDirname, basePath, getCanonicalFileName);
const updatePath: (path: string) => string = path => isRootedDiskPath(path) ? path : combinePaths(relativeDifference, path);
// Merge configs (copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios)
const result = parseJsonConfigFileContent(extendedResult.config, host, extendedDirname, /*existingOptions*/ undefined, getBaseFileName(extendedConfigPath), resolutionStack.concat([resolvedPath]));
errors.push(...result.errors);
const [include, exclude, files] = map(["include", "exclude", "files"], key => {
if (!json[key] && extendedResult.config[key]) {
return map(extendedResult.config[key], updatePath);
}
});
return [include, exclude, files, result.compileOnSave, result.options];
}
function getFileNames(errors: Diagnostic[]): ExpandResult {
function getFileNames(): ExpandResult {
let fileNames: string[];
if (hasProperty(json, "files")) {
if (isArray(json["files"])) {
@@ -1217,9 +1164,6 @@ namespace ts {
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "exclude", "Array"));
}
}
else if (hasProperty(json, "excludes")) {
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_option_excludes_Did_you_mean_exclude));
}
else {
// If no includes were specified, exclude common package folders and the outDir
excludeSpecs = includeSpecs ? [] : ["node_modules", "bower_components", "jspm_packages"];
@@ -1249,7 +1193,106 @@ namespace ts {
}
}
export function convertCompileOnSaveOptionFromJson(jsonOption: any, basePath: string, errors: Diagnostic[]): boolean | undefined {
interface ParsedTsconfig {
include: string[] | undefined;
exclude: string[] | undefined;
files: string[] | undefined;
options: CompilerOptions;
compileOnSave: boolean | undefined;
}
/**
* This *just* extracts options/include/exclude/files out of a config file.
* It does *not* resolve the included files.
*/
function parseConfig(
json: any,
host: ParseConfigHost,
basePath: string,
configFileName: string,
resolutionStack: Path[],
errors: Diagnostic[],
): ParsedTsconfig {
basePath = normalizeSlashes(basePath);
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
const resolvedPath = toPath(configFileName || "", basePath, getCanonicalFileName);
if (resolutionStack.indexOf(resolvedPath) >= 0) {
errors.push(createCompilerDiagnostic(Diagnostics.Circularity_detected_while_resolving_configuration_Colon_0, [...resolutionStack, resolvedPath].join(" -> ")));
return { include: undefined, exclude: undefined, files: undefined, options: {}, compileOnSave: undefined };
}
if (hasProperty(json, "excludes")) {
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_option_excludes_Did_you_mean_exclude));
}
let options: CompilerOptions = convertCompilerOptionsFromJsonWorker(json.compilerOptions, basePath, errors, configFileName);
let include: string[] | undefined = json.include, exclude: string[] | undefined = json.exclude, files: string[] | undefined = json.files;
let compileOnSave: boolean | undefined = json.compileOnSave;
if (json.extends) {
// copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios.
resolutionStack = resolutionStack.concat([resolvedPath]);
const base = getExtendedConfig(json.extends, host, basePath, getCanonicalFileName, resolutionStack, errors);
if (base) {
include = include || base.include;
exclude = exclude || base.exclude;
files = files || base.files;
if (compileOnSave === undefined) {
compileOnSave = base.compileOnSave;
}
options = assign({}, base.options, options);
}
}
return { include, exclude, files, options, compileOnSave };
}
function getExtendedConfig(
extended: any, // Usually a string.
host: ts.ParseConfigHost,
basePath: string,
getCanonicalFileName: (fileName: string) => string,
resolutionStack: Path[],
errors: Diagnostic[],
): ParsedTsconfig | undefined {
if (typeof extended !== "string") {
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", "string"));
return undefined;
}
extended = normalizeSlashes(extended);
// If the path isn't a rooted or relative path, don't try to resolve it (we reserve the right to special case module-id like paths in the future)
if (!(isRootedDiskPath(extended) || startsWith(extended, "./") || startsWith(extended, "../"))) {
errors.push(createCompilerDiagnostic(Diagnostics.A_path_in_an_extends_option_must_be_relative_or_rooted_but_0_is_not, extended));
return undefined;
}
let extendedConfigPath = toPath(extended, basePath, getCanonicalFileName);
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, ".json")) {
extendedConfigPath = extendedConfigPath + ".json" as Path;
if (!host.fileExists(extendedConfigPath)) {
errors.push(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, extended));
return undefined;
}
}
const extendedResult = readConfigFile(extendedConfigPath, path => host.readFile(path));
if (extendedResult.error) {
errors.push(extendedResult.error);
return undefined;
}
const extendedDirname = getDirectoryPath(extendedConfigPath);
const relativeDifference = convertToRelativePath(extendedDirname, basePath, getCanonicalFileName);
const updatePath: (path: string) => string = path => isRootedDiskPath(path) ? path : combinePaths(relativeDifference, path);
const { include, exclude, files, options, compileOnSave } = parseConfig(extendedResult.config, host, extendedDirname, getBaseFileName(extendedConfigPath), resolutionStack, errors);
return { include: map(include, updatePath), exclude: map(exclude, updatePath), files: map(files, updatePath), compileOnSave, options };
}
export function convertCompileOnSaveOptionFromJson(jsonOption: any, basePath: string, errors: Diagnostic[]): boolean {
if (!hasProperty(jsonOption, compileOnSaveCommandLineOption.name)) {
return false;
}
+41 -1
View File
@@ -3,7 +3,7 @@
namespace ts {
/** The version of the TypeScript compiler release */
export const version = "2.3.0";
export const version = "2.4.0";
}
/* @internal */
@@ -230,6 +230,8 @@ namespace ts {
* If no such value is found, it applies the callback until the parent pointer is undefined or the callback returns "quit"
* At that point findAncestor returns undefined.
*/
export function findAncestor<T extends Node>(node: Node, callback: (element: Node) => element is T): T | undefined;
export function findAncestor(node: Node, callback: (element: Node) => boolean | "quit"): Node | undefined;
export function findAncestor(node: Node, callback: (element: Node) => boolean | "quit"): Node {
while (node) {
const result = callback(node);
@@ -251,6 +253,15 @@ namespace ts {
}
}
export function zipToMap<T>(keys: string[], values: T[]): Map<T> {
Debug.assert(keys.length === values.length);
const map = createMap<T>();
for (let i = 0; i < keys.length; ++i) {
map.set(keys[i], values[i]);
}
return map;
}
/**
* Iterates through `array` by index and performs the callback on each element of array until the callback
* returns a falsey value, then returns false.
@@ -481,6 +492,35 @@ namespace ts {
return result;
}
/**
* Maps an array. If the mapped value is an array, it is spread into the result.
* Avoids allocation if all elements map to themselves.
*
* @param array The array to map.
* @param mapfn The callback used to map the result into one or more values.
*/
export function sameFlatMap<T>(array: T[], mapfn: (x: T, i: number) => T | T[]): T[] {
let result: T[];
if (array) {
for (let i = 0; i < array.length; i++) {
const item = array[i];
const mapped = mapfn(item, i);
if (result || item !== mapped || isArray(mapped)) {
if (!result) {
result = array.slice(0, i);
}
if (isArray(mapped)) {
addRange(result, mapped);
}
else {
result.push(mapped);
}
}
}
}
return result || array;
}
/**
* Computes the first matching span of elements and returns a tuple of the first span
* and the remaining elements.
+65 -45
View File
@@ -67,7 +67,7 @@ namespace ts {
let errorNameNode: DeclarationName;
const emitJsDocComments = compilerOptions.removeComments ? noop : writeJsDocComments;
const emit = compilerOptions.stripInternal ? stripInternal : emitNode;
let noDeclare: boolean;
let needsDeclare = true;
let moduleElementDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[] = [];
let asynchronousSubModuleDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[];
@@ -110,11 +110,11 @@ namespace ts {
resultHasExternalModuleIndicator = false;
if (!isBundledEmit || !isExternalModule(sourceFile)) {
noDeclare = false;
needsDeclare = true;
emitSourceFile(sourceFile);
}
else if (isExternalModule(sourceFile)) {
noDeclare = true;
needsDeclare = false;
write(`declare module "${getResolvedExternalModuleName(host, sourceFile)}" {`);
writeLine();
increaseIndent();
@@ -594,12 +594,11 @@ namespace ts {
emitLines(node.statements);
}
// Return a temp variable name to be used in `export default` statements.
// Return a temp variable name to be used in `export default`/`export class ... extends` statements.
// The temp name will be of the form _default_counter.
// Note that export default is only allowed at most once in a module, so we
// do not need to keep track of created temp names.
function getExportDefaultTempVariableName(): string {
const baseName = "_default";
function getExportTempVariableName(baseName: string): string {
if (!currentIdentifiers.has(baseName)) {
return baseName;
}
@@ -613,24 +612,31 @@ namespace ts {
}
}
function emitTempVariableDeclaration(expr: Expression, baseName: string, diagnostic: SymbolAccessibilityDiagnostic, needsDeclare: boolean): string {
const tempVarName = getExportTempVariableName(baseName);
if (needsDeclare) {
write("declare ");
}
write("const ");
write(tempVarName);
write(": ");
writer.getSymbolAccessibilityDiagnostic = () => diagnostic;
resolver.writeTypeOfExpression(expr, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
write(";");
writeLine();
return tempVarName;
}
function emitExportAssignment(node: ExportAssignment) {
if (node.expression.kind === SyntaxKind.Identifier) {
write(node.isExportEquals ? "export = " : "export default ");
writeTextOfNode(currentText, node.expression);
}
else {
// Expression
const tempVarName = getExportDefaultTempVariableName();
if (!noDeclare) {
write("declare ");
}
write("var ");
write(tempVarName);
write(": ");
writer.getSymbolAccessibilityDiagnostic = getDefaultExportAccessibilityDiagnostic;
resolver.writeTypeOfExpression(node.expression, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
write(";");
writeLine();
const tempVarName = emitTempVariableDeclaration(node.expression, "_default", {
diagnosticMessage: Diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0,
errorNode: node
}, needsDeclare);
write(node.isExportEquals ? "export = " : "export default ");
write(tempVarName);
}
@@ -644,13 +650,6 @@ namespace ts {
// write each of these declarations asynchronously
writeAsynchronousModuleElements(nodes);
}
function getDefaultExportAccessibilityDiagnostic(): SymbolAccessibilityDiagnostic {
return {
diagnosticMessage: Diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0,
errorNode: node
};
}
}
function isModuleElementVisible(node: Declaration) {
@@ -729,7 +728,7 @@ namespace ts {
if (modifiers & ModifierFlags.Default) {
write("default ");
}
else if (node.kind !== SyntaxKind.InterfaceDeclaration && !noDeclare) {
else if (node.kind !== SyntaxKind.InterfaceDeclaration && needsDeclare) {
write("declare ");
}
}
@@ -1097,7 +1096,7 @@ namespace ts {
}
}
function emitHeritageClause(className: Identifier, typeReferences: ExpressionWithTypeArguments[], isImplementsList: boolean) {
function emitHeritageClause(typeReferences: ExpressionWithTypeArguments[], isImplementsList: boolean) {
if (typeReferences) {
write(isImplementsList ? " implements " : " extends ");
emitCommaList(typeReferences, emitTypeOfTypeReference);
@@ -1110,12 +1109,6 @@ namespace ts {
else if (!isImplementsList && node.expression.kind === SyntaxKind.NullKeyword) {
write("null");
}
else {
writer.getSymbolAccessibilityDiagnostic = getHeritageClauseVisibilityError;
errorNameNode = className;
resolver.writeBaseConstructorTypeOfClass(<ClassLikeDeclaration>enclosingDeclaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
errorNameNode = undefined;
}
function getHeritageClauseVisibilityError(): SymbolAccessibilityDiagnostic {
let diagnosticMessage: DiagnosticMessage;
@@ -1134,7 +1127,7 @@ namespace ts {
return {
diagnosticMessage,
errorNode: node,
typeName: (<Declaration>node.parent.parent).name
typeName: getNameOfDeclaration(node.parent.parent)
};
}
}
@@ -1151,23 +1144,43 @@ namespace ts {
}
}
const prevEnclosingDeclaration = enclosingDeclaration;
enclosingDeclaration = node;
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
let tempVarName: string;
if (baseTypeNode && !isEntityNameExpression(baseTypeNode.expression)) {
tempVarName = baseTypeNode.expression.kind === SyntaxKind.NullKeyword ?
"null" :
emitTempVariableDeclaration(baseTypeNode.expression, `${node.name.text}_base`, {
diagnosticMessage: Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1,
errorNode: baseTypeNode,
typeName: node.name
}, !findAncestor(node, n => n.kind === SyntaxKind.ModuleDeclaration));
}
emitJsDocComments(node);
emitModuleElementDeclarationFlags(node);
if (hasModifier(node, ModifierFlags.Abstract)) {
write("abstract ");
}
write("class ");
writeTextOfNode(currentText, node.name);
const prevEnclosingDeclaration = enclosingDeclaration;
enclosingDeclaration = node;
emitTypeParameters(node.typeParameters);
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
if (baseTypeNode) {
node.name;
emitHeritageClause(node.name, [baseTypeNode], /*isImplementsList*/ false);
if (!isEntityNameExpression(baseTypeNode.expression)) {
write(" extends ");
write(tempVarName);
if (baseTypeNode.typeArguments) {
write("<");
emitCommaList(baseTypeNode.typeArguments, emitType);
write(">");
}
}
else {
emitHeritageClause([baseTypeNode], /*isImplementsList*/ false);
}
}
emitHeritageClause(node.name, getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true);
emitHeritageClause(getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true);
write(" {");
writeLine();
increaseIndent();
@@ -1189,7 +1202,7 @@ namespace ts {
emitTypeParameters(node.typeParameters);
const interfaceExtendsTypes = filter(getInterfaceBaseTypeNodes(node), base => isEntityNameExpression(base.expression));
if (interfaceExtendsTypes && interfaceExtendsTypes.length) {
emitHeritageClause(node.name, interfaceExtendsTypes, /*isImplementsList*/ false);
emitHeritageClause(interfaceExtendsTypes, /*isImplementsList*/ false);
}
write(" {");
writeLine();
@@ -1253,7 +1266,9 @@ namespace ts {
Diagnostics.Exported_variable_0_has_or_is_using_private_name_1;
}
// This check is to ensure we don't report error on constructor parameter property as that error would be reported during parameter emit
else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) {
// The only exception here is if the constructor was marked as private. we are not emitting the constructor parameters at all.
else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature ||
(node.kind === SyntaxKind.Parameter && hasModifier(node.parent, ModifierFlags.Private))) {
// TODO(jfreeman): Deal with computed properties in error reporting.
if (hasModifier(node, ModifierFlags.Static)) {
return symbolAccessibilityResult.errorModuleName ?
@@ -1262,7 +1277,7 @@ namespace ts {
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1;
}
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
else if (node.parent.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.Parameter) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
@@ -1488,6 +1503,11 @@ namespace ts {
write("[");
}
else {
if (node.kind === SyntaxKind.Constructor && hasModifier(node, ModifierFlags.Private)) {
write("();");
writeLine();
return;
}
// Construct signature or constructor type write new Signature
if (node.kind === SyntaxKind.ConstructSignature || node.kind === SyntaxKind.ConstructorType) {
write("new ");
@@ -1820,7 +1840,7 @@ namespace ts {
function writeReferencePath(referencedFile: SourceFile, addBundledFileReference: boolean, emitOnlyDtsFiles: boolean): boolean {
let declFileName: string;
let addedBundledEmitReference = false;
if (isDeclarationFile(referencedFile)) {
if (referencedFile.isDeclarationFile) {
// Declaration file, use declaration file name
declFileName = referencedFile.fileName;
}
+26 -4
View File
@@ -635,6 +635,10 @@
"category": "Error",
"code": 1203
},
"Cannot re-export a type when the '--isolatedModules' flag is provided.": {
"category": "Error",
"code": 1205
},
"Decorators are not valid here.": {
"category": "Error",
"code": 1206
@@ -1843,10 +1847,18 @@
"category": "Error",
"code": 2550
},
"Computed values are not permitted in an enum with string valued members.": {
"Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?": {
"category": "Error",
"code": 2551
},
"Cannot find name '{0}'. Did you mean '{1}'?": {
"category": "Error",
"code": 2552
},
"Computed values are not permitted in an enum with string valued members.": {
"category": "Error",
"code": 2553
},
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600
@@ -3207,9 +3219,16 @@
},
"Scoped package detected, looking in '{0}'": {
"category": "Message",
"code": "6182"
"code": 6182
},
"Reusing resolution of module '{0}' to file '{1}' from old program.": {
"category": "Message",
"code": 6183
},
"Reusing module resolutions originating in '{0}' since resolutions are unchanged from old program.": {
"category": "Message",
"code": 6184
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
@@ -3536,7 +3555,10 @@
"category": "Message",
"code": 90021
},
"Change spelling to '{0}'.": {
"category": "Message",
"code": 90022
},
"Octal literal types must use ES2015 syntax. Use the syntax '{0}'.": {
"category": "Error",
+50 -14
View File
@@ -153,7 +153,7 @@ namespace ts {
for (let i = 0; i < numNodes; i++) {
const currentNode = bundle ? bundle.sourceFiles[i] : node;
const sourceFile = isSourceFile(currentNode) ? currentNode : currentSourceFile;
const shouldSkip = compilerOptions.noEmitHelpers || (sourceFile && getExternalHelpersModuleName(sourceFile) !== undefined);
const shouldSkip = compilerOptions.noEmitHelpers || getExternalHelpersModuleName(sourceFile) !== undefined;
const shouldBundle = isSourceFile(currentNode) && !isOwnFileEmit;
const helpers = getEmitHelpers(currentNode);
if (helpers) {
@@ -212,7 +212,7 @@ namespace ts {
emitLeadingCommentsOfPosition,
} = comments;
let currentSourceFile: SourceFile;
let currentSourceFile: SourceFile | undefined;
let nodeIdToGeneratedName: string[]; // Map of generated names for specific nodes.
let autoGeneratedIdToGeneratedName: string[]; // Map of generated names for temp and loop variables.
let generatedNames: Map<string>; // Set of names generated by the NameGenerator.
@@ -264,7 +264,12 @@ namespace ts {
return endPrint();
}
function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, output: EmitTextWriter) {
/**
* If `sourceFile` is `undefined`, `node` must be a synthesized `TypeNode`.
*/
function writeNode(hint: EmitHint, node: TypeNode, sourceFile: undefined, output: EmitTextWriter): void;
function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, output: EmitTextWriter): void;
function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, output: EmitTextWriter) {
const previousWriter = writer;
setWriter(output);
print(hint, node, sourceFile);
@@ -305,8 +310,10 @@ namespace ts {
return text;
}
function print(hint: EmitHint, node: Node, sourceFile: SourceFile) {
setSourceFile(sourceFile);
function print(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined) {
if (sourceFile) {
setSourceFile(sourceFile);
}
pipelineEmitWithNotification(hint, node);
}
@@ -733,6 +740,9 @@ namespace ts {
// Transformation nodes
case SyntaxKind.PartiallyEmittedExpression:
return emitPartiallyEmittedExpression(<PartiallyEmittedExpression>node);
case SyntaxKind.CommaListExpression:
return emitCommaList(<CommaListExpression>node);
}
}
@@ -778,6 +788,7 @@ namespace ts {
function emitIdentifier(node: Identifier) {
write(getTextOfNode(node, /*includeTrivia*/ false));
emitTypeArguments(node, node.typeArguments);
}
//
@@ -812,6 +823,7 @@ namespace ts {
function emitTypeParameter(node: TypeParameterDeclaration) {
emit(node.name);
emitWithPrefix(" extends ", node.constraint);
emitWithPrefix(" = ", node.default);
}
function emitParameter(node: ParameterDeclaration) {
@@ -952,7 +964,10 @@ namespace ts {
function emitTypeLiteral(node: TypeLiteralNode) {
write("{");
emitList(node, node.members, ListFormat.TypeLiteralMembers);
// If the literal is empty, do not add spaces between braces.
if (node.members.length > 0) {
emitList(node, node.members, getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers);
}
write("}");
}
@@ -999,9 +1014,15 @@ namespace ts {
}
function emitMappedType(node: MappedTypeNode) {
const emitFlags = getEmitFlags(node);
write("{");
writeLine();
increaseIndent();
if (emitFlags & EmitFlags.SingleLine) {
write(" ");
}
else {
writeLine();
increaseIndent();
}
writeIfPresent(node.readonlyToken, "readonly ");
write("[");
emit(node.typeParameter.name);
@@ -1012,8 +1033,13 @@ namespace ts {
write(": ");
emit(node.type);
write(";");
writeLine();
decreaseIndent();
if (emitFlags & EmitFlags.SingleLine) {
write(" ");
}
else {
writeLine();
decreaseIndent();
}
write("}");
}
@@ -1032,7 +1058,7 @@ namespace ts {
}
else {
write("{");
emitList(node, elements, ListFormat.ObjectBindingPatternElements);
emitList(node, elements, getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.ObjectBindingPatternElements : ListFormat.ObjectBindingPatternElementsWithSpaceBetweenBraces);
write("}");
}
}
@@ -2101,6 +2127,10 @@ namespace ts {
emitExpression(node.expression);
}
function emitCommaList(node: CommaListExpression) {
emitExpressionList(node, node.elements, ListFormat.CommaListElements);
}
/**
* Emits any prologue directives at the start of a Statement list, returning the
* number of prologue directives written to the output.
@@ -2630,7 +2660,9 @@ namespace ts {
if (node.kind === SyntaxKind.StringLiteral && (<StringLiteral>node).textSourceNode) {
const textSourceNode = (<StringLiteral>node).textSourceNode;
if (isIdentifier(textSourceNode)) {
return "\"" + escapeNonAsciiCharacters(escapeString(getTextOfNode(textSourceNode))) + "\"";
return getEmitFlags(node) & EmitFlags.NoAsciiEscaping ?
`"${escapeString(getTextOfNode(textSourceNode))}"` :
`"${escapeNonAsciiString(getTextOfNode(textSourceNode))}"`;
}
else {
return getLiteralTextOfNode(textSourceNode);
@@ -2943,14 +2975,18 @@ namespace ts {
// Precomputed Formats
Modifiers = SingleLine | SpaceBetweenSiblings,
HeritageClauses = SingleLine | SpaceBetweenSiblings,
TypeLiteralMembers = MultiLine | Indented,
SingleLineTypeLiteralMembers = SingleLine | SpaceBetweenBraces | SpaceBetweenSiblings | Indented,
MultiLineTypeLiteralMembers = MultiLine | Indented,
TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented,
UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine,
IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine,
ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings,
ObjectBindingPatternElements = SingleLine | CommaDelimited | SpaceBetweenSiblings,
ObjectBindingPatternElementsWithSpaceBetweenBraces = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings,
ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings,
ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces,
ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets,
CommaListElements = CommaDelimited | SpaceBetweenSiblings | SingleLine,
CallExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis,
NewExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis | OptionalIfUndefined,
TemplateExpressionSpans = SingleLine | NoInterveningComments,
+567 -379
View File
File diff suppressed because it is too large. Load diff
+23 -10
View File
@@ -2,7 +2,6 @@
/// <reference path="diagnosticInformationMap.generated.ts" />
namespace ts {
/* @internal */
export function trace(host: ModuleResolutionHost, message: DiagnosticMessage, ...args: any[]): void;
export function trace(host: ModuleResolutionHost): void {
@@ -15,6 +14,7 @@ namespace ts {
}
/** Array that is only intended to be pushed to, never read. */
/* @internal */
export interface Push<T> {
push(value: T): void;
}
@@ -47,13 +47,11 @@ namespace ts {
return resolved.path;
}
/** Adds `isExernalLibraryImport` to a Resolved to get a ResolvedModule. */
function resolvedModuleFromResolved({ path, extension }: Resolved, isExternalLibraryImport: boolean): ResolvedModuleFull {
return { resolvedFileName: path, extension, isExternalLibraryImport };
}
function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
return { resolvedModule: resolved && resolvedModuleFromResolved(resolved, isExternalLibraryImport), failedLookupLocations };
return {
resolvedModule: resolved && { resolvedFileName: resolved.path, extension: resolved.extension, isExternalLibraryImport },
failedLookupLocations
};
}
export function moduleHasNonRelativeName(moduleName: string): boolean {
@@ -442,6 +440,8 @@ namespace ts {
case ModuleResolutionKind.Classic:
result = classicNameResolver(moduleName, containingFile, compilerOptions, host, cache);
break;
default:
Debug.fail(`Unexpected moduleResolution: ${moduleResolution}`);
}
if (perFolderCache) {
@@ -675,12 +675,25 @@ namespace ts {
}
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations {
return nodeModuleNameResolverWorker(moduleName, containingFile, compilerOptions, host, cache, /*jsOnly*/ false);
return nodeModuleNameResolverWorker(moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, /*jsOnly*/ false);
}
/**
* Expose resolution logic to allow us to use Node module resolution logic from arbitrary locations.
* No way to do this with `require()`: https://github.com/nodejs/node/issues/5963
* Throws an error if the module can't be resolved.
*/
/* @internal */
export function nodeModuleNameResolverWorker(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, jsOnly = false): ResolvedModuleWithFailedLookupLocations {
const containingDirectory = getDirectoryPath(containingFile);
export function resolveJavaScriptModule(moduleName: string, initialDir: string, host: ModuleResolutionHost): string {
const { resolvedModule, failedLookupLocations } =
nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ts.ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, /*jsOnly*/ true);
if (!resolvedModule) {
throw new Error(`Could not resolve JS module ${moduleName} starting at ${initialDir}. Looked in: ${failedLookupLocations.join(", ")}`);
}
return resolvedModule.resolvedFileName;
}
function nodeModuleNameResolverWorker(moduleName: string, containingDirectory: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache: ModuleResolutionCache | undefined, jsOnly: boolean): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
const failedLookupLocations: string[] = [];
+8 -4
View File
@@ -23,19 +23,19 @@ namespace ts {
}
}
function visitNode<T>(cbNode: (node: Node) => T, node: Node): T {
function visitNode<T>(cbNode: (node: Node) => T, node: Node): T | undefined {
if (node) {
return cbNode(node);
}
}
function visitNodeArray<T>(cbNodes: (nodes: Node[]) => T, nodes: Node[]) {
function visitNodeArray<T>(cbNodes: (nodes: Node[]) => T, nodes: Node[]): T | undefined {
if (nodes) {
return cbNodes(nodes);
}
}
function visitEachNode<T>(cbNode: (node: Node) => T, nodes: Node[]) {
function visitEachNode<T>(cbNode: (node: Node) => T, nodes: Node[]): T | undefined {
if (nodes) {
for (const node of nodes) {
const result = cbNode(node);
@@ -50,7 +50,7 @@ namespace ts {
// stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise,
// embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns
// a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned.
export function forEachChild<T>(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T {
export function forEachChild<T>(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T | undefined {
if (!node) {
return;
}
@@ -362,6 +362,8 @@ namespace ts {
return visitNode(cbNode, (<ExternalModuleReference>node).expression);
case SyntaxKind.MissingDeclaration:
return visitNodes(cbNodes, node.decorators);
case SyntaxKind.CommaListExpression:
return visitNodes(cbNodes, (<CommaListExpression>node).elements);
case SyntaxKind.JsxElement:
return visitNode(cbNode, (<JsxElement>node).openingElement) ||
@@ -6502,6 +6504,8 @@ namespace ts {
case "augments":
tag = parseAugmentsTag(atToken, tagName);
break;
case "arg":
case "argument":
case "param":
tag = parseParamTag(atToken, tagName);
break;
+223 -90
View File
@@ -241,6 +241,101 @@ namespace ts {
return output;
}
const redForegroundEscapeSequence = "\u001b[91m";
const yellowForegroundEscapeSequence = "\u001b[93m";
const blueForegroundEscapeSequence = "\u001b[93m";
const gutterStyleSequence = "\u001b[100;30m";
const gutterSeparator = " ";
const resetEscapeSequence = "\u001b[0m";
const ellipsis = "...";
function getCategoryFormat(category: DiagnosticCategory): string {
switch (category) {
case DiagnosticCategory.Warning: return yellowForegroundEscapeSequence;
case DiagnosticCategory.Error: return redForegroundEscapeSequence;
case DiagnosticCategory.Message: return blueForegroundEscapeSequence;
}
}
function formatAndReset(text: string, formatStyle: string) {
return formatStyle + text + resetEscapeSequence;
}
function padLeft(s: string, length: number) {
while (s.length < length) {
s = " " + s;
}
return s;
}
export function formatDiagnosticsWithColorAndContext(diagnostics: Diagnostic[], host: FormatDiagnosticsHost): string {
let output = "";
for (const diagnostic of diagnostics) {
if (diagnostic.file) {
const { start, length, file } = diagnostic;
const { line: firstLine, character: firstLineChar } = getLineAndCharacterOfPosition(file, start);
const { line: lastLine, character: lastLineChar } = getLineAndCharacterOfPosition(file, start + length);
const lastLineInFile = getLineAndCharacterOfPosition(file, file.text.length).line;
const relativeFileName = host ? convertToRelativePath(file.fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName)) : file.fileName;
const hasMoreThanFiveLines = (lastLine - firstLine) >= 4;
let gutterWidth = (lastLine + 1 + "").length;
if (hasMoreThanFiveLines) {
gutterWidth = Math.max(ellipsis.length, gutterWidth);
}
output += sys.newLine;
for (let i = firstLine; i <= lastLine; i++) {
// If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
// so we'll skip ahead to the second-to-last line.
if (hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1) {
output += formatAndReset(padLeft(ellipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + sys.newLine;
i = lastLine - 1;
}
const lineStart = getPositionOfLineAndCharacter(file, i, 0);
const lineEnd = i < lastLineInFile ? getPositionOfLineAndCharacter(file, i + 1, 0) : file.text.length;
let lineContent = file.text.slice(lineStart, lineEnd);
lineContent = lineContent.replace(/\s+$/g, ""); // trim from end
lineContent = lineContent.replace("\t", " "); // convert tabs to single spaces
// Output the gutter and the actual contents of the line.
output += formatAndReset(padLeft(i + 1 + "", gutterWidth), gutterStyleSequence) + gutterSeparator;
output += lineContent + sys.newLine;
// Output the gutter and the error span for the line using tildes.
output += formatAndReset(padLeft("", gutterWidth), gutterStyleSequence) + gutterSeparator;
output += redForegroundEscapeSequence;
if (i === firstLine) {
// If we're on the last line, then limit it to the last character of the last line.
// Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position.
const lastCharForLine = i === lastLine ? lastLineChar : undefined;
output += lineContent.slice(0, firstLineChar).replace(/\S/g, " ");
output += lineContent.slice(firstLineChar, lastCharForLine).replace(/./g, "~");
}
else if (i === lastLine) {
output += lineContent.slice(0, lastLineChar).replace(/./g, "~");
}
else {
// Squiggle the entire line.
output += lineContent.replace(/./g, "~");
}
output += resetEscapeSequence;
output += sys.newLine;
}
output += sys.newLine;
output += `${ relativeFileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `;
}
const categoryColor = getCategoryFormat(diagnostic.category);
const category = DiagnosticCategory[diagnostic.category].toLowerCase();
output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }`;
}
return output;
}
export function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string {
if (typeof messageText === "string") {
return messageText;
@@ -298,6 +393,7 @@ namespace ts {
let diagnosticsProducingTypeChecker: TypeChecker;
let noDiagnosticsTypeChecker: TypeChecker;
let classifiableNames: Map<string>;
let modifiedFilePaths: Path[] | undefined;
const cachedSemanticDiagnosticsForFile: DiagnosticCache = {};
const cachedDeclarationDiagnosticsForFile: DiagnosticCache = {};
@@ -367,7 +463,8 @@ namespace ts {
// used to track cases when two file names differ only in casing
const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createFileMap<SourceFile>(fileName => fileName.toLowerCase()) : undefined;
if (!tryReuseStructureFromOldProgram()) {
const structuralIsReused = tryReuseStructureFromOldProgram();
if (structuralIsReused !== StructureIsReused.Completely) {
forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false));
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
@@ -476,89 +573,114 @@ namespace ts {
}
interface OldProgramState {
program: Program;
program: Program | undefined;
file: SourceFile;
/** The collection of paths modified *since* the old program. */
modifiedFilePaths: Path[];
}
function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile, oldProgramState?: OldProgramState) {
if (!oldProgramState && !file.ambientModuleNames.length) {
// if old program state is not supplied and file does not contain locally defined ambient modules
// then the best we can do is fallback to the default logic
function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile, oldProgramState: OldProgramState) {
if (structuralIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
// the best we can do is fallback to the default logic.
return resolveModuleNamesWorker(moduleNames, containingFile);
}
// at this point we know that either
const oldSourceFile = oldProgramState.program && oldProgramState.program.getSourceFile(containingFile);
if (oldSourceFile !== file && file.resolvedModules) {
// `file` was created for the new program.
//
// We only set `file.resolvedModules` via work from the current function,
// so it is defined iff we already called the current function on `file`.
// That call happened no later than the creation of the `file` object,
// which per above occured during the current program creation.
// Since we assume the filesystem does not change during program creation,
// it is safe to reuse resolutions from the earlier call.
const result: ResolvedModuleFull[] = [];
for (const moduleName of moduleNames) {
const resolvedModule = file.resolvedModules.get(moduleName);
result.push(resolvedModule);
}
return result;
}
// At this point, we know at least one of the following hold:
// - file has local declarations for ambient modules
// OR
// - old program state is available
// OR
// - both of items above
// With this it is possible that we can tell how some module names from the initial list will be resolved
// without doing actual resolution (in particular if some name was resolved to ambient module).
// Such names should be excluded from the list of module names that will be provided to `resolveModuleNamesWorker`
// since we don't want to resolve them again.
// With this information, we can infer some module resolutions without performing resolution.
// this is a list of modules for which we cannot predict resolution so they should be actually resolved
/** An ordered list of module names for which we cannot recover the resolution. */
let unknownModuleNames: string[];
// this is a list of combined results assembles from predicted and resolved results.
// Order in this list matches the order in the original list of module names `moduleNames` which is important
// so later we can split results to resolutions of modules and resolutions of module augmentations.
/**
* The indexing of elements in this list matches that of `moduleNames`.
*
* Before combining results, result[i] is in one of the following states:
* * undefined: needs to be recomputed,
* * predictedToResolveToAmbientModuleMarker: known to be an ambient module.
* Needs to be reset to undefined before returning,
* * ResolvedModuleFull instance: can be reused.
*/
let result: ResolvedModuleFull[];
// a transient placeholder that is used to mark predicted resolution in the result list
/** A transient placeholder used to mark predicted resolution in the result list. */
const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = <any>{};
for (let i = 0; i < moduleNames.length; i++) {
const moduleName = moduleNames[i];
// module name is known to be resolved to ambient module if
// - module name is contained in the list of ambient modules that are locally declared in the file
// - in the old program module name was resolved to ambient module whose declaration is in non-modified file
// If we want to reuse resolutions more aggressively, we can refine this to check for whether the
// text of the corresponding modulenames has changed.
if (file === oldSourceFile) {
const oldResolvedModule = oldSourceFile && oldSourceFile.resolvedModules.get(moduleName);
if (oldResolvedModule) {
if (isTraceEnabled(options, host)) {
trace(host, Diagnostics.Reusing_resolution_of_module_0_to_file_1_from_old_program, moduleName, containingFile);
}
(result || (result = new Array(moduleNames.length)))[i] = oldResolvedModule;
continue;
}
}
// We know moduleName resolves to an ambient module provided that moduleName:
// - is in the list of ambient modules locally declared in the current source file.
// - resolved to an ambient module in the old program whose declaration is in an unmodified file
// (so the same module declaration will land in the new program)
let isKnownToResolveToAmbientModule = false;
let resolvesToAmbientModuleInNonModifiedFile = false;
if (contains(file.ambientModuleNames, moduleName)) {
isKnownToResolveToAmbientModule = true;
resolvesToAmbientModuleInNonModifiedFile = true;
if (isTraceEnabled(options, host)) {
trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, containingFile);
}
}
else {
isKnownToResolveToAmbientModule = checkModuleNameResolvedToAmbientModuleInNonModifiedFile(moduleName, oldProgramState);
resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName, oldProgramState);
}
if (isKnownToResolveToAmbientModule) {
if (!unknownModuleNames) {
// found a first module name for which result can be prediced
// this means that this module name should not be passed to `resolveModuleNamesWorker`.
// We'll use a separate list for module names that are definitely unknown.
result = new Array(moduleNames.length);
// copy all module names that appear before the current one in the list
// since they are known to be unknown
unknownModuleNames = moduleNames.slice(0, i);
}
// mark prediced resolution in the result list
result[i] = predictedToResolveToAmbientModuleMarker;
if (resolvesToAmbientModuleInNonModifiedFile) {
(result || (result = new Array(moduleNames.length)))[i] = predictedToResolveToAmbientModuleMarker;
}
else if (unknownModuleNames) {
// found unknown module name and we are already using separate list for those - add it to the list
unknownModuleNames.push(moduleName);
else {
// Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result.
(unknownModuleNames || (unknownModuleNames = [])).push(moduleName);
}
}
if (!unknownModuleNames) {
// we've looked throught the list but have not seen any predicted resolution
// use default logic
return resolveModuleNamesWorker(moduleNames, containingFile);
}
const resolutions = unknownModuleNames.length
const resolutions = unknownModuleNames && unknownModuleNames.length
? resolveModuleNamesWorker(unknownModuleNames, containingFile)
: emptyArray;
// combine results of resolutions and predicted results
// Combine results of resolutions and predicted results
if (!result) {
// There were no unresolved/ambient resolutions.
Debug.assert(resolutions.length === moduleNames.length);
return <ResolvedModuleFull[]>resolutions;
}
let j = 0;
for (let i = 0; i < result.length; i++) {
if (result[i] === predictedToResolveToAmbientModuleMarker) {
result[i] = undefined;
if (result[i]) {
// `result[i]` is either a `ResolvedModuleFull` or a marker.
// If it is the former, we can leave it as is.
if (result[i] === predictedToResolveToAmbientModuleMarker) {
result[i] = undefined;
}
}
else {
result[i] = resolutions[j];
@@ -566,18 +688,18 @@ namespace ts {
}
}
Debug.assert(j === resolutions.length);
return result;
function checkModuleNameResolvedToAmbientModuleInNonModifiedFile(moduleName: string, oldProgramState?: OldProgramState): boolean {
if (!oldProgramState) {
return false;
}
// If we change our policy of rechecking failed lookups on each program create,
// we should adjust the value returned here.
function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string, oldProgramState: OldProgramState): boolean {
const resolutionToFile = getResolvedModule(oldProgramState.file, moduleName);
if (resolutionToFile) {
// module used to be resolved to file - ignore it
return false;
}
const ambientModule = oldProgram.getTypeChecker().tryFindAmbientModuleWithoutAugmentations(moduleName);
const ambientModule = oldProgramState.program && oldProgramState.program.getTypeChecker().tryFindAmbientModuleWithoutAugmentations(moduleName);
if (!(ambientModule && ambientModule.declarations)) {
return false;
}
@@ -599,99 +721,107 @@ namespace ts {
}
}
function tryReuseStructureFromOldProgram(): boolean {
function tryReuseStructureFromOldProgram(): StructureIsReused {
if (!oldProgram) {
return false;
return StructureIsReused.Not;
}
// check properties that can affect structure of the program or module resolution strategy
// if any of these properties has changed - structure cannot be reused
const oldOptions = oldProgram.getCompilerOptions();
if (changesAffectModuleResolution(oldOptions, options)) {
return false;
return oldProgram.structureIsReused = StructureIsReused.Not;
}
Debug.assert(!oldProgram.structureIsReused);
Debug.assert(!(oldProgram.structureIsReused & (StructureIsReused.Completely | StructureIsReused.SafeModules)));
// there is an old program, check if we can reuse its structure
const oldRootNames = oldProgram.getRootFileNames();
if (!arrayIsEqualTo(oldRootNames, rootNames)) {
return false;
return oldProgram.structureIsReused = StructureIsReused.Not;
}
if (!arrayIsEqualTo(options.types, oldOptions.types)) {
return false;
return oldProgram.structureIsReused = StructureIsReused.Not;
}
// check if program source files has changed in the way that can affect structure of the program
const newSourceFiles: SourceFile[] = [];
const filePaths: Path[] = [];
const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
oldProgram.structureIsReused = StructureIsReused.Completely;
for (const oldSourceFile of oldProgram.getSourceFiles()) {
let newSourceFile = host.getSourceFileByPath
const newSourceFile = host.getSourceFileByPath
? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target)
: host.getSourceFile(oldSourceFile.fileName, options.target);
if (!newSourceFile) {
return false;
return oldProgram.structureIsReused = StructureIsReused.Not;
}
newSourceFile.path = oldSourceFile.path;
filePaths.push(newSourceFile.path);
if (oldSourceFile !== newSourceFile) {
// The `newSourceFile` object was created for the new program.
if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
// value of no-default-lib has changed
// this will affect if default library is injected into the list of files
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}
// check tripleslash references
if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
// tripleslash references has changed
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}
// check imports and module augmentations
collectExternalModuleReferences(newSourceFile);
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
// imports has changed
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}
if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) {
// moduleAugmentations has changed
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}
if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
// 'types' references has changed
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}
// tentatively approve the file
modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
}
else {
// file has no changes - use it as is
newSourceFile = oldSourceFile;
}
// if file has passed all checks it should be safe to reuse it
newSourceFiles.push(newSourceFile);
}
const modifiedFilePaths = modifiedSourceFiles.map(f => f.newFile.path);
if (oldProgram.structureIsReused !== StructureIsReused.Completely) {
return oldProgram.structureIsReused;
}
modifiedFilePaths = modifiedSourceFiles.map(f => f.newFile.path);
// try to verify results of module resolution
for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.fileName, currentDirectory);
if (resolveModuleNamesWorker) {
const moduleNames = map(concatenate(newSourceFile.imports, newSourceFile.moduleAugmentations), getTextOfLiteral);
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile, { file: oldSourceFile, program: oldProgram, modifiedFilePaths });
const oldProgramState = { program: oldProgram, file: oldSourceFile, modifiedFilePaths };
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile, oldProgramState);
// ensure that module resolution results are still correct
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
if (resolutionsChanged) {
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
}
else {
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
}
}
if (resolveTypeReferenceDirectiveNamesWorker) {
@@ -700,12 +830,17 @@ namespace ts {
// ensure that types resolutions are still correct
const resolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, resolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
if (resolutionsChanged) {
return false;
oldProgram.structureIsReused = StructureIsReused.SafeModules;
newSourceFile.resolvedTypeReferenceDirectiveNames = zipToMap(typesReferenceDirectives, resolutions);
}
else {
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
}
}
// pass the cache of module/types resolutions from the old source file
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
}
if (oldProgram.structureIsReused !== StructureIsReused.Completely) {
return oldProgram.structureIsReused;
}
// update fileName -> file mapping
@@ -720,9 +855,8 @@ namespace ts {
fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile.newFile);
}
resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
oldProgram.structureIsReused = true;
return true;
return oldProgram.structureIsReused = StructureIsReused.Completely;
}
function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
@@ -1175,7 +1309,7 @@ namespace ts {
}
function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
return isDeclarationFile(sourceFile) ? [] : getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
return sourceFile.isDeclarationFile ? [] : getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
}
function getOptionsDiagnostics(): Diagnostic[] {
@@ -1214,7 +1348,6 @@ namespace ts {
const isJavaScriptFile = isSourceFileJavaScript(file);
const isExternalModuleFile = isExternalModule(file);
const isDtsFile = isDeclarationFile(file);
let imports: LiteralExpression[];
let moduleAugmentations: LiteralExpression[];
@@ -1267,7 +1400,7 @@ namespace ts {
}
break;
case SyntaxKind.ModuleDeclaration:
if (isAmbientModule(<ModuleDeclaration>node) && (inAmbientModule || hasModifier(node, ModifierFlags.Ambient) || isDeclarationFile(file))) {
if (isAmbientModule(<ModuleDeclaration>node) && (inAmbientModule || hasModifier(node, ModifierFlags.Ambient) || file.isDeclarationFile)) {
const moduleName = <LiteralExpression>(<ModuleDeclaration>node).name;
// Ambient module declarations can be interpreted as augmentations for some existing external modules.
// This will happen in two cases:
@@ -1278,7 +1411,7 @@ namespace ts {
(moduleAugmentations || (moduleAugmentations = [])).push(moduleName);
}
else if (!inAmbientModule) {
if (isDtsFile) {
if (file.isDeclarationFile) {
// for global .d.ts files record name of ambient module
(ambientModules || (ambientModules = [])).push(moduleName.text);
}
@@ -1528,11 +1661,11 @@ namespace ts {
function processImportedModules(file: SourceFile) {
collectExternalModuleReferences(file);
if (file.imports.length || file.moduleAugmentations.length) {
file.resolvedModules = createMap<ResolvedModuleFull>();
// Because global augmentation doesn't have string literal name, we can check for global augmentation as such.
const nonGlobalAugmentation = filter(file.moduleAugmentations, (moduleAugmentation) => moduleAugmentation.kind === SyntaxKind.StringLiteral);
const moduleNames = map(concatenate(file.imports, nonGlobalAugmentation), getTextOfLiteral);
const resolutions = resolveModuleNamesReusingOldState(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory), file);
const oldProgramState = { program: oldProgram, file, modifiedFilePaths };
const resolutions = resolveModuleNamesReusingOldState(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory), file, oldProgramState);
Debug.assert(resolutions.length === moduleNames.length);
for (let i = 0; i < moduleNames.length; i++) {
const resolution = resolutions[i];
@@ -1596,7 +1729,7 @@ namespace ts {
const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
for (const sourceFile of sourceFiles) {
if (!isDeclarationFile(sourceFile)) {
if (!sourceFile.isDeclarationFile) {
const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, sourceFile.fileName, options.rootDir));
@@ -1709,13 +1842,13 @@ namespace ts {
const languageVersion = options.target || ScriptTarget.ES3;
const outFile = options.outFile || options.out;
const firstNonAmbientExternalModuleSourceFile = forEach(files, f => isExternalModule(f) && !isDeclarationFile(f) ? f : undefined);
const firstNonAmbientExternalModuleSourceFile = forEach(files, f => isExternalModule(f) && !f.isDeclarationFile ? f : undefined);
if (options.isolatedModules) {
if (options.module === ModuleKind.None && languageVersion < ScriptTarget.ES2015) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher));
}
const firstNonExternalModuleSourceFile = forEach(files, f => !isExternalModule(f) && !isDeclarationFile(f) ? f : undefined);
const firstNonExternalModuleSourceFile = forEach(files, f => !isExternalModule(f) && !f.isDeclarationFile ? f : undefined);
if (firstNonExternalModuleSourceFile) {
const span = getErrorSpanForNode(firstNonExternalModuleSourceFile, firstNonExternalModuleSourceFile);
programDiagnostics.add(createFileDiagnostic(firstNonExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_compile_namespaces_when_the_isolatedModules_flag_is_provided));
+7 -6
View File
@@ -712,11 +712,11 @@ namespace ts {
return accumulator;
}
export function forEachLeadingCommentRange<T, U>(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state?: T) {
export function forEachLeadingCommentRange<T, U>(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state?: T): U | undefined {
return iterateCommentRanges(/*reduce*/ false, text, pos, /*trailing*/ false, cb, state);
}
export function forEachTrailingCommentRange<T, U>(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state?: T) {
export function forEachTrailingCommentRange<T, U>(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state?: T): U | undefined {
return iterateCommentRanges(/*reduce*/ false, text, pos, /*trailing*/ true, cb, state);
}
@@ -746,10 +746,11 @@ namespace ts {
}
/** Optionally, get the shebang */
export function getShebang(text: string): string {
return shebangTriviaRegex.test(text)
? shebangTriviaRegex.exec(text)[0]
: undefined;
export function getShebang(text: string): string | undefined {
const match = shebangTriviaRegex.exec(text);
if (match) {
return match[0];
}
}
export function isIdentifierStart(ch: number, languageVersion: ScriptTarget): boolean {
+1 -1
View File
@@ -165,7 +165,7 @@ namespace ts {
};
function transformRoot(node: T) {
return node && (!isSourceFile(node) || !isDeclarationFile(node)) ? transformation(node) : node;
return node && (!isSourceFile(node) || !node.isDeclarationFile) ? transformation(node) : node;
}
/**
+53 -12
View File
@@ -295,7 +295,7 @@ namespace ts {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
if (node.isDeclarationFile) {
return node;
}
@@ -813,7 +813,7 @@ namespace ts {
// Create a synthetic text range for the return statement.
const closingBraceLocation = createTokenRange(skipTrivia(currentText, node.members.end), SyntaxKind.CloseBraceToken);
const localName = getLocalName(node);
const localName = getInternalName(node);
// The following partially-emitted expression exists purely to align our sourcemap
// emit with the original emitter.
@@ -870,7 +870,7 @@ namespace ts {
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*asteriskToken*/ undefined,
getDeclarationName(node),
getInternalName(node),
/*typeParameters*/ undefined,
transformConstructorParameters(constructor, hasSynthesizedSuper),
/*type*/ undefined,
@@ -2009,13 +2009,14 @@ namespace ts {
}
else {
assignment = createBinary(<Identifier>decl.name, SyntaxKind.EqualsToken, visitNode(decl.initializer, visitor, isExpression));
setTextRange(assignment, decl);
}
assignments = append(assignments, assignment);
}
}
if (assignments) {
updated = setTextRange(createStatement(reduceLeft(assignments, (acc, v) => createBinary(v, SyntaxKind.CommaToken, acc))), node);
updated = setTextRange(createStatement(inlineExpressions(assignments)), node);
}
else {
// none of declarations has initializer - the entire variable statement can be deleted
@@ -3180,7 +3181,20 @@ namespace ts {
const savedConvertedLoopState = convertedLoopState;
convertedLoopState = undefined;
const ancestorFacts = enterSubtree(HierarchyFacts.FunctionExcludes, HierarchyFacts.FunctionIncludes);
const updated = visitEachChild(node, visitor, context);
let updated: AccessorDeclaration;
if (node.transformFlags & TransformFlags.ContainsCapturedLexicalThis) {
const parameters = visitParameterList(node.parameters, visitor, context);
const body = transformFunctionBody(node);
if (node.kind === SyntaxKind.GetAccessor) {
updated = updateGetAccessor(node, node.decorators, node.modifiers, node.name, parameters, node.type, body);
}
else {
updated = updateSetAccessor(node, node.decorators, node.modifiers, node.name, parameters, body);
}
}
else {
updated = visitEachChild(node, visitor, context);
}
exitSubtree(ancestorFacts, HierarchyFacts.PropagateNewTargetMask, HierarchyFacts.None);
convertedLoopState = savedConvertedLoopState;
return updated;
@@ -3712,7 +3726,7 @@ namespace ts {
function substituteIdentifier(node: Identifier) {
// Only substitute the identifier if we have enabled substitutions for block-scoped
// bindings.
if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings) {
if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings && !isInternalName(node)) {
const original = getParseTreeNode(node, isIdentifier);
if (original && isNameOfDeclarationWithCollidingName(original)) {
return setTextRange(getGeneratedNameForNode(original), node);
@@ -3735,7 +3749,7 @@ namespace ts {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.VariableDeclaration:
return (<Declaration>parent).name === node
return (<NamedDeclaration>parent).name === node
&& resolver.isDeclarationWithCollidingName(<Declaration>parent);
}
@@ -3765,16 +3779,42 @@ namespace ts {
* @param node An Identifier node.
*/
function substituteExpressionIdentifier(node: Identifier): Identifier {
if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings) {
if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings && !isInternalName(node)) {
const declaration = resolver.getReferencedDeclarationWithCollidingName(node);
if (declaration) {
return setTextRange(getGeneratedNameForNode(declaration.name), node);
if (declaration && !(isClassLike(declaration) && isPartOfClassBody(declaration, node))) {
return setTextRange(getGeneratedNameForNode(getNameOfDeclaration(declaration)), node);
}
}
return node;
}
function isPartOfClassBody(declaration: ClassLikeDeclaration, node: Identifier) {
let currentNode = getParseTreeNode(node);
if (!currentNode || currentNode === declaration || currentNode.end <= declaration.pos || currentNode.pos >= declaration.end) {
// if the node has no correlation to a parse tree node, its definitely not
// part of the body.
// if the node is outside of the document range of the declaration, its
// definitely not part of the body.
return false;
}
const blockScope = getEnclosingBlockScopeContainer(declaration);
while (currentNode) {
if (currentNode === blockScope || currentNode === declaration) {
// if we are in the enclosing block scope of the declaration, we are definitely
// not inside the class body.
return false;
}
if (isClassElement(currentNode) && currentNode.parent === declaration) {
// we are in the class body, but we treat static fields as outside of the class body
return currentNode.kind !== SyntaxKind.PropertyDeclaration
|| (getModifierFlags(currentNode) & ModifierFlags.Static) === 0;
}
currentNode = currentNode.parent;
}
return false;
}
/**
* Substitutes `this` when contained within an arrow function.
*
@@ -3789,8 +3829,9 @@ namespace ts {
}
function getClassMemberPrefix(node: ClassExpression | ClassDeclaration, member: ClassElement) {
const expression = getLocalName(node);
return hasModifier(member, ModifierFlags.Static) ? expression : createPropertyAccess(expression, "prototype");
return hasModifier(member, ModifierFlags.Static)
? getInternalName(node)
: createPropertyAccess(getInternalName(node), "prototype");
}
function hasSynthesizedDefaultSuperCall(constructor: ConstructorDeclaration, hasExtendsClause: boolean) {
+1 -1
View File
@@ -9,7 +9,7 @@ namespace ts {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
if (node.isDeclarationFile) {
return node;
}
+1 -1
View File
@@ -47,7 +47,7 @@ namespace ts {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
if (node.isDeclarationFile) {
return node;
}
+65 -59
View File
@@ -33,7 +33,7 @@ namespace ts {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
if (node.isDeclarationFile) {
return node;
}
@@ -106,15 +106,11 @@ namespace ts {
}
}
function visitAwaitExpression(node: AwaitExpression) {
function visitAwaitExpression(node: AwaitExpression): Expression {
if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) {
const expression = visitNode(node.expression, visitor, isExpression);
return setOriginalNode(
setTextRange(
createYield(
/*asteriskToken*/ undefined,
createArrayLiteral([createLiteral("await"), expression])
),
createYield(createAwaitHelper(context, visitNode(node.expression, visitor, isExpression))),
/*location*/ node
),
node
@@ -124,18 +120,26 @@ namespace ts {
}
function visitYieldExpression(node: YieldExpression) {
if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) {
if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator && node.asteriskToken) {
const expression = visitNode(node.expression, visitor, isExpression);
return updateYield(
node,
node.asteriskToken,
node.asteriskToken
? createAsyncDelegatorHelper(context, expression, expression)
: createArrayLiteral(
expression
? [createLiteral("yield"), expression]
: [createLiteral("yield")]
)
return setOriginalNode(
setTextRange(
createYield(
createAwaitHelper(context,
updateYield(
node,
node.asteriskToken,
createAsyncDelegatorHelper(
context,
createAsyncValuesHelper(context, expression, expression),
expression
)
)
)
),
node
),
node
);
}
return visitEachChild(node, visitor, context);
@@ -347,6 +351,10 @@ namespace ts {
);
}
function awaitAsYield(expression: Expression) {
return createYield(/*asteriskToken*/ undefined, enclosingFunctionFlags & FunctionFlags.Generator ? createAwaitHelper(context, expression) : expression);
}
function transformForAwaitOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement) {
const expression = visitNode(node.expression, visitor, isExpression);
const iterator = isIdentifier(expression) ? getGeneratedNameForNode(expression) : createTempVariable(/*recordTempVariable*/ undefined);
@@ -354,16 +362,11 @@ namespace ts {
const errorRecord = createUniqueName("e");
const catchVariable = getGeneratedNameForNode(errorRecord);
const returnMethod = createTempVariable(/*recordTempVariable*/ undefined);
const values = createAsyncValuesHelper(context, expression, /*location*/ node.expression);
const next = createYield(
/*asteriskToken*/ undefined,
enclosingFunctionFlags & FunctionFlags.Generator
? createArrayLiteral([
createLiteral("await"),
createCall(createPropertyAccess(iterator, "next" ), /*typeArguments*/ undefined, [])
])
: createCall(createPropertyAccess(iterator, "next" ), /*typeArguments*/ undefined, [])
);
const callValues = createAsyncValuesHelper(context, expression, /*location*/ node.expression);
const callNext = createCall(createPropertyAccess(iterator, "next" ), /*typeArguments*/ undefined, []);
const getDone = createPropertyAccess(result, "done");
const getValue = createPropertyAccess(result, "value");
const callReturn = createFunctionCall(returnMethod, iterator, []);
hoistVariableDeclaration(errorRecord);
hoistVariableDeclaration(returnMethod);
@@ -374,16 +377,19 @@ namespace ts {
/*initializer*/ setEmitFlags(
setTextRange(
createVariableDeclarationList([
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, values), node.expression),
createVariableDeclaration(result, /*type*/ undefined, next)
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, callValues), node.expression),
createVariableDeclaration(result)
]),
node.expression
),
EmitFlags.NoHoisting
),
/*condition*/ createLogicalNot(createPropertyAccess(result, "done")),
/*incrementor*/ createAssignment(result, next),
/*statement*/ convertForOfStatementHead(node, createPropertyAccess(result, "value"))
/*condition*/ createComma(
createAssignment(result, awaitAsYield(callNext)),
createLogicalNot(getDone)
),
/*incrementor*/ undefined,
/*statement*/ convertForOfStatementHead(node, awaitAsYield(getValue))
),
/*location*/ node
),
@@ -421,26 +427,14 @@ namespace ts {
createLogicalAnd(
createLogicalAnd(
result,
createLogicalNot(
createPropertyAccess(result, "done")
)
createLogicalNot(getDone)
),
createAssignment(
returnMethod,
createPropertyAccess(iterator, "return")
)
),
createStatement(
createYield(
/*asteriskToken*/ undefined,
enclosingFunctionFlags & FunctionFlags.Generator
? createArrayLiteral([
createLiteral("await"),
createFunctionCall(returnMethod, iterator, [])
])
: createFunctionCall(returnMethod, iterator, [])
)
)
createStatement(awaitAsYield(callReturn))
),
EmitFlags.SingleLine
)
@@ -880,27 +874,39 @@ namespace ts {
);
}
const awaitHelper: EmitHelper = {
name: "typescript:await",
scoped: false,
text: `
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
`
};
function createAwaitHelper(context: TransformationContext, expression: Expression) {
context.requestEmitHelper(awaitHelper);
return createCall(getHelperName("__await"), /*typeArguments*/ undefined, [expression]);
}
const asyncGeneratorHelper: EmitHelper = {
name: "typescript:asyncGenerator",
scoped: false,
text: `
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), q = [], c, i;
return i = { next: verb("next"), "throw": verb("throw"), "return": verb("return") }, i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { return function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]), next(); }); }; }
function next() { if (!c && q.length) resume((c = q.shift())[0], c[1]); }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(c[3], e); } }
function step(r) { r.done ? settle(c[2], r) : Promise.resolve(r.value[1]).then(r.value[0] === "yield" ? send : fulfill, reject); }
function send(value) { settle(c[2], { value: value, done: false }); }
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { c = void 0, f(v), next(); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
`
};
function createAsyncGeneratorHelper(context: TransformationContext, generatorFunc: FunctionExpression) {
context.requestEmitHelper(awaitHelper);
context.requestEmitHelper(asyncGeneratorHelper);
// Mark this node as originally an async function
@@ -922,16 +928,16 @@ namespace ts {
scoped: false,
text: `
var __asyncDelegator = (this && this.__asyncDelegator) || function (o) {
var i = { next: verb("next"), "throw": verb("throw", function (e) { throw e; }), "return": verb("return", function (v) { return { value: v, done: true }; }) }, p;
return o = __asyncValues(o), i[Symbol.iterator] = function () { return this; }, i;
function verb(n, f) { return function (v) { return v = p && n === "throw" ? f(v) : p && v.done ? v : { value: p ? ["yield", v.value] : ["await", (o[n] || f).call(o, v)], done: false }, p = !p, v; }; }
var i, p;
return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; }
};
`
};
function createAsyncDelegatorHelper(context: TransformationContext, expression: Expression, location?: TextRange) {
context.requestEmitHelper(awaitHelper);
context.requestEmitHelper(asyncDelegator);
context.requestEmitHelper(asyncValues);
return setTextRange(
createCall(
getHelperName("__asyncDelegator"),
+1 -2
View File
@@ -293,8 +293,7 @@ namespace ts {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)
|| (node.transformFlags & TransformFlags.ContainsGenerator) === 0) {
if (node.isDeclarationFile || (node.transformFlags & TransformFlags.ContainsGenerator) === 0) {
return node;
}
+1 -1
View File
@@ -15,7 +15,7 @@ namespace ts {
* @param node A SourceFile node.
*/
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
if (node.isDeclarationFile) {
return node;
}
+1 -1
View File
@@ -16,7 +16,7 @@ namespace ts {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
if (node.isDeclarationFile) {
return node;
}
+1 -1
View File
@@ -55,7 +55,7 @@ namespace ts {
* @param node The SourceFile node.
*/
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node) || !(isExternalModule(node) || compilerOptions.isolatedModules)) {
if (node.isDeclarationFile || !(isExternalModule(node) || compilerOptions.isolatedModules)) {
return node;
}
+1 -3
View File
@@ -50,9 +50,7 @@ namespace ts {
* @param node The SourceFile node.
*/
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)
|| !(isExternalModule(node)
|| compilerOptions.isolatedModules)) {
if (node.isDeclarationFile || !(isExternalModule(node) || compilerOptions.isolatedModules)) {
return node;
}
+11 -13
View File
@@ -76,7 +76,7 @@ namespace ts {
* @param node A SourceFile node.
*/
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
if (node.isDeclarationFile) {
return node;
}
@@ -1762,23 +1762,19 @@ namespace ts {
}
function serializeUnionOrIntersectionType(node: UnionOrIntersectionTypeNode): SerializedTypeNode {
// Note when updating logic here also update getEntityNameForDecoratorMetadata
// so that aliases can be marked as referenced
let serializedUnion: SerializedTypeNode;
for (const typeNode of node.types) {
const serializedIndividual = serializeTypeNode(typeNode);
if (isVoidExpression(serializedIndividual)) {
// If we dont have any other type already set, set the initial type
if (!serializedUnion) {
serializedUnion = serializedIndividual;
}
}
else if (isIdentifier(serializedIndividual) && serializedIndividual.text === "Object") {
if (isIdentifier(serializedIndividual) && serializedIndividual.text === "Object") {
// One of the individual is global object, return immediately
return serializedIndividual;
}
// If there exists union that is not void 0 expression, check if the the common type is identifier.
// anything more complex and we will just default to Object
else if (serializedUnion && !isVoidExpression(serializedUnion)) {
else if (serializedUnion) {
// Different types
if (!isIdentifier(serializedUnion) ||
!isIdentifier(serializedIndividual) ||
@@ -2606,14 +2602,16 @@ namespace ts {
* Adds a leading VariableStatement for a enum or module declaration.
*/
function addVarForEnumOrModuleDeclaration(statements: Statement[], node: ModuleDeclaration | EnumDeclaration) {
// Emit a variable statement for the module.
// Emit a variable statement for the module. We emit top-level enums as a `var`
// declaration to avoid static errors in global scripts scripts due to redeclaration.
// enums in any other scope are emitted as a `let` declaration.
const statement = createVariableStatement(
visitNodes(node.modifiers, modifierVisitor, isModifier),
[
createVariableDeclarationList([
createVariableDeclaration(
getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true)
)
]
], currentScope.kind === SyntaxKind.SourceFile ? NodeFlags.None : NodeFlags.Let)
);
setOriginalNode(statement, node);
@@ -2925,7 +2923,7 @@ namespace ts {
function visitExportDeclaration(node: ExportDeclaration): VisitResult<Statement> {
if (!node.exportClause) {
// Elide a star export if the module it references does not export a value.
return resolver.moduleExportsSomeValue(node.moduleSpecifier) ? node : undefined;
return compilerOptions.isolatedModules || resolver.moduleExportsSomeValue(node.moduleSpecifier) ? node : undefined;
}
if (!resolver.isValueAliasDeclaration(node)) {
+1 -86
View File
@@ -60,93 +60,8 @@ namespace ts {
sys.write(ts.formatDiagnostics([diagnostic], host));
}
const redForegroundEscapeSequence = "\u001b[91m";
const yellowForegroundEscapeSequence = "\u001b[93m";
const blueForegroundEscapeSequence = "\u001b[93m";
const gutterStyleSequence = "\u001b[100;30m";
const gutterSeparator = " ";
const resetEscapeSequence = "\u001b[0m";
const ellipsis = "...";
function getCategoryFormat(category: DiagnosticCategory): string {
switch (category) {
case DiagnosticCategory.Warning: return yellowForegroundEscapeSequence;
case DiagnosticCategory.Error: return redForegroundEscapeSequence;
case DiagnosticCategory.Message: return blueForegroundEscapeSequence;
}
}
function formatAndReset(text: string, formatStyle: string) {
return formatStyle + text + resetEscapeSequence;
}
function reportDiagnosticWithColorAndContext(diagnostic: Diagnostic, host: FormatDiagnosticsHost): void {
let output = "";
if (diagnostic.file) {
const { start, length, file } = diagnostic;
const { line: firstLine, character: firstLineChar } = getLineAndCharacterOfPosition(file, start);
const { line: lastLine, character: lastLineChar } = getLineAndCharacterOfPosition(file, start + length);
const lastLineInFile = getLineAndCharacterOfPosition(file, file.text.length).line;
const relativeFileName = host ? convertToRelativePath(file.fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName)) : file.fileName;
const hasMoreThanFiveLines = (lastLine - firstLine) >= 4;
let gutterWidth = (lastLine + 1 + "").length;
if (hasMoreThanFiveLines) {
gutterWidth = Math.max(ellipsis.length, gutterWidth);
}
output += sys.newLine;
for (let i = firstLine; i <= lastLine; i++) {
// If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
// so we'll skip ahead to the second-to-last line.
if (hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1) {
output += formatAndReset(padLeft(ellipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + sys.newLine;
i = lastLine - 1;
}
const lineStart = getPositionOfLineAndCharacter(file, i, 0);
const lineEnd = i < lastLineInFile ? getPositionOfLineAndCharacter(file, i + 1, 0) : file.text.length;
let lineContent = file.text.slice(lineStart, lineEnd);
lineContent = lineContent.replace(/\s+$/g, ""); // trim from end
lineContent = lineContent.replace("\t", " "); // convert tabs to single spaces
// Output the gutter and the actual contents of the line.
output += formatAndReset(padLeft(i + 1 + "", gutterWidth), gutterStyleSequence) + gutterSeparator;
output += lineContent + sys.newLine;
// Output the gutter and the error span for the line using tildes.
output += formatAndReset(padLeft("", gutterWidth), gutterStyleSequence) + gutterSeparator;
output += redForegroundEscapeSequence;
if (i === firstLine) {
// If we're on the last line, then limit it to the last character of the last line.
// Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position.
const lastCharForLine = i === lastLine ? lastLineChar : undefined;
output += lineContent.slice(0, firstLineChar).replace(/\S/g, " ");
output += lineContent.slice(firstLineChar, lastCharForLine).replace(/./g, "~");
}
else if (i === lastLine) {
output += lineContent.slice(0, lastLineChar).replace(/./g, "~");
}
else {
// Squiggle the entire line.
output += lineContent.replace(/./g, "~");
}
output += resetEscapeSequence;
output += sys.newLine;
}
output += sys.newLine;
output += `${ relativeFileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `;
}
const categoryColor = getCategoryFormat(diagnostic.category);
const category = DiagnosticCategory[diagnostic.category].toLowerCase();
output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }`;
output += sys.newLine + sys.newLine;
sys.write(output);
sys.write(ts.formatDiagnosticsWithColorAndContext([diagnostic], host) + sys.newLine + sys.newLine);
}
function reportWatchDiagnostic(diagnostic: Diagnostic) {
+120 -66
View File
@@ -389,6 +389,7 @@ namespace ts {
// Transformation nodes
NotEmittedStatement,
PartiallyEmittedExpression,
CommaListExpression,
MergeDeclarationMarker,
EndOfDeclarationMarker,
@@ -571,11 +572,16 @@ namespace ts {
export interface Identifier extends PrimaryExpression {
kind: SyntaxKind.Identifier;
text: string; // Text of identifier (with escapes converted to characters)
originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later
/**
* Text of identifier (with escapes converted to characters).
* If the identifier begins with two underscores, this will begin with three.
*/
text: string;
originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later
/*@internal*/ autoGenerateKind?: GeneratedIdentifierKind; // Specifies whether to auto-generate the text for an identifier.
/*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name.
isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace
/*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name.
isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace
/*@internal*/ typeArguments?: NodeArray<TypeNode>; // Only defined on synthesized nodes. Though not syntactically valid, used in emitting diagnostics.
}
// Transient identifier node (marked by id === -1)
@@ -605,10 +611,13 @@ namespace ts {
export interface Declaration extends Node {
_declarationBrand: any;
}
export interface NamedDeclaration extends Declaration {
name?: DeclarationName;
}
export interface DeclarationStatement extends Declaration, Statement {
export interface DeclarationStatement extends NamedDeclaration, Statement {
name?: Identifier | StringLiteral | NumericLiteral;
}
@@ -622,7 +631,7 @@ namespace ts {
expression: LeftHandSideExpression;
}
export interface TypeParameterDeclaration extends Declaration {
export interface TypeParameterDeclaration extends NamedDeclaration {
kind: SyntaxKind.TypeParameter;
parent?: DeclarationWithTypeParameters;
name: Identifier;
@@ -633,7 +642,7 @@ namespace ts {
expression?: Expression;
}
export interface SignatureDeclaration extends Declaration {
export interface SignatureDeclaration extends NamedDeclaration {
name?: PropertyName;
typeParameters?: NodeArray<TypeParameterDeclaration>;
parameters: NodeArray<ParameterDeclaration>;
@@ -650,7 +659,7 @@ namespace ts {
export type BindingName = Identifier | BindingPattern;
export interface VariableDeclaration extends Declaration {
export interface VariableDeclaration extends NamedDeclaration {
kind: SyntaxKind.VariableDeclaration;
parent?: VariableDeclarationList | CatchClause;
name: BindingName; // Declared variable name
@@ -664,7 +673,7 @@ namespace ts {
declarations: NodeArray<VariableDeclaration>;
}
export interface ParameterDeclaration extends Declaration {
export interface ParameterDeclaration extends NamedDeclaration {
kind: SyntaxKind.Parameter;
parent?: SignatureDeclaration;
dotDotDotToken?: DotDotDotToken; // Present on rest parameter
@@ -674,7 +683,7 @@ namespace ts {
initializer?: Expression; // Optional initializer
}
export interface BindingElement extends Declaration {
export interface BindingElement extends NamedDeclaration {
kind: SyntaxKind.BindingElement;
parent?: BindingPattern;
propertyName?: PropertyName; // Binding property name (in object binding pattern)
@@ -699,7 +708,7 @@ namespace ts {
initializer?: Expression; // Optional initializer
}
export interface ObjectLiteralElement extends Declaration {
export interface ObjectLiteralElement extends NamedDeclaration {
_objectLiteralBrandBrand: any;
name?: PropertyName;
}
@@ -743,7 +752,7 @@ namespace ts {
// SyntaxKind.ShorthandPropertyAssignment
// SyntaxKind.EnumMember
// SyntaxKind.JSDocPropertyTag
export interface VariableLikeDeclaration extends Declaration {
export interface VariableLikeDeclaration extends NamedDeclaration {
propertyName?: PropertyName;
dotDotDotToken?: DotDotDotToken;
name: DeclarationName;
@@ -752,7 +761,7 @@ namespace ts {
initializer?: Expression;
}
export interface PropertyLikeDeclaration extends Declaration {
export interface PropertyLikeDeclaration extends NamedDeclaration {
name: PropertyName;
}
@@ -1216,8 +1225,7 @@ namespace ts {
export type BinaryOperatorToken = Token<BinaryOperator>;
// Binary expressions can be declarations if they are 'exports.foo = bar' expressions in JS files
export interface BinaryExpression extends Expression, Declaration {
export interface BinaryExpression extends Expression, Declaration {
kind: SyntaxKind.BinaryExpression;
left: Expression;
operatorToken: BinaryOperatorToken;
@@ -1417,7 +1425,7 @@ namespace ts {
export type EntityNameExpression = Identifier | PropertyAccessEntityNameExpression | ParenthesizedExpression;
export type EntityNameOrEntityNameExpression = EntityName | EntityNameExpression;
export interface PropertyAccessExpression extends MemberExpression, Declaration {
export interface PropertyAccessExpression extends MemberExpression, NamedDeclaration {
kind: SyntaxKind.PropertyAccessExpression;
expression: LeftHandSideExpression;
name: Identifier;
@@ -1506,7 +1514,7 @@ namespace ts {
// for the same reasons we treat NewExpression as a PrimaryExpression.
export interface MetaProperty extends PrimaryExpression {
kind: SyntaxKind.MetaProperty;
keywordToken: SyntaxKind;
keywordToken: SyntaxKind.NewKeyword;
name: Identifier;
}
@@ -1597,6 +1605,14 @@ namespace ts {
kind: SyntaxKind.EndOfDeclarationMarker;
}
/**
* A list of comma-seperated expressions. This node is only created by transformations.
*/
export interface CommaListExpression extends Expression {
kind: SyntaxKind.CommaListExpression;
elements: NodeArray<Expression>;
}
/**
* Marks the beginning of a merged transformed declaration.
*/
@@ -1762,7 +1778,7 @@ namespace ts {
export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration;
export interface ClassLikeDeclaration extends Declaration {
export interface ClassLikeDeclaration extends NamedDeclaration {
name?: Identifier;
typeParameters?: NodeArray<TypeParameterDeclaration>;
heritageClauses?: NodeArray<HeritageClause>;
@@ -1778,12 +1794,12 @@ namespace ts {
kind: SyntaxKind.ClassExpression;
}
export interface ClassElement extends Declaration {
export interface ClassElement extends NamedDeclaration {
_classElementBrand: any;
name?: PropertyName;
}
export interface TypeElement extends Declaration {
export interface TypeElement extends NamedDeclaration {
_typeElementBrand: any;
name?: PropertyName;
questionToken?: QuestionToken;
@@ -1811,7 +1827,7 @@ namespace ts {
type: TypeNode;
}
export interface EnumMember extends Declaration {
export interface EnumMember extends NamedDeclaration {
kind: SyntaxKind.EnumMember;
parent?: EnumDeclaration;
// This does include ComputedPropertyName, but the parser will give an error
@@ -1900,14 +1916,14 @@ namespace ts {
// import d, * as ns from "mod" => name = d, namedBinding: NamespaceImport = { name: ns }
// import { a, b as x } from "mod" => name = undefined, namedBinding: NamedImports = { elements: [{ name: a }, { name: x, propertyName: b}]}
// import d, { a, b as x } from "mod" => name = d, namedBinding: NamedImports = { elements: [{ name: a }, { name: x, propertyName: b}]}
export interface ImportClause extends Declaration {
export interface ImportClause extends NamedDeclaration {
kind: SyntaxKind.ImportClause;
parent?: ImportDeclaration;
name?: Identifier; // Default binding
namedBindings?: NamedImportBindings;
}
export interface NamespaceImport extends Declaration {
export interface NamespaceImport extends NamedDeclaration {
kind: SyntaxKind.NamespaceImport;
parent?: ImportClause;
name: Identifier;
@@ -1940,14 +1956,14 @@ namespace ts {
export type NamedImportsOrExports = NamedImports | NamedExports;
export interface ImportSpecifier extends Declaration {
export interface ImportSpecifier extends NamedDeclaration {
kind: SyntaxKind.ImportSpecifier;
parent?: NamedImports;
propertyName?: Identifier; // Name preceding "as" keyword (or undefined when "as" is absent)
name: Identifier; // Declared name
}
export interface ExportSpecifier extends Declaration {
export interface ExportSpecifier extends NamedDeclaration {
kind: SyntaxKind.ExportSpecifier;
parent?: NamedExports;
propertyName?: Identifier; // Name preceding "as" keyword (or undefined when "as" is absent)
@@ -2113,7 +2129,7 @@ namespace ts {
typeExpression: JSDocTypeExpression;
}
export interface JSDocTypedefTag extends JSDocTag, Declaration {
export interface JSDocTypedefTag extends JSDocTag, NamedDeclaration {
kind: SyntaxKind.JSDocTypedefTag;
fullName?: JSDocNamespaceDeclaration | Identifier;
name?: Identifier;
@@ -2408,7 +2424,14 @@ namespace ts {
/* @internal */ getResolvedTypeReferenceDirectives(): Map<ResolvedTypeReferenceDirective>;
/* @internal */ isSourceFileFromExternalLibrary(file: SourceFile): boolean;
// For testing purposes only.
/* @internal */ structureIsReused?: boolean;
/* @internal */ structureIsReused?: StructureIsReused;
}
/* @internal */
export const enum StructureIsReused {
Not = 0,
SafeModules = 1 << 0,
Completely = 1 << 1,
}
export interface CustomTransformers {
@@ -2505,11 +2528,11 @@ namespace ts {
indexInfoToIndexSignatureDeclaration(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): IndexSignatureDeclaration;
getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[];
getSymbolAtLocation(node: Node): Symbol;
getSymbolAtLocation(node: Node): Symbol | undefined;
getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): Symbol[];
getShorthandAssignmentValueSymbol(location: Node): Symbol;
getExportSpecifierLocalTargetSymbol(location: ExportSpecifier): Symbol;
getPropertySymbolOfDestructuringAssignment(location: Identifier): Symbol;
getShorthandAssignmentValueSymbol(location: Node): Symbol | undefined;
getExportSpecifierLocalTargetSymbol(location: ExportSpecifier): Symbol | undefined;
getPropertySymbolOfDestructuringAssignment(location: Identifier): Symbol | undefined;
getTypeAtLocation(node: Node): Type;
getTypeFromTypeNode(node: TypeNode): Type;
signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string;
@@ -2519,16 +2542,16 @@ namespace ts {
getFullyQualifiedName(symbol: Symbol): string;
getAugmentedPropertiesOfType(type: Type): Symbol[];
getRootSymbols(symbol: Symbol): Symbol[];
getContextualType(node: Expression): Type;
getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature;
getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature;
isImplementationOfOverload(node: FunctionLikeDeclaration): boolean;
getContextualType(node: Expression): Type | undefined;
getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature | undefined;
getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature | undefined;
isImplementationOfOverload(node: FunctionLikeDeclaration): boolean | undefined;
isUndefinedSymbol(symbol: Symbol): boolean;
isArgumentsSymbol(symbol: Symbol): boolean;
isUnknownSymbol(symbol: Symbol): boolean;
/* @internal */ getMergedSymbol(symbol: Symbol): Symbol;
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number;
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined;
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
/** Follow all aliases to get the original symbol. */
getAliasedSymbol(symbol: Symbol): Symbol;
@@ -2538,15 +2561,18 @@ namespace ts {
/** Unlike `getExportsOfModule`, this includes properties of an `export =` value. */
/* @internal */ getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[];
getAllAttributesTypeFromJsxOpeningLikeElement(elementNode: JsxOpeningLikeElement): Type;
getAllAttributesTypeFromJsxOpeningLikeElement(elementNode: JsxOpeningLikeElement): Type | undefined;
getJsxIntrinsicTagNames(): Symbol[];
isOptionalParameter(node: ParameterDeclaration): boolean;
getAmbientModules(): Symbol[];
tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
getApparentType(type: Type): Type;
getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined;
getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
/* @internal */ getBaseConstraintOfType(type: Type): Type | undefined;
/* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol;
/* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol | undefined;
// Should not be called directly. Should only be accessed through the Program instance.
/* @internal */ getDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[];
@@ -2557,22 +2583,42 @@ namespace ts {
/* @internal */ getIdentifierCount(): number;
/* @internal */ getSymbolCount(): number;
/* @internal */ getTypeCount(): number;
/**
* For a union, will include a property if it's defined in *any* of the member types.
* So for `{ a } | { b }`, this will include both `a` and `b`.
*/
/* @internal */ getAllPossiblePropertiesOfType(type: Type): Symbol[];
}
export enum NodeBuilderFlags {
None = 0,
allowThisInObjectLiteral = 1 << 0,
allowQualifedNameInPlaceOfIdentifier = 1 << 1,
allowTypeParameterInQualifiedName = 1 << 2,
allowAnonymousIdentifier = 1 << 3,
allowEmptyUnionOrIntersection = 1 << 4,
allowEmptyTuple = 1 << 5
// Options
NoTruncation = 1 << 0, // Don't truncate result
WriteArrayAsGenericType = 1 << 1, // Write Array<T> instead T[]
WriteTypeArgumentsOfSignature = 1 << 5, // Write the type arguments instead of type parameters of the signature
UseFullyQualifiedType = 1 << 6, // Write out the fully qualified type name (eg. Module.Type, instead of Type)
SuppressAnyReturnType = 1 << 8, // If the return type is any-like, don't offer a return type.
WriteTypeParametersInQualifiedName = 1 << 9,
// Error handling
AllowThisInObjectLiteral = 1 << 10,
AllowQualifedNameInPlaceOfIdentifier = 1 << 11,
AllowAnonymousIdentifier = 1 << 13,
AllowEmptyUnionOrIntersection = 1 << 14,
AllowEmptyTuple = 1 << 15,
IgnoreErrors = AllowThisInObjectLiteral | AllowQualifedNameInPlaceOfIdentifier | AllowAnonymousIdentifier | AllowEmptyUnionOrIntersection | AllowEmptyTuple,
// State
InObjectTypeLiteral = 1 << 20,
InTypeAlias = 1 << 23, // Writing type in type alias declaration
}
export interface SymbolDisplayBuilder {
buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
buildSymbolDisplay(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void;
buildSignatureDisplay(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): void;
buildSignatureDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): void;
buildIndexSignatureDisplay(info: IndexInfo, writer: SymbolWriter, kind: IndexKind, enclosingDeclaration?: Node, globalFlags?: TypeFormatFlags, symbolStack?: Symbol[]): void;
buildParameterDisplay(parameter: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
@@ -2722,12 +2768,11 @@ namespace ts {
getNodeCheckFlags(node: Node): NodeCheckFlags;
isDeclarationVisible(node: Declaration): boolean;
collectLinkedAliases(node: Identifier): Node[];
isImplementationOfOverload(node: FunctionLikeDeclaration): boolean;
isImplementationOfOverload(node: FunctionLikeDeclaration): boolean | undefined;
isRequiredInitializedParameter(node: ParameterDeclaration): boolean;
writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
writeBaseConstructorTypeOfClass(node: ClassLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, shouldComputeAliasToMarkVisible: boolean): SymbolAccessibilityResult;
isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult;
// Returns the constant value this property access resolves to, or 'undefined' for a non-constant
@@ -3114,7 +3159,7 @@ namespace ts {
*/
export interface TypeReference extends ObjectType {
target: GenericType; // Type reference target
typeArguments: Type[]; // Type reference type arguments (undefined if none)
typeArguments?: Type[]; // Type reference type arguments (undefined if none)
}
// Generic class and interface types
@@ -3244,7 +3289,7 @@ namespace ts {
export interface Signature {
declaration: SignatureDeclaration; // Originating declaration
typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic)
typeParameters?: TypeParameter[]; // Type parameters (undefined if non-generic)
parameters: Symbol[]; // Parameters
/* @internal */
thisParameter?: Symbol; // symbol of this-type parameter
@@ -3355,9 +3400,9 @@ namespace ts {
}
export interface Diagnostic {
file: SourceFile;
start: number;
length: number;
file: SourceFile | undefined;
start: number | undefined;
length: number | undefined;
messageText: string | DiagnosticMessageChain;
category: DiagnosticCategory;
code: number;
@@ -3506,7 +3551,7 @@ namespace ts {
export const enum NewLineKind {
CarriageReturnLineFeed = 0,
LineFeed = 1,
LineFeed = 1
}
export interface LineAndCharacter {
@@ -3949,14 +3994,16 @@ namespace ts {
HelperName = 1 << 12,
ExportName = 1 << 13, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal).
LocalName = 1 << 14, // Ensure an export prefix is not added for an identifier that points to an exported declaration.
Indented = 1 << 15, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter).
NoIndentation = 1 << 16, // Do not indent the node.
AsyncFunctionBody = 1 << 17,
ReuseTempVariableScope = 1 << 18, // Reuse the existing temp variable scope during emit.
CustomPrologue = 1 << 19, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed).
NoHoisting = 1 << 20, // Do not hoist this declaration in --module system
HasEndOfDeclarationMarker = 1 << 21, // Declaration has an associated NotEmittedStatement to mark the end of the declaration
Iterator = 1 << 22, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable.
InternalName = 1 << 15, // The name is internal to an ES5 class body function.
Indented = 1 << 16, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter).
NoIndentation = 1 << 17, // Do not indent the node.
AsyncFunctionBody = 1 << 18,
ReuseTempVariableScope = 1 << 19, // Reuse the existing temp variable scope during emit.
CustomPrologue = 1 << 20, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed).
NoHoisting = 1 << 21, // Do not hoist this declaration in --module system
HasEndOfDeclarationMarker = 1 << 22, // Declaration has an associated NotEmittedStatement to mark the end of the declaration
Iterator = 1 << 23, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable.
NoAsciiEscaping = 1 << 24, // When synthesizing nodes that lack an original node or textSourceNode, we want to write the text on the node with ASCII escaping substitutions.
}
export interface EmitHelper {
@@ -3981,11 +4028,12 @@ namespace ts {
Awaiter = 1 << 6, // __awaiter (used by ES2017 async functions transformation)
Generator = 1 << 7, // __generator (used by ES2015 generator transformation)
Values = 1 << 8, // __values (used by ES2015 for..of and yield* transformations)
Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation)
Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation)
Spread = 1 << 10, // __spread (used by ES2015 array spread and argument list spread transformations)
AsyncGenerator = 1 << 11, // __asyncGenerator (used by ES2017 async generator transformation)
AsyncDelegator = 1 << 12, // __asyncDelegator (used by ES2017 async generator yield* transformation)
AsyncValues = 1 << 13, // __asyncValues (used by ES2017 for..await..of transformation)
Await = 1 << 11, // __await (used by ES2017 async generator transformation)
AsyncGenerator = 1 << 12, // __asyncGenerator (used by ES2017 async generator transformation)
AsyncDelegator = 1 << 13, // __asyncDelegator (used by ES2017 async generator yield* transformation)
AsyncValues = 1 << 14, // __asyncValues (used by ES2017 for..await..of transformation)
// Helpers included by ES2015 for..of
ForOfIncludes = Values,
@@ -3993,6 +4041,12 @@ namespace ts {
// Helpers included by ES2017 for..await..of
ForAwaitOfIncludes = AsyncValues,
// Helpers included by ES2017 async generators
AsyncGeneratorIncludes = Await | AsyncGenerator,
// Helpers included by yield* in ES2017 async generators
AsyncDelegatorIncludes = Await | AsyncDelegator | AsyncValues,
// Helpers included by ES2015 spread
SpreadIncludes = Read | Spread,
@@ -4162,7 +4216,7 @@ namespace ts {
* Prints a bundle of source files as-is, without any emit transformations.
*/
printBundle(bundle: Bundle): string;
/*@internal*/ writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, writer: EmitTextWriter): void;
/*@internal*/ writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, writer: EmitTextWriter): void;
/*@internal*/ writeFile(sourceFile: SourceFile, writer: EmitTextWriter): void;
/*@internal*/ writeBundle(bundle: Bundle, writer: EmitTextWriter): void;
}
+157 -59
View File
@@ -11,12 +11,12 @@ namespace ts {
isTypeReferenceDirective?: boolean;
}
export function getDeclarationOfKind(symbol: Symbol, kind: SyntaxKind): Declaration {
export function getDeclarationOfKind<T extends Declaration>(symbol: Symbol, kind: T["kind"]): T {
const declarations = symbol.declarations;
if (declarations) {
for (const declaration of declarations) {
if (declaration.kind === kind) {
return declaration;
return declaration as T;
}
}
}
@@ -122,9 +122,8 @@ namespace ts {
/* @internal */
export function hasChangesInResolutions<T>(names: string[], newResolutions: T[], oldResolutions: Map<T>, comparer: (oldResolution: T, newResolution: T) => boolean): boolean {
if (names.length !== newResolutions.length) {
return false;
}
Debug.assert(names.length === newResolutions.length);
for (let i = 0; i < names.length; i++) {
const newResolution = newResolutions[i];
const oldResolution = oldResolutions && oldResolutions.get(names[i]);
@@ -329,19 +328,21 @@ namespace ts {
return getSourceTextOfNodeFromSourceFile(sourceFile, node);
}
const escapeText = getEmitFlags(node) & EmitFlags.NoAsciiEscaping ? escapeString : escapeNonAsciiString;
// If we can't reach the original source text, use the canonical form if it's a number,
// or an escaped quoted form of the original text if it's string-like.
// or a (possibly escaped) quoted form of the original text if it's string-like.
switch (node.kind) {
case SyntaxKind.StringLiteral:
return getQuotedEscapedLiteralText('"', node.text, '"');
return '"' + escapeText(node.text) + '"';
case SyntaxKind.NoSubstitutionTemplateLiteral:
return getQuotedEscapedLiteralText("`", node.text, "`");
return "`" + escapeText(node.text) + "`";
case SyntaxKind.TemplateHead:
return getQuotedEscapedLiteralText("`", node.text, "${");
return "`" + escapeText(node.text) + "${";
case SyntaxKind.TemplateMiddle:
return getQuotedEscapedLiteralText("}", node.text, "${");
return "}" + escapeText(node.text) + "${";
case SyntaxKind.TemplateTail:
return getQuotedEscapedLiteralText("}", node.text, "`");
return "}" + escapeText(node.text) + "`";
case SyntaxKind.NumericLiteral:
return node.text;
}
@@ -350,11 +351,7 @@ namespace ts {
}
export function getTextOfConstantValue(value: string | number) {
return typeof value === "string" ? getQuotedEscapedLiteralText('"', value, '"') : "" + value;
}
function getQuotedEscapedLiteralText(leftQuote: string, text: string, rightQuote: string) {
return leftQuote + escapeNonAsciiCharacters(escapeString(text)) + rightQuote;
return typeof value === "string" ? '"' + escapeNonAsciiString(value) + '"' : "" + value;
}
// Add an extra underscore to identifiers that start with two underscores to avoid issues with magic names like '__proto__'
@@ -470,7 +467,7 @@ namespace ts {
return getFullWidth(name) === 0 ? "(Missing)" : getTextOfNode(name);
}
export function getNameFromIndexInfo(info: IndexInfo) {
export function getNameFromIndexInfo(info: IndexInfo): string | undefined {
return info.declaration ? declarationNameToString(info.declaration.parameters[0].name) : undefined;
}
@@ -571,7 +568,7 @@ namespace ts {
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.TypeAliasDeclaration:
errorNode = (<Declaration>node).name;
errorNode = (<NamedDeclaration>node).name;
break;
case SyntaxKind.ArrowFunction:
return getErrorSpanForArrowFunction(sourceFile, <ArrowFunction>node);
@@ -594,10 +591,6 @@ namespace ts {
return (file.externalModuleIndicator || file.commonJsModuleIndicator) !== undefined;
}
export function isDeclarationFile(file: SourceFile): boolean {
return file.isDeclarationFile;
}
export function isConstEnumDeclaration(node: Node): boolean {
return node.kind === SyntaxKind.EnumDeclaration && isConst(node);
}
@@ -886,6 +879,16 @@ namespace ts {
return false;
}
export function isFunctionOrConstructorTypeNode(node: Node): node is FunctionTypeNode | ConstructorTypeNode {
switch (node.kind) {
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
return true;
}
return false;
}
export function introducesArgumentsExoticObject(node: Node) {
switch (node.kind) {
case SyntaxKind.MethodDeclaration:
@@ -1147,6 +1150,10 @@ namespace ts {
}
}
export function isCallOrNewExpression(node: Node): node is CallExpression | NewExpression {
return node.kind === SyntaxKind.CallExpression || node.kind === SyntaxKind.NewExpression;
}
export function getInvokedExpression(node: CallLikeExpression): Expression {
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
return (<TaggedTemplateExpression>node).tag;
@@ -1759,22 +1766,44 @@ namespace ts {
// True if the given identifier, string literal, or number literal is the name of a declaration node
export function isDeclarationName(name: Node): boolean {
if (name.kind !== SyntaxKind.Identifier && name.kind !== SyntaxKind.StringLiteral && name.kind !== SyntaxKind.NumericLiteral) {
return false;
switch (name.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
return isDeclaration(name.parent) && name.parent.name === name;
default:
return false;
}
}
const parent = name.parent;
if (parent.kind === SyntaxKind.ImportSpecifier || parent.kind === SyntaxKind.ExportSpecifier) {
if ((<ImportOrExportSpecifier>parent).propertyName) {
return true;
export function getNameOfDeclaration(declaration: Declaration): DeclarationName {
if (!declaration) {
return undefined;
}
if (declaration.kind === SyntaxKind.BinaryExpression) {
const kind = getSpecialPropertyAssignmentKind(declaration as BinaryExpression);
const lhs = (declaration as BinaryExpression).left;
switch (kind) {
case SpecialPropertyAssignmentKind.None:
case SpecialPropertyAssignmentKind.ModuleExports:
return undefined;
case SpecialPropertyAssignmentKind.ExportsProperty:
if (lhs.kind === SyntaxKind.Identifier) {
return (lhs as PropertyAccessExpression).name;
}
else {
return ((lhs as PropertyAccessExpression).expression as PropertyAccessExpression).name;
}
case SpecialPropertyAssignmentKind.ThisProperty:
case SpecialPropertyAssignmentKind.Property:
return (lhs as PropertyAccessExpression).name;
case SpecialPropertyAssignmentKind.PrototypeProperty:
return ((lhs as PropertyAccessExpression).expression as PropertyAccessExpression).name;
}
}
if (isDeclaration(parent)) {
return (<Declaration>parent).name === name;
else {
return (declaration as NamedDeclaration).name;
}
return false;
}
export function isLiteralComputedPropertyDeclarationName(node: Node) {
@@ -1797,7 +1826,7 @@ namespace ts {
case SyntaxKind.PropertyAssignment:
case SyntaxKind.PropertyAccessExpression:
// Name in member declaration or property name in property access
return (<Declaration | PropertyAccessExpression>parent).name === node;
return (<NamedDeclaration | PropertyAccessExpression>parent).name === node;
case SyntaxKind.QualifiedName:
// Name on right hand side of dot in a type query
if ((<QualifiedName>parent).right === node) {
@@ -1929,16 +1958,18 @@ namespace ts {
}
export const enum FunctionFlags {
Normal = 0,
Generator = 1 << 0,
Async = 1 << 1,
AsyncOrAsyncGenerator = Async | Generator,
Invalid = 1 << 2,
InvalidAsyncOrAsyncGenerator = AsyncOrAsyncGenerator | Invalid,
InvalidGenerator = Generator | Invalid,
Normal = 0, // Function is a normal function
Generator = 1 << 0, // Function is a generator function or async generator function
Async = 1 << 1, // Function is an async function or an async generator function
Invalid = 1 << 2, // Function is a signature or overload and does not have a body.
AsyncGenerator = Async | Generator, // Function is an async generator function
}
export function getFunctionFlags(node: FunctionLikeDeclaration) {
export function getFunctionFlags(node: FunctionLikeDeclaration | undefined) {
if (!node) {
return FunctionFlags.Invalid;
}
let flags = FunctionFlags.Normal;
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
@@ -1993,7 +2024,8 @@ namespace ts {
* Symbol.
*/
export function hasDynamicName(declaration: Declaration): boolean {
return declaration.name && isDynamicName(declaration.name);
const name = getNameOfDeclaration(declaration);
return name && isDynamicName(name);
}
export function isDynamicName(name: DeclarationName): boolean {
@@ -2303,6 +2335,9 @@ namespace ts {
case SyntaxKind.SpreadElement:
return 1;
case SyntaxKind.CommaListExpression:
return 0;
default:
return -1;
}
@@ -2435,7 +2470,8 @@ namespace ts {
}
const nonAsciiCharacters = /[^\u0000-\u007F]/g;
export function escapeNonAsciiCharacters(s: string): string {
export function escapeNonAsciiString(s: string): string {
s = escapeString(s);
// Replace non-ASCII characters with '\uNNNN' escapes if any exist.
// Otherwise just return the original string.
return nonAsciiCharacters.test(s) ?
@@ -2539,7 +2575,7 @@ namespace ts {
export function getExternalModuleNameFromDeclaration(host: EmitHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration): string {
const file = resolver.getExternalModuleFileFromDeclaration(declaration);
if (!file || isDeclarationFile(file)) {
if (!file || file.isDeclarationFile) {
return undefined;
}
return getResolvedExternalModuleName(host, file);
@@ -2612,7 +2648,7 @@ namespace ts {
/** Don't call this for `--outFile`, just for `--outDir` or plain emit. `--outFile` needs additional checks. */
export function sourceFileMayBeEmitted(sourceFile: SourceFile, options: CompilerOptions, isSourceFileFromExternalLibrary: (file: SourceFile) => boolean) {
return !(options.noEmitForJsFiles && isSourceFileJavaScript(sourceFile)) && !isDeclarationFile(sourceFile) && !isSourceFileFromExternalLibrary(sourceFile);
return !(options.noEmitForJsFiles && isSourceFileJavaScript(sourceFile)) && !sourceFile.isDeclarationFile && !isSourceFileFromExternalLibrary(sourceFile);
}
/**
@@ -2758,7 +2794,7 @@ namespace ts {
forEach(declarations, (member: Declaration) => {
if ((member.kind === SyntaxKind.GetAccessor || member.kind === SyntaxKind.SetAccessor)
&& hasModifier(member, ModifierFlags.Static) === hasModifier(accessor, ModifierFlags.Static)) {
const memberName = getPropertyNameForPropertyNameNode(member.name);
const memberName = getPropertyNameForPropertyNameNode((member as NamedDeclaration).name);
const accessorName = getPropertyNameForPropertyNameNode(accessor.name);
if (memberName === accessorName) {
if (!firstAccessor) {
@@ -3228,13 +3264,13 @@ namespace ts {
const carriageReturnLineFeed = "\r\n";
const lineFeed = "\n";
export function getNewLineCharacter(options: CompilerOptions | PrinterOptions): string {
if (options.newLine === NewLineKind.CarriageReturnLineFeed) {
return carriageReturnLineFeed;
switch (options.newLine) {
case NewLineKind.CarriageReturnLineFeed:
return carriageReturnLineFeed;
case NewLineKind.LineFeed:
return lineFeed;
}
else if (options.newLine === NewLineKind.LineFeed) {
return lineFeed;
}
else if (sys) {
if (sys) {
return sys.newLine;
}
return carriageReturnLineFeed;
@@ -3573,10 +3609,6 @@ namespace ts {
return node.kind === SyntaxKind.Identifier;
}
export function isVoidExpression(node: Node): node is VoidExpression {
return node.kind === SyntaxKind.VoidExpression;
}
export function isGeneratedIdentifier(node: Node): node is GeneratedIdentifier {
// Using `>` here catches both `GeneratedIdentifierKind.None` and `undefined`.
return isIdentifier(node) && node.autoGenerateKind > GeneratedIdentifierKind.None;
@@ -3653,7 +3685,18 @@ namespace ts {
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor
|| kind === SyntaxKind.IndexSignature
|| kind === SyntaxKind.SemicolonClassElement;
|| kind === SyntaxKind.SemicolonClassElement
|| kind === SyntaxKind.MissingDeclaration;
}
export function isTypeElement(node: Node): node is TypeElement {
const kind = node.kind;
return kind === SyntaxKind.ConstructSignature
|| kind === SyntaxKind.CallSignature
|| kind === SyntaxKind.PropertySignature
|| kind === SyntaxKind.MethodSignature
|| kind === SyntaxKind.IndexSignature
|| kind === SyntaxKind.MissingDeclaration;
}
export function isObjectLiteralElementLike(node: Node): node is ObjectLiteralElementLike {
@@ -3884,6 +3927,7 @@ namespace ts {
|| kind === SyntaxKind.SpreadElement
|| kind === SyntaxKind.AsExpression
|| kind === SyntaxKind.OmittedExpression
|| kind === SyntaxKind.CommaListExpression
|| isUnaryExpressionKind(kind);
}
@@ -3975,6 +4019,10 @@ namespace ts {
return node.kind === SyntaxKind.ImportEqualsDeclaration;
}
export function isImportDeclaration(node: Node): node is ImportDeclaration {
return node.kind === SyntaxKind.ImportDeclaration;
}
export function isImportClause(node: Node): node is ImportClause {
return node.kind === SyntaxKind.ImportClause;
}
@@ -3997,6 +4045,10 @@ namespace ts {
return node.kind === SyntaxKind.ExportSpecifier;
}
export function isExportAssignment(node: Node): node is ExportAssignment {
return node.kind === SyntaxKind.ExportAssignment;
}
export function isModuleOrEnumDeclaration(node: Node): node is ModuleDeclaration | EnumDeclaration {
return node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.EnumDeclaration;
}
@@ -4074,7 +4126,7 @@ namespace ts {
|| kind === SyntaxKind.MergeDeclarationMarker;
}
export function isDeclaration(node: Node): node is Declaration {
export function isDeclaration(node: Node): node is NamedDeclaration {
return isDeclarationKind(node.kind);
}
@@ -4203,6 +4255,52 @@ namespace ts {
// Firefox has Object.prototype.watch
return options.watch && options.hasOwnProperty("watch");
}
export function getCheckFlags(symbol: Symbol): CheckFlags {
return symbol.flags & SymbolFlags.Transient ? (<TransientSymbol>symbol).checkFlags : 0;
}
export function getDeclarationModifierFlagsFromSymbol(s: Symbol): ModifierFlags {
if (s.valueDeclaration) {
const flags = getCombinedModifierFlags(s.valueDeclaration);
return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier;
}
if (getCheckFlags(s) & CheckFlags.Synthetic) {
const checkFlags = (<TransientSymbol>s).checkFlags;
const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private :
checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public :
ModifierFlags.Protected;
const staticModifier = checkFlags & CheckFlags.ContainsStatic ? ModifierFlags.Static : 0;
return accessModifier | staticModifier;
}
if (s.flags & SymbolFlags.Prototype) {
return ModifierFlags.Public | ModifierFlags.Static;
}
return 0;
}
export function levenshtein(s1: string, s2: string): number {
let previous: number[] = new Array(s2.length + 1);
let current: number[] = new Array(s2.length + 1);
for (let i = 0; i < s2.length + 1; i++) {
previous[i] = i;
current[i] = -1;
}
for (let i = 1; i < s1.length + 1; i++) {
current[0] = i;
for (let j = 1; j < s2.length + 1; j++) {
current[j] = Math.min(
previous[j] + 1,
current[j - 1] + 1,
previous[j - 1] + (s1[i - 1] === s2[j - 1] ? 0 : 2));
}
// shift current back to previous, and then reuse previous' array
const tmp = previous;
previous = current;
current = tmp;
}
return previous[previous.length - 1];
}
}
namespace ts {
+171 -117
View File
@@ -218,17 +218,12 @@ namespace ts {
return node;
}
switch (node.kind) {
case SyntaxKind.SemicolonClassElement:
case SyntaxKind.EmptyStatement:
case SyntaxKind.OmittedExpression:
case SyntaxKind.DebuggerStatement:
case SyntaxKind.EndOfDeclarationMarker:
case SyntaxKind.MissingDeclaration:
// No need to visit nodes with no children.
return node;
switch (kind) {
// Names
case SyntaxKind.Identifier:
return updateIdentifier(<Identifier>node, nodesVisitor((<Identifier>node).typeArguments, visitor, isTypeNode));
case SyntaxKind.QualifiedName:
return updateQualifiedName(<QualifiedName>node,
visitNode((<QualifiedName>node).left, visitor, isEntityName),
@@ -238,45 +233,13 @@ namespace ts {
return updateComputedPropertyName(<ComputedPropertyName>node,
visitNode((<ComputedPropertyName>node).expression, visitor, isExpression));
// Signatures and Signature Elements
case SyntaxKind.FunctionType:
return updateFunctionTypeNode(<FunctionTypeNode>node,
nodesVisitor((<FunctionTypeNode>node).typeParameters, visitor, isTypeParameter),
visitParameterList((<FunctionTypeNode>node).parameters, visitor, context, nodesVisitor),
visitNode((<FunctionTypeNode>node).type, visitor, isTypeNode));
// Signature elements
case SyntaxKind.ConstructorType:
return updateConstructorTypeNode(<ConstructorTypeNode>node,
nodesVisitor((<ConstructorTypeNode>node).typeParameters, visitor, isTypeParameter),
visitParameterList((<ConstructorTypeNode>node).parameters, visitor, context, nodesVisitor),
visitNode((<ConstructorTypeNode>node).type, visitor, isTypeNode));
case SyntaxKind.CallSignature:
return updateCallSignatureDeclaration(<CallSignatureDeclaration>node,
nodesVisitor((<CallSignatureDeclaration>node).typeParameters, visitor, isTypeParameter),
visitParameterList((<CallSignatureDeclaration>node).parameters, visitor, context, nodesVisitor),
visitNode((<CallSignatureDeclaration>node).type, visitor, isTypeNode));
case SyntaxKind.ConstructSignature:
return updateConstructSignatureDeclaration(<ConstructSignatureDeclaration>node,
nodesVisitor((<ConstructSignatureDeclaration>node).typeParameters, visitor, isTypeParameter),
visitParameterList((<ConstructSignatureDeclaration>node).parameters, visitor, context, nodesVisitor),
visitNode((<ConstructSignatureDeclaration>node).type, visitor, isTypeNode));
case SyntaxKind.MethodSignature:
return updateMethodSignature(<MethodSignature>node,
nodesVisitor((<MethodSignature>node).typeParameters, visitor, isTypeParameter),
visitParameterList((<MethodSignature>node).parameters, visitor, context, nodesVisitor),
visitNode((<MethodSignature>node).type, visitor, isTypeNode),
visitNode((<MethodSignature>node).name, visitor, isPropertyName),
visitNode((<MethodSignature>node).questionToken, tokenVisitor, isToken));
case SyntaxKind.IndexSignature:
return updateIndexSignatureDeclaration(<IndexSignatureDeclaration>node,
nodesVisitor((<IndexSignatureDeclaration>node).decorators, visitor, isDecorator),
nodesVisitor((<IndexSignatureDeclaration>node).modifiers, visitor, isModifier),
visitParameterList((<IndexSignatureDeclaration>node).parameters, visitor, context, nodesVisitor),
visitNode((<IndexSignatureDeclaration>node).type, visitor, isTypeNode));
case SyntaxKind.TypeParameter:
return updateTypeParameterDeclaration(<TypeParameterDeclaration>node,
visitNode((<TypeParameterDeclaration>node).name, visitor, isIdentifier),
visitNode((<TypeParameterDeclaration>node).constraint, visitor, isTypeNode),
visitNode((<TypeParameterDeclaration>node).default, visitor, isTypeNode));
case SyntaxKind.Parameter:
return updateParameter(<ParameterDeclaration>node,
@@ -292,69 +255,11 @@ namespace ts {
return updateDecorator(<Decorator>node,
visitNode((<Decorator>node).expression, visitor, isExpression));
// Types
case SyntaxKind.TypeReference:
return updateTypeReferenceNode(<TypeReferenceNode>node,
visitNode((<TypeReferenceNode>node).typeName, visitor, isEntityName),
nodesVisitor((<TypeReferenceNode>node).typeArguments, visitor, isTypeNode));
case SyntaxKind.TypePredicate:
return updateTypePredicateNode(<TypePredicateNode>node,
visitNode((<TypePredicateNode>node).parameterName, visitor),
visitNode((<TypePredicateNode>node).type, visitor, isTypeNode));
case SyntaxKind.TypeQuery:
return updateTypeQueryNode((<TypeQueryNode>node), visitNode((<TypeQueryNode>node).exprName, visitor, isEntityName));
case SyntaxKind.TypeLiteral:
return updateTypeLiteralNode((<TypeLiteralNode>node), nodesVisitor((<TypeLiteralNode>node).members, visitor));
case SyntaxKind.ArrayType:
return updateArrayTypeNode(<ArrayTypeNode>node, visitNode((<ArrayTypeNode>node).elementType, visitor, isTypeNode));
case SyntaxKind.TupleType:
return updateTypleTypeNode((<TupleTypeNode>node), nodesVisitor((<TupleTypeNode>node).elementTypes, visitor, isTypeNode));
case SyntaxKind.UnionType:
case SyntaxKind.IntersectionType:
return updateUnionOrIntersectionTypeNode(<UnionOrIntersectionTypeNode>node,
nodesVisitor((<UnionOrIntersectionTypeNode>node).types, visitor, isTypeNode));
case SyntaxKind.ParenthesizedType:
throw Debug.fail("not implemented.");
case SyntaxKind.TypeOperator:
return updateTypeOperatorNode(<TypeOperatorNode>node, visitNode((<TypeOperatorNode>node).type, visitor, isTypeNode));
case SyntaxKind.IndexedAccessType:
return updateIndexedAccessTypeNode((<IndexedAccessTypeNode>node),
visitNode((<IndexedAccessTypeNode>node).objectType, visitor, isTypeNode),
visitNode((<IndexedAccessTypeNode>node).indexType, visitor, isTypeNode));
case SyntaxKind.MappedType:
return updateMappedTypeNode((<MappedTypeNode>node),
visitNode((<MappedTypeNode>node).readonlyToken, tokenVisitor, isToken),
visitNode((<MappedTypeNode>node).typeParameter, visitor, isTypeParameter),
visitNode((<MappedTypeNode>node).questionToken, tokenVisitor, isToken),
visitNode((<MappedTypeNode>node).type, visitor, isTypeNode));
case SyntaxKind.LiteralType:
return updateLiteralTypeNode(<LiteralTypeNode>node,
visitNode((<LiteralTypeNode>node).literal, visitor, isExpression));
// Type Declarations
case SyntaxKind.TypeParameter:
return updateTypeParameterDeclaration(<TypeParameterDeclaration>node,
visitNode((<TypeParameterDeclaration>node).name, visitor, isIdentifier),
visitNode((<TypeParameterDeclaration>node).constraint, visitor, isTypeNode),
visitNode((<TypeParameterDeclaration>node).default, visitor, isTypeNode));
// Type members
// Type elements
case SyntaxKind.PropertySignature:
return updatePropertySignature((<PropertySignature>node),
nodesVisitor((<PropertySignature>node).modifiers, visitor, isToken),
visitNode((<PropertySignature>node).name, visitor, isPropertyName),
visitNode((<PropertySignature>node).questionToken, tokenVisitor, isToken),
visitNode((<PropertySignature>node).type, visitor, isTypeNode),
@@ -368,6 +273,14 @@ namespace ts {
visitNode((<PropertyDeclaration>node).type, visitor, isTypeNode),
visitNode((<PropertyDeclaration>node).initializer, visitor, isExpression));
case SyntaxKind.MethodSignature:
return updateMethodSignature(<MethodSignature>node,
nodesVisitor((<MethodSignature>node).typeParameters, visitor, isTypeParameter),
nodesVisitor((<MethodSignature>node).parameters, visitor, isParameterDeclaration),
visitNode((<MethodSignature>node).type, visitor, isTypeNode),
visitNode((<MethodSignature>node).name, visitor, isPropertyName),
visitNode((<MethodSignature>node).questionToken, tokenVisitor, isToken));
case SyntaxKind.MethodDeclaration:
return updateMethod(<MethodDeclaration>node,
nodesVisitor((<MethodDeclaration>node).decorators, visitor, isDecorator),
@@ -404,7 +317,99 @@ namespace ts {
visitParameterList((<SetAccessorDeclaration>node).parameters, visitor, context, nodesVisitor),
visitFunctionBody((<SetAccessorDeclaration>node).body, visitor, context));
case SyntaxKind.CallSignature:
return updateCallSignature(<CallSignatureDeclaration>node,
nodesVisitor((<CallSignatureDeclaration>node).typeParameters, visitor, isTypeParameter),
nodesVisitor((<CallSignatureDeclaration>node).parameters, visitor, isParameterDeclaration),
visitNode((<CallSignatureDeclaration>node).type, visitor, isTypeNode));
case SyntaxKind.ConstructSignature:
return updateConstructSignature(<ConstructSignatureDeclaration>node,
nodesVisitor((<ConstructSignatureDeclaration>node).typeParameters, visitor, isTypeParameter),
nodesVisitor((<ConstructSignatureDeclaration>node).parameters, visitor, isParameterDeclaration),
visitNode((<ConstructSignatureDeclaration>node).type, visitor, isTypeNode));
case SyntaxKind.IndexSignature:
return updateIndexSignature(<IndexSignatureDeclaration>node,
nodesVisitor((<IndexSignatureDeclaration>node).decorators, visitor, isDecorator),
nodesVisitor((<IndexSignatureDeclaration>node).modifiers, visitor, isModifier),
nodesVisitor((<IndexSignatureDeclaration>node).parameters, visitor, isParameterDeclaration),
visitNode((<IndexSignatureDeclaration>node).type, visitor, isTypeNode));
// Types
case SyntaxKind.TypePredicate:
return updateTypePredicateNode(<TypePredicateNode>node,
visitNode((<TypePredicateNode>node).parameterName, visitor),
visitNode((<TypePredicateNode>node).type, visitor, isTypeNode));
case SyntaxKind.TypeReference:
return updateTypeReferenceNode(<TypeReferenceNode>node,
visitNode((<TypeReferenceNode>node).typeName, visitor, isEntityName),
nodesVisitor((<TypeReferenceNode>node).typeArguments, visitor, isTypeNode));
case SyntaxKind.FunctionType:
return updateFunctionTypeNode(<FunctionTypeNode>node,
nodesVisitor((<FunctionTypeNode>node).typeParameters, visitor, isTypeParameter),
nodesVisitor((<FunctionTypeNode>node).parameters, visitor, isParameterDeclaration),
visitNode((<FunctionTypeNode>node).type, visitor, isTypeNode));
case SyntaxKind.ConstructorType:
return updateConstructorTypeNode(<ConstructorTypeNode>node,
nodesVisitor((<ConstructorTypeNode>node).typeParameters, visitor, isTypeParameter),
nodesVisitor((<ConstructorTypeNode>node).parameters, visitor, isParameterDeclaration),
visitNode((<ConstructorTypeNode>node).type, visitor, isTypeNode));
case SyntaxKind.TypeQuery:
return updateTypeQueryNode((<TypeQueryNode>node),
visitNode((<TypeQueryNode>node).exprName, visitor, isEntityName));
case SyntaxKind.TypeLiteral:
return updateTypeLiteralNode((<TypeLiteralNode>node),
nodesVisitor((<TypeLiteralNode>node).members, visitor, isTypeElement));
case SyntaxKind.ArrayType:
return updateArrayTypeNode(<ArrayTypeNode>node,
visitNode((<ArrayTypeNode>node).elementType, visitor, isTypeNode));
case SyntaxKind.TupleType:
return updateTypleTypeNode((<TupleTypeNode>node),
nodesVisitor((<TupleTypeNode>node).elementTypes, visitor, isTypeNode));
case SyntaxKind.UnionType:
return updateUnionTypeNode(<UnionTypeNode>node,
nodesVisitor((<UnionTypeNode>node).types, visitor, isTypeNode));
case SyntaxKind.IntersectionType:
return updateIntersectionTypeNode(<IntersectionTypeNode>node,
nodesVisitor((<IntersectionTypeNode>node).types, visitor, isTypeNode));
case SyntaxKind.ParenthesizedType:
return updateParenthesizedType(<ParenthesizedTypeNode>node,
visitNode((<ParenthesizedTypeNode>node).type, visitor, isTypeNode));
case SyntaxKind.TypeOperator:
return updateTypeOperatorNode(<TypeOperatorNode>node,
visitNode((<TypeOperatorNode>node).type, visitor, isTypeNode));
case SyntaxKind.IndexedAccessType:
return updateIndexedAccessTypeNode((<IndexedAccessTypeNode>node),
visitNode((<IndexedAccessTypeNode>node).objectType, visitor, isTypeNode),
visitNode((<IndexedAccessTypeNode>node).indexType, visitor, isTypeNode));
case SyntaxKind.MappedType:
return updateMappedTypeNode((<MappedTypeNode>node),
visitNode((<MappedTypeNode>node).readonlyToken, tokenVisitor, isToken),
visitNode((<MappedTypeNode>node).typeParameter, visitor, isTypeParameter),
visitNode((<MappedTypeNode>node).questionToken, tokenVisitor, isToken),
visitNode((<MappedTypeNode>node).type, visitor, isTypeNode));
case SyntaxKind.LiteralType:
return updateLiteralTypeNode(<LiteralTypeNode>node,
visitNode((<LiteralTypeNode>node).literal, visitor, isExpression));
// Binding patterns
case SyntaxKind.ObjectBindingPattern:
return updateObjectBindingPattern(<ObjectBindingPattern>node,
nodesVisitor((<ObjectBindingPattern>node).elements, visitor, isBindingElement));
@@ -421,6 +426,7 @@ namespace ts {
visitNode((<BindingElement>node).initializer, visitor, isExpression));
// Expression
case SyntaxKind.ArrayLiteralExpression:
return updateArrayLiteral(<ArrayLiteralExpression>node,
nodesVisitor((<ArrayLiteralExpression>node).elements, visitor, isExpression));
@@ -499,11 +505,6 @@ namespace ts {
return updateAwait(<AwaitExpression>node,
visitNode((<AwaitExpression>node).expression, visitor, isExpression));
case SyntaxKind.BinaryExpression:
return updateBinary(<BinaryExpression>node,
visitNode((<BinaryExpression>node).left, visitor, isExpression),
visitNode((<BinaryExpression>node).right, visitor, isExpression));
case SyntaxKind.PrefixUnaryExpression:
return updatePrefix(<PrefixUnaryExpression>node,
visitNode((<PrefixUnaryExpression>node).operand, visitor, isExpression));
@@ -512,6 +513,11 @@ namespace ts {
return updatePostfix(<PostfixUnaryExpression>node,
visitNode((<PostfixUnaryExpression>node).operand, visitor, isExpression));
case SyntaxKind.BinaryExpression:
return updateBinary(<BinaryExpression>node,
visitNode((<BinaryExpression>node).left, visitor, isExpression),
visitNode((<BinaryExpression>node).right, visitor, isExpression));
case SyntaxKind.ConditionalExpression:
return updateConditional(<ConditionalExpression>node,
visitNode((<ConditionalExpression>node).condition, visitor, isExpression),
@@ -554,13 +560,19 @@ namespace ts {
return updateNonNullExpression(<NonNullExpression>node,
visitNode((<NonNullExpression>node).expression, visitor, isExpression));
case SyntaxKind.MetaProperty:
return updateMetaProperty(<MetaProperty>node,
visitNode((<MetaProperty>node).name, visitor, isIdentifier));
// Misc
case SyntaxKind.TemplateSpan:
return updateTemplateSpan(<TemplateSpan>node,
visitNode((<TemplateSpan>node).expression, visitor, isExpression),
visitNode((<TemplateSpan>node).literal, visitor, isTemplateMiddleOrTemplateTail));
// Element
case SyntaxKind.Block:
return updateBlock(<Block>node,
nodesVisitor((<Block>node).statements, visitor, isStatement));
@@ -677,6 +689,21 @@ namespace ts {
nodesVisitor((<ClassDeclaration>node).heritageClauses, visitor, isHeritageClause),
nodesVisitor((<ClassDeclaration>node).members, visitor, isClassElement));
case SyntaxKind.InterfaceDeclaration:
return updateInterfaceDeclaration(<InterfaceDeclaration>node,
nodesVisitor((<InterfaceDeclaration>node).decorators, visitor, isDecorator),
nodesVisitor((<InterfaceDeclaration>node).modifiers, visitor, isModifier),
visitNode((<InterfaceDeclaration>node).name, visitor, isIdentifier),
nodesVisitor((<InterfaceDeclaration>node).typeParameters, visitor, isTypeParameter),
nodesVisitor((<InterfaceDeclaration>node).heritageClauses, visitor, isHeritageClause),
nodesVisitor((<InterfaceDeclaration>node).members, visitor, isTypeElement));
case SyntaxKind.TypeAliasDeclaration:
return updateTypeAliasDeclaration(<TypeAliasDeclaration>node,
visitNode((<TypeAliasDeclaration>node).name, visitor, isIdentifier),
nodesVisitor((<TypeAliasDeclaration>node).typeParameters, visitor, isTypeParameter),
visitNode((<TypeAliasDeclaration>node).type, visitor, isTypeNode));
case SyntaxKind.EnumDeclaration:
return updateEnumDeclaration(<EnumDeclaration>node,
nodesVisitor((<EnumDeclaration>node).decorators, visitor, isDecorator),
@@ -699,6 +726,10 @@ namespace ts {
return updateCaseBlock(<CaseBlock>node,
nodesVisitor((<CaseBlock>node).clauses, visitor, isCaseOrDefaultClause));
case SyntaxKind.NamespaceExportDeclaration:
return updateNamespaceExportDeclaration(<NamespaceExportDeclaration>node,
visitNode((<NamespaceExportDeclaration>node).name, visitor, isIdentifier));
case SyntaxKind.ImportEqualsDeclaration:
return updateImportEqualsDeclaration(<ImportEqualsDeclaration>node,
nodesVisitor((<ImportEqualsDeclaration>node).decorators, visitor, isDecorator),
@@ -754,21 +785,19 @@ namespace ts {
visitNode((<ExportSpecifier>node).name, visitor, isIdentifier));
// Module references
case SyntaxKind.ExternalModuleReference:
return updateExternalModuleReference(<ExternalModuleReference>node,
visitNode((<ExternalModuleReference>node).expression, visitor, isExpression));
// JSX
case SyntaxKind.JsxElement:
return updateJsxElement(<JsxElement>node,
visitNode((<JsxElement>node).openingElement, visitor, isJsxOpeningElement),
nodesVisitor((<JsxElement>node).children, visitor, isJsxChild),
visitNode((<JsxElement>node).closingElement, visitor, isJsxClosingElement));
case SyntaxKind.JsxAttributes:
return updateJsxAttributes(<JsxAttributes>node,
nodesVisitor((<JsxAttributes>node).properties, visitor, isJsxAttributeLike));
case SyntaxKind.JsxSelfClosingElement:
return updateJsxSelfClosingElement(<JsxSelfClosingElement>node,
visitNode((<JsxSelfClosingElement>node).tagName, visitor, isJsxTagNameExpression),
@@ -788,6 +817,10 @@ namespace ts {
visitNode((<JsxAttribute>node).name, visitor, isIdentifier),
visitNode((<JsxAttribute>node).initializer, visitor, isStringLiteralOrJsxExpression));
case SyntaxKind.JsxAttributes:
return updateJsxAttributes(<JsxAttributes>node,
nodesVisitor((<JsxAttributes>node).properties, visitor, isJsxAttributeLike));
case SyntaxKind.JsxSpreadAttribute:
return updateJsxSpreadAttribute(<JsxSpreadAttribute>node,
visitNode((<JsxSpreadAttribute>node).expression, visitor, isExpression));
@@ -797,6 +830,7 @@ namespace ts {
visitNode((<JsxExpression>node).expression, visitor, isExpression));
// Clauses
case SyntaxKind.CaseClause:
return updateCaseClause(<CaseClause>node,
visitNode((<CaseClause>node).expression, visitor, isExpression),
@@ -816,6 +850,7 @@ namespace ts {
visitNode((<CatchClause>node).block, visitor, isBlock));
// Property assignments
case SyntaxKind.PropertyAssignment:
return updatePropertyAssignment(<PropertyAssignment>node,
visitNode((<PropertyAssignment>node).name, visitor, isPropertyName),
@@ -846,9 +881,15 @@ namespace ts {
return updatePartiallyEmittedExpression(<PartiallyEmittedExpression>node,
visitNode((<PartiallyEmittedExpression>node).expression, visitor, isExpression));
case SyntaxKind.CommaListExpression:
return updateCommaList(<CommaListExpression>node,
nodesVisitor((<CommaListExpression>node).elements, visitor, isExpression));
default:
// No need to visit nodes with no children.
return node;
}
}
/**
@@ -934,6 +975,15 @@ namespace ts {
break;
// Type member
case SyntaxKind.PropertySignature:
result = reduceNodes((<PropertySignature>node).modifiers, cbNodes, result);
result = reduceNode((<PropertySignature>node).name, cbNode, result);
result = reduceNode((<PropertySignature>node).questionToken, cbNode, result);
result = reduceNode((<PropertySignature>node).type, cbNode, result);
result = reduceNode((<PropertySignature>node).initializer, cbNode, result);
break;
case SyntaxKind.PropertyDeclaration:
result = reduceNodes((<PropertyDeclaration>node).decorators, cbNodes, result);
result = reduceNodes((<PropertyDeclaration>node).modifiers, cbNodes, result);
@@ -1357,6 +1407,10 @@ namespace ts {
result = reduceNode((<PartiallyEmittedExpression>node).expression, cbNode, result);
break;
case SyntaxKind.CommaListExpression:
result = reduceNodes((<CommaListExpression>node).elements, cbNodes, result);
break;
default:
break;
}
+19 -1
View File
@@ -655,7 +655,7 @@ namespace FourSlash {
ts.zipWith(endMarkers, definitions, (endMarker, definition, i) => {
const marker = this.getMarkerByName(endMarker);
if (marker.fileName !== definition.fileName || marker.position !== definition.textSpan.start) {
this.raiseError(`goToDefinition failed for definition ${i}: expected ${marker.fileName} at ${marker.position}, got ${definition.fileName} at ${definition.textSpan.start}`);
this.raiseError(`goToDefinition failed for definition ${endMarker} (${i}): expected ${marker.fileName} at ${marker.position}, got ${definition.fileName} at ${definition.textSpan.start}`);
}
});
}
@@ -3417,6 +3417,18 @@ namespace FourSlashInterface {
export class VerifyNegatable {
public not: VerifyNegatable;
public allowedClassElementKeywords = [
"public",
"private",
"protected",
"static",
"abstract",
"readonly",
"get",
"set",
"constructor",
"async"
];
constructor(protected state: FourSlash.TestState, private negative = false) {
if (!negative) {
@@ -3453,6 +3465,12 @@ namespace FourSlashInterface {
this.state.verifyCompletionListIsEmpty(this.negative);
}
public completionListContainsClassElementKeywords() {
for (const keyword of this.allowedClassElementKeywords) {
this.completionListContains(keyword, keyword, /*documentation*/ undefined, "keyword");
}
}
public completionListIsGlobal(expected: boolean) {
this.state.verifyCompletionListIsGlobal(expected);
}
+1 -1
View File
@@ -372,7 +372,7 @@ class ProjectRunner extends RunnerBase {
const compilerOptions = compilerResult.program.getCompilerOptions();
ts.forEach(compilerResult.program.getSourceFiles(), sourceFile => {
if (ts.isDeclarationFile(sourceFile)) {
if (sourceFile.isDeclarationFile) {
allInputFiles.unshift({ emittedFileName: sourceFile.fileName, code: sourceFile.text });
}
else if (!(compilerOptions.outFile || compilerOptions.out)) {
@@ -201,14 +201,13 @@ namespace ts {
assert.isTrue(diags.length === 1, "one diagnostic expected");
assert.isTrue(typeof diags[0].messageText === "string" && ((<string>diags[0].messageText).indexOf("Cannot find module") === 0), "should be 'cannot find module' message");
// assert that import will success once file appear on disk
fileMap.set(imported.name, imported);
fileExistsCalledForBar = false;
rootScriptInfo.editContent(0, root.content.length, `import {y} from "bar"`);
diags = project.getLanguageService().getSemanticDiagnostics(root.name);
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
assert.isTrue(diags.length === 0);
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
assert.isTrue(diags.length === 0, "The import should succeed once the imported file appears on disk.");
});
});
}
+12
View File
@@ -241,6 +241,18 @@ namespace ts {
*/`);
parsesCorrectly("argSynonymForParamTag",
`/**
* @arg {number} name1 Description
*/`);
parsesCorrectly("argumentSynonymForParamTag",
`/**
* @argument {number} name1 Description
*/`);
parsesCorrectly("templateTag",
`/**
* @template T
+1 -1
View File
@@ -1042,7 +1042,7 @@ import b = require("./moduleB");
assert.equal(diagnostics1.length, 1, "expected one diagnostic");
createProgram(names, {}, compilerHost, program1);
assert.isTrue(program1.structureIsReused);
assert.isTrue(program1.structureIsReused === StructureIsReused.Completely);
const diagnostics2 = program1.getFileProcessingDiagnostics().getDiagnostics();
assert.equal(diagnostics2.length, 1, "expected one diagnostic");
assert.equal(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic");
+292 -25
View File
@@ -159,12 +159,14 @@ namespace ts {
return program;
}
function updateProgram(oldProgram: ProgramWithSourceTexts, rootNames: string[], options: CompilerOptions, updater: (files: NamedSourceText[]) => void) {
const texts: NamedSourceText[] = (<ProgramWithSourceTexts>oldProgram).sourceTexts.slice(0);
updater(texts);
const host = createTestCompilerHost(texts, options.target, oldProgram);
function updateProgram(oldProgram: ProgramWithSourceTexts, rootNames: string[], options: CompilerOptions, updater: (files: NamedSourceText[]) => void, newTexts?: NamedSourceText[]) {
if (!newTexts) {
newTexts = (<ProgramWithSourceTexts>oldProgram).sourceTexts.slice(0);
}
updater(newTexts);
const host = createTestCompilerHost(newTexts, options.target, oldProgram);
const program = <ProgramWithSourceTexts>createProgram(rootNames, options, host, oldProgram);
program.sourceTexts = texts;
program.sourceTexts = newTexts;
program.host = host;
return program;
}
@@ -217,13 +219,15 @@ namespace ts {
describe("Reuse program structure", () => {
const target = ScriptTarget.Latest;
const files = [
{ name: "a.ts", text: SourceText.New(
`
const files: NamedSourceText[] = [
{
name: "a.ts", text: SourceText.New(
`
/// <reference path='b.ts'/>
/// <reference path='non-existing-file.ts'/>
/// <reference types="typerefs" />
`, "", `var x = 1`) },
`, "", `var x = 1`)
},
{ name: "b.ts", text: SourceText.New(`/// <reference path='c.ts'/>`, "", `var y = 2`) },
{ name: "c.ts", text: SourceText.New("", "", `var z = 1;`) },
{ name: "types/typerefs/index.d.ts", text: SourceText.New("", "", `declare let z: number;`) },
@@ -234,7 +238,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);
assert.isTrue(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);
@@ -245,7 +249,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);
assert.isTrue(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);
@@ -259,19 +263,19 @@ namespace ts {
`;
files[0].text = files[0].text.updateReferences(newReferences);
});
assert.isTrue(!program_1.structureIsReused);
assert.isTrue(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);
assert.isTrue(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);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
});
it("fails if change affects imports", () => {
@@ -279,7 +283,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);
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
});
it("fails if change affects type directives", () => {
@@ -291,25 +295,25 @@ namespace ts {
/// <reference types="typerefs1" />`;
files[0].text = files[0].text.updateReferences(newReferences);
});
assert.isTrue(!program_1.structureIsReused);
assert.isTrue(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);
assert.isTrue(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);
assert.isTrue(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);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
});
it("resolution cache follows imports", () => {
@@ -328,7 +332,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);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
// content of resolution cache should not change
checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") }));
@@ -338,7 +342,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);
assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules);
checkResolvedModulesCache(program_3, "a.ts", /*expectedContent*/ undefined);
const program_4 = updateProgram(program_3, ["a.ts"], options, files => {
@@ -347,7 +351,7 @@ namespace ts {
`;
files[0].text = files[0].text.updateImportsAndExports(newImports);
});
assert.isTrue(!program_3.structureIsReused);
assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules);
checkResolvedModulesCache(program_4, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts"), "c": undefined }));
});
@@ -365,7 +369,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);
assert.isTrue(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 } }));
@@ -376,7 +380,7 @@ namespace ts {
files[0].text = files[0].text.updateReferences("");
});
assert.isTrue(!program_2.structureIsReused);
assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules);
checkResolvedTypeDirectivesCache(program_3, "/a.ts", /*expectedContent*/ undefined);
updateProgram(program_3, ["/a.ts"], options, files => {
@@ -385,10 +389,74 @@ namespace ts {
`;
files[0].text = files[0].text.updateReferences(newReferences);
});
assert.isTrue(!program_3.structureIsReused);
assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules);
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
});
it("fetches imports after npm install", () => {
const file1Ts = { name: "file1.ts", text: SourceText.New("", `import * as a from "a";`, "const myX: number = a.x;") };
const file2Ts = { name: "file2.ts", text: SourceText.New("", "", "") };
const indexDTS = { name: "node_modules/a/index.d.ts", text: SourceText.New("", "export declare let x: number;", "") };
const options: CompilerOptions = { target: ScriptTarget.ES2015, traceResolution: true, moduleResolution: ModuleResolutionKind.NodeJs };
const rootFiles = [file1Ts, file2Ts];
const filesAfterNpmInstall = [file1Ts, file2Ts, indexDTS];
const initialProgram = newProgram(rootFiles, rootFiles.map(f => f.name), options);
{
assert.deepEqual(initialProgram.host.getTrace(),
[
"======== Resolving module 'a' from 'file1.ts'. ========",
"Explicitly specified module resolution kind: 'NodeJs'.",
"Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.",
"File 'node_modules/a.ts' does not exist.",
"File 'node_modules/a.tsx' does not exist.",
"File 'node_modules/a.d.ts' does not exist.",
"File 'node_modules/a/package.json' does not exist.",
"File 'node_modules/a/index.ts' does not exist.",
"File 'node_modules/a/index.tsx' does not exist.",
"File 'node_modules/a/index.d.ts' does not exist.",
"File 'node_modules/@types/a.d.ts' does not exist.",
"File 'node_modules/@types/a/package.json' does not exist.",
"File 'node_modules/@types/a/index.d.ts' does not exist.",
"Loading module 'a' from 'node_modules' folder, target file type 'JavaScript'.",
"File 'node_modules/a.js' does not exist.",
"File 'node_modules/a.jsx' does not exist.",
"File 'node_modules/a/package.json' does not exist.",
"File 'node_modules/a/index.js' does not exist.",
"File 'node_modules/a/index.jsx' does not exist.",
"======== Module name 'a' was not resolved. ========"
],
"initialProgram: execute module resolution normally.");
const initialProgramDiagnostics = initialProgram.getSemanticDiagnostics(initialProgram.getSourceFile("file1.ts"));
assert(initialProgramDiagnostics.length === 1, `initialProgram: import should fail.`);
}
const afterNpmInstallProgram = updateProgram(initialProgram, rootFiles.map(f => f.name), options, f => {
f[1].text = f[1].text.updateReferences(`/// <reference no-default-lib="true"/>`);
}, filesAfterNpmInstall);
{
assert.deepEqual(afterNpmInstallProgram.host.getTrace(),
[
"======== Resolving module 'a' from 'file1.ts'. ========",
"Explicitly specified module resolution kind: 'NodeJs'.",
"Loading module 'a' from 'node_modules' folder, target file type 'TypeScript'.",
"File 'node_modules/a.ts' does not exist.",
"File 'node_modules/a.tsx' does not exist.",
"File 'node_modules/a.d.ts' does not exist.",
"File 'node_modules/a/package.json' does not exist.",
"File 'node_modules/a/index.ts' does not exist.",
"File 'node_modules/a/index.tsx' does not exist.",
"File 'node_modules/a/index.d.ts' exist - use it as a name resolution result.",
"======== Module name 'a' was successfully resolved to 'node_modules/a/index.d.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.`);
}
});
it("can reuse ambient module declarations from non-modified files", () => {
const files = [
{ name: "/a/b/app.ts", text: SourceText.New("", "import * as fs from 'fs'", "") },
@@ -468,7 +536,206 @@ namespace ts {
"File '/fs.jsx' does not exist.",
"======== Module name 'fs' was not resolved. ========",
], "should look for 'fs' again since node.d.ts was changed");
});
it("can reuse module resolutions from non-modified files", () => {
const files = [
{ name: "a1.ts", text: SourceText.New("", "", "let x = 1;") },
{ name: "a2.ts", text: SourceText.New("", "", "let x = 1;") },
{ name: "b1.ts", text: SourceText.New("", "export class B { x: number; }", "") },
{ name: "b2.ts", text: SourceText.New("", "export class B { x: number; }", "") },
{ name: "node_modules/@types/typerefs1/index.d.ts", text: SourceText.New("", "", "declare let z: string;") },
{ name: "node_modules/@types/typerefs2/index.d.ts", text: SourceText.New("", "", "declare let z: string;") },
{
name: "f1.ts",
text:
SourceText.New(
`/// <reference path="a1.ts"/>${newLine}/// <reference types="typerefs1"/>${newLine}/// <reference no-default-lib="true"/>`,
`import { B } from './b1';${newLine}export let BB = B;`,
"declare module './b1' { interface B { y: string; } }")
},
{
name: "f2.ts",
text: SourceText.New(
`/// <reference path="a2.ts"/>${newLine}/// <reference types="typerefs2"/>`,
`import { B } from './b2';${newLine}import { BB } from './f1';`,
"(new BB).x; (new BB).y;")
},
];
const options: CompilerOptions = { target: ScriptTarget.ES2015, traceResolution: true, moduleResolution: ModuleResolutionKind.Classic };
const program_1 = newProgram(files, files.map(f => f.name), options);
let expectedErrors = 0;
{
assert.deepEqual(program_1.host.getTrace(),
[
"======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs1/package.json' does not exist.",
"File 'node_modules/@types/typerefs1/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs1' was successfully resolved to 'node_modules/@types/typerefs1/index.d.ts', primary: true. ========",
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"======== Resolving module './b2' from 'f2.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b2.ts' exist - use it as a name resolution result.",
"======== Module name './b2' was successfully resolved to 'b2.ts'. ========",
"======== Resolving module './f1' from 'f2.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"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.");
const program_1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("f2.ts"));
assert(program_1Diagnostics.length === expectedErrors, `initial program should be well-formed`);
}
const indexOfF1 = 6;
const program_2 = updateProgram(program_1, program_1.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateReferences(`/// <reference path="a1.ts"/>${newLine}/// <reference types="typerefs1"/>`);
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
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.deepEqual(program_2.host.getTrace(), [
"======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs1/package.json' does not exist.",
"File 'node_modules/@types/typerefs1/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs1' was successfully resolved to 'node_modules/@types/typerefs1/index.d.ts', primary: true. ========",
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
], "program_2: reuse module resolutions in f2 since it is unchanged");
}
const program_3 = updateProgram(program_2, program_2.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateReferences(`/// <reference path="a1.ts"/>`);
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
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.deepEqual(program_3.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
], "program_3: reuse module resolutions in f2 since it is unchanged");
}
const program_4 = updateProgram(program_3, program_3.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateReferences("");
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
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.deepEqual(program_4.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
], "program_4: reuse module resolutions in f2 since it is unchanged");
}
const program_5 = updateProgram(program_4, program_4.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateImportsAndExports(`import { B } from './b1';`);
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
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.deepEqual(program_5.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========"
], "program_5: exports do not affect program structure, so f2's resolutions are silently reused.");
}
const program_6 = updateProgram(program_5, program_5.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateProgram("");
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
const program_6Diagnostics = program_6.getSemanticDiagnostics(program_6.getSourceFile("f2.ts"));
assert(program_6Diagnostics.length === expectedErrors, `import of BB in f1 fails.`);
assert.deepEqual(program_6.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
"======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
], "program_6: reuse module resolutions in f2 since it is unchanged");
}
const program_7 = updateProgram(program_6, program_6.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateImportsAndExports("");
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
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.deepEqual(program_7.host.getTrace(), [
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
"File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
], "program_7 should reuse module resolutions in f2 since it is unchanged");
}
});
});
+69 -33
View File
@@ -3,41 +3,77 @@
namespace ts {
describe("TransformAPI", () => {
function transformsCorrectly(name: string, source: string, transformers: TransformerFactory<SourceFile>[]) {
it(name, () => {
Harness.Baseline.runBaseline(`transformApi/transformsCorrectly.${name}.js`, () => {
const transformed = transform(createSourceFile("source.ts", source, ScriptTarget.ES2015), transformers);
const printer = createPrinter({ newLine: NewLineKind.CarriageReturnLineFeed }, {
onEmitNode: transformed.emitNodeWithNotification,
substituteNode: transformed.substituteNode
});
const result = printer.printBundle(createBundle(transformed.transformed));
transformed.dispose();
return result;
});
function replaceUndefinedWithVoid0(context: ts.TransformationContext) {
const previousOnSubstituteNode = context.onSubstituteNode;
context.enableSubstitution(SyntaxKind.Identifier);
context.onSubstituteNode = (hint, node) => {
node = previousOnSubstituteNode(hint, node);
if (hint === EmitHint.Expression && node.kind === SyntaxKind.Identifier && (<Identifier>node).text === "undefined") {
node = createPartiallyEmittedExpression(
addSyntheticTrailingComment(
setTextRange(
createVoidZero(),
node),
SyntaxKind.MultiLineCommentTrivia, "undefined"));
}
return node;
};
return (file: ts.SourceFile) => file;
}
function replaceIdentifiersNamedOldNameWithNewName(context: ts.TransformationContext) {
const previousOnSubstituteNode = context.onSubstituteNode;
context.enableSubstitution(SyntaxKind.Identifier);
context.onSubstituteNode = (hint, node) => {
node = previousOnSubstituteNode(hint, node);
if (node.kind === SyntaxKind.Identifier && (<Identifier>node).text === "oldName") {
node = setTextRange(createIdentifier("newName"), node);
}
return node;
};
return (file: ts.SourceFile) => file;
}
function transformSourceFile(sourceText: string, transformers: TransformerFactory<SourceFile>[]) {
const transformed = transform(createSourceFile("source.ts", sourceText, ScriptTarget.ES2015), transformers);
const printer = createPrinter({ newLine: NewLineKind.CarriageReturnLineFeed }, {
onEmitNode: transformed.emitNodeWithNotification,
substituteNode: transformed.substituteNode
});
const result = printer.printBundle(createBundle(transformed.transformed));
transformed.dispose();
return result;
}
function testBaseline(testName: string, test: () => string) {
it(testName, () => {
Harness.Baseline.runBaseline(`transformApi/transformsCorrectly.${testName}.js`, test);
});
}
transformsCorrectly("substitution", `
var a = undefined;
`, [
context => {
const previousOnSubstituteNode = context.onSubstituteNode;
context.enableSubstitution(SyntaxKind.Identifier);
context.onSubstituteNode = (hint, node) => {
node = previousOnSubstituteNode(hint, node);
if (hint === EmitHint.Expression && node.kind === SyntaxKind.Identifier && (<Identifier>node).text === "undefined") {
node = createPartiallyEmittedExpression(
addSyntheticTrailingComment(
setTextRange(
createVoidZero(),
node),
SyntaxKind.MultiLineCommentTrivia, "undefined"));
}
return node;
};
return file => file;
}
]);
testBaseline("substitution", () => {
return transformSourceFile(`var a = undefined;`, [replaceUndefinedWithVoid0]);
});
testBaseline("types", () => {
return transformSourceFile(`let a: () => void`, [
context => file => visitNode(file, function visitor(node: Node): VisitResult<Node> {
return visitEachChild(node, visitor, context);
})
]);
});
testBaseline("fromTranspileModule", () => {
return ts.transpileModule(`var oldName = undefined;`, {
transformers: {
before: [replaceUndefinedWithVoid0],
after: [replaceIdentifiersNamedOldNameWithNewName]
},
compilerOptions: {
newLine: NewLineKind.CarriageReturnLineFeed
}
}).outputText;
});
});
}
+71 -7
View File
@@ -32,17 +32,17 @@ interface Array<T> {
[Symbol.iterator](): IterableIterator<T>;
/**
* Returns an array of key, value pairs for every entry in the array
* Returns an iterable of key, value pairs for every entry in the array
*/
entries(): IterableIterator<[number, T]>;
/**
* Returns an list of keys in the array
* Returns an iterable of keys in the array
*/
keys(): IterableIterator<number>;
/**
* Returns an list of values in the array
* Returns an iterable of values in the array
*/
values(): IterableIterator<T>;
}
@@ -66,21 +66,21 @@ interface ArrayConstructor {
}
interface ReadonlyArray<T> {
/** Iterator */
/** Iterator of values in the array. */
[Symbol.iterator](): IterableIterator<T>;
/**
* Returns an array of key, value pairs for every entry in the array
* Returns an iterable of key, value pairs for every entry in the array
*/
entries(): IterableIterator<[number, T]>;
/**
* Returns an list of keys in the array
* Returns an iterable of keys in the array
*/
keys(): IterableIterator<number>;
/**
* Returns an list of values in the array
* Returns an iterable of values in the array
*/
values(): IterableIterator<T>;
}
@@ -91,9 +91,42 @@ interface IArguments {
}
interface Map<K, V> {
/** Returns an iterable of entries in the map. */
[Symbol.iterator](): IterableIterator<[K, V]>;
/**
* Returns an iterable of key, value pairs for every entry in the map.
*/
entries(): IterableIterator<[K, V]>;
/**
* Returns an iterable of keys in the map
*/
keys(): IterableIterator<K>;
/**
* Returns an iterable of values in the map
*/
values(): IterableIterator<V>;
}
interface ReadonlyMap<K, V> {
/** Returns an iterable of entries in the map. */
[Symbol.iterator](): IterableIterator<[K, V]>;
/**
* Returns an iterable of key, value pairs for every entry in the map.
*/
entries(): IterableIterator<[K, V]>;
/**
* Returns an iterable of keys in the map
*/
keys(): IterableIterator<K>;
/**
* Returns an iterable of values in the map
*/
values(): IterableIterator<V>;
}
@@ -108,9 +141,40 @@ interface WeakMapConstructor {
}
interface Set<T> {
/** Iterates over values in the set. */
[Symbol.iterator](): IterableIterator<T>;
/**
* Returns an iterable of [v,v] pairs for every value `v` in the set.
*/
entries(): IterableIterator<[T, T]>;
/**
* Despite its name, returns an iterable of the values in the set,
*/
keys(): IterableIterator<T>;
/**
* Returns an iterable of values in the set.
*/
values(): IterableIterator<T>;
}
interface ReadonlySet<T> {
/** Iterates over values in the set. */
[Symbol.iterator](): IterableIterator<T>;
/**
* Returns an iterable of [v,v] pairs for every value `v` in the set.
*/
entries(): IterableIterator<[T, T]>;
/**
* Despite its name, returns an iterable of the values in the set,
*/
keys(): IterableIterator<T>;
/**
* Returns an iterable of values in the set.
*/
values(): IterableIterator<T>;
}
+1 -1
View File
@@ -3,7 +3,7 @@ interface ProxyHandler<T extends object> {
setPrototypeOf? (target: T, v: any): boolean;
isExtensible? (target: T): boolean;
preventExtensions? (target: T): boolean;
getOwnPropertyDescriptor? (target: T, p: PropertyKey): PropertyDescriptor;
getOwnPropertyDescriptor? (target: T, p: PropertyKey): PropertyDescriptor | undefined;
has? (target: T, p: PropertyKey): boolean;
get? (target: T, p: PropertyKey, receiver: any): any;
set? (target: T, p: PropertyKey, value: any, receiver: any): boolean;
+92 -1
View File
@@ -23,5 +23,96 @@ interface SharedArrayBufferConstructor {
readonly prototype: SharedArrayBuffer;
new (byteLength: number): SharedArrayBuffer;
}
declare var SharedArrayBuffer: SharedArrayBufferConstructor;
declare var SharedArrayBuffer: SharedArrayBufferConstructor;
interface ArrayBufferTypes {
SharedArrayBuffer: SharedArrayBuffer;
}
interface Atomics {
/**
* Adds a value to the value at the given position in the array, returning the original value.
* Until this atomic operation completes, any other read or write operation against the array
* will block.
*/
add(typedArray: Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array, index: number, value: number): number;
/**
* Stores the bitwise AND of a value with the value at the given position in the array,
* returning the original value. Until this atomic operation completes, any other read or
* write operation against the array will block.
*/
and(typedArray: Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array, index: number, value: number): number;
/**
* Replaces the value at the given position in the array if the original value equals the given
* expected value, returning the original value. Until this atomic operation completes, any
* other read or write operation against the array will block.
*/
compareExchange(typedArray: Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array, index: number, expectedValue: number, replacementValue: number): number;
/**
* Replaces the value at the given position in the array, returning the original value. Until
* this atomic operation completes, any other read or write operation against the array will
* block.
*/
exchange(typedArray: Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array, index: number, value: number): number;
/**
* Returns a value indicating whether high-performance algorithms can use atomic operations
* (`true`) or must use locks (`false`) for the given number of bytes-per-element of a typed
* array.
*/
isLockFree(size: number): boolean;
/**
* Returns the value at the given position in the array. Until this atomic operation completes,
* any other read or write operation against the array will block.
*/
load(typedArray: Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array, index: number): number;
/**
* Stores the bitwise OR of a value with the value at the given position in the array,
* returning the original value. Until this atomic operation completes, any other read or write
* operation against the array will block.
*/
or(typedArray: Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array, index: number, value: number): number;
/**
* Stores a value at the given position in the array, returning the new value. Until this
* atomic operation completes, any other read or write operation against the array will block.
*/
store(typedArray: Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array, index: number, value: number): number;
/**
* Subtracts a value from the value at the given position in the array, returning the original
* value. Until this atomic operation completes, any other read or write operation against the
* array will block.
*/
sub(typedArray: Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array, index: number, value: number): number;
/**
* If the value at the given position in the array is equal to the provided value, the current
* agent is put to sleep causing execution to suspend until the timeout expires (returning
* `"timed-out"`) or until the agent is awoken (returning `"ok"`); otherwise, returns
* `"not-equal"`.
*/
wait(typedArray: Int32Array, index: number, value: number, timeout?: number): "ok" | "not-equal" | "timed-out";
/**
* Wakes up sleeping agents that are waiting on the given index of the array, returning the
* number of agents that were awoken.
*/
wake(typedArray: Int32Array, index: number, count: number): number;
/**
* Stores the bitwise XOR of a value with the value at the given position in the array,
* returning the original value. Until this atomic operation completes, any other read or write
* operation against the array will block.
*/
xor(typedArray: Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array, index: number, value: number): number;
readonly [Symbol.toStringTag]: "Atomics";
}
declare var Atomics: Atomics;
+28 -20
View File
@@ -1386,6 +1386,14 @@ interface ArrayBuffer {
slice(begin: number, end?: number): ArrayBuffer;
}
/**
* Allowed ArrayBuffer types for the buffer of an ArrayBufferView and related Typed Arrays.
*/
interface ArrayBufferTypes {
ArrayBuffer: ArrayBuffer;
}
type ArrayBufferLike = ArrayBufferTypes[keyof ArrayBufferTypes];
interface ArrayBufferConstructor {
readonly prototype: ArrayBuffer;
new (byteLength: number): ArrayBuffer;
@@ -1397,7 +1405,7 @@ interface ArrayBufferView {
/**
* The ArrayBuffer instance referenced by the array.
*/
buffer: ArrayBuffer;
buffer: ArrayBufferLike;
/**
* The length in bytes of the array.
@@ -1539,7 +1547,7 @@ interface DataView {
}
interface DataViewConstructor {
new (buffer: ArrayBuffer, byteOffset?: number, byteLength?: number): DataView;
new (buffer: ArrayBufferLike, byteOffset?: number, byteLength?: number): DataView;
}
declare const DataView: DataViewConstructor;
@@ -1556,7 +1564,7 @@ interface Int8Array {
/**
* The ArrayBuffer instance referenced by the array.
*/
readonly buffer: ArrayBuffer;
readonly buffer: ArrayBufferLike;
/**
* The length in bytes of the array.
@@ -1799,7 +1807,7 @@ interface Int8ArrayConstructor {
readonly prototype: Int8Array;
new (length: number): Int8Array;
new (array: ArrayLike<number>): Int8Array;
new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Int8Array;
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): Int8Array;
/**
* The size in bytes of each element in the array.
@@ -1840,7 +1848,7 @@ interface Uint8Array {
/**
* The ArrayBuffer instance referenced by the array.
*/
readonly buffer: ArrayBuffer;
readonly buffer: ArrayBufferLike;
/**
* The length in bytes of the array.
@@ -2084,7 +2092,7 @@ interface Uint8ArrayConstructor {
readonly prototype: Uint8Array;
new (length: number): Uint8Array;
new (array: ArrayLike<number>): Uint8Array;
new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Uint8Array;
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): Uint8Array;
/**
* The size in bytes of each element in the array.
@@ -2125,7 +2133,7 @@ interface Uint8ClampedArray {
/**
* The ArrayBuffer instance referenced by the array.
*/
readonly buffer: ArrayBuffer;
readonly buffer: ArrayBufferLike;
/**
* The length in bytes of the array.
@@ -2369,7 +2377,7 @@ interface Uint8ClampedArrayConstructor {
readonly prototype: Uint8ClampedArray;
new (length: number): Uint8ClampedArray;
new (array: ArrayLike<number>): Uint8ClampedArray;
new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Uint8ClampedArray;
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): Uint8ClampedArray;
/**
* The size in bytes of each element in the array.
@@ -2409,7 +2417,7 @@ interface Int16Array {
/**
* The ArrayBuffer instance referenced by the array.
*/
readonly buffer: ArrayBuffer;
readonly buffer: ArrayBufferLike;
/**
* The length in bytes of the array.
@@ -2653,7 +2661,7 @@ interface Int16ArrayConstructor {
readonly prototype: Int16Array;
new (length: number): Int16Array;
new (array: ArrayLike<number>): Int16Array;
new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Int16Array;
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): Int16Array;
/**
* The size in bytes of each element in the array.
@@ -2694,7 +2702,7 @@ interface Uint16Array {
/**
* The ArrayBuffer instance referenced by the array.
*/
readonly buffer: ArrayBuffer;
readonly buffer: ArrayBufferLike;
/**
* The length in bytes of the array.
@@ -2938,7 +2946,7 @@ interface Uint16ArrayConstructor {
readonly prototype: Uint16Array;
new (length: number): Uint16Array;
new (array: ArrayLike<number>): Uint16Array;
new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Uint16Array;
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): Uint16Array;
/**
* The size in bytes of each element in the array.
@@ -2978,7 +2986,7 @@ interface Int32Array {
/**
* The ArrayBuffer instance referenced by the array.
*/
readonly buffer: ArrayBuffer;
readonly buffer: ArrayBufferLike;
/**
* The length in bytes of the array.
@@ -3222,7 +3230,7 @@ interface Int32ArrayConstructor {
readonly prototype: Int32Array;
new (length: number): Int32Array;
new (array: ArrayLike<number>): Int32Array;
new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Int32Array;
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): Int32Array;
/**
* The size in bytes of each element in the array.
@@ -3262,7 +3270,7 @@ interface Uint32Array {
/**
* The ArrayBuffer instance referenced by the array.
*/
readonly buffer: ArrayBuffer;
readonly buffer: ArrayBufferLike;
/**
* The length in bytes of the array.
@@ -3506,7 +3514,7 @@ interface Uint32ArrayConstructor {
readonly prototype: Uint32Array;
new (length: number): Uint32Array;
new (array: ArrayLike<number>): Uint32Array;
new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Uint32Array;
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): Uint32Array;
/**
* The size in bytes of each element in the array.
@@ -3546,7 +3554,7 @@ interface Float32Array {
/**
* The ArrayBuffer instance referenced by the array.
*/
readonly buffer: ArrayBuffer;
readonly buffer: ArrayBufferLike;
/**
* The length in bytes of the array.
@@ -3790,7 +3798,7 @@ interface Float32ArrayConstructor {
readonly prototype: Float32Array;
new (length: number): Float32Array;
new (array: ArrayLike<number>): Float32Array;
new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Float32Array;
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): Float32Array;
/**
* The size in bytes of each element in the array.
@@ -3831,7 +3839,7 @@ interface Float64Array {
/**
* The ArrayBuffer instance referenced by the array.
*/
readonly buffer: ArrayBuffer;
readonly buffer: ArrayBufferLike;
/**
* The length in bytes of the array.
@@ -4075,7 +4083,7 @@ interface Float64ArrayConstructor {
readonly prototype: Float64Array;
new (length: number): Float64Array;
new (array: ArrayLike<number>): Float64Array;
new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Float64Array;
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): Float64Array;
/**
* The size in bytes of each element in the array.
+1 -1
View File
@@ -552,7 +552,7 @@ namespace ts.server {
// bump up the version if
// - oldProgram is not set - this is a first time updateGraph is called
// - newProgram is different from the old program and structure of the old program was not reused.
if (!oldProgram || (this.program !== oldProgram && !oldProgram.structureIsReused)) {
if (!oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused & StructureIsReused.Completely))) {
hasChanges = true;
if (oldProgram) {
for (const f of oldProgram.getSourceFiles()) {
+1
View File
@@ -2267,6 +2267,7 @@ namespace ts.server.protocol {
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces?: boolean;
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces?: boolean;
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean;
insertSpaceAfterTypeAssertion?: boolean;
insertSpaceBeforeFunctionParenthesis?: boolean;
placeOpenBraceOnNewLineForFunctions?: boolean;
placeOpenBraceOnNewLineForControlBlocks?: boolean;
+3 -4
View File
@@ -708,12 +708,11 @@ namespace ts.server {
}
sys.require = (initialDir: string, moduleName: string): RequireResult => {
const result = nodeModuleNameResolverWorker(moduleName, initialDir + "/program.ts", { moduleResolution: ts.ModuleResolutionKind.NodeJs, allowJs: true }, sys, /*cache*/ undefined, /*jsOnly*/ true);
try {
return { module: require(result.resolvedModule.resolvedFileName), error: undefined };
return { module: require(resolveJavaScriptModule(moduleName, initialDir, sys)), error: undefined };
}
catch (e) {
return { module: undefined, error: e };
catch (error) {
return { module: undefined, error };
}
};
@@ -96,6 +96,9 @@ namespace ts.server.typingsInstaller {
this.log.writeLine(`Updating ${TypesRegistryPackageName} npm package...`);
}
this.execSync(`${this.npmPath} install ${TypesRegistryPackageName}`, { cwd: globalTypingsCacheLocation, stdio: "ignore" });
if (this.log.isEnabled()) {
this.log.writeLine(`Updated ${TypesRegistryPackageName} npm package`);
}
}
catch (e) {
if (this.log.isEnabled()) {
+11 -10
View File
@@ -1,7 +1,8 @@
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1.code],
errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1.code,
Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code],
getCodeActions: getActionsForAddMissingMember
});
@@ -110,16 +111,16 @@ namespace ts.codefix {
if (!isStatic) {
const stringTypeNode = createKeywordTypeNode(SyntaxKind.StringKeyword);
const indexingParameter = createParameter(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*dotDotDotToken*/ undefined,
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*dotDotDotToken*/ undefined,
"x",
/*questionToken*/ undefined,
/*questionToken*/ undefined,
stringTypeNode,
/*initializer*/ undefined);
const indexSignature = createIndexSignatureDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*initializer*/ undefined);
const indexSignature = createIndexSignature(
/*decorators*/ undefined,
/*modifiers*/ undefined,
[indexingParameter],
typeNode);
@@ -135,4 +136,4 @@ namespace ts.codefix {
return actions;
}
}
}
}
+53
View File
@@ -0,0 +1,53 @@
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code,
Diagnostics.Cannot_find_name_0_Did_you_mean_1.code],
getCodeActions: getActionsForCorrectSpelling
});
function getActionsForCorrectSpelling(context: CodeFixContext): CodeAction[] | undefined {
const sourceFile = context.sourceFile;
// This is the identifier of the misspelled word. eg:
// this.speling = 1;
// ^^^^^^^
const node = getTokenAtPosition(sourceFile, context.span.start);
const checker = context.program.getTypeChecker();
let suggestion: string;
if (node.kind === SyntaxKind.Identifier && isPropertyAccessExpression(node.parent)) {
const containingType = checker.getTypeAtLocation(node.parent.expression);
suggestion = checker.getSuggestionForNonexistentProperty(node as Identifier, containingType);
}
else {
const meaning = getMeaningFromLocation(node);
suggestion = checker.getSuggestionForNonexistentSymbol(node, getTextOfNode(node), convertSemanticMeaningToSymbolFlags(meaning));
}
if (suggestion) {
return [{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_spelling_to_0), [suggestion]),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start: node.getStart(), length: node.getWidth() },
newText: suggestion
}],
}],
}];
}
}
function convertSemanticMeaningToSymbolFlags(meaning: SemanticMeaning): SymbolFlags {
let flags = 0;
if (meaning & SemanticMeaning.Namespace) {
flags |= SymbolFlags.Namespace;
}
if (meaning & SemanticMeaning.Type) {
flags |= SymbolFlags.Type;
}
if (meaning & SemanticMeaning.Value) {
flags |= SymbolFlags.Value;
}
return flags;
}
}
+1
View File
@@ -1,5 +1,6 @@
/// <reference path="fixClassIncorrectlyImplementsInterface.ts" />
/// <reference path="fixAddMissingMember.ts" />
/// <reference path="fixSpelling.ts" />
/// <reference path="fixClassDoesntImplementInheritedAbstractMember.ts" />
/// <reference path="fixClassSuperMustPrecedeThisAccess.ts" />
/// <reference path="fixConstructorForDerivedNeedSuperCall.ts" />
+4 -4
View File
@@ -63,7 +63,7 @@ namespace ts.codefix {
const declaration = declarations[0] as Declaration;
// Clone name to remove leading trivia.
const name = getSynthesizedClone(<PropertyName>declaration.name);
const name = getSynthesizedClone(getNameOfDeclaration(declaration)) as PropertyName;
const visibilityModifier = createVisibilityModifier(getModifierFlags(declaration));
const modifiers = visibilityModifier ? createNodeArray([visibilityModifier]) : undefined;
const type = checker.getWidenedType(checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration));
@@ -130,7 +130,7 @@ namespace ts.codefix {
}
function signatureToMethodDeclaration(signature: Signature, enclosingDeclaration: Node, body?: Block) {
const signatureDeclaration = <MethodDeclaration>checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration);
const signatureDeclaration = <MethodDeclaration>checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.SuppressAnyReturnType);
if (signatureDeclaration) {
signatureDeclaration.decorators = undefined;
signatureDeclaration.modifiers = modifiers;
@@ -200,7 +200,7 @@ namespace ts.codefix {
}
export function createStubbedMethod(modifiers: Modifier[], name: PropertyName, optional: boolean, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], returnType: TypeNode | undefined) {
return createMethodDeclaration(
return createMethod(
/*decorators*/ undefined,
modifiers,
/*asteriskToken*/ undefined,
@@ -231,4 +231,4 @@ namespace ts.codefix {
}
return undefined;
}
}
}
+390 -388
View File
@@ -1,5 +1,14 @@
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [
Diagnostics.Cannot_find_name_0.code,
Diagnostics.Cannot_find_name_0_Did_you_mean_1.code,
Diagnostics.Cannot_find_namespace_0.code,
Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code
],
getCodeActions: getImportCodeActions
});
type ImportCodeActionKind = "CodeChange" | "InsertingIntoExistingImport" | "NewImport";
interface ImportCodeAction extends CodeAction {
@@ -113,465 +122,458 @@ namespace ts.codefix {
}
}
registerCodeFix({
errorCodes: [
Diagnostics.Cannot_find_name_0.code,
Diagnostics.Cannot_find_namespace_0.code,
Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code
],
getCodeActions: (context: CodeFixContext) => {
const sourceFile = context.sourceFile;
const checker = context.program.getTypeChecker();
const allSourceFiles = context.program.getSourceFiles();
const useCaseSensitiveFileNames = context.host.useCaseSensitiveFileNames ? context.host.useCaseSensitiveFileNames() : false;
function getImportCodeActions(context: CodeFixContext): ImportCodeAction[] {
const sourceFile = context.sourceFile;
const checker = context.program.getTypeChecker();
const allSourceFiles = context.program.getSourceFiles();
const useCaseSensitiveFileNames = context.host.useCaseSensitiveFileNames ? context.host.useCaseSensitiveFileNames() : false;
const token = getTokenAtPosition(sourceFile, context.span.start);
const name = token.getText();
const symbolIdActionMap = new ImportCodeActionMap();
const token = getTokenAtPosition(sourceFile, context.span.start);
const name = token.getText();
const symbolIdActionMap = new ImportCodeActionMap();
// this is a module id -> module import declaration map
const cachedImportDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[][] = [];
let lastImportDeclaration: Node;
// this is a module id -> module import declaration map
const cachedImportDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[][] = [];
let lastImportDeclaration: Node;
const currentTokenMeaning = getMeaningFromLocation(token);
if (context.errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code) {
const symbol = checker.getAliasedSymbol(checker.getSymbolAtLocation(token));
return getCodeActionForImport(symbol, /*isDefault*/ false, /*isNamespaceImport*/ true);
const currentTokenMeaning = getMeaningFromLocation(token);
if (context.errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code) {
const symbol = checker.getAliasedSymbol(checker.getSymbolAtLocation(token));
return getCodeActionForImport(symbol, /*isDefault*/ false, /*isNamespaceImport*/ true);
}
const candidateModules = checker.getAmbientModules();
for (const otherSourceFile of allSourceFiles) {
if (otherSourceFile !== sourceFile && isExternalOrCommonJsModule(otherSourceFile)) {
candidateModules.push(otherSourceFile.symbol);
}
}
const candidateModules = checker.getAmbientModules();
for (const otherSourceFile of allSourceFiles) {
if (otherSourceFile !== sourceFile && isExternalOrCommonJsModule(otherSourceFile)) {
candidateModules.push(otherSourceFile.symbol);
for (const moduleSymbol of candidateModules) {
context.cancellationToken.throwIfCancellationRequested();
// check the default export
const defaultExport = checker.tryGetMemberInModuleExports("default", moduleSymbol);
if (defaultExport) {
const localSymbol = getLocalSymbolForExportDefault(defaultExport);
if (localSymbol && localSymbol.name === name && checkSymbolHasMeaning(localSymbol, currentTokenMeaning)) {
// check if this symbol is already used
const symbolId = getUniqueSymbolId(localSymbol);
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, /*isDefault*/ true));
}
}
for (const moduleSymbol of candidateModules) {
context.cancellationToken.throwIfCancellationRequested();
// check exports with the same name
const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExports(name, moduleSymbol);
if (exportSymbolWithIdenticalName && checkSymbolHasMeaning(exportSymbolWithIdenticalName, currentTokenMeaning)) {
const symbolId = getUniqueSymbolId(exportSymbolWithIdenticalName);
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol));
}
}
// check the default export
const defaultExport = checker.tryGetMemberInModuleExports("default", moduleSymbol);
if (defaultExport) {
const localSymbol = getLocalSymbolForExportDefault(defaultExport);
if (localSymbol && localSymbol.name === name && checkSymbolHasMeaning(localSymbol, currentTokenMeaning)) {
// check if this symbol is already used
const symbolId = getUniqueSymbolId(localSymbol);
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, /*isDefault*/ true));
return symbolIdActionMap.getAllActions();
function getImportDeclarations(moduleSymbol: Symbol) {
const moduleSymbolId = getUniqueSymbolId(moduleSymbol);
const cached = cachedImportDeclarations[moduleSymbolId];
if (cached) {
return cached;
}
const existingDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[] = [];
for (const importModuleSpecifier of sourceFile.imports) {
const importSymbol = checker.getSymbolAtLocation(importModuleSpecifier);
if (importSymbol === moduleSymbol) {
existingDeclarations.push(getImportDeclaration(importModuleSpecifier));
}
}
cachedImportDeclarations[moduleSymbolId] = existingDeclarations;
return existingDeclarations;
function getImportDeclaration(moduleSpecifier: LiteralExpression) {
let node: Node = moduleSpecifier;
while (node) {
if (node.kind === SyntaxKind.ImportDeclaration) {
return <ImportDeclaration>node;
}
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
return <ImportEqualsDeclaration>node;
}
node = node.parent;
}
return undefined;
}
}
// check exports with the same name
const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExports(name, moduleSymbol);
if (exportSymbolWithIdenticalName && checkSymbolHasMeaning(exportSymbolWithIdenticalName, currentTokenMeaning)) {
const symbolId = getUniqueSymbolId(exportSymbolWithIdenticalName);
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol));
}
function getUniqueSymbolId(symbol: Symbol) {
if (symbol.flags & SymbolFlags.Alias) {
return getSymbolId(checker.getAliasedSymbol(symbol));
}
return getSymbolId(symbol);
}
function checkSymbolHasMeaning(symbol: Symbol, meaning: SemanticMeaning) {
const declarations = symbol.getDeclarations();
return declarations ? some(symbol.declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning)) : false;
}
function getCodeActionForImport(moduleSymbol: Symbol, isDefault?: boolean, isNamespaceImport?: boolean): ImportCodeAction[] {
const existingDeclarations = getImportDeclarations(moduleSymbol);
if (existingDeclarations.length > 0) {
// With an existing import statement, there are more than one actions the user can do.
return getCodeActionsForExistingImport(existingDeclarations);
}
else {
return [getCodeActionForNewImport()];
}
return symbolIdActionMap.getAllActions();
function getCodeActionsForExistingImport(declarations: (ImportDeclaration | ImportEqualsDeclaration)[]): ImportCodeAction[] {
const actions: ImportCodeAction[] = [];
function getImportDeclarations(moduleSymbol: Symbol) {
const moduleSymbolId = getUniqueSymbolId(moduleSymbol);
const cached = cachedImportDeclarations[moduleSymbolId];
if (cached) {
return cached;
}
const existingDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[] = [];
for (const importModuleSpecifier of sourceFile.imports) {
const importSymbol = checker.getSymbolAtLocation(importModuleSpecifier);
if (importSymbol === moduleSymbol) {
existingDeclarations.push(getImportDeclaration(importModuleSpecifier));
}
}
cachedImportDeclarations[moduleSymbolId] = existingDeclarations;
return existingDeclarations;
function getImportDeclaration(moduleSpecifier: LiteralExpression) {
let node: Node = moduleSpecifier;
while (node) {
if (node.kind === SyntaxKind.ImportDeclaration) {
return <ImportDeclaration>node;
// It is possible that multiple import statements with the same specifier exist in the file.
// e.g.
//
// import * as ns from "foo";
// import { member1, member2 } from "foo";
//
// member3/**/ <-- cusor here
//
// in this case we should provie 2 actions:
// 1. change "member3" to "ns.member3"
// 2. add "member3" to the second import statement's import list
// and it is up to the user to decide which one fits best.
let namespaceImportDeclaration: ImportDeclaration | ImportEqualsDeclaration;
let namedImportDeclaration: ImportDeclaration;
let existingModuleSpecifier: string;
for (const declaration of declarations) {
if (declaration.kind === SyntaxKind.ImportDeclaration) {
const namedBindings = declaration.importClause && declaration.importClause.namedBindings;
if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) {
// case:
// import * as ns from "foo"
namespaceImportDeclaration = declaration;
}
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
return <ImportEqualsDeclaration>node;
else {
// cases:
// import default from "foo"
// import { bar } from "foo" or combination with the first one
// import "foo"
namedImportDeclaration = declaration;
}
node = node.parent;
existingModuleSpecifier = declaration.moduleSpecifier.getText();
}
else {
// case:
// import foo = require("foo")
namespaceImportDeclaration = declaration;
existingModuleSpecifier = getModuleSpecifierFromImportEqualsDeclaration(declaration);
}
return undefined;
}
}
function getUniqueSymbolId(symbol: Symbol) {
if (symbol.flags & SymbolFlags.Alias) {
return getSymbolId(checker.getAliasedSymbol(symbol));
if (namespaceImportDeclaration) {
actions.push(getCodeActionForNamespaceImport(namespaceImportDeclaration));
}
return getSymbolId(symbol);
}
function checkSymbolHasMeaning(symbol: Symbol, meaning: SemanticMeaning) {
const declarations = symbol.getDeclarations();
return declarations ? some(symbol.declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning)) : false;
}
function getCodeActionForImport(moduleSymbol: Symbol, isDefault?: boolean, isNamespaceImport?: boolean): ImportCodeAction[] {
const existingDeclarations = getImportDeclarations(moduleSymbol);
if (existingDeclarations.length > 0) {
// With an existing import statement, there are more than one actions the user can do.
return getCodeActionsForExistingImport(existingDeclarations);
if (!isNamespaceImport && namedImportDeclaration && namedImportDeclaration.importClause &&
(namedImportDeclaration.importClause.name || namedImportDeclaration.importClause.namedBindings)) {
/**
* If the existing import declaration already has a named import list, just
* insert the identifier into that list.
*/
const fileTextChanges = getTextChangeForImportClause(namedImportDeclaration.importClause);
const moduleSpecifierWithoutQuotes = stripQuotes(namedImportDeclaration.moduleSpecifier.getText());
actions.push(createCodeAction(
Diagnostics.Add_0_to_existing_import_declaration_from_1,
[name, moduleSpecifierWithoutQuotes],
fileTextChanges,
"InsertingIntoExistingImport",
moduleSpecifierWithoutQuotes
));
}
else {
return [getCodeActionForNewImport()];
// we need to create a new import statement, but the existing module specifier can be reused.
actions.push(getCodeActionForNewImport(existingModuleSpecifier));
}
return actions;
function getModuleSpecifierFromImportEqualsDeclaration(declaration: ImportEqualsDeclaration) {
if (declaration.moduleReference && declaration.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
return declaration.moduleReference.expression.getText();
}
return declaration.moduleReference.getText();
}
function getCodeActionsForExistingImport(declarations: (ImportDeclaration | ImportEqualsDeclaration)[]): ImportCodeAction[] {
const actions: ImportCodeAction[] = [];
// It is possible that multiple import statements with the same specifier exist in the file.
// e.g.
//
// import * as ns from "foo";
// import { member1, member2 } from "foo";
//
// member3/**/ <-- cusor here
//
// in this case we should provie 2 actions:
// 1. change "member3" to "ns.member3"
// 2. add "member3" to the second import statement's import list
// and it is up to the user to decide which one fits best.
let namespaceImportDeclaration: ImportDeclaration | ImportEqualsDeclaration;
let namedImportDeclaration: ImportDeclaration;
let existingModuleSpecifier: string;
for (const declaration of declarations) {
if (declaration.kind === SyntaxKind.ImportDeclaration) {
const namedBindings = declaration.importClause && declaration.importClause.namedBindings;
if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) {
// case:
// import * as ns from "foo"
namespaceImportDeclaration = declaration;
}
else {
// cases:
// import default from "foo"
// import { bar } from "foo" or combination with the first one
// import "foo"
namedImportDeclaration = declaration;
}
existingModuleSpecifier = declaration.moduleSpecifier.getText();
}
else {
// case:
// import foo = require("foo")
namespaceImportDeclaration = declaration;
existingModuleSpecifier = getModuleSpecifierFromImportEqualsDeclaration(declaration);
}
function getTextChangeForImportClause(importClause: ImportClause): FileTextChanges[] {
const importList = <NamedImports>importClause.namedBindings;
const newImportSpecifier = createImportSpecifier(/*propertyName*/ undefined, createIdentifier(name));
// case 1:
// original text: import default from "module"
// change to: import default, { name } from "module"
// case 2:
// original text: import {} from "module"
// change to: import { name } from "module"
if (!importList || importList.elements.length === 0) {
const newImportClause = createImportClause(importClause.name, createNamedImports([newImportSpecifier]));
return createChangeTracker().replaceNode(sourceFile, importClause, newImportClause).getChanges();
}
if (namespaceImportDeclaration) {
actions.push(getCodeActionForNamespaceImport(namespaceImportDeclaration));
}
if (!isNamespaceImport && namedImportDeclaration && namedImportDeclaration.importClause &&
(namedImportDeclaration.importClause.name || namedImportDeclaration.importClause.namedBindings)) {
/**
* If the existing import declaration already has a named import list, just
* insert the identifier into that list.
*/
const fileTextChanges = getTextChangeForImportClause(namedImportDeclaration.importClause);
const moduleSpecifierWithoutQuotes = stripQuotes(namedImportDeclaration.moduleSpecifier.getText());
actions.push(createCodeAction(
Diagnostics.Add_0_to_existing_import_declaration_from_1,
[name, moduleSpecifierWithoutQuotes],
fileTextChanges,
"InsertingIntoExistingImport",
moduleSpecifierWithoutQuotes
));
}
else {
// we need to create a new import statement, but the existing module specifier can be reused.
actions.push(getCodeActionForNewImport(existingModuleSpecifier));
}
return actions;
function getModuleSpecifierFromImportEqualsDeclaration(declaration: ImportEqualsDeclaration) {
if (declaration.moduleReference && declaration.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
return declaration.moduleReference.expression.getText();
}
return declaration.moduleReference.getText();
}
function getTextChangeForImportClause(importClause: ImportClause): FileTextChanges[] {
const importList = <NamedImports>importClause.namedBindings;
const newImportSpecifier = createImportSpecifier(/*propertyName*/ undefined, createIdentifier(name));
// case 1:
// original text: import default from "module"
// change to: import default, { name } from "module"
// case 2:
// original text: import {} from "module"
// change to: import { name } from "module"
if (!importList || importList.elements.length === 0) {
const newImportClause = createImportClause(importClause.name, createNamedImports([newImportSpecifier]));
return createChangeTracker().replaceNode(sourceFile, importClause, newImportClause).getChanges();
}
/**
* If the import list has one import per line, preserve that. Otherwise, insert on same line as last element
* import {
* foo
* } from "./module";
*/
return createChangeTracker().insertNodeInListAfter(
sourceFile,
importList.elements[importList.elements.length - 1],
newImportSpecifier).getChanges();
}
function getCodeActionForNamespaceImport(declaration: ImportDeclaration | ImportEqualsDeclaration): ImportCodeAction {
let namespacePrefix: string;
if (declaration.kind === SyntaxKind.ImportDeclaration) {
namespacePrefix = (<NamespaceImport>declaration.importClause.namedBindings).name.getText();
}
else {
namespacePrefix = declaration.name.getText();
}
namespacePrefix = stripQuotes(namespacePrefix);
/**
* Cases:
* import * as ns from "mod"
* import default, * as ns from "mod"
* import ns = require("mod")
*
* Because there is no import list, we alter the reference to include the
* namespace instead of altering the import declaration. For example, "foo" would
* become "ns.foo"
*/
return createCodeAction(
Diagnostics.Change_0_to_1,
[name, `${namespacePrefix}.${name}`],
createChangeTracker().replaceNode(sourceFile, token, createPropertyAccess(createIdentifier(namespacePrefix), name)).getChanges(),
"CodeChange"
);
}
/**
* If the import list has one import per line, preserve that. Otherwise, insert on same line as last element
* import {
* foo
* } from "./module";
*/
return createChangeTracker().insertNodeInListAfter(
sourceFile,
importList.elements[importList.elements.length - 1],
newImportSpecifier).getChanges();
}
function getCodeActionForNewImport(moduleSpecifier?: string): ImportCodeAction {
if (!lastImportDeclaration) {
// insert after any existing imports
for (let i = sourceFile.statements.length - 1; i >= 0; i--) {
const statement = sourceFile.statements[i];
if (statement.kind === SyntaxKind.ImportEqualsDeclaration || statement.kind === SyntaxKind.ImportDeclaration) {
lastImportDeclaration = statement;
break;
}
}
}
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier || getModuleSpecifierForNewImport());
const changeTracker = createChangeTracker();
const importClause = isDefault
? createImportClause(createIdentifier(name), /*namedBindings*/ undefined)
: isNamespaceImport
? createImportClause(/*name*/ undefined, createNamespaceImport(createIdentifier(name)))
: createImportClause(/*name*/ undefined, createNamedImports([createImportSpecifier(/*propertyName*/ undefined, createIdentifier(name))]));
const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, importClause, createLiteral(moduleSpecifierWithoutQuotes));
if (!lastImportDeclaration) {
changeTracker.insertNodeAt(sourceFile, sourceFile.getStart(), importDecl, { suffix: `${context.newLineCharacter}${context.newLineCharacter}` });
function getCodeActionForNamespaceImport(declaration: ImportDeclaration | ImportEqualsDeclaration): ImportCodeAction {
let namespacePrefix: string;
if (declaration.kind === SyntaxKind.ImportDeclaration) {
namespacePrefix = (<NamespaceImport>declaration.importClause.namedBindings).name.getText();
}
else {
changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl, { suffix: context.newLineCharacter });
namespacePrefix = declaration.name.getText();
}
namespacePrefix = stripQuotes(namespacePrefix);
// if this file doesn't have any import statements, insert an import statement and then insert a new line
// between the only import statement and user code. Otherwise just insert the statement because chances
// are there are already a new line seperating code and import statements.
/**
* Cases:
* import * as ns from "mod"
* import default, * as ns from "mod"
* import ns = require("mod")
*
* Because there is no import list, we alter the reference to include the
* namespace instead of altering the import declaration. For example, "foo" would
* become "ns.foo"
*/
return createCodeAction(
Diagnostics.Import_0_from_1,
[name, `"${moduleSpecifierWithoutQuotes}"`],
changeTracker.getChanges(),
"NewImport",
moduleSpecifierWithoutQuotes
Diagnostics.Change_0_to_1,
[name, `${namespacePrefix}.${name}`],
createChangeTracker().replaceNode(sourceFile, token, createPropertyAccess(createIdentifier(namespacePrefix), name)).getChanges(),
"CodeChange"
);
}
}
function getModuleSpecifierForNewImport() {
const fileName = sourceFile.fileName;
const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().fileName;
const sourceDirectory = getDirectoryPath(fileName);
const options = context.program.getCompilerOptions();
return tryGetModuleNameFromAmbientModule() ||
tryGetModuleNameFromTypeRoots() ||
tryGetModuleNameAsNodeModule() ||
tryGetModuleNameFromBaseUrl() ||
tryGetModuleNameFromRootDirs() ||
removeFileExtension(getRelativePath(moduleFileName, sourceDirectory));
function tryGetModuleNameFromAmbientModule(): string {
if (moduleSymbol.valueDeclaration.kind !== SyntaxKind.SourceFile) {
return moduleSymbol.name;
}
function getCodeActionForNewImport(moduleSpecifier?: string): ImportCodeAction {
if (!lastImportDeclaration) {
// insert after any existing imports
for (let i = sourceFile.statements.length - 1; i >= 0; i--) {
const statement = sourceFile.statements[i];
if (statement.kind === SyntaxKind.ImportEqualsDeclaration || statement.kind === SyntaxKind.ImportDeclaration) {
lastImportDeclaration = statement;
break;
}
}
}
function tryGetModuleNameFromBaseUrl() {
if (!options.baseUrl) {
return undefined;
}
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier || getModuleSpecifierForNewImport());
const changeTracker = createChangeTracker();
const importClause = isDefault
? createImportClause(createIdentifier(name), /*namedBindings*/ undefined)
: isNamespaceImport
? createImportClause(/*name*/ undefined, createNamespaceImport(createIdentifier(name)))
: createImportClause(/*name*/ undefined, createNamedImports([createImportSpecifier(/*propertyName*/ undefined, createIdentifier(name))]));
const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, importClause, createLiteral(moduleSpecifierWithoutQuotes));
if (!lastImportDeclaration) {
changeTracker.insertNodeAt(sourceFile, sourceFile.getStart(), importDecl, { suffix: `${context.newLineCharacter}${context.newLineCharacter}` });
}
else {
changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl, { suffix: context.newLineCharacter });
}
let relativeName = getRelativePathIfInDirectory(moduleFileName, options.baseUrl);
if (!relativeName) {
return undefined;
}
// if this file doesn't have any import statements, insert an import statement and then insert a new line
// between the only import statement and user code. Otherwise just insert the statement because chances
// are there are already a new line seperating code and import statements.
return createCodeAction(
Diagnostics.Import_0_from_1,
[name, `"${moduleSpecifierWithoutQuotes}"`],
changeTracker.getChanges(),
"NewImport",
moduleSpecifierWithoutQuotes
);
const relativeNameWithIndex = removeFileExtension(relativeName);
relativeName = removeExtensionAndIndexPostFix(relativeName);
function getModuleSpecifierForNewImport() {
const fileName = sourceFile.fileName;
const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().fileName;
const sourceDirectory = getDirectoryPath(fileName);
const options = context.program.getCompilerOptions();
if (options.paths) {
for (const key in options.paths) {
for (const pattern of options.paths[key]) {
const indexOfStar = pattern.indexOf("*");
if (indexOfStar === 0 && pattern.length === 1) {
continue;
}
else if (indexOfStar !== -1) {
const prefix = pattern.substr(0, indexOfStar);
const suffix = pattern.substr(indexOfStar + 1);
if (relativeName.length >= prefix.length + suffix.length &&
startsWith(relativeName, prefix) &&
endsWith(relativeName, suffix)) {
const matchedStar = relativeName.substr(prefix.length, relativeName.length - suffix.length);
return key.replace("\*", matchedStar);
}
}
else if (pattern === relativeName || pattern === relativeNameWithIndex) {
return key;
}
}
}
}
return tryGetModuleNameFromAmbientModule() ||
tryGetModuleNameFromTypeRoots() ||
tryGetModuleNameAsNodeModule() ||
tryGetModuleNameFromBaseUrl() ||
tryGetModuleNameFromRootDirs() ||
removeFileExtension(getRelativePath(moduleFileName, sourceDirectory));
return relativeName;
function tryGetModuleNameFromAmbientModule(): string {
if (moduleSymbol.valueDeclaration.kind !== SyntaxKind.SourceFile) {
return moduleSymbol.name;
}
}
function tryGetModuleNameFromRootDirs() {
if (options.rootDirs) {
const normalizedTargetPath = getPathRelativeToRootDirs(moduleFileName, options.rootDirs);
const normalizedSourcePath = getPathRelativeToRootDirs(sourceDirectory, options.rootDirs);
if (normalizedTargetPath !== undefined) {
const relativePath = normalizedSourcePath !== undefined ? getRelativePath(normalizedTargetPath, normalizedSourcePath) : normalizedTargetPath;
return removeFileExtension(relativePath);
}
}
function tryGetModuleNameFromBaseUrl() {
if (!options.baseUrl) {
return undefined;
}
function tryGetModuleNameFromTypeRoots() {
const typeRoots = getEffectiveTypeRoots(options, context.host);
if (typeRoots) {
const normalizedTypeRoots = map(typeRoots, typeRoot => toPath(typeRoot, /*basePath*/ undefined, getCanonicalFileName));
for (const typeRoot of normalizedTypeRoots) {
if (startsWith(moduleFileName, typeRoot)) {
const relativeFileName = moduleFileName.substring(typeRoot.length + 1);
return removeExtensionAndIndexPostFix(relativeFileName);
}
}
}
let relativeName = getRelativePathIfInDirectory(moduleFileName, options.baseUrl);
if (!relativeName) {
return undefined;
}
function tryGetModuleNameAsNodeModule() {
if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
// nothing to do here
return undefined;
}
const relativeNameWithIndex = removeFileExtension(relativeName);
relativeName = removeExtensionAndIndexPostFix(relativeName);
const indexOfNodeModules = moduleFileName.indexOf("node_modules");
if (indexOfNodeModules < 0) {
return undefined;
}
let relativeFileName: string;
if (sourceDirectory.indexOf(moduleFileName.substring(0, indexOfNodeModules - 1)) === 0) {
// if node_modules folder is in this folder or any of its parent folder, no need to keep it.
relativeFileName = moduleFileName.substring(indexOfNodeModules + 13 /* "node_modules\".length */);
}
else {
relativeFileName = getRelativePath(moduleFileName, sourceDirectory);
}
relativeFileName = removeFileExtension(relativeFileName);
if (endsWith(relativeFileName, "/index")) {
relativeFileName = getDirectoryPath(relativeFileName);
}
else {
try {
const moduleDirectory = getDirectoryPath(moduleFileName);
const packageJsonContent = JSON.parse(context.host.readFile(combinePaths(moduleDirectory, "package.json")));
if (packageJsonContent) {
const mainFile = packageJsonContent.main || packageJsonContent.typings;
if (mainFile) {
const mainExportFile = toPath(mainFile, moduleDirectory, getCanonicalFileName);
if (removeFileExtension(mainExportFile) === removeFileExtension(moduleFileName)) {
relativeFileName = getDirectoryPath(relativeFileName);
}
if (options.paths) {
for (const key in options.paths) {
for (const pattern of options.paths[key]) {
const indexOfStar = pattern.indexOf("*");
if (indexOfStar === 0 && pattern.length === 1) {
continue;
}
else if (indexOfStar !== -1) {
const prefix = pattern.substr(0, indexOfStar);
const suffix = pattern.substr(indexOfStar + 1);
if (relativeName.length >= prefix.length + suffix.length &&
startsWith(relativeName, prefix) &&
endsWith(relativeName, suffix)) {
const matchedStar = relativeName.substr(prefix.length, relativeName.length - suffix.length);
return key.replace("\*", matchedStar);
}
}
else if (pattern === relativeName || pattern === relativeNameWithIndex) {
return key;
}
}
catch (e) { }
}
return relativeFileName;
}
return relativeName;
}
function getPathRelativeToRootDirs(path: string, rootDirs: string[]) {
for (const rootDir of rootDirs) {
const relativeName = getRelativePathIfInDirectory(path, rootDir);
if (relativeName !== undefined) {
return relativeName;
function tryGetModuleNameFromRootDirs() {
if (options.rootDirs) {
const normalizedTargetPath = getPathRelativeToRootDirs(moduleFileName, options.rootDirs);
const normalizedSourcePath = getPathRelativeToRootDirs(sourceDirectory, options.rootDirs);
if (normalizedTargetPath !== undefined) {
const relativePath = normalizedSourcePath !== undefined ? getRelativePath(normalizedTargetPath, normalizedSourcePath) : normalizedTargetPath;
return removeFileExtension(relativePath);
}
}
return undefined;
}
function removeExtensionAndIndexPostFix(fileName: string) {
fileName = removeFileExtension(fileName);
if (endsWith(fileName, "/index")) {
fileName = fileName.substr(0, fileName.length - 6/* "/index".length */);
function tryGetModuleNameFromTypeRoots() {
const typeRoots = getEffectiveTypeRoots(options, context.host);
if (typeRoots) {
const normalizedTypeRoots = map(typeRoots, typeRoot => toPath(typeRoot, /*basePath*/ undefined, getCanonicalFileName));
for (const typeRoot of normalizedTypeRoots) {
if (startsWith(moduleFileName, typeRoot)) {
const relativeFileName = moduleFileName.substring(typeRoot.length + 1);
return removeExtensionAndIndexPostFix(relativeFileName);
}
}
}
return fileName;
}
function getRelativePathIfInDirectory(path: string, directoryPath: string) {
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
return isRootedDiskPath(relativePath) || startsWith(relativePath, "..") ? undefined : relativePath;
}
function tryGetModuleNameAsNodeModule() {
if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
// nothing to do here
return undefined;
}
function getRelativePath(path: string, directoryPath: string) {
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
return moduleHasNonRelativeName(relativePath) ? "./" + relativePath : relativePath;
const indexOfNodeModules = moduleFileName.indexOf("node_modules");
if (indexOfNodeModules < 0) {
return undefined;
}
let relativeFileName: string;
if (sourceDirectory.indexOf(moduleFileName.substring(0, indexOfNodeModules - 1)) === 0) {
// if node_modules folder is in this folder or any of its parent folder, no need to keep it.
relativeFileName = moduleFileName.substring(indexOfNodeModules + 13 /* "node_modules\".length */);
}
else {
relativeFileName = getRelativePath(moduleFileName, sourceDirectory);
}
relativeFileName = removeFileExtension(relativeFileName);
if (endsWith(relativeFileName, "/index")) {
relativeFileName = getDirectoryPath(relativeFileName);
}
else {
try {
const moduleDirectory = getDirectoryPath(moduleFileName);
const packageJsonContent = JSON.parse(context.host.readFile(combinePaths(moduleDirectory, "package.json")));
if (packageJsonContent) {
const mainFile = packageJsonContent.main || packageJsonContent.typings;
if (mainFile) {
const mainExportFile = toPath(mainFile, moduleDirectory, getCanonicalFileName);
if (removeFileExtension(mainExportFile) === removeFileExtension(moduleFileName)) {
relativeFileName = getDirectoryPath(relativeFileName);
}
}
}
}
catch (e) { }
}
return relativeFileName;
}
}
function getPathRelativeToRootDirs(path: string, rootDirs: string[]) {
for (const rootDir of rootDirs) {
const relativeName = getRelativePathIfInDirectory(path, rootDir);
if (relativeName !== undefined) {
return relativeName;
}
}
return undefined;
}
function removeExtensionAndIndexPostFix(fileName: string) {
fileName = removeFileExtension(fileName);
if (endsWith(fileName, "/index")) {
fileName = fileName.substr(0, fileName.length - 6/* "/index".length */);
}
return fileName;
}
function getRelativePathIfInDirectory(path: string, directoryPath: string) {
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
return isRootedDiskPath(relativePath) || startsWith(relativePath, "..") ? undefined : relativePath;
}
function getRelativePath(path: string, directoryPath: string) {
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
return moduleHasNonRelativeName(relativePath) ? "./" + relativePath : relativePath;
}
}
function createChangeTracker() {
return textChanges.ChangeTracker.fromCodeFixContext(context);
}
function createCodeAction(
description: DiagnosticMessage,
diagnosticArgs: string[],
changes: FileTextChanges[],
kind: ImportCodeActionKind,
moduleSpecifier?: string): ImportCodeAction {
return {
description: formatMessage.apply(undefined, [undefined, description].concat(<any[]>diagnosticArgs)),
changes,
kind,
moduleSpecifier
};
}
}
});
function createChangeTracker() {
return textChanges.ChangeTracker.fromCodeFixContext(context);
}
function createCodeAction(
description: DiagnosticMessage,
diagnosticArgs: string[],
changes: FileTextChanges[],
kind: ImportCodeActionKind,
moduleSpecifier?: string): ImportCodeAction {
return {
description: formatMessage.apply(undefined, [undefined, description].concat(<any[]>diagnosticArgs)),
changes,
kind,
moduleSpecifier
};
}
}
}
+137 -129
View File
@@ -18,142 +18,150 @@ namespace ts.codefix {
switch (token.kind) {
case ts.SyntaxKind.Identifier:
switch (token.parent.kind) {
case ts.SyntaxKind.VariableDeclaration:
switch (token.parent.parent.parent.kind) {
case SyntaxKind.ForStatement:
const forStatement = <ForStatement>token.parent.parent.parent;
const forInitializer = <VariableDeclarationList>forStatement.initializer;
if (forInitializer.declarations.length === 1) {
return deleteNode(forInitializer);
}
else {
return deleteNodeInList(token.parent);
}
case SyntaxKind.ForOfStatement:
const forOfStatement = <ForOfStatement>token.parent.parent.parent;
if (forOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList) {
const forOfInitializer = <VariableDeclarationList>forOfStatement.initializer;
return replaceNode(forOfInitializer.declarations[0], createObjectLiteral());
}
break;
case SyntaxKind.ForInStatement:
// There is no valid fix in the case of:
// for .. in
return undefined;
case SyntaxKind.CatchClause:
const catchClause = <CatchClause>token.parent.parent;
const parameter = catchClause.variableDeclaration.getChildren()[0];
return deleteNode(parameter);
default:
const variableStatement = <VariableStatement>token.parent.parent.parent;
if (variableStatement.declarationList.declarations.length === 1) {
return deleteNode(variableStatement);
}
else {
return deleteNodeInList(token.parent);
}
}
// TODO: #14885
// falls through
case SyntaxKind.TypeParameter:
const typeParameters = (<DeclarationWithTypeParameters>token.parent.parent).typeParameters;
if (typeParameters.length === 1) {
const previousToken = getTokenAtPosition(sourceFile, typeParameters.pos - 1);
if (!previousToken || previousToken.kind !== SyntaxKind.LessThanToken) {
return deleteRange(typeParameters);
}
const nextToken = getTokenAtPosition(sourceFile, typeParameters.end);
if (!nextToken || nextToken.kind !== SyntaxKind.GreaterThanToken) {
return deleteRange(typeParameters);
}
return deleteNodeRange(previousToken, nextToken);
}
else {
return deleteNodeInList(token.parent);
}
case ts.SyntaxKind.Parameter:
const functionDeclaration = <FunctionDeclaration>token.parent.parent;
if (functionDeclaration.parameters.length === 1) {
return deleteNode(token.parent);
}
else {
return deleteNodeInList(token.parent);
}
// handle case where 'import a = A;'
case SyntaxKind.ImportEqualsDeclaration:
const importEquals = getAncestor(token, SyntaxKind.ImportEqualsDeclaration);
return deleteNode(importEquals);
case SyntaxKind.ImportSpecifier:
const namedImports = <NamedImports>token.parent.parent;
if (namedImports.elements.length === 1) {
// Only 1 import and it is unused. So the entire declaration should be removed.
const importSpec = getAncestor(token, SyntaxKind.ImportDeclaration);
return deleteNode(importSpec);
}
else {
// delete import specifier
return deleteNodeInList(token.parent);
}
// handle case where "import d, * as ns from './file'"
// or "'import {a, b as ns} from './file'"
case SyntaxKind.ImportClause: // this covers both 'import |d|' and 'import |d,| *'
const importClause = <ImportClause>token.parent;
if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'|
const importDecl = getAncestor(importClause, SyntaxKind.ImportDeclaration);
return deleteNode(importDecl);
}
else {
// import |d,| * as ns from './file'
const start = importClause.name.getStart(sourceFile);
const nextToken = getTokenAtPosition(sourceFile, importClause.name.end);
if (nextToken && nextToken.kind === SyntaxKind.CommaToken) {
// shift first non-whitespace position after comma to the start position of the node
return deleteRange({ pos: start, end: skipTrivia(sourceFile.text, nextToken.end, /*stopAfterLineBreaks*/ false, /*stopAtComments*/ true) });
}
else {
return deleteNode(importClause.name);
}
}
case SyntaxKind.NamespaceImport:
const namespaceImport = <NamespaceImport>token.parent;
if (namespaceImport.name === token && !(<ImportClause>namespaceImport.parent).name) {
const importDecl = getAncestor(namespaceImport, SyntaxKind.ImportDeclaration);
return deleteNode(importDecl);
}
else {
const previousToken = getTokenAtPosition(sourceFile, namespaceImport.pos - 1);
if (previousToken && previousToken.kind === SyntaxKind.CommaToken) {
const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart);
return deleteRange({ pos: startPosition, end: namespaceImport.end });
}
return deleteRange(namespaceImport);
}
}
break;
return deleteIdentifier();
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.NamespaceImport:
return deleteNode(token.parent);
default:
return deleteDefault();
}
if (isDeclarationName(token)) {
return deleteNode(token.parent);
function deleteDefault() {
if (isDeclarationName(token)) {
return deleteNode(token.parent);
}
else if (isLiteralComputedPropertyDeclarationName(token)) {
return deleteNode(token.parent.parent);
}
else {
return undefined;
}
}
else if (isLiteralComputedPropertyDeclarationName(token)) {
return deleteNode(token.parent.parent);
function deleteIdentifier(): CodeAction[] | undefined {
switch (token.parent.kind) {
case ts.SyntaxKind.VariableDeclaration:
return deleteVariableDeclaration(<ts.VariableDeclaration>token.parent);
case SyntaxKind.TypeParameter:
const typeParameters = (<DeclarationWithTypeParameters>token.parent.parent).typeParameters;
if (typeParameters.length === 1) {
const previousToken = getTokenAtPosition(sourceFile, typeParameters.pos - 1);
if (!previousToken || previousToken.kind !== SyntaxKind.LessThanToken) {
return deleteRange(typeParameters);
}
const nextToken = getTokenAtPosition(sourceFile, typeParameters.end);
if (!nextToken || nextToken.kind !== SyntaxKind.GreaterThanToken) {
return deleteRange(typeParameters);
}
return deleteNodeRange(previousToken, nextToken);
}
else {
return deleteNodeInList(token.parent);
}
case ts.SyntaxKind.Parameter:
const functionDeclaration = <FunctionDeclaration>token.parent.parent;
if (functionDeclaration.parameters.length === 1) {
return deleteNode(token.parent);
}
else {
return deleteNodeInList(token.parent);
}
// handle case where 'import a = A;'
case SyntaxKind.ImportEqualsDeclaration:
const importEquals = getAncestor(token, SyntaxKind.ImportEqualsDeclaration);
return deleteNode(importEquals);
case SyntaxKind.ImportSpecifier:
const namedImports = <NamedImports>token.parent.parent;
if (namedImports.elements.length === 1) {
// Only 1 import and it is unused. So the entire declaration should be removed.
const importSpec = getAncestor(token, SyntaxKind.ImportDeclaration);
return deleteNode(importSpec);
}
else {
// delete import specifier
return deleteNodeInList(token.parent);
}
// handle case where "import d, * as ns from './file'"
// or "'import {a, b as ns} from './file'"
case SyntaxKind.ImportClause: // this covers both 'import |d|' and 'import |d,| *'
const importClause = <ImportClause>token.parent;
if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'|
const importDecl = getAncestor(importClause, SyntaxKind.ImportDeclaration);
return deleteNode(importDecl);
}
else {
// import |d,| * as ns from './file'
const start = importClause.name.getStart(sourceFile);
const nextToken = getTokenAtPosition(sourceFile, importClause.name.end);
if (nextToken && nextToken.kind === SyntaxKind.CommaToken) {
// shift first non-whitespace position after comma to the start position of the node
return deleteRange({ pos: start, end: skipTrivia(sourceFile.text, nextToken.end, /*stopAfterLineBreaks*/ false, /*stopAtComments*/ true) });
}
else {
return deleteNode(importClause.name);
}
}
case SyntaxKind.NamespaceImport:
const namespaceImport = <NamespaceImport>token.parent;
if (namespaceImport.name === token && !(<ImportClause>namespaceImport.parent).name) {
const importDecl = getAncestor(namespaceImport, SyntaxKind.ImportDeclaration);
return deleteNode(importDecl);
}
else {
const previousToken = getTokenAtPosition(sourceFile, namespaceImport.pos - 1);
if (previousToken && previousToken.kind === SyntaxKind.CommaToken) {
const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart);
return deleteRange({ pos: startPosition, end: namespaceImport.end });
}
return deleteRange(namespaceImport);
}
default:
return deleteDefault();
}
}
else {
return undefined;
// token.parent is a variableDeclaration
function deleteVariableDeclaration(varDecl: ts.VariableDeclaration): CodeAction[] | undefined {
switch (varDecl.parent.parent.kind) {
case SyntaxKind.ForStatement:
const forStatement = <ForStatement>varDecl.parent.parent;
const forInitializer = <VariableDeclarationList>forStatement.initializer;
if (forInitializer.declarations.length === 1) {
return deleteNode(forInitializer);
}
else {
return deleteNodeInList(varDecl);
}
case SyntaxKind.ForOfStatement:
const forOfStatement = <ForOfStatement>varDecl.parent.parent;
Debug.assert(forOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList);
const forOfInitializer = <VariableDeclarationList>forOfStatement.initializer;
return replaceNode(forOfInitializer.declarations[0], createObjectLiteral());
case SyntaxKind.ForInStatement:
// There is no valid fix in the case of:
// for .. in
return undefined;
default:
const variableStatement = <VariableStatement>varDecl.parent.parent;
if (variableStatement.declarationList.declarations.length === 1) {
return deleteNode(variableStatement);
}
else {
return deleteNodeInList(varDecl);
}
}
}
function deleteNode(n: Node) {
+236 -36
View File
@@ -18,7 +18,7 @@ namespace ts.Completions {
return undefined;
}
const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, requestJsDocTagName, requestJsDocTag } = completionData;
const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords } = completionData;
if (requestJsDocTagName) {
// If the current position is a jsDoc tag name, only tag names should be provided for completion
@@ -52,7 +52,7 @@ namespace ts.Completions {
sortText: "0",
});
}
else {
else if (!hasFilteredClassMemberKeywords) {
return undefined;
}
}
@@ -60,8 +60,11 @@ namespace ts.Completions {
getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log);
}
if (hasFilteredClassMemberKeywords) {
addRange(entries, classMemberKeywordCompletions);
}
// Add keywords if this is not a member completion list
if (!isMemberCompletion && !requestJsDocTag && !requestJsDocTagName) {
else if (!isMemberCompletion && !requestJsDocTag && !requestJsDocTagName) {
addRange(entries, keywordCompletions);
}
@@ -221,11 +224,12 @@ namespace ts.Completions {
function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo, typeChecker: TypeChecker): CompletionInfo | undefined {
const candidates: Signature[] = [];
const entries: CompletionEntry[] = [];
const uniques = createMap<true>();
typeChecker.getResolvedSignature(argumentInfo.invocation, candidates);
for (const candidate of candidates) {
addStringLiteralCompletionsFromType(typeChecker.getParameterType(candidate, argumentInfo.argumentIndex), entries, typeChecker);
addStringLiteralCompletionsFromType(typeChecker.getParameterType(candidate, argumentInfo.argumentIndex), entries, typeChecker, uniques);
}
if (entries.length) {
@@ -258,25 +262,29 @@ namespace ts.Completions {
return undefined;
}
function addStringLiteralCompletionsFromType(type: Type, result: Push<CompletionEntry>, typeChecker: TypeChecker): void {
function addStringLiteralCompletionsFromType(type: Type, result: Push<CompletionEntry>, typeChecker: TypeChecker, uniques = createMap<true>()): void {
if (type && type.flags & TypeFlags.TypeParameter) {
type = typeChecker.getApparentType(type);
type = typeChecker.getBaseConstraintOfType(type);
}
if (!type) {
return;
}
if (type.flags & TypeFlags.Union) {
for (const t of (<UnionType>type).types) {
addStringLiteralCompletionsFromType(t, result, typeChecker);
addStringLiteralCompletionsFromType(t, result, typeChecker, uniques);
}
}
else if (type.flags & TypeFlags.StringLiteral) {
result.push({
name: (<StringLiteralType>type).value,
kindModifiers: ScriptElementKindModifier.none,
kind: ScriptElementKind.variableElement,
sortText: "0"
});
const name = (<StringLiteralType>type).value;
if (!uniques.has(name)) {
uniques.set(name, true);
result.push({
name,
kindModifiers: ScriptElementKindModifier.none,
kind: ScriptElementKind.variableElement,
sortText: "0"
});
}
}
}
@@ -351,7 +359,7 @@ namespace ts.Completions {
start = timestamp();
// Completion not allowed inside comments, bail out if this is the case
const insideComment = isInsideComment(sourceFile, currentToken, position);
const insideComment = isInComment(sourceFile, position, currentToken);
log("getCompletionData: Is inside comment: " + (timestamp() - start));
if (insideComment) {
@@ -406,7 +414,7 @@ namespace ts.Completions {
}
if (requestJsDocTagName || requestJsDocTag) {
return { symbols: undefined, isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, location: undefined, isRightOfDot: false, requestJsDocTagName, requestJsDocTag };
return { symbols: undefined, isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, location: undefined, isRightOfDot: false, requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords: false };
}
if (!insideJsDocTagExpression) {
@@ -505,6 +513,7 @@ namespace ts.Completions {
let isGlobalCompletion = false;
let isMemberCompletion: boolean;
let isNewIdentifierLocation: boolean;
let hasFilteredClassMemberKeywords = false;
let symbols: Symbol[] = [];
if (isRightOfDot) {
@@ -542,7 +551,7 @@ namespace ts.Completions {
log("getCompletionData: Semantic work: " + (timestamp() - semanticStart));
return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), requestJsDocTagName, requestJsDocTag };
return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords };
function getTypeScriptMemberSymbols(): void {
// Right of dot member completion list
@@ -599,6 +608,7 @@ namespace ts.Completions {
function tryGetGlobalSymbols(): boolean {
let objectLikeContainer: ObjectLiteralExpression | BindingPattern;
let namedImportsOrExports: NamedImportsOrExports;
let classLikeContainer: ClassLikeDeclaration;
let jsxContainer: JsxOpeningLikeElement;
if (objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken)) {
@@ -611,6 +621,12 @@ namespace ts.Completions {
return tryGetImportOrExportClauseCompletionSymbols(namedImportsOrExports);
}
if (classLikeContainer = tryGetClassLikeCompletionContainer(contextToken)) {
// cursor inside class declaration
getGetClassLikeCompletionSymbols(classLikeContainer);
return true;
}
if (jsxContainer = tryGetContainingJsxElement(contextToken)) {
let attrsType: Type;
if ((jsxContainer.kind === SyntaxKind.JsxSelfClosingElement) || (jsxContainer.kind === SyntaxKind.JsxOpeningElement)) {
@@ -813,19 +829,16 @@ namespace ts.Completions {
// We're looking up possible property names from contextual/inferred/declared type.
isMemberCompletion = true;
let typeForObject: Type;
let typeMembers: Symbol[];
let existingMembers: Declaration[];
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
// We are completing on contextual types, but may also include properties
// other than those within the declared type.
isNewIdentifierLocation = true;
// If the object literal is being assigned to something of type 'null | { hello: string }',
// it clearly isn't trying to satisfy the 'null' type. So we grab the non-nullable type if possible.
typeForObject = typeChecker.getContextualType(<ObjectLiteralExpression>objectLikeContainer);
typeForObject = typeForObject && typeForObject.getNonNullableType();
const typeForObject = typeChecker.getContextualType(<ObjectLiteralExpression>objectLikeContainer);
if (!typeForObject) return false;
typeMembers = typeChecker.getAllPossiblePropertiesOfType(typeForObject);
existingMembers = (<ObjectLiteralExpression>objectLikeContainer).properties;
}
else if (objectLikeContainer.kind === SyntaxKind.ObjectBindingPattern) {
@@ -849,7 +862,10 @@ namespace ts.Completions {
}
}
if (canGetType) {
typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
const typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
if (!typeForObject) return false;
// In a binding pattern, get only known properties. Everywhere else we will get all possible properties.
typeMembers = typeChecker.getPropertiesOfType(typeForObject);
existingMembers = (<ObjectBindingPattern>objectLikeContainer).elements;
}
}
@@ -861,11 +877,6 @@ namespace ts.Completions {
Debug.fail("Expected object literal or binding pattern, got " + objectLikeContainer.kind);
}
if (!typeForObject) {
return false;
}
const typeMembers = typeChecker.getPropertiesOfType(typeForObject);
if (typeMembers && typeMembers.length > 0) {
// Add filtered items to the completion list
symbols = filterObjectMembersList(typeMembers, existingMembers);
@@ -913,6 +924,62 @@ namespace ts.Completions {
return true;
}
/**
* Aggregates relevant symbols for completion in class declaration
* Relevant symbols are stored in the captured 'symbols' variable.
*/
function getGetClassLikeCompletionSymbols(classLikeDeclaration: ClassLikeDeclaration) {
// We're looking up possible property names from parent type.
isMemberCompletion = true;
// Declaring new property/method/accessor
isNewIdentifierLocation = true;
// Has keywords for class elements
hasFilteredClassMemberKeywords = true;
const baseTypeNode = getClassExtendsHeritageClauseElement(classLikeDeclaration);
const implementsTypeNodes = getClassImplementsHeritageClauseElements(classLikeDeclaration);
if (baseTypeNode || implementsTypeNodes) {
const classElement = contextToken.parent;
let classElementModifierFlags = isClassElement(classElement) && getModifierFlags(classElement);
// If this is context token is not something we are editing now, consider if this would lead to be modifier
if (contextToken.kind === SyntaxKind.Identifier && !isCurrentlyEditingNode(contextToken)) {
switch (contextToken.getText()) {
case "private":
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Private;
break;
case "static":
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Static;
break;
}
}
// No member list for private methods
if (!(classElementModifierFlags & ModifierFlags.Private)) {
let baseClassTypeToGetPropertiesFrom: Type;
if (baseTypeNode) {
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeAtLocation(baseTypeNode);
if (classElementModifierFlags & ModifierFlags.Static) {
// Use static class to get property symbols from
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeOfSymbolAtLocation(
baseClassTypeToGetPropertiesFrom.symbol, classLikeDeclaration);
}
}
const implementedInterfaceTypePropertySymbols = (classElementModifierFlags & ModifierFlags.Static) ?
undefined :
flatMap(implementsTypeNodes, typeNode => typeChecker.getPropertiesOfType(typeChecker.getTypeAtLocation(typeNode)));
// List of property symbols of base type that are not private and already implemented
symbols = filterClassMembersList(
baseClassTypeToGetPropertiesFrom ?
typeChecker.getPropertiesOfType(baseClassTypeToGetPropertiesFrom) :
undefined,
implementedInterfaceTypePropertySymbols,
classLikeDeclaration.members,
classElementModifierFlags);
}
}
}
/**
* Returns the immediate owning object literal or binding pattern of a context token,
* on the condition that one exists and that the context implies completion should be given.
@@ -953,6 +1020,49 @@ namespace ts.Completions {
return undefined;
}
function isFromClassElementDeclaration(node: Node) {
return isClassElement(node.parent) && isClassLike(node.parent.parent);
}
/**
* Returns the immediate owning class declaration of a context token,
* on the condition that one exists and that the context implies completion should be given.
*/
function tryGetClassLikeCompletionContainer(contextToken: Node): ClassLikeDeclaration {
if (contextToken) {
switch (contextToken.kind) {
case SyntaxKind.OpenBraceToken: // class c { |
if (isClassLike(contextToken.parent)) {
return contextToken.parent;
}
break;
// class c {getValue(): number; | }
case SyntaxKind.CommaToken:
case SyntaxKind.SemicolonToken:
// class c { method() { } | }
case SyntaxKind.CloseBraceToken:
if (isClassLike(location)) {
return location;
}
break;
default:
if (isFromClassElementDeclaration(contextToken) &&
(isClassMemberCompletionKeyword(contextToken.kind) ||
isClassMemberCompletionKeywordText(contextToken.getText()))) {
return contextToken.parent.parent as ClassLikeDeclaration;
}
}
}
// class c { method() { } | method2() { } }
if (location && location.kind === SyntaxKind.SyntaxList && isClassLike(location.parent)) {
return location.parent;
}
return undefined;
}
function tryGetContainingJsxElement(contextToken: Node): JsxOpeningLikeElement {
if (contextToken) {
const parent = contextToken.parent;
@@ -1081,7 +1191,7 @@ namespace ts.Completions {
isFunction(containingNodeKind);
case SyntaxKind.StaticKeyword:
return containingNodeKind === SyntaxKind.PropertyDeclaration;
return containingNodeKind === SyntaxKind.PropertyDeclaration && !isClassLike(contextToken.parent.parent);
case SyntaxKind.DotDotDotToken:
return containingNodeKind === SyntaxKind.Parameter ||
@@ -1098,13 +1208,17 @@ namespace ts.Completions {
containingNodeKind === SyntaxKind.ExportSpecifier ||
containingNodeKind === SyntaxKind.NamespaceImport;
case SyntaxKind.GetKeyword:
case SyntaxKind.SetKeyword:
if (isFromClassElementDeclaration(contextToken)) {
return false;
}
// falls through
case SyntaxKind.ClassKeyword:
case SyntaxKind.EnumKeyword:
case SyntaxKind.InterfaceKeyword:
case SyntaxKind.FunctionKeyword:
case SyntaxKind.VarKeyword:
case SyntaxKind.GetKeyword:
case SyntaxKind.SetKeyword:
case SyntaxKind.ImportKeyword:
case SyntaxKind.LetKeyword:
case SyntaxKind.ConstKeyword:
@@ -1113,6 +1227,13 @@ namespace ts.Completions {
return true;
}
// If the previous token is keyword correspoding to class member completion keyword
// there will be completion available here
if (isClassMemberCompletionKeywordText(contextToken.getText()) &&
isFromClassElementDeclaration(contextToken)) {
return false;
}
// Previous token may have been a keyword that was converted to an identifier.
switch (contextToken.getText()) {
case "abstract":
@@ -1159,7 +1280,7 @@ namespace ts.Completions {
for (const element of namedImportsOrExports) {
// If this is the current item we are editing right now, do not filter it out
if (element.getStart() <= position && position <= element.getEnd()) {
if (isCurrentlyEditingNode(element)) {
continue;
}
@@ -1198,7 +1319,7 @@ namespace ts.Completions {
}
// If this is the current item we are editing right now, do not filter it out
if (m.getStart() <= position && position <= m.getEnd()) {
if (isCurrentlyEditingNode(m)) {
continue;
}
@@ -1214,7 +1335,7 @@ namespace ts.Completions {
// TODO(jfreeman): Account for computed property name
// NOTE: if one only performs this step when m.name is an identifier,
// things like '__proto__' are not filtered out.
existingName = (<Identifier>m.name).text;
existingName = (getNameOfDeclaration(m) as Identifier).text;
}
existingMemberNames.set(existingName, true);
@@ -1223,6 +1344,58 @@ namespace ts.Completions {
return filter(contextualMemberSymbols, m => !existingMemberNames.get(m.name));
}
/**
* Filters out completion suggestions for class elements.
*
* @returns Symbols to be suggested in an class element depending on existing memebers and symbol flags
*/
function filterClassMembersList(baseSymbols: Symbol[], implementingTypeSymbols: Symbol[], existingMembers: ClassElement[], currentClassElementModifierFlags: ModifierFlags): Symbol[] {
const existingMemberNames = createMap<boolean>();
for (const m of existingMembers) {
// Ignore omitted expressions for missing members
if (m.kind !== SyntaxKind.PropertyDeclaration &&
m.kind !== SyntaxKind.MethodDeclaration &&
m.kind !== SyntaxKind.GetAccessor &&
m.kind !== SyntaxKind.SetAccessor) {
continue;
}
// If this is the current item we are editing right now, do not filter it out
if (isCurrentlyEditingNode(m)) {
continue;
}
// Dont filter member even if the name matches if it is declared private in the list
if (hasModifier(m, ModifierFlags.Private)) {
continue;
}
// do not filter it out if the static presence doesnt match
const mIsStatic = hasModifier(m, ModifierFlags.Static);
const currentElementIsStatic = !!(currentClassElementModifierFlags & ModifierFlags.Static);
if ((mIsStatic && !currentElementIsStatic) ||
(!mIsStatic && currentElementIsStatic)) {
continue;
}
const existingName = getPropertyNameForPropertyNameNode(m.name);
if (existingName) {
existingMemberNames.set(existingName, true);
}
}
return concatenate(
filter(baseSymbols, baseProperty => isValidProperty(baseProperty, ModifierFlags.Private)),
filter(implementingTypeSymbols, implementingProperty => isValidProperty(implementingProperty, ModifierFlags.NonPublicAccessibilityModifier))
);
function isValidProperty(propertySymbol: Symbol, inValidModifierFlags: ModifierFlags) {
return !existingMemberNames.get(propertySymbol.name) &&
propertySymbol.getDeclarations() &&
!(getDeclarationModifierFlagsFromSymbol(propertySymbol) & inValidModifierFlags);
}
}
/**
* Filters out completion suggestions from 'symbols' according to existing JSX attributes.
*
@@ -1233,7 +1406,7 @@ namespace ts.Completions {
const seenNames = createMap<boolean>();
for (const attr of attributes) {
// If this is the current item we are editing right now, do not filter it out
if (attr.getStart() <= position && position <= attr.getEnd()) {
if (isCurrentlyEditingNode(attr)) {
continue;
}
@@ -1244,6 +1417,10 @@ namespace ts.Completions {
return filter(symbols, a => !seenNames.get(a.name));
}
function isCurrentlyEditingNode(node: Node): boolean {
return node.getStart() <= position && position <= node.getEnd();
}
}
/**
@@ -1306,6 +1483,29 @@ namespace ts.Completions {
});
}
function isClassMemberCompletionKeyword(kind: SyntaxKind) {
switch (kind) {
case SyntaxKind.PublicKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.AbstractKeyword:
case SyntaxKind.StaticKeyword:
case SyntaxKind.ConstructorKeyword:
case SyntaxKind.ReadonlyKeyword:
case SyntaxKind.GetKeyword:
case SyntaxKind.SetKeyword:
case SyntaxKind.AsyncKeyword:
return true;
}
}
function isClassMemberCompletionKeywordText(text: string) {
return isClassMemberCompletionKeyword(stringToToken(text));
}
const classMemberKeywordCompletions = filter(keywordCompletions, entry =>
isClassMemberCompletionKeywordText(entry.name));
function isEqualityExpression(node: Node): node is BinaryExpression {
return isBinaryExpression(node) && isEqualityOperatorKind(node.operatorToken.kind);
}
+17 -32
View File
@@ -108,10 +108,6 @@ namespace ts.FindAllReferences {
switch (def.type) {
case "symbol": {
const { symbol, node } = def;
const declarations = symbol.declarations;
if (!declarations || declarations.length === 0) {
return undefined;
}
const { displayParts, kind } = getDefinitionKindAndDisplayParts(symbol, node, checker);
const name = displayParts.map(p => p.text).join("");
return { node, name, kind, displayParts };
@@ -285,11 +281,6 @@ namespace ts.FindAllReferences.Core {
return undefined;
}
// The symbol was an internal symbol and does not have a declaration e.g. undefined symbol
if (!symbol.declarations || !symbol.declarations.length) {
return undefined;
}
return getReferencedSymbolsForSymbol(symbol, node, sourceFiles, checker, cancellationToken, options);
}
@@ -559,7 +550,7 @@ namespace ts.FindAllReferences.Core {
}
function isObjectBindingPatternElementWithoutPropertyName(symbol: Symbol): boolean {
const bindingElement = <BindingElement>getDeclarationOfKind(symbol, SyntaxKind.BindingElement);
const bindingElement = getDeclarationOfKind<BindingElement>(symbol, SyntaxKind.BindingElement);
return bindingElement &&
bindingElement.parent.kind === SyntaxKind.ObjectBindingPattern &&
!bindingElement.propertyName;
@@ -567,7 +558,7 @@ namespace ts.FindAllReferences.Core {
function getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol: Symbol, checker: TypeChecker): Symbol | undefined {
if (isObjectBindingPatternElementWithoutPropertyName(symbol)) {
const bindingElement = <BindingElement>getDeclarationOfKind(symbol, SyntaxKind.BindingElement);
const bindingElement = getDeclarationOfKind<BindingElement>(symbol, SyntaxKind.BindingElement);
const typeOfPattern = checker.getTypeAtLocation(bindingElement.parent);
return typeOfPattern && checker.getPropertyOfType(typeOfPattern, (<Identifier>bindingElement.name).text);
}
@@ -646,7 +637,9 @@ namespace ts.FindAllReferences.Core {
return parent ? scope.getSourceFile() : scope;
}
function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, start: number, end: number): number[] {
function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile, fullStart = false): number[] {
const start = fullStart ? container.getFullStart() : container.getStart(sourceFile);
const end = container.getEnd();
const positions: number[] = [];
/// TODO: Cache symbol existence for files to save text search
@@ -685,16 +678,11 @@ namespace ts.FindAllReferences.Core {
const references: Entry[] = [];
const sourceFile = container.getSourceFile();
const labelName = targetLabel.text;
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, labelName, container.getStart(), container.getEnd());
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, labelName, container);
for (const position of possiblePositions) {
const node = getTouchingWord(sourceFile, position);
if (!node || node.getWidth() !== labelName.length) {
continue;
}
// Only pick labels that are either the target label, or have a target that is the target label
if (node === targetLabel ||
(isJumpStatementTarget(node) && getTargetLabel(node, labelName) === targetLabel)) {
if (node && (node === targetLabel || (isJumpStatementTarget(node) && getTargetLabel(node, labelName) === targetLabel))) {
references.push(nodeEntry(node));
}
}
@@ -706,15 +694,14 @@ namespace ts.FindAllReferences.Core {
// Compare the length so we filter out strict superstrings of the symbol we are looking for
switch (node && node.kind) {
case SyntaxKind.Identifier:
return node.getWidth() === searchSymbolName.length;
return unescapeIdentifier((node as Identifier).text).length === searchSymbolName.length;
case SyntaxKind.StringLiteral:
return (isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) &&
// For string literals we have two additional chars for the quotes
node.getWidth() === searchSymbolName.length + 2;
(node as StringLiteral).text.length === searchSymbolName.length;
case SyntaxKind.NumericLiteral:
return isLiteralNameOfPropertyDeclarationOrIndexAccess(node) && node.getWidth() === searchSymbolName.length;
return isLiteralNameOfPropertyDeclarationOrIndexAccess(node) && (node as NumericLiteral).text.length === searchSymbolName.length;
default:
return false;
@@ -731,7 +718,7 @@ namespace ts.FindAllReferences.Core {
}
function addReferencesForKeywordInFile(sourceFile: SourceFile, kind: SyntaxKind, searchText: string, references: Push<NodeEntry>): void {
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, sourceFile.getStart(), sourceFile.getEnd());
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText);
for (const position of possiblePositions) {
const referenceLocation = getTouchingPropertyName(sourceFile, position);
if (referenceLocation.kind === kind) {
@@ -755,9 +742,7 @@ namespace ts.FindAllReferences.Core {
return;
}
const start = state.findInComments ? container.getFullStart() : container.getStart();
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, search.text, start, container.getEnd());
for (const position of possiblePositions) {
for (const position of getPossibleSymbolReferencePositions(sourceFile, search.text, container, /*fullStart*/ state.findInComments)) {
getReferencesAtLocation(sourceFile, position, search, state);
}
}
@@ -908,7 +893,7 @@ namespace ts.FindAllReferences.Core {
* position of property accessing, the referenceEntry of such position will be handled in the first case.
*/
if (!(flags & SymbolFlags.Transient) && search.includes(shorthandValueSymbol)) {
addReference(valueDeclaration.name, shorthandValueSymbol, search.location, state);
addReference(getNameOfDeclaration(valueDeclaration), shorthandValueSymbol, search.location, state);
}
}
@@ -1201,7 +1186,7 @@ namespace ts.FindAllReferences.Core {
const references: Entry[] = [];
const sourceFile = searchSpaceNode.getSourceFile();
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "super", searchSpaceNode.getStart(), searchSpaceNode.getEnd());
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "super", searchSpaceNode);
for (const position of possiblePositions) {
const node = getTouchingWord(sourceFile, position);
@@ -1263,13 +1248,13 @@ namespace ts.FindAllReferences.Core {
if (searchSpaceNode.kind === SyntaxKind.SourceFile) {
forEach(sourceFiles, sourceFile => {
cancellationToken.throwIfCancellationRequested();
possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", sourceFile.getStart(), sourceFile.getEnd());
possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this");
getThisReferencesInFile(sourceFile, sourceFile, possiblePositions, references);
});
}
else {
const sourceFile = searchSpaceNode.getSourceFile();
possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", searchSpaceNode.getStart(), searchSpaceNode.getEnd());
possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", searchSpaceNode);
getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, references);
}
@@ -1323,7 +1308,7 @@ namespace ts.FindAllReferences.Core {
for (const sourceFile of sourceFiles) {
cancellationToken.throwIfCancellationRequested();
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, node.text, sourceFile.getStart(), sourceFile.getEnd());
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, node.text);
getReferencesForStringLiteralInFile(sourceFile, node.text, possiblePositions, references);
}
+2 -2
View File
@@ -362,7 +362,7 @@ namespace ts.formatting {
sourceFile: SourceFileLike): TextChange[] {
// formatting context is used by rules provider
const formattingContext = new FormattingContext(sourceFile, requestKind);
const formattingContext = new FormattingContext(sourceFile, requestKind, options);
let previousRangeHasError: boolean;
let previousRange: TextRangeWithKind;
let previousParent: Node;
@@ -487,7 +487,7 @@ namespace ts.formatting {
// falls through
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.Parameter:
return (<Declaration>node).name.kind;
return getNameOfDeclaration(<Declaration>node).kind;
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ namespace ts.formatting {
private contextNodeBlockIsOnOneLine: boolean;
private nextNodeBlockIsOnOneLine: boolean;
constructor(public readonly sourceFile: SourceFileLike, public formattingRequestKind: FormattingRequestKind) {
constructor(public readonly sourceFile: SourceFileLike, public formattingRequestKind: FormattingRequestKind, public options: ts.FormatCodeSettings) {
}
public updateContext(currentRange: TextRangeWithKind, currentTokenParent: Node, nextRange: TextRangeWithKind, nextTokenParent: Node, commonParent: Node) {
@@ -12,12 +12,11 @@ namespace ts.formatting {
static Any: RuleOperationContext = new RuleOperationContext();
public IsAny(): boolean {
return this === RuleOperationContext.Any;
}
public InContext(context: FormattingContext): boolean {
public InContext(context: FormattingContext): boolean {
if (this.IsAny()) {
return true;
}
+123 -78
View File
@@ -84,6 +84,7 @@ namespace ts.formatting {
public NoSpaceBeforeComma: Rule;
public SpaceAfterCertainKeywords: Rule;
public NoSpaceAfterNewKeywordOnConstructorSignature: Rule;
public SpaceAfterLetConstInVariableDeclaration: Rule;
public NoSpaceBeforeOpenParenInFuncCall: Rule;
public SpaceAfterFunctionInFuncDecl: Rule;
@@ -147,6 +148,9 @@ namespace ts.formatting {
// These rules are higher in priority than user-configurable rules.
public HighPriorityCommonRules: Rule[];
// These rules are applied after high priority rules.
public UserConfigurableRules: Rule[];
// These rules are lower in priority than user-configurable rules.
public LowPriorityCommonRules: Rule[];
@@ -279,7 +283,9 @@ namespace ts.formatting {
this.NoSpaceAfterDot = new Rule(RuleDescriptor.create3(SyntaxKind.DotToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
// No space before and after indexer
this.NoSpaceBeforeOpenBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeOpenBracket = new Rule(
RuleDescriptor.create2(Shared.TokenRange.AnyExcept(SyntaxKind.AsyncKeyword), SyntaxKind.OpenBracketToken),
RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceAfterCloseBracket = new Rule(RuleDescriptor.create3(SyntaxKind.CloseBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNotBeforeBlockInFunctionDeclarationContext), RuleAction.Delete));
// Place a space before open brace in a function declaration
@@ -295,10 +301,10 @@ namespace ts.formatting {
this.SpaceBeforeOpenBraceInControl = new Rule(RuleDescriptor.create2(this.ControlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext, Rules.IsNotFormatOnEnter, Rules.IsSameLineTokenOrBeforeMultilineBlockContext), RuleAction.Space), RuleFlags.CanDeleteNewLines);
// Insert a space after { and before } in single-line contexts, but remove space from empty object literals {}.
this.SpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsBraceWrappedContext), RuleAction.Space));
this.SpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsBraceWrappedContext), RuleAction.Space));
this.NoSpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), Rules.IsBraceWrappedContext), RuleAction.Space));
this.SpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), Rules.IsBraceWrappedContext), RuleAction.Space));
this.NoSpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBetweenEmptyBraceBrackets = new Rule(RuleDescriptor.create1(SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsObjectContext), RuleAction.Delete));
// Insert new line after { and before } in multi-line contexts.
@@ -331,11 +337,12 @@ namespace ts.formatting {
this.NoSpaceBeforeComma = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CommaToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceAfterCertainKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceAfterNewKeywordOnConstructorSignature = new Rule(RuleDescriptor.create1(SyntaxKind.NewKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsConstructorSignatureContext), RuleAction.Delete));
this.SpaceAfterLetConstInVariableDeclaration = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.LetKeyword, SyntaxKind.ConstKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsStartOfVariableDeclarationList), RuleAction.Space));
this.NoSpaceBeforeOpenParenInFuncCall = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionCallOrNewContext, Rules.IsPreviousTokenNotComma), RuleAction.Delete));
this.SpaceAfterFunctionInFuncDecl = new Rule(RuleDescriptor.create3(SyntaxKind.FunctionKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space));
this.SpaceBeforeOpenParenInFuncDecl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionDeclContext), RuleAction.Space));
this.NoSpaceBeforeOpenParenInFuncDecl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionDeclContext), RuleAction.Delete));
this.SpaceBeforeOpenParenInFuncDecl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceBeforeFunctionParenthesis"), Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionDeclContext), RuleAction.Space));
this.NoSpaceBeforeOpenParenInFuncDecl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceBeforeFunctionParenthesis"), Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionDeclContext), RuleAction.Delete));
this.SpaceAfterVoidOperator = new Rule(RuleDescriptor.create3(SyntaxKind.VoidKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsVoidOpContext), RuleAction.Space));
this.NoSpaceBetweenReturnAndSemicolon = new Rule(RuleDescriptor.create1(SyntaxKind.ReturnKeyword, SyntaxKind.SemicolonToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
@@ -357,9 +364,8 @@ namespace ts.formatting {
// TypeScript-specific higher priority rules
// Treat constructor as an identifier in a function declaration, and remove spaces between constructor and following left parentheses
this.SpaceAfterConstructor = new Rule(RuleDescriptor.create1(SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceAfterConstructor = new Rule(RuleDescriptor.create1(SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceAfterConstructor = new Rule(RuleDescriptor.create1(SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterConstructor"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceAfterConstructor = new Rule(RuleDescriptor.create1(SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterConstructor"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
// Use of module as a function call. e.g.: import m2 = module("m2");
this.NoSpaceAfterModuleImport = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword]), SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
@@ -416,6 +422,72 @@ namespace ts.formatting {
// No space before non-null assertion operator
this.NoSpaceBeforeNonNullAssertionOperator = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.ExclamationToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonNullAssertionContext), RuleAction.Delete));
///
/// Rules controlled by user options
///
// Insert space after comma delimiter
this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterCommaDelimiter"), Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext, Rules.IsNextTokenNotCloseBracket), RuleAction.Space));
this.NoSpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterCommaDelimiter"), Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext), RuleAction.Delete));
// Insert space before and after binary operators
this.SpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
this.SpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
this.NoSpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete));
this.NoSpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete));
// Insert space after keywords in control flow statements
this.SpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterKeywordsInControlFlowStatements"), Rules.IsControlDeclContext), RuleAction.Space));
this.NoSpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterKeywordsInControlFlowStatements"), Rules.IsControlDeclContext), RuleAction.Delete));
// Open Brace braces after function
// TypeScript: Function can have return types, which can be made of tons of different token kinds
this.NewLineBeforeOpenBraceInFunction = new Rule(RuleDescriptor.create2(this.FunctionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("placeOpenBraceOnNewLineForFunctions"), Rules.IsFunctionDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
// Open Brace braces after TypeScript module/class/interface
this.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock = new Rule(RuleDescriptor.create2(this.TypeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("placeOpenBraceOnNewLineForFunctions"), Rules.IsTypeScriptDeclWithBlockContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
// Open Brace braces after control block
this.NewLineBeforeOpenBraceInControl = new Rule(RuleDescriptor.create2(this.ControlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("placeOpenBraceOnNewLineForControlBlocks"), Rules.IsControlDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
// Insert space after semicolon in for statement
this.SpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterSemicolonInForStatements"), Rules.IsNonJsxSameLineTokenContext, Rules.IsForContext), RuleAction.Space));
this.NoSpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterSemicolonInForStatements"), Rules.IsNonJsxSameLineTokenContext, Rules.IsForContext), RuleAction.Delete));
// Insert space after opening and before closing nonempty parenthesis
this.SpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.SpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceBetweenParens = new Rule(RuleDescriptor.create1(SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
// Insert space after opening and before closing nonempty brackets
this.SpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.SpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceBetweenBrackets = new Rule(RuleDescriptor.create1(SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
// Insert space after opening and before closing template string braces
this.NoSpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
// No space after { and before } in JSX expression
this.NoSpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
this.SpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
this.NoSpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
this.SpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
// Insert space after function keyword for anonymous functions
this.SpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), Rules.IsFunctionDeclContext), RuleAction.Space));
this.NoSpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), Rules.IsFunctionDeclContext), RuleAction.Delete));
// No space after type assertion
this.NoSpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterTypeAssertion"), Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Delete));
this.SpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterTypeAssertion"), Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Space));
// These rules are higher in priority than user-configurable rules.
this.HighPriorityCommonRules = [
this.IgnoreBeforeComment, this.IgnoreAfterLineComment,
@@ -462,7 +534,27 @@ namespace ts.formatting {
this.SpaceBeforeAt,
this.NoSpaceAfterAt,
this.SpaceAfterDecorator,
this.NoSpaceBeforeNonNullAssertionOperator
this.NoSpaceBeforeNonNullAssertionOperator,
this.NoSpaceAfterNewKeywordOnConstructorSignature
];
// These rules are applied after high priority rules.
this.UserConfigurableRules = [
this.SpaceAfterConstructor, this.NoSpaceAfterConstructor,
this.SpaceAfterComma, this.NoSpaceAfterComma,
this.SpaceAfterAnonymousFunctionKeyword, this.NoSpaceAfterAnonymousFunctionKeyword,
this.SpaceAfterKeywordInControl, this.NoSpaceAfterKeywordInControl,
this.SpaceAfterOpenParen, this.SpaceBeforeCloseParen, this.NoSpaceBetweenParens, this.NoSpaceAfterOpenParen, this.NoSpaceBeforeCloseParen,
this.SpaceAfterOpenBracket, this.SpaceBeforeCloseBracket, this.NoSpaceBetweenBrackets, this.NoSpaceAfterOpenBracket, this.NoSpaceBeforeCloseBracket,
this.SpaceAfterOpenBrace, this.SpaceBeforeCloseBrace, this.NoSpaceBetweenEmptyBraceBrackets, this.NoSpaceAfterOpenBrace, this.NoSpaceBeforeCloseBrace,
this.SpaceAfterTemplateHeadAndMiddle, this.SpaceBeforeTemplateMiddleAndTail, this.NoSpaceAfterTemplateHeadAndMiddle, this.NoSpaceBeforeTemplateMiddleAndTail,
this.SpaceAfterOpenBraceInJsxExpression, this.SpaceBeforeCloseBraceInJsxExpression, this.NoSpaceAfterOpenBraceInJsxExpression, this.NoSpaceBeforeCloseBraceInJsxExpression,
this.SpaceAfterSemicolonInFor, this.NoSpaceAfterSemicolonInFor,
this.SpaceBeforeBinaryOperator, this.SpaceAfterBinaryOperator, this.NoSpaceBeforeBinaryOperator, this.NoSpaceAfterBinaryOperator,
this.SpaceBeforeOpenParenInFuncDecl, this.NoSpaceBeforeOpenParenInFuncDecl,
this.NewLineBeforeOpenBraceInControl,
this.NewLineBeforeOpenBraceInFunction, this.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock,
this.SpaceAfterTypeAssertion, this.NoSpaceAfterTypeAssertion
];
// These rules are lower in priority than user-configurable rules.
@@ -475,79 +567,28 @@ namespace ts.formatting {
this.SpaceAfterSemicolon,
this.SpaceBetweenStatements, this.SpaceAfterTryFinally
];
///
/// Rules controlled by user options
///
// Insert space after comma delimiter
this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext, Rules.IsNextTokenNotCloseBracket), RuleAction.Space));
this.NoSpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext), RuleAction.Delete));
// Insert space before and after binary operators
this.SpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
this.SpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
this.NoSpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete));
this.NoSpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete));
// Insert space after keywords in control flow statements
this.SpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext), RuleAction.Space));
this.NoSpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext), RuleAction.Delete));
// Open Brace braces after function
// TypeScript: Function can have return types, which can be made of tons of different token kinds
this.NewLineBeforeOpenBraceInFunction = new Rule(RuleDescriptor.create2(this.FunctionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
// Open Brace braces after TypeScript module/class/interface
this.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock = new Rule(RuleDescriptor.create2(this.TypeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsTypeScriptDeclWithBlockContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
// Open Brace braces after control block
this.NewLineBeforeOpenBraceInControl = new Rule(RuleDescriptor.create2(this.ControlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
// Insert space after semicolon in for statement
this.SpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsForContext), RuleAction.Space));
this.NoSpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsForContext), RuleAction.Delete));
// Insert space after opening and before closing nonempty parenthesis
this.SpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.SpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceBetweenParens = new Rule(RuleDescriptor.create1(SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
// Insert space after opening and before closing nonempty brackets
this.SpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.SpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceBetweenBrackets = new Rule(RuleDescriptor.create1(SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
// Insert space after opening and before closing template string braces
this.NoSpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.SpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
// No space after { and before } in JSX expression
this.NoSpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
this.SpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
this.NoSpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
this.SpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
// Insert space after function keyword for anonymous functions
this.SpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space));
this.NoSpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Delete));
// No space after type assertion
this.NoSpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Delete));
this.SpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Space));
}
///
/// Contexts
///
static IsOptionEnabled(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
return (context) => context.options && context.options.hasOwnProperty(optionName) && !!context.options[optionName];
}
static IsOptionDisabled(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
return (context) => context.options && context.options.hasOwnProperty(optionName) && !context.options[optionName];
}
static IsOptionDisabledOrUndefined(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
return (context) => !context.options || !context.options.hasOwnProperty(optionName) || !context.options[optionName];
}
static IsOptionEnabledOrUndefined(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
return (context) => !context.options || !context.options.hasOwnProperty(optionName) || !!context.options[optionName];
}
static IsForContext(context: FormattingContext): boolean {
return context.contextNode.kind === SyntaxKind.ForStatement;
}
@@ -845,6 +886,10 @@ namespace ts.formatting {
return context.contextNode.kind === SyntaxKind.TypeLiteral; // && context.contextNode.parent.kind !== SyntaxKind.InterfaceDeclaration;
}
static IsConstructorSignatureContext(context: FormattingContext): boolean {
return context.contextNode.kind === SyntaxKind.ConstructSignature;
}
static IsTypeArgumentOrParameterOrAssertion(token: TextRangeWithKind, parent: Node): boolean {
if (token.kind !== SyntaxKind.LessThanToken && token.kind !== SyntaxKind.GreaterThanToken) {
return false;
+1 -2
View File
@@ -41,8 +41,7 @@ namespace ts.formatting {
}
private FillRule(rule: Rule, rulesBucketConstructionStateList: RulesBucketConstructionState[]): void {
const specificRule = rule.Descriptor.LeftTokenRange !== Shared.TokenRange.Any &&
rule.Descriptor.RightTokenRange !== Shared.TokenRange.Any;
const specificRule = rule.Descriptor.LeftTokenRange.isSpecific() && rule.Descriptor.RightTokenRange.isSpecific();
rule.Descriptor.LeftTokenRange.GetTokens().forEach((left) => {
rule.Descriptor.RightTokenRange.GetTokens().forEach((right) => {
+2 -134
View File
@@ -5,11 +5,12 @@ namespace ts.formatting {
export class RulesProvider {
private globalRules: Rules;
private options: ts.FormatCodeSettings;
private activeRules: Rule[];
private rulesMap: RulesMap;
constructor() {
this.globalRules = new Rules();
const activeRules = this.globalRules.HighPriorityCommonRules.slice(0).concat(this.globalRules.UserConfigurableRules).concat(this.globalRules.LowPriorityCommonRules);
this.rulesMap = RulesMap.create(activeRules);
}
public getRuleName(rule: Rule): string {
@@ -30,141 +31,8 @@ namespace ts.formatting {
public ensureUpToDate(options: ts.FormatCodeSettings) {
if (!this.options || !ts.compareDataObjects(this.options, options)) {
const activeRules = this.createActiveRules(options);
const rulesMap = RulesMap.create(activeRules);
this.activeRules = activeRules;
this.rulesMap = rulesMap;
this.options = ts.clone(options);
}
}
private createActiveRules(options: ts.FormatCodeSettings): Rule[] {
let rules = this.globalRules.HighPriorityCommonRules.slice(0);
if (options.insertSpaceAfterConstructor) {
rules.push(this.globalRules.SpaceAfterConstructor);
}
else {
rules.push(this.globalRules.NoSpaceAfterConstructor);
}
if (options.insertSpaceAfterCommaDelimiter) {
rules.push(this.globalRules.SpaceAfterComma);
}
else {
rules.push(this.globalRules.NoSpaceAfterComma);
}
if (options.insertSpaceAfterFunctionKeywordForAnonymousFunctions) {
rules.push(this.globalRules.SpaceAfterAnonymousFunctionKeyword);
}
else {
rules.push(this.globalRules.NoSpaceAfterAnonymousFunctionKeyword);
}
if (options.insertSpaceAfterKeywordsInControlFlowStatements) {
rules.push(this.globalRules.SpaceAfterKeywordInControl);
}
else {
rules.push(this.globalRules.NoSpaceAfterKeywordInControl);
}
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis) {
rules.push(this.globalRules.SpaceAfterOpenParen);
rules.push(this.globalRules.SpaceBeforeCloseParen);
rules.push(this.globalRules.NoSpaceBetweenParens);
}
else {
rules.push(this.globalRules.NoSpaceAfterOpenParen);
rules.push(this.globalRules.NoSpaceBeforeCloseParen);
rules.push(this.globalRules.NoSpaceBetweenParens);
}
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets) {
rules.push(this.globalRules.SpaceAfterOpenBracket);
rules.push(this.globalRules.SpaceBeforeCloseBracket);
rules.push(this.globalRules.NoSpaceBetweenBrackets);
}
else {
rules.push(this.globalRules.NoSpaceAfterOpenBracket);
rules.push(this.globalRules.NoSpaceBeforeCloseBracket);
rules.push(this.globalRules.NoSpaceBetweenBrackets);
}
// The default value of InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces is true
// so if the option is undefined, we should treat it as true as well
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces !== false) {
rules.push(this.globalRules.SpaceAfterOpenBrace);
rules.push(this.globalRules.SpaceBeforeCloseBrace);
rules.push(this.globalRules.NoSpaceBetweenEmptyBraceBrackets);
}
else {
rules.push(this.globalRules.NoSpaceAfterOpenBrace);
rules.push(this.globalRules.NoSpaceBeforeCloseBrace);
rules.push(this.globalRules.NoSpaceBetweenEmptyBraceBrackets);
}
if (options.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces) {
rules.push(this.globalRules.SpaceAfterTemplateHeadAndMiddle);
rules.push(this.globalRules.SpaceBeforeTemplateMiddleAndTail);
}
else {
rules.push(this.globalRules.NoSpaceAfterTemplateHeadAndMiddle);
rules.push(this.globalRules.NoSpaceBeforeTemplateMiddleAndTail);
}
if (options.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces) {
rules.push(this.globalRules.SpaceAfterOpenBraceInJsxExpression);
rules.push(this.globalRules.SpaceBeforeCloseBraceInJsxExpression);
}
else {
rules.push(this.globalRules.NoSpaceAfterOpenBraceInJsxExpression);
rules.push(this.globalRules.NoSpaceBeforeCloseBraceInJsxExpression);
}
if (options.insertSpaceAfterSemicolonInForStatements) {
rules.push(this.globalRules.SpaceAfterSemicolonInFor);
}
else {
rules.push(this.globalRules.NoSpaceAfterSemicolonInFor);
}
if (options.insertSpaceBeforeAndAfterBinaryOperators) {
rules.push(this.globalRules.SpaceBeforeBinaryOperator);
rules.push(this.globalRules.SpaceAfterBinaryOperator);
}
else {
rules.push(this.globalRules.NoSpaceBeforeBinaryOperator);
rules.push(this.globalRules.NoSpaceAfterBinaryOperator);
}
if (options.insertSpaceBeforeFunctionParenthesis) {
rules.push(this.globalRules.SpaceBeforeOpenParenInFuncDecl);
}
else {
rules.push(this.globalRules.NoSpaceBeforeOpenParenInFuncDecl);
}
if (options.placeOpenBraceOnNewLineForControlBlocks) {
rules.push(this.globalRules.NewLineBeforeOpenBraceInControl);
}
if (options.placeOpenBraceOnNewLineForFunctions) {
rules.push(this.globalRules.NewLineBeforeOpenBraceInFunction);
rules.push(this.globalRules.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock);
}
if (options.insertSpaceAfterTypeAssertion) {
rules.push(this.globalRules.SpaceAfterTypeAssertion);
}
else {
rules.push(this.globalRules.NoSpaceAfterTypeAssertion);
}
rules = rules.concat(this.globalRules.LowPriorityCommonRules);
return rules;
}
}
}
+1 -4
View File
@@ -341,10 +341,7 @@ namespace ts.formatting {
return Value.Unknown;
}
if (node.parent && (
node.parent.kind === SyntaxKind.CallExpression ||
node.parent.kind === SyntaxKind.NewExpression) &&
(<CallExpression>node.parent).expression !== node) {
if (node.parent && isCallOrNewExpression(node.parent) && (<CallExpression>node.parent).expression !== node) {
const fullCallOrNewExpression = (<CallExpression | NewExpression>node.parent).expression;
const startingExpression = getStartingExpression(fullCallOrNewExpression);
+72 -75
View File
@@ -3,22 +3,13 @@
/* @internal */
namespace ts.formatting {
export namespace Shared {
export interface ITokenAccess {
GetTokens(): SyntaxKind[];
Contains(token: SyntaxKind): boolean;
const allTokens: SyntaxKind[] = [];
for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) {
allTokens.push(token);
}
export class TokenRangeAccess implements ITokenAccess {
private tokens: SyntaxKind[];
constructor(from: SyntaxKind, to: SyntaxKind, except: SyntaxKind[]) {
this.tokens = [];
for (let token = from; token <= to; token++) {
if (ts.indexOf(except, token) < 0) {
this.tokens.push(token);
}
}
}
class TokenValuesAccess implements TokenRange {
constructor(private readonly tokens: SyntaxKind[] = []) { }
public GetTokens(): SyntaxKind[] {
return this.tokens;
@@ -27,27 +18,12 @@ namespace ts.formatting {
public Contains(token: SyntaxKind): boolean {
return this.tokens.indexOf(token) >= 0;
}
public isSpecific() { return true; }
}
export class TokenValuesAccess implements ITokenAccess {
private tokens: SyntaxKind[];
constructor(tks: SyntaxKind[]) {
this.tokens = tks && tks.length ? tks : <SyntaxKind[]>[];
}
public GetTokens(): SyntaxKind[] {
return this.tokens;
}
public Contains(token: SyntaxKind): boolean {
return this.tokens.indexOf(token) >= 0;
}
}
export class TokenSingleValueAccess implements ITokenAccess {
constructor(public token: SyntaxKind) {
}
class TokenSingleValueAccess implements TokenRange {
constructor(private readonly token: SyntaxKind) {}
public GetTokens(): SyntaxKind[] {
return [this.token];
@@ -56,15 +32,13 @@ namespace ts.formatting {
public Contains(tokenValue: SyntaxKind): boolean {
return tokenValue === this.token;
}
public isSpecific() { return true; }
}
export class TokenAllAccess implements ITokenAccess {
class TokenAllAccess implements TokenRange {
public GetTokens(): SyntaxKind[] {
const result: SyntaxKind[] = [];
for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) {
result.push(token);
}
return result;
return allTokens;
}
public Contains(): boolean {
@@ -74,53 +48,76 @@ namespace ts.formatting {
public toString(): string {
return "[allTokens]";
}
public isSpecific() { return false; }
}
export class TokenRange {
constructor(public tokenAccess: ITokenAccess) {
}
static FromToken(token: SyntaxKind): TokenRange {
return new TokenRange(new TokenSingleValueAccess(token));
}
static FromTokens(tokens: SyntaxKind[]): TokenRange {
return new TokenRange(new TokenValuesAccess(tokens));
}
static FromRange(f: SyntaxKind, to: SyntaxKind, except: SyntaxKind[] = []): TokenRange {
return new TokenRange(new TokenRangeAccess(f, to, except));
}
static AllTokens(): TokenRange {
return new TokenRange(new TokenAllAccess());
}
class TokenAllExceptAccess implements TokenRange {
constructor(private readonly except: SyntaxKind) {}
public GetTokens(): SyntaxKind[] {
return this.tokenAccess.GetTokens();
return allTokens.filter(t => t !== this.except);
}
public Contains(token: SyntaxKind): boolean {
return this.tokenAccess.Contains(token);
return token !== this.except;
}
public toString(): string {
return this.tokenAccess.toString();
public isSpecific() { return false; }
}
export interface TokenRange {
GetTokens(): SyntaxKind[];
Contains(token: SyntaxKind): boolean;
isSpecific(): boolean;
}
export namespace TokenRange {
export function FromToken(token: SyntaxKind): TokenRange {
return new TokenSingleValueAccess(token);
}
static Any: TokenRange = TokenRange.AllTokens();
static AnyIncludingMultilineComments = TokenRange.FromTokens(TokenRange.Any.GetTokens().concat([SyntaxKind.MultiLineCommentTrivia]));
static Keywords = TokenRange.FromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword);
static BinaryOperators = TokenRange.FromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator);
static BinaryKeywordOperators = TokenRange.FromTokens([SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword, SyntaxKind.OfKeyword, SyntaxKind.AsKeyword, SyntaxKind.IsKeyword]);
static UnaryPrefixOperators = TokenRange.FromTokens([SyntaxKind.PlusPlusToken, SyntaxKind.MinusMinusToken, SyntaxKind.TildeToken, SyntaxKind.ExclamationToken]);
static UnaryPrefixExpressions = TokenRange.FromTokens([SyntaxKind.NumericLiteral, SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.OpenBraceToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
static UnaryPreincrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
static UnaryPostincrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
static UnaryPredecrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
static UnaryPostdecrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
static Comments = TokenRange.FromTokens([SyntaxKind.SingleLineCommentTrivia, SyntaxKind.MultiLineCommentTrivia]);
static TypeNames = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.NumberKeyword, SyntaxKind.StringKeyword, SyntaxKind.BooleanKeyword, SyntaxKind.SymbolKeyword, SyntaxKind.VoidKeyword, SyntaxKind.AnyKeyword]);
export function FromTokens(tokens: SyntaxKind[]): TokenRange {
return new TokenValuesAccess(tokens);
}
export function FromRange(from: SyntaxKind, to: SyntaxKind, except: SyntaxKind[] = []): TokenRange {
const tokens: SyntaxKind[] = [];
for (let token = from; token <= to; token++) {
if (ts.indexOf(except, token) < 0) {
tokens.push(token);
}
}
return new TokenValuesAccess(tokens);
}
export function AnyExcept(token: SyntaxKind): TokenRange {
return new TokenAllExceptAccess(token);
}
export const Any: TokenRange = new TokenAllAccess();
export const AnyIncludingMultilineComments = TokenRange.FromTokens([...allTokens, SyntaxKind.MultiLineCommentTrivia]);
export const Keywords = TokenRange.FromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword);
export const BinaryOperators = TokenRange.FromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator);
export const BinaryKeywordOperators = TokenRange.FromTokens([
SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword, SyntaxKind.OfKeyword, SyntaxKind.AsKeyword, SyntaxKind.IsKeyword]);
export const UnaryPrefixOperators = TokenRange.FromTokens([
SyntaxKind.PlusPlusToken, SyntaxKind.MinusMinusToken, SyntaxKind.TildeToken, SyntaxKind.ExclamationToken]);
export const UnaryPrefixExpressions = TokenRange.FromTokens([
SyntaxKind.NumericLiteral, SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken,
SyntaxKind.OpenBraceToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
export const UnaryPreincrementExpressions = TokenRange.FromTokens([
SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
export const UnaryPostincrementExpressions = TokenRange.FromTokens([
SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
export const UnaryPredecrementExpressions = TokenRange.FromTokens([
SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
export const UnaryPostdecrementExpressions = TokenRange.FromTokens([
SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
export const Comments = TokenRange.FromTokens([SyntaxKind.SingleLineCommentTrivia, SyntaxKind.MultiLineCommentTrivia]);
export const TypeNames = TokenRange.FromTokens([
SyntaxKind.Identifier, SyntaxKind.NumberKeyword, SyntaxKind.StringKeyword, SyntaxKind.BooleanKeyword,
SyntaxKind.SymbolKeyword, SyntaxKind.VoidKeyword, SyntaxKind.AnyKeyword]);
}
}
}
+31 -34
View File
@@ -50,19 +50,10 @@ namespace ts.GoToDefinition {
// get the aliased symbol instead. This allows for goto def on an import e.g.
// import {A, B} from "mod";
// to jump to the implementation directly.
if (symbol.flags & SymbolFlags.Alias) {
const declaration = symbol.declarations[0];
// Go to the original declaration for cases:
//
// (1) when the aliased symbol was declared in the location(parent).
// (2) when the aliased symbol is originating from a named import.
//
if (node.kind === SyntaxKind.Identifier &&
(node.parent === declaration ||
(declaration.kind === SyntaxKind.ImportSpecifier && declaration.parent && declaration.parent.kind === SyntaxKind.NamedImports))) {
symbol = typeChecker.getAliasedSymbol(symbol);
if (symbol.flags & SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) {
const aliased = typeChecker.getAliasedSymbol(symbol);
if (aliased.declarations) {
symbol = aliased;
}
}
@@ -85,17 +76,6 @@ namespace ts.GoToDefinition {
declaration => createDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName));
}
if (isJsxOpeningLikeElement(node.parent)) {
// If there are errors when trying to figure out stateless component function, just return the first declaration
// For example:
// declare function /*firstSource*/MainButton(buttonProps: ButtonProps): JSX.Element;
// declare function /*secondSource*/MainButton(linkProps: LinkProps): JSX.Element;
// declare function /*thirdSource*/MainButton(props: ButtonProps | LinkProps): JSX.Element;
// let opt = <Main/*firstTarget*/Button />; // Error - We get undefined for resolved signature indicating an error, then just return the first declaration
const {symbolName, symbolKind, containerName} = getSymbolInfo(typeChecker, symbol, node);
return [createDefinitionInfo(symbol.valueDeclaration, symbolKind, symbolName, containerName)];
}
// 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
@@ -106,15 +86,9 @@ namespace ts.GoToDefinition {
// function Foo(arg: Props) {}
// Foo( { pr/*1*/op1: 10, prop2: true })
const element = getContainingObjectLiteralElement(node);
if (element) {
if (typeChecker.getContextualType(element.parent as Expression)) {
const result: DefinitionInfo[] = [];
const propertySymbols = getPropertySymbolsFromContextualType(typeChecker, element);
for (const propertySymbol of propertySymbols) {
result.push(...getDefinitionFromSymbol(typeChecker, propertySymbol, node));
}
return result;
}
if (element && typeChecker.getContextualType(element.parent as Expression)) {
return flatMap(getPropertySymbolsFromContextualType(typeChecker, element), propertySymbol =>
getDefinitionFromSymbol(typeChecker, propertySymbol, node));
}
return getDefinitionFromSymbol(typeChecker, symbol, node);
}
@@ -153,6 +127,29 @@ namespace ts.GoToDefinition {
return getDefinitionFromSymbol(typeChecker, type.symbol, node);
}
// Go to the original declaration for cases:
//
// (1) when the aliased symbol was declared in the location(parent).
// (2) when the aliased symbol is originating from an import.
//
function shouldSkipAlias(node: Node, declaration: Node): boolean {
if (node.kind !== SyntaxKind.Identifier) {
return false;
}
if (node.parent === declaration) {
return true;
}
switch (declaration.kind) {
case SyntaxKind.ImportClause:
case SyntaxKind.ImportEqualsDeclaration:
return true;
case SyntaxKind.ImportSpecifier:
return declaration.parent.kind === SyntaxKind.NamedImports;
default:
return false;
}
}
function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node): DefinitionInfo[] {
const result: DefinitionInfo[] = [];
const declarations = symbol.getDeclarations();
@@ -235,7 +232,7 @@ namespace ts.GoToDefinition {
/** Creates a DefinitionInfo from a Declaration, using the declaration's name if possible. */
function createDefinitionInfo(node: Declaration, symbolKind: string, symbolName: string, containerName: string): DefinitionInfo {
return createDefinitionInfoFromName(node.name || node, symbolKind, symbolName, containerName);
return createDefinitionInfoFromName(getNameOfDeclaration(node) || node, symbolKind, symbolName, containerName);
}
/** Creates a DefinitionInfo directly from the name of a declaration. */
+23 -21
View File
@@ -212,6 +212,10 @@ namespace ts.FindAllReferences {
return;
}
if (!decl.importClause) {
return;
}
const { importClause } = decl;
const { namedBindings } = importClause;
@@ -326,7 +330,7 @@ namespace ts.FindAllReferences {
/** Calls `action` for each import, re-export, or require() in a file. */
function forEachImport(sourceFile: SourceFile, action: (importStatement: ImporterOrCallExpression, imported: StringLiteral) => void): void {
if (sourceFile.externalModuleIndicator) {
if (sourceFile.externalModuleIndicator || sourceFile.imports !== undefined) {
for (const moduleSpecifier of sourceFile.imports) {
action(importerFromModuleSpecifier(moduleSpecifier), moduleSpecifier);
}
@@ -354,27 +358,21 @@ namespace ts.FindAllReferences {
}
}
});
if (sourceFile.flags & NodeFlags.JavaScriptFile) {
// Find all 'require()' calls.
sourceFile.forEachChild(function recur(node: Node): void {
if (isRequireCall(node, /*checkArgumentIsStringLiteral*/ true)) {
action(node, node.arguments[0] as StringLiteral);
} else {
node.forEachChild(recur);
}
});
}
}
}
function importerFromModuleSpecifier(moduleSpecifier: StringLiteral): Importer {
function importerFromModuleSpecifier(moduleSpecifier: StringLiteral): ImporterOrCallExpression {
const decl = moduleSpecifier.parent;
if (decl.kind === SyntaxKind.ImportDeclaration || decl.kind === SyntaxKind.ExportDeclaration) {
return decl as ImportDeclaration | ExportDeclaration;
switch (decl.kind) {
case SyntaxKind.CallExpression:
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ExportDeclaration:
return decl as ImportDeclaration | ExportDeclaration | CallExpression;
case SyntaxKind.ExternalModuleReference:
return (decl as ExternalModuleReference).parent;
default:
Debug.assert(false);
}
Debug.assert(decl.kind === SyntaxKind.ExternalModuleReference);
return (decl as ExternalModuleReference).parent;
}
export interface ImportedSymbol {
@@ -528,14 +526,18 @@ namespace ts.FindAllReferences {
return isExternalModuleSymbol(exportingModuleSymbol) ? { exportingModuleSymbol, exportKind } : undefined;
}
function symbolName(symbol: Symbol): string {
function symbolName(symbol: Symbol): string | undefined {
if (symbol.name !== "default") {
return symbol.name;
}
const name = forEach(symbol.declarations, ({ name }) => name && name.kind === SyntaxKind.Identifier && name.text);
Debug.assert(!!name);
return name;
return forEach(symbol.declarations, decl => {
if (isExportAssignment(decl)) {
return isIdentifier(decl.expression) ? decl.expression.text : undefined;
}
const name = getNameOfDeclaration(decl);
return name && name.kind === SyntaxKind.Identifier && name.text;
});
}
/** If at an export specifier, go to the symbol it refers to. */
+1 -2
View File
@@ -28,6 +28,7 @@ namespace ts.JsDoc {
"namespace",
"param",
"private",
"prop",
"property",
"public",
"requires",
@@ -38,8 +39,6 @@ namespace ts.JsDoc {
"throws",
"type",
"typedef",
"property",
"prop",
"version"
];
let jsDocTagNameCompletionEntries: CompletionEntry[];
+22 -17
View File
@@ -52,7 +52,7 @@ namespace ts.NavigateTo {
rawItems = filter(rawItems, item => {
const decl = item.declaration;
if (decl.kind === SyntaxKind.ImportClause || decl.kind === SyntaxKind.ImportSpecifier || decl.kind === SyntaxKind.ImportEqualsDeclaration) {
const importer = checker.getSymbolAtLocation(decl.name);
const importer = checker.getSymbolAtLocation((decl as NamedDeclaration).name);
const imported = checker.getAliasedSymbol(importer);
return importer.name !== imported.name;
}
@@ -97,17 +97,20 @@ namespace ts.NavigateTo {
}
function tryAddSingleDeclarationName(declaration: Declaration, containers: string[]) {
if (declaration && declaration.name) {
const text = getTextOfIdentifierOrLiteral(declaration.name);
if (text !== undefined) {
containers.unshift(text);
}
else if (declaration.name.kind === SyntaxKind.ComputedPropertyName) {
return tryAddComputedPropertyName((<ComputedPropertyName>declaration.name).expression, containers, /*includeLastPortion*/ true);
}
else {
// Don't know how to add this.
return false;
if (declaration) {
const name = getNameOfDeclaration(declaration);
if (name) {
const text = getTextOfIdentifierOrLiteral(name);
if (text !== undefined) {
containers.unshift(text);
}
else if (name.kind === SyntaxKind.ComputedPropertyName) {
return tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ true);
}
else {
// Don't know how to add this.
return false;
}
}
}
@@ -143,8 +146,9 @@ namespace ts.NavigateTo {
// First, if we started with a computed property name, then add all but the last
// portion into the container array.
if (declaration.name.kind === SyntaxKind.ComputedPropertyName) {
if (!tryAddComputedPropertyName((<ComputedPropertyName>declaration.name).expression, containers, /*includeLastPortion*/ false)) {
const name = getNameOfDeclaration(declaration);
if (name.kind === SyntaxKind.ComputedPropertyName) {
if (!tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ false)) {
return undefined;
}
}
@@ -190,6 +194,7 @@ namespace ts.NavigateTo {
function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
const declaration = rawItem.declaration;
const container = <Declaration>getContainerNode(declaration);
const containerName = container && getNameOfDeclaration(container);
return {
name: rawItem.name,
kind: getNodeKind(declaration),
@@ -199,9 +204,9 @@ namespace ts.NavigateTo {
fileName: rawItem.fileName,
textSpan: createTextSpanFromNode(declaration),
// TODO(jfreeman): What should be the containerName when the container has a computed name?
containerName: container && container.name ? (<Identifier>container.name).text : "",
containerKind: container && container.name ? getNodeKind(container) : ""
containerName: containerName ? (<Identifier>containerName).text : "",
containerKind: containerName ? getNodeKind(container) : ""
};
}
}
}
}
+6 -6
View File
@@ -279,8 +279,8 @@ namespace ts.NavigationBar {
function mergeChildren(children: NavigationBarNode[]): void {
const nameToItems = createMap<NavigationBarNode | NavigationBarNode[]>();
filterMutate(children, child => {
const decl = <Declaration>child.node;
const name = decl.name && nodeText(decl.name);
const declName = getNameOfDeclaration(<Declaration>child.node);
const name = declName && nodeText(declName);
if (!name) {
// Anonymous items are never merged.
return true;
@@ -378,9 +378,9 @@ namespace ts.NavigationBar {
return getModuleName(<ModuleDeclaration>node);
}
const decl = <Declaration>node;
if (decl.name) {
return getPropertyNameForPropertyNameNode(decl.name);
const declName = getNameOfDeclaration(<Declaration>node);
if (declName) {
return getPropertyNameForPropertyNameNode(declName);
}
switch (node.kind) {
case SyntaxKind.FunctionExpression:
@@ -399,7 +399,7 @@ namespace ts.NavigationBar {
return getModuleName(<ModuleDeclaration>node);
}
const name = (<Declaration>node).name;
const name = getNameOfDeclaration(<Declaration>node);
if (name) {
const text = nodeText(name);
if (text.length > 0) {
+12 -13
View File
@@ -31,6 +31,9 @@ namespace ts {
/** The version of the language service API */
export const servicesVersion = "0.5";
/* @internal */
let ruleProvider: formatting.RulesProvider;
function createNode<TKind extends SyntaxKind>(kind: TKind, pos: number, end: number, parent?: Node): NodeObject | TokenObject<TKind> | IdentifierObject {
const node = kind >= SyntaxKind.FirstNode ? new NodeObject(kind, pos, end) :
kind === SyntaxKind.Identifier ? new IdentifierObject(SyntaxKind.Identifier, pos, end) :
@@ -357,6 +360,7 @@ namespace ts {
_incrementExpressionBrand: any;
_unaryExpressionBrand: any;
_expressionBrand: any;
/*@internal*/typeArguments: NodeArray<TypeNode>;
constructor(_kind: SyntaxKind.Identifier, pos: number, end: number) {
super(pos, end);
}
@@ -575,14 +579,15 @@ namespace ts {
}
function getDeclarationName(declaration: Declaration) {
if (declaration.name) {
const result = getTextOfIdentifierOrLiteral(declaration.name);
const name = getNameOfDeclaration(declaration);
if (name) {
const result = getTextOfIdentifierOrLiteral(name);
if (result !== undefined) {
return result;
}
if (declaration.name.kind === SyntaxKind.ComputedPropertyName) {
const expr = (<ComputedPropertyName>declaration.name).expression;
if (name.kind === SyntaxKind.ComputedPropertyName) {
const expr = (<ComputedPropertyName>name).expression;
if (expr.kind === SyntaxKind.PropertyAccessExpression) {
return (<PropertyAccessExpression>expr).name.text;
}
@@ -1040,10 +1045,9 @@ namespace ts {
documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory())): LanguageService {
const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
let ruleProvider: formatting.RulesProvider;
ruleProvider = ruleProvider || new formatting.RulesProvider();
let program: Program;
let lastProjectVersion: string;
let lastTypesRootVersion = 0;
const useCaseSensitivefileNames = host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames();
@@ -1072,11 +1076,6 @@ namespace ts {
}
function getRuleProvider(options: FormatCodeSettings) {
// Ensure rules are initialized and up to date wrt to formatting options
if (!ruleProvider) {
ruleProvider = new formatting.RulesProvider();
}
ruleProvider.ensureUpToDate(options);
return ruleProvider;
}
@@ -1855,8 +1854,8 @@ namespace ts {
// OK, we have found a match in the file. This is only an acceptable match if
// it is contained within a comment.
const token = getTokenAtPosition(sourceFile, matchPosition);
if (!isInsideComment(sourceFile, token, matchPosition)) {
if (!isInComment(sourceFile, matchPosition)) {
continue;
}
+6 -2
View File
@@ -1038,7 +1038,11 @@ namespace ts {
return this.forwardJSONCall(`resolveModuleName('${fileName}')`, () => {
const compilerOptions = <CompilerOptions>JSON.parse(compilerOptionsJson);
const result = resolveModuleName(moduleName, normalizeSlashes(fileName), compilerOptions, this.host);
const resolvedFileName = result.resolvedModule ? result.resolvedModule.resolvedFileName : undefined;
let resolvedFileName = result.resolvedModule ? result.resolvedModule.resolvedFileName : undefined;
if (result.resolvedModule && result.resolvedModule.extension !== Extension.Ts && result.resolvedModule.extension !== Extension.Tsx && result.resolvedModule.extension !== Extension.Dts) {
resolvedFileName = undefined;
}
return {
resolvedFileName,
failedLookupLocations: result.failedLookupLocations
@@ -1244,4 +1248,4 @@ namespace TypeScript.Services {
// TODO: it should be moved into a namespace though.
/* @internal */
const toolsVersion = "2.3";
const toolsVersion = "2.4";
+1 -163
View File
@@ -1,168 +1,6 @@
///<reference path='services.ts' />
/* @internal */
namespace ts.SignatureHelp {
// A partially written generic type expression is not guaranteed to have the correct syntax tree. the expression could be parsed as less than/greater than expression or a comma expression
// or some other combination depending on what the user has typed so far. For the purposes of signature help we need to consider any location after "<" as a possible generic type reference.
// To do this, the method will back parse the expression starting at the position required. it will try to parse the current expression as a generic type expression, if it did succeed it
// will return the generic identifier that started the expression (e.g. "foo" in "foo<any, |"). It is then up to the caller to ensure that this is a valid generic expression through
// looking up the type. The method will also keep track of the parameter index inside the expression.
// public static isInPartiallyWrittenTypeArgumentList(syntaxTree: TypeScript.SyntaxTree, position: number): any {
// let token = Syntax.findTokenOnLeft(syntaxTree.sourceUnit(), position, /*includeSkippedTokens*/ true);
// if (token && TypeScript.Syntax.hasAncestorOfKind(token, TypeScript.SyntaxKind.TypeParameterList)) {
// // We are in the wrong generic list. bail out
// return null;
// }
// let stack = 0;
// let argumentIndex = 0;
// whileLoop:
// while (token) {
// switch (token.kind()) {
// case TypeScript.SyntaxKind.LessThanToken:
// if (stack === 0) {
// // Found the beginning of the generic argument expression
// let lessThanToken = token;
// token = previousToken(token, /*includeSkippedTokens*/ true);
// if (!token || token.kind() !== TypeScript.SyntaxKind.IdentifierName) {
// break whileLoop;
// }
// // Found the name, return the data
// return {
// genericIdentifer: token,
// lessThanToken: lessThanToken,
// argumentIndex: argumentIndex
// };
// }
// else if (stack < 0) {
// // Seen one too many less than tokens, bail out
// break whileLoop;
// }
// else {
// stack--;
// }
// break;
// case TypeScript.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
// stack++;
// // Intentional fall through
// case TypeScript.SyntaxKind.GreaterThanToken:
// stack++;
// break;
// case TypeScript.SyntaxKind.CommaToken:
// if (stack === 0) {
// argumentIndex++;
// }
// break;
// case TypeScript.SyntaxKind.CloseBraceToken:
// // This can be object type, skip untill we find the matching open brace token
// let unmatchedOpenBraceTokens = 0;
// // Skip untill the matching open brace token
// token = SignatureInfoHelpers.moveBackUpTillMatchingTokenKind(token, TypeScript.SyntaxKind.CloseBraceToken, TypeScript.SyntaxKind.OpenBraceToken);
// if (!token) {
// // No matching token was found. bail out
// break whileLoop;
// }
// break;
// case TypeScript.SyntaxKind.EqualsGreaterThanToken:
// // This can be a function type or a constructor type. In either case, we want to skip the function definition
// token = previousToken(token, /*includeSkippedTokens*/ true);
// if (token && token.kind() === TypeScript.SyntaxKind.CloseParenToken) {
// // Skip untill the matching open paren token
// token = SignatureInfoHelpers.moveBackUpTillMatchingTokenKind(token, TypeScript.SyntaxKind.CloseParenToken, TypeScript.SyntaxKind.OpenParenToken);
// if (token && token.kind() === TypeScript.SyntaxKind.GreaterThanToken) {
// // Another generic type argument list, skip it\
// token = SignatureInfoHelpers.moveBackUpTillMatchingTokenKind(token, TypeScript.SyntaxKind.GreaterThanToken, TypeScript.SyntaxKind.LessThanToken);
// }
// if (token && token.kind() === TypeScript.SyntaxKind.NewKeyword) {
// // In case this was a constructor type, skip the new keyword
// token = previousToken(token, /*includeSkippedTokens*/ true);
// }
// if (!token) {
// // No matching token was found. bail out
// break whileLoop;
// }
// }
// else {
// // This is not a function type. exit the main loop
// break whileLoop;
// }
// break;
// case TypeScript.SyntaxKind.IdentifierName:
// case TypeScript.SyntaxKind.AnyKeyword:
// case TypeScript.SyntaxKind.NumberKeyword:
// case TypeScript.SyntaxKind.StringKeyword:
// case TypeScript.SyntaxKind.VoidKeyword:
// case TypeScript.SyntaxKind.BooleanKeyword:
// case TypeScript.SyntaxKind.DotToken:
// case TypeScript.SyntaxKind.OpenBracketToken:
// case TypeScript.SyntaxKind.CloseBracketToken:
// // Valid tokens in a type name. Skip.
// break;
// default:
// break whileLoop;
// }
// token = previousToken(token, /*includeSkippedTokens*/ true);
// }
// return null;
// }
// private static moveBackUpTillMatchingTokenKind(token: TypeScript.ISyntaxToken, tokenKind: TypeScript.SyntaxKind, matchingTokenKind: TypeScript.SyntaxKind): TypeScript.ISyntaxToken {
// if (!token || token.kind() !== tokenKind) {
// throw TypeScript.Errors.invalidOperation();
// }
// // Skip the current token
// token = previousToken(token, /*includeSkippedTokens*/ true);
// let stack = 0;
// while (token) {
// if (token.kind() === matchingTokenKind) {
// if (stack === 0) {
// // Found the matching token, return
// return token;
// }
// else if (stack < 0) {
// // tokens overlapped.. bail out.
// break;
// }
// else {
// stack--;
// }
// }
// else if (token.kind() === tokenKind) {
// stack++;
// }
// // Move back
// token = previousToken(token, /*includeSkippedTokens*/ true);
// }
// // Did not find matching token
// return null;
// }
const emptyArray: any[] = [];
export const enum ArgumentListKind {
@@ -262,7 +100,7 @@ namespace ts.SignatureHelp {
* in the argument of an invocation; returns undefined otherwise.
*/
export function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo {
if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) {
if (isCallOrNewExpression(node.parent)) {
const callExpression = <CallExpression>node.parent;
// There are 3 cases to handle:
// 1. The token introduces a list, and should begin a signature help session
+30 -24
View File
@@ -120,7 +120,7 @@ namespace ts.SymbolDisplay {
// try get the call/construct signature from the type if it matches
let callExpressionLike: CallExpression | NewExpression | JsxOpeningLikeElement;
if (location.kind === SyntaxKind.CallExpression || location.kind === SyntaxKind.NewExpression) {
if (isCallOrNewExpression(location)) {
callExpressionLike = <CallExpression | NewExpression>location;
}
else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) {
@@ -200,27 +200,33 @@ namespace ts.SymbolDisplay {
(location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration
// get the signature from the declaration and write it
const functionDeclaration = <FunctionLikeDeclaration>location.parent;
const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures();
if (!typeChecker.isImplementationOfOverload(functionDeclaration)) {
signature = typeChecker.getSignatureFromDeclaration(functionDeclaration);
}
else {
signature = allSignatures[0];
}
// Use function declaration to write the signatures only if the symbol corresponding to this declaration
const locationIsSymbolDeclaration = findDeclaration(symbol, declaration =>
declaration === (location.kind === SyntaxKind.ConstructorKeyword ? functionDeclaration.parent : functionDeclaration));
if (functionDeclaration.kind === SyntaxKind.Constructor) {
// show (constructor) Type(...) signature
symbolKind = ScriptElementKind.constructorImplementationElement;
addPrefixForAnyFunctionOrVar(type.symbol, symbolKind);
}
else {
// (function/method) symbol(..signature)
addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature &&
!(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind);
}
if (locationIsSymbolDeclaration) {
const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures();
if (!typeChecker.isImplementationOfOverload(functionDeclaration)) {
signature = typeChecker.getSignatureFromDeclaration(functionDeclaration);
}
else {
signature = allSignatures[0];
}
addSignatureDisplayParts(signature, allSignatures);
hasAddedSymbolInfo = true;
if (functionDeclaration.kind === SyntaxKind.Constructor) {
// show (constructor) Type(...) signature
symbolKind = ScriptElementKind.constructorImplementationElement;
addPrefixForAnyFunctionOrVar(type.symbol, symbolKind);
}
else {
// (function/method) symbol(..signature)
addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature &&
!(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind);
}
addSignatureDisplayParts(signature, allSignatures);
hasAddedSymbolInfo = true;
}
}
}
}
@@ -269,7 +275,7 @@ namespace ts.SymbolDisplay {
}
if (symbolFlags & SymbolFlags.Module) {
addNewLineIfDisplayPartsExist();
const declaration = <ModuleDeclaration>getDeclarationOfKind(symbol, SyntaxKind.ModuleDeclaration);
const declaration = getDeclarationOfKind<ModuleDeclaration>(symbol, SyntaxKind.ModuleDeclaration);
const isNamespace = declaration && declaration.name && declaration.name.kind === SyntaxKind.Identifier;
displayParts.push(keywordPart(isNamespace ? SyntaxKind.NamespaceKeyword : SyntaxKind.ModuleKeyword));
displayParts.push(spacePart());
@@ -290,9 +296,9 @@ namespace ts.SymbolDisplay {
}
else {
// Method/function type parameter
let declaration = <Node>getDeclarationOfKind(symbol, SyntaxKind.TypeParameter);
Debug.assert(declaration !== undefined);
declaration = declaration.parent;
const decl = getDeclarationOfKind(symbol, SyntaxKind.TypeParameter);
Debug.assert(decl !== undefined);
const declaration = decl.parent;
if (declaration) {
if (isFunctionLikeKind(declaration.kind)) {
+2 -22
View File
@@ -497,8 +497,8 @@ namespace ts.textChanges {
readonly node: Node;
}
export function getNonformattedText(node: Node, sourceFile: SourceFile, newLine: NewLineKind): NonFormattedText {
const options = { newLine, target: sourceFile.languageVersion };
export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLine: NewLineKind): NonFormattedText {
const options = { newLine, target: sourceFile && sourceFile.languageVersion };
const writer = new Writer(getNewLineCharacter(options));
const printer = createPrinter(options, writer);
printer.writeNode(EmitHint.Unspecified, node, sourceFile, writer);
@@ -528,26 +528,6 @@ namespace ts.textChanges {
return skipTrivia(s, 0) === s.length;
}
const nullTransformationContext: TransformationContext = {
enableEmitNotification: noop,
enableSubstitution: noop,
endLexicalEnvironment: () => undefined,
getCompilerOptions: notImplemented,
getEmitHost: notImplemented,
getEmitResolver: notImplemented,
hoistFunctionDeclaration: noop,
hoistVariableDeclaration: noop,
isEmitNotificationEnabled: notImplemented,
isSubstitutionEnabled: notImplemented,
onEmitNode: noop,
onSubstituteNode: notImplemented,
readEmitHelpers: notImplemented,
requestEmitHelper: noop,
resumeLexicalEnvironment: noop,
startLexicalEnvironment: noop,
suspendLexicalEnvironment: noop
};
function assignPositionsToNode(node: Node): Node {
const visited = visitEachChild(node, assignPositionsToNode, nullTransformationContext, assignPositionsToNodeArray, assignPositionsToNode);
// create proxy node for non synthesized nodes
+2 -1
View File
@@ -5,6 +5,7 @@ namespace ts {
reportDiagnostics?: boolean;
moduleName?: string;
renamedDependencies?: MapLike<string>;
transformers?: CustomTransformers;
}
export interface TranspileOutput {
@@ -103,7 +104,7 @@ namespace ts {
addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics());
}
// Emit
program.emit();
program.emit(/*targetSourceFile*/ undefined, /*writeFile*/ undefined, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ undefined, transpileOptions.transformers);
Debug.assert(outputText !== undefined, "Output generation failed");
+2 -1
View File
@@ -82,6 +82,7 @@
"formatting/tokenRange.ts",
"codeFixProvider.ts",
"codefixes/fixAddMissingMember.ts",
"codefixes/fixSpelling.ts",
"codefixes/fixExtendsInterfaceBecomesImplements.ts",
"codefixes/fixClassIncorrectlyImplementsInterface.ts",
"codefixes/fixClassDoesntImplementInheritedAbstractMember.ts",
@@ -94,4 +95,4 @@
"codefixes/unusedIdentifierFixes.ts",
"codefixes/disableJsDiagnostics.ts"
]
}
}
+52 -94
View File
@@ -21,7 +21,6 @@ namespace ts {
case SyntaxKind.PropertySignature:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
case SyntaxKind.EnumMember:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
@@ -40,10 +39,13 @@ namespace ts {
case SyntaxKind.TypeLiteral:
return SemanticMeaning.Type;
case SyntaxKind.EnumMember:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.EnumDeclaration:
return SemanticMeaning.Value | SemanticMeaning.Type;
case SyntaxKind.EnumDeclaration:
return SemanticMeaning.All;
case SyntaxKind.ModuleDeclaration:
if (isAmbientModule(<ModuleDeclaration>node)) {
return SemanticMeaning.Namespace | SemanticMeaning.Value;
@@ -61,7 +63,7 @@ namespace ts {
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ExportAssignment:
case SyntaxKind.ExportDeclaration:
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
return SemanticMeaning.All;
// An external module can be a Value
case SyntaxKind.SourceFile:
@@ -238,7 +240,7 @@ namespace ts {
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.ModuleDeclaration:
return (<Declaration>node.parent).name === node;
return getNameOfDeclaration(<Declaration>node.parent) === node;
case SyntaxKind.ElementAccessExpression:
return (<ElementAccessExpression>node.parent).argumentExpression === node;
case SyntaxKind.ComputedPropertyName:
@@ -254,37 +256,6 @@ namespace ts {
getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node;
}
/** Returns true if the position is within a comment */
export function isInsideComment(sourceFile: SourceFile, token: Node, position: number): boolean {
// The position has to be: 1. in the leading trivia (before token.getStart()), and 2. within a comment
return position <= token.getStart(sourceFile) &&
(isInsideCommentRange(getTrailingCommentRanges(sourceFile.text, token.getFullStart())) ||
isInsideCommentRange(getLeadingCommentRanges(sourceFile.text, token.getFullStart())));
function isInsideCommentRange(comments: CommentRange[]): boolean {
return forEach(comments, comment => {
// either we are 1. completely inside the comment, or 2. at the end of the comment
if (comment.pos < position && position < comment.end) {
return true;
}
else if (position === comment.end) {
const text = sourceFile.text;
const width = comment.end - comment.pos;
// is single line comment or just /*
if (width <= 2 || text.charCodeAt(comment.pos + 1) === CharacterCodes.slash) {
return true;
}
else {
// is unterminated multi-line comment
return !(text.charCodeAt(comment.end - 1) === CharacterCodes.slash &&
text.charCodeAt(comment.end - 2) === CharacterCodes.asterisk);
}
}
return false;
});
}
}
export function getContainerNode(node: Node): Declaration {
while (true) {
node = node.parent;
@@ -649,44 +620,26 @@ namespace ts {
return current;
}
if (includeJsDocComment) {
const jsDocChildren = ts.filter(current.getChildren(), isJSDocNode);
for (const jsDocChild of jsDocChildren) {
const start = allowPositionInLeadingTrivia ? jsDocChild.getFullStart() : jsDocChild.getStart(sourceFile, includeJsDocComment);
if (start <= position) {
const end = jsDocChild.getEnd();
if (position < end || (position === end && jsDocChild.kind === SyntaxKind.EndOfFileToken)) {
current = jsDocChild;
continue outer;
}
else if (includeItemAtEndPosition && end === position) {
const previousToken = findPrecedingToken(position, sourceFile, jsDocChild);
if (previousToken && includeItemAtEndPosition(previousToken)) {
return previousToken;
}
}
}
}
}
// find the child that contains 'position'
for (const child of current.getChildren()) {
// all jsDocComment nodes were already visited
if (isJSDocNode(child)) {
if (isJSDocNode(child) && !includeJsDocComment) {
continue;
}
const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, includeJsDocComment);
if (start <= position) {
const end = child.getEnd();
if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) {
current = child;
continue outer;
}
else if (includeItemAtEndPosition && end === position) {
const previousToken = findPrecedingToken(position, sourceFile, child);
if (previousToken && includeItemAtEndPosition(previousToken)) {
return previousToken;
}
if (start > position) {
continue;
}
const end = child.getEnd();
if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) {
current = child;
continue outer;
}
else if (includeItemAtEndPosition && end === position) {
const previousToken = findPrecedingToken(position, sourceFile, child);
if (previousToken && includeItemAtEndPosition(previousToken)) {
return previousToken;
}
}
}
@@ -832,10 +785,6 @@ namespace ts {
return false;
}
export function isInComment(sourceFile: SourceFile, position: number) {
return isInCommentHelper(sourceFile, position, /*predicate*/ undefined);
}
/**
* returns true if the position is in between the open and close elements of an JSX expression.
*/
@@ -881,15 +830,26 @@ namespace ts {
}
/**
* Returns true if the cursor at position in sourceFile is within a comment that additionally
* satisfies predicate, and false otherwise.
* Returns true if the cursor at position in sourceFile is within a comment.
*
* @param tokenAtPosition Must equal `getTokenAtPosition(sourceFile, position)
* @param predicate Additional predicate to test on the comment range.
*/
export function isInCommentHelper(sourceFile: SourceFile, position: number, predicate?: (c: CommentRange) => boolean): boolean {
const token = getTokenAtPosition(sourceFile, position);
export function isInComment(sourceFile: SourceFile, position: number, tokenAtPosition = getTokenAtPosition(sourceFile, position), predicate?: (c: CommentRange) => boolean): boolean {
return position <= tokenAtPosition.getStart(sourceFile) &&
(isInCommentRange(getLeadingCommentRanges(sourceFile.text, tokenAtPosition.pos)) ||
isInCommentRange(getTrailingCommentRanges(sourceFile.text, tokenAtPosition.pos)));
if (token && position <= token.getStart(sourceFile)) {
const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
function isInCommentRange(commentRanges: CommentRange[]): boolean {
return forEach(commentRanges, c => isPositionInCommentRange(c, position, sourceFile.text) && (!predicate || predicate(c)));
}
}
function isPositionInCommentRange({ pos, end, kind }: ts.CommentRange, position: number, text: string): boolean {
if (pos < position && position < end) {
return true;
}
else if (position === end) {
// The end marker of a single-line comment does not include the newline character.
// In the following case, we are inside a comment (^ denotes the cursor position):
//
@@ -900,15 +860,13 @@ namespace ts {
// /* asdf */^
//
// Internally, we represent the end of the comment at the newline and closing '/', respectively.
return predicate ?
forEach(commentRanges, c => c.pos < position &&
(c.kind === SyntaxKind.SingleLineCommentTrivia ? position <= c.end : position < c.end) &&
predicate(c)) :
forEach(commentRanges, c => c.pos < position &&
(c.kind === SyntaxKind.SingleLineCommentTrivia ? position <= c.end : position < c.end));
return kind === SyntaxKind.SingleLineCommentTrivia ||
// true for unterminated multi-line comment
!(text.charCodeAt(end - 1) === CharacterCodes.slash && text.charCodeAt(end - 2) === CharacterCodes.asterisk);
}
else {
return false;
}
return false;
}
export function hasDocComment(sourceFile: SourceFile, position: number) {
@@ -1036,6 +994,10 @@ namespace ts {
}
export function compareDataObjects(dst: any, src: any): boolean {
if (!dst || !src || Object.keys(dst).length !== Object.keys(src).length) {
return false;
}
for (const e in dst) {
if (typeof dst[e] === "object") {
if (!compareDataObjects(dst[e], src[e])) {
@@ -1087,21 +1049,17 @@ namespace ts {
}
export function isInReferenceComment(sourceFile: SourceFile, position: number): boolean {
return isInCommentHelper(sourceFile, position, isReferenceComment);
function isReferenceComment(c: CommentRange): boolean {
return isInComment(sourceFile, position, /*tokenAtPosition*/ undefined, c => {
const commentText = sourceFile.text.substring(c.pos, c.end);
return tripleSlashDirectivePrefixRegex.test(commentText);
}
});
}
export function isInNonReferenceComment(sourceFile: SourceFile, position: number): boolean {
return isInCommentHelper(sourceFile, position, isNonReferenceComment);
function isNonReferenceComment(c: CommentRange): boolean {
return isInComment(sourceFile, position, /*tokenAtPosition*/ undefined, c => {
const commentText = sourceFile.text.substring(c.pos, c.end);
return !tripleSlashDirectivePrefixRegex.test(commentText);
}
});
}
export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile): TextSpan {