mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into ownJsonParsing
This commit is contained in:
+11
-9
@@ -221,7 +221,7 @@ 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,
|
||||
@@ -389,9 +389,9 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1414,6 +1414,7 @@ namespace ts {
|
||||
if (isObjectLiteralOrClassExpressionMethod(node)) {
|
||||
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsObjectLiteralOrClassExpressionMethod;
|
||||
}
|
||||
// falls through
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
@@ -1715,7 +1716,7 @@ namespace ts {
|
||||
declareModuleMember(node, symbolFlags, symbolExcludes);
|
||||
break;
|
||||
}
|
||||
// fall through.
|
||||
// falls through
|
||||
default:
|
||||
if (!blockScopeContainer.locals) {
|
||||
blockScopeContainer.locals = createMap<Symbol>();
|
||||
@@ -2009,6 +2010,7 @@ namespace ts {
|
||||
bindBlockScopedDeclaration(<Declaration>parentNode, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
|
||||
break;
|
||||
}
|
||||
// falls through
|
||||
case SyntaxKind.ThisKeyword:
|
||||
if (currentFlow && (isExpression(node) || parent.kind === SyntaxKind.ShorthandPropertyAssignment)) {
|
||||
node.flowNode = currentFlow;
|
||||
@@ -2184,7 +2186,7 @@ namespace ts {
|
||||
if (!isFunctionLike(node.parent)) {
|
||||
return;
|
||||
}
|
||||
// Fall through
|
||||
// falls through
|
||||
case SyntaxKind.ModuleBlock:
|
||||
return updateStrictModeStatementList((<Block | ModuleBlock>node).statements);
|
||||
}
|
||||
@@ -2209,7 +2211,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function bindSourceFileAsExternalModule() {
|
||||
bindAnonymousDeclaration(file, SymbolFlags.ValueModule, `"${removeFileExtension(file.fileName) }"`);
|
||||
bindAnonymousDeclaration(file, SymbolFlags.ValueModule, `"${removeFileExtension(file.fileName)}"`);
|
||||
}
|
||||
|
||||
function bindExportAssignment(node: ExportAssignment | BinaryExpression) {
|
||||
@@ -2401,7 +2403,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function lookupSymbolForName(name: string) {
|
||||
return (container.symbol && container.symbol.exports && container.symbol.exports.get(name)) || container.locals.get(name);
|
||||
return (container.symbol && container.symbol.exports && container.symbol.exports.get(name)) || (container.locals && container.locals.get(name));
|
||||
}
|
||||
|
||||
function bindPropertyAssignment(functionName: string, propertyAccessExpression: PropertyAccessExpression, isPrototypeProperty: boolean) {
|
||||
@@ -2502,7 +2504,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);
|
||||
|
||||
+760
-467
File diff suppressed because it is too large
Load Diff
+260
-179
@@ -1328,221 +1328,82 @@ namespace ts {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the contents of a config file (tsconfig.json).
|
||||
* Parse the contents of a config file from json or json source file (tsconfig.json).
|
||||
* @param json The contents of the config file to parse
|
||||
* @param sourceFile sourceFile corresponding to the Json
|
||||
* @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.
|
||||
*/
|
||||
function parseJsonConfigFileContentWorker(json: any, sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string, resolutionStack: Path[] = [], extraFileExtensions: JsFileExtensionInfo[] = []): ParsedCommandLine {
|
||||
function parseJsonConfigFileContentWorker(
|
||||
json: any,
|
||||
sourceFile: JsonSourceFile,
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
existingOptions: CompilerOptions = {},
|
||||
configFileName?: string,
|
||||
resolutionStack: Path[] = [],
|
||||
extraFileExtensions: JsFileExtensionInfo[] = [],
|
||||
): ParsedCommandLine {
|
||||
Debug.assert((json === undefined && sourceFile !== undefined) || (json !== undefined && sourceFile === undefined));
|
||||
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 || convertToObject(sourceFile, errors),
|
||||
errors: errors.concat(createCompilerDiagnostic(Diagnostics.Circularity_detected_while_resolving_configuration_Colon_0, [...resolutionStack, resolvedPath].join(" -> "))),
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
}
|
||||
|
||||
let options: CompilerOptions;
|
||||
let typeAcquisition: TypeAcquisition;
|
||||
let compileOnSave: boolean;
|
||||
let hasExtendsError: boolean, extendedConfigPath: Path;
|
||||
if (json) {
|
||||
options = convertCompilerOptionsFromJsonWorker(json["compilerOptions"], basePath, errors, 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"];
|
||||
typeAcquisition = convertTypeAcquisitionFromJsonWorker(jsonOptions, basePath, errors, configFileName);
|
||||
compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors);
|
||||
}
|
||||
else {
|
||||
options = getDefaultCompilerOptions(configFileName);
|
||||
let typingOptionstypeAcquisition: TypeAcquisition;
|
||||
const optionsIterator: JsonConversionNotifier = {
|
||||
onSetOptionKeyValue(optionsObject: string, option: CommandLineOption, value: CompilerOptionsValue) {
|
||||
Debug.assert(optionsObject === "compilerOptions" || optionsObject === "typeAcquisition" || optionsObject === "typingOptions");
|
||||
const currentOption = optionsObject === "compilerOptions" ? options :
|
||||
optionsObject === "typeAcquisition" ? (typeAcquisition || (typeAcquisition = getDefaultTypeAcquisition(configFileName)) ) :
|
||||
(typingOptionstypeAcquisition || (typingOptionstypeAcquisition = getDefaultTypeAcquisition(configFileName)));
|
||||
|
||||
currentOption[option.name] = normalizeOptionValue(option, basePath, value);
|
||||
},
|
||||
onRootKeyValue(key: string, propertyName: PropertyName, value: CompilerOptionsValue, node: Expression) {
|
||||
switch (key) {
|
||||
case "extends":
|
||||
const extendsDiagnostic = getExtendsConfigPath(<string>value, (message, arg0) =>
|
||||
createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0));
|
||||
if ((<Diagnostic>extendsDiagnostic).messageText) {
|
||||
errors.push(<Diagnostic>extendsDiagnostic);
|
||||
hasExtendsError = true;
|
||||
}
|
||||
else {
|
||||
extendedConfigPath = <Path>extendsDiagnostic;
|
||||
}
|
||||
return;
|
||||
case "excludes":
|
||||
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, propertyName, Diagnostics.Unknown_option_excludes_Did_you_mean_exclude));
|
||||
return;
|
||||
case "files":
|
||||
if ((<string[]>value).length === 0) {
|
||||
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, node, Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json"));
|
||||
}
|
||||
return;
|
||||
case "compileOnSave":
|
||||
compileOnSave = <boolean>value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
json = convertToObjectWorker(sourceFile, errors, getTsconfigRootOptionsMap(), optionsIterator);
|
||||
if (!typeAcquisition) {
|
||||
if (typingOptionstypeAcquisition) {
|
||||
typeAcquisition = (typingOptionstypeAcquisition.enableAutoDiscovery !== undefined) ?
|
||||
{
|
||||
enable: typingOptionstypeAcquisition.enableAutoDiscovery,
|
||||
include: typingOptionstypeAcquisition.include,
|
||||
exclude: typingOptionstypeAcquisition.exclude
|
||||
} :
|
||||
typingOptionstypeAcquisition;
|
||||
}
|
||||
else {
|
||||
typeAcquisition = getDefaultTypeAcquisition(configFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (json["extends"]) {
|
||||
let [include, exclude, files, baseOptions]: [string[], string[], string[], CompilerOptions] = [undefined, undefined, undefined, {}];
|
||||
if (!hasExtendsError && typeof json["extends"] === "string") {
|
||||
[include, exclude, files, baseOptions] = (tryExtendsName(json["extends"], extendedConfigPath) || [include, exclude, files, baseOptions]);
|
||||
}
|
||||
else {
|
||||
createCompilerDiagnosticForJson(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);
|
||||
const parsedConfig = parseConfig(json, sourceFile, host, basePath, configFileName, resolutionStack, errors);
|
||||
const { raw } = parsedConfig;
|
||||
const options = extend(existingOptions, parsedConfig.options || {});
|
||||
options.configFilePath = configFileName;
|
||||
options.configFile = sourceFile;
|
||||
|
||||
const { fileNames, wildcardDirectories } = getFileNames(errors);
|
||||
|
||||
const { fileNames, wildcardDirectories } = getFileNames();
|
||||
return {
|
||||
options,
|
||||
fileNames,
|
||||
typeAcquisition,
|
||||
raw: json,
|
||||
typeAcquisition: parsedConfig.typeAcquisition || getDefaultTypeAcquisition(),
|
||||
raw,
|
||||
errors,
|
||||
wildcardDirectories,
|
||||
compileOnSave
|
||||
compileOnSave: !!raw.compileOnSave
|
||||
};
|
||||
|
||||
function getExtendsConfigPath<T>(extendedConfig: string, createDiagnostic: (message: DiagnosticMessage, arg1?: string) => T): T | Path {
|
||||
// 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), "../"))) {
|
||||
return createDiagnostic(Diagnostics.A_path_in_an_extends_option_must_be_relative_or_rooted_but_0_is_not, extendedConfig);
|
||||
}
|
||||
let extendedConfigPath = toPath(extendedConfig, basePath, getCanonicalFileName);
|
||||
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, ".json")) {
|
||||
extendedConfigPath = `${extendedConfigPath}.json` as Path;
|
||||
if (!host.fileExists(extendedConfigPath)) {
|
||||
return createDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig);
|
||||
}
|
||||
}
|
||||
return extendedConfigPath;
|
||||
}
|
||||
|
||||
function tryExtendsName(extendedConfig: string, extendedConfigPath?: Path): [string[], string[], string[], CompilerOptions] {
|
||||
if (!extendedConfigPath) {
|
||||
const result = getExtendsConfigPath(extendedConfig, (message, arg1) => (createCompilerDiagnosticForJson(message, arg1), true));
|
||||
if (result === true) {
|
||||
return;
|
||||
}
|
||||
extendedConfigPath = <Path>result;
|
||||
}
|
||||
|
||||
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, ".json")) {
|
||||
extendedConfigPath = `${extendedConfigPath}.json` as Path;
|
||||
}
|
||||
|
||||
const extendedResult = readJsonConfigFile(extendedConfigPath, path => host.readFile(path));
|
||||
if (extendedResult.parseDiagnostics.length) {
|
||||
errors.push(...extendedResult.parseDiagnostics);
|
||||
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 = parseJsonSourceFileConfigFileContent(extendedResult, 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] && result.raw[key]) {
|
||||
return map(result.raw[key], updatePath);
|
||||
}
|
||||
});
|
||||
return [include, exclude, files, result.options];
|
||||
}
|
||||
|
||||
function getFileNames(errors: Diagnostic[]): ExpandResult {
|
||||
function getFileNames(): ExpandResult {
|
||||
let fileNames: string[];
|
||||
if (hasProperty(json, "files")) {
|
||||
if (isArray(json["files"])) {
|
||||
fileNames = <string[]>json["files"];
|
||||
if (hasProperty(raw, "files")) {
|
||||
if (isArray(raw["files"])) {
|
||||
fileNames = <string[]>raw["files"];
|
||||
if (fileNames.length === 0) {
|
||||
createCompilerDiagnosticForJson(Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json");
|
||||
createCompilerDiagnosticOnlyIfJson(Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json");
|
||||
}
|
||||
}
|
||||
else {
|
||||
createCompilerDiagnosticForJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "files", "Array");
|
||||
createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "files", "Array");
|
||||
}
|
||||
}
|
||||
|
||||
let includeSpecs: string[];
|
||||
if (hasProperty(json, "include")) {
|
||||
if (isArray(json["include"])) {
|
||||
includeSpecs = <string[]>json["include"];
|
||||
if (hasProperty(raw, "include")) {
|
||||
if (isArray(raw["include"])) {
|
||||
includeSpecs = <string[]>raw["include"];
|
||||
}
|
||||
else {
|
||||
createCompilerDiagnosticForJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "include", "Array");
|
||||
createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "include", "Array");
|
||||
}
|
||||
}
|
||||
|
||||
let excludeSpecs: string[];
|
||||
if (hasProperty(json, "exclude")) {
|
||||
if (isArray(json["exclude"])) {
|
||||
excludeSpecs = <string[]>json["exclude"];
|
||||
if (hasProperty(raw, "exclude")) {
|
||||
if (isArray(raw["exclude"])) {
|
||||
excludeSpecs = <string[]>raw["exclude"];
|
||||
}
|
||||
else {
|
||||
createCompilerDiagnosticForJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "exclude", "Array");
|
||||
createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "exclude", "Array");
|
||||
}
|
||||
}
|
||||
else if (hasProperty(json, "excludes")) {
|
||||
createCompilerDiagnosticForJson(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"];
|
||||
|
||||
const outDir = json["compilerOptions"] && json["compilerOptions"]["outDir"];
|
||||
const outDir = raw["compilerOptions"] && raw["compilerOptions"]["outDir"];
|
||||
if (outDir) {
|
||||
excludeSpecs.push(outDir);
|
||||
}
|
||||
@@ -1554,7 +1415,7 @@ namespace ts {
|
||||
|
||||
const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors, extraFileExtensions, sourceFile);
|
||||
|
||||
if (result.fileNames.length === 0 && !hasProperty(json, "files") && resolutionStack.length === 0) {
|
||||
if (result.fileNames.length === 0 && !hasProperty(raw, "files") && resolutionStack.length === 0) {
|
||||
errors.push(
|
||||
createCompilerDiagnostic(
|
||||
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
@@ -1566,16 +1427,236 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
function createCompilerDiagnosticForJson(message: DiagnosticMessage, arg0?: string, arg1?: string) {
|
||||
function createCompilerDiagnosticOnlyIfJson(message: DiagnosticMessage, arg0?: string, arg1?: string) {
|
||||
if (!sourceFile) {
|
||||
errors.push(createCompilerDiagnostic(message, arg0, arg1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function convertCompileOnSaveOptionFromJson(jsonOption: any, basePath: string, errors: Diagnostic[]): boolean {
|
||||
interface ParsedTsconfig {
|
||||
raw: any;
|
||||
options?: CompilerOptions;
|
||||
typeAcquisition?: TypeAcquisition;
|
||||
extendedConfigPath?: Path;
|
||||
}
|
||||
|
||||
function isSuccessfulParsedTsconfig(value: ParsedTsconfig) {
|
||||
return !!value.options;
|
||||
}
|
||||
|
||||
/**
|
||||
* This *just* extracts options/include/exclude/files out of a config file.
|
||||
* It does *not* resolve the included files.
|
||||
*/
|
||||
function parseConfig(
|
||||
json: any,
|
||||
sourceFile: JsonSourceFile,
|
||||
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 { raw: json || convertToObject(sourceFile, errors) };
|
||||
}
|
||||
|
||||
const ownConfig = json ?
|
||||
parseOwnConfigOfJson(json, host, basePath, getCanonicalFileName, configFileName, errors) :
|
||||
parseOwnConfigOfJsonSourceFile(sourceFile, host, basePath, getCanonicalFileName, configFileName, errors);
|
||||
|
||||
if (ownConfig.extendedConfigPath) {
|
||||
// copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios.
|
||||
resolutionStack = resolutionStack.concat([resolvedPath]);
|
||||
const extendedConfig = getExtendedConfig(ownConfig.extendedConfigPath, host, basePath, getCanonicalFileName,
|
||||
resolutionStack, errors);
|
||||
if (extendedConfig && isSuccessfulParsedTsconfig(extendedConfig)) {
|
||||
const baseRaw = extendedConfig.raw;
|
||||
const raw = ownConfig.raw;
|
||||
const setPropertyInRawIfNotUndefined = (propertyName: string) => {
|
||||
const value = raw[propertyName] || baseRaw[propertyName];
|
||||
if (value) {
|
||||
raw[propertyName] = value;
|
||||
}
|
||||
};
|
||||
setPropertyInRawIfNotUndefined("include");
|
||||
setPropertyInRawIfNotUndefined("exclude");
|
||||
setPropertyInRawIfNotUndefined("files");
|
||||
if (raw.compileOnSave === undefined) {
|
||||
raw.compileOnSave = baseRaw.compileOnSave;
|
||||
}
|
||||
ownConfig.options = assign({}, extendedConfig.options, ownConfig.options);
|
||||
// TODO extend type typeAcquisition
|
||||
}
|
||||
}
|
||||
|
||||
return ownConfig;
|
||||
}
|
||||
|
||||
function parseOwnConfigOfJson(
|
||||
json: any,
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
getCanonicalFileName: (fileName: string) => string,
|
||||
configFileName: string,
|
||||
errors: Diagnostic[]
|
||||
): ParsedTsconfig {
|
||||
if (hasProperty(json, "excludes")) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_option_excludes_Did_you_mean_exclude));
|
||||
}
|
||||
|
||||
const options = convertCompilerOptionsFromJsonWorker(json.compilerOptions, basePath, errors, configFileName);
|
||||
// typingOptions has been deprecated and is only supported for backward compatibility purposes.
|
||||
// It should be removed in future releases - use typeAcquisition instead.
|
||||
const typeAcquisition = convertTypeAcquisitionFromJsonWorker(json["typeAcquisition"] || json["typingOptions"], basePath, errors, configFileName);
|
||||
json.compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors);
|
||||
let extendedConfigPath: Path;
|
||||
|
||||
if (json.extends) {
|
||||
if (typeof json.extends !== "string") {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", "string"));
|
||||
}
|
||||
else {
|
||||
extendedConfigPath = getExtendsConfigPath(json.extends, host, basePath, getCanonicalFileName, errors, createCompilerDiagnostic);
|
||||
}
|
||||
}
|
||||
return { raw: json, options, typeAcquisition, extendedConfigPath };
|
||||
}
|
||||
|
||||
function parseOwnConfigOfJsonSourceFile(
|
||||
sourceFile: JsonSourceFile,
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
getCanonicalFileName: (fileName: string) => string,
|
||||
configFileName: string,
|
||||
errors: Diagnostic[]
|
||||
): ParsedTsconfig {
|
||||
const options = getDefaultCompilerOptions(configFileName);
|
||||
let typeAcquisition: TypeAcquisition, typingOptionstypeAcquisition: TypeAcquisition;
|
||||
let extendedConfigPath: Path;
|
||||
|
||||
const optionsIterator: JsonConversionNotifier = {
|
||||
onSetOptionKeyValue(optionsObject: string, option: CommandLineOption, value: CompilerOptionsValue) {
|
||||
Debug.assert(optionsObject === "compilerOptions" || optionsObject === "typeAcquisition" || optionsObject === "typingOptions");
|
||||
const currentOption = optionsObject === "compilerOptions" ? options :
|
||||
optionsObject === "typeAcquisition" ? (typeAcquisition || (typeAcquisition = getDefaultTypeAcquisition(configFileName))) :
|
||||
(typingOptionstypeAcquisition || (typingOptionstypeAcquisition = getDefaultTypeAcquisition(configFileName)));
|
||||
|
||||
currentOption[option.name] = normalizeOptionValue(option, basePath, value);
|
||||
},
|
||||
onRootKeyValue(key: string, propertyName: PropertyName, value: CompilerOptionsValue, node: Expression) {
|
||||
switch (key) {
|
||||
case "extends":
|
||||
extendedConfigPath = getExtendsConfigPath(
|
||||
<string>value,
|
||||
host,
|
||||
basePath,
|
||||
getCanonicalFileName,
|
||||
errors,
|
||||
(message, arg0) =>
|
||||
createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0)
|
||||
);
|
||||
return;
|
||||
case "excludes":
|
||||
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, propertyName, Diagnostics.Unknown_option_excludes_Did_you_mean_exclude));
|
||||
return;
|
||||
case "files":
|
||||
if ((<string[]>value).length === 0) {
|
||||
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, node, Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
const json = convertToObjectWorker(sourceFile, errors, getTsconfigRootOptionsMap(), optionsIterator);
|
||||
if (!typeAcquisition) {
|
||||
if (typingOptionstypeAcquisition) {
|
||||
typeAcquisition = (typingOptionstypeAcquisition.enableAutoDiscovery !== undefined) ?
|
||||
{
|
||||
enable: typingOptionstypeAcquisition.enableAutoDiscovery,
|
||||
include: typingOptionstypeAcquisition.include,
|
||||
exclude: typingOptionstypeAcquisition.exclude
|
||||
} :
|
||||
typingOptionstypeAcquisition;
|
||||
}
|
||||
else {
|
||||
typeAcquisition = getDefaultTypeAcquisition(configFileName);
|
||||
}
|
||||
}
|
||||
|
||||
return { raw: json, options, typeAcquisition, extendedConfigPath };
|
||||
}
|
||||
|
||||
function getExtendsConfigPath(
|
||||
extendedConfig: string,
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
getCanonicalFileName: (fileName: string) => string,
|
||||
errors: Diagnostic[],
|
||||
createDiagnostic: (message: DiagnosticMessage, arg1?: string) => Diagnostic) {
|
||||
extendedConfig = normalizeSlashes(extendedConfig);
|
||||
// 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(extendedConfig, "./") || startsWith(extendedConfig, "../"))) {
|
||||
errors.push(createDiagnostic(Diagnostics.A_path_in_an_extends_option_must_be_relative_or_rooted_but_0_is_not, extendedConfig));
|
||||
return undefined;
|
||||
}
|
||||
let extendedConfigPath = toPath(extendedConfig, basePath, getCanonicalFileName);
|
||||
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, ".json")) {
|
||||
extendedConfigPath = `${extendedConfigPath}.json` as Path;
|
||||
if (!host.fileExists(extendedConfigPath)) {
|
||||
errors.push(createDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return extendedConfigPath;
|
||||
}
|
||||
|
||||
function getExtendedConfig(
|
||||
extendedConfigPath: Path,
|
||||
host: ts.ParseConfigHost,
|
||||
basePath: string,
|
||||
getCanonicalFileName: (fileName: string) => string,
|
||||
resolutionStack: Path[],
|
||||
errors: Diagnostic[],
|
||||
): ParsedTsconfig | undefined {
|
||||
const extendedResult = readJsonConfigFile(extendedConfigPath, path => host.readFile(path));
|
||||
if (extendedResult.parseDiagnostics.length) {
|
||||
errors.push(...extendedResult.parseDiagnostics);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const extendedDirname = getDirectoryPath(extendedConfigPath);
|
||||
const extendedConfig = parseConfig(/*json*/ undefined, extendedResult, host, extendedDirname,
|
||||
getBaseFileName(extendedConfigPath), resolutionStack, errors);
|
||||
|
||||
if (isSuccessfulParsedTsconfig(extendedConfig)) {
|
||||
// Update the paths to reflect base path
|
||||
const relativeDifference = convertToRelativePath(extendedDirname, basePath, getCanonicalFileName);
|
||||
const updatePath = (path: string) => isRootedDiskPath(path) ? path : combinePaths(relativeDifference, path);
|
||||
const mapPropertiesInRawIfNotUndefined = (propertyName: string) => {
|
||||
if (raw[propertyName]) {
|
||||
raw[propertyName] = map(raw[propertyName], updatePath);
|
||||
}
|
||||
};
|
||||
|
||||
const { raw } = extendedConfig;
|
||||
mapPropertiesInRawIfNotUndefined("include");
|
||||
mapPropertiesInRawIfNotUndefined("exclude");
|
||||
mapPropertiesInRawIfNotUndefined("files");
|
||||
}
|
||||
|
||||
return extendedConfig;
|
||||
}
|
||||
|
||||
function convertCompileOnSaveOptionFromJson(jsonOption: any, basePath: string, errors: Diagnostic[]): boolean {
|
||||
if (!hasProperty(jsonOption, compileOnSaveCommandLineOption.name)) {
|
||||
return false;
|
||||
return undefined;
|
||||
}
|
||||
const result = convertJsonOption(compileOnSaveCommandLineOption, jsonOption["compileOnSave"], basePath, errors);
|
||||
if (typeof result === "boolean" && result) {
|
||||
@@ -2025,4 +2106,4 @@ namespace ts {
|
||||
function caseInsensitiveKeyMapper(key: string) {
|
||||
return key.toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
-1
@@ -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 */
|
||||
@@ -224,6 +224,25 @@ namespace ts {
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
/**
|
||||
* Iterates through the parent chain of a node and performs the callback on each parent until the callback
|
||||
* returns a truthy value, then returns that value.
|
||||
* 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(node: Node, callback: (element: Node) => boolean | "quit"): Node {
|
||||
while (node) {
|
||||
const result = callback(node);
|
||||
if (result === "quit") {
|
||||
return undefined;
|
||||
}
|
||||
else if (result) {
|
||||
return node;
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function zipWith<T, U>(arrayA: T[], arrayB: U[], callback: (a: T, b: U, index: number) => void): void {
|
||||
Debug.assert(arrayA.length === arrayB.length);
|
||||
@@ -232,6 +251,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.
|
||||
|
||||
@@ -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): string {
|
||||
const tempVarName = getExportTempVariableName(baseName);
|
||||
if (!noDeclare) {
|
||||
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
|
||||
});
|
||||
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) {
|
||||
@@ -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;
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
{
|
||||
"Unterminated string literal.": {
|
||||
"category": "Error",
|
||||
"code": 1002
|
||||
@@ -175,7 +175,7 @@
|
||||
"category": "Error",
|
||||
"code": 1057
|
||||
},
|
||||
"Type used as operand to 'await' or the return type of an async function must either be a valid promise or must not contain a callable 'then' member.": {
|
||||
"The return type of an async function must either be a valid promise or must not contain a callable 'then' member.": {
|
||||
"category": "Error",
|
||||
"code": 1058
|
||||
},
|
||||
@@ -867,14 +867,26 @@
|
||||
"category": "Error",
|
||||
"code": 1319
|
||||
},
|
||||
"String literal with double quotes expected.": {
|
||||
"Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.": {
|
||||
"category": "Error",
|
||||
"code": 1320
|
||||
},
|
||||
"Property value can only be string literal, numeric literal, 'true', 'false', 'null', object literal or array literal.": {
|
||||
"Type of 'yield' operand in an async generator must either be a valid promise or must not contain a callable 'then' member.": {
|
||||
"category": "Error",
|
||||
"code": 1321
|
||||
},
|
||||
"Type of iterated elements of a 'yield*' operand must either be a valid promise or must not contain a callable 'then' member.": {
|
||||
"category": "Error",
|
||||
"code": 1322
|
||||
},
|
||||
"String literal with double quotes expected.": {
|
||||
"category": "Error",
|
||||
"code": 1323
|
||||
},
|
||||
"Property value can only be string literal, numeric literal, 'true', 'false', 'null', object literal or array literal.": {
|
||||
"category": "Error",
|
||||
"code": 1324
|
||||
},
|
||||
"Duplicate identifier '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 2300
|
||||
@@ -1839,6 +1851,14 @@
|
||||
"category": "Error",
|
||||
"code": 2550
|
||||
},
|
||||
"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
|
||||
},
|
||||
"JSX element attributes type '{0}' may not be a union type.": {
|
||||
"category": "Error",
|
||||
"code": 2600
|
||||
@@ -2115,6 +2135,10 @@
|
||||
"category": "Error",
|
||||
"code": 2709
|
||||
},
|
||||
"'{0}' are specified twice. The attribute named '{0}' will be overwritten.": {
|
||||
"category": "Error",
|
||||
"code": 2710
|
||||
},
|
||||
|
||||
"Import declaration '{0}' is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
@@ -3193,7 +3217,18 @@
|
||||
"category": "Message",
|
||||
"code": 6181
|
||||
},
|
||||
|
||||
"Scoped package detected, looking in '{0}'": {
|
||||
"category": "Message",
|
||||
"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
|
||||
@@ -3298,6 +3333,10 @@
|
||||
"category": "Error",
|
||||
"code": 7034
|
||||
},
|
||||
"Try `npm install @types/{0}` if it exists or add a new declaration (.d.ts) file containing `declare module '{0}';`": {
|
||||
"category": "Error",
|
||||
"code": 7035
|
||||
},
|
||||
"You cannot rename this element.": {
|
||||
"category": "Error",
|
||||
"code": 8000
|
||||
@@ -3516,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",
|
||||
|
||||
@@ -2457,7 +2457,7 @@ namespace ts {
|
||||
let indentation: number;
|
||||
for (const line of lines) {
|
||||
for (let i = 0; i < line.length && (indentation === undefined || i < indentation); i++) {
|
||||
if (!isWhiteSpace(line.charCodeAt(i))) {
|
||||
if (!isWhiteSpaceLike(line.charCodeAt(i))) {
|
||||
if (indentation === undefined || i < indentation) {
|
||||
indentation = i;
|
||||
break;
|
||||
|
||||
+470
-363
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
@@ -378,7 +378,7 @@ namespace ts {
|
||||
directoryPathMap.set(parent, result);
|
||||
current = parent;
|
||||
|
||||
if (current == commonPrefix) {
|
||||
if (current === commonPrefix) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -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[] = [];
|
||||
@@ -954,10 +967,25 @@ namespace ts {
|
||||
}
|
||||
nodeModulesAtTypesExists = false;
|
||||
}
|
||||
return loadModuleFromNodeModulesFolder(Extensions.DtsOnly, moduleName, nodeModulesAtTypes, nodeModulesAtTypesExists, failedLookupLocations, state);
|
||||
return loadModuleFromNodeModulesFolder(Extensions.DtsOnly, mangleScopedPackage(moduleName, state), nodeModulesAtTypes, nodeModulesAtTypesExists, failedLookupLocations, state);
|
||||
}
|
||||
}
|
||||
|
||||
/** For a scoped package, we must look in `@types/foo__bar` instead of `@types/@foo/bar`. */
|
||||
function mangleScopedPackage(moduleName: string, state: ModuleResolutionState): string {
|
||||
if (startsWith(moduleName, "@")) {
|
||||
const replaceSlash = moduleName.replace(ts.directorySeparator, "__");
|
||||
if (replaceSlash !== moduleName) {
|
||||
const mangled = replaceSlash.slice(1); // Take off the "@"
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.Scoped_package_detected_looking_in_0, mangled);
|
||||
}
|
||||
return mangled;
|
||||
}
|
||||
}
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
function tryFindNonRelativeModuleNameInCache(cache: PerModuleNameCache | undefined, moduleName: string, containingDirectory: string, traceEnabled: boolean, host: ModuleResolutionHost): SearchResult<Resolved> {
|
||||
const result = cache && cache.get(containingDirectory);
|
||||
if (result) {
|
||||
|
||||
+15
-5
@@ -56,7 +56,7 @@ namespace ts {
|
||||
// The visitXXX functions could be written as local functions that close over the cbNode and cbNodeArray
|
||||
// callback parameters, but that causes a closure allocation for each invocation with noticeable effects
|
||||
// on performance.
|
||||
const visitNodes: (cb: (node: Node | Node[]) => T, nodes: Node[]) => T = cbNodeArray ? visitNodeArray : visitEachNode;
|
||||
const visitNodes: (cb: ((node: Node) => T) | ((node: Node[]) => T), nodes: Node[]) => T = cbNodeArray ? visitNodeArray : visitEachNode;
|
||||
const cbNodes = cbNodeArray || cbNode;
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.QualifiedName:
|
||||
@@ -3606,6 +3606,7 @@ namespace ts {
|
||||
if (isAwaitExpression()) {
|
||||
return parseAwaitExpression();
|
||||
}
|
||||
// falls through
|
||||
default:
|
||||
return parseIncrementExpression();
|
||||
}
|
||||
@@ -3639,8 +3640,8 @@ namespace ts {
|
||||
if (sourceFile.languageVariant !== LanguageVariant.JSX) {
|
||||
return false;
|
||||
}
|
||||
// We are in JSX context and the token is part of JSXElement.
|
||||
// Fall through
|
||||
// We are in JSX context and the token is part of JSXElement.
|
||||
// falls through
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
@@ -3860,6 +3861,7 @@ namespace ts {
|
||||
|
||||
function parseJsxText(): JsxText {
|
||||
const node = <JsxText>createNode(SyntaxKind.JsxText, scanner.getStartPos());
|
||||
node.containsOnlyWhiteSpaces = currentToken === SyntaxKind.JsxTextAllWhiteSpaces;
|
||||
currentToken = scanner.scanJsxToken();
|
||||
return finishNode(node);
|
||||
}
|
||||
@@ -3867,6 +3869,7 @@ namespace ts {
|
||||
function parseJsxChild(): JsxChild {
|
||||
switch (token()) {
|
||||
case SyntaxKind.JsxText:
|
||||
case SyntaxKind.JsxTextAllWhiteSpaces:
|
||||
return parseJsxText();
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
return parseJsxExpression(/*inExpressionContext*/ false);
|
||||
@@ -3896,7 +3899,10 @@ namespace ts {
|
||||
else if (token() === SyntaxKind.ConflictMarkerTrivia) {
|
||||
break;
|
||||
}
|
||||
result.push(parseJsxChild());
|
||||
const child = parseJsxChild();
|
||||
if (child) {
|
||||
result.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
result.end = scanner.getTokenPos();
|
||||
@@ -6529,6 +6535,8 @@ namespace ts {
|
||||
case "augments":
|
||||
tag = parseAugmentsTag(atToken, tagName);
|
||||
break;
|
||||
case "arg":
|
||||
case "argument":
|
||||
case "param":
|
||||
tag = parseParamTag(atToken, tagName);
|
||||
break;
|
||||
@@ -6604,7 +6612,8 @@ namespace ts {
|
||||
indent += scanner.getTokenText().length;
|
||||
break;
|
||||
}
|
||||
// FALLTHROUGH otherwise to record the * as a comment
|
||||
// record the * as a comment
|
||||
// falls through
|
||||
default:
|
||||
state = JSDocState.SavingComments; // leading identifiers start recording as well
|
||||
pushComment(scanner.getTokenText());
|
||||
@@ -6830,6 +6839,7 @@ namespace ts {
|
||||
break;
|
||||
case SyntaxKind.Identifier:
|
||||
canParseTag = false;
|
||||
break;
|
||||
case SyntaxKind.EndOfFileToken:
|
||||
break;
|
||||
}
|
||||
|
||||
+149
-101
@@ -298,6 +298,7 @@ namespace ts {
|
||||
let diagnosticsProducingTypeChecker: TypeChecker;
|
||||
let noDiagnosticsTypeChecker: TypeChecker;
|
||||
let classifiableNames: Map<string>;
|
||||
let modifiedFilePaths: Path[] | undefined;
|
||||
|
||||
const cachedSemanticDiagnosticsForFile: DiagnosticCache = {};
|
||||
const cachedDeclarationDiagnosticsForFile: DiagnosticCache = {};
|
||||
@@ -368,7 +369,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
|
||||
@@ -477,89 +479,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];
|
||||
@@ -567,18 +594,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;
|
||||
}
|
||||
@@ -600,99 +627,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) {
|
||||
@@ -701,12 +736,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
|
||||
@@ -721,9 +761,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 {
|
||||
@@ -909,6 +948,13 @@ namespace ts {
|
||||
|
||||
function getSemanticDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
|
||||
return runWithCancellationToken(() => {
|
||||
// If skipLibCheck is enabled, skip reporting errors if file is a declaration file.
|
||||
// If skipDefaultLibCheck is enabled, skip reporting errors if file contains a
|
||||
// '/// <reference no-default-lib="true"/>' directive.
|
||||
if (options.skipLibCheck && sourceFile.isDeclarationFile || options.skipDefaultLibCheck && sourceFile.hasNoDefaultLib) {
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
const typeChecker = getDiagnosticsProducingTypeChecker();
|
||||
|
||||
Debug.assert(!!sourceFile.bindDiagnostics);
|
||||
@@ -931,20 +977,22 @@ namespace ts {
|
||||
*/
|
||||
function shouldReportDiagnostic(diagnostic: Diagnostic) {
|
||||
const { file, start } = diagnostic;
|
||||
const lineStarts = getLineStarts(file);
|
||||
let { line } = computeLineAndCharacterOfPosition(lineStarts, start);
|
||||
while (line > 0) {
|
||||
const previousLineText = file.text.slice(lineStarts[line - 1], lineStarts[line]);
|
||||
const result = ignoreDiagnosticCommentRegEx.exec(previousLineText);
|
||||
if (!result) {
|
||||
// non-empty line
|
||||
return true;
|
||||
if (file) {
|
||||
const lineStarts = getLineStarts(file);
|
||||
let { line } = computeLineAndCharacterOfPosition(lineStarts, start);
|
||||
while (line > 0) {
|
||||
const previousLineText = file.text.slice(lineStarts[line - 1], lineStarts[line]);
|
||||
const result = ignoreDiagnosticCommentRegEx.exec(previousLineText);
|
||||
if (!result) {
|
||||
// non-empty line
|
||||
return true;
|
||||
}
|
||||
if (result[3]) {
|
||||
// @ts-ignore
|
||||
return false;
|
||||
}
|
||||
line--;
|
||||
}
|
||||
if (result[3]) {
|
||||
// @ts-ignore
|
||||
return false;
|
||||
}
|
||||
line--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -968,8 +1016,7 @@ namespace ts {
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_can_only_be_used_in_a_ts_file, "?"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass through
|
||||
// falls through
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.Constructor:
|
||||
@@ -1049,7 +1096,7 @@ namespace ts {
|
||||
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.type_parameter_declarations_can_only_be_used_in_a_ts_file));
|
||||
return;
|
||||
}
|
||||
// pass through
|
||||
// falls through
|
||||
case SyntaxKind.VariableStatement:
|
||||
// Check modifiers
|
||||
if (nodes === (<ClassDeclaration | FunctionLikeDeclaration | VariableStatement>parent).modifiers) {
|
||||
@@ -1097,7 +1144,8 @@ namespace ts {
|
||||
if (isConstValid) {
|
||||
continue;
|
||||
}
|
||||
// Fallthrough to report error
|
||||
// to report error,
|
||||
// falls through
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
@@ -1368,7 +1416,7 @@ namespace ts {
|
||||
|
||||
// If the file was previously found via a node_modules search, but is now being processed as a root file,
|
||||
// then everything it sucks in may also be marked incorrectly, and needs to be checked again.
|
||||
if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth == 0) {
|
||||
if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth === 0) {
|
||||
sourceFilesFoundSearchingNodeModules.set(file.path, false);
|
||||
if (!options.noResolve) {
|
||||
processReferencedFiles(file, isDefaultLib);
|
||||
@@ -1523,11 +1571,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];
|
||||
|
||||
+29
-6
@@ -308,6 +308,7 @@ namespace ts {
|
||||
if (text.charCodeAt(pos) === CharacterCodes.lineFeed) {
|
||||
pos++;
|
||||
}
|
||||
// falls through
|
||||
case CharacterCodes.lineFeed:
|
||||
result.push(lineStart);
|
||||
lineStart = pos;
|
||||
@@ -366,7 +367,7 @@ namespace ts {
|
||||
return computeLineAndCharacterOfPosition(getLineStarts(sourceFile), position);
|
||||
}
|
||||
|
||||
export function isWhiteSpace(ch: number): boolean {
|
||||
export function isWhiteSpaceLike(ch: number): boolean {
|
||||
return isWhiteSpaceSingleLine(ch) || isLineBreak(ch);
|
||||
}
|
||||
|
||||
@@ -454,6 +455,7 @@ namespace ts {
|
||||
if (text.charCodeAt(pos + 1) === CharacterCodes.lineFeed) {
|
||||
pos++;
|
||||
}
|
||||
// falls through
|
||||
case CharacterCodes.lineFeed:
|
||||
pos++;
|
||||
if (stopAfterLineBreak) {
|
||||
@@ -510,7 +512,7 @@ namespace ts {
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpace(ch))) {
|
||||
if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpaceLike(ch))) {
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
@@ -625,6 +627,7 @@ namespace ts {
|
||||
if (text.charCodeAt(pos + 1) === CharacterCodes.lineFeed) {
|
||||
pos++;
|
||||
}
|
||||
// falls through
|
||||
case CharacterCodes.lineFeed:
|
||||
pos++;
|
||||
if (trailing) {
|
||||
@@ -691,7 +694,7 @@ namespace ts {
|
||||
}
|
||||
break scan;
|
||||
default:
|
||||
if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpace(ch))) {
|
||||
if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpaceLike(ch))) {
|
||||
if (hasPendingCommentRange && isLineBreak(ch)) {
|
||||
pendingHasTrailingNewLine = true;
|
||||
}
|
||||
@@ -1072,7 +1075,7 @@ namespace ts {
|
||||
if (pos < end && text.charCodeAt(pos) === CharacterCodes.lineFeed) {
|
||||
pos++;
|
||||
}
|
||||
// fall through
|
||||
// falls through
|
||||
case CharacterCodes.lineFeed:
|
||||
case CharacterCodes.lineSeparator:
|
||||
case CharacterCodes.paragraphSeparator:
|
||||
@@ -1459,6 +1462,7 @@ namespace ts {
|
||||
// This fall-through is a deviation from the EcmaScript grammar. The grammar says that a leading zero
|
||||
// can only be followed by an octal digit, a dot, or the end of the number literal. However, we are being
|
||||
// permissive and allowing decimal digits of the form 08* and 09* (which many browsers also do).
|
||||
// falls through
|
||||
case CharacterCodes._1:
|
||||
case CharacterCodes._2:
|
||||
case CharacterCodes._3:
|
||||
@@ -1723,8 +1727,12 @@ namespace ts {
|
||||
return token = SyntaxKind.OpenBraceToken;
|
||||
}
|
||||
|
||||
// First non-whitespace character on this line.
|
||||
let firstNonWhitespace = 0;
|
||||
// These initial values are special because the first line is:
|
||||
// firstNonWhitespace = 0 to indicate that we want leading whitspace,
|
||||
|
||||
while (pos < end) {
|
||||
pos++;
|
||||
char = text.charCodeAt(pos);
|
||||
if (char === CharacterCodes.openBrace) {
|
||||
break;
|
||||
@@ -1736,8 +1744,23 @@ namespace ts {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// FirstNonWhitespace is 0, then we only see whitespaces so far. If we see a linebreak, we want to ignore that whitespaces.
|
||||
// i.e (- : whitespace)
|
||||
// <div>----
|
||||
// </div> becomes <div></div>
|
||||
//
|
||||
// <div>----</div> becomes <div>----</div>
|
||||
if (isLineBreak(char) && firstNonWhitespace === 0) {
|
||||
firstNonWhitespace = -1;
|
||||
}
|
||||
else if (!isWhiteSpaceLike(char)) {
|
||||
firstNonWhitespace = pos;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
return token = SyntaxKind.JsxText;
|
||||
|
||||
return firstNonWhitespace === -1 ? SyntaxKind.JsxTextAllWhiteSpaces : SyntaxKind.JsxText;
|
||||
}
|
||||
|
||||
// Scans a JSX identifier; these differ from normal identifiers in that
|
||||
|
||||
@@ -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
|
||||
@@ -2714,9 +2715,8 @@ namespace ts {
|
||||
loopBody = createBlock([loopBody], /*multiline*/ true);
|
||||
}
|
||||
|
||||
const isAsyncBlockContainingAwait =
|
||||
hierarchyFacts & HierarchyFacts.AsyncFunctionBody
|
||||
&& (node.statement.transformFlags & TransformFlags.ContainsYield) !== 0;
|
||||
const containsYield = (node.statement.transformFlags & TransformFlags.ContainsYield) !== 0;
|
||||
const isAsyncBlockContainingAwait = containsYield && (hierarchyFacts & HierarchyFacts.AsyncFunctionBody) !== 0;
|
||||
|
||||
let loopBodyFlags: EmitFlags = 0;
|
||||
if (currentState.containsLexicalThis) {
|
||||
@@ -2739,7 +2739,7 @@ namespace ts {
|
||||
setEmitFlags(
|
||||
createFunctionExpression(
|
||||
/*modifiers*/ undefined,
|
||||
isAsyncBlockContainingAwait ? createToken(SyntaxKind.AsteriskToken) : undefined,
|
||||
containsYield ? createToken(SyntaxKind.AsteriskToken) : undefined,
|
||||
/*name*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
loopParameters,
|
||||
@@ -2833,7 +2833,7 @@ namespace ts {
|
||||
));
|
||||
}
|
||||
|
||||
const convertedLoopBodyStatements = generateCallToConvertedLoop(functionName, loopParameters, currentState, isAsyncBlockContainingAwait);
|
||||
const convertedLoopBodyStatements = generateCallToConvertedLoop(functionName, loopParameters, currentState, containsYield);
|
||||
|
||||
let loop: Statement;
|
||||
if (convert) {
|
||||
@@ -3181,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;
|
||||
@@ -3713,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);
|
||||
@@ -3766,9 +3779,9 @@ 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) {
|
||||
if (declaration && !(isClassLike(declaration) && isPartOfClassBody(declaration, node))) {
|
||||
return setTextRange(getGeneratedNameForNode(declaration.name), node);
|
||||
}
|
||||
}
|
||||
@@ -3776,6 +3789,32 @@ namespace ts {
|
||||
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.
|
||||
*
|
||||
@@ -3790,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) {
|
||||
|
||||
@@ -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) : r.value[0] === "yield" ? settle(c[2], { value: r.value[1], done: false }) : Promise.resolve(r.value[1]).then(r.value[0] === "delegate" ? delegate : fulfill, reject); }
|
||||
function delegate(r) { step(r.done ? r : { value: ["yield", r.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,14 +928,15 @@ 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 }; }) };
|
||||
return o = __asyncValues(o), i[Symbol.iterator] = function () { return this; }, i;
|
||||
function verb(n, f) { return function (v) { return { value: ["delegate", (o[n] || f).call(o, v)], done: false }; }; }
|
||||
var i, p;
|
||||
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
|
||||
function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; }
|
||||
};
|
||||
`
|
||||
};
|
||||
|
||||
function createAsyncDelegatorHelper(context: TransformationContext, expression: Expression, location?: TextRange) {
|
||||
context.requestEmitHelper(awaitHelper);
|
||||
context.requestEmitHelper(asyncDelegator);
|
||||
return setTextRange(
|
||||
createCall(
|
||||
|
||||
@@ -479,8 +479,8 @@ namespace ts {
|
||||
// module is imported only for side-effects, no emit required
|
||||
break;
|
||||
}
|
||||
// falls through
|
||||
|
||||
// fall-through
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
Debug.assert(importVariableName !== undefined);
|
||||
// save import into the local
|
||||
|
||||
@@ -521,9 +521,10 @@ namespace ts {
|
||||
// emit name if
|
||||
// - node has a name
|
||||
// - node has static initializers
|
||||
// - node has a member that is decorated
|
||||
//
|
||||
let name = node.name;
|
||||
if (!name && staticProperties.length > 0) {
|
||||
if (!name && (staticProperties.length > 0 || childIsDecorated(node))) {
|
||||
name = getGeneratedNameForNode(node);
|
||||
}
|
||||
|
||||
@@ -1761,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) ||
|
||||
@@ -2600,14 +2597,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);
|
||||
|
||||
+3
-3
@@ -30,7 +30,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function reportEmittedFiles(files: string[]): void {
|
||||
if (!files || files.length == 0) {
|
||||
if (!files || files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace ts {
|
||||
// When the configFileName is just "tsconfig.json", the watched directory should be
|
||||
// the current directory; if there is a given "project" parameter, then the configFileName
|
||||
// is an absolute file name.
|
||||
directory == "" ? "." : directory,
|
||||
directory === "" ? "." : directory,
|
||||
watchedDirectoryChanged, /*recursive*/ true);
|
||||
}
|
||||
}
|
||||
@@ -333,7 +333,7 @@ namespace ts {
|
||||
// When the configFileName is just "tsconfig.json", the watched directory should be
|
||||
// the current directory; if there is a given "project" parameter, then the configFileName
|
||||
// is an absolute file name.
|
||||
directory == "" ? "." : directory,
|
||||
directory === "" ? "." : directory,
|
||||
watchedDirectoryChanged, /*recursive*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
+36
-16
@@ -10,7 +10,7 @@ namespace ts {
|
||||
|
||||
/** ES6 Map interface. */
|
||||
export interface Map<T> {
|
||||
get(key: string): T;
|
||||
get(key: string): T | undefined;
|
||||
has(key: string): boolean;
|
||||
set(key: string, value: T): this;
|
||||
delete(key: string): boolean;
|
||||
@@ -65,6 +65,7 @@ namespace ts {
|
||||
NumericLiteral,
|
||||
StringLiteral,
|
||||
JsxText,
|
||||
JsxTextAllWhiteSpaces,
|
||||
RegularExpressionLiteral,
|
||||
NoSubstitutionTemplateLiteral,
|
||||
// Pseudo-literals
|
||||
@@ -1505,7 +1506,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;
|
||||
}
|
||||
|
||||
@@ -1572,6 +1573,7 @@ namespace ts {
|
||||
|
||||
export interface JsxText extends Node {
|
||||
kind: SyntaxKind.JsxText;
|
||||
containsOnlyWhiteSpaces: boolean;
|
||||
parent?: JsxElement;
|
||||
}
|
||||
|
||||
@@ -2410,7 +2412,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 {
|
||||
@@ -2547,6 +2556,9 @@ namespace ts {
|
||||
|
||||
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;
|
||||
/* @internal */ getBaseConstraintOfType(type: Type): Type;
|
||||
|
||||
/* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol;
|
||||
|
||||
@@ -2729,7 +2741,6 @@ namespace ts {
|
||||
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
|
||||
@@ -3354,6 +3365,7 @@ namespace ts {
|
||||
messageText: string | DiagnosticMessageChain;
|
||||
category: DiagnosticCategory;
|
||||
code: number;
|
||||
source?: string;
|
||||
}
|
||||
|
||||
export enum DiagnosticCategory {
|
||||
@@ -3945,14 +3957,15 @@ 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.
|
||||
}
|
||||
|
||||
export interface EmitHelper {
|
||||
@@ -3977,11 +3990,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,
|
||||
@@ -3989,6 +4003,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,
|
||||
|
||||
|
||||
+60
-22
@@ -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]);
|
||||
@@ -674,6 +673,7 @@ namespace ts {
|
||||
// At this point, node is either a qualified name or an identifier
|
||||
Debug.assert(node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression,
|
||||
"'node' was expected to be a qualified name, identifier or property access in 'isPartOfTypeNode'.");
|
||||
// falls through
|
||||
case SyntaxKind.QualifiedName:
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
case SyntaxKind.ThisKeyword:
|
||||
@@ -783,6 +783,7 @@ namespace ts {
|
||||
if (operand) {
|
||||
traverse(operand);
|
||||
}
|
||||
return;
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
@@ -1009,7 +1010,7 @@ namespace ts {
|
||||
if (!includeArrowFunctions) {
|
||||
continue;
|
||||
}
|
||||
// Fall through
|
||||
// falls through
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
@@ -1068,6 +1069,7 @@ namespace ts {
|
||||
if (!stopOnFunctions) {
|
||||
continue;
|
||||
}
|
||||
// falls through
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
@@ -1149,6 +1151,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;
|
||||
@@ -1267,7 +1273,7 @@ namespace ts {
|
||||
if (node.parent.kind === SyntaxKind.TypeQuery || isJSXTagName(node)) {
|
||||
return true;
|
||||
}
|
||||
// fall through
|
||||
// falls through
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.ThisKeyword:
|
||||
@@ -1870,7 +1876,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function getAncestor(node: Node | undefined, kind: SyntaxKind): Node {
|
||||
export function getAncestor(node: Node | undefined, kind: SyntaxKind): Node | undefined {
|
||||
while (node) {
|
||||
if (node.kind === kind) {
|
||||
return node;
|
||||
@@ -1925,16 +1931,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:
|
||||
@@ -1943,7 +1951,7 @@ namespace ts {
|
||||
if (node.asteriskToken) {
|
||||
flags |= FunctionFlags.Generator;
|
||||
}
|
||||
// fall through
|
||||
// falls through
|
||||
case SyntaxKind.ArrowFunction:
|
||||
if (hasModifier(node, ModifierFlags.Async)) {
|
||||
flags |= FunctionFlags.Async;
|
||||
@@ -3577,10 +3585,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;
|
||||
@@ -3657,7 +3661,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 {
|
||||
@@ -4449,7 +4464,7 @@ namespace ts {
|
||||
newEndN = Math.max(newEnd2, newEnd2 + (newEnd1 - oldEnd2));
|
||||
}
|
||||
|
||||
return createTextChangeRange(createTextSpanFromBounds(oldStartN, oldEndN), /*newLength:*/ newEndN - oldStartN);
|
||||
return createTextChangeRange(createTextSpanFromBounds(oldStartN, oldEndN), /*newLength*/ newEndN - oldStartN);
|
||||
}
|
||||
|
||||
export function getTypeParameterOwner(d: Declaration): Declaration {
|
||||
@@ -4625,7 +4640,7 @@ namespace ts {
|
||||
*/
|
||||
export function getParseTreeNode<T extends Node>(node: Node, nodeTest?: (node: Node) => node is T): T;
|
||||
export function getParseTreeNode(node: Node, nodeTest?: (node: Node) => boolean): Node {
|
||||
if (node == undefined || isParseTreeNode(node)) {
|
||||
if (node === undefined || isParseTreeNode(node)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -4647,4 +4662,27 @@ namespace ts {
|
||||
export function unescapeIdentifier(identifier: string): string {
|
||||
return identifier.length >= 3 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ && identifier.charCodeAt(2) === CharacterCodes._ ? identifier.substr(1) : identifier;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
+153
-119
@@ -218,16 +218,7 @@ 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.QualifiedName:
|
||||
return updateQualifiedName(<QualifiedName>node,
|
||||
@@ -238,45 +229,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,66 +251,7 @@ 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:
|
||||
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),
|
||||
@@ -368,6 +268,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 +312,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 +421,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 +500,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 +508,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 +555,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 +684,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 +721,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 +780,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 +812,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 +825,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 +845,7 @@ namespace ts {
|
||||
visitNode((<CatchClause>node).block, visitor, isBlock));
|
||||
|
||||
// Property assignments
|
||||
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
return updatePropertyAssignment(<PropertyAssignment>node,
|
||||
visitNode((<PropertyAssignment>node).name, visitor, isPropertyName),
|
||||
@@ -847,8 +877,10 @@ namespace ts {
|
||||
visitNode((<PartiallyEmittedExpression>node).expression, visitor, isExpression));
|
||||
|
||||
default:
|
||||
// No need to visit nodes with no children.
|
||||
return node;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -885,7 +917,7 @@ namespace ts {
|
||||
return initial;
|
||||
}
|
||||
|
||||
const reduceNodes: (nodes: NodeArray<Node>, f: (memo: T, node: Node | NodeArray<Node>) => T, initial: T) => T = cbNodeArray ? reduceNodeArray : reduceLeft;
|
||||
const reduceNodes: (nodes: NodeArray<Node>, f: ((memo: T, node: Node) => T) | ((memo: T, node: NodeArray<Node>) => T), initial: T) => T = cbNodeArray ? reduceNodeArray : reduceLeft;
|
||||
const cbNodes = cbNodeArray || cbNode;
|
||||
const kind = node.kind;
|
||||
|
||||
@@ -1289,6 +1321,7 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.JsxAttributes:
|
||||
result = reduceNodes((<JsxAttributes>node).properties, cbNodes, result);
|
||||
break;
|
||||
|
||||
case SyntaxKind.JsxClosingElement:
|
||||
result = reduceNode((<JsxClosingElement>node).tagName, cbNode, result);
|
||||
@@ -1310,7 +1343,7 @@ namespace ts {
|
||||
// Clauses
|
||||
case SyntaxKind.CaseClause:
|
||||
result = reduceNode((<CaseClause>node).expression, cbNode, result);
|
||||
// fall-through
|
||||
// falls through
|
||||
|
||||
case SyntaxKind.DefaultClause:
|
||||
result = reduceNodes((<CaseClause | DefaultClause>node).statements, cbNodes, result);
|
||||
@@ -1344,6 +1377,7 @@ namespace ts {
|
||||
case SyntaxKind.EnumMember:
|
||||
result = reduceNode((<EnumMember>node).name, cbNode, result);
|
||||
result = reduceNode((<EnumMember>node).initializer, cbNode, result);
|
||||
break;
|
||||
|
||||
// Top-level nodes
|
||||
case SyntaxKind.SourceFile:
|
||||
|
||||
@@ -2367,13 +2367,13 @@ namespace FourSlash {
|
||||
|
||||
public verifyImportFixAtPosition(expectedTextArray: string[], errorCode?: number) {
|
||||
const ranges = this.getRanges();
|
||||
if (ranges.length == 0) {
|
||||
if (ranges.length === 0) {
|
||||
this.raiseError("At least one range should be specified in the testfile.");
|
||||
}
|
||||
|
||||
const codeFixes = this.getCodeFixActions(this.activeFile.fileName, errorCode);
|
||||
|
||||
if (!codeFixes || codeFixes.length == 0) {
|
||||
if (!codeFixes || codeFixes.length === 0) {
|
||||
this.raiseError("No codefixes returned.");
|
||||
}
|
||||
|
||||
@@ -2738,7 +2738,7 @@ namespace FourSlash {
|
||||
private assertItemInCompletionList(items: ts.CompletionEntry[], name: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
|
||||
for (const item of items) {
|
||||
if (item.name === name) {
|
||||
if (documentation != undefined || text !== undefined) {
|
||||
if (documentation !== undefined || text !== undefined) {
|
||||
const details = this.getCompletionEntryDetails(item.name);
|
||||
|
||||
if (documentation !== undefined) {
|
||||
@@ -2989,9 +2989,8 @@ ${code}
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: should be '==='?
|
||||
}
|
||||
else if (line == "" || lineLength === 0) {
|
||||
else if (line === "" || lineLength === 0) {
|
||||
// Previously blank lines between fourslash content caused it to be considered as 2 files,
|
||||
// Remove this behavior since it just causes errors now
|
||||
}
|
||||
|
||||
@@ -1940,7 +1940,7 @@ namespace Harness {
|
||||
}
|
||||
|
||||
const parentDirectory = IO.directoryName(dirName);
|
||||
if (parentDirectory != "") {
|
||||
if (parentDirectory !== "") {
|
||||
createDirectoryStructure(parentDirectory);
|
||||
}
|
||||
IO.createDirectory(dirName);
|
||||
|
||||
@@ -112,8 +112,7 @@ class ProjectRunner extends RunnerBase {
|
||||
else if (url.indexOf(diskProjectPath) === 0) {
|
||||
// Replace the disk specific path into the project root path
|
||||
url = url.substr(diskProjectPath.length);
|
||||
// TODO: should be '!=='?
|
||||
if (url.charCodeAt(0) != ts.CharacterCodes.slash) {
|
||||
if (url.charCodeAt(0) !== ts.CharacterCodes.slash) {
|
||||
url = "/" + url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,11 +50,11 @@ namespace Harness.SourceMapRecorder {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sourceMapMappings.charAt(decodingIndex) == ",") {
|
||||
if (sourceMapMappings.charAt(decodingIndex) === ",") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sourceMapMappings.charAt(decodingIndex) == ";") {
|
||||
if (sourceMapMappings.charAt(decodingIndex) === ";") {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace Harness.SourceMapRecorder {
|
||||
}
|
||||
|
||||
while (decodingIndex < sourceMapMappings.length) {
|
||||
if (sourceMapMappings.charAt(decodingIndex) == ";") {
|
||||
if (sourceMapMappings.charAt(decodingIndex) === ";") {
|
||||
// New line
|
||||
decodeOfEncodedMapping.emittedLine++;
|
||||
decodeOfEncodedMapping.emittedColumn = 1;
|
||||
@@ -125,7 +125,7 @@ namespace Harness.SourceMapRecorder {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sourceMapMappings.charAt(decodingIndex) == ",") {
|
||||
if (sourceMapMappings.charAt(decodingIndex) === ",") {
|
||||
// Next entry is on same line - no action needed
|
||||
decodingIndex++;
|
||||
continue;
|
||||
|
||||
@@ -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.");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -317,6 +317,29 @@ namespace ts.projectSystem {
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, []);
|
||||
});
|
||||
|
||||
it("should save when compileOnSave is enabled in base tsconfig.json", () => {
|
||||
configFile = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"extends": "/a/tsconfig.json"
|
||||
}`
|
||||
};
|
||||
|
||||
const configFile2: FileOrFolder = {
|
||||
path: "/a/tsconfig.json",
|
||||
content: `{
|
||||
"compileOnSave": true
|
||||
}`
|
||||
};
|
||||
|
||||
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, configFile2, configFile, libFile]);
|
||||
const typingsInstaller = createTestTypingsInstaller(host);
|
||||
const session = createSession(host, typingsInstaller);
|
||||
|
||||
openFilesForSession([moduleFile1, file1Consumer1], session);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
|
||||
});
|
||||
|
||||
it("should always return the file itself if '--isolatedModules' is specified", () => {
|
||||
configFile = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
|
||||
@@ -241,6 +241,18 @@ namespace ts {
|
||||
*/`);
|
||||
|
||||
|
||||
parsesCorrectly("argSynonymForParamTag",
|
||||
`/**
|
||||
* @arg {number} name1 Description
|
||||
*/`);
|
||||
|
||||
|
||||
parsesCorrectly("argumentSynonymForParamTag",
|
||||
`/**
|
||||
* @argument {number} name1 Description
|
||||
*/`);
|
||||
|
||||
|
||||
parsesCorrectly("templateTag",
|
||||
`/**
|
||||
* @template T
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3204,6 +3204,122 @@ namespace ts.projectSystem {
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(dTsFileGetErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 0);
|
||||
});
|
||||
|
||||
it("should not report bind errors for declaration files with skipLibCheck=true", () => {
|
||||
const jsconfigFile = {
|
||||
path: "/a/jsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const jsFile = {
|
||||
path: "/a/jsFile.js",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const dTsFile1 = {
|
||||
path: "/a/dTsFile1.d.ts",
|
||||
content: `
|
||||
declare var x: number;`
|
||||
};
|
||||
const dTsFile2 = {
|
||||
path: "/a/dTsFile2.d.ts",
|
||||
content: `
|
||||
declare var x: string;`
|
||||
};
|
||||
const host = createServerHost([jsconfigFile, jsFile, dTsFile1, dTsFile2]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([jsFile], session);
|
||||
|
||||
const dTsFile1GetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: dTsFile1.path }
|
||||
);
|
||||
const error1Result = <protocol.Diagnostic[]>session.executeCommand(dTsFile1GetErrRequest).response;
|
||||
assert.isTrue(error1Result.length === 0);
|
||||
|
||||
const dTsFile2GetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: dTsFile2.path }
|
||||
);
|
||||
const error2Result = <protocol.Diagnostic[]>session.executeCommand(dTsFile2GetErrRequest).response;
|
||||
assert.isTrue(error2Result.length === 0);
|
||||
});
|
||||
|
||||
it("should report semanitc errors for loose JS files with '// @ts-check' and skipLibCheck=true", () => {
|
||||
const jsFile = {
|
||||
path: "/a/jsFile.js",
|
||||
content: `
|
||||
// @ts-check
|
||||
let x = 1;
|
||||
x === "string";`
|
||||
};
|
||||
|
||||
const host = createServerHost([jsFile]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([jsFile], session);
|
||||
|
||||
const getErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: jsFile.path }
|
||||
);
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 1);
|
||||
assert.equal(errorResult[0].code, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2.code);
|
||||
});
|
||||
|
||||
it("should report semanitc errors for configured js project with '// @ts-check' and skipLibCheck=true", () => {
|
||||
const jsconfigFile = {
|
||||
path: "/a/jsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
|
||||
const jsFile = {
|
||||
path: "/a/jsFile.js",
|
||||
content: `
|
||||
// @ts-check
|
||||
let x = 1;
|
||||
x === "string";`
|
||||
};
|
||||
|
||||
const host = createServerHost([jsconfigFile, jsFile]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([jsFile], session);
|
||||
|
||||
const getErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: jsFile.path }
|
||||
);
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 1);
|
||||
assert.equal(errorResult[0].code, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2.code);
|
||||
});
|
||||
|
||||
it("should report semanitc errors for configured js project with checkJs=true and skipLibCheck=true", () => {
|
||||
const jsconfigFile = {
|
||||
path: "/a/jsconfig.json",
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
checkJs: true,
|
||||
skipLibCheck: true
|
||||
},
|
||||
})
|
||||
};
|
||||
const jsFile = {
|
||||
path: "/a/jsFile.js",
|
||||
content: `let x = 1;
|
||||
x === "string";`
|
||||
};
|
||||
|
||||
const host = createServerHost([jsconfigFile, jsFile]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([jsFile], session);
|
||||
|
||||
const getErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: jsFile.path }
|
||||
);
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 1);
|
||||
assert.equal(errorResult[0].code, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2.code);
|
||||
});
|
||||
});
|
||||
|
||||
describe("non-existing directories listed in config file input array", () => {
|
||||
@@ -3228,7 +3344,7 @@ namespace ts.projectSystem {
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
|
||||
const configuredProject = projectService.configuredProjects[0];
|
||||
assert.isTrue(configuredProject.getFileNames().length == 0);
|
||||
assert.isTrue(configuredProject.getFileNames().length === 0);
|
||||
|
||||
const inferredProject = projectService.inferredProjects[0];
|
||||
assert.isTrue(inferredProject.containsFile(<server.NormalizedPath>file1.path));
|
||||
@@ -3493,6 +3609,30 @@ namespace ts.projectSystem {
|
||||
});
|
||||
});
|
||||
|
||||
describe("searching for config file", () => {
|
||||
it("should stop at projectRootPath if given", () => {
|
||||
const f1 = {
|
||||
path: "/a/file1.ts",
|
||||
content: ""
|
||||
};
|
||||
const configFile = {
|
||||
path: "/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const host = createServerHost([f1, configFile]);
|
||||
const service = createProjectService(host);
|
||||
service.openClientFile(f1.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, "/a");
|
||||
|
||||
checkNumberOfConfiguredProjects(service, 0);
|
||||
checkNumberOfInferredProjects(service, 1);
|
||||
|
||||
service.closeClientFile(f1.path);
|
||||
service.openClientFile(f1.path);
|
||||
checkNumberOfConfiguredProjects(service, 1);
|
||||
checkNumberOfInferredProjects(service, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("cancellationToken", () => {
|
||||
it("is attached to request", () => {
|
||||
const f1 = {
|
||||
|
||||
@@ -278,7 +278,7 @@ and grew 1cm per day`;
|
||||
const insertString = testContent.substring(rsa[i], rsa[i] + las[i]);
|
||||
svc.edit(ersa[i], elas[i], insertString);
|
||||
checkText = editFlat(ersa[i], elas[i], insertString, checkText);
|
||||
if (0 == (i % 4)) {
|
||||
if (0 === (i % 4)) {
|
||||
const snap = svc.getSnapshot();
|
||||
const snapText = snap.getText(0, checkText.length);
|
||||
assert.equal(checkText, snapText);
|
||||
|
||||
Vendored
+35
@@ -21,6 +21,22 @@ interface FormData {
|
||||
[Symbol.iterator](): IterableIterator<string | File>;
|
||||
}
|
||||
|
||||
interface Headers {
|
||||
[Symbol.iterator](): IterableIterator<[string, string]>;
|
||||
/**
|
||||
* Returns an iterator allowing to go through all key/value pairs contained in this object.
|
||||
*/
|
||||
entries(): IterableIterator<[string, string]>;
|
||||
/**
|
||||
* Returns an iterator allowing to go through all keys f the key/value pairs contained in this object.
|
||||
*/
|
||||
keys(): IterableIterator<string>;
|
||||
/**
|
||||
* Returns an iterator allowing to go through all values of the key/value pairs contained in this object.
|
||||
*/
|
||||
values(): IterableIterator<string>;
|
||||
}
|
||||
|
||||
interface NodeList {
|
||||
/**
|
||||
* Returns an array of key, value pairs for every entry in the list
|
||||
@@ -70,3 +86,22 @@ interface NodeListOf<TNode extends Node> {
|
||||
|
||||
[Symbol.iterator](): IterableIterator<TNode>;
|
||||
}
|
||||
|
||||
interface URLSearchParams {
|
||||
/**
|
||||
* Returns an array of key, value pairs for every entry in the search params
|
||||
*/
|
||||
entries(): IterableIterator<[string, string]>;
|
||||
/**
|
||||
* Returns a list of keys in the search params
|
||||
*/
|
||||
keys(): IterableIterator<string>;
|
||||
/**
|
||||
* Returns a list of values in the search params
|
||||
*/
|
||||
values(): IterableIterator<string>;
|
||||
/**
|
||||
* iterate over key/value pairs
|
||||
*/
|
||||
[Symbol.iterator](): IterableIterator<[string, string]>;
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -58,7 +58,7 @@ interface ReadonlySet<T> {
|
||||
readonly size: number;
|
||||
}
|
||||
|
||||
interface WeakSet<T extends object> {
|
||||
interface WeakSet<T> {
|
||||
add(value: T): this;
|
||||
delete(value: T): boolean;
|
||||
has(value: T): boolean;
|
||||
|
||||
Vendored
+72
-8
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -118,7 +182,7 @@ interface SetConstructor {
|
||||
new <T>(iterable: Iterable<T>): Set<T>;
|
||||
}
|
||||
|
||||
interface WeakSet<T extends object> { }
|
||||
interface WeakSet<T> { }
|
||||
|
||||
interface WeakSetConstructor {
|
||||
new <T extends object>(iterable: Iterable<T>): WeakSet<T>;
|
||||
|
||||
Vendored
+1
-1
@@ -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;
|
||||
|
||||
Vendored
+1
-1
@@ -118,7 +118,7 @@ interface Set<T> {
|
||||
readonly [Symbol.toStringTag]: "Set";
|
||||
}
|
||||
|
||||
interface WeakSet<T extends object> {
|
||||
interface WeakSet<T> {
|
||||
readonly [Symbol.toStringTag]: "WeakSet";
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-64
@@ -1742,13 +1742,6 @@ interface Int8Array {
|
||||
*/
|
||||
reverse(): Int8Array;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param index The index of the location to set.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
set(index: number, value: number): void;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param array A typed or untyped array of values to set.
|
||||
@@ -2033,13 +2026,6 @@ interface Uint8Array {
|
||||
*/
|
||||
reverse(): Uint8Array;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param index The index of the location to set.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
set(index: number, value: number): void;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param array A typed or untyped array of values to set.
|
||||
@@ -2325,19 +2311,12 @@ interface Uint8ClampedArray {
|
||||
*/
|
||||
reverse(): Uint8ClampedArray;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param index The index of the location to set.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
set(index: number, value: number): void;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param array A typed or untyped array of values to set.
|
||||
* @param offset The index in the current array at which the values are to be written.
|
||||
*/
|
||||
set(array: Uint8ClampedArray, offset?: number): void;
|
||||
set(array: ArrayLike<number>, offset?: number): void;
|
||||
|
||||
/**
|
||||
* Returns a section of an array.
|
||||
@@ -2616,13 +2595,6 @@ interface Int16Array {
|
||||
*/
|
||||
reverse(): Int16Array;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param index The index of the location to set.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
set(index: number, value: number): void;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param array A typed or untyped array of values to set.
|
||||
@@ -2908,13 +2880,6 @@ interface Uint16Array {
|
||||
*/
|
||||
reverse(): Uint16Array;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param index The index of the location to set.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
set(index: number, value: number): void;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param array A typed or untyped array of values to set.
|
||||
@@ -3199,13 +3164,6 @@ interface Int32Array {
|
||||
*/
|
||||
reverse(): Int32Array;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param index The index of the location to set.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
set(index: number, value: number): void;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param array A typed or untyped array of values to set.
|
||||
@@ -3490,13 +3448,6 @@ interface Uint32Array {
|
||||
*/
|
||||
reverse(): Uint32Array;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param index The index of the location to set.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
set(index: number, value: number): void;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param array A typed or untyped array of values to set.
|
||||
@@ -3781,13 +3732,6 @@ interface Float32Array {
|
||||
*/
|
||||
reverse(): Float32Array;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param index The index of the location to set.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
set(index: number, value: number): void;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param array A typed or untyped array of values to set.
|
||||
@@ -4073,13 +4017,6 @@ interface Float64Array {
|
||||
*/
|
||||
reverse(): Float64Array;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param index The index of the location to set.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
set(index: number, value: number): void;
|
||||
|
||||
/**
|
||||
* Sets a value or an array of values.
|
||||
* @param array A typed or untyped array of values to set.
|
||||
|
||||
@@ -257,7 +257,7 @@ namespace ts.server {
|
||||
startWatchingContainingDirectoriesForFile(fileName: string, project: InferredProject, callback: (fileName: string) => void) {
|
||||
let currentPath = getDirectoryPath(fileName);
|
||||
let parentPath = getDirectoryPath(currentPath);
|
||||
while (currentPath != parentPath) {
|
||||
while (currentPath !== parentPath) {
|
||||
if (!this.directoryWatchersForTsconfig.has(currentPath)) {
|
||||
this.projectService.logger.info(`Add watcher for: ${currentPath}`);
|
||||
this.directoryWatchersForTsconfig.set(currentPath, this.projectService.host.watchDirectory(currentPath, callback));
|
||||
@@ -618,7 +618,7 @@ namespace ts.server {
|
||||
*/
|
||||
private onConfigFileAddedForInferredProject(fileName: string) {
|
||||
// TODO: check directory separators
|
||||
if (getBaseFileName(fileName) != "tsconfig.json") {
|
||||
if (getBaseFileName(fileName) !== "tsconfig.json") {
|
||||
this.logger.info(`${fileName} is not tsconfig.json`);
|
||||
return;
|
||||
}
|
||||
@@ -787,12 +787,12 @@ namespace ts.server {
|
||||
* we first detect if there is already a configured project created for it: if so, we re-read
|
||||
* the tsconfig file content and update the project; otherwise we create a new one.
|
||||
*/
|
||||
private openOrUpdateConfiguredProjectForFile(fileName: NormalizedPath): OpenConfiguredProjectResult {
|
||||
private openOrUpdateConfiguredProjectForFile(fileName: NormalizedPath, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult {
|
||||
const searchPath = getDirectoryPath(fileName);
|
||||
this.logger.info(`Search path: ${searchPath}`);
|
||||
|
||||
// check if this file is already included in one of external projects
|
||||
const configFileName = this.findConfigFile(asNormalizedPath(searchPath));
|
||||
const configFileName = this.findConfigFile(asNormalizedPath(searchPath), projectRootPath);
|
||||
if (!configFileName) {
|
||||
this.logger.info("No config files found.");
|
||||
return {};
|
||||
@@ -826,8 +826,8 @@ namespace ts.server {
|
||||
// current directory (the directory in which tsc was invoked).
|
||||
// The server must start searching from the directory containing
|
||||
// the newly opened file.
|
||||
private findConfigFile(searchPath: NormalizedPath): NormalizedPath {
|
||||
while (true) {
|
||||
private findConfigFile(searchPath: NormalizedPath, projectRootPath?: NormalizedPath): NormalizedPath {
|
||||
while (!projectRootPath || searchPath.indexOf(projectRootPath) >= 0) {
|
||||
const tsconfigFileName = asNormalizedPath(combinePaths(searchPath, "tsconfig.json"));
|
||||
if (this.host.fileExists(tsconfigFileName)) {
|
||||
return tsconfigFileName;
|
||||
@@ -1027,7 +1027,7 @@ namespace ts.server {
|
||||
const scriptKind = propertyReader.getScriptKind(f);
|
||||
const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions);
|
||||
if (this.host.fileExists(rootFilename)) {
|
||||
const info = this.getOrCreateScriptInfoForNormalizedPath(toNormalizedPath(rootFilename), /*openedByClient*/ clientFileName == rootFilename, /*fileContent*/ undefined, scriptKind, hasMixedContent);
|
||||
const info = this.getOrCreateScriptInfoForNormalizedPath(toNormalizedPath(rootFilename), /*openedByClient*/ clientFileName === rootFilename, /*fileContent*/ undefined, scriptKind, hasMixedContent);
|
||||
project.addRoot(info);
|
||||
}
|
||||
else {
|
||||
@@ -1320,17 +1320,17 @@ namespace ts.server {
|
||||
* @param filename is absolute pathname
|
||||
* @param fileContent is a known version of the file content that is more up to date than the one on disk
|
||||
*/
|
||||
openClientFile(fileName: string, fileContent?: string, scriptKind?: ScriptKind): OpenConfiguredProjectResult {
|
||||
return this.openClientFileWithNormalizedPath(toNormalizedPath(fileName), fileContent, scriptKind);
|
||||
openClientFile(fileName: string, fileContent?: string, scriptKind?: ScriptKind, projectRootPath?: string): OpenConfiguredProjectResult {
|
||||
return this.openClientFileWithNormalizedPath(toNormalizedPath(fileName), fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath ? toNormalizedPath(projectRootPath) : undefined);
|
||||
}
|
||||
|
||||
openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean): OpenConfiguredProjectResult {
|
||||
openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult {
|
||||
let configFileName: NormalizedPath;
|
||||
let configFileErrors: Diagnostic[];
|
||||
|
||||
let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName);
|
||||
if (!project) {
|
||||
({ configFileName, configFileErrors } = this.openOrUpdateConfiguredProjectForFile(fileName));
|
||||
({ configFileName, configFileErrors } = this.openOrUpdateConfiguredProjectForFile(fileName, projectRootPath));
|
||||
if (configFileName) {
|
||||
project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
@@ -643,7 +643,7 @@ namespace ts.server {
|
||||
// check if requested version is the same that we have reported last time
|
||||
if (this.lastReportedFileNames && lastKnownVersion === this.lastReportedVersion) {
|
||||
// if current structure version is the same - return info without any changes
|
||||
if (this.projectStructureVersion == this.lastReportedVersion && !updatedFileNames) {
|
||||
if (this.projectStructureVersion === this.lastReportedVersion && !updatedFileNames) {
|
||||
return { info, projectErrors: this.projectErrors };
|
||||
}
|
||||
// compute and return the difference
|
||||
|
||||
@@ -1045,6 +1045,11 @@ namespace ts.server.protocol {
|
||||
* "TS", "JS", "TSX", "JSX"
|
||||
*/
|
||||
scriptKindName?: ScriptKindName;
|
||||
/**
|
||||
* Used to limit the searching for project config file. If given the searching will stop at this
|
||||
* root path; otherwise it will go all the way up to the dist root path.
|
||||
*/
|
||||
projectRootPath?: string;
|
||||
}
|
||||
|
||||
export type ScriptKindName = "TS" | "JS" | "TSX" | "JSX";
|
||||
@@ -1823,10 +1828,20 @@ namespace ts.server.protocol {
|
||||
*/
|
||||
text: string;
|
||||
|
||||
/**
|
||||
* The category of the diagnostic message, e.g. "error" vs. "warning"
|
||||
*/
|
||||
category: string;
|
||||
|
||||
/**
|
||||
* The error code of the diagnostic message.
|
||||
*/
|
||||
code?: number;
|
||||
|
||||
/**
|
||||
* The name of the plugin reporting the message.
|
||||
*/
|
||||
source?: string;
|
||||
}
|
||||
|
||||
export interface DiagnosticWithFileName extends Diagnostic {
|
||||
@@ -2273,15 +2288,19 @@ namespace ts.server.protocol {
|
||||
allowSyntheticDefaultImports?: boolean;
|
||||
allowUnreachableCode?: boolean;
|
||||
allowUnusedLabels?: boolean;
|
||||
alwaysStrict?: boolean;
|
||||
baseUrl?: string;
|
||||
charset?: string;
|
||||
checkJs?: boolean;
|
||||
declaration?: boolean;
|
||||
declarationDir?: string;
|
||||
disableSizeLimit?: boolean;
|
||||
downlevelIteration?: boolean;
|
||||
emitBOM?: boolean;
|
||||
emitDecoratorMetadata?: boolean;
|
||||
experimentalDecorators?: boolean;
|
||||
forceConsistentCasingInFileNames?: boolean;
|
||||
importHelpers?: boolean;
|
||||
inlineSourceMap?: boolean;
|
||||
inlineSources?: boolean;
|
||||
isolatedModules?: boolean;
|
||||
@@ -2321,6 +2340,7 @@ namespace ts.server.protocol {
|
||||
skipDefaultLibCheck?: boolean;
|
||||
sourceMap?: boolean;
|
||||
sourceRoot?: string;
|
||||
strict?: boolean;
|
||||
strictNullChecks?: boolean;
|
||||
suppressExcessPropertyErrors?: boolean;
|
||||
suppressImplicitAnyIndexErrors?: boolean;
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace ts.server {
|
||||
const lm = LineIndex.linesFromText(insertedText);
|
||||
const lines = lm.lines;
|
||||
if (lines.length > 1) {
|
||||
if (lines[lines.length - 1] == "") {
|
||||
if (lines[lines.length - 1] === "") {
|
||||
lines.length--;
|
||||
}
|
||||
}
|
||||
@@ -570,7 +570,7 @@ namespace ts.server {
|
||||
}
|
||||
if (this.checkEdits) {
|
||||
const updatedText = this.getText(0, this.root.charCount());
|
||||
Debug.assert(checkText == updatedText, "buffer edit mismatch");
|
||||
Debug.assert(checkText === updatedText, "buffer edit mismatch");
|
||||
}
|
||||
return walker.lineIndex;
|
||||
}
|
||||
|
||||
@@ -380,7 +380,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
this.projectService.updateTypingsForProject(response);
|
||||
if (response.kind == ActionSet && this.socket) {
|
||||
if (response.kind === ActionSet && this.socket) {
|
||||
this.sendEvent(0, "setTypings", response);
|
||||
}
|
||||
}
|
||||
@@ -401,7 +401,9 @@ namespace ts.server {
|
||||
byteLength: Buffer.byteLength,
|
||||
hrtime: process.hrtime,
|
||||
logger,
|
||||
canUseEvents});
|
||||
canUseEvents,
|
||||
globalPlugins: options.globalPlugins,
|
||||
pluginProbeLocations: options.pluginProbeLocations});
|
||||
|
||||
if (telemetryEnabled && typingsInstaller) {
|
||||
typingsInstaller.setTelemetrySender(this);
|
||||
@@ -706,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 };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+41
-21
@@ -25,15 +25,26 @@ namespace ts.server {
|
||||
return ((1e9 * seconds) + nanoseconds) / 1000000.0;
|
||||
}
|
||||
|
||||
function shouldSkipSemanticCheck(project: Project) {
|
||||
if (project.projectKind === ProjectKind.Inferred || project.projectKind === ProjectKind.External) {
|
||||
return project.isJsOnlyProject();
|
||||
}
|
||||
else {
|
||||
// For configured projects, require that skipLibCheck be set also
|
||||
const options = project.getCompilerOptions();
|
||||
return options.skipLibCheck && !options.checkJs && project.isJsOnlyProject();
|
||||
function isDeclarationFileInJSOnlyNonConfiguredProject(project: Project, file: NormalizedPath) {
|
||||
// Checking for semantic diagnostics is an expensive process. We want to avoid it if we
|
||||
// know for sure it is not needed.
|
||||
// For instance, .d.ts files injected by ATA automatically do not produce any relevant
|
||||
// errors to a JS- only project.
|
||||
//
|
||||
// Note that configured projects can set skipLibCheck (on by default in jsconfig.json) to
|
||||
// disable checking for declaration files. We only need to verify for inferred projects (e.g.
|
||||
// miscellaneous context in VS) and external projects(e.g.VS.csproj project) with only JS
|
||||
// files.
|
||||
//
|
||||
// We still want to check .js files in a JS-only inferred or external project (e.g. if the
|
||||
// file has '// @ts-check').
|
||||
|
||||
if ((project.projectKind === ProjectKind.Inferred || project.projectKind === ProjectKind.External) &&
|
||||
project.isJsOnlyProject()) {
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
|
||||
return scriptInfo && !scriptInfo.isJavaScript();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
interface FileStart {
|
||||
@@ -49,7 +60,7 @@ namespace ts.server {
|
||||
if (a.file < b.file) {
|
||||
return -1;
|
||||
}
|
||||
else if (a.file == b.file) {
|
||||
else if (a.file === b.file) {
|
||||
const n = compareNumber(a.start.line, b.start.line);
|
||||
if (n === 0) {
|
||||
return compareNumber(a.start.offset, b.start.offset);
|
||||
@@ -67,7 +78,9 @@ namespace ts.server {
|
||||
start: scriptInfo.positionToLineOffset(diag.start),
|
||||
end: scriptInfo.positionToLineOffset(diag.start + diag.length),
|
||||
text: ts.flattenDiagnosticMessageText(diag.messageText, "\n"),
|
||||
code: diag.code
|
||||
code: diag.code,
|
||||
category: DiagnosticCategory[diag.category].toLowerCase(),
|
||||
source: diag.source
|
||||
};
|
||||
}
|
||||
|
||||
@@ -81,7 +94,9 @@ namespace ts.server {
|
||||
end: diag.file && convertToILineInfo(getLineAndCharacterOfPosition(diag.file, diag.start + diag.length)),
|
||||
text: ts.flattenDiagnosticMessageText(diag.messageText, "\n"),
|
||||
code: diag.code,
|
||||
fileName: diag.file && diag.file.fileName
|
||||
category: DiagnosticCategory[diag.category].toLowerCase(),
|
||||
fileName: diag.file && diag.file.fileName,
|
||||
source: diag.source
|
||||
};
|
||||
}
|
||||
|
||||
@@ -520,7 +535,7 @@ namespace ts.server {
|
||||
private semanticCheck(file: NormalizedPath, project: Project) {
|
||||
try {
|
||||
let diags: Diagnostic[] = [];
|
||||
if (!shouldSkipSemanticCheck(project)) {
|
||||
if (!isDeclarationFileInJSOnlyNonConfiguredProject(project, file)) {
|
||||
diags = project.getLanguageService().getSemanticDiagnostics(file);
|
||||
}
|
||||
|
||||
@@ -634,6 +649,7 @@ namespace ts.server {
|
||||
length: d.length,
|
||||
category: DiagnosticCategory[d.category].toLowerCase(),
|
||||
code: d.code,
|
||||
source: d.source,
|
||||
startLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start),
|
||||
endLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start + d.length)
|
||||
});
|
||||
@@ -641,7 +657,7 @@ namespace ts.server {
|
||||
|
||||
private getDiagnosticsWorker(args: protocol.FileRequestArgs, isSemantic: boolean, selector: (project: Project, file: string) => Diagnostic[], includeLinePosition: boolean) {
|
||||
const { project, file } = this.getFileAndProject(args);
|
||||
if (isSemantic && shouldSkipSemanticCheck(project)) {
|
||||
if (isSemantic && isDeclarationFileInJSOnlyNonConfiguredProject(project, file)) {
|
||||
return [];
|
||||
}
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
|
||||
@@ -1023,8 +1039,8 @@ namespace ts.server {
|
||||
* @param fileName is the name of the file to be opened
|
||||
* @param fileContent is a version of the file content that is known to be more up to date than the one on disk
|
||||
*/
|
||||
private openClientFile(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind) {
|
||||
const { configFileName, configFileErrors } = this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind);
|
||||
private openClientFile(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, projectRootPath?: NormalizedPath) {
|
||||
const { configFileName, configFileErrors } = this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath);
|
||||
if (this.eventHandler) {
|
||||
this.eventHandler({
|
||||
eventName: "configFileDiag",
|
||||
@@ -1172,7 +1188,7 @@ namespace ts.server {
|
||||
// getFormattingEditsAfterKeystroke either empty or pertaining
|
||||
// only to the previous line. If all this is true, then
|
||||
// add edits necessary to properly indent the current line.
|
||||
if ((args.key == "\n") && ((!edits) || (edits.length === 0) || allEditsBeforePos(edits, position))) {
|
||||
if ((args.key === "\n") && ((!edits) || (edits.length === 0) || allEditsBeforePos(edits, position))) {
|
||||
const lineInfo = scriptInfo.getLineInfo(args.line);
|
||||
if (lineInfo && (lineInfo.leaf) && (lineInfo.leaf.text)) {
|
||||
const lineText = lineInfo.leaf.text;
|
||||
@@ -1181,10 +1197,10 @@ namespace ts.server {
|
||||
let hasIndent = 0;
|
||||
let i: number, len: number;
|
||||
for (i = 0, len = lineText.length; i < len; i++) {
|
||||
if (lineText.charAt(i) == " ") {
|
||||
if (lineText.charAt(i) === " ") {
|
||||
hasIndent++;
|
||||
}
|
||||
else if (lineText.charAt(i) == "\t") {
|
||||
else if (lineText.charAt(i) === "\t") {
|
||||
hasIndent += formatOptions.tabSize;
|
||||
}
|
||||
else {
|
||||
@@ -1587,7 +1603,7 @@ namespace ts.server {
|
||||
const normalizedFileName = toNormalizedPath(fileName);
|
||||
const project = this.projectService.getDefaultProjectForFile(normalizedFileName, /*refreshInferredProjects*/ true);
|
||||
for (const fileNameInProject of fileNamesInProject) {
|
||||
if (this.getCanonicalFileName(fileNameInProject) == this.getCanonicalFileName(fileName))
|
||||
if (this.getCanonicalFileName(fileNameInProject) === this.getCanonicalFileName(fileName))
|
||||
highPriorityFiles.push(fileNameInProject);
|
||||
else {
|
||||
const info = this.projectService.getScriptInfo(fileNameInProject);
|
||||
@@ -1608,7 +1624,7 @@ namespace ts.server {
|
||||
const checkList = fileNamesInProject.map(fileName => ({ fileName, project }));
|
||||
// Project level error analysis runs on background files too, therefore
|
||||
// doesn't require the file to be opened
|
||||
this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n == this.changeSeq, delay, 200, /*requireOpen*/ false);
|
||||
this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay, 200, /*requireOpen*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1717,7 +1733,11 @@ namespace ts.server {
|
||||
return this.requiredResponse(this.getRenameInfo(request.arguments));
|
||||
},
|
||||
[CommandNames.Open]: (request: protocol.OpenRequest) => {
|
||||
this.openClientFile(toNormalizedPath(request.arguments.file), request.arguments.fileContent, convertScriptKindName(request.arguments.scriptKindName));
|
||||
this.openClientFile(
|
||||
toNormalizedPath(request.arguments.file),
|
||||
request.arguments.fileContent,
|
||||
convertScriptKindName(request.arguments.scriptKindName),
|
||||
request.arguments.projectRootPath ? toNormalizedPath(request.arguments.projectRootPath) : undefined);
|
||||
return this.notRequired();
|
||||
},
|
||||
[CommandNames.Quickinfo]: (request: protocol.QuickInfoRequest) => {
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace ts.server {
|
||||
|
||||
function compilerOptionsChanged(opt1: CompilerOptions, opt2: CompilerOptions): boolean {
|
||||
// TODO: add more relevant properties
|
||||
return opt1.allowJs != opt2.allowJs;
|
||||
return opt1.allowJs !== opt2.allowJs;
|
||||
}
|
||||
|
||||
function unresolvedImportsChanged(imports1: SortedReadonlyArray<string>, imports2: SortedReadonlyArray<string>): boolean {
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace ts.server.typingsInstaller {
|
||||
}
|
||||
|
||||
function getNPMLocation(processName: string) {
|
||||
if (path.basename(processName).indexOf("node") == 0) {
|
||||
if (path.basename(processName).indexOf("node") === 0) {
|
||||
return `"${path.join(path.dirname(process.argv[0]), "npm")}"`;
|
||||
}
|
||||
else {
|
||||
@@ -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()) {
|
||||
|
||||
@@ -219,7 +219,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
public scheduleCollect() {
|
||||
if (!this.host.gc || this.timerId != undefined) {
|
||||
if (!this.host.gc || this.timerId !== undefined) {
|
||||
// no global.gc or collection was already scheduled - skip this request
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace ts.BreakpointResolver {
|
||||
if (isFunctionBlock(node)) {
|
||||
return spanInFunctionBlock(<Block>node);
|
||||
}
|
||||
// Fall through
|
||||
// falls through
|
||||
case SyntaxKind.ModuleBlock:
|
||||
return spanInBlock(<Block>node);
|
||||
|
||||
@@ -186,6 +186,7 @@ namespace ts.BreakpointResolver {
|
||||
if (getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) {
|
||||
return undefined;
|
||||
}
|
||||
// falls through
|
||||
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
@@ -264,7 +265,7 @@ namespace ts.BreakpointResolver {
|
||||
// a or ...c or d: x from
|
||||
// [a, b, ...c] or { a, b } or { d: x } from destructuring pattern
|
||||
if ((node.kind === SyntaxKind.Identifier ||
|
||||
node.kind == SyntaxKind.SpreadElement ||
|
||||
node.kind === SyntaxKind.SpreadElement ||
|
||||
node.kind === SyntaxKind.PropertyAssignment ||
|
||||
node.kind === SyntaxKind.ShorthandPropertyAssignment) &&
|
||||
isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) {
|
||||
@@ -473,6 +474,7 @@ namespace ts.BreakpointResolver {
|
||||
if (getModuleInstanceState(block.parent) !== ModuleInstanceState.Instantiated) {
|
||||
return undefined;
|
||||
}
|
||||
// falls through
|
||||
|
||||
// Set on parent if on same line otherwise on first statement
|
||||
case SyntaxKind.WhileStatement:
|
||||
@@ -582,6 +584,7 @@ namespace ts.BreakpointResolver {
|
||||
if (getModuleInstanceState(node.parent.parent) !== ModuleInstanceState.Instantiated) {
|
||||
return undefined;
|
||||
}
|
||||
// falls through
|
||||
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
@@ -593,7 +596,7 @@ namespace ts.BreakpointResolver {
|
||||
// Span on close brace token
|
||||
return textSpan(node);
|
||||
}
|
||||
// fall through
|
||||
// falls through
|
||||
|
||||
case SyntaxKind.CatchClause:
|
||||
return spanInNode(lastOrUndefined((<Block>node.parent).statements));
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace ts {
|
||||
case EndOfLineState.InTemplateMiddleOrTail:
|
||||
text = "}\n" + text;
|
||||
offset = 2;
|
||||
// fallthrough
|
||||
// falls through
|
||||
case EndOfLineState.InTemplateSubstitutionPosition:
|
||||
templateStack.push(SyntaxKind.TemplateHead);
|
||||
break;
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -14,7 +15,7 @@ namespace ts.codefix {
|
||||
// ^^^^^^^
|
||||
const token = getTokenAtPosition(sourceFile, start);
|
||||
|
||||
if (token.kind != SyntaxKind.Identifier) {
|
||||
if (token.kind !== SyntaxKind.Identifier) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace ts.codefix {
|
||||
|
||||
// figure out if the `this` access is actually inside the supercall
|
||||
// i.e. super(this.a), since in that case we won't suggest a fix
|
||||
if (superCall.expression && superCall.expression.kind == SyntaxKind.CallExpression) {
|
||||
if (superCall.expression && superCall.expression.kind === SyntaxKind.CallExpression) {
|
||||
const arguments = (<CallExpression>superCall.expression).arguments;
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
if ((<PropertyAccessExpression>arguments[i]).expression === token) {
|
||||
|
||||
@@ -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,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" />
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
@@ -42,12 +51,13 @@ namespace ts.codefix {
|
||||
|
||||
switch (this.compareModuleSpecifiers(existingAction.moduleSpecifier, newAction.moduleSpecifier)) {
|
||||
case ModuleSpecifierComparison.Better:
|
||||
// the new one is not worth considering if it is a new improt.
|
||||
// the new one is not worth considering if it is a new import.
|
||||
// However if it is instead a insertion into existing import, the user might want to use
|
||||
// the module specifier even it is worse by our standards. So keep it.
|
||||
if (newAction.kind === "NewImport") {
|
||||
return;
|
||||
}
|
||||
// falls through
|
||||
case ModuleSpecifierComparison.Equal:
|
||||
// the current one is safe. But it is still possible that the new one is worse
|
||||
// than another existing one. For example, you may have new imports from "./foo/bar"
|
||||
@@ -112,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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,140 +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);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -260,7 +260,7 @@ namespace ts.Completions {
|
||||
|
||||
function addStringLiteralCompletionsFromType(type: Type, result: Push<CompletionEntry>, typeChecker: TypeChecker): void {
|
||||
if (type && type.flags & TypeFlags.TypeParameter) {
|
||||
type = typeChecker.getApparentType(type);
|
||||
type = typeChecker.getBaseConstraintOfType(type);
|
||||
}
|
||||
if (!type) {
|
||||
return;
|
||||
@@ -487,7 +487,7 @@ namespace ts.Completions {
|
||||
// It has a left-hand side, so we're not in an opening JSX tag.
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
// falls through
|
||||
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
case SyntaxKind.JsxElement:
|
||||
@@ -1311,7 +1311,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
function isEqualityOperatorKind(kind: SyntaxKind) {
|
||||
return kind == SyntaxKind.EqualsEqualsToken ||
|
||||
return kind === SyntaxKind.EqualsEqualsToken ||
|
||||
kind === SyntaxKind.ExclamationEqualsToken ||
|
||||
kind === SyntaxKind.EqualsEqualsEqualsToken ||
|
||||
kind === SyntaxKind.ExclamationEqualsEqualsToken;
|
||||
|
||||
@@ -233,7 +233,7 @@ namespace ts.DocumentHighlights {
|
||||
if (statement.kind === SyntaxKind.ContinueStatement) {
|
||||
continue;
|
||||
}
|
||||
// Fall through.
|
||||
// falls through
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace ts.FindAllReferences {
|
||||
references: Entry[];
|
||||
}
|
||||
|
||||
type Definition =
|
||||
export type Definition =
|
||||
| { type: "symbol"; symbol: Symbol; node: Node }
|
||||
| { type: "label"; node: Identifier }
|
||||
| { type: "keyword"; node: ts.Node }
|
||||
@@ -20,7 +20,7 @@ namespace ts.FindAllReferences {
|
||||
node: Node;
|
||||
isInString?: true;
|
||||
}
|
||||
interface SpanEntry {
|
||||
export interface SpanEntry {
|
||||
type: "span";
|
||||
fileName: string;
|
||||
textSpan: TextSpan;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1234,7 +1225,7 @@ namespace ts.FindAllReferences.Core {
|
||||
if (isObjectLiteralMethod(searchSpaceNode)) {
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
// falls through
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.Constructor:
|
||||
@@ -1247,7 +1238,7 @@ namespace ts.FindAllReferences.Core {
|
||||
if (isExternalModule(<SourceFile>searchSpaceNode)) {
|
||||
return undefined;
|
||||
}
|
||||
// Fall through
|
||||
// falls through
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
break;
|
||||
|
||||
@@ -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;
|
||||
@@ -483,9 +483,8 @@ namespace ts.formatting {
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
if ((<MethodDeclaration>node).asteriskToken) {
|
||||
return SyntaxKind.AsteriskToken;
|
||||
}/*
|
||||
fall-through
|
||||
*/
|
||||
}
|
||||
// falls through
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.Parameter:
|
||||
return (<Declaration>node).name.kind;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ namespace ts.formatting {
|
||||
let current = position;
|
||||
while (current > 0) {
|
||||
const char = sourceFile.text.charCodeAt(current);
|
||||
if (!isWhiteSpace(char)) {
|
||||
if (!isWhiteSpaceLike(char)) {
|
||||
break;
|
||||
}
|
||||
current--;
|
||||
@@ -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);
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/* @internal */
|
||||
namespace ts.GoToImplementation {
|
||||
export function getImplementationAtPosition(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], node: Node): ImplementationLocation[] {
|
||||
const context = new FindAllReferences.FindImplementationsContext(typeChecker, cancellationToken);
|
||||
// If invoked directly on a shorthand property assignment, then return
|
||||
// the declaration of the symbol being assigned (not the symbol being assigned to).
|
||||
if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
const result: ImplementationLocation[] = [];
|
||||
FindAllReferences.getReferenceEntriesForShorthandPropertyAssignment(node, context, result);
|
||||
return result.length > 0 ? result : undefined;
|
||||
}
|
||||
else if (node.kind === SyntaxKind.SuperKeyword || isSuperProperty(node.parent)) {
|
||||
// References to and accesses on the super keyword only have one possible implementation, so no
|
||||
// need to "Find all References"
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
return symbol.valueDeclaration && [context.getReferenceEntryFromNode(symbol.valueDeclaration)];
|
||||
}
|
||||
else {
|
||||
// Perform "Find all References" and retrieve only those that are implementations
|
||||
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(context,
|
||||
node, sourceFiles, /*findInStrings*/ false, /*findInComments*/ false, /*isForRename*/ false, /*implementations*/ true);
|
||||
const result = flatMap(referencedSymbols, symbol => symbol.references);
|
||||
|
||||
return result && result.length > 0 ? result : undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,6 +212,10 @@ namespace ts.FindAllReferences {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decl.importClause) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { importClause } = decl;
|
||||
|
||||
const { namedBindings } = importClause;
|
||||
@@ -387,6 +391,7 @@ namespace ts.FindAllReferences {
|
||||
symbol: Symbol;
|
||||
exportInfo: ExportInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a local reference, we might notice that it's an import/export and recursively search for references of that.
|
||||
* If at an import, look locally for the symbol it imports.
|
||||
@@ -398,7 +403,7 @@ namespace ts.FindAllReferences {
|
||||
return comingFromExport ? getExport() : getExport() || getImport();
|
||||
|
||||
function getExport(): ExportedSymbol | ImportedSymbol | undefined {
|
||||
const { parent } = node;
|
||||
const parent = node.parent!;
|
||||
if (symbol.flags & SymbolFlags.Export) {
|
||||
if (parent.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
// When accessing an export of a JS module, there's no alias. The symbol will still be flagged as an export even though we're at the use.
|
||||
@@ -414,8 +419,8 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
else {
|
||||
const exportNode = parent.kind === SyntaxKind.VariableDeclaration ? getAncestor(parent, SyntaxKind.VariableStatement) : parent;
|
||||
if (hasModifier(exportNode, ModifierFlags.Export)) {
|
||||
const exportNode = getExportNode(parent);
|
||||
if (exportNode && hasModifier(exportNode, ModifierFlags.Export)) {
|
||||
if (exportNode.kind === SyntaxKind.ImportEqualsDeclaration && (exportNode as ImportEqualsDeclaration).moduleReference === node) {
|
||||
// We're at `Y` in `export import X = Y`. This is not the exported symbol, the left-hand-side is. So treat this as an import statement.
|
||||
if (comingFromExport) {
|
||||
@@ -492,6 +497,16 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
// If a reference is a variable declaration, the exported node would be the variable statement.
|
||||
function getExportNode(parent: Node): Node | undefined {
|
||||
if (parent.kind === SyntaxKind.VariableDeclaration) {
|
||||
const p = parent as ts.VariableDeclaration;
|
||||
return p.parent.kind === ts.SyntaxKind.CatchClause ? undefined : p.parent.parent.kind === SyntaxKind.VariableStatement ? p.parent.parent : undefined;
|
||||
} else {
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
|
||||
function isNodeImport(node: Node): { isNamedImport: boolean } | undefined {
|
||||
const { parent } = node;
|
||||
switch (parent.kind) {
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace ts.OutliningElementsCollector {
|
||||
});
|
||||
break;
|
||||
}
|
||||
// Fallthrough.
|
||||
// falls through
|
||||
|
||||
case SyntaxKind.ModuleBlock: {
|
||||
const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
|
||||
@@ -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) :
|
||||
@@ -659,7 +662,7 @@ namespace ts {
|
||||
if (!hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
// falls through
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.BindingElement: {
|
||||
const decl = <VariableDeclaration>node;
|
||||
@@ -670,6 +673,7 @@ namespace ts {
|
||||
if (decl.initializer)
|
||||
visit(decl.initializer);
|
||||
}
|
||||
// falls through
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
@@ -1039,10 +1043,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();
|
||||
@@ -1071,11 +1074,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;
|
||||
}
|
||||
@@ -2091,7 +2089,7 @@ namespace ts {
|
||||
if (node.parent.kind === SyntaxKind.ComputedPropertyName) {
|
||||
return isObjectLiteralElement(node.parent.parent) ? node.parent.parent : undefined;
|
||||
}
|
||||
// intentionally fall through
|
||||
// falls through
|
||||
case SyntaxKind.Identifier:
|
||||
return isObjectLiteralElement(node.parent) &&
|
||||
(node.parent.parent.kind === SyntaxKind.ObjectLiteralExpression || node.parent.parent.kind === SyntaxKind.JsxAttributes) &&
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace ts {
|
||||
*
|
||||
* Or undefined value if there was no change.
|
||||
*/
|
||||
getChangeRange(oldSnapshot: ScriptSnapshotShim): string;
|
||||
getChangeRange(oldSnapshot: ScriptSnapshotShim): string | undefined;
|
||||
|
||||
/** Releases all resources held by this script snapshot */
|
||||
dispose?(): void;
|
||||
@@ -292,8 +292,7 @@ namespace ts {
|
||||
public getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange {
|
||||
const oldSnapshotShim = <ScriptSnapshotShimAdapter>oldSnapshot;
|
||||
const encoded = this.scriptSnapshotShim.getChangeRange(oldSnapshotShim.scriptSnapshotShim);
|
||||
// TODO: should this be '==='?
|
||||
if (encoded == null) {
|
||||
if (encoded === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -381,8 +380,7 @@ namespace ts {
|
||||
|
||||
public getCompilationSettings(): CompilerOptions {
|
||||
const settingsJson = this.shimHost.getCompilationSettings();
|
||||
// TODO: should this be '==='?
|
||||
if (settingsJson == null || settingsJson == "") {
|
||||
if (settingsJson === null || settingsJson === "") {
|
||||
throw Error("LanguageServiceShimHostAdapter.getCompilationSettings: empty compilationSettings");
|
||||
}
|
||||
const compilerOptions = <CompilerOptions>JSON.parse(settingsJson);
|
||||
@@ -416,7 +414,7 @@ namespace ts {
|
||||
|
||||
public getLocalizedDiagnosticMessages(): any {
|
||||
const diagnosticMessagesJson = this.shimHost.getLocalizedDiagnosticMessages();
|
||||
if (diagnosticMessagesJson == null || diagnosticMessagesJson == "") {
|
||||
if (diagnosticMessagesJson === null || diagnosticMessagesJson === "") {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1246,4 +1244,4 @@ namespace TypeScript.Services {
|
||||
// TODO: it should be moved into a namespace though.
|
||||
|
||||
/* @internal */
|
||||
const toolsVersion = "2.3";
|
||||
const toolsVersion = "2.4";
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -465,7 +465,7 @@ namespace ts.textChanges {
|
||||
change.options.indentation !== undefined
|
||||
? change.options.indentation
|
||||
: change.useIndentationFromFile
|
||||
? formatting.SmartIndenter.getIndentation(change.range.pos, sourceFile, formatOptions, posStartsLine || (change.options.prefix == this.newLineCharacter))
|
||||
? formatting.SmartIndenter.getIndentation(change.range.pos, sourceFile, formatOptions, posStartsLine || (change.options.prefix === this.newLineCharacter))
|
||||
: 0;
|
||||
const delta =
|
||||
change.options.delta !== undefined
|
||||
@@ -608,7 +608,7 @@ namespace ts.textChanges {
|
||||
if (force || !isTrivia(s)) {
|
||||
this.lastNonTriviaPosition = this.writer.getTextPos();
|
||||
let i = 0;
|
||||
while (isWhiteSpace(s.charCodeAt(s.length - i - 1))) {
|
||||
while (isWhiteSpaceLike(s.charCodeAt(s.length - i - 1))) {
|
||||
i++;
|
||||
}
|
||||
// trim trailing whitespaces
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
"formatting/tokenRange.ts",
|
||||
"codeFixProvider.ts",
|
||||
"codefixes/fixAddMissingMember.ts",
|
||||
"codefixes/fixSpelling.ts",
|
||||
"codefixes/fixExtendsInterfaceBecomesImplements.ts",
|
||||
"codefixes/fixClassIncorrectlyImplementsInterface.ts",
|
||||
"codefixes/fixClassDoesntImplementInheritedAbstractMember.ts",
|
||||
@@ -95,4 +96,4 @@
|
||||
"codefixes/unusedIdentifierFixes.ts",
|
||||
"codefixes/disableJsDiagnostics.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,7 +441,7 @@ namespace ts {
|
||||
if (!(<NewExpression>n).arguments) {
|
||||
return true;
|
||||
}
|
||||
// fall through
|
||||
// falls through
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
@@ -902,10 +902,10 @@ namespace ts {
|
||||
// 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) &&
|
||||
(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));
|
||||
(c.kind === SyntaxKind.SingleLineCommentTrivia ? position <= c.end : position < c.end));
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -1036,6 +1036,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function compareDataObjects(dst: any, src: any, ignoreKey?: string): boolean {
|
||||
if (!dst || !src || Object.keys(dst).length !== Object.keys(src).length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const e in dst) {
|
||||
if (ignoreKey && ignoreKey === e) {
|
||||
continue;
|
||||
@@ -1360,7 +1364,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function getFirstNonSpaceCharacterPosition(text: string, position: number) {
|
||||
while (isWhiteSpace(text.charCodeAt(position))) {
|
||||
while (isWhiteSpaceLike(text.charCodeAt(position))) {
|
||||
position += 1;
|
||||
}
|
||||
return position;
|
||||
|
||||
Reference in New Issue
Block a user