Merge remote-tracking branch 'origin/master' into useReturnedThisFromSuperCalls

This commit is contained in:
Daniel Rosenwasser 2016-09-29 20:33:42 -07:00
commit 02b9917cd8
120 changed files with 158516 additions and 83037 deletions

View File

@ -418,9 +418,35 @@ gulp.task(servicesFile, false, ["lib", "generate-diagnostics"], () => {
]).pipe(gulp.dest(builtLocalDirectory));
});
// cancellationToken.js
const cancellationTokenJs = path.join(builtLocalDirectory, "cancellationToken.js");
gulp.task(cancellationTokenJs, false, [servicesFile], () => {
const cancellationTokenProject = tsc.createProject("src/server/cancellationToken/tsconfig.json", getCompilerSettings({}, /*useBuiltCompiler*/true));
return cancellationTokenProject.src()
.pipe(newer(cancellationTokenJs))
.pipe(sourcemaps.init())
.pipe(tsc(cancellationTokenProject))
.pipe(prependCopyright())
.pipe(sourcemaps.write("."))
.pipe(gulp.dest(builtLocalDirectory));
});
// typingsInstallerFile.js
const typingsInstallerJs = path.join(builtLocalDirectory, "typingsInstaller.js");
gulp.task(typingsInstallerJs, false, [servicesFile], () => {
const cancellationTokenProject = tsc.createProject("src/server/typingsInstaller/tsconfig.json", getCompilerSettings({}, /*useBuiltCompiler*/true));
return cancellationTokenProject.src()
.pipe(newer(typingsInstallerJs))
.pipe(sourcemaps.init())
.pipe(tsc(cancellationTokenProject))
.pipe(prependCopyright())
.pipe(sourcemaps.write("."))
.pipe(gulp.dest(builtLocalDirectory));
});
const serverFile = path.join(builtLocalDirectory, "tsserver.js");
gulp.task(serverFile, false, [servicesFile], () => {
gulp.task(serverFile, false, [servicesFile, typingsInstallerJs, cancellationTokenJs], () => {
const serverProject = tsc.createProject("src/server/tsconfig.json", getCompilerSettings({}, /*useBuiltCompiler*/true));
return serverProject.src()
.pipe(newer(serverFile))
@ -454,7 +480,6 @@ gulp.task("lssl", "Builds language service server library", [tsserverLibraryFile
gulp.task("local", "Builds the full compiler and services", [builtLocalCompiler, servicesFile, serverFile, builtGeneratedDiagnosticMessagesJSON, tsserverLibraryFile]);
gulp.task("tsc", "Builds only the compiler", [builtLocalCompiler]);
// Generate Markdown spec
const word2mdJs = path.join(scriptsDirectory, "word2md.js");
const word2mdTs = path.join(scriptsDirectory, "word2md.ts");
@ -493,7 +518,7 @@ gulp.task("useDebugMode", false, [], (done) => { useDebugMode = true; done(); })
gulp.task("dontUseDebugMode", false, [], (done) => { useDebugMode = false; done(); });
gulp.task("VerifyLKG", false, [], () => {
const expectedFiles = [builtLocalCompiler, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile].concat(libraryTargets);
const expectedFiles = [builtLocalCompiler, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile, typingsInstallerJs, cancellationTokenJs].concat(libraryTargets);
const missingFiles = expectedFiles.filter(function(f) {
return !fs.existsSync(f);
});

View File

@ -11,6 +11,8 @@ var runTestsInParallel = require("./scripts/mocha-parallel").runTestsInParallel;
var compilerDirectory = "src/compiler/";
var servicesDirectory = "src/services/";
var serverDirectory = "src/server/";
var typingsInstallerDirectory = "src/server/typingsInstaller";
var cancellationTokenDirectory = "src/server/cancellationToken";
var harnessDirectory = "src/harness/";
var libraryDirectory = "src/lib/";
var scriptsDirectory = "scripts/";
@ -164,6 +166,13 @@ var servicesSources = [
}));
var serverCoreSources = [
"types.d.ts",
"utilities.ts",
"scriptVersionCache.ts",
"typingsCache.ts",
"scriptInfo.ts",
"lsHost.ts",
"project.ts",
"editorServices.ts",
"protocol.d.ts",
"session.ts",
@ -172,12 +181,33 @@ var serverCoreSources = [
return path.join(serverDirectory, f);
});
var cancellationTokenSources = [
"cancellationToken.ts"
].map(function (f) {
return path.join(cancellationTokenDirectory, f);
});
var typingsInstallerSources = [
"../types.d.ts",
"typingsInstaller.ts",
"nodeTypingsInstaller.ts"
].map(function (f) {
return path.join(typingsInstallerDirectory, f);
});
var serverSources = serverCoreSources.concat(servicesSources);
var languageServiceLibrarySources = [
"protocol.d.ts",
"utilities.ts",
"scriptVersionCache.ts",
"scriptInfo.ts",
"lsHost.ts",
"project.ts",
"editorServices.ts",
"protocol.d.ts",
"session.ts"
"session.ts",
].map(function (f) {
return path.join(serverDirectory, f);
}).concat(servicesSources);
@ -221,15 +251,24 @@ var harnessSources = harnessCoreSources.concat([
"convertCompilerOptionsFromJson.ts",
"convertTypingOptionsFromJson.ts",
"tsserverProjectSystem.ts",
"compileOnSave.ts",
"typingsInstaller.ts",
"projectErrors.ts",
"matchFiles.ts",
"initializeTSConfig.ts",
].map(function (f) {
return path.join(unittestsDirectory, f);
})).concat([
"protocol.d.ts",
"utilities.ts",
"scriptVersionCache.ts",
"scriptInfo.ts",
"lsHost.ts",
"project.ts",
"typingsCache.ts",
"editorServices.ts",
"protocol.d.ts",
"session.ts",
"client.ts",
"editorServices.ts"
].map(function (f) {
return path.join(serverDirectory, f);
}));
@ -616,8 +655,14 @@ compileFile(
inlineSourceMap: true
});
var cancellationTokenFile = path.join(builtLocalDirectory, "cancellationToken.js");
compileFile(cancellationTokenFile, cancellationTokenSources, [builtLocalDirectory].concat(cancellationTokenSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { outDir: builtLocalDirectory, noOutFile: true });
var typingsInstallerFile = path.join(builtLocalDirectory, "typingsInstaller.js");
compileFile(typingsInstallerFile, typingsInstallerSources, [builtLocalDirectory].concat(typingsInstallerSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { outDir: builtLocalDirectory, noOutFile: false });
var serverFile = path.join(builtLocalDirectory, "tsserver.js");
compileFile(serverFile, serverSources, [builtLocalDirectory, copyright].concat(serverSources), /*prefixes*/[copyright], /*useBuiltCompiler*/ true, { types: ["node"] });
compileFile(serverFile, serverSources, [builtLocalDirectory, copyright, cancellationTokenFile, typingsInstallerFile].concat(serverSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"] });
var tsserverLibraryFile = path.join(builtLocalDirectory, "tsserverlibrary.js");
var tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibrary.d.ts");
compileFile(
@ -700,7 +745,7 @@ task("generate-spec", [specMd]);
// Makes a new LKG. This target does not build anything, but errors if not all the outputs are present in the built/local directory
desc("Makes a new LKG out of the built js files");
task("LKG", ["clean", "release", "local"].concat(libraryTargets), function () {
var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile].concat(libraryTargets);
var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile, cancellationTokenFile, typingsInstallerFile].concat(libraryTargets);
var missingFiles = expectedFiles.filter(function (f) {
return !fs.existsSync(f);
});
@ -948,7 +993,7 @@ desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is
task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFileInBrowserTest], function () {
cleanTestDirs();
host = "node";
browser = process.env.browser || process.env.b || "IE";
browser = process.env.browser || process.env.b || (os.platform() === "linux" ? "chrome" : "IE");
tests = process.env.test || process.env.tests || process.env.t;
var light = process.env.light || false;
var testConfigFile = 'test.config';
@ -1130,6 +1175,8 @@ var lintTargets = compilerSources
.concat(serverCoreSources)
.concat(tslintRulesFiles)
.concat(servicesSources)
.concat(typingsInstallerSources)
.concat(cancellationTokenSources)
.concat(["Gulpfile.ts"])
.concat([nodeServerInFile, perftscPath, "tests/perfsys.ts", webhostPath]);

41
lib/cancellationToken.js Normal file
View File

@ -0,0 +1,41 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
"use strict";
var fs = require("fs");
function createCancellationToken(args) {
var cancellationPipeName;
for (var i = 0; i < args.length - 1; i++) {
if (args[i] === "--cancellationPipeName") {
cancellationPipeName = args[i + 1];
break;
}
}
if (!cancellationPipeName) {
return { isCancellationRequested: function () { return false; } };
}
return {
isCancellationRequested: function () {
try {
fs.statSync(cancellationPipeName);
return true;
}
catch (e) {
return false;
}
}
};
}
module.exports = createCancellationToken;

258
lib/lib.d.ts vendored
View File

@ -529,8 +529,8 @@ interface NumberConstructor {
/** An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers. */
declare const Number: NumberConstructor;
interface TemplateStringsArray extends Array<string> {
readonly raw: string[];
interface TemplateStringsArray extends ReadonlyArray<string> {
readonly raw: ReadonlyArray<string>
}
interface Math {
@ -1022,7 +1022,12 @@ interface ReadonlyArray<T> {
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: T[]): T[];
concat(...items: T[][]): T[];
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: (T | T[])[]): T[];
/**
* Adds all the elements of an array separated by the specified separator string.
* @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma.
@ -1071,6 +1076,12 @@ interface ReadonlyArray<T> {
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
map<U>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => U, thisArg?: any): U[];
/**
* Returns the elements of an array that meet the condition specified in a callback function.
* @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
filter<S extends T>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => value is S, thisArg?: any): S[];
/**
* Returns the elements of an array that meet the condition specified in a callback function.
* @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array.
@ -1124,6 +1135,11 @@ interface Array<T> {
* Removes the last element from an array and returns it.
*/
pop(): T | undefined;
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: T[][]): T[];
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
@ -1271,13 +1287,44 @@ declare type PromiseConstructorLike = new <T>(executor: (resolve: (value?: T | P
interface PromiseLike<T> {
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => TResult | PromiseLike<TResult>): PromiseLike<TResult>;
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => void): PromiseLike<TResult>;
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then(
onfulfilled?: ((value: T) => T | PromiseLike<T>) | undefined | null,
onrejected?: ((reason: any) => T | PromiseLike<T>) | undefined | null): PromiseLike<T>;
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(
onfulfilled: ((value: T) => T | PromiseLike<T>) | undefined | null,
onrejected: (reason: any) => TResult | PromiseLike<TResult>): PromiseLike<T | TResult>;
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(
onfulfilled: (value: T) => TResult | PromiseLike<TResult>,
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): PromiseLike<TResult>;
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult1, TResult2>(
onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>,
onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): PromiseLike<TResult1 | TResult2>;
}
interface ArrayLike<T> {
@ -1537,7 +1584,7 @@ interface Int8Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -1545,7 +1592,7 @@ interface Int8Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -1810,7 +1857,7 @@ interface Uint8Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -1818,7 +1865,7 @@ interface Uint8Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -2084,7 +2131,7 @@ interface Uint8ClampedArray {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -2092,7 +2139,7 @@ interface Uint8ClampedArray {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -2357,7 +2404,7 @@ interface Int16Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -2365,7 +2412,7 @@ interface Int16Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -2631,7 +2678,7 @@ interface Uint16Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -2639,7 +2686,7 @@ interface Uint16Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -2904,7 +2951,7 @@ interface Int32Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -2912,7 +2959,7 @@ interface Int32Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -3177,7 +3224,7 @@ interface Uint32Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -3185,7 +3232,7 @@ interface Uint32Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -3450,7 +3497,7 @@ interface Float32Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -3458,7 +3505,7 @@ interface Float32Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -3724,7 +3771,7 @@ interface Float64Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -3732,7 +3779,7 @@ interface Float64Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -3949,12 +3996,9 @@ declare module Intl {
resolvedOptions(): ResolvedCollatorOptions;
}
var Collator: {
new (locales?: string[], options?: CollatorOptions): Collator;
new (locale?: string, options?: CollatorOptions): Collator;
(locales?: string[], options?: CollatorOptions): Collator;
(locale?: string, options?: CollatorOptions): Collator;
supportedLocalesOf(locales: string[], options?: CollatorOptions): string[];
supportedLocalesOf(locale: string, options?: CollatorOptions): string[];
new (locales?: string | string[], options?: CollatorOptions): Collator;
(locales?: string | string[], options?: CollatorOptions): Collator;
supportedLocalesOf(locales: string | string[], options?: CollatorOptions): string[];
}
interface NumberFormatOptions {
@ -3989,12 +4033,9 @@ declare module Intl {
resolvedOptions(): ResolvedNumberFormatOptions;
}
var NumberFormat: {
new (locales?: string[], options?: NumberFormatOptions): NumberFormat;
new (locale?: string, options?: NumberFormatOptions): NumberFormat;
(locales?: string[], options?: NumberFormatOptions): NumberFormat;
(locale?: string, options?: NumberFormatOptions): NumberFormat;
supportedLocalesOf(locales: string[], options?: NumberFormatOptions): string[];
supportedLocalesOf(locale: string, options?: NumberFormatOptions): string[];
new (locales?: string | string[], options?: NumberFormatOptions): NumberFormat;
(locales?: string | string[], options?: NumberFormatOptions): NumberFormat;
supportedLocalesOf(locales: string | string[], options?: NumberFormatOptions): string[];
}
interface DateTimeFormatOptions {
@ -4035,90 +4076,51 @@ declare module Intl {
resolvedOptions(): ResolvedDateTimeFormatOptions;
}
var DateTimeFormat: {
new (locales?: string[], options?: DateTimeFormatOptions): DateTimeFormat;
new (locale?: string, options?: DateTimeFormatOptions): DateTimeFormat;
(locales?: string[], options?: DateTimeFormatOptions): DateTimeFormat;
(locale?: string, options?: DateTimeFormatOptions): DateTimeFormat;
supportedLocalesOf(locales: string[], options?: DateTimeFormatOptions): string[];
supportedLocalesOf(locale: string, options?: DateTimeFormatOptions): string[];
new (locales?: string | string[], options?: DateTimeFormatOptions): DateTimeFormat;
(locales?: string | string[], options?: DateTimeFormatOptions): DateTimeFormat;
supportedLocalesOf(locales: string | string[], options?: DateTimeFormatOptions): string[];
}
}
interface String {
/**
* Determines whether two strings are equivalent in the current locale.
* Determines whether two strings are equivalent in the current or specified locale.
* @param that String to compare to target string
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.
* @param options An object that contains one or more properties that specify comparison options. see the Intl.Collator object for details.
*/
localeCompare(that: string, locales: string[], options?: Intl.CollatorOptions): number;
/**
* Determines whether two strings are equivalent in the current locale.
* @param that String to compare to target string
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.
* @param options An object that contains one or more properties that specify comparison options. see the Intl.Collator object for details.
*/
localeCompare(that: string, locale: string, options?: Intl.CollatorOptions): number;
localeCompare(that: string, locales?: string | string[], options?: Intl.CollatorOptions): number;
}
interface Number {
/**
* Converts a number to a string by using the current or specified locale.
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleString(locales?: string[], options?: Intl.NumberFormatOptions): string;
/**
* Converts a number to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleString(locale?: string, options?: Intl.NumberFormatOptions): string;
toLocaleString(locales?: string | string[], options?: Intl.NumberFormatOptions): string;
}
interface Date {
/**
* Converts a date and time to a string by using the current or specified locale.
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleString(locales?: string[], options?: Intl.DateTimeFormatOptions): string;
toLocaleString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a date to a string by using the current or specified locale.
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleDateString(locales?: string[], options?: Intl.DateTimeFormatOptions): string;
toLocaleDateString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a time to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleTimeString(locale?: string[], options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a date and time to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleString(locale?: string, options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a date to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleDateString(locale?: string, options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a time to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleTimeString(locale?: string, options?: Intl.DateTimeFormatOptions): string;
toLocaleTimeString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
}
/////////////////////////////
@ -4248,6 +4250,7 @@ interface KeyAlgorithm {
}
interface KeyboardEventInit extends EventModifierInit {
code?: string;
key?: string;
location?: number;
repeat?: boolean;
@ -6410,7 +6413,7 @@ declare var DeviceRotationRate: {
new(): DeviceRotationRate;
}
interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEvent {
interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEvent, ParentNode {
/**
* Sets or gets the URL for the current document.
*/
@ -7473,7 +7476,7 @@ declare var Document: {
new(): Document;
}
interface DocumentFragment extends Node, NodeSelector {
interface DocumentFragment extends Node, NodeSelector, ParentNode {
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
@ -7542,7 +7545,7 @@ declare var EXT_texture_filter_anisotropic: {
readonly TEXTURE_MAX_ANISOTROPY_EXT: number;
}
interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelector, ChildNode {
interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelector, ChildNode, ParentNode {
readonly classList: DOMTokenList;
className: string;
readonly clientHeight: number;
@ -7797,6 +7800,16 @@ interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelec
getElementsByClassName(classNames: string): NodeListOf<Element>;
matches(selector: string): boolean;
closest(selector: string): Element | null;
scrollIntoView(arg?: boolean | ScrollIntoViewOptions): void;
scroll(options?: ScrollToOptions): void;
scroll(x: number, y: number): void;
scrollTo(options?: ScrollToOptions): void;
scrollTo(x: number, y: number): void;
scrollBy(options?: ScrollToOptions): void;
scrollBy(x: number, y: number): void;
insertAdjacentElement(position: string, insertedElement: Element): Element | null;
insertAdjacentHTML(where: string, html: string): void;
insertAdjacentText(where: string, text: string): void;
addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureDoubleTap", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureEnd", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
@ -8568,7 +8581,7 @@ interface HTMLCanvasElement extends HTMLElement {
* @param type The standard MIME type for the image format to return. If you do not specify this parameter, the default value is a PNG format image.
*/
toDataURL(type?: string, ...args: any[]): string;
toBlob(callback: (result: Blob | null) => void, ... arguments: any[]): void;
toBlob(callback: (result: Blob | null) => void, type?: string, ...arguments: any[]): void;
}
declare var HTMLCanvasElement: {
@ -8743,11 +8756,7 @@ interface HTMLElement extends Element {
click(): void;
dragDrop(): boolean;
focus(): void;
insertAdjacentElement(position: string, insertedElement: Element): Element;
insertAdjacentHTML(where: string, html: string): void;
insertAdjacentText(where: string, text: string): void;
msGetInputContext(): MSInputMethodContext;
scrollIntoView(top?: boolean): void;
setActive(): void;
addEventListener(type: "MSContentZoom", listener: (this: this, ev: UIEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
@ -10012,6 +10021,7 @@ interface HTMLLinkElement extends HTMLElement, LinkStyle {
*/
type: string;
import?: Document;
integrity: string;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
@ -10300,7 +10310,7 @@ interface HTMLMediaElement extends HTMLElement {
*/
canPlayType(type: string): string;
/**
* Fires immediately after the client loads the object.
* Resets the audio or video object and loads a new media resource.
*/
load(): void;
/**
@ -10873,6 +10883,7 @@ interface HTMLScriptElement extends HTMLElement {
* Sets or retrieves the MIME type for the associated scripting engine.
*/
type: string;
integrity: string;
}
declare var HTMLScriptElement: {
@ -11878,6 +11889,7 @@ interface KeyboardEvent extends UIEvent {
readonly repeat: boolean;
readonly shiftKey: boolean;
readonly which: number;
readonly code: string;
getModifierState(keyArg: string): boolean;
initKeyboardEvent(typeArg: string, canBubbleArg: boolean, cancelableArg: boolean, viewArg: Window, keyArg: string, locationArg: number, modifiersListArg: string, repeat: boolean, locale: string): void;
readonly DOM_KEY_LOCATION_JOYSTICK: number;
@ -13250,6 +13262,7 @@ interface PerformanceTiming {
readonly responseStart: number;
readonly unloadEventEnd: number;
readonly unloadEventStart: number;
readonly secureConnectionStart: number;
toJSON(): any;
}
@ -15527,8 +15540,8 @@ declare var StereoPannerNode: {
interface Storage {
readonly length: number;
clear(): void;
getItem(key: string): string;
key(index: number): string;
getItem(key: string): string | null;
key(index: number): string | null;
removeItem(key: string): void;
setItem(key: string, data: string): void;
[key: string]: any;
@ -17069,7 +17082,7 @@ interface Window extends EventTarget, WindowTimers, WindowSessionStorage, Window
onunload: (this: this, ev: Event) => any;
onvolumechange: (this: this, ev: Event) => any;
onwaiting: (this: this, ev: Event) => any;
readonly opener: Window;
opener: any;
orientation: string | number;
readonly outerHeight: number;
readonly outerWidth: number;
@ -17124,6 +17137,9 @@ interface Window extends EventTarget, WindowTimers, WindowSessionStorage, Window
webkitConvertPointFromNodeToPage(node: Node, pt: WebKitPoint): WebKitPoint;
webkitConvertPointFromPageToNode(node: Node, pt: WebKitPoint): WebKitPoint;
webkitRequestAnimationFrame(callback: FrameRequestCallback): number;
scroll(options?: ScrollToOptions): void;
scrollTo(options?: ScrollToOptions): void;
scrollBy(options?: ScrollToOptions): void;
addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureDoubleTap", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureEnd", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
@ -18151,6 +18167,20 @@ interface ProgressEventInit extends EventInit {
total?: number;
}
interface ScrollOptions {
behavior?: ScrollBehavior;
}
interface ScrollToOptions extends ScrollOptions {
left?: number;
top?: number;
}
interface ScrollIntoViewOptions extends ScrollOptions {
block?: ScrollLogicalPosition;
inline?: ScrollLogicalPosition;
}
interface ClipboardEventInit extends EventInit {
data?: string;
dataType?: string;
@ -18194,7 +18224,7 @@ interface EcdsaParams extends Algorithm {
}
interface EcKeyGenParams extends Algorithm {
typedCurve: string;
namedCurve: string;
}
interface EcKeyAlgorithm extends KeyAlgorithm {
@ -18330,6 +18360,13 @@ interface JsonWebKey {
k?: string;
}
interface ParentNode {
readonly children: HTMLCollection;
readonly firstElementChild: Element;
readonly lastElementChild: Element;
readonly childElementCount: number;
}
declare type EventListenerOrEventListenerObject = EventListener | EventListenerObject;
interface ErrorEventHandler {
@ -18400,7 +18437,7 @@ declare var location: Location;
declare var locationbar: BarProp;
declare var menubar: BarProp;
declare var msCredentials: MSCredentials;
declare var name: string;
declare const name: never;
declare var navigator: Navigator;
declare var offscreenBuffering: string | boolean;
declare var onabort: (this: Window, ev: UIEvent) => any;
@ -18494,7 +18531,7 @@ declare var ontouchstart: (ev: TouchEvent) => any;
declare var onunload: (this: Window, ev: Event) => any;
declare var onvolumechange: (this: Window, ev: Event) => any;
declare var onwaiting: (this: Window, ev: Event) => any;
declare var opener: Window;
declare var opener: any;
declare var orientation: string | number;
declare var outerHeight: number;
declare var outerWidth: number;
@ -18547,6 +18584,9 @@ declare function webkitCancelAnimationFrame(handle: number): void;
declare function webkitConvertPointFromNodeToPage(node: Node, pt: WebKitPoint): WebKitPoint;
declare function webkitConvertPointFromPageToNode(node: Node, pt: WebKitPoint): WebKitPoint;
declare function webkitRequestAnimationFrame(callback: FrameRequestCallback): number;
declare function scroll(options?: ScrollToOptions): void;
declare function scrollTo(options?: ScrollToOptions): void;
declare function scrollBy(options?: ScrollToOptions): void;
declare function toString(): string;
declare function addEventListener(type: string, listener?: EventListenerOrEventListenerObject, useCapture?: boolean): void;
declare function dispatchEvent(evt: Event): boolean;
@ -18702,6 +18742,8 @@ type MSOutboundPayload = MSVideoSendPayload | MSAudioSendPayload;
type RTCIceGatherCandidate = RTCIceCandidate | RTCIceCandidateComplete;
type RTCTransport = RTCDtlsTransport | RTCSrtpSdesTransport;
type payloadtype = number;
type ScrollBehavior = "auto" | "instant" | "smooth";
type ScrollLogicalPosition = "start" | "center" | "end" | "nearest";
type IDBValidKey = number | string | Date | IDBArrayKey;
type BufferSource = ArrayBuffer | ArrayBufferView;
type MouseWheelEvent = WheelEvent;

70
lib/lib.dom.d.ts vendored
View File

@ -142,6 +142,7 @@ interface KeyAlgorithm {
}
interface KeyboardEventInit extends EventModifierInit {
code?: string;
key?: string;
location?: number;
repeat?: boolean;
@ -2304,7 +2305,7 @@ declare var DeviceRotationRate: {
new(): DeviceRotationRate;
}
interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEvent {
interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEvent, ParentNode {
/**
* Sets or gets the URL for the current document.
*/
@ -3367,7 +3368,7 @@ declare var Document: {
new(): Document;
}
interface DocumentFragment extends Node, NodeSelector {
interface DocumentFragment extends Node, NodeSelector, ParentNode {
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
@ -3436,7 +3437,7 @@ declare var EXT_texture_filter_anisotropic: {
readonly TEXTURE_MAX_ANISOTROPY_EXT: number;
}
interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelector, ChildNode {
interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelector, ChildNode, ParentNode {
readonly classList: DOMTokenList;
className: string;
readonly clientHeight: number;
@ -3691,6 +3692,16 @@ interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelec
getElementsByClassName(classNames: string): NodeListOf<Element>;
matches(selector: string): boolean;
closest(selector: string): Element | null;
scrollIntoView(arg?: boolean | ScrollIntoViewOptions): void;
scroll(options?: ScrollToOptions): void;
scroll(x: number, y: number): void;
scrollTo(options?: ScrollToOptions): void;
scrollTo(x: number, y: number): void;
scrollBy(options?: ScrollToOptions): void;
scrollBy(x: number, y: number): void;
insertAdjacentElement(position: string, insertedElement: Element): Element | null;
insertAdjacentHTML(where: string, html: string): void;
insertAdjacentText(where: string, text: string): void;
addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureDoubleTap", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureEnd", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
@ -4462,7 +4473,7 @@ interface HTMLCanvasElement extends HTMLElement {
* @param type The standard MIME type for the image format to return. If you do not specify this parameter, the default value is a PNG format image.
*/
toDataURL(type?: string, ...args: any[]): string;
toBlob(callback: (result: Blob | null) => void, ... arguments: any[]): void;
toBlob(callback: (result: Blob | null) => void, type?: string, ...arguments: any[]): void;
}
declare var HTMLCanvasElement: {
@ -4637,11 +4648,7 @@ interface HTMLElement extends Element {
click(): void;
dragDrop(): boolean;
focus(): void;
insertAdjacentElement(position: string, insertedElement: Element): Element;
insertAdjacentHTML(where: string, html: string): void;
insertAdjacentText(where: string, text: string): void;
msGetInputContext(): MSInputMethodContext;
scrollIntoView(top?: boolean): void;
setActive(): void;
addEventListener(type: "MSContentZoom", listener: (this: this, ev: UIEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
@ -5906,6 +5913,7 @@ interface HTMLLinkElement extends HTMLElement, LinkStyle {
*/
type: string;
import?: Document;
integrity: string;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
@ -6194,7 +6202,7 @@ interface HTMLMediaElement extends HTMLElement {
*/
canPlayType(type: string): string;
/**
* Fires immediately after the client loads the object.
* Resets the audio or video object and loads a new media resource.
*/
load(): void;
/**
@ -6767,6 +6775,7 @@ interface HTMLScriptElement extends HTMLElement {
* Sets or retrieves the MIME type for the associated scripting engine.
*/
type: string;
integrity: string;
}
declare var HTMLScriptElement: {
@ -7772,6 +7781,7 @@ interface KeyboardEvent extends UIEvent {
readonly repeat: boolean;
readonly shiftKey: boolean;
readonly which: number;
readonly code: string;
getModifierState(keyArg: string): boolean;
initKeyboardEvent(typeArg: string, canBubbleArg: boolean, cancelableArg: boolean, viewArg: Window, keyArg: string, locationArg: number, modifiersListArg: string, repeat: boolean, locale: string): void;
readonly DOM_KEY_LOCATION_JOYSTICK: number;
@ -9144,6 +9154,7 @@ interface PerformanceTiming {
readonly responseStart: number;
readonly unloadEventEnd: number;
readonly unloadEventStart: number;
readonly secureConnectionStart: number;
toJSON(): any;
}
@ -11421,8 +11432,8 @@ declare var StereoPannerNode: {
interface Storage {
readonly length: number;
clear(): void;
getItem(key: string): string;
key(index: number): string;
getItem(key: string): string | null;
key(index: number): string | null;
removeItem(key: string): void;
setItem(key: string, data: string): void;
[key: string]: any;
@ -12963,7 +12974,7 @@ interface Window extends EventTarget, WindowTimers, WindowSessionStorage, Window
onunload: (this: this, ev: Event) => any;
onvolumechange: (this: this, ev: Event) => any;
onwaiting: (this: this, ev: Event) => any;
readonly opener: Window;
opener: any;
orientation: string | number;
readonly outerHeight: number;
readonly outerWidth: number;
@ -13018,6 +13029,9 @@ interface Window extends EventTarget, WindowTimers, WindowSessionStorage, Window
webkitConvertPointFromNodeToPage(node: Node, pt: WebKitPoint): WebKitPoint;
webkitConvertPointFromPageToNode(node: Node, pt: WebKitPoint): WebKitPoint;
webkitRequestAnimationFrame(callback: FrameRequestCallback): number;
scroll(options?: ScrollToOptions): void;
scrollTo(options?: ScrollToOptions): void;
scrollBy(options?: ScrollToOptions): void;
addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureDoubleTap", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureEnd", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
@ -14045,6 +14059,20 @@ interface ProgressEventInit extends EventInit {
total?: number;
}
interface ScrollOptions {
behavior?: ScrollBehavior;
}
interface ScrollToOptions extends ScrollOptions {
left?: number;
top?: number;
}
interface ScrollIntoViewOptions extends ScrollOptions {
block?: ScrollLogicalPosition;
inline?: ScrollLogicalPosition;
}
interface ClipboardEventInit extends EventInit {
data?: string;
dataType?: string;
@ -14088,7 +14116,7 @@ interface EcdsaParams extends Algorithm {
}
interface EcKeyGenParams extends Algorithm {
typedCurve: string;
namedCurve: string;
}
interface EcKeyAlgorithm extends KeyAlgorithm {
@ -14224,6 +14252,13 @@ interface JsonWebKey {
k?: string;
}
interface ParentNode {
readonly children: HTMLCollection;
readonly firstElementChild: Element;
readonly lastElementChild: Element;
readonly childElementCount: number;
}
declare type EventListenerOrEventListenerObject = EventListener | EventListenerObject;
interface ErrorEventHandler {
@ -14294,7 +14329,7 @@ declare var location: Location;
declare var locationbar: BarProp;
declare var menubar: BarProp;
declare var msCredentials: MSCredentials;
declare var name: string;
declare const name: never;
declare var navigator: Navigator;
declare var offscreenBuffering: string | boolean;
declare var onabort: (this: Window, ev: UIEvent) => any;
@ -14388,7 +14423,7 @@ declare var ontouchstart: (ev: TouchEvent) => any;
declare var onunload: (this: Window, ev: Event) => any;
declare var onvolumechange: (this: Window, ev: Event) => any;
declare var onwaiting: (this: Window, ev: Event) => any;
declare var opener: Window;
declare var opener: any;
declare var orientation: string | number;
declare var outerHeight: number;
declare var outerWidth: number;
@ -14441,6 +14476,9 @@ declare function webkitCancelAnimationFrame(handle: number): void;
declare function webkitConvertPointFromNodeToPage(node: Node, pt: WebKitPoint): WebKitPoint;
declare function webkitConvertPointFromPageToNode(node: Node, pt: WebKitPoint): WebKitPoint;
declare function webkitRequestAnimationFrame(callback: FrameRequestCallback): number;
declare function scroll(options?: ScrollToOptions): void;
declare function scrollTo(options?: ScrollToOptions): void;
declare function scrollBy(options?: ScrollToOptions): void;
declare function toString(): string;
declare function addEventListener(type: string, listener?: EventListenerOrEventListenerObject, useCapture?: boolean): void;
declare function dispatchEvent(evt: Event): boolean;
@ -14596,6 +14634,8 @@ type MSOutboundPayload = MSVideoSendPayload | MSAudioSendPayload;
type RTCIceGatherCandidate = RTCIceCandidate | RTCIceCandidateComplete;
type RTCTransport = RTCDtlsTransport | RTCSrtpSdesTransport;
type payloadtype = number;
type ScrollBehavior = "auto" | "instant" | "smooth";
type ScrollLogicalPosition = "start" | "center" | "end" | "nearest";
type IDBValidKey = number | string | Date | IDBArrayKey;
type BufferSource = ArrayBuffer | ArrayBufferView;
type MouseWheelEvent = WheelEvent;

View File

@ -14,7 +14,7 @@ and limitations under the License.
***************************************************************************** */
/// <reference no-default-lib="true"/>
/// <reference path="lib.dom.generated.d.ts" />
/// <reference path="lib.dom.d.ts" />
interface DOMTokenList {
[Symbol.iterator](): IterableIterator<string>;

View File

@ -17,10 +17,10 @@ and limitations under the License.
interface Map<K, V> {
clear(): void;
delete(key: K): boolean;
forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void;
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
get(key: K): V | undefined;
has(key: K): boolean;
set(key: K, value: V): this;
set(key: K, value?: V): this;
readonly size: number;
}
@ -31,12 +31,18 @@ interface MapConstructor {
}
declare var Map: MapConstructor;
interface ReadonlyMap<K, V> {
forEach(callbackfn: (value: V, key: K, map: ReadonlyMap<K, V>) => void, thisArg?: any): void;
get(key: K): V|undefined;
has(key: K): boolean;
readonly size: number;
}
interface WeakMap<K, V> {
clear(): void;
delete(key: K): boolean;
get(key: K): V | undefined;
has(key: K): boolean;
set(key: K, value: V): this;
set(key: K, value?: V): this;
}
interface WeakMapConstructor {
@ -50,7 +56,7 @@ interface Set<T> {
add(value: T): this;
clear(): void;
delete(value: T): boolean;
forEach(callbackfn: (value: T, index: T, set: Set<T>) => void, thisArg?: any): void;
forEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: any): void;
has(value: T): boolean;
readonly size: number;
}
@ -62,9 +68,14 @@ interface SetConstructor {
}
declare var Set: SetConstructor;
interface ReadonlySet<T> {
forEach(callbackfn: (value: T, value2: T, set: ReadonlySet<T>) => void, thisArg?: any): void;
has(value: T): boolean;
readonly size: number;
}
interface WeakSet<T> {
add(value: T): this;
clear(): void;
delete(value: T): boolean;
has(value: T): boolean;
}

View File

@ -29,15 +29,15 @@ interface Array<T> {
find(predicate: (value: T, index: number, obj: Array<T>) => boolean, thisArg?: any): T | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
* order, until it finds one where predicate returns true. If such an element is found,
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: T) => boolean, thisArg?: any): number;
findIndex(predicate: (value: T, index: number, obj: Array<T>) => boolean, thisArg?: any): number;
/**
* Returns the this object after filling the section identified by start and end with value
@ -84,6 +84,10 @@ interface ArrayConstructor {
of<T>(...items: T[]): Array<T>;
}
interface DateConstructor {
new (value: Date): Date;
}
interface Function {
/**
* Returns the name of the function. Function names are read-only and can not be changed.
@ -242,7 +246,7 @@ interface NumberConstructor {
/**
* The value of the largest integer n such that n and n + 1 are both exactly representable as
* a Number value.
* The value of Number.MIN_SAFE_INTEGER is 9007199254740991 2^53 1.
* The value of Number.MAX_SAFE_INTEGER is 9007199254740991 2^53 1.
*/
readonly MAX_SAFE_INTEGER: number;
@ -359,6 +363,30 @@ interface ObjectConstructor {
defineProperty(o: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): any;
}
interface ReadonlyArray<T> {
/**
* Returns the value of the first element in the array where predicate is true, and undefined
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found, find
* immediately returns that element value. Otherwise, find returns undefined.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
find(predicate: (value: T, index: number, obj: ReadonlyArray<T>) => boolean, thisArg?: any): T | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: T, index: number, obj: Array<T>) => boolean, thisArg?: any): number;
}
interface RegExp {
/**
* Returns a string indicating the flags of the regular expression in question. This field is read-only.

View File

@ -79,6 +79,26 @@ interface ArrayConstructor {
from<T>(iterable: Iterable<T>): Array<T>;
}
interface ReadonlyArray<T> {
/** Iterator */
[Symbol.iterator](): IterableIterator<T>;
/**
* Returns an array of key, value pairs for every entry in the array
*/
entries(): IterableIterator<[number, T]>;
/**
* Returns an list of keys in the array
*/
keys(): IterableIterator<number>;
/**
* Returns an list of values in the array
*/
values(): IterableIterator<T>;
}
interface IArguments {
/** Iterator */
[Symbol.iterator](): IterableIterator<any>;

View File

@ -24,7 +24,7 @@ interface Promise<T> {
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult1, TResult2>(onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>, onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>;
then(onfulfilled?: ((value: T) => T | PromiseLike<T>) | undefined | null, onrejected?: ((reason: any) => T | PromiseLike<T>) | undefined | null): Promise<T>;
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
@ -32,20 +32,30 @@ interface Promise<T> {
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>, onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<TResult>;
then<TResult>(onfulfilled: ((value: T) => T | PromiseLike<T>) | undefined | null, onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<T | TResult>;
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>): Promise<TResult>;
then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>, onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<TResult>;
/**
* Creates a new Promise with the same internal state of this Promise.
* @returns A Promise.
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then(): Promise<T>;
then<TResult1, TResult2>(onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>, onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>;
/**
* Attaches a callback for only the rejection of the Promise.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of the callback.
*/
catch(onrejected?: ((reason: any) => T | PromiseLike<T>) | undefined | null): Promise<T>;
/**
* Attaches a callback for only the rejection of the Promise.
@ -53,13 +63,6 @@ interface Promise<T> {
* @returns A Promise for the completion of the callback.
*/
catch<TResult>(onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<T | TResult>;
/**
* Attaches a callback for only the rejection of the Promise.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of the callback.
*/
catch(onrejected: (reason: any) => T | PromiseLike<T>): Promise<T>;
}
interface PromiseConstructor {
@ -156,6 +159,86 @@ interface PromiseConstructor {
*/
all<T>(values: (T | PromiseLike<T>)[]): Promise<T[]>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>]): Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4, T5, T6, T7, T8>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>]): Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4, T5, T6, T7>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>]): Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4, T5, T6>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>]): Promise<T1 | T2 | T3 | T4 | T5 | T6>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4, T5>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>]): Promise<T1 | T2 | T3 | T4 | T5>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>]): Promise<T1 | T2 | T3 | T4>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<T1 | T2 | T3>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<T1 | T2>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T>(values: (T | PromiseLike<T>)[]): Promise<T>;
/**
* Creates a new rejected promise for the provided reason.
* @param reason The reason the promise was rejected.

View File

@ -19,7 +19,7 @@ interface ProxyHandler<T> {
setPrototypeOf? (target: T, v: any): boolean;
isExtensible? (target: T): boolean;
preventExtensions? (target: T): boolean;
getOwnPropertyDescriptor? (target: T, p: PropertyKey): PropertyDescriptor | undefined;
getOwnPropertyDescriptor? (target: T, p: PropertyKey): PropertyDescriptor;
has? (target: T, p: PropertyKey): boolean;
get? (target: T, p: PropertyKey, receiver: any): any;
set? (target: T, p: PropertyKey, value: any, receiver: any): boolean;
@ -35,4 +35,4 @@ interface ProxyConstructor {
revocable<T>(target: T, handler: ProxyHandler<T>): { proxy: T; revoke: () => void; };
new <T>(target: T, handler: ProxyHandler<T>): T
}
declare var Proxy: ProxyConstructor;
declare var Proxy: ProxyConstructor;

View File

@ -22,8 +22,7 @@ declare namespace Reflect {
function get(target: any, propertyKey: PropertyKey, receiver?: any): any;
function getOwnPropertyDescriptor(target: any, propertyKey: PropertyKey): PropertyDescriptor;
function getPrototypeOf(target: any): any;
function has(target: any, propertyKey: string): boolean;
function has(target: any, propertyKey: symbol): boolean;
function has(target: any, propertyKey: PropertyKey): boolean;
function isExtensible(target: any): boolean;
function ownKeys(target: any): Array<PropertyKey>;
function preventExtensions(target: any): boolean;

188
lib/lib.es5.d.ts vendored
View File

@ -529,8 +529,8 @@ interface NumberConstructor {
/** An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers. */
declare const Number: NumberConstructor;
interface TemplateStringsArray extends Array<string> {
readonly raw: string[];
interface TemplateStringsArray extends ReadonlyArray<string> {
readonly raw: ReadonlyArray<string>
}
interface Math {
@ -1022,7 +1022,12 @@ interface ReadonlyArray<T> {
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: T[]): T[];
concat(...items: T[][]): T[];
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: (T | T[])[]): T[];
/**
* Adds all the elements of an array separated by the specified separator string.
* @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma.
@ -1071,6 +1076,12 @@ interface ReadonlyArray<T> {
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
map<U>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => U, thisArg?: any): U[];
/**
* Returns the elements of an array that meet the condition specified in a callback function.
* @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
filter<S extends T>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => value is S, thisArg?: any): S[];
/**
* Returns the elements of an array that meet the condition specified in a callback function.
* @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array.
@ -1124,6 +1135,11 @@ interface Array<T> {
* Removes the last element from an array and returns it.
*/
pop(): T | undefined;
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: T[][]): T[];
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
@ -1271,13 +1287,44 @@ declare type PromiseConstructorLike = new <T>(executor: (resolve: (value?: T | P
interface PromiseLike<T> {
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => TResult | PromiseLike<TResult>): PromiseLike<TResult>;
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => void): PromiseLike<TResult>;
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then(
onfulfilled?: ((value: T) => T | PromiseLike<T>) | undefined | null,
onrejected?: ((reason: any) => T | PromiseLike<T>) | undefined | null): PromiseLike<T>;
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(
onfulfilled: ((value: T) => T | PromiseLike<T>) | undefined | null,
onrejected: (reason: any) => TResult | PromiseLike<TResult>): PromiseLike<T | TResult>;
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(
onfulfilled: (value: T) => TResult | PromiseLike<TResult>,
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): PromiseLike<TResult>;
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult1, TResult2>(
onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>,
onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): PromiseLike<TResult1 | TResult2>;
}
interface ArrayLike<T> {
@ -1537,7 +1584,7 @@ interface Int8Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -1545,7 +1592,7 @@ interface Int8Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -1810,7 +1857,7 @@ interface Uint8Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -1818,7 +1865,7 @@ interface Uint8Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -2084,7 +2131,7 @@ interface Uint8ClampedArray {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -2092,7 +2139,7 @@ interface Uint8ClampedArray {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -2357,7 +2404,7 @@ interface Int16Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -2365,7 +2412,7 @@ interface Int16Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -2631,7 +2678,7 @@ interface Uint16Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -2639,7 +2686,7 @@ interface Uint16Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -2904,7 +2951,7 @@ interface Int32Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -2912,7 +2959,7 @@ interface Int32Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -3177,7 +3224,7 @@ interface Uint32Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -3185,7 +3232,7 @@ interface Uint32Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -3450,7 +3497,7 @@ interface Float32Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -3458,7 +3505,7 @@ interface Float32Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -3724,7 +3771,7 @@ interface Float64Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -3732,7 +3779,7 @@ interface Float64Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -3949,12 +3996,9 @@ declare module Intl {
resolvedOptions(): ResolvedCollatorOptions;
}
var Collator: {
new (locales?: string[], options?: CollatorOptions): Collator;
new (locale?: string, options?: CollatorOptions): Collator;
(locales?: string[], options?: CollatorOptions): Collator;
(locale?: string, options?: CollatorOptions): Collator;
supportedLocalesOf(locales: string[], options?: CollatorOptions): string[];
supportedLocalesOf(locale: string, options?: CollatorOptions): string[];
new (locales?: string | string[], options?: CollatorOptions): Collator;
(locales?: string | string[], options?: CollatorOptions): Collator;
supportedLocalesOf(locales: string | string[], options?: CollatorOptions): string[];
}
interface NumberFormatOptions {
@ -3989,12 +4033,9 @@ declare module Intl {
resolvedOptions(): ResolvedNumberFormatOptions;
}
var NumberFormat: {
new (locales?: string[], options?: NumberFormatOptions): NumberFormat;
new (locale?: string, options?: NumberFormatOptions): NumberFormat;
(locales?: string[], options?: NumberFormatOptions): NumberFormat;
(locale?: string, options?: NumberFormatOptions): NumberFormat;
supportedLocalesOf(locales: string[], options?: NumberFormatOptions): string[];
supportedLocalesOf(locale: string, options?: NumberFormatOptions): string[];
new (locales?: string | string[], options?: NumberFormatOptions): NumberFormat;
(locales?: string | string[], options?: NumberFormatOptions): NumberFormat;
supportedLocalesOf(locales: string | string[], options?: NumberFormatOptions): string[];
}
interface DateTimeFormatOptions {
@ -4035,88 +4076,49 @@ declare module Intl {
resolvedOptions(): ResolvedDateTimeFormatOptions;
}
var DateTimeFormat: {
new (locales?: string[], options?: DateTimeFormatOptions): DateTimeFormat;
new (locale?: string, options?: DateTimeFormatOptions): DateTimeFormat;
(locales?: string[], options?: DateTimeFormatOptions): DateTimeFormat;
(locale?: string, options?: DateTimeFormatOptions): DateTimeFormat;
supportedLocalesOf(locales: string[], options?: DateTimeFormatOptions): string[];
supportedLocalesOf(locale: string, options?: DateTimeFormatOptions): string[];
new (locales?: string | string[], options?: DateTimeFormatOptions): DateTimeFormat;
(locales?: string | string[], options?: DateTimeFormatOptions): DateTimeFormat;
supportedLocalesOf(locales: string | string[], options?: DateTimeFormatOptions): string[];
}
}
interface String {
/**
* Determines whether two strings are equivalent in the current locale.
* Determines whether two strings are equivalent in the current or specified locale.
* @param that String to compare to target string
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.
* @param options An object that contains one or more properties that specify comparison options. see the Intl.Collator object for details.
*/
localeCompare(that: string, locales: string[], options?: Intl.CollatorOptions): number;
/**
* Determines whether two strings are equivalent in the current locale.
* @param that String to compare to target string
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.
* @param options An object that contains one or more properties that specify comparison options. see the Intl.Collator object for details.
*/
localeCompare(that: string, locale: string, options?: Intl.CollatorOptions): number;
localeCompare(that: string, locales?: string | string[], options?: Intl.CollatorOptions): number;
}
interface Number {
/**
* Converts a number to a string by using the current or specified locale.
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleString(locales?: string[], options?: Intl.NumberFormatOptions): string;
/**
* Converts a number to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleString(locale?: string, options?: Intl.NumberFormatOptions): string;
toLocaleString(locales?: string | string[], options?: Intl.NumberFormatOptions): string;
}
interface Date {
/**
* Converts a date and time to a string by using the current or specified locale.
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleString(locales?: string[], options?: Intl.DateTimeFormatOptions): string;
toLocaleString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a date to a string by using the current or specified locale.
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleDateString(locales?: string[], options?: Intl.DateTimeFormatOptions): string;
toLocaleDateString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a time to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleTimeString(locale?: string[], options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a date and time to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleString(locale?: string, options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a date to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleDateString(locale?: string, options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a time to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleTimeString(locale?: string, options?: Intl.DateTimeFormatOptions): string;
toLocaleTimeString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
}

447
lib/lib.es6.d.ts vendored
View File

@ -529,8 +529,8 @@ interface NumberConstructor {
/** An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers. */
declare const Number: NumberConstructor;
interface TemplateStringsArray extends Array<string> {
readonly raw: string[];
interface TemplateStringsArray extends ReadonlyArray<string> {
readonly raw: ReadonlyArray<string>
}
interface Math {
@ -1022,7 +1022,12 @@ interface ReadonlyArray<T> {
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: T[]): T[];
concat(...items: T[][]): T[];
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: (T | T[])[]): T[];
/**
* Adds all the elements of an array separated by the specified separator string.
* @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma.
@ -1071,6 +1076,12 @@ interface ReadonlyArray<T> {
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
map<U>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => U, thisArg?: any): U[];
/**
* Returns the elements of an array that meet the condition specified in a callback function.
* @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
filter<S extends T>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => value is S, thisArg?: any): S[];
/**
* Returns the elements of an array that meet the condition specified in a callback function.
* @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array.
@ -1124,6 +1135,11 @@ interface Array<T> {
* Removes the last element from an array and returns it.
*/
pop(): T | undefined;
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: T[][]): T[];
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
@ -1271,13 +1287,44 @@ declare type PromiseConstructorLike = new <T>(executor: (resolve: (value?: T | P
interface PromiseLike<T> {
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => TResult | PromiseLike<TResult>): PromiseLike<TResult>;
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => void): PromiseLike<TResult>;
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then(
onfulfilled?: ((value: T) => T | PromiseLike<T>) | undefined | null,
onrejected?: ((reason: any) => T | PromiseLike<T>) | undefined | null): PromiseLike<T>;
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(
onfulfilled: ((value: T) => T | PromiseLike<T>) | undefined | null,
onrejected: (reason: any) => TResult | PromiseLike<TResult>): PromiseLike<T | TResult>;
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(
onfulfilled: (value: T) => TResult | PromiseLike<TResult>,
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): PromiseLike<TResult>;
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult1, TResult2>(
onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>,
onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): PromiseLike<TResult1 | TResult2>;
}
interface ArrayLike<T> {
@ -1537,7 +1584,7 @@ interface Int8Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -1545,7 +1592,7 @@ interface Int8Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -1810,7 +1857,7 @@ interface Uint8Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -1818,7 +1865,7 @@ interface Uint8Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -2084,7 +2131,7 @@ interface Uint8ClampedArray {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -2092,7 +2139,7 @@ interface Uint8ClampedArray {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -2357,7 +2404,7 @@ interface Int16Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -2365,7 +2412,7 @@ interface Int16Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -2631,7 +2678,7 @@ interface Uint16Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -2639,7 +2686,7 @@ interface Uint16Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -2904,7 +2951,7 @@ interface Int32Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -2912,7 +2959,7 @@ interface Int32Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -3177,7 +3224,7 @@ interface Uint32Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -3185,7 +3232,7 @@ interface Uint32Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -3450,7 +3497,7 @@ interface Float32Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -3458,7 +3505,7 @@ interface Float32Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -3724,7 +3771,7 @@ interface Float64Array {
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
@ -3732,7 +3779,7 @@ interface Float64Array {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
findIndex(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number;
/**
* Performs the specified action for each element in an array.
@ -3949,12 +3996,9 @@ declare module Intl {
resolvedOptions(): ResolvedCollatorOptions;
}
var Collator: {
new (locales?: string[], options?: CollatorOptions): Collator;
new (locale?: string, options?: CollatorOptions): Collator;
(locales?: string[], options?: CollatorOptions): Collator;
(locale?: string, options?: CollatorOptions): Collator;
supportedLocalesOf(locales: string[], options?: CollatorOptions): string[];
supportedLocalesOf(locale: string, options?: CollatorOptions): string[];
new (locales?: string | string[], options?: CollatorOptions): Collator;
(locales?: string | string[], options?: CollatorOptions): Collator;
supportedLocalesOf(locales: string | string[], options?: CollatorOptions): string[];
}
interface NumberFormatOptions {
@ -3989,12 +4033,9 @@ declare module Intl {
resolvedOptions(): ResolvedNumberFormatOptions;
}
var NumberFormat: {
new (locales?: string[], options?: NumberFormatOptions): NumberFormat;
new (locale?: string, options?: NumberFormatOptions): NumberFormat;
(locales?: string[], options?: NumberFormatOptions): NumberFormat;
(locale?: string, options?: NumberFormatOptions): NumberFormat;
supportedLocalesOf(locales: string[], options?: NumberFormatOptions): string[];
supportedLocalesOf(locale: string, options?: NumberFormatOptions): string[];
new (locales?: string | string[], options?: NumberFormatOptions): NumberFormat;
(locales?: string | string[], options?: NumberFormatOptions): NumberFormat;
supportedLocalesOf(locales: string | string[], options?: NumberFormatOptions): string[];
}
interface DateTimeFormatOptions {
@ -4035,90 +4076,51 @@ declare module Intl {
resolvedOptions(): ResolvedDateTimeFormatOptions;
}
var DateTimeFormat: {
new (locales?: string[], options?: DateTimeFormatOptions): DateTimeFormat;
new (locale?: string, options?: DateTimeFormatOptions): DateTimeFormat;
(locales?: string[], options?: DateTimeFormatOptions): DateTimeFormat;
(locale?: string, options?: DateTimeFormatOptions): DateTimeFormat;
supportedLocalesOf(locales: string[], options?: DateTimeFormatOptions): string[];
supportedLocalesOf(locale: string, options?: DateTimeFormatOptions): string[];
new (locales?: string | string[], options?: DateTimeFormatOptions): DateTimeFormat;
(locales?: string | string[], options?: DateTimeFormatOptions): DateTimeFormat;
supportedLocalesOf(locales: string | string[], options?: DateTimeFormatOptions): string[];
}
}
interface String {
/**
* Determines whether two strings are equivalent in the current locale.
* Determines whether two strings are equivalent in the current or specified locale.
* @param that String to compare to target string
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.
* @param options An object that contains one or more properties that specify comparison options. see the Intl.Collator object for details.
*/
localeCompare(that: string, locales: string[], options?: Intl.CollatorOptions): number;
/**
* Determines whether two strings are equivalent in the current locale.
* @param that String to compare to target string
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.
* @param options An object that contains one or more properties that specify comparison options. see the Intl.Collator object for details.
*/
localeCompare(that: string, locale: string, options?: Intl.CollatorOptions): number;
localeCompare(that: string, locales?: string | string[], options?: Intl.CollatorOptions): number;
}
interface Number {
/**
* Converts a number to a string by using the current or specified locale.
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleString(locales?: string[], options?: Intl.NumberFormatOptions): string;
/**
* Converts a number to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleString(locale?: string, options?: Intl.NumberFormatOptions): string;
toLocaleString(locales?: string | string[], options?: Intl.NumberFormatOptions): string;
}
interface Date {
/**
* Converts a date and time to a string by using the current or specified locale.
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleString(locales?: string[], options?: Intl.DateTimeFormatOptions): string;
toLocaleString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a date to a string by using the current or specified locale.
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleDateString(locales?: string[], options?: Intl.DateTimeFormatOptions): string;
toLocaleDateString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a time to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleTimeString(locale?: string[], options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a date and time to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleString(locale?: string, options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a date to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleDateString(locale?: string, options?: Intl.DateTimeFormatOptions): string;
/**
* Converts a time to a string by using the current or specified locale.
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
* @param options An object that contains one or more properties that specify comparison options.
*/
toLocaleTimeString(locale?: string, options?: Intl.DateTimeFormatOptions): string;
toLocaleTimeString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
}
declare type PropertyKey = string | number | symbol;
@ -4135,15 +4137,15 @@ interface Array<T> {
find(predicate: (value: T, index: number, obj: Array<T>) => boolean, thisArg?: any): T | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
* order, until it finds one where predicate returns true. If such an element is found,
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: T) => boolean, thisArg?: any): number;
findIndex(predicate: (value: T, index: number, obj: Array<T>) => boolean, thisArg?: any): number;
/**
* Returns the this object after filling the section identified by start and end with value
@ -4190,6 +4192,10 @@ interface ArrayConstructor {
of<T>(...items: T[]): Array<T>;
}
interface DateConstructor {
new (value: Date): Date;
}
interface Function {
/**
* Returns the name of the function. Function names are read-only and can not be changed.
@ -4348,7 +4354,7 @@ interface NumberConstructor {
/**
* The value of the largest integer n such that n and n + 1 are both exactly representable as
* a Number value.
* The value of Number.MIN_SAFE_INTEGER is 9007199254740991 2^53 1.
* The value of Number.MAX_SAFE_INTEGER is 9007199254740991 2^53 1.
*/
readonly MAX_SAFE_INTEGER: number;
@ -4465,6 +4471,30 @@ interface ObjectConstructor {
defineProperty(o: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): any;
}
interface ReadonlyArray<T> {
/**
* Returns the value of the first element in the array where predicate is true, and undefined
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found, find
* immediately returns that element value. Otherwise, find returns undefined.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
find(predicate: (value: T, index: number, obj: ReadonlyArray<T>) => boolean, thisArg?: any): T | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: T, index: number, obj: Array<T>) => boolean, thisArg?: any): number;
}
interface RegExp {
/**
* Returns a string indicating the flags of the regular expression in question. This field is read-only.
@ -4619,7 +4649,7 @@ interface StringConstructor {
interface Map<K, V> {
clear(): void;
delete(key: K): boolean;
forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void;
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
get(key: K): V | undefined;
has(key: K): boolean;
set(key: K, value?: V): this;
@ -4633,8 +4663,14 @@ interface MapConstructor {
}
declare var Map: MapConstructor;
interface ReadonlyMap<K, V> {
forEach(callbackfn: (value: V, key: K, map: ReadonlyMap<K, V>) => void, thisArg?: any): void;
get(key: K): V|undefined;
has(key: K): boolean;
readonly size: number;
}
interface WeakMap<K, V> {
clear(): void;
delete(key: K): boolean;
get(key: K): V | undefined;
has(key: K): boolean;
@ -4652,7 +4688,7 @@ interface Set<T> {
add(value: T): this;
clear(): void;
delete(value: T): boolean;
forEach(callbackfn: (value: T, index: T, set: Set<T>) => void, thisArg?: any): void;
forEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: any): void;
has(value: T): boolean;
readonly size: number;
}
@ -4664,9 +4700,14 @@ interface SetConstructor {
}
declare var Set: SetConstructor;
interface ReadonlySet<T> {
forEach(callbackfn: (value: T, value2: T, set: ReadonlySet<T>) => void, thisArg?: any): void;
has(value: T): boolean;
readonly size: number;
}
interface WeakSet<T> {
add(value: T): this;
clear(): void;
delete(value: T): boolean;
has(value: T): boolean;
}
@ -4754,6 +4795,26 @@ interface ArrayConstructor {
from<T>(iterable: Iterable<T>): Array<T>;
}
interface ReadonlyArray<T> {
/** Iterator */
[Symbol.iterator](): IterableIterator<T>;
/**
* Returns an array of key, value pairs for every entry in the array
*/
entries(): IterableIterator<[number, T]>;
/**
* Returns an list of keys in the array
*/
keys(): IterableIterator<number>;
/**
* Returns an list of values in the array
*/
values(): IterableIterator<T>;
}
interface IArguments {
/** Iterator */
[Symbol.iterator](): IterableIterator<any>;
@ -5123,7 +5184,7 @@ interface Promise<T> {
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult1, TResult2>(onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>, onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>;
then(onfulfilled?: ((value: T) => T | PromiseLike<T>) | undefined | null, onrejected?: ((reason: any) => T | PromiseLike<T>) | undefined | null): Promise<T>;
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
@ -5131,20 +5192,30 @@ interface Promise<T> {
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>, onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<TResult>;
then<TResult>(onfulfilled: ((value: T) => T | PromiseLike<T>) | undefined | null, onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<T | TResult>;
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>): Promise<TResult>;
then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>, onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<TResult>;
/**
* Creates a new Promise with the same internal state of this Promise.
* @returns A Promise.
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then(): Promise<T>;
then<TResult1, TResult2>(onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>, onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>;
/**
* Attaches a callback for only the rejection of the Promise.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of the callback.
*/
catch(onrejected?: ((reason: any) => T | PromiseLike<T>) | undefined | null): Promise<T>;
/**
* Attaches a callback for only the rejection of the Promise.
@ -5152,13 +5223,6 @@ interface Promise<T> {
* @returns A Promise for the completion of the callback.
*/
catch<TResult>(onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<T | TResult>;
/**
* Attaches a callback for only the rejection of the Promise.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of the callback.
*/
catch(onrejected: (reason: any) => T | PromiseLike<T>): Promise<T>;
}
interface PromiseConstructor {
@ -5255,6 +5319,86 @@ interface PromiseConstructor {
*/
all<T>(values: (T | PromiseLike<T>)[]): Promise<T[]>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>]): Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4, T5, T6, T7, T8>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>]): Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4, T5, T6, T7>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>]): Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4, T5, T6>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>]): Promise<T1 | T2 | T3 | T4 | T5 | T6>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4, T5>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>]): Promise<T1 | T2 | T3 | T4 | T5>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3, T4>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>]): Promise<T1 | T2 | T3 | T4>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2, T3>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<T1 | T2 | T3>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T1, T2>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<T1 | T2>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T>(values: (T | PromiseLike<T>)[]): Promise<T>;
/**
* Creates a new rejected promise for the provided reason.
* @param reason The reason the promise was rejected.
@ -5312,8 +5456,7 @@ declare var Proxy: ProxyConstructor;declare namespace Reflect {
function get(target: any, propertyKey: PropertyKey, receiver?: any): any;
function getOwnPropertyDescriptor(target: any, propertyKey: PropertyKey): PropertyDescriptor;
function getPrototypeOf(target: any): any;
function has(target: any, propertyKey: string): boolean;
function has(target: any, propertyKey: symbol): boolean;
function has(target: any, propertyKey: PropertyKey): boolean;
function isExtensible(target: any): boolean;
function ownKeys(target: any): Array<PropertyKey>;
function preventExtensions(target: any): boolean;
@ -5808,6 +5951,7 @@ interface KeyAlgorithm {
}
interface KeyboardEventInit extends EventModifierInit {
code?: string;
key?: string;
location?: number;
repeat?: boolean;
@ -7970,7 +8114,7 @@ declare var DeviceRotationRate: {
new(): DeviceRotationRate;
}
interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEvent {
interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEvent, ParentNode {
/**
* Sets or gets the URL for the current document.
*/
@ -9033,7 +9177,7 @@ declare var Document: {
new(): Document;
}
interface DocumentFragment extends Node, NodeSelector {
interface DocumentFragment extends Node, NodeSelector, ParentNode {
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
@ -9102,7 +9246,7 @@ declare var EXT_texture_filter_anisotropic: {
readonly TEXTURE_MAX_ANISOTROPY_EXT: number;
}
interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelector, ChildNode {
interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelector, ChildNode, ParentNode {
readonly classList: DOMTokenList;
className: string;
readonly clientHeight: number;
@ -9357,6 +9501,16 @@ interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelec
getElementsByClassName(classNames: string): NodeListOf<Element>;
matches(selector: string): boolean;
closest(selector: string): Element | null;
scrollIntoView(arg?: boolean | ScrollIntoViewOptions): void;
scroll(options?: ScrollToOptions): void;
scroll(x: number, y: number): void;
scrollTo(options?: ScrollToOptions): void;
scrollTo(x: number, y: number): void;
scrollBy(options?: ScrollToOptions): void;
scrollBy(x: number, y: number): void;
insertAdjacentElement(position: string, insertedElement: Element): Element | null;
insertAdjacentHTML(where: string, html: string): void;
insertAdjacentText(where: string, text: string): void;
addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureDoubleTap", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureEnd", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
@ -10128,7 +10282,7 @@ interface HTMLCanvasElement extends HTMLElement {
* @param type The standard MIME type for the image format to return. If you do not specify this parameter, the default value is a PNG format image.
*/
toDataURL(type?: string, ...args: any[]): string;
toBlob(callback: (result: Blob | null) => void, ... arguments: any[]): void;
toBlob(callback: (result: Blob | null) => void, type?: string, ...arguments: any[]): void;
}
declare var HTMLCanvasElement: {
@ -10303,11 +10457,7 @@ interface HTMLElement extends Element {
click(): void;
dragDrop(): boolean;
focus(): void;
insertAdjacentElement(position: string, insertedElement: Element): Element;
insertAdjacentHTML(where: string, html: string): void;
insertAdjacentText(where: string, text: string): void;
msGetInputContext(): MSInputMethodContext;
scrollIntoView(top?: boolean): void;
setActive(): void;
addEventListener(type: "MSContentZoom", listener: (this: this, ev: UIEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
@ -11572,6 +11722,7 @@ interface HTMLLinkElement extends HTMLElement, LinkStyle {
*/
type: string;
import?: Document;
integrity: string;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
@ -11860,7 +12011,7 @@ interface HTMLMediaElement extends HTMLElement {
*/
canPlayType(type: string): string;
/**
* Fires immediately after the client loads the object.
* Resets the audio or video object and loads a new media resource.
*/
load(): void;
/**
@ -12433,6 +12584,7 @@ interface HTMLScriptElement extends HTMLElement {
* Sets or retrieves the MIME type for the associated scripting engine.
*/
type: string;
integrity: string;
}
declare var HTMLScriptElement: {
@ -13438,6 +13590,7 @@ interface KeyboardEvent extends UIEvent {
readonly repeat: boolean;
readonly shiftKey: boolean;
readonly which: number;
readonly code: string;
getModifierState(keyArg: string): boolean;
initKeyboardEvent(typeArg: string, canBubbleArg: boolean, cancelableArg: boolean, viewArg: Window, keyArg: string, locationArg: number, modifiersListArg: string, repeat: boolean, locale: string): void;
readonly DOM_KEY_LOCATION_JOYSTICK: number;
@ -14810,6 +14963,7 @@ interface PerformanceTiming {
readonly responseStart: number;
readonly unloadEventEnd: number;
readonly unloadEventStart: number;
readonly secureConnectionStart: number;
toJSON(): any;
}
@ -17087,8 +17241,8 @@ declare var StereoPannerNode: {
interface Storage {
readonly length: number;
clear(): void;
getItem(key: string): string;
key(index: number): string;
getItem(key: string): string | null;
key(index: number): string | null;
removeItem(key: string): void;
setItem(key: string, data: string): void;
[key: string]: any;
@ -18629,7 +18783,7 @@ interface Window extends EventTarget, WindowTimers, WindowSessionStorage, Window
onunload: (this: this, ev: Event) => any;
onvolumechange: (this: this, ev: Event) => any;
onwaiting: (this: this, ev: Event) => any;
readonly opener: Window;
opener: any;
orientation: string | number;
readonly outerHeight: number;
readonly outerWidth: number;
@ -18684,6 +18838,9 @@ interface Window extends EventTarget, WindowTimers, WindowSessionStorage, Window
webkitConvertPointFromNodeToPage(node: Node, pt: WebKitPoint): WebKitPoint;
webkitConvertPointFromPageToNode(node: Node, pt: WebKitPoint): WebKitPoint;
webkitRequestAnimationFrame(callback: FrameRequestCallback): number;
scroll(options?: ScrollToOptions): void;
scrollTo(options?: ScrollToOptions): void;
scrollBy(options?: ScrollToOptions): void;
addEventListener(type: "MSGestureChange", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureDoubleTap", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
addEventListener(type: "MSGestureEnd", listener: (this: this, ev: MSGestureEvent) => any, useCapture?: boolean): void;
@ -19711,6 +19868,20 @@ interface ProgressEventInit extends EventInit {
total?: number;
}
interface ScrollOptions {
behavior?: ScrollBehavior;
}
interface ScrollToOptions extends ScrollOptions {
left?: number;
top?: number;
}
interface ScrollIntoViewOptions extends ScrollOptions {
block?: ScrollLogicalPosition;
inline?: ScrollLogicalPosition;
}
interface ClipboardEventInit extends EventInit {
data?: string;
dataType?: string;
@ -19754,7 +19925,7 @@ interface EcdsaParams extends Algorithm {
}
interface EcKeyGenParams extends Algorithm {
typedCurve: string;
namedCurve: string;
}
interface EcKeyAlgorithm extends KeyAlgorithm {
@ -19890,6 +20061,13 @@ interface JsonWebKey {
k?: string;
}
interface ParentNode {
readonly children: HTMLCollection;
readonly firstElementChild: Element;
readonly lastElementChild: Element;
readonly childElementCount: number;
}
declare type EventListenerOrEventListenerObject = EventListener | EventListenerObject;
interface ErrorEventHandler {
@ -19960,7 +20138,7 @@ declare var location: Location;
declare var locationbar: BarProp;
declare var menubar: BarProp;
declare var msCredentials: MSCredentials;
declare var name: string;
declare const name: never;
declare var navigator: Navigator;
declare var offscreenBuffering: string | boolean;
declare var onabort: (this: Window, ev: UIEvent) => any;
@ -20054,7 +20232,7 @@ declare var ontouchstart: (ev: TouchEvent) => any;
declare var onunload: (this: Window, ev: Event) => any;
declare var onvolumechange: (this: Window, ev: Event) => any;
declare var onwaiting: (this: Window, ev: Event) => any;
declare var opener: Window;
declare var opener: any;
declare var orientation: string | number;
declare var outerHeight: number;
declare var outerWidth: number;
@ -20107,6 +20285,9 @@ declare function webkitCancelAnimationFrame(handle: number): void;
declare function webkitConvertPointFromNodeToPage(node: Node, pt: WebKitPoint): WebKitPoint;
declare function webkitConvertPointFromPageToNode(node: Node, pt: WebKitPoint): WebKitPoint;
declare function webkitRequestAnimationFrame(callback: FrameRequestCallback): number;
declare function scroll(options?: ScrollToOptions): void;
declare function scrollTo(options?: ScrollToOptions): void;
declare function scrollBy(options?: ScrollToOptions): void;
declare function toString(): string;
declare function addEventListener(type: string, listener?: EventListenerOrEventListenerObject, useCapture?: boolean): void;
declare function dispatchEvent(evt: Event): boolean;
@ -20262,6 +20443,8 @@ type MSOutboundPayload = MSVideoSendPayload | MSAudioSendPayload;
type RTCIceGatherCandidate = RTCIceCandidate | RTCIceCandidateComplete;
type RTCTransport = RTCDtlsTransport | RTCSrtpSdesTransport;
type payloadtype = number;
type ScrollBehavior = "auto" | "instant" | "smooth";
type ScrollLogicalPosition = "start" | "center" | "end" | "nearest";
type IDBValidKey = number | string | Date | IDBArrayKey;
type BufferSource = ArrayBuffer | ArrayBufferView;
type MouseWheelEvent = WheelEvent;
@ -20561,7 +20744,7 @@ interface DateConstructor {
interface Date {
getVarDate: () => VarDate;
}
/// <reference path="lib.dom.generated.d.ts" />
/// <reference path="lib.dom.d.ts" />
interface DOMTokenList {
[Symbol.iterator](): IterableIterator<string>;

View File

@ -1016,7 +1016,7 @@ interface EcdsaParams extends Algorithm {
}
interface EcKeyGenParams extends Algorithm {
typedCurve: string;
namedCurve: string;
}
interface EcKeyAlgorithm extends KeyAlgorithm {

28451
lib/tsc.js

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2642
lib/tsserverlibrary.d.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

622
lib/typescript.d.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6626
lib/typingsInstaller.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -2592,7 +2592,7 @@ namespace ts {
}
// Currently, we only support generators that were originally async function bodies.
if (asteriskToken && node.emitFlags & NodeEmitFlags.AsyncFunctionBody) {
if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}
@ -2667,7 +2667,7 @@ namespace ts {
// down-level generator.
// Currently we do not support transforming any other generator fucntions
// down level.
if (asteriskToken && node.emitFlags & NodeEmitFlags.AsyncFunctionBody) {
if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}
}
@ -2698,7 +2698,7 @@ namespace ts {
// down-level generator.
// Currently we do not support transforming any other generator fucntions
// down level.
if (asteriskToken && node.emitFlags & NodeEmitFlags.AsyncFunctionBody) {
if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}

View File

@ -1,3 +1,4 @@
/// <reference path="moduleNameResolver.ts"/>
/// <reference path="binder.ts"/>
/* @internal */
@ -4366,7 +4367,7 @@ namespace ts {
function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] {
for (const current of type.types) {
for (const prop of getPropertiesOfType(current)) {
getPropertyOfUnionOrIntersectionType(type, prop.name);
getUnionOrIntersectionProperty(type, prop.name);
}
// The properties of a union type are those that are present in all constituent types, so
// we only need to check the properties of the first type
@ -4374,7 +4375,19 @@ namespace ts {
break;
}
}
return type.resolvedProperties ? symbolsToArray(type.resolvedProperties) : emptyArray;
const props = type.resolvedProperties;
if (props) {
const result: Symbol[] = [];
for (const key in props) {
const prop = props[key];
// We need to filter out partial properties in union types
if (!(prop.flags & SymbolFlags.SyntheticProperty && (<TransientSymbol>prop).isPartial)) {
result.push(prop);
}
}
return result;
}
return emptyArray;
}
function getPropertiesOfType(type: Type): Symbol[] {
@ -4427,6 +4440,7 @@ namespace ts {
// Flags we want to propagate to the result if they exist in all source symbols
let commonFlags = (containingType.flags & TypeFlags.Intersection) ? SymbolFlags.Optional : SymbolFlags.None;
let isReadonly = false;
let isPartial = false;
for (const current of types) {
const type = getApparentType(current);
if (type !== unknownType) {
@ -4444,21 +4458,20 @@ namespace ts {
}
}
else if (containingType.flags & TypeFlags.Union) {
// A union type requires the property to be present in all constituent types
return undefined;
isPartial = true;
}
}
}
if (!props) {
return undefined;
}
if (props.length === 1) {
if (props.length === 1 && !isPartial) {
return props[0];
}
const propTypes: Type[] = [];
const declarations: Declaration[] = [];
let commonType: Type = undefined;
let hasCommonType = true;
let hasNonUniformType = false;
for (const prop of props) {
if (prop.declarations) {
addRange(declarations, prop.declarations);
@ -4468,25 +4481,26 @@ namespace ts {
commonType = type;
}
else if (type !== commonType) {
hasCommonType = false;
hasNonUniformType = true;
}
propTypes.push(getTypeOfSymbol(prop));
propTypes.push(type);
}
const result = <TransientSymbol>createSymbol(
SymbolFlags.Property |
SymbolFlags.Transient |
SymbolFlags.SyntheticProperty |
commonFlags,
name);
const result = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | SymbolFlags.SyntheticProperty | commonFlags, name);
result.containingType = containingType;
result.hasCommonType = hasCommonType;
result.hasNonUniformType = hasNonUniformType;
result.isPartial = isPartial;
result.declarations = declarations;
result.isReadonly = isReadonly;
result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes);
return result;
}
function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: string): Symbol {
// Return the symbol for a given property in a union or intersection type, or undefined if the property
// does not exist in any constituent type. Note that the returned property may only be present in some
// constituents, in which case the isPartial flag is set when the containing type is union type. We need
// these partial properties when identifying discriminant properties, but otherwise they are filtered out
// and do not appear to be present in the union type.
function getUnionOrIntersectionProperty(type: UnionOrIntersectionType, name: string): Symbol {
const properties = type.resolvedProperties || (type.resolvedProperties = createMap<Symbol>());
let property = properties[name];
if (!property) {
@ -4498,6 +4512,12 @@ namespace ts {
return property;
}
function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: string): Symbol {
const property = getUnionOrIntersectionProperty(type, name);
// We need to filter out partial properties in union types
return property && !(property.flags & SymbolFlags.SyntheticProperty && (<TransientSymbol>property).isPartial) ? property : undefined;
}
/**
* Return the symbol for the property with the given name in the given type. Creates synthetic union properties when
* necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from
@ -8078,21 +8098,10 @@ namespace ts {
function isDiscriminantProperty(type: Type, name: string) {
if (type && type.flags & TypeFlags.Union) {
let prop = getPropertyOfType(type, name);
if (!prop) {
// The type may be a union that includes nullable or primitive types. If filtering
// those out produces a different type, get the property from that type instead.
// Effectively, we're checking if this *could* be a discriminant property once nullable
// and primitive types are removed by other type guards.
const filteredType = getTypeWithFacts(type, TypeFacts.Discriminatable);
if (filteredType !== type && filteredType.flags & TypeFlags.Union) {
prop = getPropertyOfType(filteredType, name);
}
}
const prop = getUnionOrIntersectionProperty(<UnionType>type, name);
if (prop && prop.flags & SymbolFlags.SyntheticProperty) {
if ((<TransientSymbol>prop).isDiscriminantProperty === undefined) {
(<TransientSymbol>prop).isDiscriminantProperty = !(<TransientSymbol>prop).hasCommonType &&
isLiteralType(getTypeOfSymbol(prop));
(<TransientSymbol>prop).isDiscriminantProperty = (<TransientSymbol>prop).hasNonUniformType && isLiteralType(getTypeOfSymbol(prop));
}
return (<TransientSymbol>prop).isDiscriminantProperty;
}
@ -15686,6 +15695,11 @@ namespace ts {
}
function checkCollisionWithRequireExportsInGeneratedCode(node: Node, name: Identifier) {
// No need to check for require or exports for ES6 modules and later
if (modulekind >= ModuleKind.ES6) {
return;
}
if (!needCollisionCheckForIdentifier(node, name, "require") && !needCollisionCheckForIdentifier(node, name, "exports")) {
return;
}
@ -16487,7 +16501,13 @@ namespace ts {
}
checkExpression(node.expression);
error(node.expression, Diagnostics.All_symbols_within_a_with_block_will_be_resolved_to_any);
const sourceFile = getSourceFileOfNode(node);
if (!hasParseDiagnostics(sourceFile)) {
const start = getSpanOfTokenAtPosition(sourceFile, node.pos).start;
const end = node.statement.pos;
grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.The_with_statement_is_not_supported_All_symbols_in_a_with_block_will_have_type_any);
}
}
function checkSwitchStatement(node: SwitchStatement) {

View File

@ -5,12 +5,15 @@
/// <reference path="scanner.ts"/>
namespace ts {
/* @internal */
export const compileOnSaveCommandLineOption: CommandLineOption = { name: "compileOnSave", type: "boolean" };
/* @internal */
export const optionDeclarations: CommandLineOption[] = [
{
name: "charset",
type: "string",
},
compileOnSaveCommandLineOption,
{
name: "declaration",
shortName: "d",
@ -677,10 +680,10 @@ namespace ts {
* @param fileName The path to the config file
* @param jsonText The text of the config file
*/
export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } {
export function parseConfigFileTextToJson(fileName: string, jsonText: string, stripComments = true): { config?: any; error?: Diagnostic } {
try {
const jsonTextWithoutComments = removeComments(jsonText);
return { config: /\S/.test(jsonTextWithoutComments) ? JSON.parse(jsonTextWithoutComments) : {} };
const jsonTextToParse = stripComments ? removeComments(jsonText) : jsonText;
return { config: /\S/.test(jsonTextToParse) ? JSON.parse(jsonTextToParse) : {} };
}
catch (e) {
return { error: createCompilerDiagnostic(Diagnostics.Failed_to_parse_file_0_Colon_1, fileName, e.message) };
@ -849,6 +852,7 @@ namespace ts {
options.configFilePath = configFileName;
const { fileNames, wildcardDirectories } = getFileNames(errors);
const compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors);
return {
options,
@ -856,7 +860,8 @@ namespace ts {
typingOptions,
raw: json,
errors,
wildcardDirectories
wildcardDirectories,
compileOnSave
};
function tryExtendsName(extendedConfig: string): [string[], string[], string[], CompilerOptions] {
@ -943,13 +948,24 @@ namespace ts {
}
}
export function convertCompileOnSaveOptionFromJson(jsonOption: any, basePath: string, errors: Diagnostic[]): boolean {
if (!hasProperty(jsonOption, compileOnSaveCommandLineOption.name)) {
return false;
}
const result = convertJsonOption(compileOnSaveCommandLineOption, jsonOption["compileOnSave"], basePath, errors);
if (typeof result === "boolean" && result) {
return result;
}
return false;
}
export function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions, errors: Diagnostic[] } {
const errors: Diagnostic[] = [];
const options = convertCompilerOptionsFromJsonWorker(jsonOptions, basePath, errors, configFileName);
return { options, errors };
}
export function convertTypingOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions, errors: Diagnostic[] } {
export function convertTypingOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: TypingOptions, errors: Diagnostic[] } {
const errors: Diagnostic[] = [];
const options = convertTypingOptionsFromJsonWorker(jsonOptions, basePath, errors, configFileName);
return { options, errors };
@ -958,7 +974,9 @@ namespace ts {
function convertCompilerOptionsFromJsonWorker(jsonOptions: any,
basePath: string, errors: Diagnostic[], configFileName?: string): CompilerOptions {
const options: CompilerOptions = getBaseFileName(configFileName) === "jsconfig.json" ? { allowJs: true, maxNodeModuleJsDepth: 2 } : {};
const options: CompilerOptions = getBaseFileName(configFileName) === "jsconfig.json"
? { allowJs: true, maxNodeModuleJsDepth: 2, allowSyntheticDefaultImports: true }
: {};
convertOptionsFromJson(optionDeclarations, jsonOptions, basePath, options, Diagnostics.Unknown_compiler_option_0, errors);
return options;
}

View File

@ -5,7 +5,7 @@ namespace ts {
export interface CommentWriter {
reset(): void;
setSourceFile(sourceFile: SourceFile): void;
emitNodeWithComments(node: Node, emitCallback: (node: Node) => void): void;
emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void;
emitTrailingCommentsOfPosition(pos: number): void;
}
@ -34,22 +34,24 @@ namespace ts {
emitTrailingCommentsOfPosition,
};
function emitNodeWithComments(node: Node, emitCallback: (node: Node) => void) {
function emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
if (disabled) {
emitCallback(node);
emitCallback(emitContext, node);
return;
}
if (node) {
const { pos, end } = node.commentRange || node;
const emitFlags = node.emitFlags;
const { pos, end } = getCommentRange(node);
const emitFlags = getEmitFlags(node);
if ((pos < 0 && end < 0) || (pos === end)) {
// Both pos and end are synthesized, so just emit the node without comments.
if (emitFlags & NodeEmitFlags.NoNestedComments) {
disableCommentsAndEmit(node, emitCallback);
if (emitFlags & EmitFlags.NoNestedComments) {
disabled = true;
emitCallback(emitContext, node);
disabled = false;
}
else {
emitCallback(node);
emitCallback(emitContext, node);
}
}
else {
@ -58,8 +60,8 @@ namespace ts {
}
const isEmittedNode = node.kind !== SyntaxKind.NotEmittedStatement;
const skipLeadingComments = pos < 0 || (emitFlags & NodeEmitFlags.NoLeadingComments) !== 0;
const skipTrailingComments = end < 0 || (emitFlags & NodeEmitFlags.NoTrailingComments) !== 0;
const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0;
const skipTrailingComments = end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0;
// Emit leading comments if the position is not synthesized and the node
// has not opted out from emitting leading comments.
@ -90,11 +92,13 @@ namespace ts {
performance.measure("commentTime", "preEmitNodeWithComment");
}
if (emitFlags & NodeEmitFlags.NoNestedComments) {
disableCommentsAndEmit(node, emitCallback);
if (emitFlags & EmitFlags.NoNestedComments) {
disabled = true;
emitCallback(emitContext, node);
disabled = false;
}
else {
emitCallback(node);
emitCallback(emitContext, node);
}
if (extendedDiagnostics) {
@ -125,9 +129,9 @@ namespace ts {
}
const { pos, end } = detachedRange;
const emitFlags = node.emitFlags;
const skipLeadingComments = pos < 0 || (emitFlags & NodeEmitFlags.NoLeadingComments) !== 0;
const skipTrailingComments = disabled || end < 0 || (emitFlags & NodeEmitFlags.NoTrailingComments) !== 0;
const emitFlags = getEmitFlags(node);
const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0;
const skipTrailingComments = disabled || end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0;
if (!skipLeadingComments) {
emitDetachedCommentsAndUpdateCommentsInfo(detachedRange);
@ -137,8 +141,10 @@ namespace ts {
performance.measure("commentTime", "preEmitBodyWithDetachedComments");
}
if (emitFlags & NodeEmitFlags.NoNestedComments) {
disableCommentsAndEmit(node, emitCallback);
if (emitFlags & EmitFlags.NoNestedComments && !disabled) {
disabled = true;
emitCallback(node);
disabled = false;
}
else {
emitCallback(node);
@ -284,17 +290,6 @@ namespace ts {
detachedCommentsInfo = undefined;
}
function disableCommentsAndEmit(node: Node, emitCallback: (node: Node) => void): void {
if (disabled) {
emitCallback(node);
}
else {
disabled = true;
emitCallback(node);
disabled = false;
}
}
function hasDetachedComments(pos: number) {
return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos;
}

View File

@ -1,7 +1,6 @@
/// <reference path="types.ts"/>
/// <reference path="types.ts"/>
/// <reference path="performance.ts" />
/* @internal */
namespace ts {
/**
@ -47,6 +46,7 @@ namespace ts {
contains,
remove,
forEachValue: forEachValueInMap,
getKeys,
clear,
};
@ -56,6 +56,14 @@ namespace ts {
}
}
function getKeys() {
const keys: Path[] = [];
for (const key in files) {
keys.push(<Path>key);
}
return keys;
}
// path should already be well-formed so it does not need to be normalized
function get(path: Path): T {
return files[toKey(path)];
@ -397,6 +405,7 @@ namespace ts {
return [...array1, ...array2];
}
// TODO: fixme (N^2) - add optional comparer so collection can be sorted before deduplication.
export function deduplicate<T>(array: T[], areEqual?: (a: T, b: T) => boolean): T[] {
let result: T[];
if (array) {
@ -496,18 +505,25 @@ namespace ts {
* @param array A sorted array whose first element must be no larger than number
* @param number The value to be searched for in the array.
*/
export function binarySearch(array: number[], value: number): number {
export function binarySearch<T>(array: T[], value: T, comparer?: (v1: T, v2: T) => number): number {
if (!array || array.length === 0) {
return -1;
}
let low = 0;
let high = array.length - 1;
comparer = comparer !== undefined
? comparer
: (v1, v2) => (v1 < v2 ? -1 : (v1 > v2 ? 1 : 0));
while (low <= high) {
const middle = low + ((high - low) >> 1);
const midValue = array[middle];
if (midValue === value) {
if (comparer(midValue, value) === 0) {
return middle;
}
else if (midValue > value) {
else if (comparer(midValue, value) > 0) {
high = middle - 1;
}
else {
@ -824,6 +840,72 @@ namespace ts {
};
}
/**
* High-order function, creates a function that executes a function composition.
* For example, `chain(a, b)` is the equivalent of `x => ((a', b') => y => b'(a'(y)))(a(x), b(x))`
*
* @param args The functions to chain.
*/
export function chain<T, U>(...args: ((t: T) => (u: U) => U)[]): (t: T) => (u: U) => U;
export function chain<T, U>(a: (t: T) => (u: U) => U, b: (t: T) => (u: U) => U, c: (t: T) => (u: U) => U, d: (t: T) => (u: U) => U, e: (t: T) => (u: U) => U): (t: T) => (u: U) => U {
if (e) {
const args: ((t: T) => (u: U) => U)[] = [];
for (let i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
return t => compose(...map(args, f => f(t)));
}
else if (d) {
return t => compose(a(t), b(t), c(t), d(t));
}
else if (c) {
return t => compose(a(t), b(t), c(t));
}
else if (b) {
return t => compose(a(t), b(t));
}
else if (a) {
return t => compose(a(t));
}
else {
return t => u => u;
}
}
/**
* High-order function, composes functions. Note that functions are composed inside-out;
* for example, `compose(a, b)` is the equivalent of `x => b(a(x))`.
*
* @param args The functions to compose.
*/
export function compose<T>(...args: ((t: T) => T)[]): (t: T) => T;
export function compose<T>(a: (t: T) => T, b: (t: T) => T, c: (t: T) => T, d: (t: T) => T, e: (t: T) => T): (t: T) => T {
if (e) {
const args: ((t: T) => T)[] = [];
for (let i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
return t => reduceLeft<(t: T) => T, T>(args, (u, f) => f(u), t);
}
else if (d) {
return t => d(c(b(a(t))));
}
else if (c) {
return t => c(b(a(t)));
}
else if (b) {
return t => b(a(t));
}
else if (a) {
return t => a(t);
}
else {
return t => t;
}
}
function formatStringFromArgs(text: string, args: { [index: number]: any; }, baseIndex?: number): string {
baseIndex = baseIndex || 0;
@ -1096,10 +1178,49 @@ namespace ts {
return path && !isRootedDiskPath(path) && path.indexOf("://") !== -1;
}
export function isExternalModuleNameRelative(moduleName: string): boolean {
// TypeScript 1.0 spec (April 2014): 11.2.1
// An external module name is "relative" if the first term is "." or "..".
return /^\.\.?($|[\\/])/.test(moduleName);
}
export function getEmitScriptTarget(compilerOptions: CompilerOptions) {
return compilerOptions.target || ScriptTarget.ES3;
}
export function getEmitModuleKind(compilerOptions: CompilerOptions) {
return typeof compilerOptions.module === "number" ?
compilerOptions.module :
getEmitScriptTarget(compilerOptions) === ScriptTarget.ES6 ? ModuleKind.ES6 : ModuleKind.CommonJS;
}
/* @internal */
export function hasZeroOrOneAsteriskCharacter(str: string): boolean {
let seenAsterisk = false;
for (let i = 0; i < str.length; i++) {
if (str.charCodeAt(i) === CharacterCodes.asterisk) {
if (!seenAsterisk) {
seenAsterisk = true;
}
else {
// have already seen asterisk
return false;
}
}
}
return true;
}
export function isRootedDiskPath(path: string) {
return getRootLength(path) !== 0;
}
export function convertToRelativePath(absoluteOrRelativePath: string, basePath: string, getCanonicalFileName: (path: string) => string): string {
return !isRootedDiskPath(absoluteOrRelativePath)
? absoluteOrRelativePath
: getRelativePathToDirectoryOrUrl(basePath, absoluteOrRelativePath, basePath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
}
function normalizedPathComponents(path: string, rootLength: number) {
const normalizedParts = getNormalizedParts(path, rootLength);
return [path.substr(0, rootLength)].concat(normalizedParts);
@ -1591,6 +1712,14 @@ namespace ts {
return options && options.allowJs ? allSupportedExtensions : supportedTypeScriptExtensions;
}
export function hasJavaScriptFileExtension(fileName: string) {
return forEach(supportedJavascriptExtensions, extension => fileExtensionIs(fileName, extension));
}
export function hasTypeScriptFileExtension(fileName: string) {
return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension));
}
export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions) {
if (!fileName) { return false; }
@ -1715,7 +1844,6 @@ namespace ts {
this.transformFlags = TransformFlags.None;
this.parent = undefined;
this.original = undefined;
this.transformId = 0;
}
export let objectAllocator: ObjectAllocator = {
@ -1825,4 +1953,82 @@ namespace ts {
? ((fileName) => fileName)
: ((fileName) => fileName.toLowerCase());
}
/**
* patternStrings contains both pattern strings (containing "*") and regular strings.
* Return an exact match if possible, or a pattern match, or undefined.
* (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.)
*/
/* @internal */
export function matchPatternOrExact(patternStrings: string[], candidate: string): string | Pattern | undefined {
const patterns: Pattern[] = [];
for (const patternString of patternStrings) {
const pattern = tryParsePattern(patternString);
if (pattern) {
patterns.push(pattern);
}
else if (patternString === candidate) {
// pattern was matched as is - no need to search further
return patternString;
}
}
return findBestPatternMatch(patterns, _ => _, candidate);
}
/* @internal */
export function patternText({prefix, suffix}: Pattern): string {
return `${prefix}*${suffix}`;
}
/**
* Given that candidate matches pattern, returns the text matching the '*'.
* E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar"
*/
/* @internal */
export function matchedText(pattern: Pattern, candidate: string): string {
Debug.assert(isPatternMatch(pattern, candidate));
return candidate.substr(pattern.prefix.length, candidate.length - pattern.suffix.length);
}
/** Return the object corresponding to the best pattern to match `candidate`. */
/* @internal */
export function findBestPatternMatch<T>(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined {
let matchedValue: T | undefined = undefined;
// use length of prefix as betterness criteria
let longestMatchPrefixLength = -1;
for (const v of values) {
const pattern = getPattern(v);
if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) {
longestMatchPrefixLength = pattern.prefix.length;
matchedValue = v;
}
}
return matchedValue;
}
function isPatternMatch({prefix, suffix}: Pattern, candidate: string) {
return candidate.length >= prefix.length + suffix.length &&
startsWith(candidate, prefix) &&
endsWith(candidate, suffix);
}
/* @internal */
export function tryParsePattern(pattern: string): Pattern | undefined {
// This should be verified outside of here and a proper error thrown.
Debug.assert(hasZeroOrOneAsteriskCharacter(pattern));
const indexOfStar = pattern.indexOf("*");
return indexOfStar === -1 ? undefined : {
prefix: pattern.substr(0, indexOfStar),
suffix: pattern.substr(indexOfStar + 1)
};
}
export function positionIsSynthesized(pos: number): boolean {
// This is a fast way of testing the following conditions:
// pos === undefined || pos === null || isNaN(pos) || pos < 0;
return !(pos >= 0);
}
}

View File

@ -36,12 +36,12 @@ namespace ts {
return declarationDiagnostics.getDiagnostics(targetSourceFile ? targetSourceFile.fileName : undefined);
function getDeclarationDiagnosticsFromFile({ declarationFilePath }: EmitFileNames, sources: SourceFile[], isBundledEmit: boolean) {
emitDeclarations(host, resolver, declarationDiagnostics, declarationFilePath, sources, isBundledEmit);
emitDeclarations(host, resolver, declarationDiagnostics, declarationFilePath, sources, isBundledEmit, /*emitOnlyDtsFiles*/ false);
}
}
function emitDeclarations(host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, declarationFilePath: string,
sourceFiles: SourceFile[], isBundledEmit: boolean): DeclarationEmit {
sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean): DeclarationEmit {
const newLine = host.getNewLine();
const compilerOptions = host.getCompilerOptions();
@ -98,7 +98,7 @@ namespace ts {
// global file reference is added only
// - if it is not bundled emit (because otherwise it would be self reference)
// - and it is not already added
if (writeReferencePath(referencedFile, !isBundledEmit && !addedGlobalFileReference)) {
if (writeReferencePath(referencedFile, !isBundledEmit && !addedGlobalFileReference, emitOnlyDtsFiles)) {
addedGlobalFileReference = true;
}
emittedReferencedFiles.push(referencedFile);
@ -327,7 +327,7 @@ namespace ts {
}
else {
errorNameNode = declaration.name;
resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
errorNameNode = undefined;
}
}
@ -341,7 +341,7 @@ namespace ts {
}
else {
errorNameNode = signature.name;
resolver.writeReturnTypeOfSignatureDeclaration(signature, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
resolver.writeReturnTypeOfSignatureDeclaration(signature, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
errorNameNode = undefined;
}
}
@ -563,7 +563,7 @@ namespace ts {
write(tempVarName);
write(": ");
writer.getSymbolAccessibilityDiagnostic = getDefaultExportAccessibilityDiagnostic;
resolver.writeTypeOfExpression(node.expression, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
resolver.writeTypeOfExpression(node.expression, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
write(";");
writeLine();
write(node.isExportEquals ? "export = " : "export default ");
@ -1026,7 +1026,7 @@ namespace ts {
}
else {
writer.getSymbolAccessibilityDiagnostic = getHeritageClauseVisibilityError;
resolver.writeBaseConstructorTypeOfClass(<ClassLikeDeclaration>enclosingDeclaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
resolver.writeBaseConstructorTypeOfClass(<ClassLikeDeclaration>enclosingDeclaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
}
function getHeritageClauseVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
@ -1720,7 +1720,7 @@ namespace ts {
* @param referencedFile
* @param addBundledFileReference Determines if global file reference corresponding to bundled file should be emitted or not
*/
function writeReferencePath(referencedFile: SourceFile, addBundledFileReference: boolean): boolean {
function writeReferencePath(referencedFile: SourceFile, addBundledFileReference: boolean, emitOnlyDtsFiles: boolean): boolean {
let declFileName: string;
let addedBundledEmitReference = false;
if (isDeclarationFile(referencedFile)) {
@ -1729,7 +1729,7 @@ namespace ts {
}
else {
// Get the declaration file path
forEachExpectedEmitFile(host, getDeclFileName, referencedFile);
forEachExpectedEmitFile(host, getDeclFileName, referencedFile, emitOnlyDtsFiles);
}
if (declFileName) {
@ -1758,8 +1758,8 @@ namespace ts {
}
/* @internal */
export function writeDeclarationFile(declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection) {
const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFiles, isBundledEmit);
export function writeDeclarationFile(declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, emitOnlyDtsFiles: boolean) {
const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFiles, isBundledEmit, emitOnlyDtsFiles);
const emitSkipped = emitDeclarationResult.reportedDeclarationError || host.isEmitBlocked(declarationFilePath) || host.getCompilerOptions().noEmit;
if (!emitSkipped) {
const declarationOutput = emitDeclarationResult.referencesOutput

View File

@ -1279,7 +1279,7 @@
"category": "Error",
"code": 2409
},
"All symbols within a 'with' block will be resolved to 'any'.": {
"The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.": {
"category": "Error",
"code": 2410
},
@ -2857,6 +2857,10 @@
"category": "Message",
"code": 6139
},
"Auto discovery for typings is enabled in project '{0}'. Running extra resolution pass for module '{1}' using cache location '{2}'.": {
"category": "Error",
"code": 6140
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005

View File

@ -13,8 +13,11 @@ namespace ts {
_i = 0x10000000, // Use/preference flag for '_i'
}
const id = (s: SourceFile) => s;
const nullTransformers: Transformer[] = [ctx => id];
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult {
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean): EmitResult {
const delimiters = createDelimiterMap();
const brackets = createBracketsMap();
@ -192,7 +195,7 @@ const _super = (function (geti, seti) {
const emittedFilesList: string[] = compilerOptions.listEmittedFiles ? [] : undefined;
const emitterDiagnostics = createDiagnosticCollection();
const newLine = host.getNewLine();
const transformers = getTransformers(compilerOptions);
const transformers: Transformer[] = emitOnlyDtsFiles ? nullTransformers : getTransformers(compilerOptions);
const writer = createTextWriter(newLine);
const {
write,
@ -203,10 +206,8 @@ const _super = (function (geti, seti) {
const sourceMap = createSourceMapWriter(host, writer);
const {
emitStart,
emitEnd,
emitTokenStart,
emitTokenEnd
emitNodeWithSourceMap,
emitTokenWithSourceMap
} = sourceMap;
const comments = createCommentWriter(host, writer, sourceMap);
@ -231,36 +232,27 @@ const _super = (function (geti, seti) {
let isOwnFileEmit: boolean;
let emitSkipped = false;
performance.mark("beforeTransform");
const sourceFiles = getSourceFilesToEmit(host, targetSourceFile);
// Transform the source files
const transformed = transformFiles(
resolver,
host,
getSourceFilesToEmit(host, targetSourceFile),
transformers);
performance.mark("beforeTransform");
const {
transformed,
emitNodeWithSubstitution,
emitNodeWithNotification
} = transformFiles(resolver, host, sourceFiles, transformers);
performance.measure("transformTime", "beforeTransform");
// Extract helpers from the result
const {
getTokenSourceMapRange,
isSubstitutionEnabled,
isEmitNotificationEnabled,
onSubstituteNode,
onEmitNode
} = transformed;
performance.mark("beforePrint");
// Emit each output file
forEachTransformedEmitFile(host, transformed.getSourceFiles(), emitFile);
// Clean up after transformation
transformed.dispose();
performance.mark("beforePrint");
forEachTransformedEmitFile(host, transformed, emitFile, emitOnlyDtsFiles);
performance.measure("printTime", "beforePrint");
// Clean up emit nodes on parse tree
for (const sourceFile of sourceFiles) {
disposeEmitNodes(sourceFile);
}
return {
emitSkipped,
diagnostics: emitterDiagnostics.getDiagnostics(),
@ -271,18 +263,22 @@ const _super = (function (geti, seti) {
function emitFile(jsFilePath: string, sourceMapFilePath: string, declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
// Make sure not to write js file and source map file if any of them cannot be written
if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) {
printFile(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
if (!emitOnlyDtsFiles) {
printFile(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
}
}
else {
emitSkipped = true;
}
if (declarationFilePath) {
emitSkipped = writeDeclarationFile(declarationFilePath, getOriginalSourceFiles(sourceFiles), isBundledEmit, host, resolver, emitterDiagnostics) || emitSkipped;
emitSkipped = writeDeclarationFile(declarationFilePath, getOriginalSourceFiles(sourceFiles), isBundledEmit, host, resolver, emitterDiagnostics, emitOnlyDtsFiles) || emitSkipped;
}
if (!emitSkipped && emittedFilesList) {
emittedFilesList.push(jsFilePath);
if (!emitOnlyDtsFiles) {
emittedFilesList.push(jsFilePath);
}
if (sourceMapFilePath) {
emittedFilesList.push(sourceMapFilePath);
}
@ -351,154 +347,137 @@ const _super = (function (geti, seti) {
currentFileIdentifiers = node.identifiers;
sourceMap.setSourceFile(node);
comments.setSourceFile(node);
emitNodeWithNotification(node, emitWorker);
pipelineEmitWithNotification(EmitContext.SourceFile, node);
}
/**
* Emits a node.
*/
function emit(node: Node) {
emitNodeWithNotification(node, emitWithComments);
}
/**
* Emits a node with comments.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from emit.
*/
function emitWithComments(node: Node) {
emitNodeWithComments(node, emitWithSourceMap);
pipelineEmitWithNotification(EmitContext.Unspecified, node);
}
/**
* Emits a node with source maps.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from emitWithComments.
* Emits an IdentifierName.
*/
function emitWithSourceMap(node: Node) {
emitNodeWithSourceMap(node, emitWorker);
}
function emitIdentifierName(node: Identifier) {
if (node) {
emitNodeWithNotification(node, emitIdentifierNameWithComments);
}
}
function emitIdentifierNameWithComments(node: Identifier) {
emitNodeWithComments(node, emitWorker);
pipelineEmitWithNotification(EmitContext.IdentifierName, node);
}
/**
* Emits an expression node.
*/
function emitExpression(node: Expression) {
emitNodeWithNotification(node, emitExpressionWithComments);
pipelineEmitWithNotification(EmitContext.Expression, node);
}
/**
* Emits an expression with comments.
* Emits a node with possible notification.
*
* NOTE: Do not call this method directly. It is part of the emitExpression pipeline
* and should only be called indirectly from emitExpression.
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called from printSourceFile, emit, emitExpression, or
* emitIdentifierName.
*/
function emitExpressionWithComments(node: Expression) {
emitNodeWithComments(node, emitExpressionWithSourceMap);
function pipelineEmitWithNotification(emitContext: EmitContext, node: Node) {
emitNodeWithNotification(emitContext, node, pipelineEmitWithComments);
}
/**
* Emits an expression with source maps.
* Emits a node with comments.
*
* NOTE: Do not call this method directly. It is part of the emitExpression pipeline
* and should only be called indirectly from emitExpressionWithComments.
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from pipelineEmitWithNotification.
*/
function emitExpressionWithSourceMap(node: Expression) {
emitNodeWithSourceMap(node, emitExpressionWorker);
}
/**
* Emits a node with emit notification if available.
*/
function emitNodeWithNotification(node: Node, emitCallback: (node: Node) => void) {
if (node) {
if (isEmitNotificationEnabled(node)) {
onEmitNode(node, emitCallback);
}
else {
emitCallback(node);
}
}
}
function emitNodeWithSourceMap(node: Node, emitCallback: (node: Node) => void) {
if (node) {
emitStart(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
emitCallback(node);
emitEnd(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
}
}
function getSourceMapRange(node: Node) {
return node.sourceMapRange || node;
}
/**
* Determines whether to skip leading comment emit for a node.
*
* We do not emit comments for NotEmittedStatement nodes or any node that has
* NodeEmitFlags.NoLeadingComments.
*
* @param node A Node.
*/
function shouldSkipLeadingCommentsForNode(node: Node) {
return isNotEmittedStatement(node)
|| (node.emitFlags & NodeEmitFlags.NoLeadingComments) !== 0;
}
/**
* Determines whether to skip source map emit for the start position of a node.
*
* We do not emit source maps for NotEmittedStatement nodes or any node that
* has NodeEmitFlags.NoLeadingSourceMap.
*
* @param node A Node.
*/
function shouldSkipLeadingSourceMapForNode(node: Node) {
return isNotEmittedStatement(node)
|| (node.emitFlags & NodeEmitFlags.NoLeadingSourceMap) !== 0;
}
/**
* Determines whether to skip source map emit for the end position of a node.
*
* We do not emit source maps for NotEmittedStatement nodes or any node that
* has NodeEmitFlags.NoTrailingSourceMap.
*
* @param node A Node.
*/
function shouldSkipTrailingSourceMapForNode(node: Node) {
return isNotEmittedStatement(node)
|| (node.emitFlags & NodeEmitFlags.NoTrailingSourceMap) !== 0;
}
/**
* Determines whether to skip source map emit for a node and its children.
*
* We do not emit source maps for a node that has NodeEmitFlags.NoNestedSourceMaps.
*/
function shouldSkipSourceMapForChildren(node: Node) {
return (node.emitFlags & NodeEmitFlags.NoNestedSourceMaps) !== 0;
}
function emitWorker(node: Node): void {
if (tryEmitSubstitute(node, emitWorker, /*isExpression*/ false)) {
function pipelineEmitWithComments(emitContext: EmitContext, node: Node) {
// Do not emit comments for SourceFile
if (emitContext === EmitContext.SourceFile) {
pipelineEmitWithSourceMap(emitContext, node);
return;
}
emitNodeWithComments(emitContext, node, pipelineEmitWithSourceMap);
}
/**
* Emits a node with source maps.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from pipelineEmitWithComments.
*/
function pipelineEmitWithSourceMap(emitContext: EmitContext, node: Node) {
// Do not emit source mappings for SourceFile or IdentifierName
if (emitContext === EmitContext.SourceFile
|| emitContext === EmitContext.IdentifierName) {
pipelineEmitWithSubstitution(emitContext, node);
return;
}
emitNodeWithSourceMap(emitContext, node, pipelineEmitWithSubstitution);
}
/**
* Emits a node with possible substitution.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from pipelineEmitWithSourceMap or
* pipelineEmitInUnspecifiedContext (when picking a more specific context).
*/
function pipelineEmitWithSubstitution(emitContext: EmitContext, node: Node) {
emitNodeWithSubstitution(emitContext, node, pipelineEmitForContext);
}
/**
* Emits a node.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from pipelineEmitWithSubstitution.
*/
function pipelineEmitForContext(emitContext: EmitContext, node: Node): void {
switch (emitContext) {
case EmitContext.SourceFile: return pipelineEmitInSourceFileContext(node);
case EmitContext.IdentifierName: return pipelineEmitInIdentifierNameContext(node);
case EmitContext.Unspecified: return pipelineEmitInUnspecifiedContext(node);
case EmitContext.Expression: return pipelineEmitInExpressionContext(node);
}
}
/**
* Emits a node in the SourceFile EmitContext.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from pipelineEmitForContext.
*/
function pipelineEmitInSourceFileContext(node: Node): void {
const kind = node.kind;
switch (kind) {
// Top-level nodes
case SyntaxKind.SourceFile:
return emitSourceFile(<SourceFile>node);
}
}
/**
* Emits a node in the IdentifierName EmitContext.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from pipelineEmitForContext.
*/
function pipelineEmitInIdentifierNameContext(node: Node): void {
const kind = node.kind;
switch (kind) {
// Identifiers
case SyntaxKind.Identifier:
return emitIdentifier(<Identifier>node);
}
}
/**
* Emits a node in the Unspecified EmitContext.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from pipelineEmitForContext.
*/
function pipelineEmitInUnspecifiedContext(node: Node): void {
const kind = node.kind;
switch (kind) {
// Pseudo-literals
@ -534,7 +513,8 @@ const _super = (function (geti, seti) {
case SyntaxKind.StringKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.GlobalKeyword:
return writeTokenNode(node);
writeTokenText(kind);
return;
// Parse tree nodes
@ -739,25 +719,24 @@ const _super = (function (geti, seti) {
case SyntaxKind.EnumMember:
return emitEnumMember(<EnumMember>node);
// Top-level nodes
case SyntaxKind.SourceFile:
return emitSourceFile(<SourceFile>node);
// JSDoc nodes (ignored)
// Transformation nodes (ignored)
}
// If the node is an expression, try to emit it as an expression with
// substitution.
if (isExpression(node)) {
return emitExpressionWorker(node);
return pipelineEmitWithSubstitution(EmitContext.Expression, node);
}
}
function emitExpressionWorker(node: Node) {
if (tryEmitSubstitute(node, emitExpressionWorker, /*isExpression*/ true)) {
return;
}
/**
* Emits a node in the Expression EmitContext.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from pipelineEmitForContext.
*/
function pipelineEmitInExpressionContext(node: Node): void {
const kind = node.kind;
switch (kind) {
// Literals
@ -779,7 +758,8 @@ const _super = (function (geti, seti) {
case SyntaxKind.SuperKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.ThisKeyword:
return writeTokenNode(node);
writeTokenText(kind);
return;
// Expressions
case SyntaxKind.ArrayLiteralExpression:
@ -881,7 +861,7 @@ const _super = (function (geti, seti) {
//
function emitIdentifier(node: Identifier) {
if (node.emitFlags & NodeEmitFlags.UMDDefine) {
if (getEmitFlags(node) & EmitFlags.UMDDefine) {
writeLines(umdHelper);
}
else {
@ -1154,7 +1134,7 @@ const _super = (function (geti, seti) {
write("{}");
}
else {
const indentedFlag = node.emitFlags & NodeEmitFlags.Indented;
const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
if (indentedFlag) {
increaseIndent();
}
@ -1170,24 +1150,22 @@ const _super = (function (geti, seti) {
}
function emitPropertyAccessExpression(node: PropertyAccessExpression) {
if (tryEmitConstantValue(node)) {
return;
}
let indentBeforeDot = false;
let indentAfterDot = false;
if (!(node.emitFlags & NodeEmitFlags.NoIndentation)) {
if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) {
const dotRangeStart = node.expression.end;
const dotRangeEnd = skipTrivia(currentText, node.expression.end) + 1;
const dotToken = <Node>{ kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd };
indentBeforeDot = needsIndentation(node, node.expression, dotToken);
indentAfterDot = needsIndentation(node, dotToken, node.name);
}
const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression);
emitExpression(node.expression);
increaseIndentIf(indentBeforeDot);
const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression);
write(shouldEmitDotDot ? ".." : ".");
increaseIndentIf(indentAfterDot);
emit(node.name);
decreaseIndentIf(indentBeforeDot, indentAfterDot);
@ -1201,19 +1179,17 @@ const _super = (function (geti, seti) {
const text = getLiteralTextOfNode(<LiteralExpression>expression);
return text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0;
}
else {
else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) {
// check if constant enum value is integer
const constantValue = tryGetConstEnumValue(expression);
const constantValue = getConstantValue(expression);
// isFinite handles cases when constantValue is undefined
return isFinite(constantValue) && Math.floor(constantValue) === constantValue;
return isFinite(constantValue)
&& Math.floor(constantValue) === constantValue
&& compilerOptions.removeComments;
}
}
function emitElementAccessExpression(node: ElementAccessExpression) {
if (tryEmitConstantValue(node)) {
return;
}
emitExpression(node.expression);
write("[");
emitExpression(node.argumentExpression);
@ -1419,7 +1395,7 @@ const _super = (function (geti, seti) {
}
function emitBlockStatements(node: Block) {
if (node.emitFlags & NodeEmitFlags.SingleLine) {
if (getEmitFlags(node) & EmitFlags.SingleLine) {
emitList(node, node.statements, ListFormat.SingleLineBlockStatements);
}
else {
@ -1623,12 +1599,12 @@ const _super = (function (geti, seti) {
const body = node.body;
if (body) {
if (isBlock(body)) {
const indentedFlag = node.emitFlags & NodeEmitFlags.Indented;
const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
if (indentedFlag) {
increaseIndent();
}
if (node.emitFlags & NodeEmitFlags.ReuseTempVariableScope) {
if (getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) {
emitSignatureHead(node);
emitBlockFunctionBody(node, body);
}
@ -1672,7 +1648,7 @@ const _super = (function (geti, seti) {
// * A non-synthesized body's start and end position are on different lines.
// * Any statement in the body starts on a new line.
if (body.emitFlags & NodeEmitFlags.SingleLine) {
if (getEmitFlags(body) & EmitFlags.SingleLine) {
return true;
}
@ -1743,7 +1719,7 @@ const _super = (function (geti, seti) {
write("class");
emitNodeWithPrefix(" ", node.name, emitIdentifierName);
const indentedFlag = node.emitFlags & NodeEmitFlags.Indented;
const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
if (indentedFlag) {
increaseIndent();
}
@ -2076,8 +2052,8 @@ const _super = (function (geti, seti) {
// "comment1" is not considered to be leading comment for node.initializer
// but rather a trailing comment on the previous node.
const initializer = node.initializer;
if (!shouldSkipLeadingCommentsForNode(initializer)) {
const commentRange = initializer.commentRange || initializer;
if ((getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) {
const commentRange = getCommentRange(initializer);
emitTrailingCommentsOfPosition(commentRange.pos);
}
@ -2149,23 +2125,23 @@ const _super = (function (geti, seti) {
}
function emitHelpers(node: Node) {
const emitFlags = node.emitFlags;
const emitFlags = getEmitFlags(node);
let helpersEmitted = false;
if (emitFlags & NodeEmitFlags.EmitEmitHelpers) {
if (emitFlags & EmitFlags.EmitEmitHelpers) {
helpersEmitted = emitEmitHelpers(currentSourceFile);
}
if (emitFlags & NodeEmitFlags.EmitExportStar) {
if (emitFlags & EmitFlags.EmitExportStar) {
writeLines(exportStarHelper);
helpersEmitted = true;
}
if (emitFlags & NodeEmitFlags.EmitSuperHelper) {
if (emitFlags & EmitFlags.EmitSuperHelper) {
writeLines(superHelper);
helpersEmitted = true;
}
if (emitFlags & NodeEmitFlags.EmitAdvancedSuperHelper) {
if (emitFlags & EmitFlags.EmitAdvancedSuperHelper) {
writeLines(advancedSuperHelper);
helpersEmitted = true;
}
@ -2287,36 +2263,6 @@ const _super = (function (geti, seti) {
}
}
function tryEmitSubstitute(node: Node, emitNode: (node: Node) => void, isExpression: boolean) {
if (isSubstitutionEnabled(node) && (node.emitFlags & NodeEmitFlags.NoSubstitution) === 0) {
const substitute = onSubstituteNode(node, isExpression);
if (substitute !== node) {
substitute.emitFlags |= NodeEmitFlags.NoSubstitution;
emitNode(substitute);
return true;
}
}
return false;
}
function tryEmitConstantValue(node: PropertyAccessExpression | ElementAccessExpression): boolean {
const constantValue = tryGetConstEnumValue(node);
if (constantValue !== undefined) {
write(String(constantValue));
if (!compilerOptions.removeComments) {
const propertyName = isPropertyAccessExpression(node)
? declarationNameToString(node.name)
: getTextOfNode(node.argumentExpression);
write(` /* ${propertyName} */`);
}
return true;
}
return false;
}
function emitEmbeddedStatement(node: Statement) {
if (isBlock(node)) {
write(" ");
@ -2440,7 +2386,7 @@ const _super = (function (geti, seti) {
}
if (shouldEmitInterveningComments) {
const commentRange = child.commentRange || child;
const commentRange = getCommentRange(child);
emitTrailingCommentsOfPosition(commentRange.pos);
}
else {
@ -2496,31 +2442,13 @@ const _super = (function (geti, seti) {
}
function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) {
const tokenStartPos = emitTokenStart(token, pos, contextNode, shouldSkipLeadingSourceMapForToken, getTokenSourceMapRange);
const tokenEndPos = writeTokenText(token, tokenStartPos);
return emitTokenEnd(token, tokenEndPos, contextNode, shouldSkipTrailingSourceMapForToken, getTokenSourceMapRange);
}
function shouldSkipLeadingSourceMapForToken(contextNode: Node) {
return (contextNode.emitFlags & NodeEmitFlags.NoTokenLeadingSourceMaps) !== 0;
}
function shouldSkipTrailingSourceMapForToken(contextNode: Node) {
return (contextNode.emitFlags & NodeEmitFlags.NoTokenTrailingSourceMaps) !== 0;
return emitTokenWithSourceMap(contextNode, token, pos, writeTokenText);
}
function writeTokenText(token: SyntaxKind, pos?: number) {
const tokenString = tokenToString(token);
write(tokenString);
return positionIsSynthesized(pos) ? -1 : pos + tokenString.length;
}
function writeTokenNode(node: Node) {
if (node) {
emitStart(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
writeTokenText(node.kind);
emitEnd(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
}
return pos < 0 ? pos : pos + tokenString.length;
}
function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) {
@ -2685,16 +2613,6 @@ const _super = (function (geti, seti) {
return getLiteralText(node, currentSourceFile, languageVersion);
}
function tryGetConstEnumValue(node: Node): number {
if (compilerOptions.isolatedModules) {
return undefined;
}
return isPropertyAccessExpression(node) || isElementAccessExpression(node)
? resolver.getConstantValue(<PropertyAccessExpression | ElementAccessExpression>node)
: undefined;
}
function isSingleLineEmptyBlock(block: Block) {
return !block.multiLine
&& block.statements.length === 0

View File

@ -76,7 +76,7 @@ namespace ts {
// the original node. We also need to exclude specific properties and only include own-
// properties (to skip members already defined on the shared prototype).
const clone = <T>createNode(node.kind, /*location*/ undefined, node.flags);
clone.original = node;
setOriginalNode(clone, node);
for (const key in node) {
if (clone.hasOwnProperty(key) || !node.hasOwnProperty(key)) {
@ -435,7 +435,7 @@ namespace ts {
export function createPropertyAccess(expression: Expression, name: string | Identifier, location?: TextRange, flags?: NodeFlags) {
const node = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression, location, flags);
node.expression = parenthesizeForAccess(expression);
node.emitFlags = NodeEmitFlags.NoIndentation;
(node.emitNode || (node.emitNode = {})).flags |= EmitFlags.NoIndentation;
node.name = typeof name === "string" ? createIdentifier(name) : name;
return node;
}
@ -444,7 +444,7 @@ namespace ts {
if (node.expression !== expression || node.name !== name) {
const propertyAccess = createPropertyAccess(expression, name, /*location*/ node, node.flags);
// Because we are updating existed propertyAccess we want to inherit its emitFlags instead of using default from createPropertyAccess
propertyAccess.emitFlags = node.emitFlags;
(propertyAccess.emitNode || (propertyAccess.emitNode = {})).flags = getEmitFlags(node);
return updateNode(propertyAccess, node);
}
return node;
@ -1551,7 +1551,7 @@ namespace ts {
}
else {
const expression = isIdentifier(memberName) ? createPropertyAccess(target, memberName, location) : createElementAccess(target, memberName, location);
expression.emitFlags |= NodeEmitFlags.NoNestedSourceMaps;
(expression.emitNode || (expression.emitNode = {})).flags |= EmitFlags.NoNestedSourceMaps;
return expression;
}
}
@ -1744,7 +1744,7 @@ namespace ts {
);
// Mark this node as originally an async function
generatorFunc.emitFlags |= NodeEmitFlags.AsyncFunctionBody;
(generatorFunc.emitNode || (generatorFunc.emitNode = {})).flags |= EmitFlags.AsyncFunctionBody;
return createCall(
createHelperName(externalHelpersModuleName, "__awaiter"),
@ -2222,7 +2222,7 @@ namespace ts {
target.push(startOnNewLine(createStatement(createLiteral("use strict"))));
foundUseStrict = true;
}
if (statement.emitFlags & NodeEmitFlags.CustomPrologue) {
if (getEmitFlags(statement) & EmitFlags.CustomPrologue) {
target.push(visitor ? visitNode(statement, visitor, isStatement) : statement);
}
else {
@ -2643,14 +2643,178 @@ namespace ts {
export function setOriginalNode<T extends Node>(node: T, original: Node): T {
node.original = original;
if (original) {
const { emitFlags, commentRange, sourceMapRange } = original;
if (emitFlags) node.emitFlags = emitFlags;
if (commentRange) node.commentRange = commentRange;
if (sourceMapRange) node.sourceMapRange = sourceMapRange;
const emitNode = original.emitNode;
if (emitNode) node.emitNode = mergeEmitNode(emitNode, node.emitNode);
}
return node;
}
function mergeEmitNode(sourceEmitNode: EmitNode, destEmitNode: EmitNode) {
const { flags, commentRange, sourceMapRange, tokenSourceMapRanges } = sourceEmitNode;
if (!destEmitNode && (flags || commentRange || sourceMapRange || tokenSourceMapRanges)) destEmitNode = {};
if (flags) destEmitNode.flags = flags;
if (commentRange) destEmitNode.commentRange = commentRange;
if (sourceMapRange) destEmitNode.sourceMapRange = sourceMapRange;
if (tokenSourceMapRanges) destEmitNode.tokenSourceMapRanges = mergeTokenSourceMapRanges(tokenSourceMapRanges, destEmitNode.tokenSourceMapRanges);
return destEmitNode;
}
function mergeTokenSourceMapRanges(sourceRanges: Map<TextRange>, destRanges: Map<TextRange>) {
if (!destRanges) destRanges = createMap<TextRange>();
copyProperties(sourceRanges, destRanges);
return destRanges;
}
/**
* Clears any EmitNode entries from parse-tree nodes.
* @param sourceFile A source file.
*/
export function disposeEmitNodes(sourceFile: SourceFile) {
// During transformation we may need to annotate a parse tree node with transient
// transformation properties. As parse tree nodes live longer than transformation
// nodes, we need to make sure we reclaim any memory allocated for custom ranges
// from these nodes to ensure we do not hold onto entire subtrees just for position
// information. We also need to reset these nodes to a pre-transformation state
// for incremental parsing scenarios so that we do not impact later emit.
sourceFile = getSourceFileOfNode(getParseTreeNode(sourceFile));
const emitNode = sourceFile && sourceFile.emitNode;
const annotatedNodes = emitNode && emitNode.annotatedNodes;
if (annotatedNodes) {
for (const node of annotatedNodes) {
node.emitNode = undefined;
}
}
}
/**
* Associates a node with the current transformation, initializing
* various transient transformation properties.
*
* @param node The node.
*/
function getOrCreateEmitNode(node: Node) {
if (!node.emitNode) {
if (isParseTreeNode(node)) {
// To avoid holding onto transformation artifacts, we keep track of any
// parse tree node we are annotating. This allows us to clean them up after
// all transformations have completed.
if (node.kind === SyntaxKind.SourceFile) {
return node.emitNode = { annotatedNodes: [node] };
}
const sourceFile = getSourceFileOfNode(node);
getOrCreateEmitNode(sourceFile).annotatedNodes.push(node);
}
node.emitNode = {};
}
return node.emitNode;
}
/**
* Gets flags that control emit behavior of a node.
*
* @param node The node.
*/
export function getEmitFlags(node: Node) {
const emitNode = node.emitNode;
return emitNode && emitNode.flags;
}
/**
* Sets flags that control emit behavior of a node.
*
* @param node The node.
* @param emitFlags The NodeEmitFlags for the node.
*/
export function setEmitFlags<T extends Node>(node: T, emitFlags: EmitFlags) {
getOrCreateEmitNode(node).flags = emitFlags;
return node;
}
/**
* Sets a custom text range to use when emitting source maps.
*
* @param node The node.
* @param range The text range.
*/
export function setSourceMapRange<T extends Node>(node: T, range: TextRange) {
getOrCreateEmitNode(node).sourceMapRange = range;
return node;
}
/**
* Sets the TextRange to use for source maps for a token of a node.
*
* @param node The node.
* @param token The token.
* @param range The text range.
*/
export function setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: TextRange) {
const emitNode = getOrCreateEmitNode(node);
const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = createMap<TextRange>());
tokenSourceMapRanges[token] = range;
return node;
}
/**
* Sets a custom text range to use when emitting comments.
*/
export function setCommentRange<T extends Node>(node: T, range: TextRange) {
getOrCreateEmitNode(node).commentRange = range;
return node;
}
/**
* Gets a custom text range to use when emitting comments.
*
* @param node The node.
*/
export function getCommentRange(node: Node) {
const emitNode = node.emitNode;
return (emitNode && emitNode.commentRange) || node;
}
/**
* Gets a custom text range to use when emitting source maps.
*
* @param node The node.
*/
export function getSourceMapRange(node: Node) {
const emitNode = node.emitNode;
return (emitNode && emitNode.sourceMapRange) || node;
}
/**
* Gets the TextRange to use for source maps for a token of a node.
*
* @param node The node.
* @param token The token.
*/
export function getTokenSourceMapRange(node: Node, token: SyntaxKind) {
const emitNode = node.emitNode;
const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges;
return tokenSourceMapRanges && tokenSourceMapRanges[token];
}
/**
* Gets the constant value to emit for an expression.
*/
export function getConstantValue(node: PropertyAccessExpression | ElementAccessExpression) {
const emitNode = node.emitNode;
return emitNode && emitNode.constantValue;
}
/**
* Sets the constant value to emit for an expression.
*/
export function setConstantValue(node: PropertyAccessExpression | ElementAccessExpression, value: number) {
const emitNode = getOrCreateEmitNode(node);
emitNode.constantValue = value;
return node;
}
export function setTextRange<T extends TextRange>(node: T, location: TextRange): T {
if (location) {
node.pos = location.pos;
@ -2692,7 +2856,7 @@ namespace ts {
return undefined;
}
/**
/**
* Get the name of a target module from an import/export declaration as should be written in the emitted output.
* The emitted output name can be different from the input if:
* 1. The module has a /// <amd-module name="<new name>" />

View File

@ -0,0 +1,744 @@
/// <reference path="core.ts" />
/// <reference path="diagnosticInformationMap.generated.ts" />
namespace ts {
/* @internal */
export function trace(host: ModuleResolutionHost, message: DiagnosticMessage, ...args: any[]): void;
export function trace(host: ModuleResolutionHost, message: DiagnosticMessage): void {
host.trace(formatMessage.apply(undefined, arguments));
}
/* @internal */
export function isTraceEnabled(compilerOptions: CompilerOptions, host: ModuleResolutionHost): boolean {
return compilerOptions.traceResolution && host.trace !== undefined;
}
/* @internal */
export function createResolvedModule(resolvedFileName: string, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
return { resolvedModule: resolvedFileName ? { resolvedFileName, isExternalLibraryImport } : undefined, failedLookupLocations };
}
function moduleHasNonRelativeName(moduleName: string): boolean {
return !(isRootedDiskPath(moduleName) || isExternalModuleNameRelative(moduleName));
}
/* @internal */
export interface ModuleResolutionState {
host: ModuleResolutionHost;
compilerOptions: CompilerOptions;
traceEnabled: boolean;
// skip .tsx files if jsx is not enabled
skipTsx: boolean;
}
function tryReadTypesSection(packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string {
const jsonContent = readJson(packageJsonPath, state.host);
function tryReadFromField(fieldName: string) {
if (hasProperty(jsonContent, fieldName)) {
const typesFile = (<any>jsonContent)[fieldName];
if (typeof typesFile === "string") {
const typesFilePath = normalizePath(combinePaths(baseDirectory, typesFile));
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, typesFile, typesFilePath);
}
return typesFilePath;
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, fieldName, typeof typesFile);
}
}
}
}
const typesFilePath = tryReadFromField("typings") || tryReadFromField("types");
if (typesFilePath) {
return typesFilePath;
}
// Use the main module for inferring types if no types package specified and the allowJs is set
if (state.compilerOptions.allowJs && jsonContent.main && typeof jsonContent.main === "string") {
if (state.traceEnabled) {
trace(state.host, Diagnostics.No_types_specified_in_package_json_but_allowJs_is_set_so_returning_main_value_of_0, jsonContent.main);
}
const mainFilePath = normalizePath(combinePaths(baseDirectory, jsonContent.main));
return mainFilePath;
}
return undefined;
}
function readJson(path: string, host: ModuleResolutionHost): { typings?: string, types?: string, main?: string } {
try {
const jsonText = host.readFile(path);
return jsonText ? JSON.parse(jsonText) : {};
}
catch (e) {
// gracefully handle if readFile fails or returns not JSON
return {};
}
}
const typeReferenceExtensions = [".d.ts"];
export function getEffectiveTypeRoots(options: CompilerOptions, host: { directoryExists?: (directoryName: string) => boolean, getCurrentDirectory?: () => string }): string[] | undefined {
if (options.typeRoots) {
return options.typeRoots;
}
let currentDirectory: string;
if (options.configFilePath) {
currentDirectory = getDirectoryPath(options.configFilePath);
}
else if (host.getCurrentDirectory) {
currentDirectory = host.getCurrentDirectory();
}
return currentDirectory && getDefaultTypeRoots(currentDirectory, host);
}
/**
* Returns the path to every node_modules/@types directory from some ancestor directory.
* Returns undefined if there are none.
*/
function getDefaultTypeRoots(currentDirectory: string, host: { directoryExists?: (directoryName: string) => boolean }): string[] | undefined {
if (!host.directoryExists) {
return [combinePaths(currentDirectory, nodeModulesAtTypes)];
// And if it doesn't exist, tough.
}
let typeRoots: string[];
while (true) {
const atTypes = combinePaths(currentDirectory, nodeModulesAtTypes);
if (host.directoryExists(atTypes)) {
(typeRoots || (typeRoots = [])).push(atTypes);
}
const parent = getDirectoryPath(currentDirectory);
if (parent === currentDirectory) {
break;
}
currentDirectory = parent;
}
return typeRoots;
}
const nodeModulesAtTypes = combinePaths("node_modules", "@types");
/**
* @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown.
* This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups
* is assumed to be the same as root directory of the project.
*/
export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost): ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(options, host);
const moduleResolutionState: ModuleResolutionState = {
compilerOptions: options,
host: host,
skipTsx: true,
traceEnabled
};
const typeRoots = getEffectiveTypeRoots(options, host);
if (traceEnabled) {
if (containingFile === undefined) {
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_not_set, typeReferenceDirectiveName);
}
else {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_1, typeReferenceDirectiveName, typeRoots);
}
}
else {
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_not_set, typeReferenceDirectiveName, containingFile);
}
else {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2, typeReferenceDirectiveName, containingFile, typeRoots);
}
}
}
const failedLookupLocations: string[] = [];
// Check primary library paths
if (typeRoots && typeRoots.length) {
if (traceEnabled) {
trace(host, Diagnostics.Resolving_with_primary_search_path_0, typeRoots.join(", "));
}
const primarySearchPaths = typeRoots;
for (const typeRoot of primarySearchPaths) {
const candidate = combinePaths(typeRoot, typeReferenceDirectiveName);
const candidateDirectory = getDirectoryPath(candidate);
const resolvedFile = loadNodeModuleFromDirectory(typeReferenceExtensions, candidate, failedLookupLocations,
!directoryProbablyExists(candidateDirectory, host), moduleResolutionState);
if (resolvedFile) {
if (traceEnabled) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, true);
}
return {
resolvedTypeReferenceDirective: { primary: true, resolvedFileName: resolvedFile },
failedLookupLocations
};
}
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Root_directory_cannot_be_determined_skipping_primary_search_paths);
}
}
let resolvedFile: string;
let initialLocationForSecondaryLookup: string;
if (containingFile) {
initialLocationForSecondaryLookup = getDirectoryPath(containingFile);
}
if (initialLocationForSecondaryLookup !== undefined) {
// check secondary locations
if (traceEnabled) {
trace(host, Diagnostics.Looking_up_in_node_modules_folder_initial_location_0, initialLocationForSecondaryLookup);
}
resolvedFile = loadModuleFromNodeModules(typeReferenceDirectiveName, initialLocationForSecondaryLookup, failedLookupLocations, moduleResolutionState, /*checkOneLevel*/ false);
if (traceEnabled) {
if (resolvedFile) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, false);
}
else {
trace(host, Diagnostics.Type_reference_directive_0_was_not_resolved, typeReferenceDirectiveName);
}
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_modules_folder);
}
}
return {
resolvedTypeReferenceDirective: resolvedFile
? { primary: false, resolvedFileName: resolvedFile }
: undefined,
failedLookupLocations
};
}
/**
* Given a set of options, returns the set of type directive names
* that should be included for this program automatically.
* This list could either come from the config file,
* or from enumerating the types root + initial secondary types lookup location.
* More type directives might appear in the program later as a result of loading actual source files;
* this list is only the set of defaults that are implicitly included.
*/
export function getAutomaticTypeDirectiveNames(options: CompilerOptions, host: ModuleResolutionHost): string[] {
// Use explicit type list from tsconfig.json
if (options.types) {
return options.types;
}
// Walk the primary type lookup locations
const result: string[] = [];
if (host.directoryExists && host.getDirectories) {
const typeRoots = getEffectiveTypeRoots(options, host);
if (typeRoots) {
for (const root of typeRoots) {
if (host.directoryExists(root)) {
for (const typeDirectivePath of host.getDirectories(root)) {
const normalized = normalizePath(typeDirectivePath);
const packageJsonPath = pathToPackageJson(combinePaths(root, normalized));
// tslint:disable-next-line:no-null-keyword
const isNotNeededPackage = host.fileExists(packageJsonPath) && readJson(packageJsonPath, host).typings === null;
if (!isNotNeededPackage) {
// Return just the type directive names
result.push(getBaseFileName(normalized));
}
}
}
}
}
}
return result;
}
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
if (traceEnabled) {
trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile);
}
let moduleResolution = compilerOptions.moduleResolution;
if (moduleResolution === undefined) {
moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
if (traceEnabled) {
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]);
}
}
let result: ResolvedModuleWithFailedLookupLocations;
switch (moduleResolution) {
case ModuleResolutionKind.NodeJs:
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host);
break;
case ModuleResolutionKind.Classic:
result = classicNameResolver(moduleName, containingFile, compilerOptions, host);
break;
}
if (traceEnabled) {
if (result.resolvedModule) {
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName);
}
else {
trace(host, Diagnostics.Module_name_0_was_not_resolved, moduleName);
}
}
return result;
}
/*
* Every module resolution kind can has its specific understanding how to load module from a specific path on disk
* I.e. for path '/a/b/c':
* - Node loader will first to try to check if '/a/b/c' points to a file with some supported extension and if this fails
* it will try to load module from directory: directory '/a/b/c' should exist and it should have either 'package.json' with
* 'typings' entry or file 'index' with some supported extension
* - Classic loader will only try to interpret '/a/b/c' as file.
*/
type ResolutionKindSpecificLoader = (candidate: string, extensions: string[], failedLookupLocations: string[], onlyRecordFailures: boolean, state: ModuleResolutionState) => string;
/**
* Any module resolution kind can be augmented with optional settings: 'baseUrl', 'paths' and 'rootDirs' - they are used to
* mitigate differences between design time structure of the project and its runtime counterpart so the same import name
* can be resolved successfully by TypeScript compiler and runtime module loader.
* If these settings are set then loading procedure will try to use them to resolve module name and it can of failure it will
* fallback to standard resolution routine.
*
* - baseUrl - this setting controls how non-relative module names are resolved. If this setting is specified then non-relative
* names will be resolved relative to baseUrl: i.e. if baseUrl is '/a/b' then candidate location to resolve module name 'c/d' will
* be '/a/b/c/d'
* - paths - this setting can only be used when baseUrl is specified. allows to tune how non-relative module names
* will be resolved based on the content of the module name.
* Structure of 'paths' compiler options
* 'paths': {
* pattern-1: [...substitutions],
* pattern-2: [...substitutions],
* ...
* pattern-n: [...substitutions]
* }
* Pattern here is a string that can contain zero or one '*' character. During module resolution module name will be matched against
* all patterns in the list. Matching for patterns that don't contain '*' means that module name must be equal to pattern respecting the case.
* If pattern contains '*' then to match pattern "<prefix>*<suffix>" module name must start with the <prefix> and end with <suffix>.
* <MatchedStar> denotes part of the module name between <prefix> and <suffix>.
* If module name can be matches with multiple patterns then pattern with the longest prefix will be picked.
* After selecting pattern we'll use list of substitutions to get candidate locations of the module and the try to load module
* from the candidate location.
* Substitution is a string that can contain zero or one '*'. To get candidate location from substitution we'll pick every
* substitution in the list and replace '*' with <MatchedStar> string. If candidate location is not rooted it
* will be converted to absolute using baseUrl.
* For example:
* baseUrl: /a/b/c
* "paths": {
* // match all module names
* "*": [
* "*", // use matched name as is,
* // <matched name> will be looked as /a/b/c/<matched name>
*
* "folder1/*" // substitution will convert matched name to 'folder1/<matched name>',
* // since it is not rooted then final candidate location will be /a/b/c/folder1/<matched name>
* ],
* // match module names that start with 'components/'
* "components/*": [ "/root/components/*" ] // substitution will convert /components/folder1/<matched name> to '/root/components/folder1/<matched name>',
* // it is rooted so it will be final candidate location
* }
*
* 'rootDirs' allows the project to be spreaded across multiple locations and resolve modules with relative names as if
* they were in the same location. For example lets say there are two files
* '/local/src/content/file1.ts'
* '/shared/components/contracts/src/content/protocols/file2.ts'
* After bundling content of '/shared/components/contracts/src' will be merged with '/local/src' so
* if file1 has the following import 'import {x} from "./protocols/file2"' it will be resolved successfully in runtime.
* 'rootDirs' provides the way to tell compiler that in order to get the whole project it should behave as if content of all
* root dirs were merged together.
* I.e. for the example above 'rootDirs' will have two entries: [ '/local/src', '/shared/components/contracts/src' ].
* Compiler will first convert './protocols/file2' into absolute path relative to the location of containing file:
* '/local/src/content/protocols/file2' and try to load it - failure.
* Then it will search 'rootDirs' looking for a longest matching prefix of this absolute path and if such prefix is found - absolute path will
* be converted to a path relative to found rootDir entry './content/protocols/file2' (*). As a last step compiler will check all remaining
* entries in 'rootDirs', use them to build absolute path out of (*) and try to resolve module from this location.
*/
function tryLoadModuleUsingOptionalResolutionSettings(moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
failedLookupLocations: string[], supportedExtensions: string[], state: ModuleResolutionState): string {
if (moduleHasNonRelativeName(moduleName)) {
return tryLoadModuleUsingBaseUrl(moduleName, loader, failedLookupLocations, supportedExtensions, state);
}
else {
return tryLoadModuleUsingRootDirs(moduleName, containingDirectory, loader, failedLookupLocations, supportedExtensions, state);
}
}
function tryLoadModuleUsingRootDirs(moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
failedLookupLocations: string[], supportedExtensions: string[], state: ModuleResolutionState): string {
if (!state.compilerOptions.rootDirs) {
return undefined;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.rootDirs_option_is_set_using_it_to_resolve_relative_module_name_0, moduleName);
}
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
let matchedRootDir: string;
let matchedNormalizedPrefix: string;
for (const rootDir of state.compilerOptions.rootDirs) {
// rootDirs are expected to be absolute
// in case of tsconfig.json this will happen automatically - compiler will expand relative names
// using location of tsconfig.json as base location
let normalizedRoot = normalizePath(rootDir);
if (!endsWith(normalizedRoot, directorySeparator)) {
normalizedRoot += directorySeparator;
}
const isLongestMatchingPrefix =
startsWith(candidate, normalizedRoot) &&
(matchedNormalizedPrefix === undefined || matchedNormalizedPrefix.length < normalizedRoot.length);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Checking_if_0_is_the_longest_matching_prefix_for_1_2, normalizedRoot, candidate, isLongestMatchingPrefix);
}
if (isLongestMatchingPrefix) {
matchedNormalizedPrefix = normalizedRoot;
matchedRootDir = rootDir;
}
}
if (matchedNormalizedPrefix) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Longest_matching_prefix_for_0_is_1, candidate, matchedNormalizedPrefix);
}
const suffix = candidate.substr(matchedNormalizedPrefix.length);
// first - try to load from a initial location
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, matchedNormalizedPrefix, candidate);
}
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(containingDirectory, state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.Trying_other_entries_in_rootDirs);
}
// then try to resolve using remaining entries in rootDirs
for (const rootDir of state.compilerOptions.rootDirs) {
if (rootDir === matchedRootDir) {
// skip the initially matched entry
continue;
}
const candidate = combinePaths(normalizePath(rootDir), suffix);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, rootDir, candidate);
}
const baseDirectory = getDirectoryPath(candidate);
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(baseDirectory, state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.Module_resolution_using_rootDirs_has_failed);
}
}
return undefined;
}
function tryLoadModuleUsingBaseUrl(moduleName: string, loader: ResolutionKindSpecificLoader, failedLookupLocations: string[],
supportedExtensions: string[], state: ModuleResolutionState): string {
if (!state.compilerOptions.baseUrl) {
return undefined;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, state.compilerOptions.baseUrl, moduleName);
}
// string is for exact match
let matchedPattern: Pattern | string | undefined = undefined;
if (state.compilerOptions.paths) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName);
}
matchedPattern = matchPatternOrExact(getOwnKeys(state.compilerOptions.paths), moduleName);
}
if (matchedPattern) {
const matchedStar = typeof matchedPattern === "string" ? undefined : matchedText(matchedPattern, moduleName);
const matchedPatternText = typeof matchedPattern === "string" ? matchedPattern : patternText(matchedPattern);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText);
}
for (const subst of state.compilerOptions.paths[matchedPatternText]) {
const path = matchedStar ? subst.replace("*", matchedStar) : subst;
const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, path));
if (state.traceEnabled) {
trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path);
}
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
}
return undefined;
}
else {
const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, moduleName));
if (state.traceEnabled) {
trace(state.host, Diagnostics.Resolving_module_name_0_relative_to_base_url_1_2, moduleName, state.compilerOptions.baseUrl, candidate);
}
return loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
}
}
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const containingDirectory = getDirectoryPath(containingFile);
const supportedExtensions = getSupportedExtensions(compilerOptions);
const traceEnabled = isTraceEnabled(compilerOptions, host);
const failedLookupLocations: string[] = [];
const state = { compilerOptions, host, traceEnabled, skipTsx: false };
let resolvedFileName = tryLoadModuleUsingOptionalResolutionSettings(moduleName, containingDirectory, nodeLoadModuleByRelativeName,
failedLookupLocations, supportedExtensions, state);
let isExternalLibraryImport = false;
if (!resolvedFileName) {
if (moduleHasNonRelativeName(moduleName)) {
if (traceEnabled) {
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder, moduleName);
}
resolvedFileName = loadModuleFromNodeModules(moduleName, containingDirectory, failedLookupLocations, state, /*checkOneLevel*/ false);
isExternalLibraryImport = resolvedFileName !== undefined;
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
resolvedFileName = nodeLoadModuleByRelativeName(candidate, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
}
}
if (resolvedFileName && host.realpath) {
const originalFileName = resolvedFileName;
resolvedFileName = normalizePath(host.realpath(resolvedFileName));
if (traceEnabled) {
trace(host, Diagnostics.Resolving_real_path_for_0_result_1, originalFileName, resolvedFileName);
}
}
return createResolvedModule(resolvedFileName, isExternalLibraryImport, failedLookupLocations);
}
function nodeLoadModuleByRelativeName(candidate: string, supportedExtensions: string[], failedLookupLocations: string[],
onlyRecordFailures: boolean, state: ModuleResolutionState): string {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0, candidate);
}
const resolvedFileName = !pathEndsWithDirectorySeparator(candidate) && loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, onlyRecordFailures, state);
return resolvedFileName || loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, onlyRecordFailures, state);
}
/* @internal */
export function directoryProbablyExists(directoryName: string, host: { directoryExists?: (directoryName: string) => boolean }): boolean {
// if host does not support 'directoryExists' assume that directory will exist
return !host.directoryExists || host.directoryExists(directoryName);
}
/**
* @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary
* in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations.
*/
function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
// First, try adding an extension. An import of "foo" could be matched by a file "foo.ts", or "foo.js" by "foo.js.ts"
const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, failedLookupLocation, onlyRecordFailures, state);
if (resolvedByAddingExtension) {
return resolvedByAddingExtension;
}
// If that didn't work, try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one;
// e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts"
if (hasJavaScriptFileExtension(candidate)) {
const extensionless = removeFileExtension(candidate);
if (state.traceEnabled) {
const extension = candidate.substring(extensionless.length);
trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension);
}
return tryAddingExtensions(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state);
}
}
/** Try to return an existing file that adds one of the `extensions` to `candidate`. */
function tryAddingExtensions(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
if (!onlyRecordFailures) {
// check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
const directory = getDirectoryPath(candidate);
if (directory) {
onlyRecordFailures = !directoryProbablyExists(directory, state.host);
}
}
return forEach(extensions, ext =>
!(state.skipTsx && isJsxOrTsxExtension(ext)) && tryFile(candidate + ext, failedLookupLocation, onlyRecordFailures, state));
}
/** Return the file if it exists. */
function tryFile(fileName: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
if (!onlyRecordFailures && state.host.fileExists(fileName)) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName);
}
return fileName;
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_does_not_exist, fileName);
}
failedLookupLocation.push(fileName);
return undefined;
}
}
function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
const packageJsonPath = pathToPackageJson(candidate);
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
if (directoryExists && state.host.fileExists(packageJsonPath)) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
const typesFile = tryReadTypesSection(packageJsonPath, candidate, state);
if (typesFile) {
const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(typesFile), state.host);
// A package.json "typings" may specify an exact filename, or may choose to omit an extension.
const result = tryFile(typesFile, failedLookupLocation, onlyRecordFailures, state) ||
tryAddingExtensions(typesFile, extensions, failedLookupLocation, onlyRecordFailures, state);
if (result) {
return result;
}
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_does_not_have_types_field);
}
}
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_does_not_exist, packageJsonPath);
}
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
failedLookupLocation.push(packageJsonPath);
}
return loadModuleFromFile(combinePaths(candidate, "index"), extensions, failedLookupLocation, !directoryExists, state);
}
function pathToPackageJson(directory: string): string {
return combinePaths(directory, "package.json");
}
function loadModuleFromNodeModulesFolder(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string {
const nodeModulesFolder = combinePaths(directory, "node_modules");
const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host);
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
const supportedExtensions = getSupportedExtensions(state.compilerOptions);
let result = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, !nodeModulesFolderExists, state);
if (result) {
return result;
}
result = loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
if (result) {
return result;
}
}
/* @internal */
export function loadModuleFromNodeModules(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState, checkOneLevel: boolean): string {
directory = normalizeSlashes(directory);
while (true) {
const baseName = getBaseFileName(directory);
if (baseName !== "node_modules") {
// Try to load source from the package
const packageResult = loadModuleFromNodeModulesFolder(moduleName, directory, failedLookupLocations, state);
if (packageResult && hasTypeScriptFileExtension(packageResult)) {
// Always prefer a TypeScript (.ts, .tsx, .d.ts) file shipped with the package
return packageResult;
}
else {
// Else prefer a types package over non-TypeScript results (e.g. JavaScript files)
const typesResult = loadModuleFromNodeModulesFolder(combinePaths("@types", moduleName), directory, failedLookupLocations, state);
if (typesResult || packageResult) {
return typesResult || packageResult;
}
}
}
const parentPath = getDirectoryPath(directory);
if (parentPath === directory || checkOneLevel) {
break;
}
directory = parentPath;
}
return undefined;
}
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
const state = { compilerOptions, host, traceEnabled, skipTsx: !compilerOptions.jsx };
const failedLookupLocations: string[] = [];
const supportedExtensions = getSupportedExtensions(compilerOptions);
let containingDirectory = getDirectoryPath(containingFile);
const resolvedFileName = tryLoadModuleUsingOptionalResolutionSettings(moduleName, containingDirectory, loadModuleFromFile, failedLookupLocations, supportedExtensions, state);
if (resolvedFileName) {
return createResolvedModule(resolvedFileName, /*isExternalLibraryImport*/false, failedLookupLocations);
}
let referencedSourceFile: string;
if (moduleHasNonRelativeName(moduleName)) {
while (true) {
const searchName = normalizePath(combinePaths(containingDirectory, moduleName));
referencedSourceFile = loadModuleFromFile(searchName, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
if (referencedSourceFile) {
break;
}
const parentPath = getDirectoryPath(containingDirectory);
if (parentPath === containingDirectory) {
break;
}
containingDirectory = parentPath;
}
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
referencedSourceFile = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
}
return referencedSourceFile
? { resolvedModule: { resolvedFileName: referencedSourceFile }, failedLookupLocations }
: { resolvedModule: undefined, failedLookupLocations };
}
}

View File

@ -4,6 +4,7 @@
namespace ts {
/** The version of the TypeScript compiler release */
export const version = "2.1.0";
const emptyArray: any[] = [];
@ -74,788 +75,6 @@ namespace ts {
return getNormalizedPathFromPathComponents(commonPathComponents);
}
function trace(host: ModuleResolutionHost, message: DiagnosticMessage, ...args: any[]): void;
function trace(host: ModuleResolutionHost, message: DiagnosticMessage): void {
host.trace(formatMessage.apply(undefined, arguments));
}
function isTraceEnabled(compilerOptions: CompilerOptions, host: ModuleResolutionHost): boolean {
return compilerOptions.traceResolution && host.trace !== undefined;
}
/* @internal */
export function hasZeroOrOneAsteriskCharacter(str: string): boolean {
let seenAsterisk = false;
for (let i = 0; i < str.length; i++) {
if (str.charCodeAt(i) === CharacterCodes.asterisk) {
if (!seenAsterisk) {
seenAsterisk = true;
}
else {
// have already seen asterisk
return false;
}
}
}
return true;
}
function createResolvedModule(resolvedFileName: string, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
return { resolvedModule: resolvedFileName ? { resolvedFileName, isExternalLibraryImport } : undefined, failedLookupLocations };
}
function moduleHasNonRelativeName(moduleName: string): boolean {
return !(isRootedDiskPath(moduleName) || isExternalModuleNameRelative(moduleName));
}
interface ModuleResolutionState {
host: ModuleResolutionHost;
compilerOptions: CompilerOptions;
traceEnabled: boolean;
// skip .tsx files if jsx is not enabled
skipTsx: boolean;
}
function tryReadTypesSection(packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string {
const jsonContent = readJson(packageJsonPath, state.host);
function tryReadFromField(fieldName: string) {
if (hasProperty(jsonContent, fieldName)) {
const typesFile = (<any>jsonContent)[fieldName];
if (typeof typesFile === "string") {
const typesFilePath = normalizePath(combinePaths(baseDirectory, typesFile));
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, typesFile, typesFilePath);
}
return typesFilePath;
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, fieldName, typeof typesFile);
}
}
}
}
const typesFilePath = tryReadFromField("typings") || tryReadFromField("types");
if (typesFilePath) {
return typesFilePath;
}
// Use the main module for inferring types if no types package specified and the allowJs is set
if (state.compilerOptions.allowJs && jsonContent.main && typeof jsonContent.main === "string") {
if (state.traceEnabled) {
trace(state.host, Diagnostics.No_types_specified_in_package_json_but_allowJs_is_set_so_returning_main_value_of_0, jsonContent.main);
}
const mainFilePath = normalizePath(combinePaths(baseDirectory, jsonContent.main));
return mainFilePath;
}
return undefined;
}
function readJson(path: string, host: ModuleResolutionHost): { typings?: string, types?: string, main?: string } {
try {
const jsonText = host.readFile(path);
return jsonText ? JSON.parse(jsonText) : {};
}
catch (e) {
// gracefully handle if readFile fails or returns not JSON
return {};
}
}
const typeReferenceExtensions = [".d.ts"];
export function getEffectiveTypeRoots(options: CompilerOptions, host: { directoryExists?: (directoryName: string) => boolean, getCurrentDirectory?: () => string }): string[] | undefined {
if (options.typeRoots) {
return options.typeRoots;
}
let currentDirectory: string;
if (options.configFilePath) {
currentDirectory = getDirectoryPath(options.configFilePath);
}
else if (host.getCurrentDirectory) {
currentDirectory = host.getCurrentDirectory();
}
return currentDirectory && getDefaultTypeRoots(currentDirectory, host);
}
/**
* Returns the path to every node_modules/@types directory from some ancestor directory.
* Returns undefined if there are none.
*/
function getDefaultTypeRoots(currentDirectory: string, host: { directoryExists?: (directoryName: string) => boolean }): string[] | undefined {
if (!host.directoryExists) {
return [combinePaths(currentDirectory, nodeModulesAtTypes)];
// And if it doesn't exist, tough.
}
let typeRoots: string[];
while (true) {
const atTypes = combinePaths(currentDirectory, nodeModulesAtTypes);
if (host.directoryExists(atTypes)) {
(typeRoots || (typeRoots = [])).push(atTypes);
}
const parent = getDirectoryPath(currentDirectory);
if (parent === currentDirectory) {
break;
}
currentDirectory = parent;
}
return typeRoots;
}
const nodeModulesAtTypes = combinePaths("node_modules", "@types");
/**
* @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown.
* This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups
* is assumed to be the same as root directory of the project.
*/
export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost): ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(options, host);
const moduleResolutionState: ModuleResolutionState = {
compilerOptions: options,
host: host,
skipTsx: true,
traceEnabled
};
const typeRoots = getEffectiveTypeRoots(options, host);
if (traceEnabled) {
if (containingFile === undefined) {
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_not_set, typeReferenceDirectiveName);
}
else {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_1, typeReferenceDirectiveName, typeRoots);
}
}
else {
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_not_set, typeReferenceDirectiveName, containingFile);
}
else {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2, typeReferenceDirectiveName, containingFile, typeRoots);
}
}
}
const failedLookupLocations: string[] = [];
// Check primary library paths
if (typeRoots && typeRoots.length) {
if (traceEnabled) {
trace(host, Diagnostics.Resolving_with_primary_search_path_0, typeRoots.join(", "));
}
const primarySearchPaths = typeRoots;
for (const typeRoot of primarySearchPaths) {
const candidate = combinePaths(typeRoot, typeReferenceDirectiveName);
const candidateDirectory = getDirectoryPath(candidate);
const resolvedFile = loadNodeModuleFromDirectory(typeReferenceExtensions, candidate, failedLookupLocations,
!directoryProbablyExists(candidateDirectory, host), moduleResolutionState);
if (resolvedFile) {
if (traceEnabled) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, true);
}
return {
resolvedTypeReferenceDirective: { primary: true, resolvedFileName: resolvedFile },
failedLookupLocations
};
}
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Root_directory_cannot_be_determined_skipping_primary_search_paths);
}
}
let resolvedFile: string;
let initialLocationForSecondaryLookup: string;
if (containingFile) {
initialLocationForSecondaryLookup = getDirectoryPath(containingFile);
}
if (initialLocationForSecondaryLookup !== undefined) {
// check secondary locations
if (traceEnabled) {
trace(host, Diagnostics.Looking_up_in_node_modules_folder_initial_location_0, initialLocationForSecondaryLookup);
}
resolvedFile = loadModuleFromNodeModules(typeReferenceDirectiveName, initialLocationForSecondaryLookup, failedLookupLocations, moduleResolutionState);
if (traceEnabled) {
if (resolvedFile) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, false);
}
else {
trace(host, Diagnostics.Type_reference_directive_0_was_not_resolved, typeReferenceDirectiveName);
}
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_modules_folder);
}
}
return {
resolvedTypeReferenceDirective: resolvedFile
? { primary: false, resolvedFileName: resolvedFile }
: undefined,
failedLookupLocations
};
}
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
if (traceEnabled) {
trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile);
}
let moduleResolution = compilerOptions.moduleResolution;
if (moduleResolution === undefined) {
moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
if (traceEnabled) {
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]);
}
}
let result: ResolvedModuleWithFailedLookupLocations;
switch (moduleResolution) {
case ModuleResolutionKind.NodeJs:
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host);
break;
case ModuleResolutionKind.Classic:
result = classicNameResolver(moduleName, containingFile, compilerOptions, host);
break;
}
if (traceEnabled) {
if (result.resolvedModule) {
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName);
}
else {
trace(host, Diagnostics.Module_name_0_was_not_resolved, moduleName);
}
}
return result;
}
/*
* Every module resolution kind can has its specific understanding how to load module from a specific path on disk
* I.e. for path '/a/b/c':
* - Node loader will first to try to check if '/a/b/c' points to a file with some supported extension and if this fails
* it will try to load module from directory: directory '/a/b/c' should exist and it should have either 'package.json' with
* 'typings' entry or file 'index' with some supported extension
* - Classic loader will only try to interpret '/a/b/c' as file.
*/
type ResolutionKindSpecificLoader = (candidate: string, extensions: string[], failedLookupLocations: string[], onlyRecordFailures: boolean, state: ModuleResolutionState) => string;
/**
* Any module resolution kind can be augmented with optional settings: 'baseUrl', 'paths' and 'rootDirs' - they are used to
* mitigate differences between design time structure of the project and its runtime counterpart so the same import name
* can be resolved successfully by TypeScript compiler and runtime module loader.
* If these settings are set then loading procedure will try to use them to resolve module name and it can of failure it will
* fallback to standard resolution routine.
*
* - baseUrl - this setting controls how non-relative module names are resolved. If this setting is specified then non-relative
* names will be resolved relative to baseUrl: i.e. if baseUrl is '/a/b' then candidate location to resolve module name 'c/d' will
* be '/a/b/c/d'
* - paths - this setting can only be used when baseUrl is specified. allows to tune how non-relative module names
* will be resolved based on the content of the module name.
* Structure of 'paths' compiler options
* 'paths': {
* pattern-1: [...substitutions],
* pattern-2: [...substitutions],
* ...
* pattern-n: [...substitutions]
* }
* Pattern here is a string that can contain zero or one '*' character. During module resolution module name will be matched against
* all patterns in the list. Matching for patterns that don't contain '*' means that module name must be equal to pattern respecting the case.
* If pattern contains '*' then to match pattern "<prefix>*<suffix>" module name must start with the <prefix> and end with <suffix>.
* <MatchedStar> denotes part of the module name between <prefix> and <suffix>.
* If module name can be matches with multiple patterns then pattern with the longest prefix will be picked.
* After selecting pattern we'll use list of substitutions to get candidate locations of the module and the try to load module
* from the candidate location.
* Substitution is a string that can contain zero or one '*'. To get candidate location from substitution we'll pick every
* substitution in the list and replace '*' with <MatchedStar> string. If candidate location is not rooted it
* will be converted to absolute using baseUrl.
* For example:
* baseUrl: /a/b/c
* "paths": {
* // match all module names
* "*": [
* "*", // use matched name as is,
* // <matched name> will be looked as /a/b/c/<matched name>
*
* "folder1/*" // substitution will convert matched name to 'folder1/<matched name>',
* // since it is not rooted then final candidate location will be /a/b/c/folder1/<matched name>
* ],
* // match module names that start with 'components/'
* "components/*": [ "/root/components/*" ] // substitution will convert /components/folder1/<matched name> to '/root/components/folder1/<matched name>',
* // it is rooted so it will be final candidate location
* }
*
* 'rootDirs' allows the project to be spreaded across multiple locations and resolve modules with relative names as if
* they were in the same location. For example lets say there are two files
* '/local/src/content/file1.ts'
* '/shared/components/contracts/src/content/protocols/file2.ts'
* After bundling content of '/shared/components/contracts/src' will be merged with '/local/src' so
* if file1 has the following import 'import {x} from "./protocols/file2"' it will be resolved successfully in runtime.
* 'rootDirs' provides the way to tell compiler that in order to get the whole project it should behave as if content of all
* root dirs were merged together.
* I.e. for the example above 'rootDirs' will have two entries: [ '/local/src', '/shared/components/contracts/src' ].
* Compiler will first convert './protocols/file2' into absolute path relative to the location of containing file:
* '/local/src/content/protocols/file2' and try to load it - failure.
* Then it will search 'rootDirs' looking for a longest matching prefix of this absolute path and if such prefix is found - absolute path will
* be converted to a path relative to found rootDir entry './content/protocols/file2' (*). As a last step compiler will check all remaining
* entries in 'rootDirs', use them to build absolute path out of (*) and try to resolve module from this location.
*/
function tryLoadModuleUsingOptionalResolutionSettings(moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
failedLookupLocations: string[], supportedExtensions: string[], state: ModuleResolutionState): string {
if (moduleHasNonRelativeName(moduleName)) {
return tryLoadModuleUsingBaseUrl(moduleName, loader, failedLookupLocations, supportedExtensions, state);
}
else {
return tryLoadModuleUsingRootDirs(moduleName, containingDirectory, loader, failedLookupLocations, supportedExtensions, state);
}
}
function tryLoadModuleUsingRootDirs(moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
failedLookupLocations: string[], supportedExtensions: string[], state: ModuleResolutionState): string {
if (!state.compilerOptions.rootDirs) {
return undefined;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.rootDirs_option_is_set_using_it_to_resolve_relative_module_name_0, moduleName);
}
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
let matchedRootDir: string;
let matchedNormalizedPrefix: string;
for (const rootDir of state.compilerOptions.rootDirs) {
// rootDirs are expected to be absolute
// in case of tsconfig.json this will happen automatically - compiler will expand relative names
// using location of tsconfig.json as base location
let normalizedRoot = normalizePath(rootDir);
if (!endsWith(normalizedRoot, directorySeparator)) {
normalizedRoot += directorySeparator;
}
const isLongestMatchingPrefix =
startsWith(candidate, normalizedRoot) &&
(matchedNormalizedPrefix === undefined || matchedNormalizedPrefix.length < normalizedRoot.length);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Checking_if_0_is_the_longest_matching_prefix_for_1_2, normalizedRoot, candidate, isLongestMatchingPrefix);
}
if (isLongestMatchingPrefix) {
matchedNormalizedPrefix = normalizedRoot;
matchedRootDir = rootDir;
}
}
if (matchedNormalizedPrefix) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Longest_matching_prefix_for_0_is_1, candidate, matchedNormalizedPrefix);
}
const suffix = candidate.substr(matchedNormalizedPrefix.length);
// first - try to load from a initial location
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, matchedNormalizedPrefix, candidate);
}
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(containingDirectory, state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.Trying_other_entries_in_rootDirs);
}
// then try to resolve using remaining entries in rootDirs
for (const rootDir of state.compilerOptions.rootDirs) {
if (rootDir === matchedRootDir) {
// skip the initially matched entry
continue;
}
const candidate = combinePaths(normalizePath(rootDir), suffix);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, rootDir, candidate);
}
const baseDirectory = getDirectoryPath(candidate);
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(baseDirectory, state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.Module_resolution_using_rootDirs_has_failed);
}
}
return undefined;
}
function tryLoadModuleUsingBaseUrl(moduleName: string, loader: ResolutionKindSpecificLoader, failedLookupLocations: string[],
supportedExtensions: string[], state: ModuleResolutionState): string {
if (!state.compilerOptions.baseUrl) {
return undefined;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, state.compilerOptions.baseUrl, moduleName);
}
// string is for exact match
let matchedPattern: Pattern | string | undefined = undefined;
if (state.compilerOptions.paths) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName);
}
matchedPattern = matchPatternOrExact(getOwnKeys(state.compilerOptions.paths), moduleName);
}
if (matchedPattern) {
const matchedStar = typeof matchedPattern === "string" ? undefined : matchedText(matchedPattern, moduleName);
const matchedPatternText = typeof matchedPattern === "string" ? matchedPattern : patternText(matchedPattern);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText);
}
for (const subst of state.compilerOptions.paths[matchedPatternText]) {
const path = matchedStar ? subst.replace("*", matchedStar) : subst;
const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, path));
if (state.traceEnabled) {
trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path);
}
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
}
return undefined;
}
else {
const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, moduleName));
if (state.traceEnabled) {
trace(state.host, Diagnostics.Resolving_module_name_0_relative_to_base_url_1_2, moduleName, state.compilerOptions.baseUrl, candidate);
}
return loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
}
}
/**
* patternStrings contains both pattern strings (containing "*") and regular strings.
* Return an exact match if possible, or a pattern match, or undefined.
* (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.)
*/
function matchPatternOrExact(patternStrings: string[], candidate: string): string | Pattern | undefined {
const patterns: Pattern[] = [];
for (const patternString of patternStrings) {
const pattern = tryParsePattern(patternString);
if (pattern) {
patterns.push(pattern);
}
else if (patternString === candidate) {
// pattern was matched as is - no need to search further
return patternString;
}
}
return findBestPatternMatch(patterns, _ => _, candidate);
}
function patternText({prefix, suffix}: Pattern): string {
return `${prefix}*${suffix}`;
}
/**
* Given that candidate matches pattern, returns the text matching the '*'.
* E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar"
*/
function matchedText(pattern: Pattern, candidate: string): string {
Debug.assert(isPatternMatch(pattern, candidate));
return candidate.substr(pattern.prefix.length, candidate.length - pattern.suffix.length);
}
/** Return the object corresponding to the best pattern to match `candidate`. */
/* @internal */
export function findBestPatternMatch<T>(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined {
let matchedValue: T | undefined = undefined;
// use length of prefix as betterness criteria
let longestMatchPrefixLength = -1;
for (const v of values) {
const pattern = getPattern(v);
if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) {
longestMatchPrefixLength = pattern.prefix.length;
matchedValue = v;
}
}
return matchedValue;
}
function isPatternMatch({prefix, suffix}: Pattern, candidate: string) {
return candidate.length >= prefix.length + suffix.length &&
startsWith(candidate, prefix) &&
endsWith(candidate, suffix);
}
/* @internal */
export function tryParsePattern(pattern: string): Pattern | undefined {
// This should be verified outside of here and a proper error thrown.
Debug.assert(hasZeroOrOneAsteriskCharacter(pattern));
const indexOfStar = pattern.indexOf("*");
return indexOfStar === -1 ? undefined : {
prefix: pattern.substr(0, indexOfStar),
suffix: pattern.substr(indexOfStar + 1)
};
}
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const containingDirectory = getDirectoryPath(containingFile);
const supportedExtensions = getSupportedExtensions(compilerOptions);
const traceEnabled = isTraceEnabled(compilerOptions, host);
const failedLookupLocations: string[] = [];
const state = { compilerOptions, host, traceEnabled, skipTsx: false };
let resolvedFileName = tryLoadModuleUsingOptionalResolutionSettings(moduleName, containingDirectory, nodeLoadModuleByRelativeName,
failedLookupLocations, supportedExtensions, state);
let isExternalLibraryImport = false;
if (!resolvedFileName) {
if (moduleHasNonRelativeName(moduleName)) {
if (traceEnabled) {
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder, moduleName);
}
resolvedFileName = loadModuleFromNodeModules(moduleName, containingDirectory, failedLookupLocations, state);
isExternalLibraryImport = resolvedFileName !== undefined;
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
resolvedFileName = nodeLoadModuleByRelativeName(candidate, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
}
}
if (resolvedFileName && host.realpath) {
const originalFileName = resolvedFileName;
resolvedFileName = normalizePath(host.realpath(resolvedFileName));
if (traceEnabled) {
trace(host, Diagnostics.Resolving_real_path_for_0_result_1, originalFileName, resolvedFileName);
}
}
return createResolvedModule(resolvedFileName, isExternalLibraryImport, failedLookupLocations);
}
function nodeLoadModuleByRelativeName(candidate: string, supportedExtensions: string[], failedLookupLocations: string[],
onlyRecordFailures: boolean, state: ModuleResolutionState): string {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0, candidate);
}
const resolvedFileName = !pathEndsWithDirectorySeparator(candidate) && loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, onlyRecordFailures, state);
return resolvedFileName || loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, onlyRecordFailures, state);
}
/* @internal */
export function directoryProbablyExists(directoryName: string, host: { directoryExists?: (directoryName: string) => boolean }): boolean {
// if host does not support 'directoryExists' assume that directory will exist
return !host.directoryExists || host.directoryExists(directoryName);
}
/**
* @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary
* in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations.
*/
function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
// First, try adding an extension. An import of "foo" could be matched by a file "foo.ts", or "foo.js" by "foo.js.ts"
const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, failedLookupLocation, onlyRecordFailures, state);
if (resolvedByAddingExtension) {
return resolvedByAddingExtension;
}
// If that didn't work, try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one;
// e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts"
if (hasJavaScriptFileExtension(candidate)) {
const extensionless = removeFileExtension(candidate);
if (state.traceEnabled) {
const extension = candidate.substring(extensionless.length);
trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension);
}
return tryAddingExtensions(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state);
}
}
/** Try to return an existing file that adds one of the `extensions` to `candidate`. */
function tryAddingExtensions(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
if (!onlyRecordFailures) {
// check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
const directory = getDirectoryPath(candidate);
if (directory) {
onlyRecordFailures = !directoryProbablyExists(directory, state.host);
}
}
return forEach(extensions, ext =>
!(state.skipTsx && isJsxOrTsxExtension(ext)) && tryFile(candidate + ext, failedLookupLocation, onlyRecordFailures, state));
}
/** Return the file if it exists. */
function tryFile(fileName: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
if (!onlyRecordFailures && state.host.fileExists(fileName)) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName);
}
return fileName;
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_does_not_exist, fileName);
}
failedLookupLocation.push(fileName);
return undefined;
}
}
function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
const packageJsonPath = pathToPackageJson(candidate);
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
if (directoryExists && state.host.fileExists(packageJsonPath)) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
const typesFile = tryReadTypesSection(packageJsonPath, candidate, state);
if (typesFile) {
const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(typesFile), state.host);
// A package.json "typings" may specify an exact filename, or may choose to omit an extension.
const result = tryFile(typesFile, failedLookupLocation, onlyRecordFailures, state) ||
tryAddingExtensions(typesFile, extensions, failedLookupLocation, onlyRecordFailures, state);
if (result) {
return result;
}
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_does_not_have_types_field);
}
}
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_does_not_exist, packageJsonPath);
}
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
failedLookupLocation.push(packageJsonPath);
}
return loadModuleFromFile(combinePaths(candidate, "index"), extensions, failedLookupLocation, !directoryExists, state);
}
function pathToPackageJson(directory: string): string {
return combinePaths(directory, "package.json");
}
function loadModuleFromNodeModulesFolder(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string {
const nodeModulesFolder = combinePaths(directory, "node_modules");
const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host);
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
const supportedExtensions = getSupportedExtensions(state.compilerOptions);
let result = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, !nodeModulesFolderExists, state);
if (result) {
return result;
}
result = loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
if (result) {
return result;
}
}
function loadModuleFromNodeModules(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string {
directory = normalizeSlashes(directory);
while (true) {
const baseName = getBaseFileName(directory);
if (baseName !== "node_modules") {
// Try to load source from the package
const packageResult = loadModuleFromNodeModulesFolder(moduleName, directory, failedLookupLocations, state);
if (packageResult && hasTypeScriptFileExtension(packageResult)) {
// Always prefer a TypeScript (.ts, .tsx, .d.ts) file shipped with the package
return packageResult;
}
else {
// Else prefer a types package over non-TypeScript results (e.g. JavaScript files)
const typesResult = loadModuleFromNodeModulesFolder(combinePaths("@types", moduleName), directory, failedLookupLocations, state);
if (typesResult || packageResult) {
return typesResult || packageResult;
}
}
}
const parentPath = getDirectoryPath(directory);
if (parentPath === directory) {
break;
}
directory = parentPath;
}
return undefined;
}
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
const state = { compilerOptions, host, traceEnabled, skipTsx: !compilerOptions.jsx };
const failedLookupLocations: string[] = [];
const supportedExtensions = getSupportedExtensions(compilerOptions);
let containingDirectory = getDirectoryPath(containingFile);
const resolvedFileName = tryLoadModuleUsingOptionalResolutionSettings(moduleName, containingDirectory, loadModuleFromFile, failedLookupLocations, supportedExtensions, state);
if (resolvedFileName) {
return createResolvedModule(resolvedFileName, /*isExternalLibraryImport*/false, failedLookupLocations);
}
let referencedSourceFile: string;
if (moduleHasNonRelativeName(moduleName)) {
while (true) {
const searchName = normalizePath(combinePaths(containingDirectory, moduleName));
referencedSourceFile = loadModuleFromFile(searchName, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
if (referencedSourceFile) {
break;
}
const parentPath = getDirectoryPath(containingDirectory);
if (parentPath === containingDirectory) {
break;
}
containingDirectory = parentPath;
}
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
referencedSourceFile = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
}
return referencedSourceFile
? { resolvedModule: { resolvedFileName: referencedSourceFile }, failedLookupLocations }
: { resolvedModule: undefined, failedLookupLocations };
}
interface OutputFingerprint {
hash: string;
byteOrderMark: boolean;
@ -1070,44 +289,6 @@ namespace ts {
return resolutions;
}
/**
* Given a set of options, returns the set of type directive names
* that should be included for this program automatically.
* This list could either come from the config file,
* or from enumerating the types root + initial secondary types lookup location.
* More type directives might appear in the program later as a result of loading actual source files;
* this list is only the set of defaults that are implicitly included.
*/
export function getAutomaticTypeDirectiveNames(options: CompilerOptions, host: ModuleResolutionHost): string[] {
// Use explicit type list from tsconfig.json
if (options.types) {
return options.types;
}
// Walk the primary type lookup locations
const result: string[] = [];
if (host.directoryExists && host.getDirectories) {
const typeRoots = getEffectiveTypeRoots(options, host);
if (typeRoots) {
for (const root of typeRoots) {
if (host.directoryExists(root)) {
for (const typeDirectivePath of host.getDirectories(root)) {
const normalized = normalizePath(typeDirectivePath);
const packageJsonPath = pathToPackageJson(combinePaths(root, normalized));
// tslint:disable-next-line:no-null-keyword
const isNotNeededPackage = host.fileExists(packageJsonPath) && readJson(packageJsonPath, host).typings === null;
if (!isNotNeededPackage) {
// Return just the type directive names
result.push(getBaseFileName(normalized));
}
}
}
}
}
}
return result;
}
export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program {
let program: Program;
let files: SourceFile[] = [];
@ -1230,7 +411,8 @@ namespace ts {
getSymbolCount: () => getDiagnosticsProducingTypeChecker().getSymbolCount(),
getTypeCount: () => getDiagnosticsProducingTypeChecker().getTypeCount(),
getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives
getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives,
dropDiagnosticsProducingTypeChecker
};
verifyCompilerOptions();
@ -1426,19 +608,23 @@ namespace ts {
return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true));
}
function dropDiagnosticsProducingTypeChecker() {
diagnosticsProducingTypeChecker = undefined;
}
function getTypeChecker() {
return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false));
}
function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult {
return runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken));
function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean): EmitResult {
return runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles));
}
function isEmitBlocked(emitFileName: string): boolean {
return hasEmitBlockingDiagnostics.contains(toPath(emitFileName, currentDirectory, getCanonicalFileName));
}
function emitWorker(program: Program, sourceFile: SourceFile, writeFileCallback: WriteFileCallback, cancellationToken: CancellationToken): EmitResult {
function emitWorker(program: Program, sourceFile: SourceFile, writeFileCallback: WriteFileCallback, cancellationToken: CancellationToken, emitOnlyDtsFiles?: boolean): EmitResult {
let declarationDiagnostics: Diagnostic[] = [];
if (options.noEmit) {
@ -1483,7 +669,8 @@ namespace ts {
const emitResult = emitFiles(
emitResolver,
getEmitHost(writeFileCallback),
sourceFile);
sourceFile,
emitOnlyDtsFiles);
performance.mark("afterEmit");
performance.measure("Emit", "beforeEmit", "afterEmit");

View File

@ -18,11 +18,6 @@ namespace ts {
*/
reset(): void;
/**
* Gets test data for source maps.
*/
getSourceMapData(): SourceMapData;
/**
* Set the current source file.
*
@ -41,123 +36,23 @@ namespace ts {
emitPos(pos: number): void;
/**
* Emits a mapping for the start of a range.
* Emits a node with possible leading and trailing source maps.
*
* If the range's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param range The range to emit.
* @param emitContext The current emit context
* @param node The node to emit.
* @param emitCallback The callback used to emit the node.
*/
emitStart(range: TextRange): void;
emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
/**
* Emits a mapping for the start of a range.
*
* If the node's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param range The range to emit.
* @param contextNode The node for the current range.
* @param ignoreNodeCallback A callback used to determine whether to skip source map
* emit for the start position of this node.
* @param ignoreChildrenCallback A callback used to determine whether to skip source
* map emit for all children of this node.
* @param getTextRangeCallbackCallback A callback used to get a custom source map
* range for this node.
*/
emitStart(range: TextRange, contextNode: Node, ignoreNodeCallback: (node: Node) => boolean, ignoreChildrenCallback: (node: Node) => boolean, getTextRangeCallbackCallback: (node: Node) => TextRange): void;
/**
* Emits a mapping for the end of a range.
*
* If the range's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param range The range to emit.
*/
emitEnd(range: TextRange): void;
/**
* Emits a mapping for the end of a range.
*
* If the node's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param range The range to emit.
* @param contextNode The node for the current range.
* @param ignoreNodeCallback A callback used to determine whether to skip source map
* emit for the end position of this node.
* @param ignoreChildrenCallback A callback used to determine whether to skip source
* map emit for all children of this node.
* @param getTextRangeCallbackCallback A callback used to get a custom source map
* range for this node.
*/
emitEnd(range: TextRange, contextNode: Node, ignoreNodeCallback: (node: Node) => boolean, ignoreChildrenCallback: (node: Node) => boolean, getTextRangeCallbackCallback: (node: Node) => TextRange): void;
/**
* Emits a mapping for the start position of a token.
*
* If the token's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
* Emits a token of a node node with possible leading and trailing source maps.
*
* @param node The node containing the token.
* @param token The token to emit.
* @param tokenStartPos The start position of the token.
* @returns The start position of the token, following any trivia.
* @param tokenStartPos The start pos of the token.
* @param emitCallback The callback used to emit the token.
*/
emitTokenStart(token: SyntaxKind, tokenStartPos: number): number;
/**
* Emits a mapping for the start position of a token.
*
* If the token's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param token The token to emit.
* @param tokenStartPos The start position of the token.
* @param contextNode The node containing this token.
* @param ignoreTokenCallback A callback used to determine whether to skip source map
* emit for the start position of this token.
* @param getTokenTextRangeCallback A callback used to get a custom source
* map range for this node.
* @returns The start position of the token, following any trivia.
*/
emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode: Node, ignoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback: (node: Node, token: SyntaxKind) => TextRange): number;
/**
* Emits a mapping for the end position of a token.
*
* If the token's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param token The token to emit.
* @param tokenEndPos The end position of the token.
* @returns The end position of the token.
*/
emitTokenEnd(token: SyntaxKind, tokenEndPos: number): number;
/**
* Emits a mapping for the end position of a token.
*
* If the token's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param token The token to emit.
* @param tokenEndPos The end position of the token.
* @param contextNode The node containing this token.
* @param ignoreTokenCallback A callback used to determine whether to skip source map
* emit for the end position of this token.
* @param getTokenTextRangeCallback A callback used to get a custom source
* map range for this node.
* @returns The end position of the token.
*/
emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode: Node, ignoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback: (node: Node, token: SyntaxKind) => TextRange): number;
/*@deprecated*/ changeEmitSourcePos(): void;
/*@deprecated*/ stopOverridingSpan(): void;
emitTokenWithSourceMap(node: Node, token: SyntaxKind, tokenStartPos: number, emitCallback: (token: SyntaxKind, tokenStartPos: number) => number): number;
/**
* Gets the text for the source map.
@ -168,44 +63,11 @@ namespace ts {
* Gets the SourceMappingURL for the source map.
*/
getSourceMappingURL(): string;
}
export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
const compilerOptions = host.getCompilerOptions();
if (compilerOptions.sourceMap || compilerOptions.inlineSourceMap) {
if (compilerOptions.extendedDiagnostics) {
return createSourceMapWriterWithExtendedDiagnostics(host, writer);
}
return createSourceMapWriterWorker(host, writer);
}
else {
return getNullSourceMapWriter();
}
}
let nullSourceMapWriter: SourceMapWriter;
export function getNullSourceMapWriter(): SourceMapWriter {
if (nullSourceMapWriter === undefined) {
nullSourceMapWriter = {
initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void { },
reset(): void { },
getSourceMapData(): SourceMapData { return undefined; },
setSourceFile(sourceFile: SourceFile): void { },
emitPos(pos: number): void { },
emitStart(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { },
emitEnd(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { },
emitTokenStart(token: SyntaxKind, pos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { return -1; },
emitTokenEnd(token: SyntaxKind, end: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { return -1; },
changeEmitSourcePos(): void { },
stopOverridingSpan(): void { },
getText(): string { return undefined; },
getSourceMappingURL(): string { return undefined; }
};
}
return nullSourceMapWriter;
/**
* Gets test data for source maps.
*/
getSourceMapData(): SourceMapData;
}
// Used for initialize lastEncodedSourceMapSpan and reset lastEncodedSourceMapSpan when updateLastEncodedAndRecordedSpans
@ -217,14 +79,12 @@ namespace ts {
sourceIndex: 0
};
function createSourceMapWriterWorker(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
const compilerOptions = host.getCompilerOptions();
const extendedDiagnostics = compilerOptions.extendedDiagnostics;
let currentSourceFile: SourceFile;
let currentSourceText: string;
let sourceMapDir: string; // The directory in which sourcemap will be
let stopOverridingSpan = false;
let modifyLastSourcePos = false;
// Current source map file and its index in the sources list
let sourceMapSourceIndex: number;
@ -236,13 +96,7 @@ namespace ts {
// Source map data
let sourceMapData: SourceMapData;
// This keeps track of the number of times `disable` has been called without a
// corresponding call to `enable`. As long as this value is non-zero, mappings will not
// be recorded.
// This is primarily used to provide a better experience when debugging binding
// patterns and destructuring assignments for simple expressions.
let disableDepth: number;
let disabled: boolean = !(compilerOptions.sourceMap || compilerOptions.inlineSourceMap);
return {
initialize,
@ -250,12 +104,8 @@ namespace ts {
getSourceMapData: () => sourceMapData,
setSourceFile,
emitPos,
emitStart,
emitEnd,
emitTokenStart,
emitTokenEnd,
changeEmitSourcePos,
stopOverridingSpan: () => stopOverridingSpan = true,
emitNodeWithSourceMap,
emitTokenWithSourceMap,
getText,
getSourceMappingURL,
};
@ -269,13 +119,16 @@ namespace ts {
* @param isBundledEmit A value indicating whether the generated output file is a bundle.
*/
function initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
if (disabled) {
return;
}
if (sourceMapData) {
reset();
}
currentSourceFile = undefined;
currentSourceText = undefined;
disableDepth = 0;
// Current source map file and its index in the sources list
sourceMapSourceIndex = -1;
@ -338,6 +191,10 @@ namespace ts {
* Reset the SourceMapWriter to an empty state.
*/
function reset() {
if (disabled) {
return;
}
currentSourceFile = undefined;
sourceMapDir = undefined;
sourceMapSourceIndex = undefined;
@ -345,64 +202,6 @@ namespace ts {
lastEncodedSourceMapSpan = undefined;
lastEncodedNameIndex = undefined;
sourceMapData = undefined;
disableDepth = 0;
}
/**
* Re-enables the recording of mappings.
*/
function enable() {
if (disableDepth > 0) {
disableDepth--;
}
}
/**
* Disables the recording of mappings.
*/
function disable() {
disableDepth++;
}
function updateLastEncodedAndRecordedSpans() {
if (modifyLastSourcePos) {
// Reset the source pos
modifyLastSourcePos = false;
// Change Last recorded Map with last encoded emit line and character
lastRecordedSourceMapSpan.emittedLine = lastEncodedSourceMapSpan.emittedLine;
lastRecordedSourceMapSpan.emittedColumn = lastEncodedSourceMapSpan.emittedColumn;
// Pop sourceMapDecodedMappings to remove last entry
sourceMapData.sourceMapDecodedMappings.pop();
// Point the lastEncodedSourceMapSpace to the previous encoded sourceMapSpan
// If the list is empty which indicates that we are at the beginning of the file,
// we have to reset it to default value (same value when we first initialize sourceMapWriter)
lastEncodedSourceMapSpan = sourceMapData.sourceMapDecodedMappings.length ?
sourceMapData.sourceMapDecodedMappings[sourceMapData.sourceMapDecodedMappings.length - 1] :
defaultLastEncodedSourceMapSpan;
// TODO: Update lastEncodedNameIndex
// Since we dont support this any more, lets not worry about it right now.
// When we start supporting nameIndex, we will get back to this
// Change the encoded source map
const sourceMapMappings = sourceMapData.sourceMapMappings;
let lenthToSet = sourceMapMappings.length - 1;
for (; lenthToSet >= 0; lenthToSet--) {
const currentChar = sourceMapMappings.charAt(lenthToSet);
if (currentChar === ",") {
// Separator for the entry found
break;
}
if (currentChar === ";" && lenthToSet !== 0 && sourceMapMappings.charAt(lenthToSet - 1) !== ";") {
// Last line separator found
break;
}
}
sourceMapData.sourceMapMappings = sourceMapMappings.substr(0, Math.max(0, lenthToSet));
}
}
// Encoding for sourcemap span
@ -459,7 +258,7 @@ namespace ts {
* @param pos The position.
*/
function emitPos(pos: number) {
if (positionIsSynthesized(pos) || disableDepth > 0) {
if (disabled || positionIsSynthesized(pos)) {
return;
}
@ -495,209 +294,89 @@ namespace ts {
sourceColumn: sourceLinePos.character,
sourceIndex: sourceMapSourceIndex
};
stopOverridingSpan = false;
}
else if (!stopOverridingSpan) {
else {
// Take the new pos instead since there is no change in emittedLine and column since last location
lastRecordedSourceMapSpan.sourceLine = sourceLinePos.line;
lastRecordedSourceMapSpan.sourceColumn = sourceLinePos.character;
lastRecordedSourceMapSpan.sourceIndex = sourceMapSourceIndex;
}
updateLastEncodedAndRecordedSpans();
if (extendedDiagnostics) {
performance.mark("afterSourcemap");
performance.measure("Source Map", "beforeSourcemap", "afterSourcemap");
}
}
function getStartPosPastDecorators(range: TextRange) {
const rangeHasDecorators = !!(range as Node).decorators;
return skipTrivia(currentSourceText, rangeHasDecorators ? (range as Node).decorators.end : range.pos);
}
/**
* Emits a mapping for the start of a range.
* Emits a node with possible leading and trailing source maps.
*
* If the range's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param range The range to emit.0
* @param node The node to emit.
* @param emitCallback The callback used to emit the node.
*/
function emitStart(range: TextRange): void;
/**
* Emits a mapping for the start of a range.
*
* If the node's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param range The range to emit.
* @param contextNode The node for the current range.
* @param ignoreNodeCallback A callback used to determine whether to skip source map
* emit for the start position of this node.
* @param ignoreChildrenCallback A callback used to determine whether to skip source
* map emit for all children of this node.
* @param getTextRangeCallbackCallback A callback used to get a custom source map
* range for this node.
*/
function emitStart(range: TextRange, contextNode: Node, ignoreNodeCallback: (node: Node) => boolean, ignoreChildrenCallback: (node: Node) => boolean, getTextRangeCallback: (node: Node) => TextRange): void;
function emitStart(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange) {
if (contextNode) {
if (!ignoreNodeCallback(contextNode)) {
range = getTextRangeCallback(contextNode) || range;
emitPos(getStartPosPastDecorators(range));
}
if (ignoreChildrenCallback(contextNode)) {
disable();
}
function emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
if (disabled) {
return emitCallback(emitContext, node);
}
else {
emitPos(getStartPosPastDecorators(range));
if (node) {
const emitNode = node.emitNode;
const emitFlags = emitNode && emitNode.flags;
const { pos, end } = emitNode && emitNode.sourceMapRange || node;
if (node.kind !== SyntaxKind.NotEmittedStatement
&& (emitFlags & EmitFlags.NoLeadingSourceMap) === 0
&& pos >= 0) {
emitPos(skipTrivia(currentSourceText, pos));
}
if (emitFlags & EmitFlags.NoNestedSourceMaps) {
disabled = true;
emitCallback(emitContext, node);
disabled = false;
}
else {
emitCallback(emitContext, node);
}
if (node.kind !== SyntaxKind.NotEmittedStatement
&& (emitFlags & EmitFlags.NoTrailingSourceMap) === 0
&& end >= 0) {
emitPos(end);
}
}
}
/**
* Emits a mapping for the end of a range.
*
* If the range's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param range The range to emit.
*/
function emitEnd(range: TextRange): void;
/**
* Emits a mapping for the end of a range.
*
* If the node's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param range The range to emit.
* @param contextNode The node for the current range.
* @param ignoreNodeCallback A callback used to determine whether to skip source map
* emit for the end position of this node.
* @param ignoreChildrenCallback A callback used to determine whether to skip source
* map emit for all children of this node.
* @param getTextRangeCallbackCallback A callback used to get a custom source map
* range for this node.
*/
function emitEnd(range: TextRange, contextNode: Node, ignoreNodeCallback: (node: Node) => boolean, ignoreChildrenCallback: (node: Node) => boolean, getTextRangeCallback: (node: Node) => TextRange): void;
function emitEnd(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange) {
if (contextNode) {
if (ignoreChildrenCallback(contextNode)) {
enable();
}
if (!ignoreNodeCallback(contextNode)) {
range = getTextRangeCallback(contextNode) || range;
emitPos(range.end);
}
}
else {
emitPos(range.end);
}
stopOverridingSpan = false;
}
/**
* Emits a mapping for the start position of a token.
*
* If the token's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
* Emits a token of a node with possible leading and trailing source maps.
*
* @param node The node containing the token.
* @param token The token to emit.
* @param tokenStartPos The start position of the token.
* @returns The start position of the token, following any trivia.
* @param tokenStartPos The start pos of the token.
* @param emitCallback The callback used to emit the token.
*/
function emitTokenStart(token: SyntaxKind, tokenStartPos: number): number;
/**
* Emits a mapping for the start position of a token.
*
* If the token's start position is synthetic (undefined or a negative value), no mapping
* will be created. Any trivia at the start position in the original source will be
* skipped.
*
* @param token The token to emit.
* @param tokenStartPos The start position of the token.
* @param contextNode The node containing this token.
* @param ignoreTokenCallback A callback used to determine whether to skip source map
* emit for the start position of this token.
* @param getTokenTextRangeCallback A callback used to get a custom source
* map range for this node.
* @returns The start position of the token, following any trivia.
*/
function emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode: Node, ignoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback: (node: Node, token: SyntaxKind) => TextRange): number;
function emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number {
if (contextNode) {
if (ignoreTokenCallback(contextNode, token)) {
return skipTrivia(currentSourceText, tokenStartPos);
}
const range = getTokenTextRangeCallback(contextNode, token);
if (range) {
tokenStartPos = range.pos;
}
function emitTokenWithSourceMap(node: Node, token: SyntaxKind, tokenPos: number, emitCallback: (token: SyntaxKind, tokenStartPos: number) => number) {
if (disabled) {
return emitCallback(token, tokenPos);
}
tokenStartPos = skipTrivia(currentSourceText, tokenStartPos);
emitPos(tokenStartPos);
return tokenStartPos;
}
const emitNode = node && node.emitNode;
const emitFlags = emitNode && emitNode.flags;
const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges[token];
/**
* Emits a mapping for the end position of a token.
*
* If the token's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param token The token to emit.
* @param tokenEndPos The end position of the token.
* @returns The end position of the token.
*/
function emitTokenEnd(token: SyntaxKind, tokenEndPos: number): number;
/**
* Emits a mapping for the end position of a token.
*
* If the token's end position is synthetic (undefined or a negative value), no mapping
* will be created.
*
* @param token The token to emit.
* @param tokenEndPos The end position of the token.
* @param contextNode The node containing this token.
* @param ignoreTokenCallback A callback used to determine whether to skip source map
* emit for the end position of this token.
* @param getTokenTextRangeCallback A callback used to get a custom source
* map range for this node.
* @returns The end position of the token.
*/
function emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode: Node, ignoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback: (node: Node, token: SyntaxKind) => TextRange): number;
function emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number {
if (contextNode) {
if (ignoreTokenCallback(contextNode, token)) {
return tokenEndPos;
}
const range = getTokenTextRangeCallback(contextNode, token);
if (range) {
tokenEndPos = range.end;
}
tokenPos = skipTrivia(currentSourceText, range ? range.pos : tokenPos);
if ((emitFlags & EmitFlags.NoTokenLeadingSourceMaps) === 0 && tokenPos >= 0) {
emitPos(tokenPos);
}
emitPos(tokenEndPos);
return tokenEndPos;
}
tokenPos = emitCallback(token, tokenPos);
if (range) tokenPos = range.end;
if ((emitFlags & EmitFlags.NoTokenTrailingSourceMaps) === 0 && tokenPos >= 0) {
emitPos(tokenPos);
}
// @deprecated
function changeEmitSourcePos() {
Debug.assert(!modifyLastSourcePos);
modifyLastSourcePos = true;
return tokenPos;
}
/**
@ -706,6 +385,10 @@ namespace ts {
* @param sourceFile The source file.
*/
function setSourceFile(sourceFile: SourceFile) {
if (disabled) {
return;
}
currentSourceFile = sourceFile;
currentSourceText = currentSourceFile.text;
@ -738,6 +421,10 @@ namespace ts {
* Gets the text for the source map.
*/
function getText() {
if (disabled) {
return;
}
encodeLastRecordedSourceMapSpan();
return stringify({
@ -755,6 +442,10 @@ namespace ts {
* Gets the SourceMappingURL for the source map.
*/
function getSourceMappingURL() {
if (disabled) {
return;
}
if (compilerOptions.inlineSourceMap) {
// Encode the sourceMap into the sourceMap url
const base64SourceMapText = convertToBase64(getText());
@ -766,61 +457,6 @@ namespace ts {
}
}
function createSourceMapWriterWithExtendedDiagnostics(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
const {
initialize,
reset,
getSourceMapData,
setSourceFile,
emitPos,
emitStart,
emitEnd,
emitTokenStart,
emitTokenEnd,
changeEmitSourcePos,
stopOverridingSpan,
getText,
getSourceMappingURL,
} = createSourceMapWriterWorker(host, writer);
return {
initialize,
reset,
getSourceMapData,
setSourceFile,
emitPos(pos: number): void {
performance.mark("sourcemapStart");
emitPos(pos);
performance.measure("sourceMapTime", "sourcemapStart");
},
emitStart(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void {
performance.mark("emitSourcemap:emitStart");
emitStart(range, contextNode, ignoreNodeCallback, ignoreChildrenCallback, getTextRangeCallback);
performance.measure("sourceMapTime", "emitSourcemap:emitStart");
},
emitEnd(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void {
performance.mark("emitSourcemap:emitEnd");
emitEnd(range, contextNode, ignoreNodeCallback, ignoreChildrenCallback, getTextRangeCallback);
performance.measure("sourceMapTime", "emitSourcemap:emitEnd");
},
emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number {
performance.mark("emitSourcemap:emitTokenStart");
tokenStartPos = emitTokenStart(token, tokenStartPos, contextNode, ignoreTokenCallback, getTokenTextRangeCallback);
performance.measure("sourceMapTime", "emitSourcemap:emitTokenStart");
return tokenStartPos;
},
emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number {
performance.mark("emitSourcemap:emitTokenEnd");
tokenEndPos = emitTokenEnd(token, tokenEndPos, contextNode, ignoreTokenCallback, getTokenTextRangeCallback);
performance.measure("sourceMapTime", "emitSourcemap:emitTokenEnd");
return tokenEndPos;
},
changeEmitSourcePos,
stopOverridingSpan,
getText,
getSourceMappingURL,
};
}
const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
function base64FormatEncode(inValue: number) {

View File

@ -592,19 +592,40 @@ namespace ts {
};
}
function recursiveCreateDirectory(directoryPath: string, sys: System) {
const basePath = getDirectoryPath(directoryPath);
const shouldCreateParent = directoryPath !== basePath && !sys.directoryExists(basePath);
if (shouldCreateParent) {
recursiveCreateDirectory(basePath, sys);
}
if (shouldCreateParent || !sys.directoryExists(directoryPath)) {
sys.createDirectory(directoryPath);
}
}
let sys: System;
if (typeof ChakraHost !== "undefined") {
return getChakraSystem();
sys = getChakraSystem();
}
else if (typeof WScript !== "undefined" && typeof ActiveXObject === "function") {
return getWScriptSystem();
sys = getWScriptSystem();
}
else if (typeof process !== "undefined" && process.nextTick && !process.browser && typeof require !== "undefined") {
// process and process.nextTick checks if current environment is node-like
// process.browser check excludes webpack and browserify
return getNodeSystem();
sys = getNodeSystem();
}
else {
return undefined; // Unsupported host
if (sys) {
// patch writefile to create folder before writing the file
const originalWriteFile = sys.writeFile;
sys.writeFile = function(path, data, writeBom) {
const directoryPath = getDirectoryPath(normalizeSlashes(path));
if (directoryPath && !sys.directoryExists(directoryPath)) {
recursiveCreateDirectory(directoryPath, sys);
}
originalWriteFile.call(sys, path, data, writeBom);
};
}
return sys;
})();
}

View File

@ -28,46 +28,25 @@ namespace ts {
/**
* Gets the transformed source files.
*/
getSourceFiles(): SourceFile[];
transformed: SourceFile[];
/**
* Gets the TextRange to use for source maps for a token of a node.
*/
getTokenSourceMapRange(node: Node, token: SyntaxKind): TextRange;
/**
* Determines whether expression substitutions are enabled for the provided node.
*/
isSubstitutionEnabled(node: Node): boolean;
/**
* Determines whether before/after emit notifications should be raised in the pretty
* printer when it emits a node.
*/
isEmitNotificationEnabled(node: Node): boolean;
/**
* Hook used by transformers to substitute expressions just before they
* are emitted by the pretty printer.
* Emits the substitute for a node, if one is available; otherwise, emits the node.
*
* @param emitContext The current emit context.
* @param node The node to substitute.
* @param isExpression A value indicating whether the node is in an expression context.
* @param emitCallback A callback used to emit the node or its substitute.
*/
onSubstituteNode(node: Node, isExpression: boolean): Node;
emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
/**
* Hook used to allow transformers to capture state before or after
* the printer emits a node.
* Emits a node with possible notification.
*
* @param emitContext The current emit context.
* @param node The node to emit.
* @param emitCallback A callback used to emit the node.
*/
onEmitNode(node: Node, emitCallback: (node: Node) => void): void;
/**
* Reset transient transformation properties on parse tree nodes.
*/
dispose(): void;
emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
}
export interface TransformationContext extends LexicalEnvironment {
@ -75,46 +54,6 @@ namespace ts {
getEmitResolver(): EmitResolver;
getEmitHost(): EmitHost;
/**
* Gets flags used to customize later transformations or emit.
*/
getNodeEmitFlags(node: Node): NodeEmitFlags;
/**
* Sets flags used to customize later transformations or emit.
*/
setNodeEmitFlags<T extends Node>(node: T, flags: NodeEmitFlags): T;
/**
* Gets the TextRange to use for source maps for the node.
*/
getSourceMapRange(node: Node): TextRange;
/**
* Sets the TextRange to use for source maps for the node.
*/
setSourceMapRange<T extends Node>(node: T, range: TextRange): T;
/**
* Gets the TextRange to use for source maps for a token of a node.
*/
getTokenSourceMapRange(node: Node, token: SyntaxKind): TextRange;
/**
* Sets the TextRange to use for source maps for a token of a node.
*/
setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: TextRange): T;
/**
* Gets the TextRange to use for comments for the node.
*/
getCommentRange(node: Node): TextRange;
/**
* Sets the TextRange to use for comments for the node.
*/
setCommentRange<T extends Node>(node: T, range: TextRange): T;
/**
* Hoists a function declaration to the containing scope.
*/
@ -139,7 +78,7 @@ namespace ts {
* Hook used by transformers to substitute expressions just before they
* are emitted by the pretty printer.
*/
onSubstituteNode?: (node: Node, isExpression: boolean) => Node;
onSubstituteNode?: (emitContext: EmitContext, node: Node) => Node;
/**
* Enables before/after emit notifications in the pretty printer for the provided
@ -157,7 +96,7 @@ namespace ts {
* Hook used to allow transformers to capture state before or after
* the printer emits a node.
*/
onEmitNode?: (node: Node, emit: (node: Node) => void) => void;
onEmitNode?: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void;
}
/* @internal */
@ -186,14 +125,6 @@ namespace ts {
return transformers;
}
/**
* Tracks a monotonically increasing transformation id used to associate a node with a specific
* transformation. This ensures transient properties related to transformations can be safely
* stored on source tree nodes that may be reused across multiple transformations (such as
* with compile-on-save).
*/
let nextTransformId = 1;
/**
* Transforms an array of SourceFiles by passing them through each transformer.
*
@ -203,18 +134,10 @@ namespace ts {
* @param transforms An array of Transformers.
*/
export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]): TransformationResult {
const transformId = nextTransformId;
nextTransformId++;
const tokenSourceMapRanges = createMap<TextRange>();
const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = [];
const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = [];
const enabledSyntaxKindFeatures = new Array<SyntaxKindFeatureFlags>(SyntaxKind.Count);
const parseTreeNodesWithAnnotations: Node[] = [];
let lastTokenSourceMapRangeNode: Node;
let lastTokenSourceMapRangeToken: SyntaxKind;
let lastTokenSourceMapRange: TextRange;
let lexicalEnvironmentStackOffset = 0;
let hoistedVariableDeclarations: VariableDeclaration[];
let hoistedFunctionDeclarations: FunctionDeclaration[];
@ -226,22 +149,14 @@ namespace ts {
getCompilerOptions: () => host.getCompilerOptions(),
getEmitResolver: () => resolver,
getEmitHost: () => host,
getNodeEmitFlags,
setNodeEmitFlags,
getSourceMapRange,
setSourceMapRange,
getTokenSourceMapRange,
setTokenSourceMapRange,
getCommentRange,
setCommentRange,
hoistVariableDeclaration,
hoistFunctionDeclaration,
startLexicalEnvironment,
endLexicalEnvironment,
onSubstituteNode,
onSubstituteNode: (emitContext, node) => node,
enableSubstitution,
isSubstitutionEnabled,
onEmitNode,
onEmitNode: (node, emitContext, emitCallback) => emitCallback(node, emitContext),
enableEmitNotification,
isEmitNotificationEnabled
};
@ -256,30 +171,9 @@ namespace ts {
lexicalEnvironmentDisabled = true;
return {
getSourceFiles: () => transformed,
getTokenSourceMapRange,
isSubstitutionEnabled,
isEmitNotificationEnabled,
onSubstituteNode: context.onSubstituteNode,
onEmitNode: context.onEmitNode,
dispose() {
// During transformation we may need to annotate a parse tree node with transient
// transformation properties. As parse tree nodes live longer than transformation
// nodes, we need to make sure we reclaim any memory allocated for custom ranges
// from these nodes to ensure we do not hold onto entire subtrees just for position
// information. We also need to reset these nodes to a pre-transformation state
// for incremental parsing scenarios so that we do not impact later emit.
for (const node of parseTreeNodesWithAnnotations) {
if (node.transformId === transformId) {
node.transformId = 0;
node.emitFlags = 0;
node.commentRange = undefined;
node.sourceMapRange = undefined;
}
}
parseTreeNodesWithAnnotations.length = 0;
}
transformed,
emitNodeWithSubstitution,
emitNodeWithNotification
};
/**
@ -306,18 +200,29 @@ namespace ts {
* Determines whether expression substitutions are enabled for the provided node.
*/
function isSubstitutionEnabled(node: Node) {
return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.Substitution) !== 0;
return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.Substitution) !== 0
&& (getEmitFlags(node) & EmitFlags.NoSubstitution) === 0;
}
/**
* Default hook for node substitutions.
* Emits a node with possible substitution.
*
* @param node The node to substitute.
* @param isExpression A value indicating whether the node is to be used in an expression
* position.
* @param emitContext The current emit context.
* @param node The node to emit.
* @param emitCallback The callback used to emit the node or its substitute.
*/
function onSubstituteNode(node: Node, isExpression: boolean) {
return node;
function emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
if (node) {
if (isSubstitutionEnabled(node)) {
const substitute = context.onSubstituteNode(emitContext, node);
if (substitute && substitute !== node) {
emitCallback(emitContext, substitute);
return;
}
}
emitCallback(emitContext, node);
}
}
/**
@ -333,154 +238,25 @@ namespace ts {
*/
function isEmitNotificationEnabled(node: Node) {
return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.EmitNotifications) !== 0
|| (getNodeEmitFlags(node) & NodeEmitFlags.AdviseOnEmitNode) !== 0;
|| (getEmitFlags(node) & EmitFlags.AdviseOnEmitNode) !== 0;
}
/**
* Default hook for node emit.
* Emits a node with possible emit notification.
*
* @param emitContext The current emit context.
* @param node The node to emit.
* @param emit A callback used to emit the node in the printer.
* @param emitCallback The callback used to emit the node.
*/
function onEmitNode(node: Node, emit: (node: Node) => void) {
emit(node);
}
/**
* Associates a node with the current transformation, initializing
* various transient transformation properties.
*
* @param node The node.
*/
function beforeSetAnnotation(node: Node) {
if ((node.flags & NodeFlags.Synthesized) === 0 && node.transformId !== transformId) {
// To avoid holding onto transformation artifacts, we keep track of any
// parse tree node we are annotating. This allows us to clean them up after
// all transformations have completed.
parseTreeNodesWithAnnotations.push(node);
node.transformId = transformId;
}
}
/**
* Gets flags that control emit behavior of a node.
*
* If the node does not have its own NodeEmitFlags set, the node emit flags of its
* original pointer are used.
*
* @param node The node.
*/
function getNodeEmitFlags(node: Node) {
return node.emitFlags;
}
/**
* Sets flags that control emit behavior of a node.
*
* @param node The node.
* @param emitFlags The NodeEmitFlags for the node.
*/
function setNodeEmitFlags<T extends Node>(node: T, emitFlags: NodeEmitFlags) {
beforeSetAnnotation(node);
node.emitFlags = emitFlags;
return node;
}
/**
* Gets a custom text range to use when emitting source maps.
*
* If a node does not have its own custom source map text range, the custom source map
* text range of its original pointer is used.
*
* @param node The node.
*/
function getSourceMapRange(node: Node) {
return node.sourceMapRange || node;
}
/**
* Sets a custom text range to use when emitting source maps.
*
* @param node The node.
* @param range The text range.
*/
function setSourceMapRange<T extends Node>(node: T, range: TextRange) {
beforeSetAnnotation(node);
node.sourceMapRange = range;
return node;
}
/**
* Gets the TextRange to use for source maps for a token of a node.
*
* If a node does not have its own custom source map text range for a token, the custom
* source map text range for the token of its original pointer is used.
*
* @param node The node.
* @param token The token.
*/
function getTokenSourceMapRange(node: Node, token: SyntaxKind) {
// As a performance optimization, use the cached value of the most recent node.
// This helps for cases where this function is called repeatedly for the same node.
if (lastTokenSourceMapRangeNode === node && lastTokenSourceMapRangeToken === token) {
return lastTokenSourceMapRange;
}
// Get the custom token source map range for a node or from one of its original nodes.
// Custom token ranges are not stored on the node to avoid the GC burden.
let range: TextRange;
let current = node;
while (current) {
range = current.id ? tokenSourceMapRanges[current.id + "-" + token] : undefined;
if (range !== undefined) {
break;
function emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
if (node) {
if (isEmitNotificationEnabled(node)) {
context.onEmitNode(emitContext, node, emitCallback);
}
else {
emitCallback(emitContext, node);
}
current = current.original;
}
// Cache the most recently requested value.
lastTokenSourceMapRangeNode = node;
lastTokenSourceMapRangeToken = token;
lastTokenSourceMapRange = range;
return range;
}
/**
* Sets the TextRange to use for source maps for a token of a node.
*
* @param node The node.
* @param token The token.
* @param range The text range.
*/
function setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: TextRange) {
// Cache the most recently requested value.
lastTokenSourceMapRangeNode = node;
lastTokenSourceMapRangeToken = token;
lastTokenSourceMapRange = range;
tokenSourceMapRanges[getNodeId(node) + "-" + token] = range;
return node;
}
/**
* Gets a custom text range to use when emitting comments.
*
* If a node does not have its own custom source map text range, the custom source map
* text range of its original pointer is used.
*
* @param node The node.
*/
function getCommentRange(node: Node) {
return node.commentRange || node;
}
/**
* Sets a custom text range to use when emitting comments.
*/
function setCommentRange<T extends Node>(node: T, range: TextRange) {
beforeSetAnnotation(node);
node.commentRange = range;
return node;
}
/**
@ -563,70 +339,4 @@ namespace ts {
return statements;
}
}
/**
* High-order function, creates a function that executes a function composition.
* For example, `chain(a, b)` is the equivalent of `x => ((a', b') => y => b'(a'(y)))(a(x), b(x))`
*
* @param args The functions to chain.
*/
function chain<T, U>(...args: ((t: T) => (u: U) => U)[]): (t: T) => (u: U) => U;
function chain<T, U>(a: (t: T) => (u: U) => U, b: (t: T) => (u: U) => U, c: (t: T) => (u: U) => U, d: (t: T) => (u: U) => U, e: (t: T) => (u: U) => U): (t: T) => (u: U) => U {
if (e) {
const args: ((t: T) => (u: U) => U)[] = [];
for (let i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
return t => compose(...map(args, f => f(t)));
}
else if (d) {
return t => compose(a(t), b(t), c(t), d(t));
}
else if (c) {
return t => compose(a(t), b(t), c(t));
}
else if (b) {
return t => compose(a(t), b(t));
}
else if (a) {
return t => compose(a(t));
}
else {
return t => u => u;
}
}
/**
* High-order function, composes functions. Note that functions are composed inside-out;
* for example, `compose(a, b)` is the equivalent of `x => b(a(x))`.
*
* @param args The functions to compose.
*/
function compose<T>(...args: ((t: T) => T)[]): (t: T) => T;
function compose<T>(a: (t: T) => T, b: (t: T) => T, c: (t: T) => T, d: (t: T) => T, e: (t: T) => T): (t: T) => T {
if (e) {
const args: ((t: T) => T)[] = [];
for (let i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
return t => reduceLeft<(t: T) => T, T>(args, (u, f) => f(u), t);
}
else if (d) {
return t => d(c(b(a(t))));
}
else if (c) {
return t => c(b(a(t)));
}
else if (b) {
return t => b(a(t));
}
else if (a) {
return t => a(t);
}
else {
return t => t;
}
}
}

View File

@ -66,7 +66,7 @@ namespace ts {
// NOTE: this completely disables source maps, but aligns with the behavior of
// `emitAssignment` in the old emitter.
context.setNodeEmitFlags(expression, NodeEmitFlags.NoNestedSourceMaps);
setEmitFlags(expression, EmitFlags.NoNestedSourceMaps);
aggregateTransformFlags(expression);
expressions.push(expression);
@ -102,7 +102,7 @@ namespace ts {
// NOTE: this completely disables source maps, but aligns with the behavior of
// `emitAssignment` in the old emitter.
context.setNodeEmitFlags(declaration, NodeEmitFlags.NoNestedSourceMaps);
setEmitFlags(declaration, EmitFlags.NoNestedSourceMaps);
aggregateTransformFlags(declaration);
declarations.push(declaration);
@ -147,7 +147,7 @@ namespace ts {
// NOTE: this completely disables source maps, but aligns with the behavior of
// `emitAssignment` in the old emitter.
context.setNodeEmitFlags(declaration, NodeEmitFlags.NoNestedSourceMaps);
setEmitFlags(declaration, EmitFlags.NoNestedSourceMaps);
declarations.push(declaration);
aggregateTransformFlags(declaration);
@ -211,7 +211,7 @@ namespace ts {
// NOTE: this completely disables source maps, but aligns with the behavior of
// `emitAssignment` in the old emitter.
context.setNodeEmitFlags(expression, NodeEmitFlags.NoNestedSourceMaps);
setEmitFlags(expression, EmitFlags.NoNestedSourceMaps);
pendingAssignments.push(expression);
return expression;
@ -271,8 +271,8 @@ namespace ts {
}
else {
const name = getMutableClone(<Identifier>target);
context.setSourceMapRange(name, target);
context.setCommentRange(name, target);
setSourceMapRange(name, target);
setCommentRange(name, target);
emitAssignment(name, value, location, /*original*/ undefined);
}
}

View File

@ -168,13 +168,6 @@ namespace ts {
startLexicalEnvironment,
endLexicalEnvironment,
hoistVariableDeclaration,
getNodeEmitFlags,
setNodeEmitFlags,
getCommentRange,
setCommentRange,
getSourceMapRange,
setSourceMapRange,
setTokenSourceMapRange,
} = context;
const resolver = context.getEmitResolver();
@ -209,6 +202,10 @@ namespace ts {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
return node;
}
currentSourceFile = node;
currentText = node.text;
return visitNode(node, visitor, isSourceFile);
@ -432,7 +429,7 @@ namespace ts {
enclosingFunction = currentNode;
if (currentNode.kind !== SyntaxKind.ArrowFunction) {
enclosingNonArrowFunction = currentNode;
if (!(currentNode.emitFlags & NodeEmitFlags.AsyncFunctionBody)) {
if (!(getEmitFlags(currentNode) & EmitFlags.AsyncFunctionBody)) {
enclosingNonAsyncFunctionBody = currentNode;
}
}
@ -695,19 +692,19 @@ namespace ts {
// To preserve the behavior of the old emitter, we explicitly indent
// the body of the function here if it was requested in an earlier
// transformation.
if (getNodeEmitFlags(node) & NodeEmitFlags.Indented) {
setNodeEmitFlags(classFunction, NodeEmitFlags.Indented);
if (getEmitFlags(node) & EmitFlags.Indented) {
setEmitFlags(classFunction, EmitFlags.Indented);
}
// "inner" and "outer" below are added purely to preserve source map locations from
// the old emitter
const inner = createPartiallyEmittedExpression(classFunction);
inner.end = node.end;
setNodeEmitFlags(inner, NodeEmitFlags.NoComments);
setEmitFlags(inner, EmitFlags.NoComments);
const outer = createPartiallyEmittedExpression(inner);
outer.end = skipTrivia(currentText, node.pos);
setNodeEmitFlags(outer, NodeEmitFlags.NoComments);
setEmitFlags(outer, EmitFlags.NoComments);
return createParen(
createCall(
@ -741,17 +738,17 @@ namespace ts {
// emit with the original emitter.
const outer = createPartiallyEmittedExpression(localName);
outer.end = closingBraceLocation.end;
setNodeEmitFlags(outer, NodeEmitFlags.NoComments);
setEmitFlags(outer, EmitFlags.NoComments);
const statement = createReturn(outer);
statement.pos = closingBraceLocation.pos;
setNodeEmitFlags(statement, NodeEmitFlags.NoComments | NodeEmitFlags.NoTokenSourceMaps);
setEmitFlags(statement, EmitFlags.NoComments | EmitFlags.NoTokenSourceMaps);
statements.push(statement);
addRange(statements, endLexicalEnvironment());
const block = createBlock(createNodeArray(statements, /*location*/ node.members), /*location*/ undefined, /*multiLine*/ true);
setNodeEmitFlags(block, NodeEmitFlags.NoComments);
setEmitFlags(block, EmitFlags.NoComments);
return block;
}
@ -798,7 +795,7 @@ namespace ts {
);
if (extendsClauseElement) {
setNodeEmitFlags(constructorFunction, NodeEmitFlags.CapturesThis);
setEmitFlags(constructorFunction, EmitFlags.CapturesThis);
}
statements.push(constructorFunction);
}
@ -890,7 +887,7 @@ namespace ts {
);
if (!constructor) {
setNodeEmitFlags(block, NodeEmitFlags.NoComments);
setEmitFlags(block, EmitFlags.NoComments);
}
return block;
@ -1013,7 +1010,7 @@ namespace ts {
function createDefaultSuperCallOrThis() {
const actualThis = createThis();
setNodeEmitFlags(actualThis, NodeEmitFlags.NoSubstitution);
setEmitFlags(actualThis, EmitFlags.NoSubstitution);
const superCall = createFunctionApply(
createIdentifier("_super"),
actualThis,
@ -1116,27 +1113,27 @@ namespace ts {
// of an initializer, we must emit that expression to preserve side effects.
if (name.elements.length > 0) {
statements.push(
setNodeEmitFlags(
setEmitFlags(
createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(
flattenParameterDestructuring(context, parameter, temp, visitor)
)
),
NodeEmitFlags.CustomPrologue
EmitFlags.CustomPrologue
)
);
}
else if (initializer) {
statements.push(
setNodeEmitFlags(
setEmitFlags(
createStatement(
createAssignment(
temp,
visitNode(initializer, visitor, isExpression)
)
),
NodeEmitFlags.CustomPrologue
EmitFlags.CustomPrologue
)
);
}
@ -1157,23 +1154,23 @@ namespace ts {
getSynthesizedClone(name),
createVoidZero()
),
setNodeEmitFlags(
setEmitFlags(
createBlock([
createStatement(
createAssignment(
setNodeEmitFlags(getMutableClone(name), NodeEmitFlags.NoSourceMap),
setNodeEmitFlags(initializer, NodeEmitFlags.NoSourceMap | getNodeEmitFlags(initializer)),
setEmitFlags(getMutableClone(name), EmitFlags.NoSourceMap),
setEmitFlags(initializer, EmitFlags.NoSourceMap | getEmitFlags(initializer)),
/*location*/ parameter
)
)
], /*location*/ parameter),
NodeEmitFlags.SingleLine | NodeEmitFlags.NoTrailingSourceMap | NodeEmitFlags.NoTokenSourceMaps
EmitFlags.SingleLine | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTokenSourceMaps
),
/*elseStatement*/ undefined,
/*location*/ parameter
);
statement.startsOnNewLine = true;
setNodeEmitFlags(statement, NodeEmitFlags.NoTokenSourceMaps | NodeEmitFlags.NoTrailingSourceMap | NodeEmitFlags.CustomPrologue);
setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.CustomPrologue);
statements.push(statement);
}
@ -1206,7 +1203,7 @@ namespace ts {
// `declarationName` is the name of the local declaration for the parameter.
const declarationName = getMutableClone(<Identifier>parameter.name);
setNodeEmitFlags(declarationName, NodeEmitFlags.NoSourceMap);
setEmitFlags(declarationName, EmitFlags.NoSourceMap);
// `expressionName` is the name of the parameter used in expressions.
const expressionName = getSynthesizedClone(<Identifier>parameter.name);
@ -1215,7 +1212,7 @@ namespace ts {
// var param = [];
statements.push(
setNodeEmitFlags(
setEmitFlags(
createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList([
@ -1227,7 +1224,7 @@ namespace ts {
]),
/*location*/ parameter
),
NodeEmitFlags.CustomPrologue
EmitFlags.CustomPrologue
)
);
@ -1260,7 +1257,7 @@ namespace ts {
])
);
setNodeEmitFlags(forStatement, NodeEmitFlags.CustomPrologue);
setEmitFlags(forStatement, EmitFlags.CustomPrologue);
startOnNewLine(forStatement);
statements.push(forStatement);
}
@ -1291,7 +1288,7 @@ namespace ts {
originalStatement
);
setNodeEmitFlags(captureThisStatement, NodeEmitFlags.NoComments | NodeEmitFlags.CustomPrologue);
setEmitFlags(captureThisStatement, EmitFlags.NoComments | EmitFlags.CustomPrologue);
setSourceMapRange(captureThisStatement, node);
statements.push(captureThisStatement);
}
@ -1354,7 +1351,7 @@ namespace ts {
const sourceMapRange = getSourceMapRange(member);
const func = transformFunctionLikeToExpression(member, /*location*/ member, /*name*/ undefined);
setNodeEmitFlags(func, NodeEmitFlags.NoComments);
setEmitFlags(func, EmitFlags.NoComments);
setSourceMapRange(func, sourceMapRange);
const statement = createStatement(
@ -1375,7 +1372,7 @@ namespace ts {
// The location for the statement is used to emit comments only.
// No source map should be emitted for this statement to align with the
// old emitter.
setNodeEmitFlags(statement, NodeEmitFlags.NoSourceMap);
setEmitFlags(statement, EmitFlags.NoSourceMap);
return statement;
}
@ -1394,7 +1391,7 @@ namespace ts {
// The location for the statement is used to emit source maps only.
// No comments should be emitted for this statement to align with the
// old emitter.
setNodeEmitFlags(statement, NodeEmitFlags.NoComments);
setEmitFlags(statement, EmitFlags.NoComments);
return statement;
}
@ -1408,11 +1405,11 @@ namespace ts {
// To align with source maps in the old emitter, the receiver and property name
// arguments are both mapped contiguously to the accessor name.
const target = getMutableClone(receiver);
setNodeEmitFlags(target, NodeEmitFlags.NoComments | NodeEmitFlags.NoTrailingSourceMap);
setEmitFlags(target, EmitFlags.NoComments | EmitFlags.NoTrailingSourceMap);
setSourceMapRange(target, firstAccessor.name);
const propertyName = createExpressionForPropertyName(visitNode(firstAccessor.name, visitor, isPropertyName));
setNodeEmitFlags(propertyName, NodeEmitFlags.NoComments | NodeEmitFlags.NoLeadingSourceMap);
setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoLeadingSourceMap);
setSourceMapRange(propertyName, firstAccessor.name);
const properties: ObjectLiteralElementLike[] = [];
@ -1463,7 +1460,7 @@ namespace ts {
}
const func = transformFunctionLikeToExpression(node, /*location*/ node, /*name*/ undefined);
setNodeEmitFlags(func, NodeEmitFlags.CapturesThis);
setEmitFlags(func, EmitFlags.CapturesThis);
return func;
}
@ -1588,7 +1585,7 @@ namespace ts {
const expression = visitNode(body, visitor, isExpression);
const returnStatement = createReturn(expression, /*location*/ body);
setNodeEmitFlags(returnStatement, NodeEmitFlags.NoTokenSourceMaps | NodeEmitFlags.NoTrailingSourceMap | NodeEmitFlags.NoTrailingComments);
setEmitFlags(returnStatement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTrailingComments);
statements.push(returnStatement);
// To align with the source map emit for the old emitter, we set a custom
@ -1606,7 +1603,7 @@ namespace ts {
const block = createBlock(createNodeArray(statements, statementsLocation), node.body, multiLine);
if (!multiLine && singleLine) {
setNodeEmitFlags(block, NodeEmitFlags.SingleLine);
setEmitFlags(block, EmitFlags.SingleLine);
}
if (closeBraceLocation) {
@ -2029,7 +2026,7 @@ namespace ts {
}
// The old emitter does not emit source maps for the expression
setNodeEmitFlags(expression, NodeEmitFlags.NoSourceMap | getNodeEmitFlags(expression));
setEmitFlags(expression, EmitFlags.NoSourceMap | getEmitFlags(expression));
// The old emitter does not emit source maps for the block.
// We add the location to preserve comments.
@ -2038,7 +2035,7 @@ namespace ts {
/*location*/ bodyLocation
);
setNodeEmitFlags(body, NodeEmitFlags.NoSourceMap | NodeEmitFlags.NoTokenSourceMaps);
setEmitFlags(body, EmitFlags.NoSourceMap | EmitFlags.NoTokenSourceMaps);
const forStatement = createFor(
createVariableDeclarationList([
@ -2056,7 +2053,7 @@ namespace ts {
);
// Disable trailing source maps for the OpenParenToken to align source map emit with the old emitter.
setNodeEmitFlags(forStatement, NodeEmitFlags.NoTokenTrailingSourceMaps);
setEmitFlags(forStatement, EmitFlags.NoTokenTrailingSourceMaps);
return forStatement;
}
@ -2092,13 +2089,13 @@ namespace ts {
const expressions: Expression[] = [];
const assignment = createAssignment(
temp,
setNodeEmitFlags(
setEmitFlags(
createObjectLiteral(
visitNodes(properties, visitor, isObjectLiteralElementLike, 0, numInitialProperties),
/*location*/ undefined,
node.multiLine
),
NodeEmitFlags.Indented
EmitFlags.Indented
)
);
if (node.multiLine) {
@ -2221,16 +2218,16 @@ namespace ts {
const isAsyncBlockContainingAwait =
enclosingNonArrowFunction
&& (enclosingNonArrowFunction.emitFlags & NodeEmitFlags.AsyncFunctionBody) !== 0
&& (getEmitFlags(enclosingNonArrowFunction) & EmitFlags.AsyncFunctionBody) !== 0
&& (node.statement.transformFlags & TransformFlags.ContainsYield) !== 0;
let loopBodyFlags: NodeEmitFlags = 0;
let loopBodyFlags: EmitFlags = 0;
if (currentState.containsLexicalThis) {
loopBodyFlags |= NodeEmitFlags.CapturesThis;
loopBodyFlags |= EmitFlags.CapturesThis;
}
if (isAsyncBlockContainingAwait) {
loopBodyFlags |= NodeEmitFlags.AsyncFunctionBody;
loopBodyFlags |= EmitFlags.AsyncFunctionBody;
}
const convertedLoopVariable =
@ -2241,7 +2238,7 @@ namespace ts {
createVariableDeclaration(
functionName,
/*type*/ undefined,
setNodeEmitFlags(
setEmitFlags(
createFunctionExpression(
isAsyncBlockContainingAwait ? createToken(SyntaxKind.AsteriskToken) : undefined,
/*name*/ undefined,
@ -2634,7 +2631,7 @@ namespace ts {
// Methods with computed property names are handled in visitObjectLiteralExpression.
Debug.assert(!isComputedPropertyName(node.name));
const functionExpression = transformFunctionLikeToExpression(node, /*location*/ moveRangePos(node, -1), /*name*/ undefined);
setNodeEmitFlags(functionExpression, NodeEmitFlags.NoLeadingComments | getNodeEmitFlags(functionExpression));
setEmitFlags(functionExpression, EmitFlags.NoLeadingComments | getEmitFlags(functionExpression));
return createPropertyAssignment(
node.name,
functionExpression,
@ -2694,7 +2691,7 @@ namespace ts {
const { target, thisArg } = createCallBinding(node.expression, hoistVariableDeclaration);
if (node.expression.kind === SyntaxKind.SuperKeyword) {
setNodeEmitFlags(thisArg, NodeEmitFlags.NoSubstitution);
setEmitFlags(thisArg, EmitFlags.NoSubstitution);
}
let resultingCall: CallExpression | BinaryExpression;
if (node.transformFlags & TransformFlags.ContainsSpreadElementExpression) {
@ -2739,7 +2736,7 @@ namespace ts {
if (node.expression.kind === SyntaxKind.SuperKeyword) {
const actualThis = createThis();
setNodeEmitFlags(actualThis, NodeEmitFlags.NoSubstitution);
setEmitFlags(actualThis, EmitFlags.NoSubstitution);
const initializer =
createLogicalOr(
resultingCall,
@ -3035,7 +3032,7 @@ namespace ts {
*
* @param node The node to be printed.
*/
function onEmitNode(node: Node, emit: (node: Node) => void) {
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
const savedEnclosingFunction = enclosingFunction;
if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && isFunctionLike(node)) {
@ -3043,7 +3040,7 @@ namespace ts {
enclosingFunction = node;
}
previousOnEmitNode(node, emit);
previousOnEmitNode(emitContext, node, emitCallback);
enclosingFunction = savedEnclosingFunction;
}
@ -3084,10 +3081,10 @@ namespace ts {
* @param isExpression A value indicating whether the node is to be used in an expression
* position.
*/
function onSubstituteNode(node: Node, isExpression: boolean) {
node = previousOnSubstituteNode(node, isExpression);
function onSubstituteNode(emitContext: EmitContext, node: Node) {
node = previousOnSubstituteNode(emitContext, node);
if (isExpression) {
if (emitContext === EmitContext.Expression) {
return substituteExpression(node);
}
@ -3175,7 +3172,7 @@ namespace ts {
function substituteThisKeyword(node: PrimaryExpression): PrimaryExpression {
if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis
&& enclosingFunction
&& enclosingFunction.emitFlags & NodeEmitFlags.CapturesThis) {
&& getEmitFlags(enclosingFunction) & EmitFlags.CapturesThis) {
return createIdentifier("_this", /*location*/ node);
}
@ -3193,7 +3190,7 @@ namespace ts {
* @param allowSourceMaps A value indicating whether source maps may be emitted for the name.
*/
function getLocalName(node: ClassDeclaration | ClassExpression | FunctionDeclaration, allowComments?: boolean, allowSourceMaps?: boolean) {
return getDeclarationName(node, allowComments, allowSourceMaps, NodeEmitFlags.LocalName);
return getDeclarationName(node, allowComments, allowSourceMaps, EmitFlags.LocalName);
}
/**
@ -3202,18 +3199,18 @@ namespace ts {
* @param node The declaration.
* @param allowComments Allow comments for the name.
*/
function getDeclarationName(node: DeclarationStatement | ClassExpression, allowComments?: boolean, allowSourceMaps?: boolean, emitFlags?: NodeEmitFlags) {
function getDeclarationName(node: DeclarationStatement | ClassExpression, allowComments?: boolean, allowSourceMaps?: boolean, emitFlags?: EmitFlags) {
if (node.name && !isGeneratedIdentifier(node.name)) {
const name = getMutableClone(node.name);
emitFlags |= getNodeEmitFlags(node.name);
emitFlags |= getEmitFlags(node.name);
if (!allowSourceMaps) {
emitFlags |= NodeEmitFlags.NoSourceMap;
emitFlags |= EmitFlags.NoSourceMap;
}
if (!allowComments) {
emitFlags |= NodeEmitFlags.NoComments;
emitFlags |= EmitFlags.NoComments;
}
if (emitFlags) {
setNodeEmitFlags(name, emitFlags);
setEmitFlags(name, emitFlags);
}
return name;
}

View File

@ -9,6 +9,10 @@ namespace ts {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
return node;
}
return visitEachChild(node, visitor, context);
}

View File

@ -231,9 +231,6 @@ namespace ts {
endLexicalEnvironment,
hoistFunctionDeclaration,
hoistVariableDeclaration,
setSourceMapRange,
setCommentRange,
setNodeEmitFlags
} = context;
const compilerOptions = context.getCompilerOptions();
@ -294,6 +291,10 @@ namespace ts {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
return node;
}
if (node.transformFlags & TransformFlags.ContainsGenerator) {
currentSourceFile = node;
node = visitEachChild(node, visitor, context);
@ -444,7 +445,7 @@ namespace ts {
*/
function visitFunctionDeclaration(node: FunctionDeclaration): Statement {
// Currently, we only support generators that were originally async functions.
if (node.asteriskToken && node.emitFlags & NodeEmitFlags.AsyncFunctionBody) {
if (node.asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
node = setOriginalNode(
createFunctionDeclaration(
/*decorators*/ undefined,
@ -492,7 +493,7 @@ namespace ts {
*/
function visitFunctionExpression(node: FunctionExpression): Expression {
// Currently, we only support generators that were originally async functions.
if (node.asteriskToken && node.emitFlags & NodeEmitFlags.AsyncFunctionBody) {
if (node.asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
node = setOriginalNode(
createFunctionExpression(
/*asteriskToken*/ undefined,
@ -616,7 +617,7 @@ namespace ts {
}
else {
// Do not hoist custom prologues.
if (node.emitFlags & NodeEmitFlags.CustomPrologue) {
if (getEmitFlags(node) & EmitFlags.CustomPrologue) {
return node;
}
@ -1887,9 +1888,9 @@ namespace ts {
return -1;
}
function onSubstituteNode(node: Node, isExpression: boolean): Node {
node = previousOnSubstituteNode(node, isExpression);
if (isExpression) {
function onSubstituteNode(emitContext: EmitContext, node: Node): Node {
node = previousOnSubstituteNode(emitContext, node);
if (emitContext === EmitContext.Expression) {
return substituteExpression(<Expression>node);
}
return node;
@ -2585,7 +2586,7 @@ namespace ts {
/*typeArguments*/ undefined,
[
createThis(),
setNodeEmitFlags(
setEmitFlags(
createFunctionExpression(
/*asteriskToken*/ undefined,
/*name*/ undefined,
@ -2598,7 +2599,7 @@ namespace ts {
/*multiLine*/ buildResult.length > 0
)
),
NodeEmitFlags.ReuseTempVariableScope
EmitFlags.ReuseTempVariableScope
)
]
);

View File

@ -16,6 +16,10 @@ namespace ts {
* @param node A SourceFile node.
*/
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
return node;
}
currentSourceFile = node;
node = visitEachChild(node, visitor, context);
currentSourceFile = undefined;

View File

@ -12,6 +12,10 @@ namespace ts {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
return node;
}
if (isExternalModule(node) || compilerOptions.isolatedModules) {
currentSourceFile = node;
return visitEachChild(node, visitor, context);

View File

@ -15,9 +15,6 @@ namespace ts {
startLexicalEnvironment,
endLexicalEnvironment,
hoistVariableDeclaration,
setNodeEmitFlags,
getNodeEmitFlags,
setSourceMapRange,
} = context;
const compilerOptions = context.getCompilerOptions();
@ -54,6 +51,10 @@ namespace ts {
* @param node The SourceFile node.
*/
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
return node;
}
if (isExternalModule(node) || compilerOptions.isolatedModules) {
currentSourceFile = node;
@ -92,7 +93,7 @@ namespace ts {
const updated = updateSourceFile(node, statements);
if (hasExportStarsToExportValues) {
setNodeEmitFlags(updated, NodeEmitFlags.EmitExportStar | getNodeEmitFlags(node));
setEmitFlags(updated, EmitFlags.EmitExportStar | getEmitFlags(node));
}
return updated;
@ -116,7 +117,7 @@ namespace ts {
*/
function transformUMDModule(node: SourceFile) {
const define = createIdentifier("define");
setNodeEmitFlags(define, NodeEmitFlags.UMDDefine);
setEmitFlags(define, EmitFlags.UMDDefine);
return transformAsynchronousModule(node, define, /*moduleName*/ undefined, /*includeNonAmdDependencies*/ false);
}
@ -220,7 +221,7 @@ namespace ts {
if (hasExportStarsToExportValues) {
// If we have any `export * from ...` declarations
// we need to inform the emitter to add the __export helper.
setNodeEmitFlags(body, NodeEmitFlags.EmitExportStar);
setEmitFlags(body, EmitFlags.EmitExportStar);
}
return body;
@ -234,7 +235,7 @@ namespace ts {
/*location*/ exportEquals
);
setNodeEmitFlags(statement, NodeEmitFlags.NoTokenSourceMaps | NodeEmitFlags.NoComments);
setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoComments);
statements.push(statement);
}
else {
@ -249,7 +250,7 @@ namespace ts {
/*location*/ exportEquals
);
setNodeEmitFlags(statement, NodeEmitFlags.NoComments);
setEmitFlags(statement, EmitFlags.NoComments);
statements.push(statement);
}
}
@ -388,7 +389,7 @@ namespace ts {
// Set emitFlags on the name of the importEqualsDeclaration
// This is so the printer will not substitute the identifier
setNodeEmitFlags(node.name, NodeEmitFlags.NoSubstitution);
setEmitFlags(node.name, EmitFlags.NoSubstitution);
const statements: Statement[] = [];
if (moduleKind !== ModuleKind.AMD) {
if (hasModifier(node, ModifierFlags.Export)) {
@ -598,7 +599,7 @@ namespace ts {
}
else {
statements.push(
createExportStatement(node.name, setNodeEmitFlags(getSynthesizedClone(node.name), NodeEmitFlags.LocalName), /*location*/ node)
createExportStatement(node.name, setEmitFlags(getSynthesizedClone(node.name), EmitFlags.LocalName), /*location*/ node)
);
}
}
@ -813,7 +814,7 @@ namespace ts {
)],
/*location*/ node
);
setNodeEmitFlags(transformedStatement, NodeEmitFlags.NoComments);
setEmitFlags(transformedStatement, EmitFlags.NoComments);
statements.push(transformedStatement);
}
@ -821,14 +822,14 @@ namespace ts {
return node.name ? getSynthesizedClone(node.name) : getGeneratedNameForNode(node);
}
function onEmitNode(node: Node, emit: (node: Node) => void): void {
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void {
if (node.kind === SyntaxKind.SourceFile) {
bindingNameExportSpecifiersMap = bindingNameExportSpecifiersForFileMap[getOriginalNodeId(node)];
previousOnEmitNode(node, emit);
previousOnEmitNode(emitContext, node, emitCallback);
bindingNameExportSpecifiersMap = undefined;
}
else {
previousOnEmitNode(node, emit);
previousOnEmitNode(emitContext, node, emitCallback);
}
}
@ -839,9 +840,9 @@ namespace ts {
* @param isExpression A value indicating whether the node is to be used in an expression
* position.
*/
function onSubstituteNode(node: Node, isExpression: boolean) {
node = previousOnSubstituteNode(node, isExpression);
if (isExpression) {
function onSubstituteNode(emitContext: EmitContext, node: Node) {
node = previousOnSubstituteNode(emitContext, node);
if (emitContext === EmitContext.Expression) {
return substituteExpression(<Expression>node);
}
else if (isShorthandPropertyAssignment(node)) {
@ -890,7 +891,7 @@ namespace ts {
// If the left-hand-side of the binaryExpression is an identifier and its is export through export Specifier
if (isIdentifier(left) && isAssignmentOperator(node.operatorToken.kind)) {
if (bindingNameExportSpecifiersMap && hasProperty(bindingNameExportSpecifiersMap, left.text)) {
setNodeEmitFlags(node, NodeEmitFlags.NoSubstitution);
setEmitFlags(node, EmitFlags.NoSubstitution);
let nestedExportAssignment: BinaryExpression;
for (const specifier of bindingNameExportSpecifiersMap[left.text]) {
nestedExportAssignment = nestedExportAssignment ?
@ -910,7 +911,7 @@ namespace ts {
const operand = node.operand;
if (isIdentifier(operand) && bindingNameExportSpecifiersForFileMap) {
if (bindingNameExportSpecifiersMap && hasProperty(bindingNameExportSpecifiersMap, operand.text)) {
setNodeEmitFlags(node, NodeEmitFlags.NoSubstitution);
setEmitFlags(node, EmitFlags.NoSubstitution);
let transformedUnaryExpression: BinaryExpression;
if (node.kind === SyntaxKind.PostfixUnaryExpression) {
transformedUnaryExpression = createBinary(
@ -920,7 +921,7 @@ namespace ts {
/*location*/ node
);
// We have to set no substitution flag here to prevent visit the binary expression and substitute it again as we will preform all necessary substitution in here
setNodeEmitFlags(transformedUnaryExpression, NodeEmitFlags.NoSubstitution);
setEmitFlags(transformedUnaryExpression, EmitFlags.NoSubstitution);
}
let nestedExportAssignment: BinaryExpression;
for (const specifier of bindingNameExportSpecifiersMap[operand.text]) {
@ -935,9 +936,9 @@ namespace ts {
}
function trySubstituteExportedName(node: Identifier) {
const emitFlags = getNodeEmitFlags(node);
if ((emitFlags & NodeEmitFlags.LocalName) === 0) {
const container = resolver.getReferencedExportContainer(node, (emitFlags & NodeEmitFlags.ExportName) !== 0);
const emitFlags = getEmitFlags(node);
if ((emitFlags & EmitFlags.LocalName) === 0) {
const container = resolver.getReferencedExportContainer(node, (emitFlags & EmitFlags.ExportName) !== 0);
if (container) {
if (container.kind === SyntaxKind.SourceFile) {
return createPropertyAccess(
@ -953,7 +954,7 @@ namespace ts {
}
function trySubstituteImportedName(node: Identifier): Expression {
if ((getNodeEmitFlags(node) & NodeEmitFlags.LocalName) === 0) {
if ((getEmitFlags(node) & EmitFlags.LocalName) === 0) {
const declaration = resolver.getReferencedImportDeclaration(node);
if (declaration) {
if (isImportClause(declaration)) {
@ -1077,7 +1078,7 @@ namespace ts {
if (includeNonAmdDependencies && importAliasName) {
// Set emitFlags on the name of the classDeclaration
// This is so that when printer will not substitute the identifier
setNodeEmitFlags(importAliasName, NodeEmitFlags.NoSubstitution);
setEmitFlags(importAliasName, EmitFlags.NoSubstitution);
aliasedModuleNames.push(externalModuleName);
importAliasNames.push(createParameter(importAliasName));
}

View File

@ -10,8 +10,6 @@ namespace ts {
}
const {
getNodeEmitFlags,
setNodeEmitFlags,
startLexicalEnvironment,
endLexicalEnvironment,
hoistVariableDeclaration,
@ -50,6 +48,10 @@ namespace ts {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
return node;
}
if (isExternalModule(node) || compilerOptions.isolatedModules) {
currentSourceFile = node;
currentNode = node;
@ -116,9 +118,9 @@ namespace ts {
createParameter(contextObjectForFile)
],
/*type*/ undefined,
setNodeEmitFlags(
setEmitFlags(
createBlock(statements, /*location*/ undefined, /*multiLine*/ true),
NodeEmitFlags.EmitEmitHelpers
EmitFlags.EmitEmitHelpers
)
);
@ -135,7 +137,7 @@ namespace ts {
: [dependencies, body]
)
)
], /*nodeEmitFlags*/ ~NodeEmitFlags.EmitEmitHelpers & getNodeEmitFlags(node));
], /*nodeEmitFlags*/ ~EmitFlags.EmitEmitHelpers & getEmitFlags(node));
}
/**
@ -984,14 +986,14 @@ namespace ts {
// Substitutions
//
function onEmitNode(node: Node, emit: (node: Node) => void): void {
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void {
if (node.kind === SyntaxKind.SourceFile) {
exportFunctionForFile = exportFunctionForFileMap[getOriginalNodeId(node)];
previousOnEmitNode(node, emit);
previousOnEmitNode(emitContext, node, emitCallback);
exportFunctionForFile = undefined;
}
else {
previousOnEmitNode(node, emit);
previousOnEmitNode(emitContext, node, emitCallback);
}
}
@ -1002,9 +1004,9 @@ namespace ts {
* @param isExpression A value indicating whether the node is to be used in an expression
* position.
*/
function onSubstituteNode(node: Node, isExpression: boolean) {
node = previousOnSubstituteNode(node, isExpression);
if (isExpression) {
function onSubstituteNode(emitContext: EmitContext, node: Node) {
node = previousOnSubstituteNode(emitContext, node);
if (emitContext === EmitContext.Expression) {
return substituteExpression(<Expression>node);
}
@ -1053,7 +1055,7 @@ namespace ts {
}
function substituteAssignmentExpression(node: BinaryExpression): Expression {
setNodeEmitFlags(node, NodeEmitFlags.NoSubstitution);
setEmitFlags(node, EmitFlags.NoSubstitution);
const left = node.left;
switch (left.kind) {
@ -1176,7 +1178,7 @@ namespace ts {
const exportDeclaration = resolver.getReferencedExportContainer(<Identifier>operand);
if (exportDeclaration) {
const expr = createPrefix(node.operator, operand, node);
setNodeEmitFlags(expr, NodeEmitFlags.NoSubstitution);
setEmitFlags(expr, EmitFlags.NoSubstitution);
const call = createExportExpression(<Identifier>operand, expr);
if (node.kind === SyntaxKind.PrefixUnaryExpression) {
return call;
@ -1241,7 +1243,7 @@ namespace ts {
]),
m,
createBlock([
setNodeEmitFlags(
setEmitFlags(
createIf(
condition,
createStatement(
@ -1251,7 +1253,7 @@ namespace ts {
)
)
),
NodeEmitFlags.SingleLine
EmitFlags.SingleLine
)
])
),
@ -1393,10 +1395,10 @@ namespace ts {
hoistBindingElement(node, /*isExported*/ false);
}
function updateSourceFile(node: SourceFile, statements: Statement[], nodeEmitFlags: NodeEmitFlags) {
function updateSourceFile(node: SourceFile, statements: Statement[], nodeEmitFlags: EmitFlags) {
const updated = getMutableClone(node);
updated.statements = createNodeArray(statements, node.statements);
setNodeEmitFlags(updated, nodeEmitFlags);
setEmitFlags(updated, nodeEmitFlags);
return updated;
}
}

View File

@ -24,10 +24,6 @@ namespace ts {
export function transformTypeScript(context: TransformationContext) {
const {
getNodeEmitFlags,
setNodeEmitFlags,
setCommentRange,
setSourceMapRange,
startLexicalEnvironment,
endLexicalEnvironment,
hoistVariableDeclaration,
@ -46,6 +42,10 @@ namespace ts {
context.onEmitNode = onEmitNode;
context.onSubstituteNode = onSubstituteNode;
// Enable substitution for property/element access to emit const enum values.
context.enableSubstitution(SyntaxKind.PropertyAccessExpression);
context.enableSubstitution(SyntaxKind.ElementAccessExpression);
// These variables contain state that changes as we descend into the tree.
let currentSourceFile: SourceFile;
let currentNamespace: ModuleDeclaration;
@ -85,6 +85,10 @@ namespace ts {
* @param node A SourceFile node.
*/
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
return node;
}
return visitNode(node, visitor, isSourceFile);
}
@ -449,7 +453,7 @@ namespace ts {
node = visitEachChild(node, visitor, context);
}
setNodeEmitFlags(node, NodeEmitFlags.EmitEmitHelpers | node.emitFlags);
setEmitFlags(node, EmitFlags.EmitEmitHelpers | getEmitFlags(node));
return node;
}
@ -521,7 +525,7 @@ namespace ts {
// To better align with the old emitter, we should not emit a trailing source map
// entry if the class has static properties.
if (staticProperties.length > 0) {
setNodeEmitFlags(classDeclaration, NodeEmitFlags.NoTrailingSourceMap | getNodeEmitFlags(classDeclaration));
setEmitFlags(classDeclaration, EmitFlags.NoTrailingSourceMap | getEmitFlags(classDeclaration));
}
statements.push(classDeclaration);
@ -776,7 +780,7 @@ namespace ts {
// To preserve the behavior of the old emitter, we explicitly indent
// the body of a class with static initializers.
setNodeEmitFlags(classExpression, NodeEmitFlags.Indented | getNodeEmitFlags(classExpression));
setEmitFlags(classExpression, EmitFlags.Indented | getEmitFlags(classExpression));
expressions.push(startOnNewLine(createAssignment(temp, classExpression)));
addRange(expressions, generateInitializedPropertyExpressions(node, staticProperties, temp));
expressions.push(startOnNewLine(temp));
@ -1009,10 +1013,10 @@ namespace ts {
Debug.assert(isIdentifier(node.name));
const name = node.name as Identifier;
const propertyName = getMutableClone(name);
setNodeEmitFlags(propertyName, NodeEmitFlags.NoComments | NodeEmitFlags.NoSourceMap);
setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoSourceMap);
const localName = getMutableClone(name);
setNodeEmitFlags(localName, NodeEmitFlags.NoComments);
setEmitFlags(localName, EmitFlags.NoComments);
return startOnNewLine(
createStatement(
@ -1418,7 +1422,7 @@ namespace ts {
moveRangePastDecorators(member)
);
setNodeEmitFlags(helper, NodeEmitFlags.NoComments);
setEmitFlags(helper, EmitFlags.NoComments);
return helper;
}
@ -1467,7 +1471,7 @@ namespace ts {
);
const result = createAssignment(getDeclarationName(node), expression, moveRangePastDecorators(node));
setNodeEmitFlags(result, NodeEmitFlags.NoComments);
setEmitFlags(result, EmitFlags.NoComments);
return result;
}
// Emit the call to __decorate. Given the class:
@ -1491,7 +1495,7 @@ namespace ts {
moveRangePastDecorators(node)
);
setNodeEmitFlags(result, NodeEmitFlags.NoComments);
setEmitFlags(result, EmitFlags.NoComments);
return result;
}
}
@ -1521,7 +1525,7 @@ namespace ts {
transformDecorator(decorator),
parameterOffset,
/*location*/ decorator.expression);
setNodeEmitFlags(helper, NodeEmitFlags.NoComments);
setEmitFlags(helper, EmitFlags.NoComments);
expressions.push(helper);
}
}
@ -2324,11 +2328,11 @@ namespace ts {
if (languageVersion >= ScriptTarget.ES6) {
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) {
enableSubstitutionForAsyncMethodsWithSuper();
setNodeEmitFlags(block, NodeEmitFlags.EmitAdvancedSuperHelper);
setEmitFlags(block, EmitFlags.EmitAdvancedSuperHelper);
}
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) {
enableSubstitutionForAsyncMethodsWithSuper();
setNodeEmitFlags(block, NodeEmitFlags.EmitSuperHelper);
setEmitFlags(block, EmitFlags.EmitSuperHelper);
}
}
@ -2375,7 +2379,7 @@ namespace ts {
setOriginalNode(parameter, node);
setCommentRange(parameter, node);
setSourceMapRange(parameter, moveRangePastModifiers(node));
setNodeEmitFlags(parameter.name, NodeEmitFlags.NoTrailingSourceMap);
setEmitFlags(parameter.name, EmitFlags.NoTrailingSourceMap);
return parameter;
}
@ -2534,7 +2538,7 @@ namespace ts {
// We request to be advised when the printer is about to print this node. This allows
// us to set up the correct state for later substitutions.
let emitFlags = NodeEmitFlags.AdviseOnEmitNode;
let emitFlags = EmitFlags.AdviseOnEmitNode;
// If needed, we should emit a variable declaration for the enum. If we emit
// a leading variable declaration, we should not emit leading comments for the
@ -2544,7 +2548,7 @@ namespace ts {
// We should still emit the comments if we are emitting a system module.
if (moduleKind !== ModuleKind.System || currentScope !== currentSourceFile) {
emitFlags |= NodeEmitFlags.NoLeadingComments;
emitFlags |= EmitFlags.NoLeadingComments;
}
}
@ -2584,7 +2588,7 @@ namespace ts {
);
setOriginalNode(enumStatement, node);
setNodeEmitFlags(enumStatement, emitFlags);
setEmitFlags(enumStatement, emitFlags);
statements.push(enumStatement);
if (isNamespaceExport(node)) {
@ -2736,7 +2740,7 @@ namespace ts {
// })(m1 || (m1 = {})); // trailing comment module
//
setCommentRange(statement, node);
setNodeEmitFlags(statement, NodeEmitFlags.NoTrailingComments);
setEmitFlags(statement, EmitFlags.NoTrailingComments);
statements.push(statement);
}
@ -2759,7 +2763,7 @@ namespace ts {
// We request to be advised when the printer is about to print this node. This allows
// us to set up the correct state for later substitutions.
let emitFlags = NodeEmitFlags.AdviseOnEmitNode;
let emitFlags = EmitFlags.AdviseOnEmitNode;
// If needed, we should emit a variable declaration for the module. If we emit
// a leading variable declaration, we should not emit leading comments for the
@ -2768,7 +2772,7 @@ namespace ts {
addVarForEnumOrModuleDeclaration(statements, node);
// We should still emit the comments if we are emitting a system module.
if (moduleKind !== ModuleKind.System || currentScope !== currentSourceFile) {
emitFlags |= NodeEmitFlags.NoLeadingComments;
emitFlags |= EmitFlags.NoLeadingComments;
}
}
@ -2820,7 +2824,7 @@ namespace ts {
);
setOriginalNode(moduleStatement, node);
setNodeEmitFlags(moduleStatement, emitFlags);
setEmitFlags(moduleStatement, emitFlags);
statements.push(moduleStatement);
return statements;
}
@ -2895,7 +2899,7 @@ namespace ts {
// })(hello || (hello = {}));
// We only want to emit comment on the namespace which contains block body itself, not the containing namespaces.
if (body.kind !== SyntaxKind.ModuleBlock) {
setNodeEmitFlags(block, block.emitFlags | NodeEmitFlags.NoComments);
setEmitFlags(block, getEmitFlags(block) | EmitFlags.NoComments);
}
return block;
}
@ -2936,7 +2940,7 @@ namespace ts {
}
const moduleReference = createExpressionFromEntityName(<EntityName>node.moduleReference);
setNodeEmitFlags(moduleReference, NodeEmitFlags.NoComments | NodeEmitFlags.NoNestedComments);
setEmitFlags(moduleReference, EmitFlags.NoComments | EmitFlags.NoNestedComments);
if (isNamedExternalModuleExport(node) || !isNamespaceExport(node)) {
// export var ${name} = ${moduleReference};
@ -3048,15 +3052,15 @@ namespace ts {
function getNamespaceMemberName(name: Identifier, allowComments?: boolean, allowSourceMaps?: boolean): Expression {
const qualifiedName = createPropertyAccess(currentNamespaceContainerName, getSynthesizedClone(name), /*location*/ name);
let emitFlags: NodeEmitFlags;
let emitFlags: EmitFlags;
if (!allowComments) {
emitFlags |= NodeEmitFlags.NoComments;
emitFlags |= EmitFlags.NoComments;
}
if (!allowSourceMaps) {
emitFlags |= NodeEmitFlags.NoSourceMap;
emitFlags |= EmitFlags.NoSourceMap;
}
if (emitFlags) {
setNodeEmitFlags(qualifiedName, emitFlags);
setEmitFlags(qualifiedName, emitFlags);
}
return qualifiedName;
}
@ -3093,7 +3097,7 @@ namespace ts {
* @param allowComments A value indicating whether comments may be emitted for the name.
*/
function getLocalName(node: DeclarationStatement | ClassExpression, noSourceMaps?: boolean, allowComments?: boolean) {
return getDeclarationName(node, allowComments, !noSourceMaps, NodeEmitFlags.LocalName);
return getDeclarationName(node, allowComments, !noSourceMaps, EmitFlags.LocalName);
}
/**
@ -3111,7 +3115,7 @@ namespace ts {
return getNamespaceMemberName(getDeclarationName(node), allowComments, !noSourceMaps);
}
return getDeclarationName(node, allowComments, !noSourceMaps, NodeEmitFlags.ExportName);
return getDeclarationName(node, allowComments, !noSourceMaps, EmitFlags.ExportName);
}
/**
@ -3122,20 +3126,20 @@ namespace ts {
* @param allowSourceMaps A value indicating whether source maps may be emitted for the name.
* @param emitFlags Additional NodeEmitFlags to specify for the name.
*/
function getDeclarationName(node: DeclarationStatement | ClassExpression, allowComments?: boolean, allowSourceMaps?: boolean, emitFlags?: NodeEmitFlags) {
function getDeclarationName(node: DeclarationStatement | ClassExpression, allowComments?: boolean, allowSourceMaps?: boolean, emitFlags?: EmitFlags) {
if (node.name) {
const name = getMutableClone(node.name);
emitFlags |= getNodeEmitFlags(node.name);
emitFlags |= getEmitFlags(node.name);
if (!allowSourceMaps) {
emitFlags |= NodeEmitFlags.NoSourceMap;
emitFlags |= EmitFlags.NoSourceMap;
}
if (!allowComments) {
emitFlags |= NodeEmitFlags.NoComments;
emitFlags |= EmitFlags.NoComments;
}
if (emitFlags) {
setNodeEmitFlags(name, emitFlags);
setEmitFlags(name, emitFlags);
}
return name;
@ -3231,7 +3235,7 @@ namespace ts {
* @param node The node to emit.
* @param emit A callback used to emit the node in the printer.
*/
function onEmitNode(node: Node, emit: (node: Node) => void): void {
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void {
const savedApplicableSubstitutions = applicableSubstitutions;
const savedCurrentSuperContainer = currentSuperContainer;
// If we need to support substitutions for `super` in an async method,
@ -3248,7 +3252,7 @@ namespace ts {
applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers;
}
previousOnEmitNode(node, emit);
previousOnEmitNode(emitContext, node, emitCallback);
applicableSubstitutions = savedApplicableSubstitutions;
currentSuperContainer = savedCurrentSuperContainer;
@ -3261,9 +3265,9 @@ namespace ts {
* @param isExpression A value indicating whether the node is to be used in an expression
* position.
*/
function onSubstituteNode(node: Node, isExpression: boolean) {
node = previousOnSubstituteNode(node, isExpression);
if (isExpression) {
function onSubstituteNode(emitContext: EmitContext, node: Node) {
node = previousOnSubstituteNode(emitContext, node);
if (emitContext === EmitContext.Expression) {
return substituteExpression(<Expression>node);
}
else if (isShorthandPropertyAssignment(node)) {
@ -3294,17 +3298,15 @@ namespace ts {
switch (node.kind) {
case SyntaxKind.Identifier:
return substituteExpressionIdentifier(<Identifier>node);
}
if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper) {
switch (node.kind) {
case SyntaxKind.CallExpression:
case SyntaxKind.PropertyAccessExpression:
return substitutePropertyAccessExpression(<PropertyAccessExpression>node);
case SyntaxKind.ElementAccessExpression:
return substituteElementAccessExpression(<ElementAccessExpression>node);
case SyntaxKind.CallExpression:
if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper) {
return substituteCallExpression(<CallExpression>node);
case SyntaxKind.PropertyAccessExpression:
return substitutePropertyAccessExpression(<PropertyAccessExpression>node);
case SyntaxKind.ElementAccessExpression:
return substituteElementAccessExpression(<ElementAccessExpression>node);
}
}
break;
}
return node;
@ -3342,7 +3344,7 @@ namespace ts {
function trySubstituteNamespaceExportedName(node: Identifier): Expression {
// If this is explicitly a local name, do not substitute.
if (enabledSubstitutions & applicableSubstitutions && (getNodeEmitFlags(node) & NodeEmitFlags.LocalName) === 0) {
if (enabledSubstitutions & applicableSubstitutions && (getEmitFlags(node) & EmitFlags.LocalName) === 0) {
// If we are nested within a namespace declaration, we may need to qualifiy
// an identifier that is exported from a merged namespace.
const container = resolver.getReferencedExportContainer(node, /*prefixLocals*/ false);
@ -3381,7 +3383,7 @@ namespace ts {
}
function substitutePropertyAccessExpression(node: PropertyAccessExpression) {
if (node.expression.kind === SyntaxKind.SuperKeyword) {
if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper && node.expression.kind === SyntaxKind.SuperKeyword) {
const flags = getSuperContainerAsyncMethodFlags();
if (flags) {
return createSuperAccessInAsyncMethod(
@ -3392,11 +3394,11 @@ namespace ts {
}
}
return node;
return substituteConstantValue(node);
}
function substituteElementAccessExpression(node: ElementAccessExpression) {
if (node.expression.kind === SyntaxKind.SuperKeyword) {
if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper && node.expression.kind === SyntaxKind.SuperKeyword) {
const flags = getSuperContainerAsyncMethodFlags();
if (flags) {
return createSuperAccessInAsyncMethod(
@ -3407,9 +3409,39 @@ namespace ts {
}
}
return substituteConstantValue(node);
}
function substituteConstantValue(node: PropertyAccessExpression | ElementAccessExpression): LeftHandSideExpression {
const constantValue = tryGetConstEnumValue(node);
if (constantValue !== undefined) {
const substitute = createLiteral(constantValue);
setSourceMapRange(substitute, node);
setCommentRange(substitute, node);
if (!compilerOptions.removeComments) {
const propertyName = isPropertyAccessExpression(node)
? declarationNameToString(node.name)
: getTextOfNode(node.argumentExpression);
substitute.trailingComment = ` ${propertyName} `;
}
setConstantValue(node, constantValue);
return substitute;
}
return node;
}
function tryGetConstEnumValue(node: Node): number {
if (compilerOptions.isolatedModules) {
return undefined;
}
return isPropertyAccessExpression(node) || isElementAccessExpression(node)
? resolver.getConstantValue(<PropertyAccessExpression | ElementAccessExpression>node)
: undefined;
}
function createSuperAccessInAsyncMethod(argumentExpression: Expression, flags: NodeCheckFlags, location: TextRange): LeftHandSideExpression {
if (flags & NodeCheckFlags.AsyncMethodWithSuperBinding) {
return createPropertyAccess(

View File

@ -19,6 +19,7 @@ namespace ts {
remove(fileName: Path): void;
forEachValue(f: (key: Path, v: T) => void): void;
getKeys(): Path[];
clear(): void;
}
@ -493,10 +494,7 @@ namespace ts {
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
/* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes)
/* @internal */ flowNode?: FlowNode; // Associated FlowNode (initialized by binding)
/* @internal */ transformId?: number; // Associates transient transformation properties with a specific transformation (initialized by transformation).
/* @internal */ emitFlags?: NodeEmitFlags; // Transient emit flags for a synthesized node (initialized by transformation).
/* @internal */ sourceMapRange?: TextRange; // Transient custom sourcemap range for a synthesized node (initialized by transformation).
/* @internal */ commentRange?: TextRange; // Transient custom comment range for a synthesized node (initialized by transformation).
/* @internal */ emitNode?: EmitNode; // Associated EmitNode (initialized by transforms)
}
export interface NodeArray<T extends Node> extends Array<T>, TextRange {
@ -1863,7 +1861,7 @@ namespace ts {
* used for writing the JavaScript and declaration files. Otherwise, the writeFile parameter
* will be invoked when writing the JavaScript and declaration files.
*/
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean): EmitResult;
getOptionsDiagnostics(cancellationToken?: CancellationToken): Diagnostic[];
getGlobalDiagnostics(cancellationToken?: CancellationToken): Diagnostic[];
@ -1881,6 +1879,7 @@ namespace ts {
// For testing purposes only. Should not be used by any other consumers (including the
// language service).
/* @internal */ getDiagnosticsProducingTypeChecker(): TypeChecker;
/* @internal */ dropDiagnosticsProducingTypeChecker(): void;
/* @internal */ getClassifiableNames(): Map<string>;
@ -2052,6 +2051,7 @@ namespace ts {
UseFullyQualifiedType = 0x00000080, // Write out the fully qualified type name (eg. Module.Type, instead of Type)
InFirstTypeArgument = 0x00000100, // Writing first type argument of the instantiated type
InTypeAlias = 0x00000200, // Writing type in type alias declaration
UseTypeAliasValue = 0x00000400, // Serialize the type instead of using type-alias. This is needed when we emit declaration file.
}
export const enum SymbolFormatFlags {
@ -2289,7 +2289,8 @@ namespace ts {
mapper?: TypeMapper; // Type mapper for instantiation alias
referenced?: boolean; // True if alias symbol has been referenced as a value
containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property
hasCommonType?: boolean; // True if constituents of synthetic property all have same type
hasNonUniformType?: boolean; // True if constituents have non-uniform types
isPartial?: boolean; // True if syntheric property of union type occurs in some but not all constituents
isDiscriminantProperty?: boolean; // True if discriminant synthetic property
resolvedExports?: SymbolTable; // Resolved exports of module
exportsChecked?: boolean; // True if exports of external module have been checked
@ -2850,6 +2851,7 @@ namespace ts {
raw?: any;
errors: Diagnostic[];
wildcardDirectories?: MapLike<WatchDirectoryFlags>;
compileOnSave?: boolean;
}
export const enum WatchDirectoryFlags {
@ -3170,7 +3172,17 @@ namespace ts {
}
/* @internal */
export const enum NodeEmitFlags {
export interface EmitNode {
flags?: EmitFlags;
commentRange?: TextRange;
sourceMapRange?: TextRange;
tokenSourceMapRanges?: Map<TextRange>;
annotatedNodes?: Node[]; // Tracks Parse-tree nodes with EmitNodes for eventual cleanup.
constantValue?: number;
}
/* @internal */
export const enum EmitFlags {
EmitEmitHelpers = 1 << 0, // Any emit helpers should be written to this node.
EmitExportStar = 1 << 1, // The export * helper should be written to this node.
EmitSuperHelper = 1 << 2, // Emit the basic _super helper for async methods.
@ -3200,6 +3212,14 @@ namespace ts {
CustomPrologue = 1 << 23, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed).
}
/* @internal */
export const enum EmitContext {
SourceFile, // Emitting a SourceFile
Expression, // Emitting an Expression
IdentifierName, // Emitting an IdentifierName
Unspecified, // Emitting an otherwise unspecified node
}
/** Additional context provided to `visitEachChild` */
/* @internal */
export interface LexicalEnvironment {

View File

@ -1,4 +1,4 @@
/// <reference path="sys.ts" />
/// <reference path="sys.ts" />
/* @internal */
namespace ts {
@ -83,7 +83,7 @@ namespace ts {
return node.end - node.pos;
}
export function arrayIsEqualTo<T>(array1: T[], array2: T[], equaler?: (a: T, b: T) => boolean): boolean {
export function arrayIsEqualTo<T>(array1: ReadonlyArray<T>, array2: ReadonlyArray<T>, equaler?: (a: T, b: T) => boolean): boolean {
if (!array1 || !array2) {
return array1 === array2;
}
@ -1253,12 +1253,6 @@ namespace ts {
return false;
}
export function isExternalModuleNameRelative(moduleName: string): boolean {
// TypeScript 1.0 spec (April 2014): 11.2.1
// An external module name is "relative" if the first term is "." or "..".
return /^\.\.?($|[\\/])/.test(moduleName);
}
export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) {
const moduleState = getModuleInstanceState(node);
return moduleState === ModuleInstanceState.Instantiated ||
@ -1949,12 +1943,6 @@ namespace ts {
|| positionIsSynthesized(node.end);
}
export function positionIsSynthesized(pos: number): boolean {
// This is a fast way of testing the following conditions:
// pos === undefined || pos === null || isNaN(pos) || pos < 0;
return !(pos >= 0);
}
export function getOriginalNode(node: Node): Node {
if (node) {
while (node.original !== undefined) {
@ -2507,22 +2495,10 @@ namespace ts {
const options = host.getCompilerOptions();
const outputDir = options.declarationDir || options.outDir; // Prefer declaration folder if specified
if (options.declaration) {
const path = outputDir
? getSourceFilePathInNewDir(sourceFile, host, outputDir)
: sourceFile.fileName;
return removeFileExtension(path) + ".d.ts";
}
}
export function getEmitScriptTarget(compilerOptions: CompilerOptions) {
return compilerOptions.target || ScriptTarget.ES3;
}
export function getEmitModuleKind(compilerOptions: CompilerOptions) {
return typeof compilerOptions.module === "number" ?
compilerOptions.module :
getEmitScriptTarget(compilerOptions) === ScriptTarget.ES6 ? ModuleKind.ES6 : ModuleKind.CommonJS;
const path = outputDir
? getSourceFilePathInNewDir(sourceFile, host, outputDir)
: sourceFile.fileName;
return removeFileExtension(path) + ".d.ts";
}
export interface EmitFileNames {
@ -2575,7 +2551,8 @@ namespace ts {
* @param action The action to execute.
*/
export function forEachTransformedEmitFile(host: EmitHost, sourceFiles: SourceFile[],
action: (jsFilePath: string, sourceMapFilePath: string, declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) => void) {
action: (jsFilePath: string, sourceMapFilePath: string, declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) => void,
emitOnlyDtsFiles?: boolean) {
const options = host.getCompilerOptions();
// Emit on each source file
if (options.outFile || options.out) {
@ -2608,7 +2585,7 @@ namespace ts {
}
const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, extension);
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
const declarationFilePath = !isSourceFileJavaScript(sourceFile) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined;
const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (options.declaration || emitOnlyDtsFiles) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined;
action(jsFilePath, sourceMapFilePath, declarationFilePath, [sourceFile], /*isBundledEmit*/ false);
}
@ -2636,8 +2613,9 @@ namespace ts {
* @param targetSourceFile An optional target source file to emit.
*/
export function forEachExpectedEmitFile(host: EmitHost,
action: (emitFileNames: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean) => void,
targetSourceFile?: SourceFile) {
action: (emitFileNames: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean) => void,
targetSourceFile?: SourceFile,
emitOnlyDtsFiles?: boolean) {
const options = host.getCompilerOptions();
// Emit on each source file
if (options.outFile || options.out) {
@ -2670,12 +2648,13 @@ namespace ts {
}
}
const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, extension);
const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined;
const emitFileNames: EmitFileNames = {
jsFilePath,
sourceMapFilePath: getSourceMapFilePath(jsFilePath, options),
declarationFilePath: !isSourceFileJavaScript(sourceFile) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined
declarationFilePath
};
action(emitFileNames, [sourceFile], /*isBundledEmit*/false);
action(emitFileNames, [sourceFile], /*isBundledEmit*/false, emitOnlyDtsFiles);
}
function onBundledEmit(host: EmitHost) {
@ -2693,7 +2672,7 @@ namespace ts {
sourceMapFilePath: getSourceMapFilePath(jsFilePath, options),
declarationFilePath: options.declaration ? removeFileExtension(jsFilePath) + ".d.ts" : undefined
};
action(emitFileNames, bundledSources, /*isBundledEmit*/true);
action(emitFileNames, bundledSources, /*isBundledEmit*/true, emitOnlyDtsFiles);
}
}
}
@ -3132,19 +3111,10 @@ namespace ts {
return symbol && symbol.valueDeclaration && hasModifier(symbol.valueDeclaration, ModifierFlags.Default) ? symbol.valueDeclaration.localSymbol : undefined;
}
export function hasJavaScriptFileExtension(fileName: string) {
return forEach(supportedJavascriptExtensions, extension => fileExtensionIs(fileName, extension));
}
export function hasTypeScriptFileExtension(fileName: string) {
return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension));
}
/** Return ".ts", ".d.ts", or ".tsx", if that is the extension. */
export function tryExtractTypeScriptExtension(fileName: string): string | undefined {
return find(supportedTypescriptExtensionsForExtractExtension, extension => fileExtensionIs(fileName, extension));
}
/**
* Replace each instance of non-ascii characters by one, two, three, or four escape sequences
* representing the UTF-8 encoding of the character, and return the expanded char code list.
@ -3270,12 +3240,6 @@ namespace ts {
return result;
}
export function convertToRelativePath(absoluteOrRelativePath: string, basePath: string, getCanonicalFileName: (path: string) => string): string {
return !isRootedDiskPath(absoluteOrRelativePath)
? absoluteOrRelativePath
: getRelativePathToDirectoryOrUrl(basePath, absoluteOrRelativePath, basePath, getCanonicalFileName, /* isAbsolutePathAnUrl */ false);
}
const carriageReturnLineFeed = "\r\n";
const lineFeed = "\n";
export function getNewLineCharacter(options: CompilerOptions): string {

View File

@ -206,7 +206,7 @@ namespace FourSlash {
// Whether or not we should format on keystrokes
public enableFormatting = true;
public formatCodeOptions: ts.FormatCodeOptions;
public formatCodeSettings: ts.FormatCodeSettings;
private inputFiles = ts.createMap<string>(); // Map between inputFile's fileName and its content for easily looking up when resolving references
@ -350,26 +350,26 @@ namespace FourSlash {
Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false);
}
this.formatCodeOptions = {
BaseIndentSize: 0,
IndentSize: 4,
TabSize: 4,
NewLineCharacter: Harness.IO.newLine(),
ConvertTabsToSpaces: true,
IndentStyle: ts.IndentStyle.Smart,
InsertSpaceAfterCommaDelimiter: true,
InsertSpaceAfterSemicolonInForStatements: true,
InsertSpaceBeforeAndAfterBinaryOperators: true,
InsertSpaceAfterKeywordsInControlFlowStatements: true,
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
InsertSpaceAfterTypeAssertion: false,
PlaceOpenBraceOnNewLineForFunctions: false,
PlaceOpenBraceOnNewLineForControlBlocks: false,
this.formatCodeSettings = {
baseIndentSize: 0,
indentSize: 4,
tabSize: 4,
newLineCharacter: Harness.IO.newLine(),
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Smart,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
insertSpaceBeforeAndAfterBinaryOperators: true,
insertSpaceAfterKeywordsInControlFlowStatements: true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
insertSpaceAfterTypeAssertion: false,
placeOpenBraceOnNewLineForFunctions: false,
placeOpenBraceOnNewLineForControlBlocks: false,
};
// Open the first file by default
@ -1428,7 +1428,7 @@ namespace FourSlash {
// Handle post-keystroke formatting
if (this.enableFormatting) {
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings);
if (edits.length) {
offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
// this.checkPostEditInvariants();
@ -1466,7 +1466,7 @@ namespace FourSlash {
// Handle post-keystroke formatting
if (this.enableFormatting) {
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings);
if (edits.length) {
offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
}
@ -1514,7 +1514,7 @@ namespace FourSlash {
// Handle post-keystroke formatting
if (this.enableFormatting) {
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings);
if (edits.length) {
offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
}
@ -1538,7 +1538,7 @@ namespace FourSlash {
// Handle formatting
if (this.enableFormatting) {
const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, offset, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, offset, this.formatCodeSettings);
if (edits.length) {
offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
}
@ -1611,30 +1611,30 @@ namespace FourSlash {
return runningOffset;
}
public copyFormatOptions(): ts.FormatCodeOptions {
return ts.clone(this.formatCodeOptions);
public copyFormatOptions(): ts.FormatCodeSettings {
return ts.clone(this.formatCodeSettings);
}
public setFormatOptions(formatCodeOptions: ts.FormatCodeOptions): ts.FormatCodeOptions {
const oldFormatCodeOptions = this.formatCodeOptions;
this.formatCodeOptions = formatCodeOptions;
public setFormatOptions(formatCodeOptions: ts.FormatCodeOptions | ts.FormatCodeSettings): ts.FormatCodeSettings {
const oldFormatCodeOptions = this.formatCodeSettings;
this.formatCodeSettings = ts.toEditorSettings(formatCodeOptions);
return oldFormatCodeOptions;
}
public formatDocument() {
const edits = this.languageService.getFormattingEditsForDocument(this.activeFile.fileName, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsForDocument(this.activeFile.fileName, this.formatCodeSettings);
this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
this.fixCaretPosition();
}
public formatSelection(start: number, end: number) {
const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, end, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, end, this.formatCodeSettings);
this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
this.fixCaretPosition();
}
public formatOnType(pos: number, key: string) {
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, pos, key, this.formatCodeOptions);
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, pos, key, this.formatCodeSettings);
this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
this.fixCaretPosition();
}
@ -1835,11 +1835,9 @@ namespace FourSlash {
}
private getIndentation(fileName: string, position: number, indentStyle: ts.IndentStyle, baseIndentSize: number): number {
const formatOptions = ts.clone(this.formatCodeOptions);
formatOptions.IndentStyle = indentStyle;
formatOptions.BaseIndentSize = baseIndentSize;
const formatOptions = ts.clone(this.formatCodeSettings);
formatOptions.indentStyle = indentStyle;
formatOptions.baseIndentSize = baseIndentSize;
return this.languageService.getIndentationAtPosition(fileName, position, formatOptions);
}
@ -3485,7 +3483,7 @@ namespace FourSlashInterface {
this.state.formatDocument();
}
public copyFormatOptions(): ts.FormatCodeOptions {
public copyFormatOptions(): ts.FormatCodeSettings {
return this.state.copyFormatOptions();
}
@ -3505,7 +3503,7 @@ namespace FourSlashInterface {
public setOption(name: string, value: string): void;
public setOption(name: string, value: boolean): void;
public setOption(name: string, value: any): void {
this.state.formatCodeOptions[name] = value;
(<any>this.state.formatCodeSettings)[name] = value;
}
}

View File

@ -374,7 +374,7 @@ namespace Utils {
// call this on both nodes to ensure all propagated flags have been set (and thus can be
// compared).
assert.equal(ts.containsParseError(node1), ts.containsParseError(node2));
assert.equal(node1.flags, node2.flags, "node1.flags !== node2.flags");
assert.equal(node1.flags & ~ts.NodeFlags.ReachabilityAndEmitFlags, node2.flags & ~ts.NodeFlags.ReachabilityAndEmitFlags, "node1.flags !== node2.flags");
ts.forEachChild(node1,
child1 => {

View File

@ -218,6 +218,9 @@ namespace Harness.LanguageService {
const snapshot = this.getScriptSnapshot(path);
return snapshot.getText(0, snapshot.getLength());
}
getTypeRootsVersion() {
return 0;
}
log(s: string): void { }
@ -605,7 +608,6 @@ namespace Harness.LanguageService {
this.writeMessage(message);
}
readFile(fileName: string): string {
if (fileName.indexOf(Harness.Compiler.defaultLibFileName) >= 0) {
fileName = Harness.Compiler.defaultLibFileName;
@ -681,7 +683,11 @@ namespace Harness.LanguageService {
return true;
}
isVerbose() {
getLogFileName(): string {
return undefined;
}
hasLevel() {
return false;
}
@ -703,6 +709,14 @@ namespace Harness.LanguageService {
clearTimeout(timeoutId: any): void {
clearTimeout(timeoutId);
}
setImmediate(callback: (...args: any[]) => void, ms: number, ...args: any[]): any {
return setImmediate(callback, args);
}
clearImmediate(timeoutId: any): void {
clearImmediate(timeoutId);
}
}
export class ServerLanguageServiceAdapter implements LanguageServiceAdapter {
@ -717,8 +731,12 @@ namespace Harness.LanguageService {
// host to answer server queries about files on disk
const serverHost = new SessionServerHost(clientHost);
const server = new ts.server.Session(serverHost,
Buffer ? Buffer.byteLength : (string: string, encoding?: string) => string.length,
process.hrtime, serverHost);
{ isCancellationRequested: () => false },
/*useOneInferredProject*/ false,
/*typingsInstaller*/ undefined,
Utils.byteLength,
process.hrtime, serverHost,
/*canUseEvents*/ true);
// Fake the connection between the client and the server
serverHost.writeMessage = client.onMessage.bind(client);

View File

@ -104,6 +104,9 @@
"./unittests/convertTypingOptionsFromJson.ts",
"./unittests/tsserverProjectSystem.ts",
"./unittests/matchFiles.ts",
"./unittests/initializeTSConfig.ts"
"./unittests/initializeTSConfig.ts",
"./unittests/compileOnSave.ts",
"./unittests/typingsInstaller.ts",
"./unittests/projectErrors.ts"
]
}

View File

@ -64,26 +64,29 @@ namespace ts {
};
},
setTimeout,
clearTimeout
clearTimeout,
setImmediate,
clearImmediate
};
}
function createProject(rootFile: string, serverHost: server.ServerHost): { project: server.Project, rootScriptInfo: server.ScriptInfo } {
const logger: server.Logger = {
close() { },
isVerbose: () => false,
hasLevel: () => false,
loggingEnabled: () => false,
perftrc: (s: string) => { },
info: (s: string) => { },
startGroup: () => { },
endGroup: () => { },
msg: (s: string, type?: string) => { }
msg: (s: string, type?: string) => { },
getLogFileName: (): string => undefined
};
const projectService = new server.ProjectService(serverHost, logger);
const rootScriptInfo = projectService.openFile(rootFile, /* openedByClient */true);
const project = projectService.createInferredProject(rootScriptInfo);
project.setProjectOptions({ files: [rootScriptInfo.fileName], compilerOptions: { module: ts.ModuleKind.AMD } });
const projectService = new server.ProjectService(serverHost, logger, { isCancellationRequested: () => false }, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined);
const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */true, /*containingProject*/ undefined);
const project = projectService.createInferredProjectWithRootFileIfNecessary(rootScriptInfo);
project.setCompilerOptions({ module: ts.ModuleKind.AMD } );
return {
project,
rootScriptInfo
@ -106,10 +109,9 @@ namespace ts {
const { project, rootScriptInfo } = createProject(root.name, serverHost);
// ensure that imported file was found
let diags = project.compilerService.languageService.getSemanticDiagnostics(imported.name);
let diags = project.getLanguageService().getSemanticDiagnostics(imported.name);
assert.equal(diags.length, 1);
let content = rootScriptInfo.getText();
const originalFileExists = serverHost.fileExists;
{
@ -121,10 +123,9 @@ namespace ts {
const newContent = `import {x} from "f1"
var x: string = 1;`;
rootScriptInfo.editContent(0, content.length, newContent);
content = newContent;
rootScriptInfo.editContent(0, root.content.length, newContent);
// trigger synchronization to make sure that import will be fetched from the cache
diags = project.compilerService.languageService.getSemanticDiagnostics(imported.name);
diags = project.getLanguageService().getSemanticDiagnostics(imported.name);
// ensure file has correct number of errors after edit
assert.equal(diags.length, 1);
}
@ -139,12 +140,11 @@ namespace ts {
return originalFileExists.call(serverHost, fileName);
};
const newContent = `import {x} from "f2"`;
rootScriptInfo.editContent(0, content.length, newContent);
content = newContent;
rootScriptInfo.editContent(0, root.content.length, newContent);
try {
// trigger synchronization to make sure that LSHost will try to find 'f2' module on disk
project.compilerService.languageService.getSemanticDiagnostics(imported.name);
project.getLanguageService().getSemanticDiagnostics(imported.name);
assert.isTrue(false, `should not find file '${imported.name}'`);
}
catch (e) {
@ -165,20 +165,18 @@ namespace ts {
};
const newContent = `import {x} from "f1"`;
rootScriptInfo.editContent(0, content.length, newContent);
content = newContent;
project.compilerService.languageService.getSemanticDiagnostics(imported.name);
rootScriptInfo.editContent(0, root.content.length, newContent);
project.getLanguageService().getSemanticDiagnostics(imported.name);
assert.isTrue(fileExistsCalled);
// setting compiler options discards module resolution cache
fileExistsCalled = false;
const opts = ts.clone(project.projectOptions);
opts.compilerOptions = ts.clone(opts.compilerOptions);
opts.compilerOptions.target = ts.ScriptTarget.ES5;
project.setProjectOptions(opts);
const compilerOptions = ts.clone(project.getCompilerOptions());
compilerOptions.target = ts.ScriptTarget.ES5;
project.setCompilerOptions(compilerOptions);
project.compilerService.languageService.getSemanticDiagnostics(imported.name);
project.getLanguageService().getSemanticDiagnostics(imported.name);
assert.isTrue(fileExistsCalled);
}
});
@ -211,8 +209,8 @@ namespace ts {
};
const { project, rootScriptInfo } = createProject(root.name, serverHost);
const content = rootScriptInfo.getText();
let diags = project.compilerService.languageService.getSemanticDiagnostics(root.name);
let diags = project.getLanguageService().getSemanticDiagnostics(root.name);
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
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");
@ -220,9 +218,9 @@ namespace ts {
// assert that import will success once file appear on disk
fileMap[imported.name] = imported;
fileExistsCalledForBar = false;
rootScriptInfo.editContent(0, content.length, `import {y} from "bar"`);
rootScriptInfo.editContent(0, root.content.length, `import {y} from "bar"`);
diags = project.compilerService.languageService.getSemanticDiagnostics(root.name);
diags = project.getLanguageService().getSemanticDiagnostics(root.name);
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
assert.isTrue(diags.length === 0);
});

View File

@ -0,0 +1,494 @@
/// <reference path="../harness.ts" />
/// <reference path="./tsserverProjectSystem.ts" />
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
namespace ts.projectSystem {
function createTestTypingsInstaller(host: server.ServerHost) {
return new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host);
}
describe("CompileOnSave affected list", () => {
function sendAffectedFileRequestAndCheckResult(session: server.Session, request: server.protocol.Request, expectedFileList: { projectFileName: string, files: FileOrFolder[] }[]) {
const response: server.protocol.CompileOnSaveAffectedFileListSingleProject[] = session.executeCommand(request).response;
const actualResult = response.sort((list1, list2) => compareStrings(list1.projectFileName, list2.projectFileName));
expectedFileList = expectedFileList.sort((list1, list2) => compareStrings(list1.projectFileName, list2.projectFileName));
assert.equal(actualResult.length, expectedFileList.length, `Actual result project number is different from the expected project number`);
for (let i = 0; i < actualResult.length; i++) {
const actualResultSingleProject = actualResult[i];
const expectedResultSingleProject = expectedFileList[i];
assert.equal(actualResultSingleProject.projectFileName, expectedResultSingleProject.projectFileName, `Actual result contains different projects than the expected result`);
const actualResultSingleProjectFileNameList = actualResultSingleProject.fileNames.sort();
const expectedResultSingleProjectFileNameList = map(expectedResultSingleProject.files, f => f.path).sort();
assert.isTrue(
arrayIsEqualTo(actualResultSingleProjectFileNameList, expectedResultSingleProjectFileNameList),
`For project ${actualResultSingleProject.projectFileName}, the actual result is ${actualResultSingleProjectFileNameList}, while expected ${expectedResultSingleProjectFileNameList}`);
}
}
describe("for configured projects", () => {
let moduleFile1: FileOrFolder;
let file1Consumer1: FileOrFolder;
let file1Consumer2: FileOrFolder;
let moduleFile2: FileOrFolder;
let globalFile3: FileOrFolder;
let configFile: FileOrFolder;
let changeModuleFile1ShapeRequest1: server.protocol.Request;
let changeModuleFile1InternalRequest1: server.protocol.Request;
let changeModuleFile1ShapeRequest2: server.protocol.Request;
// A compile on save affected file request using file1
let moduleFile1FileListRequest: server.protocol.Request;
beforeEach(() => {
moduleFile1 = {
path: "/a/b/moduleFile1.ts",
content: "export function Foo() { };"
};
file1Consumer1 = {
path: "/a/b/file1Consumer1.ts",
content: `import {Foo} from "./moduleFile1"; export var y = 10;`
};
file1Consumer2 = {
path: "/a/b/file1Consumer2.ts",
content: `import {Foo} from "./moduleFile1"; let z = 10;`
};
moduleFile2 = {
path: "/a/b/moduleFile2.ts",
content: `export var Foo4 = 10;`
};
globalFile3 = {
path: "/a/b/globalFile3.ts",
content: `interface GlobalFoo { age: number }`
};
configFile = {
path: "/a/b/tsconfig.json",
content: `{
"compileOnSave": true
}`
};
// Change the content of file1 to `export var T: number;export function Foo() { };`
changeModuleFile1ShapeRequest1 = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: `export var T: number;`
});
// Change the content of file1 to `export var T: number;export function Foo() { };`
changeModuleFile1InternalRequest1 = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: `var T1: number;`
});
// Change the content of file1 to `export var T: number;export function Foo() { };`
changeModuleFile1ShapeRequest2 = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: `export var T2: number;`
});
moduleFile1FileListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: moduleFile1.path, projectFileName: configFile.path });
});
it("should contains only itself if a module file's shape didn't change, and all files referencing it if its shape changed", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1, file1Consumer1], session);
// Send an initial compileOnSave request
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
session.executeCommand(changeModuleFile1ShapeRequest1);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
// Change the content of file1 to `export var T: number;export function Foo() { console.log('hi'); };`
const changeFile1InternalRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 46,
endLine: 1,
endOffset: 46,
insertString: `console.log('hi');`
});
session.executeCommand(changeFile1InternalRequest);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1] }]);
});
it("should be up-to-date with the reference map changes", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1, file1Consumer1], session);
// Send an initial compileOnSave request
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
// Change file2 content to `let y = Foo();`
const removeFile1Consumer1ImportRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: file1Consumer1.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 28,
insertString: ""
});
session.executeCommand(removeFile1Consumer1ImportRequest);
session.executeCommand(changeModuleFile1ShapeRequest1);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer2] }]);
// Add the import statements back to file2
const addFile2ImportRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: file1Consumer1.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: `import {Foo} from "./moduleFile1";`
});
session.executeCommand(addFile2ImportRequest);
// Change the content of file1 to `export var T2: string;export var T: number;export function Foo() { };`
const changeModuleFile1ShapeRequest2 = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: `export var T2: string;`
});
session.executeCommand(changeModuleFile1ShapeRequest2);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
});
it("should be up-to-date with changes made in non-open files", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1], session);
// Send an initial compileOnSave request
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
file1Consumer1.content = `let y = 10;`;
host.reloadFS([moduleFile1, file1Consumer1, file1Consumer2, configFile, libFile]);
host.triggerFileWatcherCallback(file1Consumer1.path, /*removed*/ false);
session.executeCommand(changeModuleFile1ShapeRequest1);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer2] }]);
});
it("should be up-to-date with deleted files", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
session.executeCommand(changeModuleFile1ShapeRequest1);
// Delete file1Consumer2
host.reloadFS([moduleFile1, file1Consumer1, configFile, libFile]);
host.triggerFileWatcherCallback(file1Consumer2.path, /*removed*/ true);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1] }]);
});
it("should be up-to-date with newly created files", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
const file1Consumer3: FileOrFolder = {
path: "/a/b/file1Consumer3.ts",
content: `import {Foo} from "./moduleFile1"; let y = Foo();`
};
host.reloadFS([moduleFile1, file1Consumer1, file1Consumer2, file1Consumer3, globalFile3, configFile, libFile]);
host.triggerDirectoryWatcherCallback(ts.getDirectoryPath(file1Consumer3.path), file1Consumer3.path);
host.runQueuedTimeoutCallbacks();
session.executeCommand(changeModuleFile1ShapeRequest1);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2, file1Consumer3] }]);
});
it("should detect changes in non-root files", () => {
moduleFile1 = {
path: "/a/b/moduleFile1.ts",
content: "export function Foo() { };"
};
file1Consumer1 = {
path: "/a/b/file1Consumer1.ts",
content: `import {Foo} from "./moduleFile1"; let y = Foo();`
};
configFile = {
path: "/a/b/tsconfig.json",
content: `{
"compileOnSave": true,
"files": ["${file1Consumer1.path}"]
}`
};
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1, file1Consumer1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1] }]);
// change file1 shape now, and verify both files are affected
session.executeCommand(changeModuleFile1ShapeRequest1);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1] }]);
// change file1 internal, and verify only file1 is affected
session.executeCommand(changeModuleFile1InternalRequest1);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1] }]);
});
it("should return all files if a global file changed shape", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([globalFile3], session);
const changeGlobalFile3ShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: globalFile3.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: `var T2: string;`
});
// check after file1 shape changes
session.executeCommand(changeGlobalFile3ShapeRequest);
const globalFile3FileListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: globalFile3.path });
sendAffectedFileRequestAndCheckResult(session, globalFile3FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2] }]);
});
it("should return empty array if CompileOnSave is not enabled", () => {
configFile = {
path: "/a/b/tsconfig.json",
content: `{}`
};
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, []);
});
it("should always return the file itself if '--isolatedModules' is specified", () => {
configFile = {
path: "/a/b/tsconfig.json",
content: `{
"compileOnSave": true,
"compilerOptions": {
"isolatedModules": true
}
}`
};
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1], session);
const file1ChangeShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 27,
endLine: 1,
endOffset: 27,
insertString: `Point,`
});
session.executeCommand(file1ChangeShapeRequest);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1] }]);
});
it("should always return the file itself if '--out' or '--outFile' is specified", () => {
configFile = {
path: "/a/b/tsconfig.json",
content: `{
"compileOnSave": true,
"compilerOptions": {
"module": "system",
"outFile": "/a/b/out.js"
}
}`
};
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1], session);
const file1ChangeShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 27,
endLine: 1,
endOffset: 27,
insertString: `Point,`
});
session.executeCommand(file1ChangeShapeRequest);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1] }]);
});
it("should return cascaded affected file list", () => {
const file1Consumer1Consumer1: FileOrFolder = {
path: "/a/b/file1Consumer1Consumer1.ts",
content: `import {y} from "./file1Consumer1";`
};
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer1Consumer1, globalFile3, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([moduleFile1, file1Consumer1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer1Consumer1] }]);
const changeFile1Consumer1ShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
file: file1Consumer1.path,
line: 2,
offset: 1,
endLine: 2,
endOffset: 1,
insertString: `export var T: number;`
});
session.executeCommand(changeModuleFile1ShapeRequest1);
session.executeCommand(changeFile1Consumer1ShapeRequest);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer1Consumer1] }]);
});
it("should work fine for files with circular references", () => {
const file1: FileOrFolder = {
path: "/a/b/file1.ts",
content: `
/// <reference path="./file2.ts" />
export var t1 = 10;`
};
const file2: FileOrFolder = {
path: "/a/b/file2.ts",
content: `
/// <reference path="./file1.ts" />
export var t2 = 10;`
};
const host = createServerHost([file1, file2, configFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([file1, file2], session);
const file1AffectedListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: file1.path });
sendAffectedFileRequestAndCheckResult(session, file1AffectedListRequest, [{ projectFileName: configFile.path, files: [file1, file2] }]);
});
it("should return results for all projects if not specifying projectFileName", () => {
const file1: FileOrFolder = { path: "/a/b/file1.ts", content: "export var t = 10;" };
const file2: FileOrFolder = { path: "/a/b/file2.ts", content: `import {t} from "./file1"; var t2 = 11;` };
const file3: FileOrFolder = { path: "/a/c/file2.ts", content: `import {t} from "../b/file1"; var t3 = 11;` };
const configFile1: FileOrFolder = { path: "/a/b/tsconfig.json", content: `{ "compileOnSave": true }` };
const configFile2: FileOrFolder = { path: "/a/c/tsconfig.json", content: `{ "compileOnSave": true }` };
const host = createServerHost([file1, file2, file3, configFile1, configFile2]);
const session = createSession(host);
openFilesForSession([file1, file2, file3], session);
const file1AffectedListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: file1.path });
sendAffectedFileRequestAndCheckResult(session, file1AffectedListRequest, [
{ projectFileName: configFile1.path, files: [file1, file2] },
{ projectFileName: configFile2.path, files: [file1, file3] }
]);
});
it("should detect removed code file", () => {
const referenceFile1: FileOrFolder = {
path: "/a/b/referenceFile1.ts",
content: `
/// <reference path="./moduleFile1.ts" />
export var x = Foo();`
};
const host = createServerHost([moduleFile1, referenceFile1, configFile]);
const session = createSession(host);
openFilesForSession([referenceFile1], session);
host.reloadFS([referenceFile1, configFile]);
host.triggerFileWatcherCallback(moduleFile1.path, /*removed*/ true);
const request = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: referenceFile1.path });
sendAffectedFileRequestAndCheckResult(session, request, [
{ projectFileName: configFile.path, files: [referenceFile1] }
]);
const requestForMissingFile = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: moduleFile1.path });
sendAffectedFileRequestAndCheckResult(session, requestForMissingFile, []);
});
it("should detect non-existing code file", () => {
const referenceFile1: FileOrFolder = {
path: "/a/b/referenceFile1.ts",
content: `
/// <reference path="./moduleFile2.ts" />
export var x = Foo();`
};
const host = createServerHost([referenceFile1, configFile]);
const session = createSession(host);
openFilesForSession([referenceFile1], session);
const request = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: referenceFile1.path });
sendAffectedFileRequestAndCheckResult(session, request, [
{ projectFileName: configFile.path, files: [referenceFile1] }
]);
});
});
});
describe("EmitFile test", () => {
it("should emit specified file", () => {
const file1 = {
path: "/a/b/f1.ts",
content: `export function Foo() { return 10; }`
};
const file2 = {
path: "/a/b/f2.ts",
content: `import {Foo} from "./f1"; let y = Foo();`
};
const configFile = {
path: "/a/b/tsconfig.json",
content: `{}`
};
const host = createServerHost([file1, file2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
openFilesForSession([file1, file2], session);
const compileFileRequest = makeSessionRequest<server.protocol.CompileOnSaveEmitFileRequestArgs>(server.CommandNames.CompileOnSaveEmitFile, { file: file1.path, projectFileName: configFile.path });
session.executeCommand(compileFileRequest);
const expectedEmittedFileName = "/a/b/f1.js";
assert.isTrue(host.fileExists(expectedEmittedFileName));
assert.equal(host.readFile(expectedEmittedFileName), `"use strict";\r\nfunction Foo() { return 10; }\r\nexports.Foo = Foo;\r\n`);
});
});
}

View File

@ -404,6 +404,7 @@ namespace ts {
compilerOptions: <CompilerOptions>{
allowJs: true,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@ -431,6 +432,7 @@ namespace ts {
compilerOptions: <CompilerOptions>{
allowJs: false,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@ -453,7 +455,8 @@ namespace ts {
compilerOptions:
{
allowJs: true,
maxNodeModuleJsDepth: 2
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true
},
errors: [{
file: undefined,
@ -473,7 +476,8 @@ namespace ts {
compilerOptions:
{
allowJs: true,
maxNodeModuleJsDepth: 2
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true
},
errors: <Diagnostic[]>[]
}

View File

@ -0,0 +1,186 @@
/// <reference path="../harness.ts" />
/// <reference path="./tsserverProjectSystem.ts" />
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
namespace ts.projectSystem {
describe("Project errors", () => {
function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: string[]) {
assert.isTrue(projectFiles !== undefined, "missing project files");
const errors = projectFiles.projectErrors;
assert.equal(errors ? errors.length : 0, expectedErrors.length, `expected ${expectedErrors.length} error in the list`);
if (expectedErrors.length) {
for (let i = 0; i < errors.length; i++) {
const actualMessage = flattenDiagnosticMessageText(errors[i].messageText, "\n");
const expectedMessage = expectedErrors[i];
assert.isTrue(actualMessage.indexOf(expectedMessage) === 0, `error message does not match, expected ${actualMessage} to start with ${expectedMessage}`);
}
}
}
it("external project - diagnostics for missing files", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const file2 = {
path: "/a/b/lib.ts",
content: ""
};
// only file1 exists - expect error
const host = createServerHost([file1]);
const projectService = createProjectService(host);
const projectFileName = "/a/b/test.csproj";
{
projectService.openExternalProject({
projectFileName,
options: {},
rootFiles: toExternalFiles([file1.path, file2.path])
});
projectService.checkNumberOfProjects({ externalProjects: 1 });
const knownProjects = projectService.synchronizeProjectList([]);
checkProjectErrors(knownProjects[0], ["File '/a/b/lib.ts' not found."]);
}
// only file2 exists - expect error
host.reloadFS([file2]);
{
projectService.openExternalProject({
projectFileName,
options: {},
rootFiles: toExternalFiles([file1.path, file2.path])
});
projectService.checkNumberOfProjects({ externalProjects: 1 });
const knownProjects = projectService.synchronizeProjectList([]);
checkProjectErrors(knownProjects[0], ["File '/a/b/app.ts' not found."]);
}
// both files exist - expect no errors
host.reloadFS([file1, file2]);
{
projectService.openExternalProject({
projectFileName,
options: {},
rootFiles: toExternalFiles([file1.path, file2.path])
});
projectService.checkNumberOfProjects({ externalProjects: 1 });
const knownProjects = projectService.synchronizeProjectList([]);
checkProjectErrors(knownProjects[0], []);
}
});
it("configured projects - diagnostics for missing files", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const file2 = {
path: "/a/b/lib.ts",
content: ""
};
const config = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
};
const host = createServerHost([file1, config]);
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
projectService.checkNumberOfProjects({ configuredProjects: 1 });
checkProjectErrors(projectService.synchronizeProjectList([])[0], ["File '/a/b/lib.ts' not found."]);
host.reloadFS([file1, file2, config]);
projectService.openClientFile(file1.path);
projectService.checkNumberOfProjects({ configuredProjects: 1 });
checkProjectErrors(projectService.synchronizeProjectList([])[0], []);
});
it("configured projects - diagnostics for corrupted config 1", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const file2 = {
path: "/a/b/lib.ts",
content: ""
};
const correctConfig = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
};
const corruptedConfig = {
path: correctConfig.path,
content: correctConfig.content.substr(1)
};
const host = createServerHost([file1, file2, corruptedConfig]);
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
{
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
assert.isTrue(configuredProject !== undefined, "should find configured project");
checkProjectErrors(configuredProject, [
"')' expected.",
"Declaration or statement expected.",
"Declaration or statement expected.",
"Failed to parse file '/a/b/tsconfig.json'"
]);
}
// fix config and trigger watcher
host.reloadFS([file1, file2, correctConfig]);
host.triggerFileWatcherCallback(correctConfig.path, /*false*/);
{
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
assert.isTrue(configuredProject !== undefined, "should find configured project");
checkProjectErrors(configuredProject, []);
}
});
it("configured projects - diagnostics for corrupted config 2", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const file2 = {
path: "/a/b/lib.ts",
content: ""
};
const correctConfig = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
};
const corruptedConfig = {
path: correctConfig.path,
content: correctConfig.content.substr(1)
};
const host = createServerHost([file1, file2, correctConfig]);
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
{
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
assert.isTrue(configuredProject !== undefined, "should find configured project");
checkProjectErrors(configuredProject, []);
}
// break config and trigger watcher
host.reloadFS([file1, file2, corruptedConfig]);
host.triggerFileWatcherCallback(corruptedConfig.path, /*false*/);
{
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
assert.isTrue(configuredProject !== undefined, "should find configured project");
checkProjectErrors(configuredProject, [
"')' expected.",
"Declaration or statement expected.",
"Declaration or statement expected.",
"Failed to parse file '/a/b/tsconfig.json'"
]);
}
});
});
}

View File

@ -22,17 +22,21 @@ namespace ts.server {
readDirectory(): string[] { return []; },
exit(): void { },
setTimeout(callback, ms, ...args) { return 0; },
clearTimeout(timeoutId) { }
clearTimeout(timeoutId) { },
setImmediate: () => 0,
clearImmediate() {}
};
const nullCancellationToken: HostCancellationToken = { isCancellationRequested: () => false };
const mockLogger: Logger = {
close(): void {},
isVerbose(): boolean { return false; },
hasLevel(): boolean { return false; },
loggingEnabled(): boolean { return false; },
perftrc(s: string): void {},
info(s: string): void {},
startGroup(): void {},
endGroup(): void {},
msg(s: string, type?: string): void {},
getLogFileName: (): string => undefined
};
describe("the Session class", () => {
@ -40,7 +44,7 @@ namespace ts.server {
let lastSent: protocol.Message;
beforeEach(() => {
session = new Session(mockHost, Utils.byteLength, process.hrtime, mockLogger);
session = new Session(mockHost, nullCancellationToken, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, mockLogger, /*canUseEvents*/ true);
session.send = (msg: protocol.Message) => {
lastSent = msg;
};
@ -265,7 +269,7 @@ namespace ts.server {
lastSent: protocol.Message;
customHandler = "testhandler";
constructor() {
super(mockHost, Utils.byteLength, process.hrtime, mockLogger);
super(mockHost, nullCancellationToken, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, mockLogger, /*canUseEvents*/ true);
this.addProtocolHandler(this.customHandler, () => {
return { response: undefined, responseRequired: true };
});
@ -323,7 +327,7 @@ namespace ts.server {
class InProcSession extends Session {
private queue: protocol.Request[] = [];
constructor(private client: InProcClient) {
super(mockHost, Utils.byteLength, process.hrtime, mockLogger);
super(mockHost, nullCancellationToken, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, mockLogger, /*canUseEvents*/ true);
this.addProtocolHandler("echo", (req: protocol.Request) => ({
response: req.arguments,
responseRequired: true

View File

@ -191,7 +191,7 @@ namespace ts {
}
"files": ["file1.ts"]
}`;
const { configJsonObject, diagnostics } = parseAndReEmitConfigJSONFile(content);
const { configJsonObject, diagnostics } = sanitizeConfigFile("config.json", content);
const expectedResult = {
compilerOptions: {
allowJs: true,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,723 @@
/// <reference path="../harness.ts" />
/// <reference path="./tsserverProjectSystem.ts" />
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
namespace ts.projectSystem {
import TI = server.typingsInstaller;
interface InstallerParams {
globalTypingsCacheLocation?: string;
throttleLimit?: number;
}
class Installer extends TestTypingsInstaller {
constructor(host: server.ServerHost, p?: InstallerParams) {
super(
(p && p.globalTypingsCacheLocation) || "/a/data",
(p && p.throttleLimit) || 5,
host);
}
installAll(expectedView: typeof TI.NpmViewRequest[], expectedInstall: typeof TI.NpmInstallRequest[]) {
this.checkPendingCommands(expectedView);
this.executePendingCommands();
this.checkPendingCommands(expectedInstall);
this.executePendingCommands();
}
}
describe("typingsInstaller", () => {
function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[], typingFiles: FileOrFolder[], requestKind: TI.RequestKind, cb: TI.RequestCompletedAction): void {
switch (requestKind) {
case TI.NpmInstallRequest:
self.addPostExecAction(requestKind, installedTypings, (err, stdout, stderr) => {
for (const file of typingFiles) {
host.createFileOrFolder(file, /*createParentDirectory*/ true);
}
cb(err, stdout, stderr);
});
break;
case TI.NpmViewRequest:
self.addPostExecAction(requestKind, installedTypings, cb);
break;
default:
assert.isTrue(false, `unexpected request kind ${requestKind}`);
break;
}
}
it("configured projects (typings installed) 1", () => {
const file1 = {
path: "/a/b/app.js",
content: ""
};
const tsconfig = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({
compilerOptions: {
allowJs: true
},
typingOptions: {
enableAutoDiscovery: true
}
})
};
const packageJson = {
path: "/a/b/package.json",
content: JSON.stringify({
name: "test",
dependencies: {
jquery: "^3.1.0"
}
})
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const $: { x: number }"
};
const host = createServerHost([file1, tsconfig, packageJson]);
const installer = new (class extends Installer {
constructor() {
super(host);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
const installedTypings = ["@types/jquery"];
const typingFiles = [jquery];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
projectService.openClientFile(file1.path);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const p = projectService.configuredProjects[0];
checkProjectActualFiles(p, [file1.path]);
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
checkProjectActualFiles(p, [file1.path, jquery.path]);
});
it("inferred project (typings installed)", () => {
const file1 = {
path: "/a/b/app.js",
content: ""
};
const packageJson = {
path: "/a/b/package.json",
content: JSON.stringify({
name: "test",
dependencies: {
jquery: "^3.1.0"
}
})
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const $: { x: number }"
};
const host = createServerHost([file1, packageJson]);
const installer = new (class extends Installer {
constructor() {
super(host);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
const installedTypings = ["@types/jquery"];
const typingFiles = [jquery];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
projectService.openClientFile(file1.path);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
const p = projectService.inferredProjects[0];
checkProjectActualFiles(p, [file1.path]);
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
checkProjectActualFiles(p, [file1.path, jquery.path]);
});
it("external project - no typing options, no .d.ts/js files", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const host = createServerHost([file1]);
const installer = new (class extends Installer {
constructor() {
super(host);
}
enqueueInstallTypingsRequest() {
assert(false, "auto discovery should not be enabled");
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: {},
rootFiles: [toExternalFile(file1.path)]
});
installer.checkPendingCommands([]);
// by default auto discovery will kick in if project contain only .js/.d.ts files
// in this case project contain only ts files - no auto discovery
projectService.checkNumberOfProjects({ externalProjects: 1 });
});
it("external project - no autoDiscovery in typing options, no .d.ts/js files", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const host = createServerHost([file1]);
const installer = new (class extends Installer {
constructor() {
super(host);
}
enqueueInstallTypingsRequest() {
assert(false, "auto discovery should not be enabled");
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: {},
rootFiles: [toExternalFile(file1.path)],
typingOptions: { include: ["jquery"] }
});
installer.checkPendingCommands([]);
// by default auto discovery will kick in if project contain only .js/.d.ts files
// in this case project contain only ts files - no auto discovery even if typing options is set
projectService.checkNumberOfProjects({ externalProjects: 1 });
});
it("external project - autoDiscovery = true, no .d.ts/js files", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const $: { x: number }"
};
const host = createServerHost([file1]);
let enqueueIsCalled = false;
const installer = new (class extends Installer {
constructor() {
super(host);
}
enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions) {
enqueueIsCalled = true;
super.enqueueInstallTypingsRequest(project, typingOptions);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings = ["@types/jquery"];
const typingFiles = [jquery];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: {},
rootFiles: [toExternalFile(file1.path)],
typingOptions: { enableAutoDiscovery: true, include: ["node"] }
});
assert.isTrue(enqueueIsCalled, "expected enqueueIsCalled to be true");
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
// autoDiscovery is set in typing options - use it even if project contains only .ts files
projectService.checkNumberOfProjects({ externalProjects: 1 });
});
it("external project - no typing options, with only js, jsx, d.ts files", () => {
// Tests:
// 1. react typings are installed for .jsx
// 2. loose files names are matched against safe list for typings if
// this is a JS project (only js, jsx, d.ts files are present)
const file1 = {
path: "/a/b/lodash.js",
content: ""
};
const file2 = {
path: "/a/b/file2.jsx",
content: ""
};
const file3 = {
path: "/a/b/file3.d.ts",
content: ""
};
const react = {
path: "/a/data/node_modules/@types/react/index.d.ts",
content: "declare const react: { x: number }"
};
const lodash = {
path: "/a/data/node_modules/@types/lodash/index.d.ts",
content: "declare const lodash: { x: number }"
};
const host = createServerHost([file1, file2, file3]);
const installer = new (class extends Installer {
constructor() {
super(host);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings = ["@types/lodash", "@types/react"];
const typingFiles = [lodash, react];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
rootFiles: [toExternalFile(file1.path), toExternalFile(file2.path), toExternalFile(file3.path)],
typingOptions: {}
});
const p = projectService.externalProjects[0];
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path, file3.path]);
installer.installAll([TI.NpmViewRequest, TI.NpmViewRequest], [TI.NpmInstallRequest], );
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path, file3.path, lodash.path, react.path]);
});
it("external project - no typing options, with js & ts files", () => {
// Tests:
// 1. No typings are included for JS projects when the project contains ts files
const file1 = {
path: "/a/b/jquery.js",
content: ""
};
const file2 = {
path: "/a/b/file2.ts",
content: ""
};
const host = createServerHost([file1, file2]);
let enqueueIsCalled = false;
const installer = new (class extends Installer {
constructor() {
super(host);
}
enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions) {
enqueueIsCalled = true;
super.enqueueInstallTypingsRequest(project, typingOptions);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings: string[] = [];
const typingFiles: FileOrFolder[] = [];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
rootFiles: [toExternalFile(file1.path), toExternalFile(file2.path)],
typingOptions: {}
});
const p = projectService.externalProjects[0];
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path]);
installer.checkPendingCommands([]);
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path]);
});
it("external project - with typing options, with only js, d.ts files", () => {
// Tests:
// 1. Safelist matching, typing options includes/excludes and package.json typings are all acquired
// 2. Types for safelist matches are not included when they also appear in the typing option exclude list
// 3. Multiple includes and excludes are respected in typing options
const file1 = {
path: "/a/b/lodash.js",
content: ""
};
const file2 = {
path: "/a/b/commander.js",
content: ""
};
const file3 = {
path: "/a/b/file3.d.ts",
content: ""
};
const packageJson = {
path: "/a/b/package.json",
content: JSON.stringify({
name: "test",
dependencies: {
express: "^3.1.0"
}
})
};
const commander = {
path: "/a/data/node_modules/@types/commander/index.d.ts",
content: "declare const commander: { x: number }"
};
const express = {
path: "/a/data/node_modules/@types/express/index.d.ts",
content: "declare const express: { x: number }"
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const jquery: { x: number }"
};
const moment = {
path: "/a/data/node_modules/@types/moment/index.d.ts",
content: "declare const moment: { x: number }"
};
const host = createServerHost([file1, file2, file3, packageJson]);
const installer = new (class extends Installer {
constructor() {
super(host);
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment"];
const typingFiles = [commander, express, jquery, moment];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
rootFiles: [toExternalFile(file1.path), toExternalFile(file2.path), toExternalFile(file3.path)],
typingOptions: { include: ["jquery", "moment"], exclude: ["lodash"] }
});
const p = projectService.externalProjects[0];
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path, file3.path]);
installer.installAll(
[TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest],
[TI.NpmInstallRequest]
);
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectActualFiles(p, [file1.path, file2.path, file3.path, commander.path, express.path, jquery.path, moment.path]);
});
it("Throttle - delayed typings to install", () => {
const lodashJs = {
path: "/a/b/lodash.js",
content: ""
};
const commanderJs = {
path: "/a/b/commander.js",
content: ""
};
const file3 = {
path: "/a/b/file3.d.ts",
content: ""
};
const packageJson = {
path: "/a/b/package.json",
content: JSON.stringify({
name: "test",
dependencies: {
express: "^3.1.0"
}
})
};
const commander = {
path: "/a/data/node_modules/@types/commander/index.d.ts",
content: "declare const commander: { x: number }"
};
const express = {
path: "/a/data/node_modules/@types/express/index.d.ts",
content: "declare const express: { x: number }"
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const jquery: { x: number }"
};
const moment = {
path: "/a/data/node_modules/@types/moment/index.d.ts",
content: "declare const moment: { x: number }"
};
const lodash = {
path: "/a/data/node_modules/@types/lodash/index.d.ts",
content: "declare const lodash: { x: number }"
};
const typingFiles = [commander, express, jquery, moment, lodash];
const host = createServerHost([lodashJs, commanderJs, file3, packageJson]);
const installer = new (class extends Installer {
constructor() {
super(host, { throttleLimit: 3 });
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment", "@types/lodash"];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectFileName = "/a/app/test.csproj";
const projectService = createProjectService(host, { typingsInstaller: installer });
projectService.openExternalProject({
projectFileName,
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
rootFiles: [toExternalFile(lodashJs.path), toExternalFile(commanderJs.path), toExternalFile(file3.path)],
typingOptions: { include: ["jquery", "moment"] }
});
const p = projectService.externalProjects[0];
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(p, [lodashJs.path, commanderJs.path, file3.path]);
// expected 3 view requests in the queue
installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest]);
assert.equal(installer.pendingRunRequests.length, 2, "expected 2 pending requests");
// push view requests
installer.executePendingCommands();
// expected 2 remaining view requests in the queue
installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest]);
// push view requests
installer.executePendingCommands();
// expected one install request
installer.checkPendingCommands([TI.NpmInstallRequest]);
installer.executePendingCommands();
// expected all typings file to exist
for (const f of typingFiles) {
assert.isTrue(host.fileExists(f.path), `expected file ${f.path} to exist`);
}
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectActualFiles(p, [lodashJs.path, commanderJs.path, file3.path, commander.path, express.path, jquery.path, moment.path, lodash.path]);
});
it("Throttle - delayed run install requests", () => {
const lodashJs = {
path: "/a/b/lodash.js",
content: ""
};
const commanderJs = {
path: "/a/b/commander.js",
content: ""
};
const file3 = {
path: "/a/b/file3.d.ts",
content: ""
};
const commander = {
path: "/a/data/node_modules/@types/commander/index.d.ts",
content: "declare const commander: { x: number }",
typings: "@types/commander"
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const jquery: { x: number }",
typings: "@types/jquery"
};
const lodash = {
path: "/a/data/node_modules/@types/lodash/index.d.ts",
content: "declare const lodash: { x: number }",
typings: "@types/lodash"
};
const cordova = {
path: "/a/data/node_modules/@types/cordova/index.d.ts",
content: "declare const cordova: { x: number }",
typings: "@types/cordova"
};
const grunt = {
path: "/a/data/node_modules/@types/grunt/index.d.ts",
content: "declare const grunt: { x: number }",
typings: "@types/grunt"
};
const gulp = {
path: "/a/data/node_modules/@types/gulp/index.d.ts",
content: "declare const gulp: { x: number }",
typings: "@types/gulp"
};
const host = createServerHost([lodashJs, commanderJs, file3]);
const installer = new (class extends Installer {
constructor() {
super(host, { throttleLimit: 3 });
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
if (requestKind === TI.NpmInstallRequest) {
let typingFiles: (FileOrFolder & { typings: string}) [] = [];
if (command.indexOf("commander") >= 0) {
typingFiles = [commander, jquery, lodash, cordova];
}
else {
typingFiles = [grunt, gulp];
}
executeCommand(this, host, typingFiles.map(f => f.typings), typingFiles, requestKind, cb);
}
else {
executeCommand(this, host, [], [], requestKind, cb);
}
}
})();
// Create project #1 with 4 typings
const projectService = createProjectService(host, { typingsInstaller: installer });
const projectFileName1 = "/a/app/test1.csproj";
projectService.openExternalProject({
projectFileName: projectFileName1,
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
rootFiles: [toExternalFile(lodashJs.path), toExternalFile(commanderJs.path), toExternalFile(file3.path)],
typingOptions: { include: ["jquery", "cordova" ] }
});
installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest]);
assert.equal(installer.pendingRunRequests.length, 1, "expect one throttled request");
// Create project #2 with 2 typings
const projectFileName2 = "/a/app/test2.csproj";
projectService.openExternalProject({
projectFileName: projectFileName2,
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
rootFiles: [toExternalFile(file3.path)],
typingOptions: { include: ["grunt", "gulp"] }
});
assert.equal(installer.pendingRunRequests.length, 3, "expect three throttled request");
const p1 = projectService.externalProjects[0];
const p2 = projectService.externalProjects[1];
projectService.checkNumberOfProjects({ externalProjects: 2 });
checkProjectActualFiles(p1, [lodashJs.path, commanderJs.path, file3.path]);
checkProjectActualFiles(p2, [file3.path]);
installer.executePendingCommands();
// expected one view request from the first project and two - from the second one
installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest]);
assert.equal(installer.pendingRunRequests.length, 0, "expected no throttled requests");
installer.executePendingCommands();
// should be two install requests from both projects
installer.checkPendingCommands([TI.NpmInstallRequest, TI.NpmInstallRequest]);
installer.executePendingCommands();
checkProjectActualFiles(p1, [lodashJs.path, commanderJs.path, file3.path, commander.path, jquery.path, lodash.path, cordova.path]);
checkProjectActualFiles(p2, [file3.path, grunt.path, gulp.path ]);
});
it("configured projects discover from node_modules", () => {
const app = {
path: "/app.js",
content: ""
};
const jsconfig = {
path: "/jsconfig.json",
content: JSON.stringify({})
};
const jquery = {
path: "/node_modules/jquery/index.js",
content: ""
};
const jqueryPackage = {
path: "/node_modules/jquery/package.json",
content: JSON.stringify({ name: "jquery" })
};
const jqueryDTS = {
path: "/tmp/node_modules/@types/jquery/index.d.ts",
content: ""
};
const host = createServerHost([app, jsconfig, jquery, jqueryPackage]);
const installer = new (class extends Installer {
constructor() {
super(host, { globalTypingsCacheLocation: "/tmp" });
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
const installedTypings = ["@types/jquery"];
const typingFiles = [jqueryDTS];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
projectService.openClientFile(app.path);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const p = projectService.configuredProjects[0];
checkProjectActualFiles(p, [app.path]);
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
checkProjectActualFiles(p, [app.path, jqueryDTS.path]);
});
it("configured projects discover from bower.josn", () => {
const app = {
path: "/app.js",
content: ""
};
const jsconfig = {
path: "/jsconfig.json",
content: JSON.stringify({})
};
const bowerJson = {
path: "/bower.json",
content: JSON.stringify({
"dependencies": {
"jquery": "^3.1.0"
}
})
};
const jqueryDTS = {
path: "/tmp/node_modules/@types/jquery/index.d.ts",
content: ""
};
const host = createServerHost([app, jsconfig, bowerJson]);
const installer = new (class extends Installer {
constructor() {
super(host, { globalTypingsCacheLocation: "/tmp" });
}
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
const installedTypings = ["@types/jquery"];
const typingFiles = [jqueryDTS];
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
}
})();
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
projectService.openClientFile(app.path);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const p = projectService.configuredProjects[0];
checkProjectActualFiles(p, [app.path]);
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
checkProjectActualFiles(p, [app.path, jqueryDTS.path]);
});
});
}

View File

@ -7536,11 +7536,12 @@ declare var HashChangeEvent: {
interface History {
readonly length: number;
readonly state: any;
back(distance?: any): void;
forward(distance?: any): void;
go(delta?: any): void;
pushState(statedata: any, title?: string, url?: string): void;
replaceState(statedata: any, title?: string, url?: string): void;
scrollRestoration: ScrollRestoration;
back(): void;
forward(): void;
go(delta?: number): void;
pushState(data: any, title: string, url?: string | null): void;
replaceState(data: any, title: string, url?: string | null): void;
}
declare var History: {
@ -13112,7 +13113,6 @@ interface Window extends EventTarget, WindowTimers, WindowSessionStorage, Window
addEventListener(type: "waiting", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "wheel", listener: (this: this, ev: WheelEvent) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
[index: number]: Window;
}
declare var Window: {
@ -13143,7 +13143,7 @@ declare var XMLDocument: {
}
interface XMLHttpRequest extends EventTarget, XMLHttpRequestEventTarget {
onreadystatechange: (this: this, ev: ProgressEvent) => any;
onreadystatechange: (this: this, ev: Event) => any;
readonly readyState: number;
readonly response: any;
readonly responseText: string;
@ -13170,13 +13170,13 @@ interface XMLHttpRequest extends EventTarget, XMLHttpRequestEventTarget {
readonly LOADING: number;
readonly OPENED: number;
readonly UNSENT: number;
addEventListener(type: "abort", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
addEventListener(type: "load", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "abort", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "error", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "load", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "loadend", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "loadstart", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "loadstart", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "progress", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "readystatechange", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "readystatechange", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "timeout", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
@ -13517,183 +13517,183 @@ interface NavigatorUserMedia {
}
interface NodeSelector {
querySelector(selectors: "a"): HTMLAnchorElement;
querySelector(selectors: "abbr"): HTMLElement;
querySelector(selectors: "acronym"): HTMLElement;
querySelector(selectors: "address"): HTMLElement;
querySelector(selectors: "applet"): HTMLAppletElement;
querySelector(selectors: "area"): HTMLAreaElement;
querySelector(selectors: "article"): HTMLElement;
querySelector(selectors: "aside"): HTMLElement;
querySelector(selectors: "audio"): HTMLAudioElement;
querySelector(selectors: "b"): HTMLElement;
querySelector(selectors: "base"): HTMLBaseElement;
querySelector(selectors: "basefont"): HTMLBaseFontElement;
querySelector(selectors: "bdo"): HTMLElement;
querySelector(selectors: "big"): HTMLElement;
querySelector(selectors: "blockquote"): HTMLQuoteElement;
querySelector(selectors: "body"): HTMLBodyElement;
querySelector(selectors: "br"): HTMLBRElement;
querySelector(selectors: "button"): HTMLButtonElement;
querySelector(selectors: "canvas"): HTMLCanvasElement;
querySelector(selectors: "caption"): HTMLTableCaptionElement;
querySelector(selectors: "center"): HTMLElement;
querySelector(selectors: "circle"): SVGCircleElement;
querySelector(selectors: "cite"): HTMLElement;
querySelector(selectors: "clippath"): SVGClipPathElement;
querySelector(selectors: "code"): HTMLElement;
querySelector(selectors: "col"): HTMLTableColElement;
querySelector(selectors: "colgroup"): HTMLTableColElement;
querySelector(selectors: "datalist"): HTMLDataListElement;
querySelector(selectors: "dd"): HTMLElement;
querySelector(selectors: "defs"): SVGDefsElement;
querySelector(selectors: "del"): HTMLModElement;
querySelector(selectors: "desc"): SVGDescElement;
querySelector(selectors: "dfn"): HTMLElement;
querySelector(selectors: "dir"): HTMLDirectoryElement;
querySelector(selectors: "div"): HTMLDivElement;
querySelector(selectors: "dl"): HTMLDListElement;
querySelector(selectors: "dt"): HTMLElement;
querySelector(selectors: "ellipse"): SVGEllipseElement;
querySelector(selectors: "em"): HTMLElement;
querySelector(selectors: "embed"): HTMLEmbedElement;
querySelector(selectors: "feblend"): SVGFEBlendElement;
querySelector(selectors: "fecolormatrix"): SVGFEColorMatrixElement;
querySelector(selectors: "fecomponenttransfer"): SVGFEComponentTransferElement;
querySelector(selectors: "fecomposite"): SVGFECompositeElement;
querySelector(selectors: "feconvolvematrix"): SVGFEConvolveMatrixElement;
querySelector(selectors: "fediffuselighting"): SVGFEDiffuseLightingElement;
querySelector(selectors: "fedisplacementmap"): SVGFEDisplacementMapElement;
querySelector(selectors: "fedistantlight"): SVGFEDistantLightElement;
querySelector(selectors: "feflood"): SVGFEFloodElement;
querySelector(selectors: "fefunca"): SVGFEFuncAElement;
querySelector(selectors: "fefuncb"): SVGFEFuncBElement;
querySelector(selectors: "fefuncg"): SVGFEFuncGElement;
querySelector(selectors: "fefuncr"): SVGFEFuncRElement;
querySelector(selectors: "fegaussianblur"): SVGFEGaussianBlurElement;
querySelector(selectors: "feimage"): SVGFEImageElement;
querySelector(selectors: "femerge"): SVGFEMergeElement;
querySelector(selectors: "femergenode"): SVGFEMergeNodeElement;
querySelector(selectors: "femorphology"): SVGFEMorphologyElement;
querySelector(selectors: "feoffset"): SVGFEOffsetElement;
querySelector(selectors: "fepointlight"): SVGFEPointLightElement;
querySelector(selectors: "fespecularlighting"): SVGFESpecularLightingElement;
querySelector(selectors: "fespotlight"): SVGFESpotLightElement;
querySelector(selectors: "fetile"): SVGFETileElement;
querySelector(selectors: "feturbulence"): SVGFETurbulenceElement;
querySelector(selectors: "fieldset"): HTMLFieldSetElement;
querySelector(selectors: "figcaption"): HTMLElement;
querySelector(selectors: "figure"): HTMLElement;
querySelector(selectors: "filter"): SVGFilterElement;
querySelector(selectors: "font"): HTMLFontElement;
querySelector(selectors: "footer"): HTMLElement;
querySelector(selectors: "foreignobject"): SVGForeignObjectElement;
querySelector(selectors: "form"): HTMLFormElement;
querySelector(selectors: "frame"): HTMLFrameElement;
querySelector(selectors: "frameset"): HTMLFrameSetElement;
querySelector(selectors: "g"): SVGGElement;
querySelector(selectors: "h1"): HTMLHeadingElement;
querySelector(selectors: "h2"): HTMLHeadingElement;
querySelector(selectors: "h3"): HTMLHeadingElement;
querySelector(selectors: "h4"): HTMLHeadingElement;
querySelector(selectors: "h5"): HTMLHeadingElement;
querySelector(selectors: "h6"): HTMLHeadingElement;
querySelector(selectors: "head"): HTMLHeadElement;
querySelector(selectors: "header"): HTMLElement;
querySelector(selectors: "hgroup"): HTMLElement;
querySelector(selectors: "hr"): HTMLHRElement;
querySelector(selectors: "html"): HTMLHtmlElement;
querySelector(selectors: "i"): HTMLElement;
querySelector(selectors: "iframe"): HTMLIFrameElement;
querySelector(selectors: "image"): SVGImageElement;
querySelector(selectors: "img"): HTMLImageElement;
querySelector(selectors: "input"): HTMLInputElement;
querySelector(selectors: "ins"): HTMLModElement;
querySelector(selectors: "isindex"): HTMLUnknownElement;
querySelector(selectors: "kbd"): HTMLElement;
querySelector(selectors: "keygen"): HTMLElement;
querySelector(selectors: "label"): HTMLLabelElement;
querySelector(selectors: "legend"): HTMLLegendElement;
querySelector(selectors: "li"): HTMLLIElement;
querySelector(selectors: "line"): SVGLineElement;
querySelector(selectors: "lineargradient"): SVGLinearGradientElement;
querySelector(selectors: "link"): HTMLLinkElement;
querySelector(selectors: "listing"): HTMLPreElement;
querySelector(selectors: "map"): HTMLMapElement;
querySelector(selectors: "mark"): HTMLElement;
querySelector(selectors: "marker"): SVGMarkerElement;
querySelector(selectors: "marquee"): HTMLMarqueeElement;
querySelector(selectors: "mask"): SVGMaskElement;
querySelector(selectors: "menu"): HTMLMenuElement;
querySelector(selectors: "meta"): HTMLMetaElement;
querySelector(selectors: "metadata"): SVGMetadataElement;
querySelector(selectors: "meter"): HTMLMeterElement;
querySelector(selectors: "nav"): HTMLElement;
querySelector(selectors: "nextid"): HTMLUnknownElement;
querySelector(selectors: "nobr"): HTMLElement;
querySelector(selectors: "noframes"): HTMLElement;
querySelector(selectors: "noscript"): HTMLElement;
querySelector(selectors: "object"): HTMLObjectElement;
querySelector(selectors: "ol"): HTMLOListElement;
querySelector(selectors: "optgroup"): HTMLOptGroupElement;
querySelector(selectors: "option"): HTMLOptionElement;
querySelector(selectors: "p"): HTMLParagraphElement;
querySelector(selectors: "param"): HTMLParamElement;
querySelector(selectors: "path"): SVGPathElement;
querySelector(selectors: "pattern"): SVGPatternElement;
querySelector(selectors: "picture"): HTMLPictureElement;
querySelector(selectors: "plaintext"): HTMLElement;
querySelector(selectors: "polygon"): SVGPolygonElement;
querySelector(selectors: "polyline"): SVGPolylineElement;
querySelector(selectors: "pre"): HTMLPreElement;
querySelector(selectors: "progress"): HTMLProgressElement;
querySelector(selectors: "q"): HTMLQuoteElement;
querySelector(selectors: "radialgradient"): SVGRadialGradientElement;
querySelector(selectors: "rect"): SVGRectElement;
querySelector(selectors: "rt"): HTMLElement;
querySelector(selectors: "ruby"): HTMLElement;
querySelector(selectors: "s"): HTMLElement;
querySelector(selectors: "samp"): HTMLElement;
querySelector(selectors: "script"): HTMLScriptElement;
querySelector(selectors: "section"): HTMLElement;
querySelector(selectors: "select"): HTMLSelectElement;
querySelector(selectors: "small"): HTMLElement;
querySelector(selectors: "source"): HTMLSourceElement;
querySelector(selectors: "span"): HTMLSpanElement;
querySelector(selectors: "stop"): SVGStopElement;
querySelector(selectors: "strike"): HTMLElement;
querySelector(selectors: "strong"): HTMLElement;
querySelector(selectors: "style"): HTMLStyleElement;
querySelector(selectors: "sub"): HTMLElement;
querySelector(selectors: "sup"): HTMLElement;
querySelector(selectors: "svg"): SVGSVGElement;
querySelector(selectors: "switch"): SVGSwitchElement;
querySelector(selectors: "symbol"): SVGSymbolElement;
querySelector(selectors: "table"): HTMLTableElement;
querySelector(selectors: "tbody"): HTMLTableSectionElement;
querySelector(selectors: "td"): HTMLTableDataCellElement;
querySelector(selectors: "template"): HTMLTemplateElement;
querySelector(selectors: "text"): SVGTextElement;
querySelector(selectors: "textpath"): SVGTextPathElement;
querySelector(selectors: "textarea"): HTMLTextAreaElement;
querySelector(selectors: "tfoot"): HTMLTableSectionElement;
querySelector(selectors: "th"): HTMLTableHeaderCellElement;
querySelector(selectors: "thead"): HTMLTableSectionElement;
querySelector(selectors: "title"): HTMLTitleElement;
querySelector(selectors: "tr"): HTMLTableRowElement;
querySelector(selectors: "track"): HTMLTrackElement;
querySelector(selectors: "tspan"): SVGTSpanElement;
querySelector(selectors: "tt"): HTMLElement;
querySelector(selectors: "u"): HTMLElement;
querySelector(selectors: "ul"): HTMLUListElement;
querySelector(selectors: "use"): SVGUseElement;
querySelector(selectors: "var"): HTMLElement;
querySelector(selectors: "video"): HTMLVideoElement;
querySelector(selectors: "view"): SVGViewElement;
querySelector(selectors: "wbr"): HTMLElement;
querySelector(selectors: "x-ms-webview"): MSHTMLWebViewElement;
querySelector(selectors: "xmp"): HTMLPreElement;
querySelector(selectors: string): Element;
querySelector(selectors: "a"): HTMLAnchorElement | null;
querySelector(selectors: "abbr"): HTMLElement | null;
querySelector(selectors: "acronym"): HTMLElement | null;
querySelector(selectors: "address"): HTMLElement | null;
querySelector(selectors: "applet"): HTMLAppletElement | null;
querySelector(selectors: "area"): HTMLAreaElement | null;
querySelector(selectors: "article"): HTMLElement | null;
querySelector(selectors: "aside"): HTMLElement | null;
querySelector(selectors: "audio"): HTMLAudioElement | null;
querySelector(selectors: "b"): HTMLElement | null;
querySelector(selectors: "base"): HTMLBaseElement | null;
querySelector(selectors: "basefont"): HTMLBaseFontElement | null;
querySelector(selectors: "bdo"): HTMLElement | null;
querySelector(selectors: "big"): HTMLElement | null;
querySelector(selectors: "blockquote"): HTMLQuoteElement | null;
querySelector(selectors: "body"): HTMLBodyElement | null;
querySelector(selectors: "br"): HTMLBRElement | null;
querySelector(selectors: "button"): HTMLButtonElement | null;
querySelector(selectors: "canvas"): HTMLCanvasElement | null;
querySelector(selectors: "caption"): HTMLTableCaptionElement | null;
querySelector(selectors: "center"): HTMLElement | null;
querySelector(selectors: "circle"): SVGCircleElement | null;
querySelector(selectors: "cite"): HTMLElement | null;
querySelector(selectors: "clippath"): SVGClipPathElement | null;
querySelector(selectors: "code"): HTMLElement | null;
querySelector(selectors: "col"): HTMLTableColElement | null;
querySelector(selectors: "colgroup"): HTMLTableColElement | null;
querySelector(selectors: "datalist"): HTMLDataListElement | null;
querySelector(selectors: "dd"): HTMLElement | null;
querySelector(selectors: "defs"): SVGDefsElement | null;
querySelector(selectors: "del"): HTMLModElement | null;
querySelector(selectors: "desc"): SVGDescElement | null;
querySelector(selectors: "dfn"): HTMLElement | null;
querySelector(selectors: "dir"): HTMLDirectoryElement | null;
querySelector(selectors: "div"): HTMLDivElement | null;
querySelector(selectors: "dl"): HTMLDListElement | null;
querySelector(selectors: "dt"): HTMLElement | null;
querySelector(selectors: "ellipse"): SVGEllipseElement | null;
querySelector(selectors: "em"): HTMLElement | null;
querySelector(selectors: "embed"): HTMLEmbedElement | null;
querySelector(selectors: "feblend"): SVGFEBlendElement | null;
querySelector(selectors: "fecolormatrix"): SVGFEColorMatrixElement | null;
querySelector(selectors: "fecomponenttransfer"): SVGFEComponentTransferElement | null;
querySelector(selectors: "fecomposite"): SVGFECompositeElement | null;
querySelector(selectors: "feconvolvematrix"): SVGFEConvolveMatrixElement | null;
querySelector(selectors: "fediffuselighting"): SVGFEDiffuseLightingElement | null;
querySelector(selectors: "fedisplacementmap"): SVGFEDisplacementMapElement | null;
querySelector(selectors: "fedistantlight"): SVGFEDistantLightElement | null;
querySelector(selectors: "feflood"): SVGFEFloodElement | null;
querySelector(selectors: "fefunca"): SVGFEFuncAElement | null;
querySelector(selectors: "fefuncb"): SVGFEFuncBElement | null;
querySelector(selectors: "fefuncg"): SVGFEFuncGElement | null;
querySelector(selectors: "fefuncr"): SVGFEFuncRElement | null;
querySelector(selectors: "fegaussianblur"): SVGFEGaussianBlurElement | null;
querySelector(selectors: "feimage"): SVGFEImageElement | null;
querySelector(selectors: "femerge"): SVGFEMergeElement | null;
querySelector(selectors: "femergenode"): SVGFEMergeNodeElement | null;
querySelector(selectors: "femorphology"): SVGFEMorphologyElement | null;
querySelector(selectors: "feoffset"): SVGFEOffsetElement | null;
querySelector(selectors: "fepointlight"): SVGFEPointLightElement | null;
querySelector(selectors: "fespecularlighting"): SVGFESpecularLightingElement | null;
querySelector(selectors: "fespotlight"): SVGFESpotLightElement | null;
querySelector(selectors: "fetile"): SVGFETileElement | null;
querySelector(selectors: "feturbulence"): SVGFETurbulenceElement | null;
querySelector(selectors: "fieldset"): HTMLFieldSetElement | null;
querySelector(selectors: "figcaption"): HTMLElement | null;
querySelector(selectors: "figure"): HTMLElement | null;
querySelector(selectors: "filter"): SVGFilterElement | null;
querySelector(selectors: "font"): HTMLFontElement | null;
querySelector(selectors: "footer"): HTMLElement | null;
querySelector(selectors: "foreignobject"): SVGForeignObjectElement | null;
querySelector(selectors: "form"): HTMLFormElement | null;
querySelector(selectors: "frame"): HTMLFrameElement | null;
querySelector(selectors: "frameset"): HTMLFrameSetElement | null;
querySelector(selectors: "g"): SVGGElement | null;
querySelector(selectors: "h1"): HTMLHeadingElement | null;
querySelector(selectors: "h2"): HTMLHeadingElement | null;
querySelector(selectors: "h3"): HTMLHeadingElement | null;
querySelector(selectors: "h4"): HTMLHeadingElement | null;
querySelector(selectors: "h5"): HTMLHeadingElement | null;
querySelector(selectors: "h6"): HTMLHeadingElement | null;
querySelector(selectors: "head"): HTMLHeadElement | null;
querySelector(selectors: "header"): HTMLElement | null;
querySelector(selectors: "hgroup"): HTMLElement | null;
querySelector(selectors: "hr"): HTMLHRElement | null;
querySelector(selectors: "html"): HTMLHtmlElement | null;
querySelector(selectors: "i"): HTMLElement | null;
querySelector(selectors: "iframe"): HTMLIFrameElement | null;
querySelector(selectors: "image"): SVGImageElement | null;
querySelector(selectors: "img"): HTMLImageElement | null;
querySelector(selectors: "input"): HTMLInputElement | null;
querySelector(selectors: "ins"): HTMLModElement | null;
querySelector(selectors: "isindex"): HTMLUnknownElement | null;
querySelector(selectors: "kbd"): HTMLElement | null;
querySelector(selectors: "keygen"): HTMLElement | null;
querySelector(selectors: "label"): HTMLLabelElement | null;
querySelector(selectors: "legend"): HTMLLegendElement | null;
querySelector(selectors: "li"): HTMLLIElement | null;
querySelector(selectors: "line"): SVGLineElement | null;
querySelector(selectors: "lineargradient"): SVGLinearGradientElement | null;
querySelector(selectors: "link"): HTMLLinkElement | null;
querySelector(selectors: "listing"): HTMLPreElement | null;
querySelector(selectors: "map"): HTMLMapElement | null;
querySelector(selectors: "mark"): HTMLElement | null;
querySelector(selectors: "marker"): SVGMarkerElement | null;
querySelector(selectors: "marquee"): HTMLMarqueeElement | null;
querySelector(selectors: "mask"): SVGMaskElement | null;
querySelector(selectors: "menu"): HTMLMenuElement | null;
querySelector(selectors: "meta"): HTMLMetaElement | null;
querySelector(selectors: "metadata"): SVGMetadataElement | null;
querySelector(selectors: "meter"): HTMLMeterElement | null;
querySelector(selectors: "nav"): HTMLElement | null;
querySelector(selectors: "nextid"): HTMLUnknownElement | null;
querySelector(selectors: "nobr"): HTMLElement | null;
querySelector(selectors: "noframes"): HTMLElement | null;
querySelector(selectors: "noscript"): HTMLElement | null;
querySelector(selectors: "object"): HTMLObjectElement | null;
querySelector(selectors: "ol"): HTMLOListElement | null;
querySelector(selectors: "optgroup"): HTMLOptGroupElement | null;
querySelector(selectors: "option"): HTMLOptionElement | null;
querySelector(selectors: "p"): HTMLParagraphElement | null;
querySelector(selectors: "param"): HTMLParamElement | null;
querySelector(selectors: "path"): SVGPathElement | null;
querySelector(selectors: "pattern"): SVGPatternElement | null;
querySelector(selectors: "picture"): HTMLPictureElement | null;
querySelector(selectors: "plaintext"): HTMLElement | null;
querySelector(selectors: "polygon"): SVGPolygonElement | null;
querySelector(selectors: "polyline"): SVGPolylineElement | null;
querySelector(selectors: "pre"): HTMLPreElement | null;
querySelector(selectors: "progress"): HTMLProgressElement | null;
querySelector(selectors: "q"): HTMLQuoteElement | null;
querySelector(selectors: "radialgradient"): SVGRadialGradientElement | null;
querySelector(selectors: "rect"): SVGRectElement | null;
querySelector(selectors: "rt"): HTMLElement | null;
querySelector(selectors: "ruby"): HTMLElement | null;
querySelector(selectors: "s"): HTMLElement | null;
querySelector(selectors: "samp"): HTMLElement | null;
querySelector(selectors: "script"): HTMLScriptElement | null;
querySelector(selectors: "section"): HTMLElement | null;
querySelector(selectors: "select"): HTMLSelectElement | null;
querySelector(selectors: "small"): HTMLElement | null;
querySelector(selectors: "source"): HTMLSourceElement | null;
querySelector(selectors: "span"): HTMLSpanElement | null;
querySelector(selectors: "stop"): SVGStopElement | null;
querySelector(selectors: "strike"): HTMLElement | null;
querySelector(selectors: "strong"): HTMLElement | null;
querySelector(selectors: "style"): HTMLStyleElement | null;
querySelector(selectors: "sub"): HTMLElement | null;
querySelector(selectors: "sup"): HTMLElement | null;
querySelector(selectors: "svg"): SVGSVGElement | null;
querySelector(selectors: "switch"): SVGSwitchElement | null;
querySelector(selectors: "symbol"): SVGSymbolElement | null;
querySelector(selectors: "table"): HTMLTableElement | null;
querySelector(selectors: "tbody"): HTMLTableSectionElement | null;
querySelector(selectors: "td"): HTMLTableDataCellElement | null;
querySelector(selectors: "template"): HTMLTemplateElement | null;
querySelector(selectors: "text"): SVGTextElement | null;
querySelector(selectors: "textpath"): SVGTextPathElement | null;
querySelector(selectors: "textarea"): HTMLTextAreaElement | null;
querySelector(selectors: "tfoot"): HTMLTableSectionElement | null;
querySelector(selectors: "th"): HTMLTableHeaderCellElement | null;
querySelector(selectors: "thead"): HTMLTableSectionElement | null;
querySelector(selectors: "title"): HTMLTitleElement | null;
querySelector(selectors: "tr"): HTMLTableRowElement | null;
querySelector(selectors: "track"): HTMLTrackElement | null;
querySelector(selectors: "tspan"): SVGTSpanElement | null;
querySelector(selectors: "tt"): HTMLElement | null;
querySelector(selectors: "u"): HTMLElement | null;
querySelector(selectors: "ul"): HTMLUListElement | null;
querySelector(selectors: "use"): SVGUseElement | null;
querySelector(selectors: "var"): HTMLElement | null;
querySelector(selectors: "video"): HTMLVideoElement | null;
querySelector(selectors: "view"): SVGViewElement | null;
querySelector(selectors: "wbr"): HTMLElement | null;
querySelector(selectors: "x-ms-webview"): MSHTMLWebViewElement | null;
querySelector(selectors: "xmp"): HTMLPreElement | null;
querySelector(selectors: string): Element | null;
querySelectorAll(selectors: "a"): NodeListOf<HTMLAnchorElement>;
querySelectorAll(selectors: "abbr"): NodeListOf<HTMLElement>;
querySelectorAll(selectors: "acronym"): NodeListOf<HTMLElement>;
@ -14622,4 +14622,5 @@ type ScrollBehavior = "auto" | "instant" | "smooth";
type ScrollLogicalPosition = "start" | "center" | "end" | "nearest";
type IDBValidKey = number | string | Date | IDBArrayKey;
type BufferSource = ArrayBuffer | ArrayBufferView;
type MouseWheelEvent = WheelEvent;
type MouseWheelEvent = WheelEvent;
type ScrollRestoration = "auto" | "manual";

View File

@ -728,7 +728,7 @@ declare var Worker: {
}
interface XMLHttpRequest extends EventTarget, XMLHttpRequestEventTarget {
onreadystatechange: (this: this, ev: ProgressEvent) => any;
onreadystatechange: (this: this, ev: Event) => any;
readonly readyState: number;
readonly response: any;
readonly responseText: string;
@ -754,13 +754,13 @@ interface XMLHttpRequest extends EventTarget, XMLHttpRequestEventTarget {
readonly LOADING: number;
readonly OPENED: number;
readonly UNSENT: number;
addEventListener(type: "abort", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "error", listener: (this: this, ev: ErrorEvent) => any, useCapture?: boolean): void;
addEventListener(type: "load", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "abort", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "error", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "load", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "loadend", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "loadstart", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "loadstart", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "progress", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "readystatechange", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: "readystatechange", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "timeout", listener: (this: this, ev: ProgressEvent) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}

382
src/server/builder.ts Normal file
View File

@ -0,0 +1,382 @@
/// <reference path="..\compiler\commandLineParser.ts" />
/// <reference path="..\services\services.ts" />
/// <reference path="protocol.d.ts" />
/// <reference path="session.ts" />
/// <reference types="node" />
namespace ts.server {
interface Hash {
update(data: any, input_encoding?: string): Hash;
digest(encoding: string): any;
}
const crypto: {
createHash(algorithm: string): Hash
} = require("crypto");
export function shouldEmitFile(scriptInfo: ScriptInfo) {
return !scriptInfo.hasMixedContent;
}
/**
* An abstract file info that maintains a shape signature.
*/
export class BuilderFileInfo {
private lastCheckedShapeSignature: string;
constructor(public readonly scriptInfo: ScriptInfo, public readonly project: Project) {
}
public isExternalModuleOrHasOnlyAmbientExternalModules() {
const sourceFile = this.getSourceFile();
return isExternalModule(sourceFile) || this.containsOnlyAmbientModules(sourceFile);
}
/**
* For script files that contains only ambient external modules, although they are not actually external module files,
* they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore,
* there are no point to rebuild all script files if these special files have changed. However, if any statement
* in the file is not ambient external module, we treat it as a regular script file.
*/
private containsOnlyAmbientModules(sourceFile: SourceFile) {
for (const statement of sourceFile.statements) {
if (statement.kind !== SyntaxKind.ModuleDeclaration || (<ModuleDeclaration>statement).name.kind !== SyntaxKind.StringLiteral) {
return false;
}
}
return true;
}
private computeHash(text: string): string {
return crypto.createHash("md5")
.update(text)
.digest("base64");
}
private getSourceFile(): SourceFile {
return this.project.getSourceFile(this.scriptInfo.path);
}
/**
* @return {boolean} indicates if the shape signature has changed since last update.
*/
public updateShapeSignature() {
const sourceFile = this.getSourceFile();
if (!sourceFile) {
return true;
}
const lastSignature = this.lastCheckedShapeSignature;
if (sourceFile.isDeclarationFile) {
this.lastCheckedShapeSignature = this.computeHash(sourceFile.text);
}
else {
const emitOutput = this.project.getFileEmitOutput(this.scriptInfo, /*emitOnlyDtsFiles*/ true);
if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) {
this.lastCheckedShapeSignature = this.computeHash(emitOutput.outputFiles[0].text);
}
}
return !lastSignature || this.lastCheckedShapeSignature !== lastSignature;
}
}
export interface Builder {
readonly project: Project;
getFilesAffectedBy(scriptInfo: ScriptInfo): string[];
onProjectUpdateGraph(): void;
emitFile(scriptInfo: ScriptInfo, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => void): boolean;
}
abstract class AbstractBuilder<T extends BuilderFileInfo> implements Builder {
private fileInfos = createFileMap<T>();
constructor(public readonly project: Project, private ctor: { new (scriptInfo: ScriptInfo, project: Project): T }) {
}
protected getFileInfo(path: Path): T {
return this.fileInfos.get(path);
}
protected getOrCreateFileInfo(path: Path): T {
let fileInfo = this.getFileInfo(path);
if (!fileInfo) {
const scriptInfo = this.project.getScriptInfo(path);
fileInfo = new this.ctor(scriptInfo, this.project);
this.setFileInfo(path, fileInfo);
}
return fileInfo;
}
protected getFileInfoPaths(): Path[] {
return this.fileInfos.getKeys();
}
protected setFileInfo(path: Path, info: T) {
this.fileInfos.set(path, info);
}
protected removeFileInfo(path: Path) {
this.fileInfos.remove(path);
}
protected forEachFileInfo(action: (fileInfo: T) => any) {
this.fileInfos.forEachValue((path: Path, value: T) => action(value));
}
abstract getFilesAffectedBy(scriptInfo: ScriptInfo): string[];
abstract onProjectUpdateGraph(): void;
/**
* @returns {boolean} whether the emit was conducted or not
*/
emitFile(scriptInfo: ScriptInfo, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => void): boolean {
const fileInfo = this.getFileInfo(scriptInfo.path);
if (!fileInfo) {
return false;
}
const { emitSkipped, outputFiles } = this.project.getFileEmitOutput(fileInfo.scriptInfo, /*emitOnlyDtsFiles*/ false);
if (!emitSkipped) {
const projectRootPath = this.project.getProjectRootPath();
for (const outputFile of outputFiles) {
const outputFileAbsoluteFileName = getNormalizedAbsolutePath(outputFile.name, projectRootPath ? projectRootPath : getDirectoryPath(scriptInfo.fileName));
writeFile(outputFileAbsoluteFileName, outputFile.text, outputFile.writeByteOrderMark);
}
}
return !emitSkipped;
}
}
class NonModuleBuilder extends AbstractBuilder<BuilderFileInfo> {
constructor(public readonly project: Project) {
super(project, BuilderFileInfo);
}
onProjectUpdateGraph() {
}
/**
* Note: didn't use path as parameter because the returned file names will be directly
* consumed by the API user, which will use it to interact with file systems. Path
* should only be used internally, because the case sensitivity is not trustable.
*/
getFilesAffectedBy(scriptInfo: ScriptInfo): string[] {
const info = this.getOrCreateFileInfo(scriptInfo.path);
const singleFileResult = scriptInfo.hasMixedContent ? [] : [scriptInfo.fileName];
if (info.updateShapeSignature()) {
const options = this.project.getCompilerOptions();
// If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project,
// so returning the file itself is good enough.
if (options && (options.out || options.outFile)) {
return singleFileResult;
}
return this.project.getAllEmittableFiles();
}
return singleFileResult;
}
}
class ModuleBuilderFileInfo extends BuilderFileInfo {
references: ModuleBuilderFileInfo[] = [];
referencedBy: ModuleBuilderFileInfo[] = [];
scriptVersionForReferences: string;
static compareFileInfos(lf: ModuleBuilderFileInfo, rf: ModuleBuilderFileInfo): number {
const l = lf.scriptInfo.fileName;
const r = rf.scriptInfo.fileName;
return (l < r ? -1 : (l > r ? 1 : 0));
};
static addToReferenceList(array: ModuleBuilderFileInfo[], fileInfo: ModuleBuilderFileInfo) {
if (array.length === 0) {
array.push(fileInfo);
return;
}
const insertIndex = binarySearch(array, fileInfo, ModuleBuilderFileInfo.compareFileInfos);
if (insertIndex < 0) {
array.splice(~insertIndex, 0, fileInfo);
}
}
static removeFromReferenceList(array: ModuleBuilderFileInfo[], fileInfo: ModuleBuilderFileInfo) {
if (!array || array.length === 0) {
return;
}
if (array[0] === fileInfo) {
array.splice(0, 1);
return;
}
const removeIndex = binarySearch(array, fileInfo, ModuleBuilderFileInfo.compareFileInfos);
if (removeIndex >= 0) {
array.splice(removeIndex, 1);
}
}
addReferencedBy(fileInfo: ModuleBuilderFileInfo): void {
ModuleBuilderFileInfo.addToReferenceList(this.referencedBy, fileInfo);
}
removeReferencedBy(fileInfo: ModuleBuilderFileInfo): void {
ModuleBuilderFileInfo.removeFromReferenceList(this.referencedBy, fileInfo);
}
removeFileReferences() {
for (const reference of this.references) {
reference.removeReferencedBy(this);
}
this.references = [];
}
}
class ModuleBuilder extends AbstractBuilder<ModuleBuilderFileInfo> {
constructor(public readonly project: Project) {
super(project, ModuleBuilderFileInfo);
}
private projectVersionForDependencyGraph: string;
private getReferencedFileInfos(fileInfo: ModuleBuilderFileInfo): ModuleBuilderFileInfo[] {
if (!fileInfo.isExternalModuleOrHasOnlyAmbientExternalModules()) {
return [];
}
const referencedFilePaths = this.project.getReferencedFiles(fileInfo.scriptInfo.path);
if (referencedFilePaths.length > 0) {
return map(referencedFilePaths, f => this.getOrCreateFileInfo(f)).sort(ModuleBuilderFileInfo.compareFileInfos);
}
return [];
}
onProjectUpdateGraph() {
this.ensureProjectDependencyGraphUpToDate();
}
private ensureProjectDependencyGraphUpToDate() {
if (!this.projectVersionForDependencyGraph || this.project.getProjectVersion() !== this.projectVersionForDependencyGraph) {
const currentScriptInfos = this.project.getScriptInfos();
for (const scriptInfo of currentScriptInfos) {
const fileInfo = this.getOrCreateFileInfo(scriptInfo.path);
this.updateFileReferences(fileInfo);
}
this.forEachFileInfo(fileInfo => {
if (!this.project.containsScriptInfo(fileInfo.scriptInfo)) {
// This file was deleted from this project
fileInfo.removeFileReferences();
this.removeFileInfo(fileInfo.scriptInfo.path);
}
});
this.projectVersionForDependencyGraph = this.project.getProjectVersion();
}
}
private updateFileReferences(fileInfo: ModuleBuilderFileInfo) {
// Only need to update if the content of the file changed.
if (fileInfo.scriptVersionForReferences === fileInfo.scriptInfo.getLatestVersion()) {
return;
}
const newReferences = this.getReferencedFileInfos(fileInfo);
const oldReferences = fileInfo.references;
let oldIndex = 0;
let newIndex = 0;
while (oldIndex < oldReferences.length && newIndex < newReferences.length) {
const oldReference = oldReferences[oldIndex];
const newReference = newReferences[newIndex];
const compare = ModuleBuilderFileInfo.compareFileInfos(oldReference, newReference);
if (compare < 0) {
// New reference is greater then current reference. That means
// the current reference doesn't exist anymore after parsing. So delete
// references.
oldReference.removeReferencedBy(fileInfo);
oldIndex++;
}
else if (compare > 0) {
// A new reference info. Add it.
newReference.addReferencedBy(fileInfo);
newIndex++;
}
else {
// Equal. Go to next
oldIndex++;
newIndex++;
}
}
// Clean old references
for (let i = oldIndex; i < oldReferences.length; i++) {
oldReferences[i].removeReferencedBy(fileInfo);
}
// Update new references
for (let i = newIndex; i < newReferences.length; i++) {
newReferences[i].addReferencedBy(fileInfo);
}
fileInfo.references = newReferences;
fileInfo.scriptVersionForReferences = fileInfo.scriptInfo.getLatestVersion();
}
getFilesAffectedBy(scriptInfo: ScriptInfo): string[] {
this.ensureProjectDependencyGraphUpToDate();
const singleFileResult = scriptInfo.hasMixedContent ? [] : [scriptInfo.fileName];
const fileInfo = this.getFileInfo(scriptInfo.path);
if (!fileInfo || !fileInfo.updateShapeSignature()) {
return singleFileResult;
}
if (!fileInfo.isExternalModuleOrHasOnlyAmbientExternalModules()) {
return this.project.getAllEmittableFiles();
}
const options = this.project.getCompilerOptions();
if (options && (options.isolatedModules || options.out || options.outFile)) {
return singleFileResult;
}
// Now we need to if each file in the referencedBy list has a shape change as well.
// Because if so, its own referencedBy files need to be saved as well to make the
// emitting result consistent with files on disk.
// Use slice to clone the array to avoid manipulating in place
const queue = fileInfo.referencedBy.slice(0);
const fileNameSet = createMap<ScriptInfo>();
fileNameSet[scriptInfo.fileName] = scriptInfo;
while (queue.length > 0) {
const processingFileInfo = queue.pop();
if (processingFileInfo.updateShapeSignature() && processingFileInfo.referencedBy.length > 0) {
for (const potentialFileInfo of processingFileInfo.referencedBy) {
if (!fileNameSet[potentialFileInfo.scriptInfo.fileName]) {
queue.push(potentialFileInfo);
}
}
}
fileNameSet[processingFileInfo.scriptInfo.fileName] = processingFileInfo.scriptInfo;
}
const result: string[] = [];
for (const fileName in fileNameSet) {
if (shouldEmitFile(fileNameSet[fileName])) {
result.push(fileName);
}
}
return result;
}
}
export function createBuilder(project: Project): Builder {
const moduleKind = project.getCompilerOptions().module;
switch (moduleKind) {
case ModuleKind.None:
return new NonModuleBuilder(project);
default:
return new ModuleBuilder(project);
}
}
}

View File

@ -0,0 +1,34 @@
/// <reference types="node" />
// TODO: extract services types
interface HostCancellationToken {
isCancellationRequested(): boolean;
}
import fs = require("fs");
function createCancellationToken(args: string[]): HostCancellationToken {
let cancellationPipeName: string;
for (let i = 0; i < args.length - 1; i++) {
if (args[i] === "--cancellationPipeName") {
cancellationPipeName = args[i + 1];
break;
}
}
if (!cancellationPipeName) {
return { isCancellationRequested: () => false };
}
return {
isCancellationRequested() {
try {
fs.statSync(cancellationPipeName);
return true;
}
catch (e) {
return false;
}
}
};
}
export = createCancellationToken;

View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"noImplicitAny": true,
"noImplicitThis": true,
"removeComments": true,
"preserveConstEnums": true,
"pretty": true,
"outDir": "../../../built/local",
"module": "commonjs",
"sourceMap": true,
"stripInternal": true,
"types": [
"node"
]
},
"files": [
"cancellationToken.ts"
]
}

File diff suppressed because it is too large Load Diff

198
src/server/lsHost.ts Normal file
View File

@ -0,0 +1,198 @@
/// <reference path="..\services\services.ts" />
/// <reference path="utilities.ts" />
/// <reference path="scriptInfo.ts" />
namespace ts.server {
export class LSHost implements ts.LanguageServiceHost, ModuleResolutionHost, ServerLanguageServiceHost {
private compilationSettings: ts.CompilerOptions;
private readonly resolvedModuleNames: ts.FileMap<Map<ResolvedModuleWithFailedLookupLocations>>;
private readonly resolvedTypeReferenceDirectives: ts.FileMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>;
private readonly getCanonicalFileName: (fileName: string) => string;
private readonly resolveModuleName: typeof resolveModuleName;
readonly trace: (s: string) => void;
constructor(private readonly host: ServerHost, private readonly project: Project, private readonly cancellationToken: HostCancellationToken) {
this.getCanonicalFileName = ts.createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
this.resolvedModuleNames = createFileMap<Map<ResolvedModuleWithFailedLookupLocations>>();
this.resolvedTypeReferenceDirectives = createFileMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
if (host.trace) {
this.trace = s => host.trace(s);
}
this.resolveModuleName = (moduleName, containingFile, compilerOptions, host) => {
const primaryResult = resolveModuleName(moduleName, containingFile, compilerOptions, host);
if (primaryResult.resolvedModule) {
// return result immediately only if it is .ts, .tsx or .d.ts
// otherwise try to load typings from @types
if (fileExtensionIsAny(primaryResult.resolvedModule.resolvedFileName, supportedTypeScriptExtensions)) {
return primaryResult;
}
}
// create different collection of failed lookup locations for second pass
// if it will fail and we've already found something during the first pass - we don't want to pollute its results
const secondaryLookupFailedLookupLocations: string[] = [];
const globalCache = this.project.projectService.typingsInstaller.globalTypingsCacheLocation;
if (this.project.getTypingOptions().enableAutoDiscovery && globalCache) {
const traceEnabled = isTraceEnabled(compilerOptions, host);
if (traceEnabled) {
trace(host, Diagnostics.Auto_discovery_for_typings_is_enabled_in_project_0_Running_extra_resolution_pass_for_module_1_using_cache_location_2, this.project.getProjectName(), moduleName, globalCache);
}
const state: ModuleResolutionState = { compilerOptions, host, skipTsx: false, traceEnabled };
const resolvedName = loadModuleFromNodeModules(moduleName, globalCache, secondaryLookupFailedLookupLocations, state, /*checkOneLevel*/ true);
if (resolvedName) {
return createResolvedModule(resolvedName, /*isExternalLibraryImport*/ true, primaryResult.failedLookupLocations.concat(secondaryLookupFailedLookupLocations));
}
}
if (!primaryResult.resolvedModule && secondaryLookupFailedLookupLocations.length) {
primaryResult.failedLookupLocations = primaryResult.failedLookupLocations.concat(secondaryLookupFailedLookupLocations);
}
return primaryResult;
};
}
private resolveNamesWithLocalCache<T extends { failedLookupLocations: string[] }, R>(
names: string[],
containingFile: string,
cache: ts.FileMap<Map<T>>,
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => T,
getResult: (s: T) => R): R[] {
const path = toPath(containingFile, this.host.getCurrentDirectory(), this.getCanonicalFileName);
const currentResolutionsInFile = cache.get(path);
const newResolutions: Map<T> = createMap<T>();
const resolvedModules: R[] = [];
const compilerOptions = this.getCompilationSettings();
for (const name of names) {
// check if this is a duplicate entry in the list
let resolution = newResolutions[name];
if (!resolution) {
const existingResolution = currentResolutionsInFile && currentResolutionsInFile[name];
if (moduleResolutionIsValid(existingResolution)) {
// ok, it is safe to use existing name resolution results
resolution = existingResolution;
}
else {
newResolutions[name] = resolution = loader(name, containingFile, compilerOptions, this);
}
}
ts.Debug.assert(resolution !== undefined);
resolvedModules.push(getResult(resolution));
}
// replace old results with a new one
cache.set(path, newResolutions);
return resolvedModules;
function moduleResolutionIsValid(resolution: T): boolean {
if (!resolution) {
return false;
}
if (getResult(resolution)) {
// TODO: consider checking failedLookupLocations
return true;
}
// consider situation if we have no candidate locations as valid resolution.
// after all there is no point to invalidate it if we have no idea where to look for the module.
return resolution.failedLookupLocations.length === 0;
}
}
getProjectVersion() {
return this.project.getProjectVersion();
}
getCompilationSettings() {
return this.compilationSettings;
}
useCaseSensitiveFileNames() {
return this.host.useCaseSensitiveFileNames;
}
getCancellationToken() {
return this.cancellationToken;
}
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] {
return this.resolveNamesWithLocalCache(typeDirectiveNames, containingFile, this.resolvedTypeReferenceDirectives, resolveTypeReferenceDirective, m => m.resolvedTypeReferenceDirective);
}
resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModule[] {
return this.resolveNamesWithLocalCache(moduleNames, containingFile, this.resolvedModuleNames, this.resolveModuleName, m => m.resolvedModule);
}
getDefaultLibFileName() {
const nodeModuleBinDir = getDirectoryPath(normalizePath(this.host.getExecutingFilePath()));
return combinePaths(nodeModuleBinDir, getDefaultLibFileName(this.compilationSettings));
}
getScriptSnapshot(filename: string): ts.IScriptSnapshot {
const scriptInfo = this.project.getScriptInfoLSHost(filename);
if (scriptInfo) {
return scriptInfo.snap();
}
}
getScriptFileNames() {
return this.project.getRootFilesLSHost();
}
getTypeRootsVersion() {
return this.project.typesVersion;
}
getScriptKind(fileName: string) {
const info = this.project.getScriptInfoLSHost(fileName);
return info && info.scriptKind;
}
getScriptVersion(filename: string) {
const info = this.project.getScriptInfoLSHost(filename);
return info && info.getLatestVersion();
}
getCurrentDirectory(): string {
return this.host.getCurrentDirectory();
}
resolvePath(path: string): string {
return this.host.resolvePath(path);
}
fileExists(path: string): boolean {
return this.host.fileExists(path);
}
directoryExists(path: string): boolean {
return this.host.directoryExists(path);
}
readFile(fileName: string): string {
return this.host.readFile(fileName);
}
getDirectories(path: string): string[] {
return this.host.getDirectories(path);
}
notifyFileRemoved(info: ScriptInfo) {
this.resolvedModuleNames.remove(info.path);
this.resolvedTypeReferenceDirectives.remove(info.path);
}
setCompilationSettings(opt: ts.CompilerOptions) {
this.compilationSettings = opt;
// conservatively assume that changing compiler options might affect module resolution strategy
this.resolvedModuleNames.clear();
this.resolvedTypeReferenceDirectives.clear();
}
}
}

760
src/server/project.ts Normal file
View File

@ -0,0 +1,760 @@
/// <reference path="..\services\services.ts" />
/// <reference path="utilities.ts"/>
/// <reference path="scriptInfo.ts"/>
/// <reference path="lsHost.ts"/>
/// <reference path="typingsCache.ts"/>
/// <reference path="builder.ts"/>
namespace ts.server {
export enum ProjectKind {
Inferred,
Configured,
External
}
function remove<T>(items: T[], item: T) {
const index = items.indexOf(item);
if (index >= 0) {
items.splice(index, 1);
}
}
function isJsOrDtsFile(info: ScriptInfo) {
return info.scriptKind === ScriptKind.JS || info.scriptKind == ScriptKind.JSX || fileExtensionIs(info.fileName, ".d.ts");
}
export function allRootFilesAreJsOrDts(project: Project): boolean {
return project.getRootScriptInfos().every(isJsOrDtsFile);
}
export function allFilesAreJsOrDts(project: Project): boolean {
return project.getScriptInfos().every(isJsOrDtsFile);
}
export interface ProjectFilesWithTSDiagnostics extends protocol.ProjectFiles {
projectErrors: Diagnostic[];
}
export abstract class Project {
private rootFiles: ScriptInfo[] = [];
private rootFilesMap: FileMap<ScriptInfo> = createFileMap<ScriptInfo>();
private lsHost: ServerLanguageServiceHost;
private program: ts.Program;
private languageService: LanguageService;
builder: Builder;
/**
* Set of files that was returned from the last call to getChangesSinceVersion.
*/
private lastReportedFileNames: Map<string>;
/**
* Last version that was reported.
*/
private lastReportedVersion = 0;
/**
* Current project structure version.
* This property is changed in 'updateGraph' based on the set of files in program
*/
private projectStructureVersion = 0;
/**
* Current version of the project state. It is changed when:
* - new root file was added/removed
* - edit happen in some file that is currently included in the project.
* This property is different from projectStructureVersion since in most cases edits don't affect set of files in the project
*/
private projectStateVersion = 0;
private typingFiles: TypingsArray;
protected projectErrors: Diagnostic[];
public typesVersion = 0;
public isJsOnlyProject() {
this.updateGraph();
return allFilesAreJsOrDts(this);
}
constructor(
readonly projectKind: ProjectKind,
readonly projectService: ProjectService,
private documentRegistry: ts.DocumentRegistry,
hasExplicitListOfFiles: boolean,
public languageServiceEnabled: boolean,
private compilerOptions: CompilerOptions,
public compileOnSaveEnabled: boolean) {
if (!this.compilerOptions) {
this.compilerOptions = ts.getDefaultCompilerOptions();
this.compilerOptions.allowNonTsExtensions = true;
this.compilerOptions.allowJs = true;
}
else if (hasExplicitListOfFiles) {
// If files are listed explicitly, allow all extensions
this.compilerOptions.allowNonTsExtensions = true;
}
if (languageServiceEnabled) {
this.enableLanguageService();
}
else {
this.disableLanguageService();
}
this.builder = createBuilder(this);
this.markAsDirty();
}
getProjectErrors() {
return this.projectErrors;
}
getLanguageService(ensureSynchronized = true): LanguageService {
if (ensureSynchronized) {
this.updateGraph();
}
return this.languageService;
}
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[] {
if (!this.languageServiceEnabled) {
return [];
}
this.updateGraph();
return this.builder.getFilesAffectedBy(scriptInfo);
}
getProjectVersion() {
return this.projectStateVersion.toString();
}
enableLanguageService() {
const lsHost = new LSHost(this.projectService.host, this, this.projectService.cancellationToken);
lsHost.setCompilationSettings(this.compilerOptions);
this.languageService = ts.createLanguageService(lsHost, this.documentRegistry);
this.lsHost = lsHost;
this.languageServiceEnabled = true;
}
disableLanguageService() {
this.languageService = nullLanguageService;
this.lsHost = nullLanguageServiceHost;
this.languageServiceEnabled = false;
}
abstract getProjectName(): string;
abstract getProjectRootPath(): string | undefined;
abstract getTypingOptions(): TypingOptions;
getSourceFile(path: Path) {
if (!this.program) {
return undefined;
}
return this.program.getSourceFileByPath(path);
}
updateTypes() {
this.typesVersion++;
this.markAsDirty();
this.updateGraph();
}
close() {
if (this.program) {
// if we have a program - release all files that are enlisted in program
for (const f of this.program.getSourceFiles()) {
const info = this.projectService.getScriptInfo(f.fileName);
info.detachFromProject(this);
}
}
else {
// release all root files
for (const root of this.rootFiles) {
root.detachFromProject(this);
}
}
this.rootFiles = undefined;
this.rootFilesMap = undefined;
this.program = undefined;
// signal language service to release source files acquired from document registry
this.languageService.dispose();
}
getCompilerOptions() {
return this.compilerOptions;
}
hasRoots() {
return this.rootFiles && this.rootFiles.length > 0;
}
getRootFiles() {
return this.rootFiles && this.rootFiles.map(info => info.fileName);
}
getRootFilesLSHost() {
const result: string[] = [];
if (this.rootFiles) {
for (const f of this.rootFiles) {
result.push(f.fileName);
}
if (this.typingFiles) {
for (const f of this.typingFiles) {
result.push(f);
}
}
}
return result;
}
getRootScriptInfos() {
return this.rootFiles;
}
getScriptInfos() {
return map(this.program.getSourceFiles(), sourceFile => this.getScriptInfoLSHost(sourceFile.path));
}
getFileEmitOutput(info: ScriptInfo, emitOnlyDtsFiles: boolean) {
if (!this.languageServiceEnabled) {
return undefined;
}
return this.getLanguageService().getEmitOutput(info.fileName, emitOnlyDtsFiles);
}
getFileNames() {
if (!this.program) {
return [];
}
if (!this.languageServiceEnabled) {
// if language service is disabled assume that all files in program are root files + default library
let rootFiles = this.getRootFiles();
if (this.compilerOptions) {
const defaultLibrary = getDefaultLibFilePath(this.compilerOptions);
if (defaultLibrary) {
(rootFiles || (rootFiles = [])).push(asNormalizedPath(defaultLibrary));
}
}
return rootFiles;
}
const sourceFiles = this.program.getSourceFiles();
return sourceFiles.map(sourceFile => asNormalizedPath(sourceFile.fileName));
}
getAllEmittableFiles() {
if (!this.languageServiceEnabled) {
return [];
}
const defaultLibraryFileName = getDefaultLibFileName(this.compilerOptions);
const infos = this.getScriptInfos();
const result: string[] = [];
for (const info of infos) {
if (getBaseFileName(info.fileName) !== defaultLibraryFileName && shouldEmitFile(info)) {
result.push(info.fileName);
}
}
return result;
}
containsScriptInfo(info: ScriptInfo): boolean {
return this.isRoot(info) || (this.program && this.program.getSourceFileByPath(info.path) !== undefined);
}
containsFile(filename: NormalizedPath, requireOpen?: boolean) {
const info = this.projectService.getScriptInfoForNormalizedPath(filename);
if (info && (info.isOpen || !requireOpen)) {
return this.containsScriptInfo(info);
}
}
isRoot(info: ScriptInfo) {
return this.rootFilesMap && this.rootFilesMap.contains(info.path);
}
// add a root file to project
addRoot(info: ScriptInfo) {
if (!this.isRoot(info)) {
this.rootFiles.push(info);
this.rootFilesMap.set(info.path, info);
info.attachToProject(this);
this.markAsDirty();
}
}
removeFile(info: ScriptInfo, detachFromProject = true) {
this.removeRootFileIfNecessary(info);
this.lsHost.notifyFileRemoved(info);
if (detachFromProject) {
info.detachFromProject(this);
}
this.markAsDirty();
}
markAsDirty() {
this.projectStateVersion++;
}
/**
* Updates set of files that contribute to this project
* @returns: true if set of files in the project stays the same and false - otherwise.
*/
updateGraph(): boolean {
if (!this.languageServiceEnabled) {
return true;
}
let hasChanges = this.updateGraphWorker();
const cachedTypings = this.projectService.typingsCache.getTypingsForProject(this, hasChanges);
if (this.setTypings(cachedTypings)) {
hasChanges = this.updateGraphWorker() || hasChanges;
}
if (hasChanges) {
this.projectStructureVersion++;
}
return !hasChanges;
}
private setTypings(typings: TypingsArray): boolean {
if (arrayIsEqualTo(this.typingFiles, typings)) {
return false;
}
this.typingFiles = typings;
this.markAsDirty();
return true;
}
private updateGraphWorker() {
const oldProgram = this.program;
this.program = this.languageService.getProgram();
let hasChanges = false;
// 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)) {
hasChanges = true;
if (oldProgram) {
for (const f of oldProgram.getSourceFiles()) {
if (this.program.getSourceFileByPath(f.path)) {
continue;
}
// new program does not contain this file - detach it from the project
const scriptInfoToDetach = this.projectService.getScriptInfo(f.fileName);
if (scriptInfoToDetach) {
scriptInfoToDetach.detachFromProject(this);
}
}
}
}
this.builder.onProjectUpdateGraph();
return hasChanges;
}
getScriptInfoLSHost(fileName: string) {
const scriptInfo = this.projectService.getOrCreateScriptInfo(fileName, /*openedByClient*/ false);
if (scriptInfo) {
scriptInfo.attachToProject(this);
}
return scriptInfo;
}
getScriptInfoForNormalizedPath(fileName: NormalizedPath) {
const scriptInfo = this.projectService.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ false);
if (scriptInfo && !scriptInfo.isAttached(this)) {
return Errors.ThrowProjectDoesNotContainDocument(fileName, this);
}
return scriptInfo;
}
getScriptInfo(uncheckedFileName: string) {
return this.getScriptInfoForNormalizedPath(toNormalizedPath(uncheckedFileName));
}
filesToString() {
if (!this.program) {
return "";
}
let strBuilder = "";
for (const file of this.program.getSourceFiles()) {
strBuilder += `${file.fileName}\n`;
}
return strBuilder;
}
setCompilerOptions(compilerOptions: CompilerOptions) {
if (compilerOptions) {
if (this.projectKind === ProjectKind.Inferred) {
compilerOptions.allowJs = true;
}
compilerOptions.allowNonTsExtensions = true;
this.compilerOptions = compilerOptions;
this.lsHost.setCompilationSettings(compilerOptions);
this.markAsDirty();
}
}
reloadScript(filename: NormalizedPath): boolean {
const script = this.projectService.getScriptInfoForNormalizedPath(filename);
if (script) {
Debug.assert(script.isAttached(this));
script.reloadFromFile();
return true;
}
return false;
}
getChangesSinceVersion(lastKnownVersion?: number): ProjectFilesWithTSDiagnostics {
this.updateGraph();
const info = {
projectName: this.getProjectName(),
version: this.projectStructureVersion,
isInferred: this.projectKind === ProjectKind.Inferred,
options: this.getCompilerOptions()
};
// 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 witout any changes
if (this.projectStructureVersion == this.lastReportedVersion) {
return { info, projectErrors: this.projectErrors };
}
// compute and return the difference
const lastReportedFileNames = this.lastReportedFileNames;
const currentFiles = arrayToMap(this.getFileNames(), x => x);
const added: string[] = [];
const removed: string[] = [];
for (const id in currentFiles) {
if (!hasProperty(lastReportedFileNames, id)) {
added.push(id);
}
}
for (const id in lastReportedFileNames) {
if (!hasProperty(currentFiles, id)) {
removed.push(id);
}
}
this.lastReportedFileNames = currentFiles;
this.lastReportedVersion = this.projectStructureVersion;
return { info, changes: { added, removed }, projectErrors: this.projectErrors };
}
else {
// unknown version - return everything
const projectFileNames = this.getFileNames();
this.lastReportedFileNames = arrayToMap(projectFileNames, x => x);
this.lastReportedVersion = this.projectStructureVersion;
return { info, files: projectFileNames, projectErrors: this.projectErrors };
}
}
getReferencedFiles(path: Path): Path[] {
if (!this.languageServiceEnabled) {
return [];
}
const sourceFile = this.getSourceFile(path);
if (!sourceFile) {
return [];
}
// We need to use a set here since the code can contain the same import twice,
// but that will only be one dependency.
// To avoid invernal conversion, the key of the referencedFiles map must be of type Path
const referencedFiles = createMap<boolean>();
if (sourceFile.imports && sourceFile.imports.length > 0) {
const checker: TypeChecker = this.program.getTypeChecker();
for (const importName of sourceFile.imports) {
const symbol = checker.getSymbolAtLocation(importName);
if (symbol && symbol.declarations && symbol.declarations[0]) {
const declarationSourceFile = symbol.declarations[0].getSourceFile();
if (declarationSourceFile) {
referencedFiles[declarationSourceFile.path] = true;
}
}
}
}
const currentDirectory = getDirectoryPath(path);
const getCanonicalFileName = createGetCanonicalFileName(this.projectService.host.useCaseSensitiveFileNames);
// Handle triple slash references
if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) {
for (const referencedFile of sourceFile.referencedFiles) {
const referencedPath = toPath(referencedFile.fileName, currentDirectory, getCanonicalFileName);
referencedFiles[referencedPath] = true;
}
}
// Handle type reference directives
if (sourceFile.resolvedTypeReferenceDirectiveNames) {
for (const typeName in sourceFile.resolvedTypeReferenceDirectiveNames) {
const resolvedTypeReferenceDirective = sourceFile.resolvedTypeReferenceDirectiveNames[typeName];
if (!resolvedTypeReferenceDirective) {
continue;
}
const fileName = resolvedTypeReferenceDirective.resolvedFileName;
const typeFilePath = toPath(fileName, currentDirectory, getCanonicalFileName);
referencedFiles[typeFilePath] = true;
}
}
const allFileNames = map(Object.keys(referencedFiles), key => <Path>key);
return filter(allFileNames, file => this.projectService.host.fileExists(file));
}
// remove a root file from project
private removeRootFileIfNecessary(info: ScriptInfo): void {
if (this.isRoot(info)) {
remove(this.rootFiles, info);
this.rootFilesMap.remove(info.path);
}
}
}
export class InferredProject extends Project {
private static NextId = 1;
/**
* Unique name that identifies this particular inferred project
*/
private readonly inferredProjectName: string;
// Used to keep track of what directories are watched for this project
directoriesWatchedForTsconfig: string[] = [];
constructor(projectService: ProjectService, documentRegistry: ts.DocumentRegistry, languageServiceEnabled: boolean, compilerOptions: CompilerOptions, public compileOnSaveEnabled: boolean) {
super(ProjectKind.Inferred,
projectService,
documentRegistry,
/*files*/ undefined,
languageServiceEnabled,
compilerOptions,
compileOnSaveEnabled);
this.inferredProjectName = makeInferredProjectName(InferredProject.NextId);
InferredProject.NextId++;
}
getProjectName() {
return this.inferredProjectName;
}
getProjectRootPath() {
// Single inferred project does not have a project root.
if (this.projectService.useSingleInferredProject) {
return undefined;
}
const rootFiles = this.getRootFiles();
return getDirectoryPath(rootFiles[0]);
}
close() {
super.close();
for (const directory of this.directoriesWatchedForTsconfig) {
this.projectService.stopWatchingDirectory(directory);
}
}
getTypingOptions(): TypingOptions {
return {
enableAutoDiscovery: allRootFilesAreJsOrDts(this),
include: [],
exclude: []
};
}
}
export class ConfiguredProject extends Project {
private typingOptions: TypingOptions;
private projectFileWatcher: FileWatcher;
private directoryWatcher: FileWatcher;
private directoriesWatchedForWildcards: Map<FileWatcher>;
private typeRootsWatchers: FileWatcher[];
/** Used for configured projects which may have multiple open roots */
openRefCount = 0;
constructor(readonly configFileName: NormalizedPath,
projectService: ProjectService,
documentRegistry: ts.DocumentRegistry,
hasExplicitListOfFiles: boolean,
compilerOptions: CompilerOptions,
private wildcardDirectories: Map<WatchDirectoryFlags>,
languageServiceEnabled: boolean,
public compileOnSaveEnabled: boolean) {
super(ProjectKind.Configured, projectService, documentRegistry, hasExplicitListOfFiles, languageServiceEnabled, compilerOptions, compileOnSaveEnabled);
}
getProjectRootPath() {
return getDirectoryPath(this.configFileName);
}
setProjectErrors(projectErrors: Diagnostic[]) {
this.projectErrors = projectErrors;
}
setTypingOptions(newTypingOptions: TypingOptions): void {
this.typingOptions = newTypingOptions;
}
getTypingOptions() {
return this.typingOptions;
}
getProjectName() {
return this.configFileName;
}
watchConfigFile(callback: (project: ConfiguredProject) => void) {
this.projectFileWatcher = this.projectService.host.watchFile(this.configFileName, _ => callback(this));
}
watchTypeRoots(callback: (project: ConfiguredProject, path: string) => void) {
const roots = this.getEffectiveTypeRoots();
const watchers: FileWatcher[] = [];
for (const root of roots) {
this.projectService.logger.info(`Add type root watcher for: ${root}`);
watchers.push(this.projectService.host.watchDirectory(root, path => callback(this, path), /*recursive*/ false));
}
this.typeRootsWatchers = watchers;
}
watchConfigDirectory(callback: (project: ConfiguredProject, path: string) => void) {
if (this.directoryWatcher) {
return;
}
const directoryToWatch = getDirectoryPath(this.configFileName);
this.projectService.logger.info(`Add recursive watcher for: ${directoryToWatch}`);
this.directoryWatcher = this.projectService.host.watchDirectory(directoryToWatch, path => callback(this, path), /*recursive*/ true);
}
watchWildcards(callback: (project: ConfiguredProject, path: string) => void) {
if (!this.wildcardDirectories) {
return;
}
const configDirectoryPath = getDirectoryPath(this.configFileName);
this.directoriesWatchedForWildcards = reduceProperties(this.wildcardDirectories, (watchers, flag, directory) => {
if (comparePaths(configDirectoryPath, directory, ".", !this.projectService.host.useCaseSensitiveFileNames) !== Comparison.EqualTo) {
const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0;
this.projectService.logger.info(`Add ${recursive ? "recursive " : ""}watcher for: ${directory}`);
watchers[directory] = this.projectService.host.watchDirectory(
directory,
path => callback(this, path),
recursive
);
}
return watchers;
}, <Map<FileWatcher>>{});
}
stopWatchingDirectory() {
if (this.directoryWatcher) {
this.directoryWatcher.close();
this.directoryWatcher = undefined;
}
}
close() {
super.close();
if (this.projectFileWatcher) {
this.projectFileWatcher.close();
}
if (this.typeRootsWatchers) {
for (const watcher of this.typeRootsWatchers) {
watcher.close();
}
this.typeRootsWatchers = undefined;
}
for (const id in this.directoriesWatchedForWildcards) {
this.directoriesWatchedForWildcards[id].close();
}
this.directoriesWatchedForWildcards = undefined;
this.stopWatchingDirectory();
}
addOpenRef() {
this.openRefCount++;
}
deleteOpenRef() {
this.openRefCount--;
return this.openRefCount;
}
getEffectiveTypeRoots() {
return ts.getEffectiveTypeRoots(this.getCompilerOptions(), this.projectService.host) || [];
}
}
export class ExternalProject extends Project {
private typingOptions: TypingOptions;
constructor(readonly externalProjectName: string,
projectService: ProjectService,
documentRegistry: ts.DocumentRegistry,
compilerOptions: CompilerOptions,
languageServiceEnabled: boolean,
public compileOnSaveEnabled: boolean,
private readonly projectFilePath?: string) {
super(ProjectKind.External, projectService, documentRegistry, /*hasExplicitListOfFiles*/ true, languageServiceEnabled, compilerOptions, compileOnSaveEnabled);
}
getProjectRootPath() {
if (this.projectFilePath) {
return getDirectoryPath(this.projectFilePath);
}
// if the projectFilePath is not given, we make the assumption that the project name
// is the path of the project file. AS the project name is provided by VS, we need to
// normalize slashes before using it as a file name.
return getDirectoryPath(normalizeSlashes(this.externalProjectName));
}
getTypingOptions() {
return this.typingOptions;
}
setProjectErrors(projectErrors: Diagnostic[]) {
this.projectErrors = projectErrors;
}
setTypingOptions(newTypingOptions: TypingOptions): void {
if (!newTypingOptions) {
// set default typings options
newTypingOptions = {
enableAutoDiscovery: allRootFilesAreJsOrDts(this),
include: [],
exclude: []
};
}
else {
if (newTypingOptions.enableAutoDiscovery === undefined) {
// if autoDiscovery was not specified by the caller - set it based on the content of the project
newTypingOptions.enableAutoDiscovery = allRootFilesAreJsOrDts(this);
}
if (!newTypingOptions.include) {
newTypingOptions.include = [];
}
if (!newTypingOptions.exclude) {
newTypingOptions.exclude = [];
}
}
this.typingOptions = newTypingOptions;
}
getProjectName() {
return this.externalProjectName;
}
}
}

View File

@ -91,6 +91,27 @@ declare namespace ts.server.protocol {
* The file for the request (absolute pathname required).
*/
file: string;
/*
* Optional name of project that contains file
*/
projectFileName?: string;
}
export interface TodoCommentRequest extends FileRequest {
arguments: TodoCommentRequestArgs;
}
export interface TodoCommentRequestArgs extends FileRequestArgs {
descriptors: TodoCommentDescriptor[];
}
export interface IndentationRequest extends FileLocationRequest {
arguments: IndentationRequestArgs;
}
export interface IndentationRequestArgs extends FileLocationRequestArgs {
options?: EditorSettings;
}
/**
@ -110,6 +131,14 @@ declare namespace ts.server.protocol {
arguments: ProjectInfoRequestArgs;
}
export interface ProjectRequest extends Request {
arguments: ProjectRequestArgs;
}
export interface ProjectRequestArgs {
projectFileName: string;
}
/**
* Response message body for "projectInfo" request
*/
@ -129,6 +158,16 @@ declare namespace ts.server.protocol {
languageServiceDisabled?: boolean;
}
export interface DiagnosticWithLinePosition {
message: string;
start: number;
length: number;
startLocation: Location;
endLocation: Location;
category: string;
code: number;
}
/**
* Response message for "projectInfo" request
*/
@ -151,12 +190,17 @@ declare namespace ts.server.protocol {
/**
* The line number for the request (1-based).
*/
line: number;
line?: number;
/**
* The character offset (on the line) for the request (1-based).
*/
offset: number;
offset?: number;
/**
* Position (can be specified instead of line/offset pair)
*/
position?: number;
}
/**
@ -166,6 +210,15 @@ declare namespace ts.server.protocol {
arguments: FileLocationRequestArgs;
}
export interface FileSpanRequestArgs extends FileRequestArgs {
start: number;
length: number;
}
export interface FileSpanRequest extends FileRequest {
arguments: FileSpanRequestArgs;
}
/**
* Arguments in document highlight request; include: filesToSearch, file,
* line, offset.
@ -255,6 +308,14 @@ declare namespace ts.server.protocol {
body?: FileSpan[];
}
export interface BraceCompletionRequest extends FileLocationRequest {
arguments: BraceCompletionRequestArgs;
}
export interface BraceCompletionRequestArgs extends FileLocationRequestArgs {
openingBrace: string;
}
/**
* Get occurrences request; value of command field is
* "occurrences". Return response giving spans that are relevant
@ -442,6 +503,62 @@ declare namespace ts.server.protocol {
body?: RenameResponseBody;
}
export interface ExternalFile {
fileName: string;
scriptKind?: ScriptKind;
hasMixedContent?: boolean;
content?: string;
}
export interface ExternalProject {
projectFileName: string;
rootFiles: ExternalFile[];
options: ExternalProjectCompilerOptions;
typingOptions?: TypingOptions;
}
/**
* For external projects, some of the project settings are sent together with
* compiler settings.
*/
export interface ExternalProjectCompilerOptions extends CompilerOptions {
compileOnSave?: boolean;
}
export interface ProjectVersionInfo {
projectName: string;
isInferred: boolean;
version: number;
options: CompilerOptions;
}
export interface ProjectChanges {
added: string[];
removed: string[];
}
/**
* Describes set of files in the project.
* info might be omitted in case of inferred projects
* if files is set - then this is the entire set of files in the project
* if changes is set - then this is the set of changes that should be applied to existing project
* otherwise - assume that nothing is changed
*/
export interface ProjectFiles {
info?: ProjectVersionInfo;
files?: string[];
changes?: ProjectChanges;
}
export interface ProjectFilesWithDiagnostics extends ProjectFiles {
projectErrors: DiagnosticWithLinePosition[];
}
export interface ChangedOpenFile {
fileName: string;
changes: ts.TextChange[];
}
/**
* Editor options
*/
@ -494,9 +611,6 @@ declare namespace ts.server.protocol {
/** Defines whether an open brace is put onto a new line for control blocks or not. Default value is false. */
placeOpenBraceOnNewLineForControlBlocks?: boolean;
/** Index operator */
[key: string]: string | number | boolean | undefined;
}
/**
@ -519,6 +633,11 @@ declare namespace ts.server.protocol {
* The format options to use during formatting and other code editing features.
*/
formatOptions?: FormatOptions;
/**
* If set to true - then all loose files will land into one inferred project
*/
useOneInferredProject?: boolean;
}
/**
@ -564,6 +683,54 @@ declare namespace ts.server.protocol {
arguments: OpenRequestArgs;
}
type OpenExternalProjectArgs = ExternalProject;
export interface OpenExternalProjectRequest extends Request {
arguments: OpenExternalProjectArgs;
}
export interface CloseExternalProjectRequestArgs {
projectFileName: string;
}
export interface OpenExternalProjectsRequest extends Request {
arguments: OpenExternalProjectsArgs;
}
export interface OpenExternalProjectsArgs {
projects: ExternalProject[];
}
export interface CloseExternalProjectRequest extends Request {
arguments: CloseExternalProjectRequestArgs;
}
export interface SynchronizeProjectListRequest extends Request {
arguments: SynchronizeProjectListRequestArgs;
}
export interface SynchronizeProjectListRequestArgs {
knownProjects: protocol.ProjectVersionInfo[];
}
export interface ApplyChangedToOpenFilesRequest extends Request {
arguments: ApplyChangedToOpenFilesRequestArgs;
}
export interface ApplyChangedToOpenFilesRequestArgs {
openFiles?: ExternalFile[];
changedFiles?: ChangedOpenFile[];
closedFiles?: string[];
}
export interface SetCompilerOptionsForInferredProjectsArgs {
options: ExternalProjectCompilerOptions;
}
export interface SetCompilerOptionsForInferredProjectsRequest extends Request {
arguments: SetCompilerOptionsForInferredProjectsArgs;
}
/**
* Exit request; value of command field is "exit". Ask the server process
* to exit.
@ -581,6 +748,26 @@ declare namespace ts.server.protocol {
export interface CloseRequest extends FileRequest {
}
export interface CompileOnSaveAffectedFileListRequest extends FileRequest {
}
export interface CompileOnSaveAffectedFileListSingleProject {
projectFileName: string;
fileNames: string[];
}
export interface CompileOnSaveAffectedFileListResponse extends Response {
body: CompileOnSaveAffectedFileListSingleProject[];
}
export interface CompileOnSaveEmitFileRequest extends FileRequest {
args: CompileOnSaveEmitFileRequestArgs;
}
export interface CompileOnSaveEmitFileRequestArgs extends FileRequestArgs {
forced?: boolean;
}
/**
* Quickinfo request; value of command field is
* "quickinfo". Return response giving a quick type and
@ -645,6 +832,9 @@ declare namespace ts.server.protocol {
* Character offset on last line of range for which to format text in file.
*/
endOffset: number;
endPosition?: number;
options?: ts.FormatCodeOptions;
}
/**
@ -698,6 +888,8 @@ declare namespace ts.server.protocol {
* Key pressed (';', '\n', or '}').
*/
key: string;
options?: ts.FormatCodeOptions;
}
/**
@ -949,26 +1141,36 @@ declare namespace ts.server.protocol {
* Synchronous request for semantic diagnostics of one file.
*/
export interface SemanticDiagnosticsSyncRequest extends FileRequest {
arguments: SemanticDiagnosticsSyncRequestArgs;
}
export interface SemanticDiagnosticsSyncRequestArgs extends FileRequestArgs {
includeLinePosition?: boolean;
}
/**
* Response object for synchronous sematic diagnostics request.
*/
export interface SemanticDiagnosticsSyncResponse extends Response {
body?: Diagnostic[];
body?: Diagnostic[] | DiagnosticWithLinePosition[];
}
/**
* Synchronous request for syntactic diagnostics of one file.
*/
export interface SyntacticDiagnosticsSyncRequest extends FileRequest {
arguments: SyntacticDiagnosticsSyncRequestArgs;
}
export interface SyntacticDiagnosticsSyncRequestArgs extends FileRequestArgs {
includeLinePosition?: boolean;
}
/**
* Response object for synchronous syntactic diagnostics request.
*/
export interface SyntacticDiagnosticsSyncResponse extends Response {
body?: Diagnostic[];
body?: Diagnostic[] | DiagnosticWithLinePosition[];
}
/**
@ -1161,6 +1363,8 @@ declare namespace ts.server.protocol {
* or the entire project.
*/
currentFileOnly?: boolean;
projectFileName?: string;
}
/**

198
src/server/scriptInfo.ts Normal file
View File

@ -0,0 +1,198 @@
/// <reference path="scriptVersionCache.ts"/>
namespace ts.server {
export class ScriptInfo {
/**
* All projects that include this file
*/
readonly containingProjects: Project[] = [];
private formatCodeSettings: ts.FormatCodeSettings;
readonly path: Path;
private fileWatcher: FileWatcher;
private svc: ScriptVersionCache;
// TODO: allow to update hasMixedContent from the outside
constructor(
private readonly host: ServerHost,
readonly fileName: NormalizedPath,
content: string,
readonly scriptKind: ScriptKind,
public isOpen = false,
public hasMixedContent = false) {
this.path = toPath(fileName, host.getCurrentDirectory(), createGetCanonicalFileName(host.useCaseSensitiveFileNames));
this.svc = ScriptVersionCache.fromString(host, content);
this.scriptKind = scriptKind
? scriptKind
: getScriptKindFromFileName(fileName);
}
getFormatCodeSettings() {
return this.formatCodeSettings;
}
attachToProject(project: Project): boolean {
const isNew = !this.isAttached(project);
if (isNew) {
this.containingProjects.push(project);
}
return isNew;
}
isAttached(project: Project) {
// unrolled for common cases
switch (this.containingProjects.length) {
case 0: return false;
case 1: return this.containingProjects[0] === project;
case 2: return this.containingProjects[0] === project || this.containingProjects[1] === project;
default: return contains(this.containingProjects, project);
}
}
detachFromProject(project: Project) {
// unrolled for common cases
switch (this.containingProjects.length) {
case 0:
return;
case 1:
if (this.containingProjects[0] === project) {
this.containingProjects.pop();
}
break;
case 2:
if (this.containingProjects[0] === project) {
this.containingProjects[0] = this.containingProjects.pop();
}
else if (this.containingProjects[1] === project) {
this.containingProjects.pop();
}
break;
default:
removeItemFromSet(this.containingProjects, project);
break;
}
}
detachAllProjects() {
for (const p of this.containingProjects) {
// detach is unnecessary since we'll clean the list of containing projects anyways
p.removeFile(this, /*detachFromProjects*/ false);
}
this.containingProjects.length = 0;
}
getDefaultProject() {
if (this.containingProjects.length === 0) {
return Errors.ThrowNoProject();
}
Debug.assert(this.containingProjects.length !== 0);
return this.containingProjects[0];
}
setFormatOptions(formatSettings: protocol.FormatOptions): void {
if (formatSettings) {
if (!this.formatCodeSettings) {
this.formatCodeSettings = getDefaultFormatCodeSettings(this.host);
}
mergeMaps(this.formatCodeSettings, formatSettings);
}
}
setWatcher(watcher: FileWatcher): void {
this.stopWatcher();
this.fileWatcher = watcher;
}
stopWatcher() {
if (this.fileWatcher) {
this.fileWatcher.close();
this.fileWatcher = undefined;
}
}
getLatestVersion() {
return this.svc.latestVersion().toString();
}
reload(script: string) {
this.svc.reload(script);
this.markContainingProjectsAsDirty();
}
saveTo(fileName: string) {
const snap = this.snap();
this.host.writeFile(fileName, snap.getText(0, snap.getLength()));
}
reloadFromFile() {
if (this.hasMixedContent) {
this.reload("");
}
else {
this.svc.reloadFromFile(this.fileName);
this.markContainingProjectsAsDirty();
}
}
snap() {
return this.svc.getSnapshot();
}
getLineInfo(line: number) {
const snap = this.snap();
return snap.index.lineNumberToInfo(line);
}
editContent(start: number, end: number, newText: string): void {
this.svc.edit(start, end - start, newText);
this.markContainingProjectsAsDirty();
}
markContainingProjectsAsDirty() {
for (const p of this.containingProjects) {
p.markAsDirty();
}
}
/**
* @param line 1 based index
*/
lineToTextSpan(line: number) {
const index = this.snap().index;
const lineInfo = index.lineNumberToInfo(line + 1);
let len: number;
if (lineInfo.leaf) {
len = lineInfo.leaf.text.length;
}
else {
const nextLineInfo = index.lineNumberToInfo(line + 2);
len = nextLineInfo.offset - lineInfo.offset;
}
return ts.createTextSpan(lineInfo.offset, len);
}
/**
* @param line 1 based index
* @param offset 1 based index
*/
lineOffsetToPosition(line: number, offset: number): number {
const index = this.snap().index;
const lineInfo = index.lineNumberToInfo(line);
// TODO: assert this offset is actually on the line
return (lineInfo.offset + offset - 1);
}
/**
* @param line 1-based index
* @param offset 1-based index
*/
positionToLineOffset(position: number): ILineInfo {
const index = this.snap().index;
const lineOffset = index.charOffsetToLineNumberAndPos(position);
return { line: lineOffset.line, offset: lineOffset.offset + 1 };
}
}
}

View File

@ -0,0 +1,951 @@
/// <reference path="..\compiler\commandLineParser.ts" />
/// <reference path="..\services\services.ts" />
/// <reference path="protocol.d.ts" />
/// <reference path="session.ts" />
namespace ts.server {
const lineCollectionCapacity = 4;
export interface LineCollection {
charCount(): number;
lineCount(): number;
isLeaf(): boolean;
walk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker): void;
}
export interface ILineInfo {
line: number;
offset: number;
text?: string;
leaf?: LineLeaf;
}
export enum CharRangeSection {
PreStart,
Start,
Entire,
Mid,
End,
PostEnd
}
export interface ILineIndexWalker {
goSubtree: boolean;
done: boolean;
leaf(relativeStart: number, relativeLength: number, lineCollection: LineLeaf): void;
pre?(relativeStart: number, relativeLength: number, lineCollection: LineCollection,
parent: LineNode, nodeType: CharRangeSection): LineCollection;
post?(relativeStart: number, relativeLength: number, lineCollection: LineCollection,
parent: LineNode, nodeType: CharRangeSection): LineCollection;
}
class BaseLineIndexWalker implements ILineIndexWalker {
goSubtree = true;
done = false;
leaf(rangeStart: number, rangeLength: number, ll: LineLeaf) {
}
}
class EditWalker extends BaseLineIndexWalker {
lineIndex = new LineIndex();
// path to start of range
startPath: LineCollection[];
endBranch: LineCollection[] = [];
branchNode: LineNode;
// path to current node
stack: LineNode[];
state = CharRangeSection.Entire;
lineCollectionAtBranch: LineCollection;
initialText = "";
trailingText = "";
suppressTrailingText = false;
constructor() {
super();
this.lineIndex.root = new LineNode();
this.startPath = [this.lineIndex.root];
this.stack = [this.lineIndex.root];
}
insertLines(insertedText: string) {
if (this.suppressTrailingText) {
this.trailingText = "";
}
if (insertedText) {
insertedText = this.initialText + insertedText + this.trailingText;
}
else {
insertedText = this.initialText + this.trailingText;
}
const lm = LineIndex.linesFromText(insertedText);
const lines = lm.lines;
if (lines.length > 1) {
if (lines[lines.length - 1] == "") {
lines.length--;
}
}
let branchParent: LineNode;
let lastZeroCount: LineCollection;
for (let k = this.endBranch.length - 1; k >= 0; k--) {
(<LineNode>this.endBranch[k]).updateCounts();
if (this.endBranch[k].charCount() === 0) {
lastZeroCount = this.endBranch[k];
if (k > 0) {
branchParent = <LineNode>this.endBranch[k - 1];
}
else {
branchParent = this.branchNode;
}
}
}
if (lastZeroCount) {
branchParent.remove(lastZeroCount);
}
// path at least length two (root and leaf)
let insertionNode = <LineNode>this.startPath[this.startPath.length - 2];
const leafNode = <LineLeaf>this.startPath[this.startPath.length - 1];
const len = lines.length;
if (len > 0) {
leafNode.text = lines[0];
if (len > 1) {
let insertedNodes = <LineCollection[]>new Array(len - 1);
let startNode = <LineCollection>leafNode;
for (let i = 1, len = lines.length; i < len; i++) {
insertedNodes[i - 1] = new LineLeaf(lines[i]);
}
let pathIndex = this.startPath.length - 2;
while (pathIndex >= 0) {
insertionNode = <LineNode>this.startPath[pathIndex];
insertedNodes = insertionNode.insertAt(startNode, insertedNodes);
pathIndex--;
startNode = insertionNode;
}
let insertedNodesLen = insertedNodes.length;
while (insertedNodesLen > 0) {
const newRoot = new LineNode();
newRoot.add(this.lineIndex.root);
insertedNodes = newRoot.insertAt(this.lineIndex.root, insertedNodes);
insertedNodesLen = insertedNodes.length;
this.lineIndex.root = newRoot;
}
this.lineIndex.root.updateCounts();
}
else {
for (let j = this.startPath.length - 2; j >= 0; j--) {
(<LineNode>this.startPath[j]).updateCounts();
}
}
}
else {
// no content for leaf node, so delete it
insertionNode.remove(leafNode);
for (let j = this.startPath.length - 2; j >= 0; j--) {
(<LineNode>this.startPath[j]).updateCounts();
}
}
return this.lineIndex;
}
post(relativeStart: number, relativeLength: number, lineCollection: LineCollection, parent: LineCollection, nodeType: CharRangeSection): LineCollection {
// have visited the path for start of range, now looking for end
// if range is on single line, we will never make this state transition
if (lineCollection === this.lineCollectionAtBranch) {
this.state = CharRangeSection.End;
}
// always pop stack because post only called when child has been visited
this.stack.length--;
return undefined;
}
pre(relativeStart: number, relativeLength: number, lineCollection: LineCollection, parent: LineCollection, nodeType: CharRangeSection) {
// currentNode corresponds to parent, but in the new tree
const currentNode = this.stack[this.stack.length - 1];
if ((this.state === CharRangeSection.Entire) && (nodeType === CharRangeSection.Start)) {
// if range is on single line, we will never make this state transition
this.state = CharRangeSection.Start;
this.branchNode = currentNode;
this.lineCollectionAtBranch = lineCollection;
}
let child: LineCollection;
function fresh(node: LineCollection): LineCollection {
if (node.isLeaf()) {
return new LineLeaf("");
}
else return new LineNode();
}
switch (nodeType) {
case CharRangeSection.PreStart:
this.goSubtree = false;
if (this.state !== CharRangeSection.End) {
currentNode.add(lineCollection);
}
break;
case CharRangeSection.Start:
if (this.state === CharRangeSection.End) {
this.goSubtree = false;
}
else {
child = fresh(lineCollection);
currentNode.add(child);
this.startPath[this.startPath.length] = child;
}
break;
case CharRangeSection.Entire:
if (this.state !== CharRangeSection.End) {
child = fresh(lineCollection);
currentNode.add(child);
this.startPath[this.startPath.length] = child;
}
else {
if (!lineCollection.isLeaf()) {
child = fresh(lineCollection);
currentNode.add(child);
this.endBranch[this.endBranch.length] = child;
}
}
break;
case CharRangeSection.Mid:
this.goSubtree = false;
break;
case CharRangeSection.End:
if (this.state !== CharRangeSection.End) {
this.goSubtree = false;
}
else {
if (!lineCollection.isLeaf()) {
child = fresh(lineCollection);
currentNode.add(child);
this.endBranch[this.endBranch.length] = child;
}
}
break;
case CharRangeSection.PostEnd:
this.goSubtree = false;
if (this.state !== CharRangeSection.Start) {
currentNode.add(lineCollection);
}
break;
}
if (this.goSubtree) {
this.stack[this.stack.length] = <LineNode>child;
}
return lineCollection;
}
// just gather text from the leaves
leaf(relativeStart: number, relativeLength: number, ll: LineLeaf) {
if (this.state === CharRangeSection.Start) {
this.initialText = ll.text.substring(0, relativeStart);
}
else if (this.state === CharRangeSection.Entire) {
this.initialText = ll.text.substring(0, relativeStart);
this.trailingText = ll.text.substring(relativeStart + relativeLength);
}
else {
// state is CharRangeSection.End
this.trailingText = ll.text.substring(relativeStart + relativeLength);
}
}
}
// text change information
export class TextChange {
constructor(public pos: number, public deleteLen: number, public insertedText?: string) {
}
getTextChangeRange() {
return ts.createTextChangeRange(ts.createTextSpan(this.pos, this.deleteLen),
this.insertedText ? this.insertedText.length : 0);
}
}
export class ScriptVersionCache {
changes: TextChange[] = [];
versions: LineIndexSnapshot[] = new Array<LineIndexSnapshot>(ScriptVersionCache.maxVersions);
minVersion = 0; // no versions earlier than min version will maintain change history
private host: ServerHost;
private currentVersion = 0;
static changeNumberThreshold = 8;
static changeLengthThreshold = 256;
static maxVersions = 8;
private versionToIndex(version: number) {
if (version < this.minVersion || version > this.currentVersion) {
return undefined;
}
return version % ScriptVersionCache.maxVersions;
}
private currentVersionToIndex() {
return this.currentVersion % ScriptVersionCache.maxVersions;
}
// REVIEW: can optimize by coalescing simple edits
edit(pos: number, deleteLen: number, insertedText?: string) {
this.changes[this.changes.length] = new TextChange(pos, deleteLen, insertedText);
if ((this.changes.length > ScriptVersionCache.changeNumberThreshold) ||
(deleteLen > ScriptVersionCache.changeLengthThreshold) ||
(insertedText && (insertedText.length > ScriptVersionCache.changeLengthThreshold))) {
this.getSnapshot();
}
}
latest() {
return this.versions[this.currentVersionToIndex()];
}
latestVersion() {
if (this.changes.length > 0) {
this.getSnapshot();
}
return this.currentVersion;
}
reloadFromFile(filename: string) {
let content = this.host.readFile(filename);
// If the file doesn't exist or cannot be read, we should
// wipe out its cached content on the server to avoid side effects.
if (!content) {
content = "";
}
this.reload(content);
}
// reload whole script, leaving no change history behind reload
reload(script: string) {
this.currentVersion++;
this.changes = []; // history wiped out by reload
const snap = new LineIndexSnapshot(this.currentVersion, this);
// delete all versions
for (let i = 0; i < this.versions.length; i++) {
this.versions[i] = undefined;
}
this.versions[this.currentVersionToIndex()] = snap;
snap.index = new LineIndex();
const lm = LineIndex.linesFromText(script);
snap.index.load(lm.lines);
this.minVersion = this.currentVersion;
}
getSnapshot() {
let snap = this.versions[this.currentVersionToIndex()];
if (this.changes.length > 0) {
let snapIndex = snap.index;
for (let i = 0, len = this.changes.length; i < len; i++) {
const change = this.changes[i];
snapIndex = snapIndex.edit(change.pos, change.deleteLen, change.insertedText);
}
snap = new LineIndexSnapshot(this.currentVersion + 1, this);
snap.index = snapIndex;
snap.changesSincePreviousVersion = this.changes;
this.currentVersion = snap.version;
this.versions[this.currentVersionToIndex()] = snap;
this.changes = [];
if ((this.currentVersion - this.minVersion) >= ScriptVersionCache.maxVersions) {
this.minVersion = (this.currentVersion - ScriptVersionCache.maxVersions) + 1;
}
}
return snap;
}
getTextChangesBetweenVersions(oldVersion: number, newVersion: number) {
if (oldVersion < newVersion) {
if (oldVersion >= this.minVersion) {
const textChangeRanges: ts.TextChangeRange[] = [];
for (let i = oldVersion + 1; i <= newVersion; i++) {
const snap = this.versions[this.versionToIndex(i)];
for (let j = 0, len = snap.changesSincePreviousVersion.length; j < len; j++) {
const textChange = snap.changesSincePreviousVersion[j];
textChangeRanges[textChangeRanges.length] = textChange.getTextChangeRange();
}
}
return ts.collapseTextChangeRangesAcrossMultipleVersions(textChangeRanges);
}
else {
return undefined;
}
}
else {
return ts.unchangedTextChangeRange;
}
}
static fromString(host: ServerHost, script: string) {
const svc = new ScriptVersionCache();
const snap = new LineIndexSnapshot(0, svc);
svc.versions[svc.currentVersion] = snap;
svc.host = host;
snap.index = new LineIndex();
const lm = LineIndex.linesFromText(script);
snap.index.load(lm.lines);
return svc;
}
}
export class LineIndexSnapshot implements ts.IScriptSnapshot {
index: LineIndex;
changesSincePreviousVersion: TextChange[] = [];
constructor(public version: number, public cache: ScriptVersionCache) {
}
getText(rangeStart: number, rangeEnd: number) {
return this.index.getText(rangeStart, rangeEnd - rangeStart);
}
getLength() {
return this.index.root.charCount();
}
// this requires linear space so don't hold on to these
getLineStartPositions(): number[] {
const starts: number[] = [-1];
let count = 1;
let pos = 0;
this.index.every((ll, s, len) => {
starts[count] = pos;
count++;
pos += ll.text.length;
return true;
}, 0);
return starts;
}
getLineMapper() {
return (line: number) => {
return this.index.lineNumberToInfo(line).offset;
};
}
getTextChangeRangeSinceVersion(scriptVersion: number) {
if (this.version <= scriptVersion) {
return ts.unchangedTextChangeRange;
}
else {
return this.cache.getTextChangesBetweenVersions(scriptVersion, this.version);
}
}
getChangeRange(oldSnapshot: ts.IScriptSnapshot): ts.TextChangeRange {
const oldSnap = <LineIndexSnapshot>oldSnapshot;
return this.getTextChangeRangeSinceVersion(oldSnap.version);
}
}
export class LineIndex {
root: LineNode;
// set this to true to check each edit for accuracy
checkEdits = false;
charOffsetToLineNumberAndPos(charOffset: number) {
return this.root.charOffsetToLineNumberAndPos(1, charOffset);
}
lineNumberToInfo(lineNumber: number): ILineInfo {
const lineCount = this.root.lineCount();
if (lineNumber <= lineCount) {
const lineInfo = this.root.lineNumberToInfo(lineNumber, 0);
lineInfo.line = lineNumber;
return lineInfo;
}
else {
return {
line: lineNumber,
offset: this.root.charCount()
};
}
}
load(lines: string[]) {
if (lines.length > 0) {
const leaves: LineLeaf[] = [];
for (let i = 0, len = lines.length; i < len; i++) {
leaves[i] = new LineLeaf(lines[i]);
}
this.root = LineIndex.buildTreeFromBottom(leaves);
}
else {
this.root = new LineNode();
}
}
walk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker) {
this.root.walk(rangeStart, rangeLength, walkFns);
}
getText(rangeStart: number, rangeLength: number) {
let accum = "";
if ((rangeLength > 0) && (rangeStart < this.root.charCount())) {
this.walk(rangeStart, rangeLength, {
goSubtree: true,
done: false,
leaf: (relativeStart: number, relativeLength: number, ll: LineLeaf) => {
accum = accum.concat(ll.text.substring(relativeStart, relativeStart + relativeLength));
}
});
}
return accum;
}
getLength(): number {
return this.root.charCount();
}
every(f: (ll: LineLeaf, s: number, len: number) => boolean, rangeStart: number, rangeEnd?: number) {
if (!rangeEnd) {
rangeEnd = this.root.charCount();
}
const walkFns = {
goSubtree: true,
done: false,
leaf: function (this: ILineIndexWalker, relativeStart: number, relativeLength: number, ll: LineLeaf) {
if (!f(ll, relativeStart, relativeLength)) {
this.done = true;
}
}
};
this.walk(rangeStart, rangeEnd - rangeStart, walkFns);
return !walkFns.done;
}
edit(pos: number, deleteLength: number, newText?: string) {
function editFlat(source: string, s: number, dl: number, nt = "") {
return source.substring(0, s) + nt + source.substring(s + dl, source.length);
}
if (this.root.charCount() === 0) {
// TODO: assert deleteLength === 0
if (newText !== undefined) {
this.load(LineIndex.linesFromText(newText).lines);
return this;
}
}
else {
let checkText: string;
if (this.checkEdits) {
checkText = editFlat(this.getText(0, this.root.charCount()), pos, deleteLength, newText);
}
const walker = new EditWalker();
if (pos >= this.root.charCount()) {
// insert at end
pos = this.root.charCount() - 1;
const endString = this.getText(pos, 1);
if (newText) {
newText = endString + newText;
}
else {
newText = endString;
}
deleteLength = 0;
walker.suppressTrailingText = true;
}
else if (deleteLength > 0) {
// check whether last characters deleted are line break
const e = pos + deleteLength;
const lineInfo = this.charOffsetToLineNumberAndPos(e);
if ((lineInfo && (lineInfo.offset === 0))) {
// move range end just past line that will merge with previous line
deleteLength += lineInfo.text.length;
// store text by appending to end of insertedText
if (newText) {
newText = newText + lineInfo.text;
}
else {
newText = lineInfo.text;
}
}
}
if (pos < this.root.charCount()) {
this.root.walk(pos, deleteLength, walker);
walker.insertLines(newText);
}
if (this.checkEdits) {
const updatedText = this.getText(0, this.root.charCount());
Debug.assert(checkText == updatedText, "buffer edit mismatch");
}
return walker.lineIndex;
}
}
static buildTreeFromBottom(nodes: LineCollection[]): LineNode {
const nodeCount = Math.ceil(nodes.length / lineCollectionCapacity);
const interiorNodes: LineNode[] = [];
let nodeIndex = 0;
for (let i = 0; i < nodeCount; i++) {
interiorNodes[i] = new LineNode();
let charCount = 0;
let lineCount = 0;
for (let j = 0; j < lineCollectionCapacity; j++) {
if (nodeIndex < nodes.length) {
interiorNodes[i].add(nodes[nodeIndex]);
charCount += nodes[nodeIndex].charCount();
lineCount += nodes[nodeIndex].lineCount();
}
else {
break;
}
nodeIndex++;
}
interiorNodes[i].totalChars = charCount;
interiorNodes[i].totalLines = lineCount;
}
if (interiorNodes.length === 1) {
return interiorNodes[0];
}
else {
return this.buildTreeFromBottom(interiorNodes);
}
}
static linesFromText(text: string) {
const lineStarts = ts.computeLineStarts(text);
if (lineStarts.length === 0) {
return { lines: <string[]>[], lineMap: lineStarts };
}
const lines = <string[]>new Array(lineStarts.length);
const lc = lineStarts.length - 1;
for (let lmi = 0; lmi < lc; lmi++) {
lines[lmi] = text.substring(lineStarts[lmi], lineStarts[lmi + 1]);
}
const endText = text.substring(lineStarts[lc]);
if (endText.length > 0) {
lines[lc] = endText;
}
else {
lines.length--;
}
return { lines: lines, lineMap: lineStarts };
}
}
export class LineNode implements LineCollection {
totalChars = 0;
totalLines = 0;
children: LineCollection[] = [];
isLeaf() {
return false;
}
updateCounts() {
this.totalChars = 0;
this.totalLines = 0;
for (let i = 0, len = this.children.length; i < len; i++) {
const child = this.children[i];
this.totalChars += child.charCount();
this.totalLines += child.lineCount();
}
}
execWalk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker, childIndex: number, nodeType: CharRangeSection) {
if (walkFns.pre) {
walkFns.pre(rangeStart, rangeLength, this.children[childIndex], this, nodeType);
}
if (walkFns.goSubtree) {
this.children[childIndex].walk(rangeStart, rangeLength, walkFns);
if (walkFns.post) {
walkFns.post(rangeStart, rangeLength, this.children[childIndex], this, nodeType);
}
}
else {
walkFns.goSubtree = true;
}
return walkFns.done;
}
skipChild(relativeStart: number, relativeLength: number, childIndex: number, walkFns: ILineIndexWalker, nodeType: CharRangeSection) {
if (walkFns.pre && (!walkFns.done)) {
walkFns.pre(relativeStart, relativeLength, this.children[childIndex], this, nodeType);
walkFns.goSubtree = true;
}
}
walk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker) {
// assume (rangeStart < this.totalChars) && (rangeLength <= this.totalChars)
let childIndex = 0;
let child = this.children[0];
let childCharCount = child.charCount();
// find sub-tree containing start
let adjustedStart = rangeStart;
while (adjustedStart >= childCharCount) {
this.skipChild(adjustedStart, rangeLength, childIndex, walkFns, CharRangeSection.PreStart);
adjustedStart -= childCharCount;
childIndex++;
child = this.children[childIndex];
childCharCount = child.charCount();
}
// Case I: both start and end of range in same subtree
if ((adjustedStart + rangeLength) <= childCharCount) {
if (this.execWalk(adjustedStart, rangeLength, walkFns, childIndex, CharRangeSection.Entire)) {
return;
}
}
else {
// Case II: start and end of range in different subtrees (possibly with subtrees in the middle)
if (this.execWalk(adjustedStart, childCharCount - adjustedStart, walkFns, childIndex, CharRangeSection.Start)) {
return;
}
let adjustedLength = rangeLength - (childCharCount - adjustedStart);
childIndex++;
child = this.children[childIndex];
childCharCount = child.charCount();
while (adjustedLength > childCharCount) {
if (this.execWalk(0, childCharCount, walkFns, childIndex, CharRangeSection.Mid)) {
return;
}
adjustedLength -= childCharCount;
childIndex++;
child = this.children[childIndex];
childCharCount = child.charCount();
}
if (adjustedLength > 0) {
if (this.execWalk(0, adjustedLength, walkFns, childIndex, CharRangeSection.End)) {
return;
}
}
}
// Process any subtrees after the one containing range end
if (walkFns.pre) {
const clen = this.children.length;
if (childIndex < (clen - 1)) {
for (let ej = childIndex + 1; ej < clen; ej++) {
this.skipChild(0, 0, ej, walkFns, CharRangeSection.PostEnd);
}
}
}
}
charOffsetToLineNumberAndPos(lineNumber: number, charOffset: number): ILineInfo {
const childInfo = this.childFromCharOffset(lineNumber, charOffset);
if (!childInfo.child) {
return {
line: lineNumber,
offset: charOffset,
};
}
else if (childInfo.childIndex < this.children.length) {
if (childInfo.child.isLeaf()) {
return {
line: childInfo.lineNumber,
offset: childInfo.charOffset,
text: (<LineLeaf>(childInfo.child)).text,
leaf: (<LineLeaf>(childInfo.child))
};
}
else {
const lineNode = <LineNode>(childInfo.child);
return lineNode.charOffsetToLineNumberAndPos(childInfo.lineNumber, childInfo.charOffset);
}
}
else {
const lineInfo = this.lineNumberToInfo(this.lineCount(), 0);
return { line: this.lineCount(), offset: lineInfo.leaf.charCount() };
}
}
lineNumberToInfo(lineNumber: number, charOffset: number): ILineInfo {
const childInfo = this.childFromLineNumber(lineNumber, charOffset);
if (!childInfo.child) {
return {
line: lineNumber,
offset: charOffset
};
}
else if (childInfo.child.isLeaf()) {
return {
line: lineNumber,
offset: childInfo.charOffset,
text: (<LineLeaf>(childInfo.child)).text,
leaf: (<LineLeaf>(childInfo.child))
};
}
else {
const lineNode = <LineNode>(childInfo.child);
return lineNode.lineNumberToInfo(childInfo.relativeLineNumber, childInfo.charOffset);
}
}
childFromLineNumber(lineNumber: number, charOffset: number) {
let child: LineCollection;
let relativeLineNumber = lineNumber;
let i: number;
let len: number;
for (i = 0, len = this.children.length; i < len; i++) {
child = this.children[i];
const childLineCount = child.lineCount();
if (childLineCount >= relativeLineNumber) {
break;
}
else {
relativeLineNumber -= childLineCount;
charOffset += child.charCount();
}
}
return {
child: child,
childIndex: i,
relativeLineNumber: relativeLineNumber,
charOffset: charOffset
};
}
childFromCharOffset(lineNumber: number, charOffset: number) {
let child: LineCollection;
let i: number;
let len: number;
for (i = 0, len = this.children.length; i < len; i++) {
child = this.children[i];
if (child.charCount() > charOffset) {
break;
}
else {
charOffset -= child.charCount();
lineNumber += child.lineCount();
}
}
return {
child: child,
childIndex: i,
charOffset: charOffset,
lineNumber: lineNumber
};
}
splitAfter(childIndex: number) {
let splitNode: LineNode;
const clen = this.children.length;
childIndex++;
const endLength = childIndex;
if (childIndex < clen) {
splitNode = new LineNode();
while (childIndex < clen) {
splitNode.add(this.children[childIndex]);
childIndex++;
}
splitNode.updateCounts();
}
this.children.length = endLength;
return splitNode;
}
remove(child: LineCollection) {
const childIndex = this.findChildIndex(child);
const clen = this.children.length;
if (childIndex < (clen - 1)) {
for (let i = childIndex; i < (clen - 1); i++) {
this.children[i] = this.children[i + 1];
}
}
this.children.length--;
}
findChildIndex(child: LineCollection) {
let childIndex = 0;
const clen = this.children.length;
while ((this.children[childIndex] !== child) && (childIndex < clen)) childIndex++;
return childIndex;
}
insertAt(child: LineCollection, nodes: LineCollection[]) {
let childIndex = this.findChildIndex(child);
const clen = this.children.length;
const nodeCount = nodes.length;
// if child is last and there is more room and only one node to place, place it
if ((clen < lineCollectionCapacity) && (childIndex === (clen - 1)) && (nodeCount === 1)) {
this.add(nodes[0]);
this.updateCounts();
return [];
}
else {
const shiftNode = this.splitAfter(childIndex);
let nodeIndex = 0;
childIndex++;
while ((childIndex < lineCollectionCapacity) && (nodeIndex < nodeCount)) {
this.children[childIndex] = nodes[nodeIndex];
childIndex++;
nodeIndex++;
}
let splitNodes: LineNode[] = [];
let splitNodeCount = 0;
if (nodeIndex < nodeCount) {
splitNodeCount = Math.ceil((nodeCount - nodeIndex) / lineCollectionCapacity);
splitNodes = <LineNode[]>new Array(splitNodeCount);
let splitNodeIndex = 0;
for (let i = 0; i < splitNodeCount; i++) {
splitNodes[i] = new LineNode();
}
let splitNode = <LineNode>splitNodes[0];
while (nodeIndex < nodeCount) {
splitNode.add(nodes[nodeIndex]);
nodeIndex++;
if (splitNode.children.length === lineCollectionCapacity) {
splitNodeIndex++;
splitNode = <LineNode>splitNodes[splitNodeIndex];
}
}
for (let i = splitNodes.length - 1; i >= 0; i--) {
if (splitNodes[i].children.length === 0) {
splitNodes.length--;
}
}
}
if (shiftNode) {
splitNodes[splitNodes.length] = shiftNode;
}
this.updateCounts();
for (let i = 0; i < splitNodeCount; i++) {
(<LineNode>splitNodes[i]).updateCounts();
}
return splitNodes;
}
}
// assume there is room for the item; return true if more room
add(collection: LineCollection) {
this.children[this.children.length] = collection;
return (this.children.length < lineCollectionCapacity);
}
charCount() {
return this.totalChars;
}
lineCount() {
return this.totalLines;
}
}
export class LineLeaf implements LineCollection {
constructor(public text: string) {
}
isLeaf() {
return true;
}
walk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker) {
walkFns.leaf(rangeStart, rangeLength, this);
}
charCount() {
return this.text.length;
}
lineCount() {
return 1;
}
}
}

View File

@ -4,6 +4,48 @@
/* tslint:disable:no-null-keyword */
namespace ts.server {
const net: {
connect(options: { port: number }, onConnect?: () => void): NodeSocket
} = require("net");
const childProcess: {
fork(modulePath: string, args: string[], options?: { execArgv: string[], env?: MapLike<string> }): NodeChildProcess;
} = require("child_process");
const os: {
homedir(): string
} = require("os");
function getGlobalTypingsCacheLocation() {
let basePath: string;
switch (process.platform) {
case "win32":
basePath = process.env.LOCALAPPDATA || process.env.APPDATA || os.homedir();
break;
case "linux":
basePath = os.homedir();
break;
case "darwin":
basePath = combinePaths(os.homedir(), "Library/Application Support/");
break;
}
Debug.assert(basePath !== undefined);
return combinePaths(normalizeSlashes(basePath), "Microsoft/TypeScript");
}
interface NodeChildProcess {
send(message: any, sendHandle?: any): void;
on(message: "message", f: (m: any) => void): void;
kill(): void;
}
interface NodeSocket {
write(data: string, encoding: string): boolean;
}
interface ReadLineOptions {
input: NodeJS.ReadableStream;
output?: NodeJS.WritableStream;
@ -46,6 +88,7 @@ namespace ts.server {
const readline: {
createInterface(options: ReadLineOptions): NodeJS.EventEmitter;
} = require("readline");
const fs: {
openSync(path: string, options: string): number;
close(fd: number): void;
@ -55,6 +98,7 @@ namespace ts.server {
stat(path: string, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void;
} = require("fs");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
@ -62,12 +106,14 @@ namespace ts.server {
});
class Logger implements ts.server.Logger {
fd = -1;
seq = 0;
inGroup = false;
firstInGroup = true;
private fd = -1;
private seq = 0;
private inGroup = false;
private firstInGroup = true;
constructor(public logFilename: string, public level: string) {
constructor(private readonly logFilename: string,
private readonly traceToConsole: boolean,
private readonly level: LogLevel) {
}
static padStringRight(str: string, padding: string) {
@ -80,12 +126,16 @@ namespace ts.server {
}
}
getLogFileName() {
return this.logFilename;
}
perftrc(s: string) {
this.msg(s, "Perf");
this.msg(s, Msg.Perf);
}
info(s: string) {
this.msg(s, "Info");
this.msg(s, Msg.Info);
}
startGroup() {
@ -100,21 +150,20 @@ namespace ts.server {
}
loggingEnabled() {
return !!this.logFilename;
return !!this.logFilename || this.traceToConsole;
}
isVerbose() {
return this.loggingEnabled() && (this.level == "verbose");
hasLevel(level: LogLevel) {
return this.loggingEnabled() && this.level >= level;
}
msg(s: string, type = "Err") {
msg(s: string, type: Msg.Types = Msg.Err) {
if (this.fd < 0) {
if (this.logFilename) {
this.fd = fs.openSync(this.logFilename, "w");
}
}
if (this.fd >= 0) {
if (this.fd >= 0 || this.traceToConsole) {
s = s + "\n";
const prefix = Logger.padStringRight(type + " " + this.seq.toString(), " ");
if (this.firstInGroup) {
@ -125,19 +174,112 @@ namespace ts.server {
this.seq++;
this.firstInGroup = true;
}
const buf = new Buffer(s);
fs.writeSync(this.fd, buf, 0, buf.length, null);
if (this.fd >= 0) {
const buf = new Buffer(s);
fs.writeSync(this.fd, buf, 0, buf.length, null);
}
if (this.traceToConsole) {
console.warn(s);
}
}
}
}
class NodeTypingsInstaller implements ITypingsInstaller {
private installer: NodeChildProcess;
private socket: NodeSocket;
private projectService: ProjectService;
constructor(
private readonly logger: server.Logger,
private readonly eventPort: number,
readonly globalTypingsCacheLocation: string,
private newLine: string) {
if (eventPort) {
const s = net.connect({ port: eventPort }, () => {
this.socket = s;
});
}
}
attach(projectService: ProjectService) {
this.projectService = projectService;
if (this.logger.hasLevel(LogLevel.requestTime)) {
this.logger.info("Binding...");
}
const args: string[] = ["--globalTypingsCacheLocation", this.globalTypingsCacheLocation];
if (this.logger.loggingEnabled() && this.logger.getLogFileName()) {
args.push("--logFile", combinePaths(getDirectoryPath(normalizeSlashes(this.logger.getLogFileName())), `ti-${process.pid}.log`));
}
const execArgv: string[] = [];
{
for (const arg of process.execArgv) {
const match = /^--(debug|inspect)(=(\d+))?$/.exec(arg);
if (match) {
// if port is specified - use port + 1
// otherwise pick a default port depending on if 'debug' or 'inspect' and use its value + 1
const currentPort = match[3] !== undefined
? +match[3]
: match[1] === "debug" ? 5858 : 9229;
execArgv.push(`--${match[1]}=${currentPort + 1}`);
break;
}
}
}
this.installer = childProcess.fork(combinePaths(__dirname, "typingsInstaller.js"), args, { execArgv });
this.installer.on("message", m => this.handleMessage(m));
process.on("exit", () => {
this.installer.kill();
});
}
onProjectClosed(p: Project): void {
this.installer.send({ projectName: p.getProjectName(), kind: "closeProject" });
}
enqueueInstallTypingsRequest(project: Project, typingOptions: TypingOptions): void {
const request = createInstallTypingsRequest(project, typingOptions);
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Sending request: ${JSON.stringify(request)}`);
}
this.installer.send(request);
}
private handleMessage(response: SetTypings | InvalidateCachedTypings) {
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Received response: ${JSON.stringify(response)}`);
}
this.projectService.updateTypingsForProject(response);
if (response.kind == "set" && this.socket) {
this.socket.write(formatMessage({ seq: 0, type: "event", message: response }, this.logger, Buffer.byteLength, this.newLine), "utf8");
}
}
}
class IOSession extends Session {
constructor(host: ServerHost, logger: ts.server.Logger) {
super(host, Buffer.byteLength, process.hrtime, logger);
constructor(
host: ServerHost,
cancellationToken: HostCancellationToken,
installerEventPort: number,
canUseEvents: boolean,
useSingleInferredProject: boolean,
globalTypingsCacheLocation: string,
logger: server.Logger) {
super(
host,
cancellationToken,
useSingleInferredProject,
new NodeTypingsInstaller(logger, installerEventPort, globalTypingsCacheLocation, host.newLine),
Buffer.byteLength,
process.hrtime,
logger,
canUseEvents);
}
exit() {
this.projectService.log("Exiting...", "Info");
this.logger.info("Exiting...");
this.projectService.closeLog();
process.exit(0);
}
@ -156,11 +298,13 @@ namespace ts.server {
interface LogOptions {
file?: string;
detailLevel?: string;
detailLevel?: LogLevel;
traceToConsole?: boolean;
logToFile?: boolean;
}
function parseLoggingEnvironmentString(logEnvStr: string): LogOptions {
const logEnv: LogOptions = {};
const logEnv: LogOptions = { logToFile: true };
const args = logEnvStr.split(" ");
for (let i = 0, len = args.length; i < (len - 1); i += 2) {
const option = args[i];
@ -168,10 +312,17 @@ namespace ts.server {
if (option && value) {
switch (option) {
case "-file":
logEnv.file = value;
logEnv.file = stripQuotes(value);
break;
case "-level":
logEnv.detailLevel = value;
const level: LogLevel = (<any>LogLevel)[value];
logEnv.detailLevel = typeof level === "number" ? level : LogLevel.normal;
break;
case "-traceToConsole":
logEnv.traceToConsole = value.toLowerCase() === "true";
break;
case "-logToFile":
logEnv.logToFile = value.toLowerCase() === "true";
break;
}
}
@ -182,21 +333,25 @@ namespace ts.server {
// TSS_LOG "{ level: "normal | verbose | terse", file?: string}"
function createLoggerFromEnv() {
let fileName: string = undefined;
let detailLevel = "normal";
let detailLevel = LogLevel.normal;
let traceToConsole = false;
const logEnvStr = process.env["TSS_LOG"];
if (logEnvStr) {
const logEnv = parseLoggingEnvironmentString(logEnvStr);
if (logEnv.file) {
fileName = logEnv.file;
}
else {
fileName = __dirname + "/.log" + process.pid.toString();
if (logEnv.logToFile) {
if (logEnv.file) {
fileName = logEnv.file;
}
else {
fileName = __dirname + "/.log" + process.pid.toString();
}
}
if (logEnv.detailLevel) {
detailLevel = logEnv.detailLevel;
}
traceToConsole = logEnv.traceToConsole;
}
return new Logger(fileName, detailLevel);
return new Logger(fileName, traceToConsole, detailLevel);
}
// This places log file in the directory containing editorServices.js
// TODO: check that this location is writable
@ -295,15 +450,16 @@ namespace ts.server {
const pollingWatchedFileSet = createPollingWatchedFileSet();
const logger = createLoggerFromEnv();
const pending: string[] = [];
const pending: Buffer[] = [];
let canWrite = true;
function writeMessage(s: string) {
function writeMessage(buf: Buffer) {
if (!canWrite) {
pending.push(s);
pending.push(buf);
}
else {
canWrite = false;
process.stdout.write(new Buffer(s, "utf8"), setCanWriteFlagAndWriteMessageIfNecessary);
process.stdout.write(buf, setCanWriteFlagAndWriteMessageIfNecessary);
}
}
@ -317,7 +473,7 @@ namespace ts.server {
const sys = <ServerHost>ts.sys;
// Override sys.write because fs.writeSync is not reliable on Node 4
sys.write = (s: string) => writeMessage(s);
sys.write = (s: string) => writeMessage(new Buffer(s, "utf8"));
sys.watchFile = (fileName, callback) => {
const watchedFile = pollingWatchedFileSet.addFile(fileName, callback);
return {
@ -327,9 +483,44 @@ namespace ts.server {
sys.setTimeout = setTimeout;
sys.clearTimeout = clearTimeout;
sys.setImmediate = setImmediate;
sys.clearImmediate = clearImmediate;
if (typeof global !== "undefined" && global.gc) {
sys.gc = () => global.gc();
}
const ioSession = new IOSession(sys, logger);
process.on("uncaughtException", function(err: Error) {
let cancellationToken: HostCancellationToken;
try {
const factory = require("./cancellationToken");
cancellationToken = factory(sys.args);
}
catch (e) {
cancellationToken = {
isCancellationRequested: () => false
};
};
let eventPort: number;
{
const index = sys.args.indexOf("--eventPort");
if (index >= 0 && index < sys.args.length - 1) {
const v = parseInt(sys.args[index + 1]);
if (!isNaN(v)) {
eventPort = v;
}
}
}
const useSingleInferredProject = sys.args.indexOf("--useSingleInferredProject") >= 0;
const ioSession = new IOSession(
sys,
cancellationToken,
eventPort,
/*canUseEvents*/ eventPort === undefined,
useSingleInferredProject,
getGlobalTypingsCacheLocation(),
logger);
process.on("uncaughtException", function (err: Error) {
ioSession.logError(err, "unknown");
});
// Start listening

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,12 @@
"files": [
"../services/shims.ts",
"../services/utilities.ts",
"utilities.ts",
"scriptVersionCache.ts",
"scriptInfo.ts",
"lshost.ts",
"typingsCache.ts",
"project.ts",
"editorServices.ts",
"protocol.d.ts",
"session.ts",

View File

@ -12,6 +12,12 @@
"files": [
"../services/shims.ts",
"../services/utilities.ts",
"utilities.ts",
"scriptVersionCache.ts",
"scriptInfo.ts",
"lshost.ts",
"typingsCache.ts",
"project.ts",
"editorServices.ts",
"protocol.d.ts",
"session.ts"

60
src/server/types.d.ts vendored Normal file
View File

@ -0,0 +1,60 @@
/// <reference path="../compiler/types.ts"/>
/// <reference path="../compiler/sys.ts"/>
/// <reference path="../services/jsTyping.ts"/>
declare namespace ts.server {
export interface CompressedData {
length: number;
compressionKind: string;
data: any;
}
export interface ServerHost extends System {
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
clearTimeout(timeoutId: any): void;
setImmediate(callback: (...args: any[]) => void, ...args: any[]): any;
clearImmediate(timeoutId: any): void;
gc?(): void;
trace?(s: string): void;
}
export interface TypingInstallerRequest {
readonly projectName: string;
readonly kind: "discover" | "closeProject";
}
export interface DiscoverTypings extends TypingInstallerRequest {
readonly fileNames: string[];
readonly projectRootPath: ts.Path;
readonly typingOptions: ts.TypingOptions;
readonly compilerOptions: ts.CompilerOptions;
readonly cachePath?: string;
readonly kind: "discover";
}
export interface CloseProject extends TypingInstallerRequest {
readonly kind: "closeProject";
}
export interface TypingInstallerResponse {
readonly projectName: string;
readonly kind: "set" | "invalidate";
}
export interface SetTypings extends TypingInstallerResponse {
readonly typingOptions: ts.TypingOptions;
readonly compilerOptions: ts.CompilerOptions;
readonly typings: string[];
readonly kind: "set";
}
export interface InvalidateCachedTypings extends TypingInstallerResponse {
readonly kind: "invalidate";
}
export interface InstallTypingHost extends JsTyping.TypingResolutionHost {
writeFile(path: string, content: string): void;
createDirectory(path: string): void;
watchFile?(path: string, callback: FileWatcherCallback): FileWatcher;
}
}

125
src/server/typingsCache.ts Normal file
View File

@ -0,0 +1,125 @@
/// <reference path="project.ts"/>
namespace ts.server {
export interface ITypingsInstaller {
enqueueInstallTypingsRequest(p: Project, typingOptions: TypingOptions): void;
attach(projectService: ProjectService): void;
onProjectClosed(p: Project): void;
readonly globalTypingsCacheLocation: string;
}
export const nullTypingsInstaller: ITypingsInstaller = {
enqueueInstallTypingsRequest: () => {},
attach: (projectService: ProjectService) => {},
onProjectClosed: (p: Project) => {},
globalTypingsCacheLocation: undefined
};
class TypingsCacheEntry {
readonly typingOptions: TypingOptions;
readonly compilerOptions: CompilerOptions;
readonly typings: TypingsArray;
poisoned: boolean;
}
function setIsEqualTo(arr1: string[], arr2: string[]): boolean {
if (arr1 === arr2) {
return true;
}
if ((arr1 || emptyArray).length === 0 && (arr2 || emptyArray).length === 0) {
return true;
}
const set: Map<boolean> = createMap<boolean>();
let unique = 0;
for (const v of arr1) {
if (set[v] !== true) {
set[v] = true;
unique++;
}
}
for (const v of arr2) {
if (!hasProperty(set, v)) {
return false;
}
if (set[v] === true) {
set[v] = false;
unique--;
}
}
return unique === 0;
}
function typingOptionsChanged(opt1: TypingOptions, opt2: TypingOptions): boolean {
return opt1.enableAutoDiscovery !== opt2.enableAutoDiscovery ||
!setIsEqualTo(opt1.include, opt2.include) ||
!setIsEqualTo(opt1.exclude, opt2.exclude);
}
function compilerOptionsChanged(opt1: CompilerOptions, opt2: CompilerOptions): boolean {
// TODO: add more relevant properties
return opt1.allowJs != opt2.allowJs;
}
export interface TypingsArray extends ReadonlyArray<string> {
" __typingsArrayBrand": any;
}
function toTypingsArray(arr: string[]): TypingsArray {
arr.sort();
return <any>arr;
}
export class TypingsCache {
private readonly perProjectCache: Map<TypingsCacheEntry> = createMap<TypingsCacheEntry>();
constructor(private readonly installer: ITypingsInstaller) {
}
getTypingsForProject(project: Project, forceRefresh: boolean): TypingsArray {
const typingOptions = project.getTypingOptions();
if (!typingOptions || !typingOptions.enableAutoDiscovery) {
return <any>emptyArray;
}
const entry = this.perProjectCache[project.getProjectName()];
const result: TypingsArray = entry ? entry.typings : <any>emptyArray;
if (forceRefresh || !entry || typingOptionsChanged(typingOptions, entry.typingOptions) || compilerOptionsChanged(project.getCompilerOptions(), entry.compilerOptions)) {
// Note: entry is now poisoned since it does not really contain typings for a given combination of compiler options\typings options.
// instead it acts as a placeholder to prevent issuing multiple requests
this.perProjectCache[project.getProjectName()] = {
compilerOptions: project.getCompilerOptions(),
typingOptions,
typings: result,
poisoned: true
};
// something has been changed, issue a request to update typings
this.installer.enqueueInstallTypingsRequest(project, typingOptions);
}
return result;
}
invalidateCachedTypingsForProject(project: Project) {
const typingOptions = project.getTypingOptions();
if (!typingOptions.enableAutoDiscovery) {
return;
}
this.installer.enqueueInstallTypingsRequest(project, typingOptions);
}
updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typingOptions: TypingOptions, newTypings: string[]) {
this.perProjectCache[projectName] = {
compilerOptions,
typingOptions,
typings: toTypingsArray(newTypings),
poisoned: false
};
}
onProjectClosed(project: Project) {
delete this.perProjectCache[project.getProjectName()];
this.installer.onProjectClosed(project);
}
}
}

View File

@ -0,0 +1,115 @@
/// <reference path="typingsInstaller.ts"/>
/// <reference types="node" />
namespace ts.server.typingsInstaller {
const fs: {
appendFileSync(file: string, content: string): void
} = require("fs");
const path: {
join(...parts: string[]): string;
dirname(path: string): string;
basename(path: string, extension?: string): string;
} = require("path");
class FileLog implements Log {
constructor(private readonly logFile?: string) {
}
isEnabled() {
return this.logFile !== undefined;
}
writeLine(text: string) {
fs.appendFileSync(this.logFile, text + sys.newLine);
}
}
function getNPMLocation(processName: string) {
if (path.basename(processName).indexOf("node") == 0) {
return `"${path.join(path.dirname(process.argv[0]), "npm")}"`;
}
else {
return "npm";
}
}
export class NodeTypingsInstaller extends TypingsInstaller {
private readonly exec: { (command: string, options: { cwd: string }, callback?: (error: Error, stdout: string, stderr: string) => void): any };
readonly installTypingHost: InstallTypingHost = sys;
constructor(globalTypingsCacheLocation: string, throttleLimit: number, log: Log) {
super(
globalTypingsCacheLocation,
/*npmPath*/ getNPMLocation(process.argv[0]),
toPath("typingSafeList.json", __dirname, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)),
throttleLimit,
log);
if (this.log.isEnabled()) {
this.log.writeLine(`Process id: ${process.pid}`);
}
const { exec } = require("child_process");
this.exec = exec;
}
init() {
super.init();
process.on("message", (req: DiscoverTypings | CloseProject) => {
switch (req.kind) {
case "discover":
this.install(req);
break;
case "closeProject":
this.closeProject(req);
}
});
}
protected sendResponse(response: SetTypings | InvalidateCachedTypings) {
if (this.log.isEnabled()) {
this.log.writeLine(`Sending response: ${JSON.stringify(response)}`);
}
process.send(response);
if (this.log.isEnabled()) {
this.log.writeLine(`Response has been sent.`);
}
}
protected runCommand(requestKind: RequestKind, requestId: number, command: string, cwd: string, onRequestCompleted: RequestCompletedAction): void {
if (this.log.isEnabled()) {
this.log.writeLine(`#${requestId} running command '${command}'.`);
}
this.exec(command, { cwd }, (err, stdout, stderr) => {
if (this.log.isEnabled()) {
this.log.writeLine(`${requestKind} #${requestId} stdout: ${stdout}`);
this.log.writeLine(`${requestKind} #${requestId} stderr: ${stderr}`);
}
onRequestCompleted(err, stdout, stderr);
});
}
}
function findArgument(argumentName: string) {
const index = sys.args.indexOf(argumentName);
return index >= 0 && index < sys.args.length - 1
? sys.args[index + 1]
: undefined;
}
const logFilePath = findArgument("--logFile");
const globalTypingsCacheLocation = findArgument("--globalTypingsCacheLocation");
const log = new FileLog(logFilePath);
if (log.isEnabled()) {
process.on("uncaughtException", (e: Error) => {
log.writeLine(`Unhandled exception: ${e} at ${e.stack}`);
});
}
process.on("disconnect", () => {
if (log.isEnabled()) {
log.writeLine(`Parent process has exited, shutting down...`);
}
process.exit(0);
});
const installer = new NodeTypingsInstaller(globalTypingsCacheLocation, /*throttleLimit*/5, log);
installer.init();
}

View File

@ -0,0 +1,20 @@
{
"compilerOptions": {
"noImplicitAny": true,
"noImplicitThis": true,
"removeComments": true,
"preserveConstEnums": true,
"pretty": true,
"out": "../../built/local/typingsInstaller.js",
"sourceMap": true,
"stripInternal": true,
"types": [
"node"
]
},
"files": [
"../types.d.ts",
"typingsInstaller.ts",
"nodeTypingsInstaller.ts"
]
}

View File

@ -0,0 +1,349 @@
/// <reference path="../../compiler/core.ts" />
/// <reference path="../../compiler/moduleNameResolver.ts" />
/// <reference path="../../services/jsTyping.ts"/>
/// <reference path="../types.d.ts"/>
namespace ts.server.typingsInstaller {
interface NpmConfig {
devDependencies: MapLike<any>;
}
export interface Log {
isEnabled(): boolean;
writeLine(text: string): void;
}
const nullLog: Log = {
isEnabled: () => false,
writeLine: () => {}
};
function typingToFileName(cachePath: string, packageName: string, installTypingHost: InstallTypingHost): string {
const result = resolveModuleName(packageName, combinePaths(cachePath, "index.d.ts"), { moduleResolution: ModuleResolutionKind.NodeJs }, installTypingHost);
return result.resolvedModule && result.resolvedModule.resolvedFileName;
}
export const NpmViewRequest: "npm view" = "npm view";
export const NpmInstallRequest: "npm install" = "npm install";
export type RequestKind = typeof NpmViewRequest | typeof NpmInstallRequest;
export type RequestCompletedAction = (err: Error, stdout: string, stderr: string) => void;
type PendingRequest = {
requestKind: RequestKind;
requestId: number;
command: string;
cwd: string;
onRequestCompleted: RequestCompletedAction
};
export abstract class TypingsInstaller {
private readonly packageNameToTypingLocation: Map<string> = createMap<string>();
private readonly missingTypingsSet: Map<true> = createMap<true>();
private readonly knownCachesSet: Map<true> = createMap<true>();
private readonly projectWatchers: Map<FileWatcher[]> = createMap<FileWatcher[]>();
readonly pendingRunRequests: PendingRequest[] = [];
private installRunCount = 1;
private inFlightRequestCount = 0;
abstract readonly installTypingHost: InstallTypingHost;
constructor(
readonly globalCachePath: string,
readonly npmPath: string,
readonly safeListPath: Path,
readonly throttleLimit: number,
protected readonly log = nullLog) {
if (this.log.isEnabled()) {
this.log.writeLine(`Global cache location '${globalCachePath}', safe file path '${safeListPath}'`);
}
}
init() {
this.processCacheLocation(this.globalCachePath);
}
closeProject(req: CloseProject) {
this.closeWatchers(req.projectName);
}
private closeWatchers(projectName: string): void {
if (this.log.isEnabled()) {
this.log.writeLine(`Closing file watchers for project '${projectName}'`);
}
const watchers = this.projectWatchers[projectName];
if (!watchers) {
if (this.log.isEnabled()) {
this.log.writeLine(`No watchers are registered for project '${projectName}'`);
}
return;
}
for (const w of watchers) {
w.close();
}
delete this.projectWatchers[projectName];
if (this.log.isEnabled()) {
this.log.writeLine(`Closing file watchers for project '${projectName}' - done.`);
}
}
install(req: DiscoverTypings) {
if (this.log.isEnabled()) {
this.log.writeLine(`Got install request ${JSON.stringify(req)}`);
}
// load existing typing information from the cache
if (req.cachePath) {
if (this.log.isEnabled()) {
this.log.writeLine(`Request specifies cache path '${req.cachePath}', loading cached information...`);
}
this.processCacheLocation(req.cachePath);
}
const discoverTypingsResult = JsTyping.discoverTypings(
this.installTypingHost,
req.fileNames,
req.projectRootPath,
this.safeListPath,
this.packageNameToTypingLocation,
req.typingOptions,
req.compilerOptions);
if (this.log.isEnabled()) {
this.log.writeLine(`Finished typings discovery: ${JSON.stringify(discoverTypingsResult)}`);
}
// respond with whatever cached typings we have now
this.sendResponse(this.createSetTypings(req, discoverTypingsResult.cachedTypingPaths));
// start watching files
this.watchFiles(req.projectName, discoverTypingsResult.filesToWatch);
// install typings
if (discoverTypingsResult.newTypingNames.length) {
this.installTypings(req, req.cachePath || this.globalCachePath, discoverTypingsResult.cachedTypingPaths, discoverTypingsResult.newTypingNames);
}
else {
if (this.log.isEnabled()) {
this.log.writeLine(`No new typings were requested as a result of typings discovery`);
}
}
}
private processCacheLocation(cacheLocation: string) {
if (this.log.isEnabled()) {
this.log.writeLine(`Processing cache location '${cacheLocation}'`);
}
if (this.knownCachesSet[cacheLocation]) {
if (this.log.isEnabled()) {
this.log.writeLine(`Cache location was already processed...`);
}
return;
}
const packageJson = combinePaths(cacheLocation, "package.json");
if (this.log.isEnabled()) {
this.log.writeLine(`Trying to find '${packageJson}'...`);
}
if (this.installTypingHost.fileExists(packageJson)) {
const npmConfig = <NpmConfig>JSON.parse(this.installTypingHost.readFile(packageJson));
if (this.log.isEnabled()) {
this.log.writeLine(`Loaded content of '${packageJson}': ${JSON.stringify(npmConfig)}`);
}
if (npmConfig.devDependencies) {
for (const key in npmConfig.devDependencies) {
// key is @types/<package name>
const packageName = getBaseFileName(key);
if (!packageName) {
continue;
}
const typingFile = typingToFileName(cacheLocation, packageName, this.installTypingHost);
if (!typingFile) {
continue;
}
const existingTypingFile = this.packageNameToTypingLocation[packageName];
if (existingTypingFile === typingFile) {
continue;
}
if (existingTypingFile) {
if (this.log.isEnabled()) {
this.log.writeLine(`New typing for package ${packageName} from '${typingFile}' conflicts with existing typing file '${existingTypingFile}'`);
}
}
if (this.log.isEnabled()) {
this.log.writeLine(`Adding entry into typings cache: '${packageName}' => '${typingFile}'`);
}
this.packageNameToTypingLocation[packageName] = typingFile;
}
}
}
if (this.log.isEnabled()) {
this.log.writeLine(`Finished processing cache location '${cacheLocation}'`);
}
this.knownCachesSet[cacheLocation] = true;
}
private installTypings(req: DiscoverTypings, cachePath: string, currentlyCachedTypings: string[], typingsToInstall: string[]) {
if (this.log.isEnabled()) {
this.log.writeLine(`Installing typings ${JSON.stringify(typingsToInstall)}`);
}
typingsToInstall = filter(typingsToInstall, x => !this.missingTypingsSet[x]);
if (typingsToInstall.length === 0) {
if (this.log.isEnabled()) {
this.log.writeLine(`All typings are known to be missing - no need to go any further`);
}
return;
}
const npmConfigPath = combinePaths(cachePath, "package.json");
if (this.log.isEnabled()) {
this.log.writeLine(`Npm config file: ${npmConfigPath}`);
}
if (!this.installTypingHost.fileExists(npmConfigPath)) {
if (this.log.isEnabled()) {
this.log.writeLine(`Npm config file: '${npmConfigPath}' is missing, creating new one...`);
}
this.ensureDirectoryExists(cachePath, this.installTypingHost);
this.installTypingHost.writeFile(npmConfigPath, "{}");
}
this.runInstall(cachePath, typingsToInstall, installedTypings => {
// TODO: watch project directory
if (this.log.isEnabled()) {
this.log.writeLine(`Requested to install typings ${JSON.stringify(typingsToInstall)}, installed typings ${JSON.stringify(installedTypings)}`);
}
const installedPackages: Map<true> = createMap<true>();
const installedTypingFiles: string[] = [];
for (const t of installedTypings) {
const packageName = getBaseFileName(t);
if (!packageName) {
continue;
}
installedPackages[packageName] = true;
const typingFile = typingToFileName(cachePath, packageName, this.installTypingHost);
if (!typingFile) {
continue;
}
if (!this.packageNameToTypingLocation[packageName]) {
this.packageNameToTypingLocation[packageName] = typingFile;
}
installedTypingFiles.push(typingFile);
}
if (this.log.isEnabled()) {
this.log.writeLine(`Installed typing files ${JSON.stringify(installedTypingFiles)}`);
}
for (const toInstall of typingsToInstall) {
if (!installedPackages[toInstall]) {
if (this.log.isEnabled()) {
this.log.writeLine(`New missing typing package '${toInstall}'`);
}
this.missingTypingsSet[toInstall] = true;
}
}
this.sendResponse(this.createSetTypings(req, currentlyCachedTypings.concat(installedTypingFiles)));
});
}
private runInstall(cachePath: string, typingsToInstall: string[], postInstallAction: (installedTypings: string[]) => void): void {
const requestId = this.installRunCount;
this.installRunCount++;
let execInstallCmdCount = 0;
const filteredTypings: string[] = [];
for (const typing of typingsToInstall) {
execNpmViewTyping(this, typing);
}
function execNpmViewTyping(self: TypingsInstaller, typing: string) {
const command = `${self.npmPath} view @types/${typing} --silent name`;
self.execAsync(NpmViewRequest, requestId, command, cachePath, (err, stdout, stderr) => {
if (stdout) {
filteredTypings.push(typing);
}
execInstallCmdCount++;
if (execInstallCmdCount === typingsToInstall.length) {
installFilteredTypings(self, filteredTypings);
}
});
}
function installFilteredTypings(self: TypingsInstaller, filteredTypings: string[]) {
if (filteredTypings.length === 0) {
postInstallAction([]);
return;
}
const scopedTypings = filteredTypings.map(t => "@types/" + t);
const command = `${self.npmPath} install ${scopedTypings.join(" ")} --save-dev`;
self.execAsync(NpmInstallRequest, requestId, command, cachePath, (err, stdout, stderr) => {
postInstallAction(stdout ? scopedTypings : []);
});
}
}
private ensureDirectoryExists(directory: string, host: InstallTypingHost): void {
const directoryName = getDirectoryPath(directory);
if (!host.directoryExists(directoryName)) {
this.ensureDirectoryExists(directoryName, host);
}
if (!host.directoryExists(directory)) {
host.createDirectory(directory);
}
}
private watchFiles(projectName: string, files: string[]) {
if (!files.length) {
return;
}
// shut down existing watchers
this.closeWatchers(projectName);
// handler should be invoked once for the entire set of files since it will trigger full rediscovery of typings
let isInvoked = false;
const watchers: FileWatcher[] = [];
for (const file of files) {
const w = this.installTypingHost.watchFile(file, f => {
if (this.log.isEnabled()) {
this.log.writeLine(`Got FS notification for ${f}, handler is already invoked '${isInvoked}'`);
}
this.sendResponse({ projectName: projectName, kind: "invalidate" });
isInvoked = true;
});
watchers.push(w);
}
this.projectWatchers[projectName] = watchers;
}
private createSetTypings(request: DiscoverTypings, typings: string[]): SetTypings {
return {
projectName: request.projectName,
typingOptions: request.typingOptions,
compilerOptions: request.compilerOptions,
typings,
kind: "set"
};
}
private execAsync(requestKind: RequestKind, requestId: number, command: string, cwd: string, onRequestCompleted: RequestCompletedAction): void {
this.pendingRunRequests.unshift({ requestKind, requestId, command, cwd, onRequestCompleted });
this.executeWithThrottling();
}
private executeWithThrottling() {
while (this.inFlightRequestCount < this.throttleLimit && this.pendingRunRequests.length) {
this.inFlightRequestCount++;
const request = this.pendingRunRequests.pop();
this.runCommand(request.requestKind, request.requestId, request.command, request.cwd, (err, stdout, stderr) => {
this.inFlightRequestCount--;
request.onRequestCompleted(err, stdout, stderr);
this.executeWithThrottling();
});
}
}
protected abstract runCommand(requestKind: RequestKind, requestId: number, command: string, cwd: string, onRequestCompleted: RequestCompletedAction): void;
protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings): void;
}
}

287
src/server/utilities.ts Normal file
View File

@ -0,0 +1,287 @@
/// <reference path="types.d.ts" />
namespace ts.server {
export enum LogLevel {
terse,
normal,
requestTime,
verbose
}
export const emptyArray: ReadonlyArray<any> = [];
export interface Logger {
close(): void;
hasLevel(level: LogLevel): boolean;
loggingEnabled(): boolean;
perftrc(s: string): void;
info(s: string): void;
startGroup(): void;
endGroup(): void;
msg(s: string, type?: Msg.Types): void;
getLogFileName(): string;
}
export namespace Msg {
export type Err = "Err";
export const Err: Err = "Err";
export type Info = "Info";
export const Info: Info = "Info";
export type Perf = "Perf";
export const Perf: Perf = "Perf";
export type Types = Err | Info | Perf;
}
function getProjectRootPath(project: Project): Path {
switch (project.projectKind) {
case ProjectKind.Configured:
return <Path>getDirectoryPath(project.getProjectName());
case ProjectKind.Inferred:
// TODO: fixme
return <Path>"";
case ProjectKind.External:
const projectName = normalizeSlashes(project.getProjectName());
return project.projectService.host.fileExists(projectName) ? <Path>getDirectoryPath(projectName) : <Path>projectName;
}
}
export function createInstallTypingsRequest(project: Project, typingOptions: TypingOptions, cachePath?: string): DiscoverTypings {
return {
projectName: project.getProjectName(),
fileNames: project.getFileNames(),
compilerOptions: project.getCompilerOptions(),
typingOptions,
projectRootPath: getProjectRootPath(project),
cachePath,
kind: "discover"
};
}
export namespace Errors {
export function ThrowNoProject(): never {
throw new Error("No Project.");
}
export function ThrowProjectLanguageServiceDisabled(): never {
throw new Error("The project's language service is disabled.");
}
export function ThrowProjectDoesNotContainDocument(fileName: string, project: Project): never {
throw new Error(`Project '${project.getProjectName()}' does not contain document '${fileName}'`);
}
}
export function getDefaultFormatCodeSettings(host: ServerHost): FormatCodeSettings {
return {
indentSize: 4,
tabSize: 4,
newLineCharacter: host.newLine || "\n",
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Smart,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
insertSpaceBeforeAndAfterBinaryOperators: true,
insertSpaceAfterKeywordsInControlFlowStatements: true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
placeOpenBraceOnNewLineForFunctions: false,
placeOpenBraceOnNewLineForControlBlocks: false,
};
}
export function mergeMaps(target: MapLike<any>, source: MapLike <any>): void {
for (const key in source) {
if (hasProperty(source, key)) {
target[key] = source[key];
}
}
}
export function removeItemFromSet<T>(items: T[], itemToRemove: T) {
if (items.length === 0) {
return;
}
const index = items.indexOf(itemToRemove);
if (index < 0) {
return;
}
if (index === items.length - 1) {
// last item - pop it
items.pop();
}
else {
// non-last item - replace it with the last one
items[index] = items.pop();
}
}
export type NormalizedPath = string & { __normalizedPathTag: any };
export function toNormalizedPath(fileName: string): NormalizedPath {
return <NormalizedPath>normalizePath(fileName);
}
export function normalizedPathToPath(normalizedPath: NormalizedPath, currentDirectory: string, getCanonicalFileName: (f: string) => string): Path {
const f = isRootedDiskPath(normalizedPath) ? normalizedPath : getNormalizedAbsolutePath(normalizedPath, currentDirectory);
return <Path>getCanonicalFileName(f);
}
export function asNormalizedPath(fileName: string): NormalizedPath {
return <NormalizedPath>fileName;
}
export interface NormalizedPathMap<T> {
get(path: NormalizedPath): T;
set(path: NormalizedPath, value: T): void;
contains(path: NormalizedPath): boolean;
remove(path: NormalizedPath): void;
}
export function createNormalizedPathMap<T>(): NormalizedPathMap<T> {
/* tslint:disable:no-null-keyword */
const map: Map<T> = Object.create(null);
/* tslint:enable:no-null-keyword */
return {
get(path) {
return map[path];
},
set(path, value) {
map[path] = value;
},
contains(path) {
return hasProperty(map, path);
},
remove(path) {
delete map[path];
}
};
}
function throwLanguageServiceIsDisabledError() {
throw new Error("LanguageService is disabled");
}
export const nullLanguageService: LanguageService = {
cleanupSemanticCache: (): any => throwLanguageServiceIsDisabledError(),
getSyntacticDiagnostics: (): any => throwLanguageServiceIsDisabledError(),
getSemanticDiagnostics: (): any => throwLanguageServiceIsDisabledError(),
getCompilerOptionsDiagnostics: (): any => throwLanguageServiceIsDisabledError(),
getSyntacticClassifications: (): any => throwLanguageServiceIsDisabledError(),
getEncodedSyntacticClassifications: (): any => throwLanguageServiceIsDisabledError(),
getSemanticClassifications: (): any => throwLanguageServiceIsDisabledError(),
getEncodedSemanticClassifications: (): any => throwLanguageServiceIsDisabledError(),
getCompletionsAtPosition: (): any => throwLanguageServiceIsDisabledError(),
findReferences: (): any => throwLanguageServiceIsDisabledError(),
getCompletionEntryDetails: (): any => throwLanguageServiceIsDisabledError(),
getQuickInfoAtPosition: (): any => throwLanguageServiceIsDisabledError(),
findRenameLocations: (): any => throwLanguageServiceIsDisabledError(),
getNameOrDottedNameSpan: (): any => throwLanguageServiceIsDisabledError(),
getBreakpointStatementAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getBraceMatchingAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getSignatureHelpItems: (): any => throwLanguageServiceIsDisabledError(),
getDefinitionAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getRenameInfo: (): any => throwLanguageServiceIsDisabledError(),
getTypeDefinitionAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getReferencesAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getDocumentHighlights: (): any => throwLanguageServiceIsDisabledError(),
getOccurrencesAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getNavigateToItems: (): any => throwLanguageServiceIsDisabledError(),
getNavigationBarItems: (): any => throwLanguageServiceIsDisabledError(),
getOutliningSpans: (): any => throwLanguageServiceIsDisabledError(),
getTodoComments: (): any => throwLanguageServiceIsDisabledError(),
getIndentationAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getFormattingEditsForRange: (): any => throwLanguageServiceIsDisabledError(),
getFormattingEditsForDocument: (): any => throwLanguageServiceIsDisabledError(),
getFormattingEditsAfterKeystroke: (): any => throwLanguageServiceIsDisabledError(),
getDocCommentTemplateAtPosition: (): any => throwLanguageServiceIsDisabledError(),
isValidBraceCompletionAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getEmitOutput: (): any => throwLanguageServiceIsDisabledError(),
getProgram: (): any => throwLanguageServiceIsDisabledError(),
getNonBoundSourceFile: (): any => throwLanguageServiceIsDisabledError(),
dispose: (): any => throwLanguageServiceIsDisabledError(),
getCompletionEntrySymbol: (): any => throwLanguageServiceIsDisabledError(),
getImplementationAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getSourceFile: (): any => throwLanguageServiceIsDisabledError()
};
export interface ServerLanguageServiceHost {
setCompilationSettings(options: CompilerOptions): void;
notifyFileRemoved(info: ScriptInfo): void;
}
export const nullLanguageServiceHost: ServerLanguageServiceHost = {
setCompilationSettings: () => undefined,
notifyFileRemoved: () => undefined
};
export interface ProjectOptions {
/**
* true if config file explicitly listed files
**/
configHasFilesProperty?: boolean;
/**
* these fields can be present in the project file
**/
files?: string[];
wildcardDirectories?: Map<WatchDirectoryFlags>;
compilerOptions?: CompilerOptions;
typingOptions?: TypingOptions;
compileOnSave?: boolean;
}
export function isInferredProjectName(name: string) {
// POSIX defines /dev/null as a device - there should be no file with this prefix
return /dev\/null\/inferredProject\d+\*/.test(name);
}
export function makeInferredProjectName(counter: number) {
return `/dev/null/inferredProject${counter}*`;
}
export class ThrottledOperations {
private pendingTimeouts: Map<any> = createMap<any>();
constructor(private readonly host: ServerHost) {
}
public schedule(operationId: string, delay: number, cb: () => void) {
if (hasProperty(this.pendingTimeouts, operationId)) {
// another operation was already scheduled for this id - cancel it
this.host.clearTimeout(this.pendingTimeouts[operationId]);
}
// schedule new operation, pass arguments
this.pendingTimeouts[operationId] = this.host.setTimeout(ThrottledOperations.run, delay, this, operationId, cb);
}
private static run(self: ThrottledOperations, operationId: string, cb: () => void) {
delete self.pendingTimeouts[operationId];
cb();
}
}
export class GcTimer {
private timerId: any;
constructor(private readonly host: ServerHost, private readonly delay: number, private readonly logger: Logger) {
}
public scheduleCollect() {
if (!this.host.gc || this.timerId != undefined) {
// no global.gc or collection was already scheduled - skip this request
return;
}
this.timerId = this.host.setTimeout(GcTimer.run, this.delay, this);
}
private static run(self: GcTimer) {
self.timerId = undefined;
const log = self.logger.hasLevel(LogLevel.requestTime);
const before = log && self.host.getMemoryUsage();
self.host.gc();
if (log) {
const after = self.host.getMemoryUsage();
self.logger.perftrc(`GC::before ${before}, after ${after}`);
}
}
}
}

View File

@ -56,7 +56,7 @@ namespace ts.Completions {
addRange(entries, keywordCompletions);
}
return { isMemberCompletion, isNewIdentifierLocation: isNewIdentifierLocation || isSourceFileJavaScript(sourceFile), entries };
return { isMemberCompletion, isNewIdentifierLocation: isNewIdentifierLocation, entries };
function getJavaScriptCompletionEntries(sourceFile: SourceFile, position: number, uniqueNames: Map<string>): CompletionEntry[] {
const entries: CompletionEntry[] = [];

View File

@ -67,7 +67,7 @@ namespace ts.formatting {
delta: number;
}
export function formatOnEnter(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
export function formatOnEnter(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
const line = sourceFile.getLineAndCharacterOfPosition(position).line;
if (line === 0) {
return [];
@ -96,15 +96,15 @@ namespace ts.formatting {
return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnEnter);
}
export function formatOnSemicolon(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
export function formatOnSemicolon(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
return formatOutermostParent(position, SyntaxKind.SemicolonToken, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnSemicolon);
}
export function formatOnClosingCurly(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
export function formatOnClosingCurly(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
return formatOutermostParent(position, SyntaxKind.CloseBraceToken, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnClosingCurlyBrace);
}
export function formatDocument(sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
export function formatDocument(sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
const span = {
pos: 0,
end: sourceFile.text.length
@ -112,7 +112,7 @@ namespace ts.formatting {
return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatDocument);
}
export function formatSelection(start: number, end: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
export function formatSelection(start: number, end: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
// format from the beginning of the line
const span = {
pos: getLineStartPositionForPosition(start, sourceFile),
@ -121,7 +121,7 @@ namespace ts.formatting {
return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatSelection);
}
function formatOutermostParent(position: number, expectedLastToken: SyntaxKind, sourceFile: SourceFile, options: FormatCodeOptions, rulesProvider: RulesProvider, requestKind: FormattingRequestKind): TextChange[] {
function formatOutermostParent(position: number, expectedLastToken: SyntaxKind, sourceFile: SourceFile, options: FormatCodeSettings, rulesProvider: RulesProvider, requestKind: FormattingRequestKind): TextChange[] {
const parent = findOutermostParent(position, expectedLastToken, sourceFile);
if (!parent) {
return [];
@ -294,7 +294,7 @@ namespace ts.formatting {
* if parent is on the different line - its delta was already contributed
* to the initial indentation.
*/
function getOwnOrInheritedDelta(n: Node, options: FormatCodeOptions, sourceFile: SourceFile): number {
function getOwnOrInheritedDelta(n: Node, options: FormatCodeSettings, sourceFile: SourceFile): number {
let previousLine = Constants.Unknown;
let child: Node;
while (n) {
@ -304,7 +304,7 @@ namespace ts.formatting {
}
if (SmartIndenter.shouldIndentChildNode(n, child)) {
return options.IndentSize;
return options.indentSize;
}
previousLine = line;
@ -316,7 +316,7 @@ namespace ts.formatting {
function formatSpan(originalRange: TextRange,
sourceFile: SourceFile,
options: FormatCodeOptions,
options: FormatCodeSettings,
rulesProvider: RulesProvider,
requestKind: FormattingRequestKind): TextChange[] {
@ -413,7 +413,7 @@ namespace ts.formatting {
effectiveParentStartLine: number): Indentation {
let indentation = inheritedIndentation;
let delta = SmartIndenter.shouldIndentChildNode(node) ? options.IndentSize : 0;
let delta = SmartIndenter.shouldIndentChildNode(node) ? options.indentSize : 0;
if (effectiveParentStartLine === startLine) {
// if node is located on the same line with the parent
@ -422,7 +422,7 @@ namespace ts.formatting {
indentation = startLine === lastIndentedLine
? indentationOnLastIndentedLine
: parentDynamicIndentation.getIndentation();
delta = Math.min(options.IndentSize, parentDynamicIndentation.getDelta(node) + delta);
delta = Math.min(options.indentSize, parentDynamicIndentation.getDelta(node) + delta);
}
else if (indentation === Constants.Unknown) {
if (SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(parent, node, startLine, sourceFile)) {
@ -506,14 +506,14 @@ namespace ts.formatting {
recomputeIndentation: lineAdded => {
if (node.parent && SmartIndenter.shouldIndentChildNode(node.parent, node)) {
if (lineAdded) {
indentation += options.IndentSize;
indentation += options.indentSize;
}
else {
indentation -= options.IndentSize;
indentation -= options.indentSize;
}
if (SmartIndenter.shouldIndentChildNode(node)) {
delta = options.IndentSize;
delta = options.indentSize;
}
else {
delta = 0;
@ -1042,7 +1042,7 @@ namespace ts.formatting {
// edit should not be applied only if we have one line feed between elements
const lineDelta = currentStartLine - previousStartLine;
if (lineDelta !== 1) {
recordReplace(previousRange.end, currentRange.pos - previousRange.end, options.NewLineCharacter);
recordReplace(previousRange.end, currentRange.pos - previousRange.end, options.newLineCharacter);
}
break;
case RuleAction.Space:
@ -1108,19 +1108,19 @@ namespace ts.formatting {
let internedTabsIndentation: string[];
let internedSpacesIndentation: string[];
export function getIndentationString(indentation: number, options: FormatCodeOptions): string {
export function getIndentationString(indentation: number, options: EditorSettings): string {
// reset interned strings if FormatCodeOptions were changed
const resetInternedStrings =
!internedSizes || (internedSizes.tabSize !== options.TabSize || internedSizes.indentSize !== options.IndentSize);
!internedSizes || (internedSizes.tabSize !== options.tabSize || internedSizes.indentSize !== options.indentSize);
if (resetInternedStrings) {
internedSizes = { tabSize: options.TabSize, indentSize: options.IndentSize };
internedSizes = { tabSize: options.tabSize, indentSize: options.indentSize };
internedTabsIndentation = internedSpacesIndentation = undefined;
}
if (!options.ConvertTabsToSpaces) {
const tabs = Math.floor(indentation / options.TabSize);
const spaces = indentation - tabs * options.TabSize;
if (!options.convertTabsToSpaces) {
const tabs = Math.floor(indentation / options.tabSize);
const spaces = indentation - tabs * options.tabSize;
let tabString: string;
if (!internedTabsIndentation) {
@ -1138,14 +1138,14 @@ namespace ts.formatting {
}
else {
let spacesString: string;
const quotient = Math.floor(indentation / options.IndentSize);
const remainder = indentation % options.IndentSize;
const quotient = Math.floor(indentation / options.indentSize);
const remainder = indentation % options.indentSize;
if (!internedSpacesIndentation) {
internedSpacesIndentation = [];
}
if (internedSpacesIndentation[quotient] === undefined) {
spacesString = repeat(" ", options.IndentSize * quotient);
spacesString = repeat(" ", options.indentSize * quotient);
internedSpacesIndentation[quotient] = spacesString;
}
else {

View File

@ -4,7 +4,7 @@
namespace ts.formatting {
export class RulesProvider {
private globalRules: Rules;
private options: ts.FormatCodeOptions;
private options: ts.FormatCodeSettings;
private activeRules: Rule[];
private rulesMap: RulesMap;
@ -24,7 +24,7 @@ namespace ts.formatting {
return this.rulesMap;
}
public ensureUpToDate(options: ts.FormatCodeOptions) {
public ensureUpToDate(options: ts.FormatCodeSettings) {
if (!this.options || !ts.compareDataObjects(this.options, options)) {
const activeRules = this.createActiveRules(options);
const rulesMap = RulesMap.create(activeRules);
@ -35,31 +35,31 @@ namespace ts.formatting {
}
}
private createActiveRules(options: ts.FormatCodeOptions): Rule[] {
private createActiveRules(options: ts.FormatCodeSettings): Rule[] {
let rules = this.globalRules.HighPriorityCommonRules.slice(0);
if (options.InsertSpaceAfterCommaDelimiter) {
if (options.insertSpaceAfterCommaDelimiter) {
rules.push(this.globalRules.SpaceAfterComma);
}
else {
rules.push(this.globalRules.NoSpaceAfterComma);
}
if (options.InsertSpaceAfterFunctionKeywordForAnonymousFunctions) {
if (options.insertSpaceAfterFunctionKeywordForAnonymousFunctions) {
rules.push(this.globalRules.SpaceAfterAnonymousFunctionKeyword);
}
else {
rules.push(this.globalRules.NoSpaceAfterAnonymousFunctionKeyword);
}
if (options.InsertSpaceAfterKeywordsInControlFlowStatements) {
if (options.insertSpaceAfterKeywordsInControlFlowStatements) {
rules.push(this.globalRules.SpaceAfterKeywordInControl);
}
else {
rules.push(this.globalRules.NoSpaceAfterKeywordInControl);
}
if (options.InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis) {
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis) {
rules.push(this.globalRules.SpaceAfterOpenParen);
rules.push(this.globalRules.SpaceBeforeCloseParen);
rules.push(this.globalRules.NoSpaceBetweenParens);
@ -70,7 +70,7 @@ namespace ts.formatting {
rules.push(this.globalRules.NoSpaceBetweenParens);
}
if (options.InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets) {
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets) {
rules.push(this.globalRules.SpaceAfterOpenBracket);
rules.push(this.globalRules.SpaceBeforeCloseBracket);
rules.push(this.globalRules.NoSpaceBetweenBrackets);
@ -83,7 +83,7 @@ namespace ts.formatting {
// 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) {
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces !== false) {
rules.push(this.globalRules.SpaceAfterOpenBrace);
rules.push(this.globalRules.SpaceBeforeCloseBrace);
rules.push(this.globalRules.NoSpaceBetweenEmptyBraceBrackets);
@ -94,7 +94,7 @@ namespace ts.formatting {
rules.push(this.globalRules.NoSpaceBetweenEmptyBraceBrackets);
}
if (options.InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces) {
if (options.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces) {
rules.push(this.globalRules.SpaceAfterTemplateHeadAndMiddle);
rules.push(this.globalRules.SpaceBeforeTemplateMiddleAndTail);
}
@ -103,7 +103,7 @@ namespace ts.formatting {
rules.push(this.globalRules.NoSpaceBeforeTemplateMiddleAndTail);
}
if (options.InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces) {
if (options.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces) {
rules.push(this.globalRules.SpaceAfterOpenBraceInJsxExpression);
rules.push(this.globalRules.SpaceBeforeCloseBraceInJsxExpression);
}
@ -112,14 +112,14 @@ namespace ts.formatting {
rules.push(this.globalRules.NoSpaceBeforeCloseBraceInJsxExpression);
}
if (options.InsertSpaceAfterSemicolonInForStatements) {
if (options.insertSpaceAfterSemicolonInForStatements) {
rules.push(this.globalRules.SpaceAfterSemicolonInFor);
}
else {
rules.push(this.globalRules.NoSpaceAfterSemicolonInFor);
}
if (options.InsertSpaceBeforeAndAfterBinaryOperators) {
if (options.insertSpaceBeforeAndAfterBinaryOperators) {
rules.push(this.globalRules.SpaceBeforeBinaryOperator);
rules.push(this.globalRules.SpaceAfterBinaryOperator);
}
@ -128,16 +128,16 @@ namespace ts.formatting {
rules.push(this.globalRules.NoSpaceAfterBinaryOperator);
}
if (options.PlaceOpenBraceOnNewLineForControlBlocks) {
if (options.placeOpenBraceOnNewLineForControlBlocks) {
rules.push(this.globalRules.NewLineBeforeOpenBraceInControl);
}
if (options.PlaceOpenBraceOnNewLineForFunctions) {
if (options.placeOpenBraceOnNewLineForFunctions) {
rules.push(this.globalRules.NewLineBeforeOpenBraceInFunction);
rules.push(this.globalRules.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock);
}
if (options.InsertSpaceAfterTypeAssertion) {
if (options.insertSpaceAfterTypeAssertion) {
rules.push(this.globalRules.SpaceAfterTypeAssertion);
}
else {
@ -149,4 +149,4 @@ namespace ts.formatting {
return rules;
}
}
}
}

View File

@ -8,14 +8,14 @@ namespace ts.formatting {
Unknown = -1
}
export function getIndentation(position: number, sourceFile: SourceFile, options: EditorOptions): number {
export function getIndentation(position: number, sourceFile: SourceFile, options: EditorSettings): number {
if (position > sourceFile.text.length) {
return getBaseIndentation(options); // past EOF
}
// no indentation when the indent style is set to none,
// so we can return fast
if (options.IndentStyle === IndentStyle.None) {
if (options.indentStyle === IndentStyle.None) {
return 0;
}
@ -35,7 +35,7 @@ namespace ts.formatting {
// indentation is first non-whitespace character in a previous line
// for block indentation, we should look for a line which contains something that's not
// whitespace.
if (options.IndentStyle === IndentStyle.Block) {
if (options.indentStyle === IndentStyle.Block) {
// move backwards until we find a line with a non-whitespace character,
// then find the first non-whitespace character for that line.
@ -75,7 +75,7 @@ namespace ts.formatting {
indentationDelta = 0;
}
else {
indentationDelta = lineAtPosition !== currentStart.line ? options.IndentSize : 0;
indentationDelta = lineAtPosition !== currentStart.line ? options.indentSize : 0;
}
break;
@ -88,7 +88,7 @@ namespace ts.formatting {
}
actualIndentation = getLineIndentationWhenExpressionIsInMultiLine(current, sourceFile, options);
if (actualIndentation !== Value.Unknown) {
return actualIndentation + options.IndentSize;
return actualIndentation + options.indentSize;
}
previous = current;
@ -103,22 +103,22 @@ namespace ts.formatting {
return getIndentationForNodeWorker(current, currentStart, /*ignoreActualIndentationRange*/ undefined, indentationDelta, sourceFile, options);
}
export function getBaseIndentation(options: EditorOptions) {
return options.BaseIndentSize || 0;
}
export function getIndentationForNode(n: Node, ignoreActualIndentationRange: TextRange, sourceFile: SourceFile, options: FormatCodeOptions): number {
export function getIndentationForNode(n: Node, ignoreActualIndentationRange: TextRange, sourceFile: SourceFile, options: EditorSettings): number {
const start = sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile));
return getIndentationForNodeWorker(n, start, ignoreActualIndentationRange, /*indentationDelta*/ 0, sourceFile, options);
}
export function getBaseIndentation(options: EditorSettings) {
return options.baseIndentSize || 0;
}
function getIndentationForNodeWorker(
current: Node,
currentStart: LineAndCharacter,
ignoreActualIndentationRange: TextRange,
indentationDelta: number,
sourceFile: SourceFile,
options: EditorOptions): number {
options: EditorSettings): number {
let parent: Node = current.parent;
let parentStart: LineAndCharacter;
@ -158,7 +158,7 @@ namespace ts.formatting {
// increase indentation if parent node wants its content to be indented and parent and child nodes don't start on the same line
if (shouldIndentChildNode(parent, current) && !parentAndChildShareLine) {
indentationDelta += options.IndentSize;
indentationDelta += options.indentSize;
}
current = parent;
@ -182,7 +182,7 @@ namespace ts.formatting {
/*
* Function returns Value.Unknown if indentation cannot be determined
*/
function getActualIndentationForListItemBeforeComma(commaToken: Node, sourceFile: SourceFile, options: EditorOptions): number {
function getActualIndentationForListItemBeforeComma(commaToken: Node, sourceFile: SourceFile, options: EditorSettings): number {
// previous token is comma that separates items in list - find the previous item and try to derive indentation from it
const commaItemInfo = findListItemInfo(commaToken);
if (commaItemInfo && commaItemInfo.listItemIndex > 0) {
@ -202,7 +202,7 @@ namespace ts.formatting {
currentLineAndChar: LineAndCharacter,
parentAndChildShareLine: boolean,
sourceFile: SourceFile,
options: EditorOptions): number {
options: EditorSettings): number {
// actual indentation is used for statements\declarations if one of cases below is true:
// - parent is SourceFile - by default immediate children of SourceFile are not indented except when user indents them manually
@ -309,7 +309,7 @@ namespace ts.formatting {
return undefined;
}
function getActualIndentationForListItem(node: Node, sourceFile: SourceFile, options: EditorOptions): number {
function getActualIndentationForListItem(node: Node, sourceFile: SourceFile, options: EditorSettings): number {
const containingList = getContainingList(node, sourceFile);
return containingList ? getActualIndentationFromList(containingList) : Value.Unknown;
@ -319,7 +319,7 @@ namespace ts.formatting {
}
}
function getLineIndentationWhenExpressionIsInMultiLine(node: Node, sourceFile: SourceFile, options: EditorOptions): number {
function getLineIndentationWhenExpressionIsInMultiLine(node: Node, sourceFile: SourceFile, options: EditorSettings): number {
// actual indentation should not be used when:
// - node is close parenthesis - this is the end of the expression
if (node.kind === SyntaxKind.CloseParenToken) {
@ -367,7 +367,7 @@ namespace ts.formatting {
}
}
function deriveActualIndentationFromList(list: Node[], index: number, sourceFile: SourceFile, options: EditorOptions): number {
function deriveActualIndentationFromList(list: Node[], index: number, sourceFile: SourceFile, options: EditorSettings): number {
Debug.assert(index >= 0 && index < list.length);
const node = list[index];
@ -389,7 +389,7 @@ namespace ts.formatting {
return Value.Unknown;
}
function findColumnForFirstNonWhitespaceCharacterInLine(lineAndCharacter: LineAndCharacter, sourceFile: SourceFile, options: EditorOptions): number {
function findColumnForFirstNonWhitespaceCharacterInLine(lineAndCharacter: LineAndCharacter, sourceFile: SourceFile, options: EditorSettings): number {
const lineStart = sourceFile.getPositionOfLineAndCharacter(lineAndCharacter.line, 0);
return findFirstNonWhitespaceColumn(lineStart, lineStart + lineAndCharacter.character, sourceFile, options);
}
@ -401,7 +401,7 @@ namespace ts.formatting {
value of 'character' for '$' is 3
value of 'column' for '$' is 6 (assuming that tab size is 4)
*/
export function findFirstNonWhitespaceCharacterAndColumn(startPos: number, endPos: number, sourceFile: SourceFile, options: EditorOptions) {
export function findFirstNonWhitespaceCharacterAndColumn(startPos: number, endPos: number, sourceFile: SourceFile, options: EditorSettings) {
let character = 0;
let column = 0;
for (let pos = startPos; pos < endPos; pos++) {
@ -411,7 +411,7 @@ namespace ts.formatting {
}
if (ch === CharacterCodes.tab) {
column += options.TabSize + (column % options.TabSize);
column += options.tabSize + (column % options.tabSize);
}
else {
column++;
@ -422,7 +422,7 @@ namespace ts.formatting {
return { column, character };
}
export function findFirstNonWhitespaceColumn(startPos: number, endPos: number, sourceFile: SourceFile, options: EditorOptions): number {
export function findFirstNonWhitespaceColumn(startPos: number, endPos: number, sourceFile: SourceFile, options: EditorSettings): number {
return findFirstNonWhitespaceCharacterAndColumn(startPos, endPos, sourceFile, options).column;
}

View File

@ -1,7 +1,9 @@
// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0.
// See LICENSE.txt in the project root for complete license information.
/// <reference path='services.ts' />
/// <reference path='../compiler/types.ts' />
/// <reference path='../compiler/core.ts' />
/// <reference path='../compiler/commandLineParser.ts' />
/* @internal */
namespace ts.JsTyping {
@ -27,6 +29,8 @@ namespace ts.JsTyping {
// that we are confident require typings
let safeList: Map<string>;
const EmptySafeList: Map<string> = createMap<string>();
/**
* @param host is the object providing I/O related operations.
* @param fileNames are the file names that belong to the same project
@ -54,11 +58,14 @@ namespace ts.JsTyping {
}
// Only infer typings for .js and .jsx files
fileNames = filter(map(fileNames, normalizePath), f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JS, ScriptKind.JSX));
fileNames = filter(map(fileNames, normalizePath), f => {
const kind = ensureScriptKind(f, getScriptKindFromFileName(f));
return kind === ScriptKind.JS || kind === ScriptKind.JSX;
});
if (!safeList) {
const result = readConfigFile(safeListPath, (path: string) => host.readFile(path));
safeList = createMap<string>(result.config);
safeList = result.config ? createMap<string>(result.config) : EmptySafeList;
}
const filesToWatch: string[] = [];
@ -158,14 +165,12 @@ namespace ts.JsTyping {
const jsFileNames = filter(fileNames, hasJavaScriptFileExtension);
const inferredTypingNames = map(jsFileNames, f => removeFileExtension(getBaseFileName(f.toLowerCase())));
const cleanedTypingNames = map(inferredTypingNames, f => f.replace(/((?:\.|-)min(?=\.|$))|((?:-|\.)\d+)/g, ""));
if (safeList === undefined) {
mergeTypings(cleanedTypingNames);
}
else {
if (safeList !== EmptySafeList) {
mergeTypings(filter(cleanedTypingNames, f => f in safeList));
}
const hasJsxFile = forEach(fileNames, f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JSX));
const hasJsxFile = forEach(fileNames, f => ensureScriptKind(f, getScriptKindFromFileName(f)) === ScriptKind.JSX);
if (hasJsxFile) {
mergeTypings(["react"]);
}
@ -182,7 +187,7 @@ namespace ts.JsTyping {
}
const typingNames: string[] = [];
const fileNames = host.readDirectory(nodeModulesPath, ["*.json"], /*excludes*/ undefined, /*includes*/ undefined, /*depth*/ 2);
const fileNames = host.readDirectory(nodeModulesPath, [".json"], /*excludes*/ undefined, /*includes*/ undefined, /*depth*/ 2);
for (const fileName of fileNames) {
const normalizedFileName = normalizePath(fileName);
if (getBaseFileName(normalizedFileName) !== "package.json") {

View File

@ -2,7 +2,7 @@
namespace ts.NavigateTo {
type RawNavigateToItem = { name: string; fileName: string; matchKind: PatternMatchKind; isCaseSensitive: boolean; declaration: Declaration };
export function getNavigateToItems(sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken, searchValue: string, maxResultCount: number): NavigateToItem[] {
export function getNavigateToItems(sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken, searchValue: string, maxResultCount: number, excludeDtsFiles: boolean): NavigateToItem[] {
const patternMatcher = createPatternMatcher(searchValue);
let rawItems: RawNavigateToItem[] = [];
@ -13,6 +13,10 @@ namespace ts.NavigateTo {
forEach(sourceFiles, sourceFile => {
cancellationToken.throwIfCancellationRequested();
if (excludeDtsFiles && fileExtensionIs(sourceFile.fileName, ".d.ts")) {
return;
}
const nameToDeclarations = sourceFile.getNamedDeclarations();
for (const name in nameToDeclarations) {
const declarations = nameToDeclarations[name];

View File

@ -677,6 +677,34 @@ namespace ts {
displayParts(): SymbolDisplayPart[];
}
/* @internal */
export function toEditorSettings(options: FormatCodeOptions | FormatCodeSettings): FormatCodeSettings;
export function toEditorSettings(options: EditorOptions | EditorSettings): EditorSettings;
export function toEditorSettings(optionsAsMap: MapLike<any>): MapLike<any> {
let allPropertiesAreCamelCased = true;
for (const key in optionsAsMap) {
if (hasProperty(optionsAsMap, key) && !isCamelCase(key)) {
allPropertiesAreCamelCased = false;
break;
}
}
if (allPropertiesAreCamelCased) {
return optionsAsMap;
}
const settings: MapLike<any> = {};
for (const key in optionsAsMap) {
if (hasProperty(optionsAsMap, key)) {
const newKey = isCamelCase(key) ? key : key.charAt(0).toLowerCase() + key.substr(1);
settings[newKey] = optionsAsMap[key];
}
}
return settings;
}
function isCamelCase(s: string) {
return !s.length || s.charAt(0) === s.charAt(0).toLowerCase();
}
export function displayPartsToString(displayParts: SymbolDisplayPart[]) {
if (displayParts) {
return map(displayParts, displayPart => displayPart.text).join("");
@ -917,6 +945,8 @@ namespace ts {
let program: Program;
let lastProjectVersion: string;
let lastTypesRootVersion = 0;
const useCaseSensitivefileNames = false;
const cancellationToken = new CancellationTokenObject(host.getCancellationToken && host.getCancellationToken());
@ -942,7 +972,7 @@ namespace ts {
return sourceFile;
}
function getRuleProvider(options: FormatCodeOptions) {
function getRuleProvider(options: FormatCodeSettings) {
// Ensure rules are initialized and up to date wrt to formatting options
if (!ruleProvider) {
ruleProvider = new formatting.RulesProvider();
@ -965,6 +995,13 @@ namespace ts {
}
}
const typeRootsVersion = host.getTypeRootsVersion ? host.getTypeRootsVersion() : 0;
if (lastTypesRootVersion !== typeRootsVersion) {
log("TypeRoots version has changed; provide new program");
program = undefined;
lastTypesRootVersion = typeRootsVersion;
}
// Get a fresh cache of the host information
let hostCache = new HostCache(host, getCanonicalFileName);
@ -1355,14 +1392,14 @@ namespace ts {
}
/// NavigateTo
function getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string): NavigateToItem[] {
function getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string, excludeDtsFiles?: boolean): NavigateToItem[] {
synchronizeHostData();
const sourceFiles = fileName ? [getValidSourceFile(fileName)] : program.getSourceFiles();
return ts.NavigateTo.getNavigateToItems(sourceFiles, program.getTypeChecker(), cancellationToken, searchValue, maxResultCount);
return ts.NavigateTo.getNavigateToItems(sourceFiles, program.getTypeChecker(), cancellationToken, searchValue, maxResultCount, excludeDtsFiles);
}
function getEmitOutput(fileName: string): EmitOutput {
function getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput {
synchronizeHostData();
const sourceFile = getValidSourceFile(fileName);
@ -1376,7 +1413,7 @@ namespace ts {
});
}
const emitOutput = program.emit(sourceFile, writeFile, cancellationToken);
const emitOutput = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles);
return {
outputFiles,
@ -1552,40 +1589,44 @@ namespace ts {
}
}
function getIndentationAtPosition(fileName: string, position: number, editorOptions: EditorOptions) {
function getIndentationAtPosition(fileName: string, position: number, editorOptions: EditorOptions | EditorSettings) {
let start = timestamp();
const settings = toEditorSettings(editorOptions);
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
log("getIndentationAtPosition: getCurrentSourceFile: " + (timestamp() - start));
start = timestamp();
const result = formatting.SmartIndenter.getIndentation(position, sourceFile, editorOptions);
const result = formatting.SmartIndenter.getIndentation(position, sourceFile, settings);
log("getIndentationAtPosition: computeIndentation : " + (timestamp() - start));
return result;
}
function getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions): TextChange[] {
function getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
return formatting.formatSelection(start, end, sourceFile, getRuleProvider(options), options);
const settings = toEditorSettings(options);
return formatting.formatSelection(start, end, sourceFile, getRuleProvider(settings), settings);
}
function getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions): TextChange[] {
function getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
return formatting.formatDocument(sourceFile, getRuleProvider(options), options);
const settings = toEditorSettings(options);
return formatting.formatDocument(sourceFile, getRuleProvider(settings), settings);
}
function getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): TextChange[] {
function getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
const settings = toEditorSettings(options);
if (key === "}") {
return formatting.formatOnClosingCurly(position, sourceFile, getRuleProvider(options), options);
return formatting.formatOnClosingCurly(position, sourceFile, getRuleProvider(settings), settings);
}
else if (key === ";") {
return formatting.formatOnSemicolon(position, sourceFile, getRuleProvider(options), options);
return formatting.formatOnSemicolon(position, sourceFile, getRuleProvider(settings), settings);
}
else if (key === "\n") {
return formatting.formatOnEnter(position, sourceFile, getRuleProvider(options), options);
return formatting.formatOnEnter(position, sourceFile, getRuleProvider(settings), settings);
}
return [];

View File

@ -16,7 +16,7 @@
/// <reference path='services.ts' />
/* @internal */
let debugObjectHost = new Function("return this")();
let debugObjectHost = (function (this: any) { return this; })();
// We need to use 'null' to interface with the managed side.
/* tslint:disable:no-null-keyword */
@ -67,6 +67,7 @@ namespace ts {
getProjectVersion?(): string;
useCaseSensitiveFileNames?(): boolean;
getTypeRootsVersion?(): number;
readDirectory(rootDir: string, extension: string, basePaths?: string, excludeEx?: string, includeFileEx?: string, includeDirEx?: string, depth?: number): string;
readFile(path: string, encoding?: string): string;
fileExists(path: string): boolean;
@ -364,6 +365,13 @@ namespace ts {
return this.shimHost.getProjectVersion();
}
public getTypeRootsVersion(): number {
if (!this.shimHost.getTypeRootsVersion) {
return 0;
}
return this.shimHost.getTypeRootsVersion();
}
public useCaseSensitiveFileNames(): boolean {
return this.shimHost.useCaseSensitiveFileNames ? this.shimHost.useCaseSensitiveFileNames() : false;
}

View File

@ -148,6 +148,11 @@ namespace ts {
readFile?(path: string, encoding?: string): string;
fileExists?(path: string): boolean;
/*
* LS host can optionally implement these methods to support automatic updating when new type libraries are installed
*/
getTypeRootsVersion?(): number;
/*
* LS host can optionally implement this method if it wants to be completely in charge of module name resolution.
* if implementation is omitted then language service will use built-in module resolution logic and get answers to
@ -218,23 +223,23 @@ namespace ts {
/** @deprecated */
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string): NavigateToItem[];
getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string, excludeDtsFiles?: boolean): NavigateToItem[];
getNavigationBarItems(fileName: string): NavigationBarItem[];
getOutliningSpans(fileName: string): OutliningSpan[];
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[];
getIndentationAtPosition(fileName: string, position: number, options: EditorOptions): number;
getIndentationAtPosition(fileName: string, position: number, options: EditorOptions | EditorSettings): number;
getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions): TextChange[];
getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions): TextChange[];
getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): TextChange[];
getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions | FormatCodeSettings): TextChange[];
getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[];
getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[];
getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion;
isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean;
getEmitOutput(fileName: string): EmitOutput;
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput;
getProgram(): Program;
@ -339,6 +344,13 @@ namespace ts {
containerKind: string;
}
export enum IndentStyle {
None = 0,
Block = 1,
Smart = 2,
}
/* @deprecated - consider using EditorSettings instead */
export interface EditorOptions {
BaseIndentSize?: number;
IndentSize: number;
@ -348,12 +360,16 @@ namespace ts {
IndentStyle: IndentStyle;
}
export enum IndentStyle {
None = 0,
Block = 1,
Smart = 2,
export interface EditorSettings {
baseIndentSize?: number;
indentSize: number;
tabSize: number;
newLineCharacter: string;
convertTabsToSpaces: boolean;
indentStyle: IndentStyle;
}
/* @deprecated - consider using FormatCodeSettings instead */
export interface FormatCodeOptions extends EditorOptions {
InsertSpaceAfterCommaDelimiter: boolean;
InsertSpaceAfterSemicolonInForStatements: boolean;
@ -368,7 +384,22 @@ namespace ts {
InsertSpaceAfterTypeAssertion?: boolean;
PlaceOpenBraceOnNewLineForFunctions: boolean;
PlaceOpenBraceOnNewLineForControlBlocks: boolean;
[s: string]: boolean | number | string | undefined;
}
export interface FormatCodeSettings extends EditorSettings {
insertSpaceAfterCommaDelimiter: boolean;
insertSpaceAfterSemicolonInForStatements: boolean;
insertSpaceBeforeAndAfterBinaryOperators: boolean;
insertSpaceAfterKeywordsInControlFlowStatements: boolean;
insertSpaceAfterFunctionKeywordForAnonymousFunctions: boolean;
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: boolean;
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: boolean;
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces?: boolean;
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: boolean;
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: boolean;
insertSpaceAfterTypeAssertion?: boolean;
placeOpenBraceOnNewLineForFunctions: boolean;
placeOpenBraceOnNewLineForControlBlocks: boolean;
}
export interface DefinitionInfo {

View File

@ -1335,7 +1335,7 @@ namespace ts {
return ensureScriptKind(fileName, scriptKind);
}
export function parseAndReEmitConfigJSONFile(content: string) {
export function sanitizeConfigFile(configFileName: string, content: string) {
const options: TranspileOptions = {
fileName: "config.js",
compilerOptions: {
@ -1349,10 +1349,13 @@ namespace ts {
// also, the emitted result will have "(" in the beginning and ");" in the end. We need to strip these
// as well
const trimmedOutput = outputText.trim();
const configJsonObject = JSON.parse(trimmedOutput.substring(1, trimmedOutput.length - 2));
for (const diagnostic of diagnostics) {
diagnostic.start = diagnostic.start - 1;
}
return { configJsonObject, diagnostics };
const {config, error} = parseConfigFileTextToJson(configFileName, trimmedOutput.substring(1, trimmedOutput.length - 2), /*stripComments*/ false);
return {
configJsonObject: config || {},
diagnostics: error ? concatenate(diagnostics, [error]) : diagnostics
};
}
}

View File

@ -2,7 +2,7 @@ tests/cases/compiler/ambientWithStatements.ts(2,5): error TS1036: Statements are
tests/cases/compiler/ambientWithStatements.ts(3,5): error TS1104: A 'continue' statement can only be used within an enclosing iteration statement.
tests/cases/compiler/ambientWithStatements.ts(7,15): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter.
tests/cases/compiler/ambientWithStatements.ts(11,5): error TS1108: A 'return' statement can only be used within a function body.
tests/cases/compiler/ambientWithStatements.ts(25,11): error TS2410: All symbols within a 'with' block will be resolved to 'any'.
tests/cases/compiler/ambientWithStatements.ts(25,5): error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
==== tests/cases/compiler/ambientWithStatements.ts (5 errors) ====
@ -39,7 +39,7 @@ tests/cases/compiler/ambientWithStatements.ts(25,11): error TS2410: All symbols
finally {
}
with (x) {
~
!!! error TS2410: All symbols within a 'with' block will be resolved to 'any'.
~~~~~~~~
!!! error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
}
}

View File

@ -1,10 +1,10 @@
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(3,1): error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(3,7): error TS2304: Cannot find name 'window'.
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(3,7): error TS2410: All symbols within a 'with' block will be resolved to 'any'.
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(19,1): error TS2304: Cannot find name 'window'.
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(31,9): error TS2322: Type '() => number' is not assignable to type 'E'.
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(32,16): error TS2332: 'this' cannot be referenced in current location.
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(44,5): error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(44,11): error TS2304: Cannot find name 'window'.
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(44,11): error TS2410: All symbols within a 'with' block will be resolved to 'any'.
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(60,5): error TS2304: Cannot find name 'window'.
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(72,13): error TS2322: Type '() => number' is not assignable to type 'E'.
tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(73,20): error TS2332: 'this' cannot be referenced in current location.
@ -14,10 +14,10 @@ tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(73,20): e
// Arrow function used in with statement
with (window) {
~~~~~~~~~~~~~
!!! error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
~~~~~~
!!! error TS2304: Cannot find name 'window'.
~~~~~~
!!! error TS2410: All symbols within a 'with' block will be resolved to 'any'.
var p = () => this;
}
@ -65,10 +65,10 @@ tests/cases/conformance/expressions/functions/arrowFunctionContexts.ts(73,20): e
module M2 {
// Arrow function used in with statement
with (window) {
~~~~~~~~~~~~~
!!! error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
~~~~~~
!!! error TS2304: Cannot find name 'window'.
~~~~~~
!!! error TS2410: All symbols within a 'with' block will be resolved to 'any'.
var p = () => this;
}

View File

@ -2,7 +2,7 @@ tests/cases/compiler/constDeclarations-invalidContexts.ts(4,5): error TS1156: 'c
tests/cases/compiler/constDeclarations-invalidContexts.ts(6,5): error TS1156: 'const' declarations can only be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(9,5): error TS1156: 'const' declarations can only be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(12,5): error TS1156: 'const' declarations can only be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(16,7): error TS2410: All symbols within a 'with' block will be resolved to 'any'.
tests/cases/compiler/constDeclarations-invalidContexts.ts(16,1): error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
tests/cases/compiler/constDeclarations-invalidContexts.ts(20,5): error TS1156: 'const' declarations can only be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(23,5): error TS1156: 'const' declarations can only be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(26,12): error TS1156: 'const' declarations can only be declared inside a block.
@ -34,8 +34,8 @@ tests/cases/compiler/constDeclarations-invalidContexts.ts(29,29): error TS1156:
var obj;
with (obj)
~~~
!!! error TS2410: All symbols within a 'with' block will be resolved to 'any'.
~~~~~~~~~~
!!! error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
const c5 = 0; // No Error will be reported here since we turn off all type checking
for (var i = 0; i < 10; i++)

View File

@ -1,6 +1,6 @@
tests/cases/compiler/constDeclarations-scopes.ts(13,5): error TS7027: Unreachable code detected.
tests/cases/compiler/constDeclarations-scopes.ts(22,1): error TS7027: Unreachable code detected.
tests/cases/compiler/constDeclarations-scopes.ts(28,7): error TS2410: All symbols within a 'with' block will be resolved to 'any'.
tests/cases/compiler/constDeclarations-scopes.ts(28,1): error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
==== tests/cases/compiler/constDeclarations-scopes.ts (3 errors) ====
@ -36,8 +36,8 @@ tests/cases/compiler/constDeclarations-scopes.ts(28,7): error TS2410: All symbol
var obj;
with (obj) {
~~~
!!! error TS2410: All symbols within a 'with' block will be resolved to 'any'.
~~~~~~~~~~
!!! error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
const c = 0;
n = c;
}

View File

@ -1,4 +1,4 @@
tests/cases/compiler/constDeclarations-validContexts.ts(20,7): error TS2410: All symbols within a 'with' block will be resolved to 'any'.
tests/cases/compiler/constDeclarations-validContexts.ts(20,1): error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
==== tests/cases/compiler/constDeclarations-validContexts.ts (1 errors) ====
@ -22,8 +22,8 @@ tests/cases/compiler/constDeclarations-validContexts.ts(20,7): error TS2410: All
var obj;
with (obj) {
~~~
!!! error TS2410: All symbols within a 'with' block will be resolved to 'any'.
~~~~~~~~~~
!!! error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
const c5 = 0;
}

View File

@ -23,15 +23,15 @@ let c1 = Foo["C"].toString();
//// [constEnumToStringWithComments.js]
var x0 = 100 /* X */..toString();
var x1 = 100 /* "X" */..toString();
var x0 = 100 /* X */.toString();
var x1 = 100 /* "X" */.toString();
var y0 = 0.5 /* Y */.toString();
var y1 = 0.5 /* "Y" */.toString();
var z0 = 2 /* Z */..toString();
var z1 = 2 /* "Z" */..toString();
var a0 = -1 /* A */..toString();
var a1 = -1 /* "A" */..toString();
var z0 = 2 /* Z */.toString();
var z1 = 2 /* "Z" */.toString();
var a0 = -1 /* A */.toString();
var a1 = -1 /* "A" */.toString();
var b0 = -1.5 /* B */.toString();
var b1 = -1.5 /* "B" */.toString();
var c0 = -1 /* C */..toString();
var c1 = -1 /* "C" */..toString();
var c0 = -1 /* C */.toString();
var c1 = -1 /* "C" */.toString();

View File

@ -1,11 +1,11 @@
tests/cases/compiler/es5-asyncFunctionWithStatements.ts(4,5): error TS1300: 'with' statements are not allowed in an async function block.
tests/cases/compiler/es5-asyncFunctionWithStatements.ts(4,11): error TS2410: All symbols within a 'with' block will be resolved to 'any'.
tests/cases/compiler/es5-asyncFunctionWithStatements.ts(4,5): error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
tests/cases/compiler/es5-asyncFunctionWithStatements.ts(10,5): error TS1300: 'with' statements are not allowed in an async function block.
tests/cases/compiler/es5-asyncFunctionWithStatements.ts(10,11): error TS2410: All symbols within a 'with' block will be resolved to 'any'.
tests/cases/compiler/es5-asyncFunctionWithStatements.ts(10,5): error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
tests/cases/compiler/es5-asyncFunctionWithStatements.ts(16,5): error TS1300: 'with' statements are not allowed in an async function block.
tests/cases/compiler/es5-asyncFunctionWithStatements.ts(16,11): error TS2410: All symbols within a 'with' block will be resolved to 'any'.
tests/cases/compiler/es5-asyncFunctionWithStatements.ts(16,5): error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
tests/cases/compiler/es5-asyncFunctionWithStatements.ts(24,5): error TS1300: 'with' statements are not allowed in an async function block.
tests/cases/compiler/es5-asyncFunctionWithStatements.ts(24,11): error TS2410: All symbols within a 'with' block will be resolved to 'any'.
tests/cases/compiler/es5-asyncFunctionWithStatements.ts(24,5): error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
==== tests/cases/compiler/es5-asyncFunctionWithStatements.ts (8 errors) ====
@ -15,8 +15,8 @@ tests/cases/compiler/es5-asyncFunctionWithStatements.ts(24,11): error TS2410: Al
with (x) {
~~~~
!!! error TS1300: 'with' statements are not allowed in an async function block.
~
!!! error TS2410: All symbols within a 'with' block will be resolved to 'any'.
~~~~~~~~
!!! error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
y;
}
}
@ -25,8 +25,8 @@ tests/cases/compiler/es5-asyncFunctionWithStatements.ts(24,11): error TS2410: Al
with (await x) {
~~~~
!!! error TS1300: 'with' statements are not allowed in an async function block.
~~~~~~~
!!! error TS2410: All symbols within a 'with' block will be resolved to 'any'.
~~~~~~~~~~~~~~
!!! error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
y;
}
}
@ -35,8 +35,8 @@ tests/cases/compiler/es5-asyncFunctionWithStatements.ts(24,11): error TS2410: Al
with (x) {
~~~~
!!! error TS1300: 'with' statements are not allowed in an async function block.
~
!!! error TS2410: All symbols within a 'with' block will be resolved to 'any'.
~~~~~~~~
!!! error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
a;
await y;
b;
@ -47,8 +47,8 @@ tests/cases/compiler/es5-asyncFunctionWithStatements.ts(24,11): error TS2410: Al
with (x) {
~~~~
!!! error TS1300: 'with' statements are not allowed in an async function block.
~
!!! error TS2410: All symbols within a 'with' block will be resolved to 'any'.
~~~~~~~~
!!! error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
with (z) {
a;
await y;

View File

@ -0,0 +1,30 @@
//// [tests/cases/compiler/es6UseOfTopLevelRequire.ts] ////
//// [b.ts]
export default function require(s: string): void {
}
//// [c.ts]
export const exports = 0;
export default exports;
//// [a.ts]
import require from "./b"
require("arg");
import exports from "./c"
var x = exports + 2;
//// [b.js]
export default function require(s) {
}
//// [c.js]
export const exports = 0;
export default exports;
//// [a.js]
import require from "./b";
require("arg");
import exports from "./c";
var x = exports + 2;

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