diff --git a/.gitignore b/.gitignore
index 90b078fc94f..40c473d13dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -59,3 +59,11 @@ internal/
.idea
yarn.lock
.parallelperf.*
+tests/cases/user/*/package-lock.json
+tests/cases/user/*/node_modules/
+tests/cases/user/*/**/*.js
+tests/cases/user/*/**/*.js.map
+tests/cases/user/*/**/*.d.ts
+!tests/cases/user/zone.js/
+!tests/cases/user/bignumber.js/
+!tests/cases/user/discord.js/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000000..f83d0f77c9e
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,15 @@
+[submodule "tests/cases/user/TypeScript-React-Starter/TypeScript-React-Starter"]
+ path = tests/cases/user/TypeScript-React-Starter/TypeScript-React-Starter
+ url = https://github.com/Microsoft/TypeScript-React-Starter
+[submodule "tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter"]
+ path = tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter
+ url = https://github.com/Microsoft/TypeScript-Node-Starter.git
+[submodule "tests/cases/user/TypeScript-React-Native-Starter/TypeScript-React-Native-Starter"]
+ path = tests/cases/user/TypeScript-React-Native-Starter/TypeScript-React-Native-Starter
+ url = https://github.com/Microsoft/TypeScript-React-Native-Starter.git
+[submodule "tests/cases/user/TypeScript-Vue-Starter/TypeScript-Vue-Starter"]
+ path = tests/cases/user/TypeScript-Vue-Starter/TypeScript-Vue-Starter
+ url = https://github.com/Microsoft/TypeScript-Vue-Starter.git
+[submodule "tests/cases/user/TypeScript-WeChat-Starter/TypeScript-WeChat-Starter"]
+ path = tests/cases/user/TypeScript-WeChat-Starter/TypeScript-WeChat-Starter
+ url = https://github.com/Microsoft/TypeScript-WeChat-Starter.git
diff --git a/.travis.yml b/.travis.yml
index d24e155b580..06e912c55f5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,8 +2,8 @@ language: node_js
node_js:
- 'stable'
+ - '8'
- '6'
- - '4'
sudo: false
diff --git a/Gulpfile.ts b/Gulpfile.ts
index 283535208ed..aedde5c33d9 100644
--- a/Gulpfile.ts
+++ b/Gulpfile.ts
@@ -46,14 +46,15 @@ const cmdLineOptions = minimist(process.argv.slice(2), {
boolean: ["debug", "inspect", "light", "colors", "lint", "soft"],
string: ["browser", "tests", "host", "reporter", "stackTraceLimit", "timeout"],
alias: {
- b: "browser",
- d: "debug", "debug-brk": "debug",
- i: "inspect", "inspect-brk": "inspect",
- t: "tests", test: "tests",
- r: "reporter",
- c: "colors", color: "colors",
- f: "files", file: "files",
- w: "workers",
+ "b": "browser",
+ "d": "debug", "debug-brk": "debug",
+ "i": "inspect", "inspect-brk": "inspect",
+ "t": "tests", "test": "tests",
+ "ru": "runners", "runner": "runners",
+ "r": "reporter",
+ "c": "colors", "color": "colors",
+ "f": "files", "file": "files",
+ "w": "workers",
},
default: {
soft: false,
@@ -64,6 +65,7 @@ const cmdLineOptions = minimist(process.argv.slice(2), {
browser: process.env.browser || process.env.b || "IE",
timeout: process.env.timeout || 40000,
tests: process.env.test || process.env.tests || process.env.t,
+ runners: process.env.runners || process.env.runner || process.env.ru,
light: process.env.light === undefined || process.env.light !== "false",
reporter: process.env.reporter || process.env.r,
lint: process.env.lint || true,
@@ -72,7 +74,8 @@ const cmdLineOptions = minimist(process.argv.slice(2), {
}
});
-function exec(cmd: string, args: string[], complete: () => void = (() => { }), error: (e: any, status: number) => void = (() => { })) {
+const noop = () => {}; // tslint:disable-line no-empty
+function exec(cmd: string, args: string[], complete: () => void = noop, error: (e: any, status: number) => void = noop) {
console.log(`${cmd} ${args.join(" ")}`);
// TODO (weswig): Update child_process types to add windowsVerbatimArguments to the type definition
const subshellFlag = isWin ? "/c" : "-c";
@@ -99,12 +102,12 @@ const lclDirectory = "src/loc/lcl";
const builtDirectory = "built/";
const builtLocalDirectory = "built/local/";
-const LKGDirectory = "lib/";
+const lkgDirectory = "lib/";
const copyright = "CopyrightNotice.txt";
const compilerFilename = "tsc.js";
-const LKGCompiler = path.join(LKGDirectory, compilerFilename);
+const lkgCompiler = path.join(lkgDirectory, compilerFilename);
const builtLocalCompiler = path.join(builtLocalDirectory, compilerFilename);
const nodeModulesPathPrefix = path.resolve("./node_modules/.bin/");
@@ -123,34 +126,31 @@ const es2015LibrarySources = [
"es2015.symbol.wellknown.d.ts"
];
-const es2015LibrarySourceMap = es2015LibrarySources.map(function(source) {
- return { target: "lib." + source, sources: ["header.d.ts", source] };
-});
+const es2015LibrarySourceMap = es2015LibrarySources.map(source =>
+ ({ target: "lib." + source, sources: ["header.d.ts", source] }));
const es2016LibrarySource = ["es2016.array.include.d.ts"];
-const es2016LibrarySourceMap = es2016LibrarySource.map(function(source) {
- return { target: "lib." + source, sources: ["header.d.ts", source] };
-});
+const es2016LibrarySourceMap = es2016LibrarySource.map(source =>
+ ({ target: "lib." + source, sources: ["header.d.ts", source] }));
const es2017LibrarySource = [
"es2017.object.d.ts",
"es2017.sharedmemory.d.ts",
"es2017.string.d.ts",
"es2017.intl.d.ts",
+ "es2017.typedarrays.d.ts",
];
-const es2017LibrarySourceMap = es2017LibrarySource.map(function(source) {
- return { target: "lib." + source, sources: ["header.d.ts", source] };
-});
+const es2017LibrarySourceMap = es2017LibrarySource.map(source =>
+ ({ target: "lib." + source, sources: ["header.d.ts", source] }));
const esnextLibrarySource = [
"esnext.asynciterable.d.ts"
];
-const esnextLibrarySourceMap = esnextLibrarySource.map(function (source) {
- return { target: "lib." + source, sources: ["header.d.ts", source] };
-});
+const esnextLibrarySourceMap = esnextLibrarySource.map(source =>
+ ({ target: "lib." + source, sources: ["header.d.ts", source] }));
const hostsLibrarySources = ["dom.generated.d.ts", "webworker.importscripts.d.ts", "scripthost.d.ts"];
@@ -176,9 +176,8 @@ const librarySourceMap = [
{ target: "lib.esnext.full.d.ts", sources: ["header.d.ts", "esnext.d.ts"].concat(hostsLibrarySources, "dom.iterable.d.ts") },
].concat(es2015LibrarySourceMap, es2016LibrarySourceMap, es2017LibrarySourceMap, esnextLibrarySourceMap);
-const libraryTargets = librarySourceMap.map(function(f) {
- return path.join(builtLocalDirectory, f.target);
-});
+const libraryTargets = librarySourceMap.map(f =>
+ path.join(builtLocalDirectory, f.target));
/**
* .lcg file is what localization team uses to know what messages to localize.
@@ -193,22 +192,19 @@ const generatedLCGFile = path.join(builtLocalDirectory, "enu", "diagnosticMessag
* 2. 'src\compiler\diagnosticMessages.generated.json' => 'built\local\ENU\diagnosticMessages.generated.json.lcg'
* generate the lcg file (source of messages to localize) from the diagnosticMessages.generated.json
*/
-const localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-BR", "ru", "tr", "zh-CN", "zh-TW"].map(function (f) {
- return path.join(builtLocalDirectory, f, "diagnosticMessages.generated.json");
-}).concat(generatedLCGFile);
+const localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-BR", "ru", "tr", "zh-CN", "zh-TW"]
+ .map(f => path.join(builtLocalDirectory, f, "diagnosticMessages.generated.json"))
+ .concat(generatedLCGFile);
for (const i in libraryTargets) {
const entry = librarySourceMap[i];
const target = libraryTargets[i];
- const sources = [copyright].concat(entry.sources.map(function(s) {
- return path.join(libraryDirectory, s);
- }));
- gulp.task(target, /*help*/ false, [], function() {
- return gulp.src(sources)
+ const sources = [copyright].concat(entry.sources.map(s => path.join(libraryDirectory, s)));
+ gulp.task(target, /*help*/ false, [], () =>
+ gulp.src(sources)
.pipe(newer(target))
.pipe(concat(target, { newLine: "\n\n" }))
- .pipe(gulp.dest("."));
- });
+ .pipe(gulp.dest(".")));
}
const configureNightlyJs = path.join(scriptsDirectory, "configureNightly.js");
@@ -575,9 +571,7 @@ gulp.task(specMd, /*help*/ false, [word2mdJs], (done) => {
const specMDFullPath = path.resolve(specMd);
const cmd = "cscript //nologo " + word2mdJs + " \"" + specWordFullPath + "\" " + "\"" + specMDFullPath + "\"";
console.log(cmd);
- cp.exec(cmd, function() {
- done();
- });
+ cp.exec(cmd, done);
});
gulp.task("generate-spec", "Generates a Markdown version of the Language Specification", [specMd]);
@@ -599,7 +593,7 @@ gulp.task("VerifyLKG", /*help*/ false, [], () => {
". The following files are missing:\n" + missingFiles.join("\n"));
}
// Copy all the targets into the LKG directory
- return gulp.src([...expectedFiles, path.join(builtLocalDirectory, "**"), `!${path.join(builtLocalDirectory, "tslint")}`, `!${path.join(builtLocalDirectory, "*.*")}`]).pipe(gulp.dest(LKGDirectory));
+ return gulp.src([...expectedFiles, path.join(builtLocalDirectory, "**"), `!${path.join(builtLocalDirectory, "tslint")}`, `!${path.join(builtLocalDirectory, "*.*")}`]).pipe(gulp.dest(lkgDirectory));
});
gulp.task("LKGInternal", /*help*/ false, ["lib", "local"]);
@@ -658,6 +652,7 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
const debug = cmdLineOptions.debug;
const inspect = cmdLineOptions.inspect;
const tests = cmdLineOptions.tests;
+ const runners = cmdLineOptions.runners;
const light = cmdLineOptions.light;
const stackTraceLimit = cmdLineOptions.stackTraceLimit;
const testConfigFile = "test.config";
@@ -678,8 +673,8 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
workerCount = cmdLineOptions.workers;
}
- if (tests || light || taskConfigsFolder) {
- writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit);
+ if (tests || runners || light || taskConfigsFolder) {
+ writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit);
}
if (tests && tests.toLocaleLowerCase() === "rwc") {
@@ -714,17 +709,13 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
}
args.push(run);
setNodeEnvToDevelopment();
- exec(mocha, args, lintThenFinish, function(e, status) {
- finish(e, status);
- });
+ exec(mocha, args, lintThenFinish, finish);
}
else {
// run task to load all tests and partition them between workers
setNodeEnvToDevelopment();
- exec(host, [run], lintThenFinish, function(e, status) {
- finish(e, status);
- });
+ exec(host, [run], lintThenFinish, finish);
}
});
@@ -874,8 +865,8 @@ function cleanTestDirs(done: (e?: any) => void) {
}
// used to pass data from jake command line directly to run.js
-function writeTestConfigFile(tests: string, light: boolean, taskConfigsFolder?: string, workerCount?: number, stackTraceLimit?: string) {
- const testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, light, workerCount, stackTraceLimit, taskConfigsFolder, noColor: !cmdLineOptions.colors });
+function writeTestConfigFile(tests: string, runners: string, light: boolean, taskConfigsFolder?: string, workerCount?: number, stackTraceLimit?: string) {
+ const testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, runner: runners ? runners.split(",") : undefined, light, workerCount, stackTraceLimit, taskConfigsFolder, noColor: !cmdLineOptions.colors });
console.log("Running tests with config: " + testConfigContents);
fs.writeFileSync("test.config", testConfigContents);
}
@@ -886,13 +877,14 @@ gulp.task("runtests-browser", "Runs the tests using the built run.js file like '
if (err) { console.error(err); done(err); process.exit(1); }
host = "node";
const tests = cmdLineOptions.tests;
+ const runners = cmdLineOptions.runners;
const light = cmdLineOptions.light;
const testConfigFile = "test.config";
if (fs.existsSync(testConfigFile)) {
fs.unlinkSync(testConfigFile);
}
- if (tests || light) {
- writeTestConfigFile(tests, light);
+ if (tests || runners || light) {
+ writeTestConfigFile(tests, runners, light);
}
const args = [nodeServerOutFile];
@@ -1006,7 +998,7 @@ gulp.task(loggedIOJsPath, /*help*/ false, [], (done) => {
const temp = path.join(builtLocalDirectory, "temp");
mkdirP(temp, (err) => {
if (err) { console.error(err); done(err); process.exit(1); }
- exec(host, [LKGCompiler, "--types", "--target es5", "--lib es5", "--outdir", temp, loggedIOpath], () => {
+ exec(host, [lkgCompiler, "--types", "--target es5", "--lib es5", "--outdir", temp, loggedIOpath], () => {
fs.renameSync(path.join(temp, "/harness/loggedIO.js"), loggedIOJsPath);
del(temp).then(() => done(), done);
}, done);
@@ -1043,7 +1035,7 @@ gulp.task("update-sublime", "Updates the sublime plugin's tsserver", ["local", s
});
gulp.task("build-rules", "Compiles tslint rules to js", () => {
- const settings: tsc.Settings = getCompilerSettings({ module: "commonjs", "lib": ["es6"] }, /*useBuiltCompiler*/ false);
+ const settings: tsc.Settings = getCompilerSettings({ module: "commonjs", lib: ["es6"] }, /*useBuiltCompiler*/ false);
const dest = path.join(builtLocalDirectory, "tslint");
return gulp.src("scripts/tslint/**/*.ts")
.pipe(newer({
@@ -1082,7 +1074,7 @@ function sendNextFile(files: {path: string}[], child: cp.ChildProcess, callback:
function spawnLintWorker(files: {path: string}[], callback: (failures: number) => void) {
const child = cp.fork("./scripts/parallel-lint");
let failures = 0;
- child.on("message", function(data) {
+ child.on("message", data => {
switch (data.kind) {
case "result":
if (data.failures > 0) {
@@ -1106,7 +1098,7 @@ gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are:
const fileMatcher = cmdLineOptions.files;
const files = fileMatcher
? `src/**/${fileMatcher}`
- : "Gulpfile.ts 'scripts/generateLocalizedDiagnosticMessages.ts' 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
+ : "Gulpfile.ts 'scripts/generateLocalizedDiagnosticMessages.ts' 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude 'src/lib/*.d.ts'";
const cmd = `node node_modules/tslint/bin/tslint ${files} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish`;
console.log("Linting: " + cmd);
child_process.execSync(cmd, { stdio: [0, 1, 2] });
diff --git a/Jakefile.js b/Jakefile.js
index da7d96f0699..4f39d6af5c6 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -105,6 +105,7 @@ var harnessCoreSources = [
"projectsRunner.ts",
"loggedIO.ts",
"rwcRunner.ts",
+ "externalCompileRunner.ts",
"test262Runner.ts",
"./parallel/shared.ts",
"./parallel/host.ts",
@@ -196,7 +197,8 @@ var es2017LibrarySource = [
"es2017.object.d.ts",
"es2017.sharedmemory.d.ts",
"es2017.string.d.ts",
- "es2017.intl.d.ts"
+ "es2017.intl.d.ts",
+ "es2017.typedarrays.d.ts",
];
var es2017LibrarySourceMap = es2017LibrarySource.map(function (source) {
@@ -729,7 +731,10 @@ compileFile(word2mdJs,
[word2mdTs],
[word2mdTs],
[],
- /*useBuiltCompiler*/ false);
+ /*useBuiltCompiler*/ false,
+ {
+ lib: "scripthost,es5"
+ });
// The generated spec.md; built for the 'generate-spec' task
file(specMd, [word2mdJs, specWord], function () {
@@ -843,8 +848,9 @@ function cleanTestDirs() {
}
// used to pass data from jake command line directly to run.js
-function writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit, colors) {
+function writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, colors) {
var testConfigContents = JSON.stringify({
+ runners: runners ? runners.split(",") : undefined,
test: tests ? [tests] : undefined,
light: light,
workerCount: workerCount,
@@ -870,6 +876,7 @@ function runConsoleTests(defaultReporter, runInParallel) {
var debug = process.env.debug || process.env["debug-brk"] || process.env.d;
var inspect = process.env.inspect || process.env["inspect-brk"] || process.env.i;
var testTimeout = process.env.timeout || defaultTestTimeout;
+ var runners = process.env.runners || process.env.runner || process.env.ru;
var tests = process.env.test || process.env.tests || process.env.t;
var light = process.env.light === undefined || process.env.light !== "false";
var stackTraceLimit = process.env.stackTraceLimit;
@@ -891,8 +898,8 @@ function runConsoleTests(defaultReporter, runInParallel) {
workerCount = process.env.workerCount || process.env.p || os.cpus().length;
}
- if (tests || light || taskConfigsFolder) {
- writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit, colors);
+ if (tests || runners || light || taskConfigsFolder) {
+ writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, colors);
}
if (tests && tests.toLocaleLowerCase() === "rwc") {
@@ -1027,14 +1034,15 @@ task("runtests-browser", ["browserify", nodeServerOutFile], function () {
cleanTestDirs();
host = "node";
var browser = process.env.browser || process.env.b || (os.platform() === "linux" ? "chrome" : "IE");
+ var runners = process.env.runners || process.env.runner || process.env.ru;
var tests = process.env.test || process.env.tests || process.env.t;
var light = process.env.light || false;
var testConfigFile = 'test.config';
if (fs.existsSync(testConfigFile)) {
fs.unlinkSync(testConfigFile);
}
- if (tests || light) {
- writeTestConfigFile(tests, light);
+ if (tests || runners || light) {
+ writeTestConfigFile(tests, runners, light);
}
tests = tests ? tests : '';
@@ -1282,7 +1290,7 @@ task("lint", ["build-rules"], () => {
const fileMatcher = process.env.f || process.env.file || process.env.files;
const files = fileMatcher
? `src/**/${fileMatcher}`
- : "Gulpfile.ts 'scripts/generateLocalizedDiagnosticMessages.ts' 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
+ : "Gulpfile.ts 'scripts/generateLocalizedDiagnosticMessages.ts' 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude 'src/lib/*.d.ts'";
const cmd = `node node_modules/tslint/bin/tslint ${files} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish`;
console.log("Linting: " + cmd);
jake.exec([cmd], { interactive: true }, () => {
diff --git a/netci.groovy b/netci.groovy
index fc6d00e4e7f..5fa8b02baf2 100644
--- a/netci.groovy
+++ b/netci.groovy
@@ -5,7 +5,7 @@ import jobs.generation.Utilities;
def project = GithubProject
def branch = GithubBranchName
-def nodeVersions = ['stable', '6', '4']
+def nodeVersions = ['stable', '8', '6']
nodeVersions.each { nodeVer ->
diff --git a/package.json b/package.json
index e76d480937c..e329db05d85 100644
--- a/package.json
+++ b/package.json
@@ -74,10 +74,12 @@
"q": "latest",
"run-sequence": "latest",
"sorcery": "latest",
+ "source-map-support": "latest",
"through2": "latest",
"travis-fold": "latest",
"ts-node": "latest",
"tslint": "latest",
+ "vinyl": "latest",
"colors": "latest",
"typescript": "next"
},
diff --git a/scripts/generateLocalizedDiagnosticMessages.ts b/scripts/generateLocalizedDiagnosticMessages.ts
index 00bd8314a9b..566eb557fd5 100644
--- a/scripts/generateLocalizedDiagnosticMessages.ts
+++ b/scripts/generateLocalizedDiagnosticMessages.ts
@@ -87,9 +87,9 @@ function main(): void {
const out: any = {};
for (const item of o.LCX.Item[0].Item[0].Item) {
let ItemId = item.$.ItemId;
- let Val = item.Str[0].Tgt ? item.Str[0].Tgt[0].Val[0] : item.Str[0].Val[0];
+ let val = item.Str[0].Tgt ? item.Str[0].Tgt[0].Val[0] : item.Str[0].Val[0];
- if (typeof ItemId !== "string" || typeof Val !== "string") {
+ if (typeof ItemId !== "string" || typeof val !== "string") {
console.error("Unexpected XML file structure");
process.exit(1);
}
@@ -98,8 +98,8 @@ function main(): void {
ItemId = ItemId.slice(1); // remove leading semicolon
}
- Val = Val.replace(/]5D;/, "]"); // unescape `]`
- out[ItemId] = Val;
+ val = val.replace(/]5D;/, "]"); // unescape `]`
+ out[ItemId] = val;
}
return JSON.stringify(out, undefined, 2);
}
diff --git a/scripts/ior.ts b/scripts/ior.ts
index 91580203350..374747d8439 100644
--- a/scripts/ior.ts
+++ b/scripts/ior.ts
@@ -64,7 +64,6 @@ module Commands {
}
if (path.charAt(1) === ":") {
if (path.charAt(2) === directorySeparator) return 3;
- return 2;
}
return 0;
}
diff --git a/scripts/processDiagnosticMessages.ts b/scripts/processDiagnosticMessages.ts
index ff4047d310d..dd66564b134 100644
--- a/scripts/processDiagnosticMessages.ts
+++ b/scripts/processDiagnosticMessages.ts
@@ -1,4 +1,5 @@
///
+///
interface DiagnosticDetails {
category: string;
@@ -9,80 +10,79 @@ interface DiagnosticDetails {
type InputDiagnosticMessageTable = ts.Map;
function main(): void {
- var sys = ts.sys;
+ const sys = ts.sys;
if (sys.args.length < 1) {
- sys.write("Usage:" + sys.newLine)
+ sys.write("Usage:" + sys.newLine);
sys.write("\tnode processDiagnosticMessages.js " + sys.newLine);
return;
}
function writeFile(fileName: string, contents: string) {
- // TODO: Fix path joining
- var inputDirectory = inputFilePath.substr(0,inputFilePath.lastIndexOf("/"));
- var fileOutputPath = inputDirectory + "/" + fileName;
+ const inputDirectory = ts.getDirectoryPath(inputFilePath);
+ const fileOutputPath = ts.combinePaths(inputDirectory, fileName);
sys.writeFile(fileOutputPath, contents);
}
- var inputFilePath = sys.args[0].replace(/\\/g, "/");
- var inputStr = sys.readFile(inputFilePath);
+ const inputFilePath = sys.args[0].replace(/\\/g, "/");
+ const inputStr = sys.readFile(inputFilePath);
- var diagnosticMessagesJson: { [key: string]: DiagnosticDetails } = JSON.parse(inputStr);
- // Check that there are no duplicates.
- const seenNames = ts.createMap();
- for (const name of Object.keys(diagnosticMessagesJson)) {
- if (seenNames.has(name))
- throw new Error(`Name ${name} appears twice`);
- seenNames.set(name, true);
- }
+ const diagnosticMessagesJson: { [key: string]: DiagnosticDetails } = JSON.parse(inputStr);
const diagnosticMessages: InputDiagnosticMessageTable = ts.createMapFromTemplate(diagnosticMessagesJson);
- var infoFileOutput = buildInfoFileOutput(diagnosticMessages);
+ const outputFilesDir = ts.getDirectoryPath(inputFilePath);
+ const thisFilePathRel = ts.getRelativePathToDirectoryOrUrl(outputFilesDir, sys.getExecutingFilePath(),
+ sys.getCurrentDirectory(), ts.createGetCanonicalFileName(sys.useCaseSensitiveFileNames), /* isAbsolutePathAnUrl */ false);
+
+ const infoFileOutput = buildInfoFileOutput(diagnosticMessages, "./diagnosticInformationMap.generated.ts", thisFilePathRel);
checkForUniqueCodes(diagnosticMessages);
writeFile("diagnosticInformationMap.generated.ts", infoFileOutput);
- var messageOutput = buildDiagnosticMessageOutput(diagnosticMessages);
+ const messageOutput = buildDiagnosticMessageOutput(diagnosticMessages);
writeFile("diagnosticMessages.generated.json", messageOutput);
}
function checkForUniqueCodes(diagnosticTable: InputDiagnosticMessageTable) {
const allCodes: { [key: number]: true | undefined } = [];
diagnosticTable.forEach(({ code }) => {
- if (allCodes[code])
+ if (allCodes[code]) {
throw new Error(`Diagnostic code ${code} appears more than once.`);
+ }
allCodes[code] = true;
});
}
-function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable): string {
- var result =
- '// \r\n' +
- '/// \r\n' +
- '/* @internal */\r\n' +
- 'namespace ts {\r\n' +
+function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, inputFilePathRel: string, thisFilePathRel: string): string {
+ let result =
+ "// \r\n" +
+ "// generated from '" + inputFilePathRel + "' by '" + thisFilePathRel + "'\r\n" +
+ "/// \r\n" +
+ "/* @internal */\r\n" +
+ "namespace ts {\r\n" +
" function diag(code: number, category: DiagnosticCategory, key: string, message: string): DiagnosticMessage {\r\n" +
" return { code, category, key, message };\r\n" +
" }\r\n" +
- ' export const Diagnostics = {\r\n';
+ " // tslint:disable-next-line variable-name\r\n" +
+ " export const Diagnostics = {\r\n";
messageTable.forEach(({ code, category }, name) => {
const propName = convertPropertyName(name);
result += ` ${propName}: diag(${code}, DiagnosticCategory.${category}, "${createKey(propName, code)}", ${JSON.stringify(name)}),\r\n`;
});
- result += ' };\r\n}';
+ result += " };\r\n}";
return result;
}
function buildDiagnosticMessageOutput(messageTable: InputDiagnosticMessageTable): string {
- let result = '{';
+ let result = "{";
messageTable.forEach(({ code }, name) => {
const propName = convertPropertyName(name);
result += `\r\n "${createKey(propName, code)}" : "${name.replace(/[\"]/g, '\\"')}",`;
});
// Shave trailing comma, then add newline and ending brace
- result = result.slice(0, result.length - 1) + '\r\n}';
+ result = result.slice(0, result.length - 1) + "\r\n}";
// Assert that we generated valid JSON
JSON.parse(result);
@@ -90,15 +90,15 @@ function buildDiagnosticMessageOutput(messageTable: InputDiagnosticMessageTable)
return result;
}
-function createKey(name: string, code: number) : string {
- return name.slice(0, 100) + '_' + code;
+function createKey(name: string, code: number): string {
+ return name.slice(0, 100) + "_" + code;
}
function convertPropertyName(origName: string): string {
- var result = origName.split("").map(char => {
- if (char === '*') { return "_Asterisk"; }
- if (char === '/') { return "_Slash"; }
- if (char === ':') { return "_Colon"; }
+ let result = origName.split("").map(char => {
+ if (char === "*") { return "_Asterisk"; }
+ if (char === "/") { return "_Slash"; }
+ if (char === ":") { return "_Colon"; }
return /\w/.test(char) ? char : "_";
}).join("");
@@ -106,7 +106,7 @@ function convertPropertyName(origName: string): string {
result = result.replace(/_+/g, "_");
// remove any leading underscore, unless it is followed by a number.
- result = result.replace(/^_([^\d])/, "$1")
+ result = result.replace(/^_([^\d])/, "$1");
// get rid of all trailing underscores.
result = result.replace(/_$/, "");
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 602a9c505ad..a0146740678 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -133,7 +133,7 @@ namespace ts {
let symbolCount = 0;
- let Symbol: { new (flags: SymbolFlags, name: __String): Symbol };
+ let Symbol: { new (flags: SymbolFlags, name: __String): Symbol }; // tslint:disable-line variable-name
let classifiableNames: UnderscoreEscapedMap;
const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
@@ -1550,7 +1550,7 @@ namespace ts {
function setExportContextFlag(node: ModuleDeclaration | SourceFile) {
// A declaration source file or ambient module declaration that contains no export declarations (but possibly regular
// declarations with export modifiers) is an export context in which declarations are implicitly exported.
- if (isInAmbientContext(node) && !hasExportDeclarations(node)) {
+ if (node.flags & NodeFlags.Ambient && !hasExportDeclarations(node)) {
node.flags |= NodeFlags.ExportContext;
}
else {
@@ -1570,7 +1570,7 @@ namespace ts {
else {
let pattern: Pattern | undefined;
if (node.name.kind === SyntaxKind.StringLiteral) {
- const text = (node.name).text;
+ const { text } = node.name;
if (hasZeroOrOneAsteriskCharacter(text)) {
pattern = tryParsePattern(text);
}
@@ -1589,22 +1589,13 @@ namespace ts {
else {
const state = declareModuleSymbol(node);
if (state !== ModuleInstanceState.NonInstantiated) {
- if (node.symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.RegularEnum)) {
- // if module was already merged with some function, class or non-const enum
- // treat is a non-const-enum-only
- node.symbol.constEnumOnlyModule = false;
- }
- else {
- const currentModuleIsConstEnumOnly = state === ModuleInstanceState.ConstEnumOnly;
- if (node.symbol.constEnumOnlyModule === undefined) {
- // non-merged case - use the current state
- node.symbol.constEnumOnlyModule = currentModuleIsConstEnumOnly;
- }
- else {
- // merged case: module is const enum only if all its pieces are non-instantiated or const enum
- node.symbol.constEnumOnlyModule = node.symbol.constEnumOnlyModule && currentModuleIsConstEnumOnly;
- }
- }
+ const { symbol } = node;
+ // if module was already merged with some function, class or non-const enum, treat it as non-const-enum-only
+ symbol.constEnumOnlyModule = (!(symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.RegularEnum)))
+ // Current must be `const enum` only
+ && state === ModuleInstanceState.ConstEnumOnly
+ // Can't have been set to 'false' in a previous merged symbol. ('undefined' OK)
+ && symbol.constEnumOnlyModule !== false;
}
}
}
@@ -1726,7 +1717,7 @@ namespace ts {
node.originalKeywordKind >= SyntaxKind.FirstFutureReservedWord &&
node.originalKeywordKind <= SyntaxKind.LastFutureReservedWord &&
!isIdentifierName(node) &&
- !isInAmbientContext(node)) {
+ !(node.flags & NodeFlags.Ambient)) {
// Report error only if there are no parse errors in file
if (!file.parseDiagnostics.length) {
@@ -2205,15 +2196,14 @@ namespace ts {
bindAnonymousDeclaration(node, SymbolFlags.Alias, getDeclarationName(node));
}
else {
- // An export default clause with an expression exports a value
- // We want to exclude both class and function here, this is necessary to issue an error when there are both
- // default export-assignment and default export function and class declaration.
- const flags = node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node)
+ const flags = node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node)
// An export default clause with an EntityNameExpression exports all meanings of that identifier
? SymbolFlags.Alias
// An export default clause with any other expression exports a value
: SymbolFlags.Property;
- declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.Property | SymbolFlags.AliasExcludes | SymbolFlags.Class | SymbolFlags.Function);
+ // If there is an `export default x;` alias declaration, can't `export default` anything else.
+ // (In contrast, you can still have `export default function f() {}` and `export default interface I {}`.)
+ declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.All);
}
}
@@ -2481,7 +2471,7 @@ namespace ts {
}
function bindParameter(node: ParameterDeclaration) {
- if (inStrictMode && !isInAmbientContext(node)) {
+ if (inStrictMode && !(node.flags & NodeFlags.Ambient)) {
// It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a
// strict mode FunctionLikeDeclaration or FunctionExpression(13.1)
checkStrictModeEvalOrArguments(node, node.name);
@@ -2503,7 +2493,7 @@ namespace ts {
}
function bindFunctionDeclaration(node: FunctionDeclaration) {
- if (!file.isDeclarationFile && !isInAmbientContext(node)) {
+ if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient)) {
if (isAsyncFunction(node)) {
emitFlags |= NodeFlags.HasAsyncFunctions;
}
@@ -2520,7 +2510,7 @@ namespace ts {
}
function bindFunctionExpression(node: FunctionExpression) {
- if (!file.isDeclarationFile && !isInAmbientContext(node)) {
+ if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient)) {
if (isAsyncFunction(node)) {
emitFlags |= NodeFlags.HasAsyncFunctions;
}
@@ -2534,7 +2524,7 @@ namespace ts {
}
function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
- if (!file.isDeclarationFile && !isInAmbientContext(node) && isAsyncFunction(node)) {
+ if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient) && isAsyncFunction(node)) {
emitFlags |= NodeFlags.HasAsyncFunctions;
}
@@ -2583,7 +2573,7 @@ namespace ts {
// On the other side we do want to report errors on non-initialized 'lets' because of TDZ
const reportUnreachableCode =
!options.allowUnreachableCode &&
- !isInAmbientContext(node) &&
+ !(node.flags & NodeFlags.Ambient) &&
(
node.kind !== SyntaxKind.VariableStatement ||
getCombinedNodeFlags((node).declarationList) & NodeFlags.BlockScoped ||
@@ -2963,6 +2953,7 @@ namespace ts {
|| hasModifier(node, ModifierFlags.TypeScriptModifier)
|| node.typeParameters
|| node.type
+ || (node.name && isComputedPropertyName(node.name)) // While computed method names aren't typescript, the TS transform must visit them to emit property declarations correctly
|| !node.body) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -2993,6 +2984,7 @@ namespace ts {
if (node.decorators
|| hasModifier(node, ModifierFlags.TypeScriptModifier)
|| node.type
+ || (node.name && isComputedPropertyName(node.name)) // While computed accessor names aren't typescript, the TS transform must visit them to emit property declarations correctly
|| !node.body) {
transformFlags |= TransformFlags.AssertTypeScript;
}
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index ccb7c869137..4f261fbff2c 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -48,9 +48,11 @@ namespace ts {
let requestedExternalEmitHelpers: ExternalEmitHelpers;
let externalHelpersModule: Symbol;
+ // tslint:disable variable-name
const Symbol = objectAllocator.getSymbolConstructor();
const Type = objectAllocator.getTypeConstructor();
const Signature = objectAllocator.getSignatureConstructor();
+ // tslint:enable variable-name
let typeCount = 0;
let symbolCount = 0;
@@ -64,7 +66,7 @@ namespace ts {
const languageVersion = getEmitScriptTarget(compilerOptions);
const modulekind = getEmitModuleKind(compilerOptions);
const noUnusedIdentifiers = !!compilerOptions.noUnusedLocals || !!compilerOptions.noUnusedParameters;
- const allowSyntheticDefaultImports = typeof compilerOptions.allowSyntheticDefaultImports !== "undefined" ? compilerOptions.allowSyntheticDefaultImports : modulekind === ModuleKind.System;
+ const allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions);
const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");
const strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes");
const noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny");
@@ -281,6 +283,7 @@ namespace ts {
const voidType = createIntrinsicType(TypeFlags.Void, "void");
const neverType = createIntrinsicType(TypeFlags.Never, "never");
const silentNeverType = createIntrinsicType(TypeFlags.Never, "never");
+ const implicitNeverType = createIntrinsicType(TypeFlags.Never, "never");
const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object");
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
@@ -452,29 +455,29 @@ namespace ts {
}
const typeofEQFacts = createMapFromTemplate({
- "string": TypeFacts.TypeofEQString,
- "number": TypeFacts.TypeofEQNumber,
- "boolean": TypeFacts.TypeofEQBoolean,
- "symbol": TypeFacts.TypeofEQSymbol,
- "undefined": TypeFacts.EQUndefined,
- "object": TypeFacts.TypeofEQObject,
- "function": TypeFacts.TypeofEQFunction
+ string: TypeFacts.TypeofEQString,
+ number: TypeFacts.TypeofEQNumber,
+ boolean: TypeFacts.TypeofEQBoolean,
+ symbol: TypeFacts.TypeofEQSymbol,
+ undefined: TypeFacts.EQUndefined,
+ object: TypeFacts.TypeofEQObject,
+ function: TypeFacts.TypeofEQFunction
});
const typeofNEFacts = createMapFromTemplate({
- "string": TypeFacts.TypeofNEString,
- "number": TypeFacts.TypeofNENumber,
- "boolean": TypeFacts.TypeofNEBoolean,
- "symbol": TypeFacts.TypeofNESymbol,
- "undefined": TypeFacts.NEUndefined,
- "object": TypeFacts.TypeofNEObject,
- "function": TypeFacts.TypeofNEFunction
+ string: TypeFacts.TypeofNEString,
+ number: TypeFacts.TypeofNENumber,
+ boolean: TypeFacts.TypeofNEBoolean,
+ symbol: TypeFacts.TypeofNESymbol,
+ undefined: TypeFacts.NEUndefined,
+ object: TypeFacts.TypeofNEObject,
+ function: TypeFacts.TypeofNEFunction
});
const typeofTypesByName = createMapFromTemplate({
- "string": stringType,
- "number": numberType,
- "boolean": booleanType,
- "symbol": esSymbolType,
- "undefined": undefinedType
+ string: stringType,
+ number: numberType,
+ boolean: booleanType,
+ symbol: esSymbolType,
+ undefined: undefinedType
});
const typeofType = createTypeofType();
@@ -488,17 +491,6 @@ namespace ts {
/** Things we lazy load from the JSX namespace */
const jsxTypes = createUnderscoreEscapedMap();
- const JsxNames = {
- JSX: "JSX" as __String,
- IntrinsicElements: "IntrinsicElements" as __String,
- ElementClass: "ElementClass" as __String,
- ElementAttributesPropertyNameContainer: "ElementAttributesProperty" as __String,
- ElementChildrenAttributeNameContainer: "ElementChildrenAttribute" as __String,
- Element: "Element" as __String,
- IntrinsicAttributes: "IntrinsicAttributes" as __String,
- IntrinsicClassAttributes: "IntrinsicClassAttributes" as __String
- };
-
const subtypeRelation = createMap();
const assignableRelation = createMap();
const comparableRelation = createMap();
@@ -529,6 +521,11 @@ namespace ts {
Strict,
}
+ const enum MappedTypeModifiers {
+ Readonly = 1 << 0,
+ Optional = 1 << 1,
+ }
+
const builtinGlobals = createSymbolTable();
builtinGlobals.set(undefinedSymbol.escapedName, undefinedSymbol);
@@ -691,7 +688,7 @@ namespace ts {
else {
// find a module that about to be augmented
// do not validate names of augmentations that are defined in ambient context
- const moduleNotFoundError = !isInAmbientContext(moduleName.parent.parent)
+ const moduleNotFoundError = !(moduleName.parent.parent.flags & NodeFlags.Ambient)
? Diagnostics.Invalid_module_name_in_augmentation_module_0_cannot_be_found
: undefined;
let mainModule = resolveExternalModuleNameWorker(moduleName, moduleName, moduleNotFoundError, /*isForAugmentation*/ true);
@@ -796,7 +793,7 @@ namespace ts {
if ((modulekind && (declarationFile.externalModuleIndicator || useFile.externalModuleIndicator)) ||
(!compilerOptions.outFile && !compilerOptions.out) ||
isInTypeQuery(usage) ||
- isInAmbientContext(declaration)) {
+ declaration.flags & NodeFlags.Ambient) {
// nodes are in different files and order cannot be determined
return true;
}
@@ -1384,7 +1381,7 @@ namespace ts {
Debug.assert(declaration !== undefined, "Declaration to checkResolvedBlockScopedVariable is undefined");
- if (!isInAmbientContext(declaration) && !isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) {
+ if (!(declaration.flags & NodeFlags.Ambient) && !isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) {
if (result.flags & SymbolFlags.BlockScopedVariable) {
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration)));
}
@@ -1907,8 +1904,9 @@ namespace ts {
* Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument
* Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables
*/
- function extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: ExportCollisionTrackerTable, exportNode?: ExportDeclaration) {
- source && source.forEach((sourceSymbol, id) => {
+ function extendExportSymbols(target: SymbolTable, source: SymbolTable | undefined, lookupTable?: ExportCollisionTrackerTable, exportNode?: ExportDeclaration) {
+ if (!source) return;
+ source.forEach((sourceSymbol, id) => {
if (id === "default") return;
const targetSymbol = target.get(id);
@@ -2163,7 +2161,6 @@ namespace ts {
return forEachEntry(symbols, symbolFromSymbolTable => {
if (symbolFromSymbolTable.flags & SymbolFlags.Alias
&& symbolFromSymbolTable.escapedName !== "export="
- && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)
&& !(isUMDExportSymbol(symbolFromSymbolTable) && enclosingDeclaration && isExternalModule(getSourceFileOfNode(enclosingDeclaration)))
// If `!useOnlyExternalAliasing`, we can use any type of alias to get the name
&& (!useOnlyExternalAliasing || some(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration))) {
@@ -2854,8 +2851,7 @@ namespace ts {
function mapToTypeNodes(types: Type[], context: NodeBuilderContext): TypeNode[] {
if (some(types)) {
const result = [];
- for (let i = 0; i < types.length; ++i) {
- const type = types[i];
+ for (const type of types) {
const typeNode = typeToTypeNodeHelper(type, context);
if (typeNode) {
result.push(typeNode);
@@ -3949,7 +3945,7 @@ namespace ts {
const parent = getDeclarationContainer(node);
// If the node is not exported or it is not ambient module element (except import declaration)
if (!(getCombinedModifierFlags(node) & ModifierFlags.Export) &&
- !(node.kind !== SyntaxKind.ImportEqualsDeclaration && parent.kind !== SyntaxKind.SourceFile && isInAmbientContext(parent))) {
+ !(node.kind !== SyntaxKind.ImportEqualsDeclaration && parent.kind !== SyntaxKind.SourceFile && parent.flags & NodeFlags.Ambient)) {
return isGlobalSourceFile(parent);
}
// Exported members/ambient module elements (exception import declaration) are visible if parent is visible
@@ -4327,7 +4323,7 @@ namespace ts {
if ((noImplicitAny || isInJavaScriptFile(declaration)) &&
declaration.kind === SyntaxKind.VariableDeclaration && !isBindingPattern(declaration.name) &&
- !(getCombinedModifierFlags(declaration) & ModifierFlags.Export) && !isInAmbientContext(declaration)) {
+ !(getCombinedModifierFlags(declaration) & ModifierFlags.Export) && !(declaration.flags & NodeFlags.Ambient)) {
// If --noImplicitAny is on or the declaration is in a Javascript file,
// use control flow tracked 'any' type for non-ambient, non-exported var or let variables with no
// initializer or a 'null' or 'undefined' initializer.
@@ -5204,7 +5200,7 @@ namespace ts {
function isLiteralEnumMember(member: EnumMember) {
const expr = member.initializer;
if (!expr) {
- return !isInAmbientContext(member);
+ return !(member.flags & NodeFlags.Ambient);
}
switch (expr.kind) {
case SyntaxKind.StringLiteral:
@@ -5879,6 +5875,17 @@ namespace ts {
return type.modifiersType;
}
+ function getMappedTypeModifiers(type: MappedType): MappedTypeModifiers {
+ return (type.declaration.readonlyToken ? MappedTypeModifiers.Readonly : 0) |
+ (type.declaration.questionToken ? MappedTypeModifiers.Optional : 0);
+ }
+
+ function getCombinedMappedTypeModifiers(type: MappedType): MappedTypeModifiers {
+ const modifiersType = getModifiersTypeFromMappedType(type);
+ return getMappedTypeModifiers(type) |
+ (isGenericMappedType(modifiersType) ? getMappedTypeModifiers(modifiersType) : 0);
+ }
+
function isPartialMappedType(type: Type) {
return getObjectFlags(type) & ObjectFlags.Mapped && !!(type).declaration.questionToken;
}
@@ -7305,6 +7312,9 @@ namespace ts {
property.type = typeParameter;
properties.push(property);
}
+ const lengthSymbol = createSymbol(SymbolFlags.Property, "length" as __String);
+ lengthSymbol.type = getLiteralType(arity);
+ properties.push(lengthSymbol);
const type = createObjectType(ObjectFlags.Tuple | ObjectFlags.Reference);
type.typeParameters = typeParameters;
type.outerTypeParameters = undefined;
@@ -7354,28 +7364,12 @@ namespace ts {
unionIndex?: number;
}
- function binarySearchTypes(types: Type[], type: Type): number {
- let low = 0;
- let high = types.length - 1;
- const typeId = type.id;
- while (low <= high) {
- const middle = low + ((high - low) >> 1);
- const id = types[middle].id;
- if (id === typeId) {
- return middle;
- }
- else if (id > typeId) {
- high = middle - 1;
- }
- else {
- low = middle + 1;
- }
- }
- return ~low;
+ function getTypeId(type: Type) {
+ return type.id;
}
function containsType(types: Type[], type: Type): boolean {
- return binarySearchTypes(types, type) >= 0;
+ return binarySearch(types, type, getTypeId, compareValues) >= 0;
}
// Return true if the given intersection type contains (a) more than one unit type or (b) an object
@@ -7416,7 +7410,7 @@ namespace ts {
if (flags & TypeFlags.Number) typeSet.containsNumber = true;
if (flags & TypeFlags.StringOrNumberLiteral) typeSet.containsStringOrNumberLiteral = true;
const len = typeSet.length;
- const index = len && type.id > typeSet[len - 1].id ? ~len : binarySearchTypes(typeSet, type);
+ const index = len && type.id > typeSet[len - 1].id ? ~len : binarySearch(typeSet, type, getTypeId, compareValues);
if (index < 0) {
if (!(flags & TypeFlags.Object && (type).objectFlags & ObjectFlags.Anonymous &&
type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && containsIdenticalType(typeSet, type))) {
@@ -7443,9 +7437,12 @@ namespace ts {
return false;
}
- function isSubtypeOfAny(candidate: Type, types: Type[]): boolean {
- for (const type of types) {
- if (candidate !== type && isTypeSubtypeOf(candidate, type)) {
+ function isSubtypeOfAny(source: Type, targets: Type[]): boolean {
+ for (const target of targets) {
+ if (source !== target && isTypeSubtypeOf(source, target) && (
+ !(getObjectFlags(getTargetType(source)) & ObjectFlags.Class) ||
+ !(getObjectFlags(getTargetType(target)) & ObjectFlags.Class) ||
+ isTypeDerivedFrom(source, target))) {
return true;
}
}
@@ -7592,11 +7589,11 @@ namespace ts {
}
}
- // Add the given types to the given type set. Order is preserved, duplicates are removed,
- // and nested types of the given kind are flattened into the set.
+ // Add the given types to the given type set. Order is preserved, freshness is removed from literal
+ // types, duplicates are removed, and nested types of the given kind are flattened into the set.
function addTypesToIntersection(typeSet: TypeSet, types: Type[]) {
for (const type of types) {
- addTypeToIntersection(typeSet, type);
+ addTypeToIntersection(typeSet, getRegularTypeOfLiteralType(type));
}
}
@@ -7685,7 +7682,7 @@ namespace ts {
function getIndexTypeOrString(type: Type): Type {
const indexType = getIndexType(type);
- return indexType !== neverType ? indexType : stringType;
+ return indexType.flags & TypeFlags.Never ? stringType : indexType;
}
function getTypeFromTypeOperatorNode(node: TypeOperatorNode) {
@@ -7994,11 +7991,16 @@ namespace ts {
}
}
- const spread = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
+ const spread = createAnonymousType(
+ symbol,
+ members,
+ emptyArray,
+ emptyArray,
+ getNonReadonlyIndexSignature(stringIndexInfo),
+ getNonReadonlyIndexSignature(numberIndexInfo));
spread.flags |= propagatedFlags;
spread.flags |= TypeFlags.FreshLiteral | TypeFlags.ContainsObjectLiteral;
(spread as ObjectType).objectFlags |= (ObjectFlags.ObjectLiteral | ObjectFlags.ContainsSpread);
- spread.symbol = symbol;
return spread;
}
@@ -8014,6 +8016,13 @@ namespace ts {
return result;
}
+ function getNonReadonlyIndexSignature(index: IndexInfo) {
+ if (index && index.isReadonly) {
+ return createIndexInfo(index.type, /*isReadonly*/ false, index.declaration);
+ }
+ return index;
+ }
+
function isClassMethod(prop: Symbol) {
return prop.flags & SymbolFlags.Method && find(prop.declarations, decl => isClassLike(decl.parent));
}
@@ -8568,12 +8577,19 @@ namespace ts {
return isTypeRelatedTo(source, target, assignableRelation);
}
- // A type S is considered to be an instance of a type T if S and T are the same type or if S is a
- // subtype of T but not structurally identical to T. This specifically means that two distinct but
- // structurally identical types (such as two classes) are not considered instances of each other.
- function isTypeInstanceOf(source: Type, target: Type): boolean {
- return getTargetType(source) === getTargetType(target) || isTypeSubtypeOf(source, target) && !isTypeIdenticalTo(source, target);
- }
+ // An object type S is considered to be derived from an object type T if
+ // S is a union type and every constituent of S is derived from T,
+ // T is a union type and S is derived from at least one constituent of T, or
+ // T is one of the global types Object and Function and S is a subtype of T, or
+ // T occurs directly or indirectly in an 'extends' clause of S.
+ // Note that this check ignores type parameters and only considers the
+ // inheritance hierarchy.
+ function isTypeDerivedFrom(source: Type, target: Type): boolean {
+ return source.flags & TypeFlags.Union ? every((source).types, t => isTypeDerivedFrom(t, target)) :
+ target.flags & TypeFlags.Union ? some((target).types, t => isTypeDerivedFrom(source, t)) :
+ target === globalObjectType || target === globalFunctionType ? isTypeSubtypeOf(source, target) :
+ hasBaseType(source, getTargetType(target));
+ }
/**
* This is *not* a bi-directional relationship.
@@ -8855,8 +8871,8 @@ namespace ts {
function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) {
const s = source.flags;
const t = target.flags;
- if (t & TypeFlags.Never) return false;
if (t & TypeFlags.Any || s & TypeFlags.Never) return true;
+ if (t & TypeFlags.Never) return false;
if (s & TypeFlags.StringLike && t & TypeFlags.String) return true;
if (s & TypeFlags.StringLiteral && s & TypeFlags.EnumLiteral &&
t & TypeFlags.StringLiteral && !(t & TypeFlags.EnumLiteral) &&
@@ -8894,7 +8910,9 @@ namespace ts {
if (target.flags & TypeFlags.StringOrNumberLiteral && target.flags & TypeFlags.FreshLiteral) {
target = (target).regularType;
}
- if (source === target || relation !== identityRelation && isSimpleTypeRelatedTo(source, target, relation)) {
+ if (source === target ||
+ relation === comparableRelation && !(target.flags & TypeFlags.Never) && isSimpleTypeRelatedTo(target, source, relation) ||
+ relation !== identityRelation && isSimpleTypeRelatedTo(source, target, relation)) {
return true;
}
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
@@ -9037,7 +9055,8 @@ namespace ts {
return isIdenticalTo(source, target);
}
- if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True;
+ if (relation === comparableRelation && !(target.flags & TypeFlags.Never) && isSimpleTypeRelatedTo(target, source, relation) ||
+ isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True;
if (isObjectLiteralType(source) && source.flags & TypeFlags.FreshLiteral) {
if (hasExcessProperties(source, target, reportErrors)) {
@@ -9242,20 +9261,24 @@ namespace ts {
return Ternary.False;
}
+ // Keep this up-to-date with the same logic within `getApparentTypeOfContextualType`, since they should behave similarly
function findMatchingDiscriminantType(source: Type, target: UnionOrIntersectionType) {
let match: Type;
const sourceProperties = getPropertiesOfObjectType(source);
if (sourceProperties) {
- const sourceProperty = findSingleDiscriminantProperty(sourceProperties, target);
- if (sourceProperty) {
- const sourceType = getTypeOfSymbol(sourceProperty);
- for (const type of target.types) {
- const targetType = getTypeOfPropertyOfType(type, sourceProperty.escapedName);
- if (targetType && isRelatedTo(sourceType, targetType)) {
- if (match) {
- return undefined;
+ const sourcePropertiesFiltered = findDiscriminantProperties(sourceProperties, target);
+ if (sourcePropertiesFiltered) {
+ for (const sourceProperty of sourcePropertiesFiltered) {
+ const sourceType = getTypeOfSymbol(sourceProperty);
+ for (const type of target.types) {
+ const targetType = getTypeOfPropertyOfType(type, sourceProperty.escapedName);
+ if (targetType && isRelatedTo(sourceType, targetType)) {
+ if (type === match) continue; // Finding multiple fields which discriminate to the same type is fine
+ if (match) {
+ return undefined;
+ }
+ match = type;
}
- match = type;
}
}
}
@@ -9426,6 +9449,7 @@ namespace ts {
function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
let result: Ternary;
+ let originalErrorInfo: DiagnosticMessageChain;
const saveErrorInfo = errorInfo;
if (target.flags & TypeFlags.TypeParameter) {
// A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P].
@@ -9505,6 +9529,7 @@ namespace ts {
// if we have indexed access types with identical index types, see if relationship holds for
// the two object types.
if (result = isRelatedTo((source).objectType, (target).objectType, reportErrors)) {
+ errorInfo = saveErrorInfo;
return result;
}
}
@@ -9536,6 +9561,10 @@ namespace ts {
if (!(reportErrors && some(variances, v => v === Variance.Invariant))) {
return Ternary.False;
}
+ // We remember the original error information so we can restore it in case the structural
+ // comparison unexpectedly succeeds. This can happen when the structural comparison result
+ // is a Ternary.Maybe for example caused by the recursion depth limiter.
+ originalErrorInfo = errorInfo;
errorInfo = saveErrorInfo;
}
}
@@ -9574,8 +9603,11 @@ namespace ts {
}
}
if (result) {
- errorInfo = saveErrorInfo;
- return result;
+ if (!originalErrorInfo) {
+ errorInfo = saveErrorInfo;
+ return result;
+ }
+ errorInfo = originalErrorInfo;
}
}
}
@@ -9586,13 +9618,10 @@ namespace ts {
// related to Y, where X' is an instantiation of X in which P is replaced with Q. Notice
// that S and T are contra-variant whereas X and Y are co-variant.
function mappedTypeRelatedTo(source: MappedType, target: MappedType, reportErrors: boolean): Ternary {
- const sourceReadonly = !!source.declaration.readonlyToken;
- const sourceOptional = !!source.declaration.questionToken;
- const targetReadonly = !!target.declaration.readonlyToken;
- const targetOptional = !!target.declaration.questionToken;
- const modifiersRelated = relation === identityRelation ?
- sourceReadonly === targetReadonly && sourceOptional === targetOptional :
- relation === comparableRelation || !sourceOptional || targetOptional;
+ const modifiersRelated = relation === comparableRelation || (
+ relation === identityRelation ? getMappedTypeModifiers(source) === getMappedTypeModifiers(target) :
+ !(getCombinedMappedTypeModifiers(source) & MappedTypeModifiers.Optional) ||
+ getCombinedMappedTypeModifiers(target) & MappedTypeModifiers.Optional);
if (modifiersRelated) {
let result: Ternary;
if (result = isRelatedTo(getConstraintTypeFromMappedType(target), getConstraintTypeFromMappedType(source), reportErrors)) {
@@ -9607,7 +9636,7 @@ namespace ts {
if (relation === identityRelation) {
return propertiesIdenticalTo(source, target);
}
- const requireOptionalProperties = relation === subtypeRelation && !isObjectLiteralType(source);
+ const requireOptionalProperties = relation === subtypeRelation && !isObjectLiteralType(source) && !isEmptyArrayLiteralType(source);
const unmatchedProperty = getUnmatchedProperty(source, target, requireOptionalProperties);
if (unmatchedProperty) {
if (reportErrors) {
@@ -10315,6 +10344,11 @@ namespace ts {
!(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType);
}
+ function isEmptyArrayLiteralType(type: Type): boolean {
+ const elementType = isArrayType(type) ? (type).typeArguments[0] : undefined;
+ return elementType === undefinedWideningType || elementType === implicitNeverType;
+ }
+
function isTupleLikeType(type: Type): boolean {
return !!getPropertyOfType(type, "0" as __String);
}
@@ -10795,7 +10829,7 @@ namespace ts {
}
function getUnmatchedProperty(source: Type, target: Type, requireOptionalProperties: boolean) {
- const properties = getPropertiesOfObjectType(target);
+ const properties = target.flags & TypeFlags.Intersection ? getPropertiesOfUnionOrIntersectionType(target) : getPropertiesOfObjectType(target);
for (const targetProp of properties) {
if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional)) {
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
@@ -10869,22 +10903,26 @@ namespace ts {
// Because the anyFunctionType is internal, it should not be exposed to the user by adding
// it as an inference candidate. Hopefully, a better candidate will come along that does
// not contain anyFunctionType when we come back to this argument for its second round
- // of inference. Also, we exclude inferences for silentNeverType which is used as a wildcard
- // when constructing types from type parameters that had no inference candidates.
+ // of inference. Also, we exclude inferences for silentNeverType (which is used as a wildcard
+ // when constructing types from type parameters that had no inference candidates).
if (source.flags & TypeFlags.ContainsAnyFunctionType || source === silentNeverType) {
return;
}
const inference = getInferenceInfoForType(target);
if (inference) {
if (!inference.isFixed) {
- if (!inference.candidates || priority < inference.priority) {
+ // We give lowest priority to inferences of implicitNeverType (which is used as the
+ // element type for empty array literals). Thus, inferences from empty array literals
+ // only matter when no other inferences are made.
+ const p = priority | (source === implicitNeverType ? InferencePriority.NeverType : 0);
+ if (!inference.candidates || p < inference.priority) {
inference.candidates = [source];
- inference.priority = priority;
+ inference.priority = p;
}
- else if (priority === inference.priority) {
+ else if (p === inference.priority) {
inference.candidates.push(source);
}
- if (!(priority & InferencePriority.ReturnType) && target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, target)) {
+ if (!(p & InferencePriority.ReturnType) && target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, target)) {
inference.topLevel = false;
}
}
@@ -11357,14 +11395,15 @@ namespace ts {
return false;
}
- function findSingleDiscriminantProperty(sourceProperties: Symbol[], target: Type): Symbol | undefined {
- let result: Symbol;
+ function findDiscriminantProperties(sourceProperties: Symbol[], target: Type): Symbol[] | undefined {
+ let result: Symbol[];
for (const sourceProperty of sourceProperties) {
if (isDiscriminantProperty(target, sourceProperty.escapedName)) {
if (result) {
- return undefined;
+ result.push(sourceProperty);
+ continue;
}
- result = sourceProperty;
+ result = [sourceProperty];
}
}
return result;
@@ -12418,7 +12457,7 @@ namespace ts {
}
if (targetType) {
- return getNarrowedType(type, targetType, assumeTrue, isTypeInstanceOf);
+ return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom);
}
return type;
@@ -12748,7 +12787,7 @@ namespace ts {
const assumeInitialized = isParameter || isAlias || isOuterVariable ||
type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) ||
node.parent.kind === SyntaxKind.NonNullExpression ||
- isInAmbientContext(declaration);
+ declaration.flags & NodeFlags.Ambient;
const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, getRootDeclaration(declaration) as VariableLikeDeclaration) : type) :
type === autoType || type === autoArrayType ? undefinedType :
getNullableType(type, TypeFlags.Undefined);
@@ -13652,8 +13691,32 @@ namespace ts {
// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
// be "pushed" onto a node using the contextualType property.
function getApparentTypeOfContextualType(node: Expression): Type {
- const type = getContextualType(node);
- return type && getApparentType(type);
+ let contextualType = getContextualType(node);
+ contextualType = contextualType && mapType(contextualType, getApparentType);
+ if (!(contextualType && contextualType.flags & TypeFlags.Union && isObjectLiteralExpression(node))) {
+ return contextualType;
+ }
+ // Keep the below up-to-date with the work done within `isRelatedTo` by `findMatchingDiscriminantType`
+ let match: Type | undefined;
+ propLoop: for (const prop of node.properties) {
+ if (!prop.symbol) continue;
+ if (prop.kind !== SyntaxKind.PropertyAssignment) continue;
+ if (isDiscriminantProperty(contextualType, prop.symbol.escapedName)) {
+ const discriminatingType = getTypeOfNode(prop.initializer);
+ for (const type of (contextualType as UnionType).types) {
+ const targetType = getTypeOfPropertyOfType(type, prop.symbol.escapedName);
+ if (targetType && checkTypeAssignableTo(discriminatingType, targetType, /*errorNode*/ undefined)) {
+ if (match) {
+ if (type === match) continue; // Finding multiple fields which discriminate to the same type is fine
+ match = undefined;
+ break propLoop;
+ }
+ match = type;
+ }
+ }
+ }
+ }
+ return match || contextualType;
}
/**
@@ -13886,7 +13949,6 @@ namespace ts {
type.pattern = node;
return type;
}
- const contextualType = getApparentTypeOfContextualType(node);
if (contextualType && contextualTypeIsTupleLikeType(contextualType)) {
const pattern = contextualType.pattern;
// If array literal is contextually typed by a binding pattern or an assignment pattern, pad the resulting
@@ -13913,7 +13975,7 @@ namespace ts {
}
return createArrayType(elementTypes.length ?
getUnionType(elementTypes, /*subtypeReduction*/ true) :
- strictNullChecks ? neverType : undefinedWideningType);
+ strictNullChecks ? implicitNeverType : undefinedWideningType);
}
function isNumericName(name: DeclarationName): boolean {
@@ -15108,12 +15170,11 @@ namespace ts {
if (flags & ModifierFlags.Static) {
return true;
}
- // An instance property must be accessed through an instance of the enclosing class
- if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) {
+ if (type.flags & TypeFlags.TypeParameter) {
// get the original type -- represented as the type constraint of the 'this' type
- type = getConstraintOfTypeParameter(type);
+ type = (type as TypeParameter).isThisType ? getConstraintOfTypeParameter(type) : getBaseConstraintOfType(type);
}
- if (!(getObjectFlags(getTargetType(type)) & ObjectFlags.ClassOrInterface && hasBaseType(type, enclosingClass))) {
+ if (!type || !hasBaseType(type, enclosingClass)) {
error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1, symbolToString(prop), typeToString(enclosingClass));
return false;
}
@@ -15170,7 +15231,7 @@ namespace ts {
if (indexInfo.isReadonly && (isAssignmentTarget(node) || isDeleteTarget(node))) {
error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType));
}
- return indexInfo.type;
+ return getFlowTypeOfPropertyAccess(node, /*prop*/ undefined, indexInfo.type, getAssignmentTargetKind(node));
}
if (right.escapedText && !checkAndReportErrorForExtendingInterface(node)) {
reportNonexistentProperty(right, type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType ? apparentType : type);
@@ -15195,16 +15256,21 @@ namespace ts {
return unknownType;
}
}
+ return getFlowTypeOfPropertyAccess(node, prop, propType, assignmentKind);
+ }
- // Only compute control flow type if this is a property access expression that isn't an
- // assignment target, and the referenced property was declared as a variable, property,
- // accessor, or optional method.
- if (node.kind !== SyntaxKind.PropertyAccessExpression || assignmentKind === AssignmentKind.Definite ||
- !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) &&
- !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
- return propType;
+ /**
+ * Only compute control flow type if this is a property access expression that isn't an
+ * assignment target, and the referenced property was declared as a variable, property,
+ * accessor, or optional method.
+ */
+ function getFlowTypeOfPropertyAccess(node: PropertyAccessExpression | QualifiedName, prop: Symbol | undefined, type: Type, assignmentKind: AssignmentKind) {
+ if (node.kind !== SyntaxKind.PropertyAccessExpression ||
+ assignmentKind === AssignmentKind.Definite ||
+ prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && type.flags & TypeFlags.Union)) {
+ return type;
}
- const flowType = getFlowTypeOfReference(node, propType);
+ const flowType = getFlowTypeOfReference(node, type);
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
}
@@ -15221,7 +15287,7 @@ namespace ts {
}
else if (valueDeclaration.kind === SyntaxKind.ClassDeclaration &&
node.parent.kind !== SyntaxKind.TypeReference &&
- !isInAmbientContext(valueDeclaration) &&
+ !(valueDeclaration.flags & NodeFlags.Ambient) &&
!isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right)) {
error(right, Diagnostics.Class_0_used_before_its_declaration, idText(right));
}
@@ -15293,9 +15359,10 @@ namespace ts {
return suggestion && symbolName(suggestion);
}
- function getSuggestionForNonexistentSymbol(location: Node, name: __String, meaning: SymbolFlags): string {
- const result = resolveNameHelper(location, name, meaning, /*nameNotFoundMessage*/ undefined, name, /*isUse*/ false, (symbols, name, meaning) => {
- // `name` from the callback === the outer `name`
+ function getSuggestionForNonexistentSymbol(location: Node, outerName: __String, meaning: SymbolFlags): string {
+ Debug.assert(outerName !== undefined, "outername should always be defined");
+ const result = resolveNameHelper(location, outerName, meaning, /*nameNotFoundMessage*/ undefined, outerName, /*isUse*/ false, (symbols, name, meaning) => {
+ Debug.assertEqual(outerName, name, "name should equal outerName");
const symbol = getSymbol(symbols, name, meaning);
// Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function
// So the table *contains* `x` but `x` isn't actually in scope.
@@ -16620,21 +16687,9 @@ namespace ts {
* but is a subtype of the Function interface, the call is an untyped function call.
*/
function isUntypedFunctionCall(funcType: Type, apparentFuncType: Type, numCallSignatures: number, numConstructSignatures: number) {
- if (isTypeAny(funcType)) {
- return true;
- }
- if (isTypeAny(apparentFuncType) && funcType.flags & TypeFlags.TypeParameter) {
- return true;
- }
- if (!numCallSignatures && !numConstructSignatures) {
- // We exclude union types because we may have a union of function types that happen to have
- // no common signatures.
- if (funcType.flags & TypeFlags.Union) {
- return false;
- }
- return isTypeAssignableTo(funcType, globalFunctionType);
- }
- return false;
+ // We exclude union types because we may have a union of function types that happen to have no common signatures.
+ return isTypeAny(funcType) || isTypeAny(apparentFuncType) && funcType.flags & TypeFlags.TypeParameter ||
+ !numCallSignatures && !numConstructSignatures && !(funcType.flags & (TypeFlags.Union | TypeFlags.Never)) && isTypeAssignableTo(funcType, globalFunctionType);
}
function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[]): Signature {
@@ -16686,7 +16741,7 @@ namespace ts {
// only the class declaration node will have the Abstract flag set.
const valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol);
if (valueDecl && hasModifier(valueDecl, ModifierFlags.Abstract)) {
- error(node, Diagnostics.Cannot_create_an_instance_of_the_abstract_class_0, declarationNameToString(getNameOfDeclaration(valueDecl)));
+ error(node, Diagnostics.Cannot_create_an_instance_of_an_abstract_class);
return resolveErrorCall(node);
}
@@ -16991,8 +17046,7 @@ namespace ts {
* @returns On success, the expression's signature's return type. On failure, anyType.
*/
function checkCallExpression(node: CallExpression | NewExpression): Type {
- // Grammar checking; stop grammar-checking if checkGrammarTypeArguments return true
- checkGrammarTypeArguments(node, node.typeArguments) || checkGrammarArguments(node.arguments);
+ if (!checkGrammarTypeArguments(node, node.typeArguments)) checkGrammarArguments(node.arguments);
const signature = getResolvedSignature(node);
@@ -17039,7 +17093,7 @@ namespace ts {
function checkImportCallExpression(node: ImportCall): Type {
// Check grammar of dynamic import
- checkGrammarArguments(node.arguments) || checkGrammarImportCallExpression(node);
+ if (!checkGrammarArguments(node.arguments)) checkGrammarImportCallExpression(node);
if (node.arguments.length === 0) {
return createPromiseReturnType(node, anyType);
@@ -17089,7 +17143,7 @@ namespace ts {
return type;
}
- function isCommonJsRequire(node: Node) {
+ function isCommonJsRequire(node: Node): boolean {
if (!isRequireCall(node, /*checkArgumentIsStringLiteral*/ true)) {
return false;
}
@@ -17113,7 +17167,7 @@ namespace ts {
if (targetDeclarationKind !== SyntaxKind.Unknown) {
const decl = getDeclarationOfKind(resolvedRequire, targetDeclarationKind);
// function/variable declaration should be ambient
- return isInAmbientContext(decl);
+ return !!(decl.flags & NodeFlags.Ambient);
}
return false;
}
@@ -18090,14 +18144,6 @@ namespace ts {
return (target.flags & TypeFlags.Nullable) !== 0 || isTypeComparableTo(source, target);
}
- function getBestChoiceType(type1: Type, type2: Type): Type {
- const firstAssignableToSecond = isTypeAssignableTo(type1, type2);
- const secondAssignableToFirst = isTypeAssignableTo(type2, type1);
- return secondAssignableToFirst && !firstAssignableToSecond ? type1 :
- firstAssignableToSecond && !secondAssignableToFirst ? type2 :
- getUnionType([type1, type2], /*subtypeReduction*/ true);
- }
-
function checkBinaryExpression(node: BinaryExpression, checkMode?: CheckMode) {
return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, checkMode, node);
}
@@ -18234,7 +18280,7 @@ namespace ts {
leftType;
case SyntaxKind.BarBarToken:
return getTypeFacts(leftType) & TypeFacts.Falsy ?
- getBestChoiceType(removeDefinitelyFalsyTypes(leftType), rightType) :
+ getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], /*subtypeReduction*/ true) :
leftType;
case SyntaxKind.EqualsToken:
checkAssignmentOperator(rightType);
@@ -18394,7 +18440,7 @@ namespace ts {
checkExpression(node.condition);
const type1 = checkExpression(node.whenTrue, checkMode);
const type2 = checkExpression(node.whenFalse, checkMode);
- return getBestChoiceType(type1, type2);
+ return getUnionType([type1, type2], /*subtypeReduction*/ true);
}
function checkTemplateExpression(node: TemplateExpression): Type {
@@ -18722,9 +18768,7 @@ namespace ts {
// It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the
// Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code
// or if its FunctionBody is strict code(11.1.5).
-
- // Grammar checking
- checkGrammarDecorators(node) || checkGrammarModifiers(node);
+ checkGrammarDecoratorsAndModifiers(node);
checkVariableLikeDeclaration(node);
const func = getContainingFunction(node);
@@ -19114,14 +19158,13 @@ namespace ts {
function checkPropertyDeclaration(node: PropertyDeclaration) {
// Grammar checking
- checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarProperty(node) || checkGrammarComputedPropertyName(node.name);
-
+ if (!checkGrammarDecoratorsAndModifiers(node) && !checkGrammarProperty(node)) checkGrammarComputedPropertyName(node.name);
checkVariableLikeDeclaration(node);
}
function checkMethodDeclaration(node: MethodDeclaration) {
// Grammar checking
- checkGrammarMethod(node) || checkGrammarComputedPropertyName(node.name);
+ if (!checkGrammarMethod(node)) checkGrammarComputedPropertyName(node.name);
// Grammar checking for modifiers is done inside the function checkGrammarFunctionLikeDeclaration
checkFunctionOrMethodDeclaration(node);
@@ -19137,7 +19180,7 @@ namespace ts {
// Grammar check on signature of constructor and modifier of the constructor is done in checkSignatureDeclaration function.
checkSignatureDeclaration(node);
// Grammar check for checking only related to constructorDeclaration
- checkGrammarConstructorTypeParameters(node) || checkGrammarConstructorTypeAnnotation(node);
+ if (!checkGrammarConstructorTypeParameters(node)) checkGrammarConstructorTypeAnnotation(node);
checkSourceElement(node.body);
registerForUnusedIdentifiersCheck(node);
@@ -19234,12 +19277,12 @@ namespace ts {
function checkAccessorDeclaration(node: AccessorDeclaration) {
if (produceDiagnostics) {
// Grammar checking accessors
- checkGrammarFunctionLikeDeclaration(node) || checkGrammarAccessor(node) || checkGrammarComputedPropertyName(node.name);
+ if (!checkGrammarFunctionLikeDeclaration(node) && !checkGrammarAccessor(node)) checkGrammarComputedPropertyName(node.name);
checkDecorators(node);
checkSignatureDeclaration(node);
if (node.kind === SyntaxKind.GetAccessor) {
- if (!isInAmbientContext(node) && nodeIsPresent(node.body) && (node.flags & NodeFlags.HasImplicitReturn)) {
+ if (!(node.flags & NodeFlags.Ambient) && nodeIsPresent(node.body) && (node.flags & NodeFlags.HasImplicitReturn)) {
if (!(node.flags & NodeFlags.HasExplicitReturn)) {
error(node.name, Diagnostics.A_get_accessor_must_return_a_value);
}
@@ -19422,7 +19465,7 @@ namespace ts {
}
function isPrivateWithinAmbient(node: Node): boolean {
- return hasModifier(node, ModifierFlags.Private) && isInAmbientContext(node);
+ return hasModifier(node, ModifierFlags.Private) && !!(node.flags & NodeFlags.Ambient);
}
function getEffectiveDeclarationFlags(n: Node, flagsToCheck: ModifierFlags): ModifierFlags {
@@ -19433,7 +19476,7 @@ namespace ts {
if (n.parent.kind !== SyntaxKind.InterfaceDeclaration &&
n.parent.kind !== SyntaxKind.ClassDeclaration &&
n.parent.kind !== SyntaxKind.ClassExpression &&
- isInAmbientContext(n)) {
+ n.flags & NodeFlags.Ambient) {
if (!(flags & ModifierFlags.Ambient)) {
// It is nested in an ambient context, which means it is automatically exported
flags |= ModifierFlags.Export;
@@ -19572,7 +19615,7 @@ namespace ts {
let multipleConstructorImplementation = false;
for (const current of declarations) {
const node = current;
- const inAmbientContext = isInAmbientContext(node);
+ const inAmbientContext = node.flags & NodeFlags.Ambient;
const inAmbientContextOrInterface = node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.TypeLiteral || inAmbientContext;
if (inAmbientContextOrInterface) {
// check if declarations are consecutive only if they are non-ambient
@@ -20228,6 +20271,10 @@ namespace ts {
case SyntaxKind.Parameter:
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(node));
+ const containingSignature = (node as ParameterDeclaration).parent;
+ for (const parameter of containingSignature.parameters) {
+ markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter));
+ }
break;
}
}
@@ -20413,21 +20460,20 @@ namespace ts {
case SyntaxKind.MethodSignature:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
- case SyntaxKind.IndexSignature:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
- checkUnusedTypeParameters(node);
- break;
case SyntaxKind.TypeAliasDeclaration:
- checkUnusedTypeParameters(node);
+ checkUnusedTypeParameters(node);
break;
+ default:
+ Debug.fail("Node should not have been registered for unused identifiers check");
}
}
}
}
function checkUnusedLocalsAndParameters(node: Node): void {
- if (node.parent.kind !== SyntaxKind.InterfaceDeclaration && noUnusedIdentifiers && !isInAmbientContext(node)) {
+ if (noUnusedIdentifiers && !(node.flags & NodeFlags.Ambient)) {
node.locals.forEach(local => {
if (!local.isReferenced) {
if (local.valueDeclaration && getRootDeclaration(local.valueDeclaration).kind === SyntaxKind.Parameter) {
@@ -20480,7 +20526,7 @@ namespace ts {
}
function checkUnusedClassMembers(node: ClassDeclaration | ClassExpression): void {
- if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) {
+ if (compilerOptions.noUnusedLocals && !(node.flags & NodeFlags.Ambient)) {
if (node.members) {
for (const member of node.members) {
if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.PropertyDeclaration) {
@@ -20501,7 +20547,7 @@ namespace ts {
}
function checkUnusedTypeParameters(node: ClassDeclaration | ClassExpression | FunctionDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction | ConstructorDeclaration | SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration) {
- if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) {
+ if (compilerOptions.noUnusedLocals && !(node.flags & NodeFlags.Ambient)) {
if (node.typeParameters) {
// Only report errors on the last declaration for the type parameter container;
// this ensures that all uses have been accounted for.
@@ -20520,7 +20566,7 @@ namespace ts {
}
function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile): void {
- if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) {
+ if (compilerOptions.noUnusedLocals && !(node.flags & NodeFlags.Ambient)) {
node.locals.forEach(local => {
if (!local.isReferenced && !local.exportSymbol) {
for (const declaration of local.declarations) {
@@ -20553,7 +20599,7 @@ namespace ts {
function checkCollisionWithArgumentsInGeneratedCode(node: SignatureDeclaration) {
// no rest parameters \ declaration context \ overload - no codegen impact
- if (!hasRestParameter(node) || isInAmbientContext(node) || nodeIsMissing((node).body)) {
+ if (!hasRestParameter(node) || node.flags & NodeFlags.Ambient || nodeIsMissing((node).body)) {
return;
}
@@ -20579,7 +20625,7 @@ namespace ts {
return false;
}
- if (isInAmbientContext(node)) {
+ if (node.flags & NodeFlags.Ambient) {
// ambient context - no codegen impact
return false;
}
@@ -20644,7 +20690,7 @@ namespace ts {
// bubble up and find containing type
const enclosingClass = getContainingClass(node);
// if containing type was not found or it is ambient - exit (no codegen)
- if (!enclosingClass || isInAmbientContext(enclosingClass)) {
+ if (!enclosingClass || enclosingClass.flags & NodeFlags.Ambient) {
return;
}
@@ -20997,8 +21043,7 @@ namespace ts {
function checkVariableStatement(node: VariableStatement) {
// Grammar checking
- checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarVariableDeclarationList(node.declarationList) || checkGrammarForDisallowedLetOrConstStatement(node);
-
+ if (!checkGrammarDecoratorsAndModifiers(node) && !checkGrammarVariableDeclarationList(node.declarationList)) checkGrammarForDisallowedLetOrConstStatement(node);
forEach(node.declarationList.declarations, checkSourceElement);
}
@@ -21506,7 +21551,7 @@ namespace ts {
function checkBreakOrContinueStatement(node: BreakOrContinueStatement) {
// Grammar checking
- checkGrammarStatementInAmbientContext(node) || checkGrammarBreakOrContinueStatement(node);
+ if (!checkGrammarStatementInAmbientContext(node)) checkGrammarBreakOrContinueStatement(node);
// TODO: Check that target label is valid
}
@@ -21959,7 +22004,7 @@ namespace ts {
checkClassForDuplicateDeclarations(node);
// Only check for reserved static identifiers on non-ambient context.
- if (!isInAmbientContext(node)) {
+ if (!(node.flags & NodeFlags.Ambient)) {
checkClassForStaticPropertyNameConflicts(node);
}
@@ -22187,7 +22232,7 @@ namespace ts {
function checkInterfaceDeclaration(node: InterfaceDeclaration) {
// Grammar checking
- checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarInterfaceDeclaration(node);
+ if (!checkGrammarDecoratorsAndModifiers(node)) checkGrammarInterfaceDeclaration(node);
checkTypeParameters(node.typeParameters);
if (produceDiagnostics) {
@@ -22229,7 +22274,7 @@ namespace ts {
function checkTypeAliasDeclaration(node: TypeAliasDeclaration) {
// Grammar checking
- checkGrammarDecorators(node) || checkGrammarModifiers(node);
+ checkGrammarDecoratorsAndModifiers(node);
checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0);
checkTypeParameters(node.typeParameters);
@@ -22265,7 +22310,7 @@ namespace ts {
}
// In ambient enum declarations that specify no const modifier, enum member declarations that omit
// a value are considered computed members (as opposed to having auto-incremented values).
- if (isInAmbientContext(member.parent) && !isConst(member.parent)) {
+ if (member.parent.flags & NodeFlags.Ambient && !isConst(member.parent)) {
return undefined;
}
// If the member declaration specifies no value, the member is considered a constant enum member.
@@ -22298,7 +22343,7 @@ namespace ts {
else if (isConstEnum) {
error(initializer, Diagnostics.In_const_enum_declarations_member_initializer_must_be_constant_expression);
}
- else if (isInAmbientContext(member.parent)) {
+ else if (member.parent.flags & NodeFlags.Ambient) {
error(initializer, Diagnostics.In_ambient_enum_declarations_member_initializer_must_be_constant_expression);
}
else {
@@ -22376,7 +22421,7 @@ namespace ts {
const declaration = memberSymbol.valueDeclaration;
if (declaration !== member) {
if (isBlockScopedNameDeclaredBeforeUse(declaration, member)) {
- return getNodeLinks(declaration).enumMemberValue;
+ return getEnumMemberValue(declaration as EnumMember);
}
error(expr, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums);
return 0;
@@ -22399,7 +22444,7 @@ namespace ts {
}
// Grammar checking
- checkGrammarDecorators(node) || checkGrammarModifiers(node);
+ checkGrammarDecoratorsAndModifiers(node);
checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0);
checkCollisionWithCapturedThisVariable(node, node.name);
@@ -22411,7 +22456,7 @@ namespace ts {
computeEnumMemberValues(node);
const enumIsConst = isConst(node);
- if (compilerOptions.isolatedModules && enumIsConst && isInAmbientContext(node)) {
+ if (compilerOptions.isolatedModules && enumIsConst && node.flags & NodeFlags.Ambient) {
error(node.name, Diagnostics.Ambient_const_enums_are_not_allowed_when_the_isolatedModules_flag_is_provided);
}
@@ -22463,7 +22508,7 @@ namespace ts {
for (const declaration of declarations) {
if ((declaration.kind === SyntaxKind.ClassDeclaration ||
(declaration.kind === SyntaxKind.FunctionDeclaration && nodeIsPresent((declaration).body))) &&
- !isInAmbientContext(declaration)) {
+ !(declaration.flags & NodeFlags.Ambient)) {
return declaration;
}
}
@@ -22488,7 +22533,7 @@ namespace ts {
if (produceDiagnostics) {
// Grammar checking
const isGlobalAugmentation = isGlobalScopeAugmentation(node);
- const inAmbientContext = isInAmbientContext(node);
+ const inAmbientContext = node.flags & NodeFlags.Ambient;
if (isGlobalAugmentation && !inAmbientContext) {
error(node.name, Diagnostics.Augmentations_for_the_global_scope_should_have_declare_modifier_unless_they_appear_in_already_ambient_context);
}
@@ -22502,7 +22547,7 @@ namespace ts {
return;
}
- if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node)) {
+ if (!checkGrammarDecoratorsAndModifiers(node)) {
if (!inAmbientContext && node.name.kind === SyntaxKind.StringLiteral) {
grammarErrorOnNode(node.name, Diagnostics.Only_ambient_modules_can_use_quoted_names);
}
@@ -22707,7 +22752,7 @@ namespace ts {
if (compilerOptions.isolatedModules
&& node.kind === SyntaxKind.ExportSpecifier
&& !(target.flags & SymbolFlags.Value)
- && !isInAmbientContext(node)) {
+ && !(node.flags & NodeFlags.Ambient)) {
error(node, Diagnostics.Cannot_re_export_a_type_when_the_isolatedModules_flag_is_provided);
}
}
@@ -22725,7 +22770,7 @@ namespace ts {
// If we hit an import declaration in an illegal context, just bail out to avoid cascading errors.
return;
}
- if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && hasModifiers(node)) {
+ if (!checkGrammarDecoratorsAndModifiers(node) && hasModifiers(node)) {
grammarErrorOnFirstToken(node, Diagnostics.An_import_declaration_cannot_have_modifiers);
}
if (checkExternalImportOrExportDeclaration(node)) {
@@ -22752,13 +22797,13 @@ namespace ts {
return;
}
- checkGrammarDecorators(node) || checkGrammarModifiers(node);
+ checkGrammarDecoratorsAndModifiers(node);
if (isInternalModuleImportEqualsDeclaration(node) || checkExternalImportOrExportDeclaration(node)) {
checkImportBinding(node);
if (hasModifier(node, ModifierFlags.Export)) {
markExportAsReferenced(node);
}
- if (isInternalModuleImportEqualsDeclaration(node)) {
+ if (node.moduleReference.kind !== SyntaxKind.ExternalModuleReference) {
const target = resolveAlias(getSymbolOfNode(node));
if (target !== unknownSymbol) {
if (target.flags & SymbolFlags.Value) {
@@ -22774,7 +22819,7 @@ namespace ts {
}
}
else {
- if (modulekind >= ModuleKind.ES2015 && !isInAmbientContext(node)) {
+ if (modulekind >= ModuleKind.ES2015 && !(node.flags & NodeFlags.Ambient)) {
// Import equals declaration is deprecated in es6 or above
grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead);
}
@@ -22788,7 +22833,7 @@ namespace ts {
return;
}
- if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && hasModifiers(node)) {
+ if (!checkGrammarDecoratorsAndModifiers(node) && hasModifiers(node)) {
grammarErrorOnFirstToken(node, Diagnostics.An_export_declaration_cannot_have_modifiers);
}
@@ -22800,7 +22845,7 @@ namespace ts {
const inAmbientExternalModule = node.parent.kind === SyntaxKind.ModuleBlock && isAmbientModule(node.parent.parent);
const inAmbientNamespaceDeclaration = !inAmbientExternalModule && node.parent.kind === SyntaxKind.ModuleBlock &&
- !node.moduleSpecifier && isInAmbientContext(node);
+ !node.moduleSpecifier && node.flags & NodeFlags.Ambient;
if (node.parent.kind !== SyntaxKind.SourceFile && !inAmbientExternalModule && !inAmbientNamespaceDeclaration) {
error(node, Diagnostics.Export_declarations_are_not_permitted_in_a_namespace);
}
@@ -22861,7 +22906,7 @@ namespace ts {
return;
}
// Grammar checking
- if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && hasModifiers(node)) {
+ if (!checkGrammarDecoratorsAndModifiers(node) && hasModifiers(node)) {
grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers);
}
if (node.expression.kind === SyntaxKind.Identifier) {
@@ -22873,11 +22918,11 @@ namespace ts {
checkExternalModuleExports(container);
- if (isInAmbientContext(node) && !isEntityNameExpression(node.expression)) {
+ if ((node.flags & NodeFlags.Ambient) && !isEntityNameExpression(node.expression)) {
grammarErrorOnNode(node.expression, Diagnostics.The_expression_of_an_export_assignment_must_be_an_identifier_or_qualified_name_in_an_ambient_context);
}
- if (node.isExportEquals && !isInAmbientContext(node)) {
+ if (node.isExportEquals && !(node.flags & NodeFlags.Ambient)) {
if (modulekind >= ModuleKind.ES2015) {
// export assignment is not supported in es6 modules
grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_export_default_or_another_module_format_instead);
@@ -22906,29 +22951,31 @@ namespace ts {
}
// Checks for export * conflicts
const exports = getExportsOfModule(moduleSymbol);
- exports && exports.forEach(({ declarations, flags }, id) => {
- if (id === "__export") {
- return;
- }
- // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries.
- // (TS Exceptions: namespaces, function overloads, enums, and interfaces)
- if (flags & (SymbolFlags.Namespace | SymbolFlags.Interface | SymbolFlags.Enum)) {
- return;
- }
- const exportedDeclarationsCount = countWhere(declarations, isNotOverloadAndNotAccessor);
- if (flags & SymbolFlags.TypeAlias && exportedDeclarationsCount <= 2) {
- // it is legal to merge type alias with other values
- // so count should be either 1 (just type alias) or 2 (type alias + merged value)
- return;
- }
- if (exportedDeclarationsCount > 1) {
- for (const declaration of declarations) {
- if (isNotOverload(declaration)) {
- diagnostics.add(createDiagnosticForNode(declaration, Diagnostics.Cannot_redeclare_exported_variable_0, unescapeLeadingUnderscores(id)));
+ if (exports) {
+ exports.forEach(({ declarations, flags }, id) => {
+ if (id === "__export") {
+ return;
+ }
+ // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries.
+ // (TS Exceptions: namespaces, function overloads, enums, and interfaces)
+ if (flags & (SymbolFlags.Namespace | SymbolFlags.Interface | SymbolFlags.Enum)) {
+ return;
+ }
+ const exportedDeclarationsCount = countWhere(declarations, isNotOverloadAndNotAccessor);
+ if (flags & SymbolFlags.TypeAlias && exportedDeclarationsCount <= 2) {
+ // it is legal to merge type alias with other values
+ // so count should be either 1 (just type alias) or 2 (type alias + merged value)
+ return;
+ }
+ if (exportedDeclarationsCount > 1) {
+ for (const declaration of declarations) {
+ if (isNotOverload(declaration)) {
+ diagnostics.add(createDiagnosticForNode(declaration, Diagnostics.Cannot_redeclare_exported_variable_0, unescapeLeadingUnderscores(id)));
+ }
}
}
- }
- });
+ });
+ }
links.exportsChecked = true;
}
}
@@ -23168,7 +23215,7 @@ namespace ts {
checkDeferredNodes();
- if (isExternalModule(node)) {
+ if (isExternalOrCommonJsModule(node)) {
registerForUnusedIdentifiersCheck(node);
}
@@ -23641,6 +23688,8 @@ namespace ts {
return objectType && getPropertyOfType(objectType, escapeLeadingUnderscores((node as StringLiteral | NumericLiteral).text));
case SyntaxKind.DefaultKeyword:
+ case SyntaxKind.FunctionKeyword:
+ case SyntaxKind.EqualsGreaterThanToken:
return getSymbolOfNode(node.parent);
default:
@@ -24509,7 +24558,7 @@ namespace ts {
function checkExternalEmitHelpers(location: Node, helpers: ExternalEmitHelpers) {
if ((requestedExternalEmitHelpers & helpers) !== helpers && compilerOptions.importHelpers) {
const sourceFile = getSourceFileOfNode(location);
- if (isEffectiveExternalModule(sourceFile, compilerOptions) && !isInAmbientContext(location)) {
+ if (isEffectiveExternalModule(sourceFile, compilerOptions) && !(location.flags & NodeFlags.Ambient)) {
const helpersModule = resolveHelpersModule(sourceFile, location);
if (helpersModule !== unknownSymbol) {
const uncheckedHelpers = helpers & ~requestedExternalEmitHelpers;
@@ -24559,12 +24608,16 @@ namespace ts {
}
// GRAMMAR CHECKING
+ function checkGrammarDecoratorsAndModifiers(node: Node): boolean {
+ return checkGrammarDecorators(node) || checkGrammarModifiers(node);
+ }
+
function checkGrammarDecorators(node: Node): boolean {
if (!node.decorators) {
return false;
}
if (!nodeCanBeDecorated(node)) {
- if (node.kind === SyntaxKind.MethodDeclaration && !ts.nodeIsPresent((node).body)) {
+ if (node.kind === SyntaxKind.MethodDeclaration && !nodeIsPresent((node).body)) {
return grammarErrorOnFirstToken(node, Diagnostics.A_decorator_can_only_decorate_a_method_implementation_not_an_overload);
}
else {
@@ -24711,7 +24764,7 @@ namespace ts {
else if (node.kind === SyntaxKind.Parameter) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "declare");
}
- else if (isInAmbientContext(node.parent) && node.parent.kind === SyntaxKind.ModuleBlock) {
+ else if ((node.parent.flags & NodeFlags.Ambient) && node.parent.kind === SyntaxKind.ModuleBlock) {
return grammarErrorOnNode(modifier, Diagnostics.A_declare_modifier_cannot_be_used_in_an_already_ambient_context);
}
flags |= ModifierFlags.Ambient;
@@ -24747,7 +24800,7 @@ namespace ts {
if (flags & ModifierFlags.Async) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "async");
}
- else if (flags & ModifierFlags.Ambient || isInAmbientContext(node.parent)) {
+ else if (flags & ModifierFlags.Ambient || node.parent.flags & NodeFlags.Ambient) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_in_an_ambient_context, "async");
}
else if (node.kind === SyntaxKind.Parameter) {
@@ -24914,7 +24967,7 @@ namespace ts {
function checkGrammarFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
// Prevent cascading error by short-circuit
const file = getSourceFileOfNode(node);
- return checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarTypeParameterList(node.typeParameters, file) ||
+ return checkGrammarDecoratorsAndModifiers(node) || checkGrammarTypeParameterList(node.typeParameters, file) ||
checkGrammarParameterList(node.parameters) || checkGrammarArrowFunction(node, file);
}
@@ -24970,7 +25023,7 @@ namespace ts {
function checkGrammarIndexSignature(node: SignatureDeclaration) {
// Prevent cascading error by short-circuit
- return checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarIndexSignatureParameters(node);
+ return checkGrammarDecoratorsAndModifiers(node) || checkGrammarIndexSignatureParameters(node);
}
function checkGrammarForAtLeastOneTypeArgument(node: Node, typeArguments: NodeArray): boolean {
@@ -25021,7 +25074,7 @@ namespace ts {
let seenExtendsClause = false;
let seenImplementsClause = false;
- if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && node.heritageClauses) {
+ if (!checkGrammarDecoratorsAndModifiers(node) && node.heritageClauses) {
for (const heritageClause of node.heritageClauses) {
if (heritageClause.token === SyntaxKind.ExtendsKeyword) {
if (seenExtendsClause) {
@@ -25095,7 +25148,7 @@ namespace ts {
node.kind === SyntaxKind.FunctionDeclaration ||
node.kind === SyntaxKind.FunctionExpression ||
node.kind === SyntaxKind.MethodDeclaration);
- if (isInAmbientContext(node)) {
+ if (node.flags & NodeFlags.Ambient) {
return grammarErrorOnNode(node.asteriskToken, Diagnostics.Generators_are_not_allowed_in_an_ambient_context);
}
if (!node.body) {
@@ -25111,11 +25164,13 @@ namespace ts {
}
function checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression, inDestructuring: boolean) {
- const seen = createUnderscoreEscapedMap();
- const Property = 1;
- const GetAccessor = 2;
- const SetAccessor = 4;
- const GetOrSetAccessor = GetAccessor | SetAccessor;
+ const enum Flags {
+ Property = 1,
+ GetAccessor = 2,
+ SetAccessor = 4,
+ GetOrSetAccessor = GetAccessor | SetAccessor,
+ }
+ const seen = createUnderscoreEscapedMap();
for (const prop of node.properties) {
if (prop.kind === SyntaxKind.SpreadAssignment) {
@@ -25150,26 +25205,27 @@ namespace ts {
// c.IsAccessorDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true.
// d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true
// and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields
- let currentKind: number;
- if (prop.kind === SyntaxKind.PropertyAssignment || prop.kind === SyntaxKind.ShorthandPropertyAssignment) {
- // Grammar checking for computedPropertyName and shorthandPropertyAssignment
- checkGrammarForInvalidQuestionMark((prop).questionToken, Diagnostics.An_object_member_cannot_be_declared_optional);
- if (name.kind === SyntaxKind.NumericLiteral) {
- checkGrammarNumericLiteral(name);
- }
- currentKind = Property;
- }
- else if (prop.kind === SyntaxKind.MethodDeclaration) {
- currentKind = Property;
- }
- else if (prop.kind === SyntaxKind.GetAccessor) {
- currentKind = GetAccessor;
- }
- else if (prop.kind === SyntaxKind.SetAccessor) {
- currentKind = SetAccessor;
- }
- else {
- Debug.assertNever(prop, "Unexpected syntax kind:" + (prop).kind);
+ let currentKind: Flags;
+ switch (prop.kind) {
+ case SyntaxKind.PropertyAssignment:
+ case SyntaxKind.ShorthandPropertyAssignment:
+ // Grammar checking for computedPropertyName and shorthandPropertyAssignment
+ checkGrammarForInvalidQuestionMark((prop).questionToken, Diagnostics.An_object_member_cannot_be_declared_optional);
+ if (name.kind === SyntaxKind.NumericLiteral) {
+ checkGrammarNumericLiteral(name);
+ }
+ // falls through
+ case SyntaxKind.MethodDeclaration:
+ currentKind = Flags.Property;
+ break;
+ case SyntaxKind.GetAccessor:
+ currentKind = Flags.GetAccessor;
+ break;
+ case SyntaxKind.SetAccessor:
+ currentKind = Flags.SetAccessor;
+ break;
+ default:
+ Debug.assertNever(prop, "Unexpected syntax kind:" + (prop).kind);
}
const effectiveName = getPropertyNameForPropertyNameNode(name);
@@ -25182,11 +25238,11 @@ namespace ts {
seen.set(effectiveName, currentKind);
}
else {
- if (currentKind === Property && existingKind === Property) {
+ if (currentKind === Flags.Property && existingKind === Flags.Property) {
grammarErrorOnNode(name, Diagnostics.Duplicate_identifier_0, getTextOfNode(name));
}
- else if ((currentKind & GetOrSetAccessor) && (existingKind & GetOrSetAccessor)) {
- if (existingKind !== GetOrSetAccessor && currentKind !== existingKind) {
+ else if ((currentKind & Flags.GetOrSetAccessor) && (existingKind & Flags.GetOrSetAccessor)) {
+ if (existingKind !== Flags.GetOrSetAccessor && currentKind !== existingKind) {
seen.set(effectiveName, currentKind | existingKind);
}
else {
@@ -25282,7 +25338,7 @@ namespace ts {
if (languageVersion < ScriptTarget.ES5) {
return grammarErrorOnNode(accessor.name, Diagnostics.Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher);
}
- else if (isInAmbientContext(accessor)) {
+ else if (accessor.flags & NodeFlags.Ambient) {
return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_be_declared_in_an_ambient_context);
}
else if (accessor.body === undefined && !hasModifier(accessor, ModifierFlags.Abstract)) {
@@ -25361,7 +25417,7 @@ namespace ts {
// However, property declarations disallow computed names in general,
// and accessors are not allowed in ambient contexts in general,
// so this error only really matters for methods.
- if (isInAmbientContext(node)) {
+ if (node.flags & NodeFlags.Ambient) {
return checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_an_ambient_context_must_directly_refer_to_a_built_in_symbol);
}
else if (!node.body) {
@@ -25456,7 +25512,7 @@ namespace ts {
function checkGrammarVariableDeclaration(node: VariableDeclaration) {
if (node.parent.parent.kind !== SyntaxKind.ForInStatement && node.parent.parent.kind !== SyntaxKind.ForOfStatement) {
- if (isInAmbientContext(node)) {
+ if (node.flags & NodeFlags.Ambient) {
if (node.initializer) {
if (isConst(node) && !node.type) {
if (!isStringOrNumberLiteralExpression(node.initializer)) {
@@ -25486,7 +25542,7 @@ namespace ts {
}
if (compilerOptions.module !== ModuleKind.ES2015 && compilerOptions.module !== ModuleKind.ESNext && compilerOptions.module !== ModuleKind.System && !compilerOptions.noEmit &&
- !isInAmbientContext(node.parent.parent) && hasModifier(node.parent.parent, ModifierFlags.Export)) {
+ !(node.parent.parent.flags & NodeFlags.Ambient) && hasModifier(node.parent.parent, ModifierFlags.Export)) {
checkESModuleMarker(node.name);
}
@@ -25645,7 +25701,7 @@ namespace ts {
}
}
- if (isInAmbientContext(node) && node.initializer) {
+ if (node.flags & NodeFlags.Ambient && node.initializer) {
return grammarErrorOnFirstToken(node.initializer, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
}
}
@@ -25688,11 +25744,11 @@ namespace ts {
}
function checkGrammarSourceFile(node: SourceFile): boolean {
- return isInAmbientContext(node) && checkGrammarTopLevelElementsForRequiredDeclareModifier(node);
+ return !!(node.flags & NodeFlags.Ambient) && checkGrammarTopLevelElementsForRequiredDeclareModifier(node);
}
function checkGrammarStatementInAmbientContext(node: Node): boolean {
- if (isInAmbientContext(node)) {
+ if (node.flags & NodeFlags.Ambient) {
// An accessors is already reported about the ambient context
if (isAccessor(node.parent)) {
return getNodeLinks(node).hasReportedStatementInAmbientContext = true;
@@ -25814,4 +25870,17 @@ namespace ts {
return false;
}
}
+
+ namespace JsxNames {
+ // tslint:disable variable-name
+ export const JSX = "JSX" as __String;
+ export const IntrinsicElements = "IntrinsicElements" as __String;
+ export const ElementClass = "ElementClass" as __String;
+ export const ElementAttributesPropertyNameContainer = "ElementAttributesProperty" as __String;
+ export const ElementChildrenAttributeNameContainer = "ElementChildrenAttribute" as __String;
+ export const Element = "Element" as __String;
+ export const IntrinsicAttributes = "IntrinsicAttributes" as __String;
+ export const IntrinsicClassAttributes = "IntrinsicClassAttributes" as __String;
+ // tslint:enable variable-name
+ }
}
diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts
index b7003fbffd9..43ea77c8020 100644
--- a/src/compiler/commandLineParser.ts
+++ b/src/compiler/commandLineParser.ts
@@ -76,13 +76,13 @@ namespace ts {
name: "target",
shortName: "t",
type: createMapFromTemplate({
- "es3": ScriptTarget.ES3,
- "es5": ScriptTarget.ES5,
- "es6": ScriptTarget.ES2015,
- "es2015": ScriptTarget.ES2015,
- "es2016": ScriptTarget.ES2016,
- "es2017": ScriptTarget.ES2017,
- "esnext": ScriptTarget.ESNext,
+ es3: ScriptTarget.ES3,
+ es5: ScriptTarget.ES5,
+ es6: ScriptTarget.ES2015,
+ es2015: ScriptTarget.ES2015,
+ es2016: ScriptTarget.ES2016,
+ es2017: ScriptTarget.ES2017,
+ esnext: ScriptTarget.ESNext,
}),
paramType: Diagnostics.VERSION,
showInSimplifiedHelpView: true,
@@ -93,14 +93,14 @@ namespace ts {
name: "module",
shortName: "m",
type: createMapFromTemplate({
- "none": ModuleKind.None,
- "commonjs": ModuleKind.CommonJS,
- "amd": ModuleKind.AMD,
- "system": ModuleKind.System,
- "umd": ModuleKind.UMD,
- "es6": ModuleKind.ES2015,
- "es2015": ModuleKind.ES2015,
- "esnext": ModuleKind.ESNext
+ none: ModuleKind.None,
+ commonjs: ModuleKind.CommonJS,
+ amd: ModuleKind.AMD,
+ system: ModuleKind.System,
+ umd: ModuleKind.UMD,
+ es6: ModuleKind.ES2015,
+ es2015: ModuleKind.ES2015,
+ esnext: ModuleKind.ESNext
}),
paramType: Diagnostics.KIND,
showInSimplifiedHelpView: true,
@@ -141,6 +141,7 @@ namespace ts {
"es2017.sharedmemory": "lib.es2017.sharedmemory.d.ts",
"es2017.string": "lib.es2017.string.d.ts",
"es2017.intl": "lib.es2017.intl.d.ts",
+ "es2017.typedarrays": "lib.es2017.typedarrays.d.ts",
"esnext.asynciterable": "lib.esnext.asynciterable.d.ts",
}),
},
@@ -325,8 +326,8 @@ namespace ts {
{
name: "moduleResolution",
type: createMapFromTemplate({
- "node": ModuleResolutionKind.NodeJs,
- "classic": ModuleResolutionKind.Classic,
+ node: ModuleResolutionKind.NodeJs,
+ classic: ModuleResolutionKind.Classic,
}),
paramType: Diagnostics.STRATEGY,
category: Diagnostics.Module_Resolution_Options,
@@ -521,8 +522,8 @@ namespace ts {
{
name: "newLine",
type: createMapFromTemplate({
- "crlf": NewLineKind.CarriageReturnLineFeed,
- "lf": NewLineKind.LineFeed
+ crlf: NewLineKind.CarriageReturnLineFeed,
+ lf: NewLineKind.LineFeed
}),
paramType: Diagnostics.NEWLINE,
category: Diagnostics.Advanced_Options,
diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts
index 025a4f36d26..1fec1e9562f 100644
--- a/src/compiler/comments.ts
+++ b/src/compiler/comments.ts
@@ -260,7 +260,7 @@ namespace ts {
}
}
- function emitLeadingComment(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
+ function emitLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
if (!hasWrittenComment) {
emitNewLineBeforeLeadingCommentOfPosition(currentLineMap, writer, rangePos, commentPos);
hasWrittenComment = true;
@@ -274,7 +274,7 @@ namespace ts {
if (hasTrailingNewLine) {
writer.writeLine();
}
- else {
+ else if (kind === SyntaxKind.MultiLineCommentTrivia) {
writer.write(" ");
}
}
diff --git a/src/compiler/core.ts b/src/compiler/core.ts
index 23493b3087d..f3dd99ee5bf 100644
--- a/src/compiler/core.ts
+++ b/src/compiler/core.ts
@@ -20,12 +20,6 @@ namespace ts {
/* @internal */
namespace ts {
-
- // More efficient to create a collator once and use its `compare` than to call `a.localeCompare(b)` many times.
- export const collator: { compare(a: string, b: string): number } = typeof Intl === "object" && typeof Intl.Collator === "function" ? new Intl.Collator(/*locales*/ undefined, { usage: "sort", sensitivity: "accent" }) : undefined;
- // Intl is missing in Safari, and node 0.10 treats "a" as greater than "B".
- export const localeCompareIsCorrect = ts.collator && ts.collator.compare("a", "B") < 0;
-
/** Create a MapLike with good performance. */
function createDictionaryObject(): MapLike {
const map = Object.create(/*prototype*/ null); // tslint:disable-line:no-null-keyword
@@ -74,13 +68,13 @@ namespace ts {
}
// The global Map object. This may not be available, so we must test for it.
- declare const Map: { new(): Map } | undefined;
+ declare const Map: { new (): Map } | undefined;
// Internet Explorer's Map doesn't support iteration, so don't use it.
- // tslint:disable-next-line:no-in-operator
+ // tslint:disable-next-line no-in-operator variable-name
const MapCtr = typeof Map !== "undefined" && "entries" in Map.prototype ? Map : shimMap();
// Keep the class inside a function so it doesn't get compiled if it's not used.
- function shimMap(): { new(): Map } {
+ function shimMap(): { new (): Map } {
class MapIterator {
private data: MapLike;
@@ -103,7 +97,7 @@ namespace ts {
}
}
- return class implements Map {
+ return class implements Map {
private data = createDictionaryObject();
public size = 0;
@@ -165,12 +159,6 @@ namespace ts {
return getCanonicalFileName(nonCanonicalizedPath);
}
- export const enum Comparison {
- LessThan = -1,
- EqualTo = 0,
- GreaterThan = 1
- }
-
export function length(array: ReadonlyArray) {
return array ? array.length : 0;
}
@@ -307,10 +295,10 @@ namespace ts {
Debug.fail();
}
- export function contains(array: ReadonlyArray, value: T): boolean {
+ export function contains(array: ReadonlyArray, value: T, equalityComparer: EqualityComparer = equateValues): boolean {
if (array) {
for (const v of array) {
- if (v === value) {
+ if (equalityComparer(v, value)) {
return true;
}
}
@@ -655,24 +643,85 @@ namespace ts {
return [...array1, ...array2];
}
- // TODO: fixme (N^2) - add optional comparer so collection can be sorted before deduplication.
- export function deduplicate(array: ReadonlyArray, areEqual?: (a: T, b: T) => boolean): T[] {
- let result: T[];
- if (array) {
- result = [];
- loop: for (const item of array) {
- for (const res of result) {
- if (areEqual ? areEqual(res, item) : res === item) {
- continue loop;
- }
- }
- result.push(item);
+ function deduplicateRelational(array: ReadonlyArray, equalityComparer: EqualityComparer, comparer: Comparer) {
+ // Perform a stable sort of the array. This ensures the first entry in a list of
+ // duplicates remains the first entry in the result.
+ const indices = array.map((_, i) => i);
+ stableSortIndices(array, indices, comparer);
+
+ let last = array[indices[0]];
+ const deduplicated: number[] = [indices[0]];
+ for (let i = 1; i < indices.length; i++) {
+ const index = indices[i];
+ const item = array[index];
+ if (!equalityComparer(last, item)) {
+ deduplicated.push(index);
+ last = item;
}
}
+
+ // restore original order
+ deduplicated.sort();
+ return deduplicated.map(i => array[i]);
+ }
+
+ function deduplicateEquality(array: ReadonlyArray, equalityComparer: EqualityComparer) {
+ const result: T[] = [];
+ for (const item of array) {
+ pushIfUnique(result, item, equalityComparer);
+ }
return result;
}
- export function arrayIsEqualTo(array1: ReadonlyArray, array2: ReadonlyArray, equaler?: (a: T, b: T) => boolean): boolean {
+ /**
+ * Deduplicates an unsorted array.
+ * @param equalityComparer An optional `EqualityComparer` used to determine if two values are duplicates.
+ * @param comparer An optional `Comparer` used to sort entries before comparison, though the
+ * result will remain in the original order in `array`.
+ */
+ export function deduplicate(array: ReadonlyArray, equalityComparer: EqualityComparer, comparer?: Comparer): T[] {
+ return !array ? undefined :
+ array.length === 0 ? [] :
+ array.length === 1 ? array.slice() :
+ comparer ? deduplicateRelational(array, equalityComparer, comparer) :
+ deduplicateEquality(array, equalityComparer);
+ }
+
+ /**
+ * Deduplicates an array that has already been sorted.
+ */
+ function deduplicateSorted(array: ReadonlyArray, comparer: EqualityComparer | Comparer) {
+ if (!array) return undefined;
+ if (array.length === 0) return [];
+
+ let last = array[0];
+ const deduplicated: T[] = [last];
+ for (let i = 1; i < array.length; i++) {
+ const next = array[i];
+ switch (comparer(next, last)) {
+ // equality comparison
+ case true:
+
+ // relational comparison
+ case Comparison.EqualTo:
+ continue;
+
+ case Comparison.LessThan:
+ // If `array` is sorted, `next` should **never** be less than `last`.
+ return Debug.fail("Array is unsorted.");
+ }
+
+ deduplicated.push(last = next);
+ }
+
+ return deduplicated;
+ }
+
+ export function sortAndDeduplicate(array: ReadonlyArray, comparer: Comparer, equalityComparer?: EqualityComparer) {
+ return deduplicateSorted(sort(array, comparer), equalityComparer || comparer);
+ }
+
+ export function arrayIsEqualTo(array1: ReadonlyArray, array2: ReadonlyArray, equalityComparer: (a: T, b: T) => boolean = equateValues): boolean {
if (!array1 || !array2) {
return array1 === array2;
}
@@ -682,8 +731,7 @@ namespace ts {
}
for (let i = 0; i < array1.length; i++) {
- const equals = equaler ? equaler(array1[i], array2[i]) : array1[i] === array2[i];
- if (!equals) {
+ if (!equalityComparer(array1[i], array2[i])) {
return false;
}
}
@@ -734,22 +782,44 @@ namespace ts {
}
/**
- * Gets the relative complement of `arrayA` with respect to `b`, returning the elements that
+ * Gets the relative complement of `arrayA` with respect to `arrayB`, returning the elements that
* are not present in `arrayA` but are present in `arrayB`. Assumes both arrays are sorted
* based on the provided comparer.
*/
- export function relativeComplement(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: Comparer = compareValues, offsetA = 0, offsetB = 0): T[] | undefined {
+ export function relativeComplement(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: Comparer): T[] | undefined {
if (!arrayB || !arrayA || arrayB.length === 0 || arrayA.length === 0) return arrayB;
const result: T[] = [];
- outer: for (; offsetB < arrayB.length; offsetB++) {
- inner: for (; offsetA < arrayA.length; offsetA++) {
+ loopB: for (let offsetA = 0, offsetB = 0; offsetB < arrayB.length; offsetB++) {
+ if (offsetB > 0) {
+ // Ensure `arrayB` is properly sorted.
+ Debug.assertGreaterThanOrEqual(comparer(arrayB[offsetB], arrayB[offsetB - 1]), Comparison.EqualTo);
+ }
+
+ loopA: for (const startA = offsetA; offsetA < arrayA.length; offsetA++) {
+ if (offsetA > startA) {
+ // Ensure `arrayA` is properly sorted. We only need to perform this check if
+ // `offsetA` has changed since we entered the loop.
+ Debug.assertGreaterThanOrEqual(comparer(arrayA[offsetA], arrayA[offsetA - 1]), Comparison.EqualTo);
+ }
+
switch (comparer(arrayB[offsetB], arrayA[offsetA])) {
- case Comparison.LessThan: break inner;
- case Comparison.EqualTo: continue outer;
- case Comparison.GreaterThan: continue inner;
+ case Comparison.LessThan:
+ // If B is less than A, B does not exist in arrayA. Add B to the result and
+ // move to the next element in arrayB without changing the current position
+ // in arrayA.
+ result.push(arrayB[offsetB]);
+ continue loopB;
+ case Comparison.EqualTo:
+ // If B is equal to A, B exists in arrayA. Move to the next element in
+ // arrayB without adding B to the result or changing the current position
+ // in arrayA.
+ continue loopB;
+ case Comparison.GreaterThan:
+ // If B is greater than A, we need to keep looking for B in arrayA. Move to
+ // the next element in arrayA and recheck.
+ continue loopA;
}
}
- result.push(arrayB[offsetB]);
}
return result;
}
@@ -802,8 +872,7 @@ namespace ts {
start = start === undefined ? 0 : toOffset(from, start);
end = end === undefined ? from.length : toOffset(from, end);
for (let i = start; i < end && i < from.length; i++) {
- const v = from[i];
- if (v !== undefined) {
+ if (from[i] !== undefined) {
to.push(from[i]);
}
}
@@ -813,8 +882,8 @@ namespace ts {
/**
* @return Whether the value was added.
*/
- export function pushIfUnique(array: T[], toAdd: T): boolean {
- if (contains(array, toAdd)) {
+ export function pushIfUnique(array: T[], toAdd: T, equalityComparer?: EqualityComparer): boolean {
+ if (contains(array, toAdd, equalityComparer)) {
return false;
}
else {
@@ -826,9 +895,9 @@ namespace ts {
/**
* Unlike `pushIfUnique`, this can take `undefined` as an input, and returns a new array.
*/
- export function appendIfUnique(array: T[] | undefined, toAdd: T): T[] {
+ export function appendIfUnique(array: T[] | undefined, toAdd: T, equalityComparer?: EqualityComparer): T[] {
if (array) {
- pushIfUnique(array, toAdd);
+ pushIfUnique(array, toAdd, equalityComparer);
return array;
}
else {
@@ -836,14 +905,25 @@ namespace ts {
}
}
+ function stableSortIndices(array: ReadonlyArray, indices: number[], comparer: Comparer) {
+ // sort indices by value then position
+ indices.sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y));
+ }
+
+ /**
+ * Returns a new sorted array.
+ */
+ export function sort(array: ReadonlyArray, comparer: Comparer) {
+ return array.slice().sort(comparer);
+ }
+
/**
* Stable sort of an array. Elements equal to each other maintain their relative position in the array.
*/
- export function stableSort(array: ReadonlyArray, comparer: Comparer = compareValues) {
- return array
- .map((_, i) => i) // create array of indices
- .sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y)) // sort indices by value then position
- .map(i => array[i]); // get sorted array
+ export function stableSort(array: ReadonlyArray, comparer: Comparer) {
+ const indices = array.map((_, i) => i);
+ stableSortIndices(array, indices, comparer);
+ return indices.map(i => array[i]);
}
export function rangeEquals(array1: ReadonlyArray, array2: ReadonlyArray, pos: number, end: number) {
@@ -921,38 +1001,37 @@ namespace ts {
return result;
}
- export type Comparer = (a: T, b: T) => Comparison;
-
/**
- * Performs a binary search, finding the index at which 'value' occurs in 'array'.
+ * Performs a binary search, finding the index at which `value` occurs in `array`.
* If no such index is found, returns the 2's-complement of first index at which
- * number[index] exceeds number.
+ * `array[index]` exceeds `value`.
* @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.
+ * @param value The value to be searched for in the array.
+ * @param keySelector A callback used to select the search key from `value` and each element of
+ * `array`.
+ * @param keyComparer A callback used to compare two keys in a sorted array.
+ * @param offset An offset into `array` at which to start the search.
*/
- export function binarySearch(array: ReadonlyArray, value: T, comparer?: Comparer, offset?: number): number {
+ export function binarySearch(array: ReadonlyArray, value: T, keySelector: (v: T) => U, keyComparer: Comparer, offset?: number): number {
if (!array || array.length === 0) {
return -1;
}
let low = offset || 0;
let high = array.length - 1;
- comparer = comparer !== undefined
- ? comparer
- : (v1, v2) => (v1 < v2 ? -1 : (v1 > v2 ? 1 : 0));
-
+ const key = keySelector(value);
while (low <= high) {
const middle = low + ((high - low) >> 1);
- const midValue = array[middle];
-
- if (comparer(midValue, value) === 0) {
- return middle;
- }
- else if (comparer(midValue, value) > 0) {
- high = middle - 1;
- }
- else {
- low = middle + 1;
+ const midKey = keySelector(array[middle]);
+ switch (keyComparer(midKey, key)) {
+ case Comparison.LessThan:
+ low = middle + 1;
+ break;
+ case Comparison.EqualTo:
+ return middle;
+ case Comparison.GreaterThan:
+ high = middle - 1;
+ break;
}
}
@@ -1104,13 +1183,13 @@ namespace ts {
* @param left A map-like whose properties should be compared.
* @param right A map-like whose properties should be compared.
*/
- export function equalOwnProperties(left: MapLike, right: MapLike, equalityComparer?: (left: T, right: T) => boolean) {
+ export function equalOwnProperties(left: MapLike, right: MapLike, equalityComparer: EqualityComparer = equateValues) {
if (left === right) return true;
if (!left || !right) return false;
for (const key in left) {
if (hasOwnProperty.call(left, key)) {
if (!hasOwnProperty.call(right, key) === undefined) return false;
- if (equalityComparer ? !equalityComparer(left[key], right[key]) : left[key] !== right[key]) return false;
+ if (!equalityComparer(left[key], right[key])) return false;
}
}
@@ -1262,7 +1341,7 @@ namespace ts {
}
/** Does nothing. */
- export function noop(): void { }
+ export function noop(_?: {} | null | undefined): void { } // tslint:disable-line no-empty
/** Do nothing and return false */
export function returnFalse(): false { return false; }
@@ -1463,37 +1542,210 @@ namespace ts {
return headChain;
}
- export function compareValues(a: T, b: T): Comparison {
- if (a === b) return Comparison.EqualTo;
- if (a === undefined) return Comparison.LessThan;
- if (b === undefined) return Comparison.GreaterThan;
- return a < b ? Comparison.LessThan : Comparison.GreaterThan;
+ export function equateValues(a: T, b: T) {
+ return a === b;
}
- export function compareStrings(a: string, b: string, ignoreCase?: boolean): Comparison {
+ /**
+ * Compare the equality of two strings using a case-sensitive ordinal comparison.
+ *
+ * Case-sensitive comparisons compare both strings one code-point at a time using the integer
+ * value of each code-point after applying `toUpperCase` to each string. We always map both
+ * strings to their upper-case form as some unicode characters do not properly round-trip to
+ * lowercase (such as `ẞ` (German sharp capital s)).
+ */
+ export function equateStringsCaseInsensitive(a: string, b: string) {
+ return a === b
+ || a !== undefined
+ && b !== undefined
+ && a.toUpperCase() === b.toUpperCase();
+ }
+
+ /**
+ * Compare the equality of two strings using a case-sensitive ordinal comparison.
+ *
+ * Case-sensitive comparisons compare both strings one code-point at a time using the
+ * integer value of each code-point.
+ */
+ export function equateStringsCaseSensitive(a: string, b: string) {
+ return equateValues(a, b);
+ }
+
+ function compareComparableValues(a: string, b: string): Comparison;
+ function compareComparableValues(a: number, b: number): Comparison;
+ function compareComparableValues(a: string | number, b: string | number) {
+ return a === b ? Comparison.EqualTo :
+ a === undefined ? Comparison.LessThan :
+ b === undefined ? Comparison.GreaterThan :
+ a < b ? Comparison.LessThan :
+ Comparison.GreaterThan;
+ }
+
+ /**
+ * Compare two numeric values for their order relative to each other.
+ * To compare strings, use any of the `compareStrings` functions.
+ */
+ export function compareValues(a: number, b: number) {
+ return compareComparableValues(a, b);
+ }
+
+ /**
+ * Compare two strings using a case-insensitive ordinal comparison.
+ *
+ * Ordinal comparisons are based on the difference between the unicode code points of both
+ * strings. Characters with multiple unicode representations are considered unequal. Ordinal
+ * comparisons provide predictable ordering, but place "a" after "B".
+ *
+ * Case-insensitive comparisons compare both strings one code-point at a time using the integer
+ * value of each code-point after applying `toUpperCase` to each string. We always map both
+ * strings to their upper-case form as some unicode characters do not properly round-trip to
+ * lowercase (such as `ẞ` (German sharp capital s)).
+ */
+ export function compareStringsCaseInsensitive(a: string, b: string) {
if (a === b) return Comparison.EqualTo;
if (a === undefined) return Comparison.LessThan;
if (b === undefined) return Comparison.GreaterThan;
- if (ignoreCase) {
- // Checking if "collator exists indicates that Intl is available.
- // We still have to check if "collator.compare" is correct. If it is not, use "String.localeComapre"
- if (collator) {
- const result = localeCompareIsCorrect ?
- collator.compare(a, b) :
- a.localeCompare(b, /*locales*/ undefined, { usage: "sort", sensitivity: "accent" }); // accent means a ≠ b, a ≠ á, a = A
- return result < 0 ? Comparison.LessThan : result > 0 ? Comparison.GreaterThan : Comparison.EqualTo;
- }
+ a = a.toUpperCase();
+ b = b.toUpperCase();
+ return a < b ? Comparison.LessThan : a > b ? Comparison.GreaterThan : Comparison.EqualTo;
+ }
- a = a.toUpperCase();
- b = b.toUpperCase();
+ /**
+ * Compare two strings using a case-sensitive ordinal comparison.
+ *
+ * Ordinal comparisons are based on the difference between the unicode code points of both
+ * strings. Characters with multiple unicode representations are considered unequal. Ordinal
+ * comparisons provide predictable ordering, but place "a" after "B".
+ *
+ * Case-sensitive comparisons compare both strings one code-point at a time using the integer
+ * value of each code-point.
+ */
+ export function compareStringsCaseSensitive(a: string, b: string) {
+ return compareComparableValues(a, b);
+ }
+
+ /**
+ * Creates a string comparer for use with string collation in the UI.
+ */
+ const createUIStringComparer = (() => {
+ let defaultComparer: Comparer | undefined;
+ let enUSComparer: Comparer | undefined;
+
+ const stringComparerFactory = getStringComparerFactory();
+ return createStringComparer;
+
+ function compareWithCallback(a: string | undefined, b: string | undefined, comparer: (a: string, b: string) => number) {
if (a === b) return Comparison.EqualTo;
+ if (a === undefined) return Comparison.LessThan;
+ if (b === undefined) return Comparison.GreaterThan;
+ const value = comparer(a, b);
+ return value < 0 ? Comparison.LessThan : value > 0 ? Comparison.GreaterThan : Comparison.EqualTo;
}
- return a < b ? Comparison.LessThan : Comparison.GreaterThan;
+ function createIntlCollatorStringComparer(locale: string | undefined): Comparer {
+ // Intl.Collator.prototype.compare is bound to the collator. See NOTE in
+ // http://www.ecma-international.org/ecma-402/2.0/#sec-Intl.Collator.prototype.compare
+ const comparer = new Intl.Collator(locale, { usage: "sort", sensitivity: "variant" }).compare;
+ return (a, b) => compareWithCallback(a, b, comparer);
+ }
+
+ function createLocaleCompareStringComparer(locale: string | undefined): Comparer {
+ // if the locale is not the default locale (`undefined`), use the fallback comparer.
+ if (locale !== undefined) return createFallbackStringComparer();
+
+ return (a, b) => compareWithCallback(a, b, compareStrings);
+
+ function compareStrings(a: string, b: string) {
+ return a.localeCompare(b);
+ }
+ }
+
+ function createFallbackStringComparer(): Comparer {
+ // An ordinal comparison puts "A" after "b", but for the UI we want "A" before "b".
+ // We first sort case insensitively. So "Aaa" will come before "baa".
+ // Then we sort case sensitively, so "aaa" will come before "Aaa".
+ //
+ // For case insensitive comparisons we always map both strings to their
+ // upper-case form as some unicode characters do not properly round-trip to
+ // lowercase (such as `ẞ` (German sharp capital s)).
+ return (a, b) => compareWithCallback(a, b, compareDictionaryOrder);
+
+ function compareDictionaryOrder(a: string, b: string) {
+ return compareStrings(a.toUpperCase(), b.toUpperCase()) || compareStrings(a, b);
+ }
+
+ function compareStrings(a: string, b: string) {
+ return a < b ? Comparison.LessThan : a > b ? Comparison.GreaterThan : Comparison.EqualTo;
+ }
+ }
+
+ function getStringComparerFactory() {
+ // If the host supports Intl, we use it for comparisons using the default locale.
+ if (typeof Intl === "object" && typeof Intl.Collator === "function") {
+ return createIntlCollatorStringComparer;
+ }
+
+ // If the host does not support Intl, we fall back to localeCompare.
+ // localeCompare in Node v0.10 is just an ordinal comparison, so don't use it.
+ if (typeof String.prototype.localeCompare === "function" &&
+ typeof String.prototype.toLocaleUpperCase === "function" &&
+ "a".localeCompare("B") < 0) {
+ return createLocaleCompareStringComparer;
+ }
+
+ // Otherwise, fall back to ordinal comparison:
+ return createFallbackStringComparer;
+ }
+
+ function createStringComparer(locale: string | undefined) {
+ // Hold onto common string comparers. This avoids constantly reallocating comparers during
+ // tests.
+ if (locale === undefined) {
+ return defaultComparer || (defaultComparer = stringComparerFactory(locale));
+ }
+ else if (locale === "en-US") {
+ return enUSComparer || (enUSComparer = stringComparerFactory(locale));
+ }
+ else {
+ return stringComparerFactory(locale);
+ }
+ }
+ })();
+
+ let uiComparerCaseSensitive: Comparer | undefined;
+ let uiLocale: string | undefined;
+
+ export function getUILocale() {
+ return uiLocale;
}
- export function compareStringsCaseInsensitive(a: string, b: string) {
- return compareStrings(a, b, /*ignoreCase*/ true);
+ export function setUILocale(value: string) {
+ if (uiLocale !== value) {
+ uiLocale = value;
+ uiComparerCaseSensitive = undefined;
+ }
+ }
+
+ /**
+ * Compare two strings in a using the case-sensitive sort behavior of the UI locale.
+ *
+ * Ordering is not predictable between different host locales, but is best for displaying
+ * ordered data for UI presentation. Characters with multiple unicode representations may
+ * be considered equal.
+ *
+ * Case-sensitive comparisons compare strings that differ in base characters, or
+ * accents/diacritic marks, or case as unequal.
+ */
+ export function compareStringsCaseSensitiveUI(a: string, b: string) {
+ const comparer = uiComparerCaseSensitive || (uiComparerCaseSensitive = createUIStringComparer(uiLocale));
+ return comparer(a, b);
+ }
+
+ export function compareProperties(a: T, b: T, key: K, comparer: Comparer) {
+ return a === b ? Comparison.EqualTo :
+ a === undefined ? Comparison.LessThan :
+ b === undefined ? Comparison.GreaterThan :
+ comparer(a[key], b[key]);
}
function getDiagnosticFileName(diagnostic: Diagnostic): string {
@@ -1501,7 +1753,7 @@ namespace ts {
}
export function compareDiagnostics(d1: Diagnostic, d2: Diagnostic): Comparison {
- return compareValues(getDiagnosticFileName(d1), getDiagnosticFileName(d2)) ||
+ return compareStringsCaseSensitive(getDiagnosticFileName(d1), getDiagnosticFileName(d2)) ||
compareValues(d1.start, d2.start) ||
compareValues(d1.length, d2.length) ||
compareValues(d1.code, d2.code) ||
@@ -1515,7 +1767,7 @@ namespace ts {
const string1 = isString(text1) ? text1 : text1.messageText;
const string2 = isString(text2) ? text2 : text2.messageText;
- const res = compareValues(string1, string2);
+ const res = compareStringsCaseSensitive(string1, string2);
if (res) {
return res;
}
@@ -1533,27 +1785,8 @@ namespace ts {
return text1 ? Comparison.GreaterThan : Comparison.LessThan;
}
- export function sortAndDeduplicateDiagnostics(diagnostics: Diagnostic[]): Diagnostic[] {
- return deduplicateSortedDiagnostics(diagnostics.sort(compareDiagnostics));
- }
-
- export function deduplicateSortedDiagnostics(diagnostics: Diagnostic[]): Diagnostic[] {
- if (diagnostics.length < 2) {
- return diagnostics;
- }
-
- const newDiagnostics = [diagnostics[0]];
- let previousDiagnostic = diagnostics[0];
- for (let i = 1; i < diagnostics.length; i++) {
- const currentDiagnostic = diagnostics[i];
- const isDupe = compareDiagnostics(currentDiagnostic, previousDiagnostic) === Comparison.EqualTo;
- if (!isDupe) {
- newDiagnostics.push(currentDiagnostic);
- previousDiagnostic = currentDiagnostic;
- }
- }
-
- return newDiagnostics;
+ export function sortAndDeduplicateDiagnostics(diagnostics: ReadonlyArray): Diagnostic[] {
+ return sortAndDeduplicate(diagnostics, compareDiagnostics);
}
export function normalizeSlashes(path: string): string {
@@ -1574,7 +1807,6 @@ namespace ts {
}
if (path.charCodeAt(1) === CharacterCodes.colon) {
if (path.charCodeAt(2) === CharacterCodes.slash) return 3;
- return 2;
}
// Per RFC 1738 'file' URI schema has the shape file:///
// if is omitted then it is assumed that host value is 'localhost',
@@ -1684,6 +1916,13 @@ namespace ts {
return moduleResolution;
}
+ export function getAllowSyntheticDefaultImports(compilerOptions: CompilerOptions) {
+ const moduleKind = getEmitModuleKind(compilerOptions);
+ return compilerOptions.allowSyntheticDefaultImports !== undefined
+ ? compilerOptions.allowSyntheticDefaultImports
+ : moduleKind === ModuleKind.System;
+ }
+
export type StrictOptionName = "noImplicitAny" | "noImplicitThis" | "strictNullChecks" | "strictFunctionTypes" | "alwaysStrict";
export function getStrictOptionValue(compilerOptions: CompilerOptions, flag: StrictOptionName): boolean {
@@ -1884,8 +2123,9 @@ namespace ts {
const aComponents = getNormalizedPathComponents(a, currentDirectory);
const bComponents = getNormalizedPathComponents(b, currentDirectory);
const sharedLength = Math.min(aComponents.length, bComponents.length);
+ const comparer = ignoreCase ? compareStringsCaseInsensitive : compareStringsCaseSensitive;
for (let i = 0; i < sharedLength; i++) {
- const result = compareStrings(aComponents[i], bComponents[i], ignoreCase);
+ const result = comparer(aComponents[i], bComponents[i]);
if (result !== Comparison.EqualTo) {
return result;
}
@@ -1906,9 +2146,9 @@ namespace ts {
return false;
}
+ const equalityComparer = ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive;
for (let i = 0; i < parentComponents.length; i++) {
- const result = compareStrings(parentComponents[i], childComponents[i], ignoreCase);
- if (result !== Comparison.EqualTo) {
+ if (!equalityComparer(parentComponents[i], childComponents[i])) {
return false;
}
}
@@ -2153,6 +2393,7 @@ namespace ts {
path = normalizePath(path);
currentDirectory = normalizePath(currentDirectory);
+ const comparer = useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive;
const patterns = getFileMatcherPatterns(path, excludes, includes, useCaseSensitiveFileNames, currentDirectory);
const regexFlag = useCaseSensitiveFileNames ? "" : "i";
@@ -2164,7 +2405,6 @@ namespace ts {
// If there are no "includes", then just put everything in results[0].
const results: string[][] = includeFileRegexes ? includeFileRegexes.map(() => []) : [[]];
- const comparer = useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive;
for (const basePath of patterns.basePaths) {
visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth);
}
@@ -2172,10 +2412,9 @@ namespace ts {
return flatten(results);
function visitDirectory(path: string, absolutePath: string, depth: number | undefined) {
- let { files, directories } = getFileSystemEntries(path);
- files = files.slice().sort(comparer);
+ const { files, directories } = getFileSystemEntries(path);
- for (const current of files) {
+ for (const current of sort(files, comparer)) {
const name = combinePaths(path, current);
const absoluteName = combinePaths(absolutePath, current);
if (extensions && !fileExtensionIsOneOf(name, extensions)) continue;
@@ -2198,8 +2437,7 @@ namespace ts {
}
}
- directories = directories.slice().sort(comparer);
- for (const current of directories) {
+ for (const current of sort(directories, comparer)) {
const name = combinePaths(path, current);
const absoluteName = combinePaths(absolutePath, current);
if ((!includeDirectoryRegex || includeDirectoryRegex.test(absoluteName)) &&
@@ -2229,7 +2467,7 @@ namespace ts {
}
// Sort the offsets array using either the literal or canonical path representations.
- includeBasePaths.sort(useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive);
+ includeBasePaths.sort(useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive);
// Iterate over each include base path and include unique base paths that are not a
// subpath of an existing base path
@@ -2296,7 +2534,11 @@ namespace ts {
if (!extraFileExtensions || extraFileExtensions.length === 0 || !needAllExtensions) {
return needAllExtensions ? allSupportedExtensions : supportedTypeScriptExtensions;
}
- return deduplicate([...allSupportedExtensions, ...extraFileExtensions.map(e => e.extension)]);
+ return deduplicate(
+ [...allSupportedExtensions, ...extraFileExtensions.map(e => e.extension)],
+ equateStringsCaseSensitive,
+ compareStringsCaseSensitive
+ );
}
export function hasJavaScriptFileExtension(fileName: string) {
@@ -2393,6 +2635,17 @@ namespace ts {
return (removeFileExtension(path) + newExtension);
}
+ /**
+ * Takes a string like "jquery-min.4.2.3" and returns "jquery"
+ */
+ export function removeMinAndVersionNumbers(fileName: string) {
+ // Match a "." or "-" followed by a version number or 'min' at the end of the name
+ const trailingMinOrVersion = /[.-]((min)|(\d+(\.\d+)*))$/;
+
+ // The "min" or version may both be present, in either order, so try applying the above twice.
+ return fileName.replace(trailingMinOrVersion, "").replace(trailingMinOrVersion, "");
+ }
+
export interface ObjectAllocator {
getNodeConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => Node;
getTokenConstructor(): new (kind: TKind, pos?: number, end?: number) => Token;
@@ -2417,8 +2670,7 @@ namespace ts {
}
}
- function Signature() {
- }
+ function Signature() {} // tslint:disable-line no-empty
function Node(this: Node, kind: SyntaxKind, pos: number, end: number) {
this.id = 0;
@@ -2594,7 +2846,7 @@ namespace ts {
return findBestPatternMatch(patterns, _ => _, candidate);
}
- export function patternText({prefix, suffix}: Pattern): string {
+ export function patternText({ prefix, suffix }: Pattern): string {
return `${prefix}*${suffix}`;
}
@@ -2624,7 +2876,7 @@ namespace ts {
return matchedValue;
}
- function isPatternMatch({prefix, suffix}: Pattern, candidate: string) {
+ function isPatternMatch({ prefix, suffix }: Pattern, candidate: string) {
return candidate.length >= prefix.length + suffix.length &&
startsWith(candidate, prefix) &&
endsWith(candidate, suffix);
@@ -2689,7 +2941,7 @@ namespace ts {
return (arg: T) => f(arg) && g(arg);
}
- export function assertTypeIsNever(_: never): void { }
+ export function assertTypeIsNever(_: never): void { } // tslint:disable-line no-empty
export interface FileAndDirectoryExistence {
fileExists: boolean;
@@ -2856,10 +3108,9 @@ namespace ts {
function addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path) {
const existingResult = getCachedFileSystemEntries(fileOrDirectoryPath);
if (existingResult) {
- // This was a folder already present, remove it if this doesnt exist any more
- if (!host.directoryExists(fileOrDirectory)) {
- cachedReadDirectoryResult.delete(fileOrDirectoryPath);
- }
+ // Just clear the cache for now
+ // For now just clear the cache, since this could mean that multiple level entries might need to be re-evaluated
+ clearCache();
}
else {
// This was earlier a file (hence not in cached directory contents)
@@ -2872,8 +3123,14 @@ namespace ts {
fileExists: host.fileExists(fileOrDirectoryPath),
directoryExists: host.directoryExists(fileOrDirectoryPath)
};
- updateFilesOfFileSystemEntry(parentResult, baseName, fsQueryResult.fileExists);
- updateFileSystemEntry(parentResult.directories, baseName, fsQueryResult.directoryExists);
+ if (fsQueryResult.directoryExists || hasEntry(parentResult.directories, baseName)) {
+ // Folder added or removed, clear the cache instead of updating the folder and its structure
+ clearCache();
+ }
+ else {
+ // No need to update the directory structure, just files
+ updateFilesOfFileSystemEntry(parentResult, baseName, fsQueryResult.fileExists);
+ }
return fsQueryResult;
}
}
diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts
index 48b97b048e9..3276e68b2ac 100644
--- a/src/compiler/declarationEmitter.ts
+++ b/src/compiler/declarationEmitter.ts
@@ -1429,45 +1429,40 @@ namespace ts {
function getAccessorDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
let diagnosticMessage: DiagnosticMessage;
if (accessorWithTypeAnnotation.kind === SyntaxKind.SetAccessor) {
- // Setters have to have type named and cannot infer it so, the type should always be named
- if (hasModifier(accessorWithTypeAnnotation.parent, ModifierFlags.Static)) {
+ // Getters can infer the return type from the returned expression, but setters cannot, so the
+ // "_from_external_module_1_but_cannot_be_named" case cannot occur.
+ if (hasModifier(accessorWithTypeAnnotation, ModifierFlags.Static)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
- Diagnostics.Parameter_0_of_public_static_property_setter_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
- Diagnostics.Parameter_0_of_public_static_property_setter_from_exported_class_has_or_is_using_private_name_1;
+ Diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
+ Diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_private_name_1;
}
else {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
- Diagnostics.Parameter_0_of_public_property_setter_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
- Diagnostics.Parameter_0_of_public_property_setter_from_exported_class_has_or_is_using_private_name_1;
+ Diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
+ Diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_private_name_1;
}
- return {
- diagnosticMessage,
- errorNode: accessorWithTypeAnnotation.parameters[0],
- // TODO(jfreeman): Investigate why we are passing node.name instead of node.parameters[0].name
- typeName: accessorWithTypeAnnotation.name
- };
}
else {
if (hasModifier(accessorWithTypeAnnotation, ModifierFlags.Static)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
- Diagnostics.Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
- Diagnostics.Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_name_0_from_private_module_1 :
- Diagnostics.Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_private_name_0;
+ Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
+ Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
+ Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_private_name_1;
}
else {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
- Diagnostics.Return_type_of_public_property_getter_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
- Diagnostics.Return_type_of_public_property_getter_from_exported_class_has_or_is_using_name_0_from_private_module_1 :
- Diagnostics.Return_type_of_public_property_getter_from_exported_class_has_or_is_using_private_name_0;
+ Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
+ Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
+ Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_private_name_1;
}
- return {
- diagnosticMessage,
- errorNode: accessorWithTypeAnnotation.name,
- typeName: undefined
- };
}
+ return {
+ diagnosticMessage,
+ errorNode: accessorWithTypeAnnotation.name,
+ typeName: accessorWithTypeAnnotation.name
+ };
}
}
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 8f855347bd8..964264ff6e8 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -1716,7 +1716,7 @@
"category": "Error",
"code": 2510
},
- "Cannot create an instance of the abstract class '{0}'.": {
+ "Cannot create an instance of an abstract class.": {
"category": "Error",
"code": 2511
},
@@ -2321,43 +2321,43 @@
"category": "Error",
"code": 4033
},
- "Parameter '{0}' of public static property setter from exported class has or is using name '{1}' from private module '{2}'.": {
+ "Parameter type of public static setter '{0}' from exported class has or is using name '{1}' from private module '{2}'.": {
"category": "Error",
"code": 4034
},
- "Parameter '{0}' of public static property setter from exported class has or is using private name '{1}'.": {
+ "Parameter type of public static setter '{0}' from exported class has or is using private name '{1}'.": {
"category": "Error",
"code": 4035
},
- "Parameter '{0}' of public property setter from exported class has or is using name '{1}' from private module '{2}'.": {
+ "Parameter type of public setter '{0}' from exported class has or is using name '{1}' from private module '{2}'.": {
"category": "Error",
"code": 4036
},
- "Parameter '{0}' of public property setter from exported class has or is using private name '{1}'.": {
+ "Parameter type of public setter '{0}' from exported class has or is using private name '{1}'.": {
"category": "Error",
"code": 4037
},
- "Return type of public static property getter from exported class has or is using name '{0}' from external module {1} but cannot be named.": {
+ "Return type of public static getter '{0}' from exported class has or is using name '{1}' from external module {2} but cannot be named.": {
"category": "Error",
"code": 4038
},
- "Return type of public static property getter from exported class has or is using name '{0}' from private module '{1}'.": {
+ "Return type of public static getter '{0}' from exported class has or is using name '{1}' from private module '{2}'.": {
"category": "Error",
"code": 4039
},
- "Return type of public static property getter from exported class has or is using private name '{0}'.": {
+ "Return type of public static getter '{0}' from exported class has or is using private name '{1}'.": {
"category": "Error",
"code": 4040
},
- "Return type of public property getter from exported class has or is using name '{0}' from external module {1} but cannot be named.": {
+ "Return type of public getter '{0}' from exported class has or is using name '{1}' from external module {2} but cannot be named.": {
"category": "Error",
"code": 4041
},
- "Return type of public property getter from exported class has or is using name '{0}' from private module '{1}'.": {
+ "Return type of public getter '{0}' from exported class has or is using name '{1}' from private module '{2}'.": {
"category": "Error",
"code": 4042
},
- "Return type of public property getter from exported class has or is using private name '{0}'.": {
+ "Return type of public getter '{0}' from exported class has or is using private name '{1}'.": {
"category": "Error",
"code": 4043
},
@@ -3793,5 +3793,21 @@
"Infer parameter types from usage.": {
"category": "Message",
"code": 95012
+ },
+ "Convert to default import": {
+ "category": "Message",
+ "code": 95013
+ },
+ "Install '{0}'": {
+ "category": "Message",
+ "code": 95014
+ },
+ "Import '{0}' = require(\"{1}\").": {
+ "category": "Message",
+ "code": 95015
+ },
+ "Import * as '{0}' from \"{1}\".": {
+ "category": "Message",
+ "code": 95016
}
}
diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts
index 8c41eda192a..7a1d88d3bfd 100644
--- a/src/compiler/emitter.ts
+++ b/src/compiler/emitter.ts
@@ -1733,26 +1733,15 @@ namespace ts {
increaseIndent();
}
- if (getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) {
- emitSignatureHead(node);
- if (onEmitNode) {
- onEmitNode(EmitHint.Unspecified, body, emitBlockCallback);
- }
- else {
- emitBlockFunctionBody(body);
- }
+ pushNameGenerationScope(node);
+ emitSignatureHead(node);
+ if (onEmitNode) {
+ onEmitNode(EmitHint.Unspecified, body, emitBlockCallback);
}
else {
- pushNameGenerationScope();
- emitSignatureHead(node);
- if (onEmitNode) {
- onEmitNode(EmitHint.Unspecified, body, emitBlockCallback);
- }
- else {
- emitBlockFunctionBody(body);
- }
- popNameGenerationScope();
+ emitBlockFunctionBody(body);
}
+ popNameGenerationScope(node);
if (indentedFlag) {
decreaseIndent();
@@ -1871,11 +1860,9 @@ namespace ts {
emitTypeParameters(node, node.typeParameters);
emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses);
- pushNameGenerationScope();
write(" {");
emitList(node, node.members, ListFormat.ClassMembers);
write("}");
- popNameGenerationScope();
if (indentedFlag) {
decreaseIndent();
@@ -1909,11 +1896,9 @@ namespace ts {
emitModifiers(node, node.modifiers);
write("enum ");
emit(node.name);
- pushNameGenerationScope();
write(" {");
emitList(node, node.members, ListFormat.EnumMembers);
write("}");
- popNameGenerationScope();
}
function emitModuleDeclaration(node: ModuleDeclaration) {
@@ -1935,11 +1920,11 @@ namespace ts {
}
function emitModuleBlock(node: ModuleBlock) {
- pushNameGenerationScope();
+ pushNameGenerationScope(node);
write("{");
emitBlockStatements(node, /*forceSingleLine*/ isEmptyBlock(node));
write("}");
- popNameGenerationScope();
+ popNameGenerationScope(node);
}
function emitCaseBlock(node: CaseBlock) {
@@ -2284,11 +2269,11 @@ namespace ts {
function emitSourceFileWorker(node: SourceFile) {
const statements = node.statements;
- pushNameGenerationScope();
+ pushNameGenerationScope(node);
emitHelpersIndirect(node);
const index = findIndex(statements, statement => !isPrologueDirective(statement));
emitList(node, statements, ListFormat.MultiLine, index === -1 ? statements.length : index);
- popNameGenerationScope();
+ popNameGenerationScope(node);
}
// Transformation nodes
@@ -2661,8 +2646,8 @@ namespace ts {
function writeLines(text: string): void {
const lines = text.split(/\r\n?|\n/g);
const indentation = guessIndentation(lines);
- for (let i = 0; i < lines.length; i++) {
- const line = indentation ? lines[i].slice(indentation) : lines[i];
+ for (const lineText of lines) {
+ const line = indentation ? lineText.slice(indentation) : lineText;
if (line.length) {
writeLine();
write(line);
@@ -2751,7 +2736,7 @@ namespace ts {
}
}
else {
- return nextNode.startsOnNewLine;
+ return getStartsOnNewLine(nextNode);
}
}
@@ -2782,7 +2767,7 @@ namespace ts {
function synthesizedNodeStartsOnNewLine(node: Node, format?: ListFormat) {
if (nodeIsSynthesized(node)) {
- const startsOnNewLine = node.startsOnNewLine;
+ const startsOnNewLine = getStartsOnNewLine(node);
if (startsOnNewLine === undefined) {
return (format & ListFormat.PreferNewLine) !== 0;
}
@@ -2799,7 +2784,7 @@ namespace ts {
node2 = skipSynthesizedParentheses(node2);
// Always use a newline for synthesized code if the synthesizer desires it.
- if (node2.startsOnNewLine) {
+ if (getStartsOnNewLine(node2)) {
return true;
}
@@ -2858,7 +2843,10 @@ namespace ts {
/**
* Push a new name generation scope.
*/
- function pushNameGenerationScope() {
+ function pushNameGenerationScope(node: Node | undefined) {
+ if (node && getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) {
+ return;
+ }
tempFlagsStack.push(tempFlags);
tempFlags = 0;
}
@@ -2866,7 +2854,10 @@ namespace ts {
/**
* Pop the current name generation scope.
*/
- function popNameGenerationScope() {
+ function popNameGenerationScope(node: Node | undefined) {
+ if (node && getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) {
+ return;
+ }
tempFlags = tempFlagsStack.pop();
}
@@ -2877,8 +2868,17 @@ namespace ts {
if (name.autoGenerateKind === GeneratedIdentifierKind.Node) {
// Node names generate unique names based on their original node
// and are cached based on that node's id.
- const node = getNodeForGeneratedName(name);
- return generateNameCached(node);
+ if (name.skipNameGenerationScope) {
+ const savedTempFlags = tempFlags;
+ popNameGenerationScope(/*node*/ undefined);
+ const result = generateNameCached(getNodeForGeneratedName(name));
+ pushNameGenerationScope(/*node*/ undefined);
+ tempFlags = savedTempFlags;
+ return result;
+ }
+ else {
+ return generateNameCached(getNodeForGeneratedName(name));
+ }
}
else {
// Auto, Loop, and Unique names are cached based on their unique
diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts
index fe5482cf593..e8dfcbc1e94 100644
--- a/src/compiler/factory.ts
+++ b/src/compiler/factory.ts
@@ -13,9 +13,6 @@ namespace ts {
if (updated !== original) {
setOriginalNode(updated, original);
setTextRange(updated, original);
- if (original.startsOnNewLine) {
- updated.startsOnNewLine = true;
- }
aggregateTransformFlags(updated);
}
return updated;
@@ -73,11 +70,10 @@ namespace ts {
// Literals
- export function createLiteral(value: string): StringLiteral;
+ /** If a node is passed, creates a string literal whose source text is read from a source node during emit. */
+ export function createLiteral(value: string | StringLiteral | NumericLiteral | Identifier): StringLiteral;
export function createLiteral(value: number): NumericLiteral;
export function createLiteral(value: boolean): BooleanLiteral;
- /** Create a string literal whose source text is read from a source node during emit. */
- export function createLiteral(sourceNode: StringLiteral | NumericLiteral | Identifier): StringLiteral;
export function createLiteral(value: string | number | boolean): PrimaryExpression;
export function createLiteral(value: string | number | boolean | StringLiteral | NumericLiteral | Identifier): PrimaryExpression {
if (typeof value === "number") {
@@ -116,6 +112,7 @@ namespace ts {
export function createIdentifier(text: string): Identifier;
/* @internal */
+ // tslint:disable-next-line unified-signatures
export function createIdentifier(text: string, typeArguments: ReadonlyArray): Identifier;
export function createIdentifier(text: string, typeArguments?: ReadonlyArray): Identifier {
const node = createSynthesizedNode(SyntaxKind.Identifier);
@@ -168,11 +165,15 @@ namespace ts {
}
/** Create a unique name generated for a node. */
- export function getGeneratedNameForNode(node: Node): Identifier {
+ export function getGeneratedNameForNode(node: Node): Identifier;
+ // tslint:disable-next-line unified-signatures
+ /*@internal*/ export function getGeneratedNameForNode(node: Node, shouldSkipNameGenerationScope?: boolean): Identifier;
+ export function getGeneratedNameForNode(node: Node, shouldSkipNameGenerationScope?: boolean): Identifier {
const name = createIdentifier("");
name.autoGenerateKind = GeneratedIdentifierKind.Node;
name.autoGenerateId = nextAutoGenerateId;
name.original = node;
+ name.skipNameGenerationScope = !!shouldSkipNameGenerationScope;
nextAutoGenerateId++;
return name;
}
@@ -2641,7 +2642,7 @@ namespace ts {
/**
* Gets a custom text range to use when emitting source maps.
*/
- export function getSourceMapRange(node: Node) {
+ export function getSourceMapRange(node: Node): SourceMapRange {
const emitNode = node.emitNode;
return (emitNode && emitNode.sourceMapRange) || node;
}
@@ -2654,6 +2655,7 @@ namespace ts {
return node;
}
+ // tslint:disable-next-line variable-name
let SourceMapSource: new (fileName: string, text: string, skipTrivia?: (pos: number) => number) => SourceMapSource;
/**
@@ -2682,6 +2684,24 @@ namespace ts {
return node;
}
+ /**
+ * Gets a custom text range to use when emitting comments.
+ */
+ /*@internal*/
+ export function getStartsOnNewLine(node: Node) {
+ const emitNode = node.emitNode;
+ return emitNode && emitNode.startsOnNewLine;
+ }
+
+ /**
+ * Sets a custom text range to use when emitting comments.
+ */
+ /*@internal*/
+ export function setStartsOnNewLine(node: T, newLine: boolean) {
+ getOrCreateEmitNode(node).startsOnNewLine = newLine;
+ return node;
+ }
+
/**
* Gets a custom text range to use when emitting comments.
*/
@@ -2840,7 +2860,8 @@ namespace ts {
sourceMapRange,
tokenSourceMapRanges,
constantValue,
- helpers
+ helpers,
+ startsOnNewLine,
} = sourceEmitNode;
if (!destEmitNode) destEmitNode = {};
// We are using `.slice()` here in case `destEmitNode.leadingComments` is pushed to later.
@@ -2852,6 +2873,7 @@ namespace ts {
if (tokenSourceMapRanges) destEmitNode.tokenSourceMapRanges = mergeTokenSourceMapRanges(tokenSourceMapRanges, destEmitNode.tokenSourceMapRanges);
if (constantValue !== undefined) destEmitNode.constantValue = constantValue;
if (helpers) destEmitNode.helpers = addRange(destEmitNode.helpers, helpers);
+ if (startsOnNewLine !== undefined) destEmitNode.startsOnNewLine = startsOnNewLine;
return destEmitNode;
}
@@ -3013,7 +3035,7 @@ namespace ts {
if (children.length > 1) {
for (const child of children) {
- child.startsOnNewLine = true;
+ startOnNewLine(child);
argumentsList.push(child);
}
}
@@ -3044,7 +3066,7 @@ namespace ts {
if (children && children.length > 0) {
if (children.length > 1) {
for (const child of children) {
- child.startsOnNewLine = true;
+ startOnNewLine(child);
argumentsList.push(child);
}
}
@@ -3619,8 +3641,8 @@ namespace ts {
);
setOriginalNode(updated, node);
setTextRange(updated, node);
- if (node.startsOnNewLine) {
- updated.startsOnNewLine = true;
+ if (getStartsOnNewLine(node)) {
+ setStartsOnNewLine(updated, /*newLine*/ true);
}
aggregateTransformFlags(updated);
return updated;
@@ -4249,8 +4271,7 @@ namespace ts {
}
export function startOnNewLine(node: T): T {
- node.startsOnNewLine = true;
- return node;
+ return setStartsOnNewLine(node, /*newLine*/ true);
}
export function getExternalHelpersModuleName(node: SourceFile) {
@@ -4298,7 +4319,7 @@ namespace ts {
const namespaceDeclaration = getNamespaceDeclarationNode(node);
if (namespaceDeclaration && !isDefaultImport(node)) {
const name = namespaceDeclaration.name;
- return isGeneratedIdentifier(name) ? name : createIdentifier(getSourceTextOfNodeFromSourceFile(sourceFile, namespaceDeclaration.name));
+ return isGeneratedIdentifier(name) ? name : createIdentifier(getSourceTextOfNodeFromSourceFile(sourceFile, name) || idText(name));
}
if (node.kind === SyntaxKind.ImportDeclaration && (node).importClause) {
return getGeneratedNameForNode(node);
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 648f3e30d1c..64cd8219a01 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -12,10 +12,12 @@ namespace ts {
JSDoc = 1 << 5,
}
+ // tslint:disable variable-name
let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let IdentifierConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
+ // tslint:enable variable-name
export function createNode(kind: SyntaxKind, pos?: number, end?: number): Node {
if (kind === SyntaxKind.SourceFile) {
@@ -524,10 +526,24 @@ namespace ts {
const disallowInAndDecoratorContext = NodeFlags.DisallowInContext | NodeFlags.DecoratorContext;
// capture constructors in 'initializeState' to avoid null checks
+ // tslint:disable variable-name
let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let IdentifierConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
+ interface Fail extends Node { kind: SyntaxKind.Unknown; }
+ interface FailList extends NodeArray { pos: -1; }
+ let Fail: Fail;
+ let FailList: FailList;
+ function isFail(x: Node | undefined): x is Fail {
+ Debug.assert(Fail !== undefined);
+ return x === Fail;
+ }
+ function isFailList(x: NodeArray | undefined): x is FailList {
+ Debug.assert(Fail !== undefined);
+ return x === FailList;
+ }
+ // tslint:enable variable-name
let sourceFile: SourceFile;
let parseDiagnostics: Diagnostic[];
@@ -631,6 +647,7 @@ namespace ts {
}
export function parseIsolatedEntityName(content: string, languageVersion: ScriptTarget): EntityName {
+ // Choice of `isDeclarationFile` should be arbitrary
initializeState(content, languageVersion, /*syntaxCursor*/ undefined, ScriptKind.JS);
// Prime the scanner.
nextToken();
@@ -643,7 +660,7 @@ namespace ts {
export function parseJsonText(fileName: string, sourceText: string): JsonSourceFile {
initializeState(sourceText, ScriptTarget.ES2015, /*syntaxCursor*/ undefined, ScriptKind.JSON);
// Set source file so that errors will be reported with this file name
- sourceFile = createSourceFile(fileName, ScriptTarget.ES2015, ScriptKind.JSON);
+ sourceFile = createSourceFile(fileName, ScriptTarget.ES2015, ScriptKind.JSON, /*isDeclaration*/ false);
const result = sourceFile;
// Prime the scanner.
@@ -676,6 +693,9 @@ namespace ts {
IdentifierConstructor = objectAllocator.getIdentifierConstructor();
SourceFileConstructor = objectAllocator.getSourceFileConstructor();
+ Fail = createNode(SyntaxKind.Unknown) as Fail;
+ FailList = createNodeArray([], -1) as FailList;
+
sourceText = _sourceText;
syntaxCursor = _syntaxCursor;
@@ -685,7 +705,16 @@ namespace ts {
identifierCount = 0;
nodeCount = 0;
- contextFlags = scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JSON ? NodeFlags.JavaScriptFile : NodeFlags.None;
+ switch (scriptKind) {
+ case ScriptKind.JS:
+ case ScriptKind.JSX:
+ case ScriptKind.JSON:
+ contextFlags = NodeFlags.JavaScriptFile;
+ break;
+ default:
+ contextFlags = NodeFlags.None;
+ break;
+ }
parseErrorBeforeNextFinishedNode = false;
// Initialize and prime the scanner before parsing the source elements.
@@ -709,7 +738,12 @@ namespace ts {
}
function parseSourceFileWorker(fileName: string, languageVersion: ScriptTarget, setParentNodes: boolean, scriptKind: ScriptKind): SourceFile {
- sourceFile = createSourceFile(fileName, languageVersion, scriptKind);
+ const isDeclarationFile = isDeclarationFileName(fileName);
+ if (isDeclarationFile) {
+ contextFlags |= NodeFlags.Ambient;
+ }
+
+ sourceFile = createSourceFile(fileName, languageVersion, scriptKind, isDeclarationFile);
sourceFile.flags = contextFlags;
// Prime the scanner.
@@ -717,7 +751,7 @@ namespace ts {
processReferenceComments(sourceFile);
sourceFile.statements = parseList(ParsingContext.SourceElements, parseStatement);
- Debug.assert(token() === SyntaxKind.EndOfFileToken);
+ Debug.assertEqual(token(), SyntaxKind.EndOfFileToken);
sourceFile.endOfFileToken = addJSDocComment(parseTokenNode() as EndOfFileToken);
setExternalModuleIndicator(sourceFile);
@@ -786,7 +820,7 @@ namespace ts {
}
}
- function createSourceFile(fileName: string, languageVersion: ScriptTarget, scriptKind: ScriptKind): SourceFile {
+ function createSourceFile(fileName: string, languageVersion: ScriptTarget, scriptKind: ScriptKind, isDeclarationFile: boolean): SourceFile {
// code from createNode is inlined here so createNode won't have to deal with special case of creating source files
// this is quite rare comparing to other nodes and createNode should be as fast as possible
const sourceFile = new SourceFileConstructor(SyntaxKind.SourceFile, /*pos*/ 0, /* end */ sourceText.length);
@@ -797,7 +831,7 @@ namespace ts {
sourceFile.languageVersion = languageVersion;
sourceFile.fileName = normalizePath(fileName);
sourceFile.languageVariant = getLanguageVariant(scriptKind);
- sourceFile.isDeclarationFile = fileExtensionIs(sourceFile.fileName, Extension.Dts);
+ sourceFile.isDeclarationFile = isDeclarationFile;
sourceFile.scriptKind = scriptKind;
return sourceFile;
@@ -984,7 +1018,7 @@ namespace ts {
return currentToken = scanner.scanJsxAttributeValue();
}
- function speculationHelper(callback: () => T, isLookAhead: boolean): T {
+ function speculationHelper(callback: () => T, isLookAhead: boolean): T | undefined {
// Keep track of the state we'll need to rollback to if lookahead fails (or if the
// caller asked us to always reset our state).
const saveToken = currentToken;
@@ -996,6 +1030,7 @@ namespace ts {
// descent nature of our parser. However, we still store this here just so we can
// assert that invariant holds.
const saveContextFlags = contextFlags;
+ const saveParsingContext = parsingContext;
// If we're only looking ahead, then tell the scanner to only lookahead as well.
// Otherwise, if we're actually speculatively parsing, then tell the scanner to do the
@@ -1004,7 +1039,8 @@ namespace ts {
? scanner.lookAhead(callback)
: scanner.tryScan(callback);
- Debug.assert(saveContextFlags === contextFlags);
+ Debug.assertEqual(saveContextFlags, contextFlags);
+ Debug.assertEqual(saveParsingContext, parsingContext);
// If our callback returned something 'falsy' or we're just looking ahead,
// then unconditionally restore us to where we were.
@@ -1558,7 +1594,7 @@ namespace ts {
return createNodeArray(list, listPos);
}
- function parseListElement(parsingContext: ParsingContext, parseElement: () => T): T {
+ function parseListElement(parsingContext: ParsingContext, parseElement: () => T): T {
const node = currentNode(parsingContext);
if (node) {
return consumeNode(node);
@@ -1882,17 +1918,24 @@ namespace ts {
}
// Parses a comma-delimited list of elements
- function parseDelimitedList(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimiter?: boolean): NodeArray {
+ function parseDelimitedList(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimiter?: boolean): NodeArray;
+ function parseDelimitedList(kind: ParsingContext, parseElement: () => T | Fail, considerSemicolonAsDelimiter?: boolean): NodeArray | FailList;
+ function parseDelimitedList(kind: ParsingContext, parseElement: () => T | Fail, considerSemicolonAsDelimiter?: boolean): NodeArray | FailList {
const saveParsingContext = parsingContext;
parsingContext |= 1 << kind;
- const list = [];
+ const list: T[] = [];
const listPos = getNodePos();
let commaStart = -1; // Meaning the previous token was not a comma
while (true) {
if (isListElement(kind, /*inErrorRecovery*/ false)) {
const startPos = scanner.getStartPos();
- list.push(parseListElement(kind, parseElement));
+ const elem = parseListElement(kind, parseElement);
+ if (isFail(elem)) {
+ parsingContext = saveParsingContext;
+ return FailList;
+ }
+ list.push(elem);
commaStart = scanner.getTokenPos();
if (parseOptional(SyntaxKind.CommaToken)) {
@@ -2252,7 +2295,13 @@ namespace ts {
isStartOfType(/*inStartOfParameter*/ true);
}
- function parseParameter(requireEqualsToken?: boolean): ParameterDeclaration {
+ function tryParseParameter(): ParameterDeclaration | Fail {
+ return parseParameterWorker(/*inSpeculation*/ true);
+ }
+ function parseParameter(): ParameterDeclaration {
+ return parseParameterWorker(/*inSpeculation*/ false) as ParameterDeclaration;
+ }
+ function parseParameterWorker(inSpeculation: boolean): ParameterDeclaration | Fail {
const node = createNode(SyntaxKind.Parameter);
if (token() === SyntaxKind.ThisKeyword) {
node.name = createIdentifier(/*isIdentifier*/ true);
@@ -2266,7 +2315,11 @@ namespace ts {
// FormalParameter [Yield,Await]:
// BindingElement[?Yield,?Await]
- node.name = parseIdentifierOrPattern();
+ const name = parseIdentifierOrPattern(inSpeculation);
+ if (isFail(name)) {
+ return Fail;
+ }
+ node.name = name;
if (getFullWidth(node.name) === 0 && !hasModifiers(node) && isModifierKind(token())) {
// in cases like
// 'use strict'
@@ -2281,20 +2334,27 @@ namespace ts {
node.questionToken = parseOptionalToken(SyntaxKind.QuestionToken);
node.type = parseParameterType();
- node.initializer = parseInitializer(/*inParameter*/ true, requireEqualsToken);
+ const initializer = parseInitializer(/*inParameter*/ true, inSpeculation);
+ if (isFail(initializer)) {
+ return Fail;
+ }
+ node.initializer = initializer;
return addJSDocComment(finishNode(node));
}
- function fillSignature(
- returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken,
- flags: SignatureFlags,
- signature: SignatureDeclaration): void {
+ /** @return 'true' on success. */
+ function fillSignature(returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, flags: SignatureFlags, signature: SignatureDeclaration, inSpeculation?: boolean): boolean {
if (!(flags & SignatureFlags.JSDoc)) {
signature.typeParameters = parseTypeParameters();
}
- signature.parameters = parseParameterList(flags);
+ const parameters = parseParameterList(flags, inSpeculation);
+ if (isFailList(parameters)) {
+ return false;
+ }
+ signature.parameters = parameters;
signature.type = parseReturnType(returnToken, !!(flags & SignatureFlags.Type));
+ return true;
}
function parseReturnType(returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, isType: boolean): TypeNode | undefined {
@@ -2317,7 +2377,7 @@ namespace ts {
return false;
}
- function parseParameterList(flags: SignatureFlags) {
+ function parseParameterList(flags: SignatureFlags, inSpeculation: boolean): NodeArray | FailList {
// FormalParameters [Yield,Await]: (modified)
// [empty]
// FormalParameterList[?Yield,Await]
@@ -2338,9 +2398,9 @@ namespace ts {
setYieldContext(!!(flags & SignatureFlags.Yield));
setAwaitContext(!!(flags & SignatureFlags.Await));
- const result = parseDelimitedList(ParsingContext.Parameters,
- flags & SignatureFlags.JSDoc ? parseJSDocParameter : () => parseParameter(!!(flags & SignatureFlags.RequireCompleteParameterList)));
-
+ const result = parseDelimitedList(
+ ParsingContext.Parameters,
+ flags & SignatureFlags.JSDoc ? parseJSDocParameter : inSpeculation ? tryParseParameter : parseParameter);
setYieldContext(savedYieldContext);
setAwaitContext(savedAwaitContext);
@@ -3013,14 +3073,16 @@ namespace ts {
while ((operatorToken = parseOptionalToken(SyntaxKind.CommaToken))) {
expr = makeBinaryExpression(expr, operatorToken, parseAssignmentExpressionOrHigher());
}
-
if (saveDecoratorContext) {
setDecoratorContext(/*val*/ true);
}
+
return expr;
}
- function parseInitializer(inParameter: boolean, requireEqualsToken?: boolean): Expression {
+ function parseInitializer(inParameter: boolean): Expression | undefined;
+ function parseInitializer(inParameter: boolean, inSpeculation?: boolean): Expression | Fail | undefined;
+ function parseInitializer(inParameter: boolean, inSpeculation?: boolean): Expression | Fail | undefined {
if (token() !== SyntaxKind.EqualsToken) {
// It's not uncommon during typing for the user to miss writing the '=' token. Check if
// there is no newline after the last token and if we're on an expression. If so, parse
@@ -3035,12 +3097,8 @@ namespace ts {
// do not try to parse initializer
return undefined;
}
- if (inParameter && requireEqualsToken) {
- // = is required when speculatively parsing arrow function parameters,
- // so return a fake initializer as a signal that the equals token was missing
- const result = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics._0_expected, "=") as Identifier;
- result.escapedText = "= not found" as __String;
- return result;
+ if (inSpeculation) {
+ return Fail;
}
}
@@ -3206,7 +3264,7 @@ namespace ts {
// it out, but don't allow any ambiguity, and return 'undefined' if this could be an
// expression instead.
const arrowFunction = triState === Tristate.True
- ? parseParenthesizedArrowFunctionExpressionHead(/*allowAmbiguity*/ true)
+ ? parseParenthesizedArrowFunctionExpressionHead(/*inSpeculation*/ false)
: tryParse(parsePossibleParenthesizedArrowFunctionExpressionHead);
if (!arrowFunction) {
@@ -3354,7 +3412,7 @@ namespace ts {
}
function parsePossibleParenthesizedArrowFunctionExpressionHead(): ArrowFunction {
- return parseParenthesizedArrowFunctionExpressionHead(/*allowAmbiguity*/ false);
+ return parseParenthesizedArrowFunctionExpressionHead(/*inSpeculation*/ true);
}
function tryParseAsyncSimpleArrowFunctionExpression(): ArrowFunction | undefined {
@@ -3390,7 +3448,7 @@ namespace ts {
return Tristate.False;
}
- function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction {
+ function parseParenthesizedArrowFunctionExpressionHead(inSpeculation: boolean): ArrowFunction | undefined {
const node = createNode(SyntaxKind.ArrowFunction);
node.modifiers = parseModifiersForArrowFunction();
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
@@ -3401,7 +3459,10 @@ namespace ts {
// a => (b => c)
// And think that "(b =>" was actually a parenthesized arrow function with a missing
// close paren.
- fillSignature(SyntaxKind.ColonToken, isAsync | (allowAmbiguity ? SignatureFlags.None : SignatureFlags.RequireCompleteParameterList), node);
+
+ if (!fillSignature(SyntaxKind.ColonToken, isAsync | (inSpeculation ? SignatureFlags.RequireCompleteParameterList : SignatureFlags.None), node, inSpeculation)) {
+ return undefined;
+ }
// If we couldn't get parameters, we definitely could not parse out an arrow function.
if (!node.parameters) {
@@ -3416,8 +3477,7 @@ namespace ts {
// - "a ? (b): c" will have "(b):" parsed as a signature with a return type annotation.
//
// So we need just a bit of lookahead to ensure that it can only be a signature.
- if (!allowAmbiguity && ((token() !== SyntaxKind.EqualsGreaterThanToken && token() !== SyntaxKind.OpenBraceToken) ||
- find(node.parameters, p => p.initializer && ts.isIdentifier(p.initializer) && p.initializer.escapedText === "= not found"))) {
+ if (inSpeculation && token() !== SyntaxKind.EqualsGreaterThanToken && token() !== SyntaxKind.OpenBraceToken) {
// Returning undefined here will cause our caller to rewind to where we started from.
return undefined;
}
@@ -4555,7 +4615,6 @@ namespace ts {
if (saveDecoratorContext) {
setDecoratorContext(/*val*/ false);
}
-
const node = createNode(SyntaxKind.FunctionExpression);
node.modifiers = parseModifiers();
parseExpected(SyntaxKind.FunctionKeyword);
@@ -4571,7 +4630,6 @@ namespace ts {
fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node);
node.body = parseFunctionBlock(isGenerator | isAsync);
-
if (saveDecoratorContext) {
setDecoratorContext(/*val*/ true);
}
@@ -4634,7 +4692,6 @@ namespace ts {
}
const block = parseBlock(!!(flags & SignatureFlags.IgnoreMissingOpenBrace), diagnosticMessage);
-
if (saveDecoratorContext) {
setDecoratorContext(/*val*/ true);
}
@@ -5135,6 +5192,18 @@ namespace ts {
const fullStart = getNodePos();
const decorators = parseDecorators();
const modifiers = parseModifiers();
+ if (some(modifiers, m => m.kind === SyntaxKind.DeclareKeyword)) {
+ for (const m of modifiers) {
+ m.flags |= NodeFlags.Ambient;
+ }
+ return doInsideOfContext(NodeFlags.Ambient, () => parseDeclarationWorker(fullStart, decorators, modifiers));
+ }
+ else {
+ return parseDeclarationWorker(fullStart, decorators, modifiers);
+ }
+ }
+
+ function parseDeclarationWorker(fullStart: number, decorators: NodeArray | undefined, modifiers: NodeArray | undefined): Statement {
switch (token()) {
case SyntaxKind.VarKeyword:
case SyntaxKind.LetKeyword:
@@ -5196,18 +5265,38 @@ namespace ts {
// DECLARATIONS
+ function tryParseArrayBindingElement(): ArrayBindingElement | Fail {
+ return parseArrayBindingElementWorker(/*inSpeculation*/ true);
+ }
function parseArrayBindingElement(): ArrayBindingElement {
+ return parseArrayBindingElementWorker(/*inSpeculation*/ false) as ArrayBindingElement;
+ }
+ function parseArrayBindingElementWorker(inSpeculation: boolean): ArrayBindingElement | Fail {
if (token() === SyntaxKind.CommaToken) {
return createNode(SyntaxKind.OmittedExpression);
}
const node = createNode(SyntaxKind.BindingElement);
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
- node.name = parseIdentifierOrPattern();
- node.initializer = parseInitializer(/*inParameter*/ false);
+ const name = parseIdentifierOrPattern(inSpeculation);
+ if (isFail(name)) {
+ return Fail;
+ }
+ node.name = name;
+ const init = parseInitializer(/*inParameter*/ false, inSpeculation);
+ if (isFail(init)) {
+ return Fail;
+ }
+ node.initializer = init;
return finishNode(node);
}
+ function tryParseObjectBindingElement(): BindingElement | Fail {
+ return parseObjectBindingElementWorker(/*inSpeculation*/ true);
+ }
function parseObjectBindingElement(): BindingElement {
+ return parseObjectBindingElementWorker(/*inSpeculation*/ false) as BindingElement;
+ }
+ function parseObjectBindingElementWorker(inSpeculation: boolean): BindingElement | Fail {
const node = createNode(SyntaxKind.BindingElement);
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
const tokenIsIdentifier = isIdentifier();
@@ -5218,24 +5307,46 @@ namespace ts {
else {
parseExpected(SyntaxKind.ColonToken);
node.propertyName = propertyName;
- node.name = parseIdentifierOrPattern();
+ const name = parseIdentifierOrPattern(inSpeculation);
+ if (isFail(name)) {
+ return Fail;
+ }
+ node.name = name;
}
- node.initializer = parseInitializer(/*inParameter*/ false);
+ const init = parseInitializer(/*inParameter*/ false, inSpeculation);
+ if (isFail(init)) {
+ return Fail;
+ }
+ node.initializer = init;
return finishNode(node);
}
- function parseObjectBindingPattern(): ObjectBindingPattern {
+ function parseObjectBindingPattern(inSpeculation: boolean): ObjectBindingPattern | Fail {
const node = createNode(SyntaxKind.ObjectBindingPattern);
parseExpected(SyntaxKind.OpenBraceToken);
- node.elements = parseDelimitedList(ParsingContext.ObjectBindingElements, parseObjectBindingElement);
+ const elements = parseDelimitedList(
+ ParsingContext.ObjectBindingElements,
+ inSpeculation ? tryParseObjectBindingElement : parseObjectBindingElement,
+ /*considerSemicolonAsDelimiter*/ undefined);
+ if (isFailList(elements)) {
+ return Fail;
+ }
+ node.elements = elements;
parseExpected(SyntaxKind.CloseBraceToken);
return finishNode(node);
}
- function parseArrayBindingPattern(): ArrayBindingPattern {
+ function parseArrayBindingPattern(inSpeculation: boolean): ArrayBindingPattern | Fail {
const node = createNode(SyntaxKind.ArrayBindingPattern);
parseExpected(SyntaxKind.OpenBracketToken);
- node.elements = parseDelimitedList(ParsingContext.ArrayBindingElements, parseArrayBindingElement);
+ const elements = parseDelimitedList(
+ ParsingContext.ArrayBindingElements,
+ inSpeculation ? tryParseArrayBindingElement : parseArrayBindingElement,
+ /*considerSemicolonAsDelimiter*/ undefined);
+ if (isFailList(elements)) {
+ return Fail;
+ }
+ node.elements = elements;
parseExpected(SyntaxKind.CloseBracketToken);
return finishNode(node);
}
@@ -5244,12 +5355,14 @@ namespace ts {
return token() === SyntaxKind.OpenBraceToken || token() === SyntaxKind.OpenBracketToken || isIdentifier();
}
- function parseIdentifierOrPattern(): Identifier | BindingPattern {
+ function parseIdentifierOrPattern(): Identifier | BindingPattern;
+ function parseIdentifierOrPattern(inSpeculation: boolean): Identifier | BindingPattern | Fail;
+ function parseIdentifierOrPattern(inSpeculation?: boolean): Identifier | BindingPattern | Fail {
if (token() === SyntaxKind.OpenBracketToken) {
- return parseArrayBindingPattern();
+ return parseArrayBindingPattern(inSpeculation);
}
if (token() === SyntaxKind.OpenBraceToken) {
- return parseObjectBindingPattern();
+ return parseObjectBindingPattern(inSpeculation);
}
return parseIdentifier();
}
@@ -5297,9 +5410,7 @@ namespace ts {
else {
const savedDisallowIn = inDisallowInContext();
setDisallowInContext(inForStatementInitializer);
-
node.declarations = parseDelimitedList(ParsingContext.VariableDeclarations, parseVariableDeclaration);
-
setDisallowInContext(savedDisallowIn);
}
@@ -5397,7 +5508,7 @@ namespace ts {
}
}
- function parseNonParameterInitializer() {
+ function parseNonParameterInitializer(): Expression | undefined {
return parseInitializer(/*inParameter*/ false);
}
@@ -5492,8 +5603,8 @@ namespace ts {
return false;
}
- function parseDecorators(): NodeArray {
- let list: Decorator[];
+ function parseDecorators(): NodeArray | undefined {
+ let list: Decorator[] | undefined;
const listPos = getNodePos();
while (true) {
const decoratorStart = getNodePos();
@@ -6115,7 +6226,7 @@ namespace ts {
const checkJsDirectiveMatchResult = checkJsDirectiveRegEx.exec(comment);
if (checkJsDirectiveMatchResult) {
checkJsDirective = {
- enabled: compareStrings(checkJsDirectiveMatchResult[1], "@ts-check", /*ignoreCase*/ true) === Comparison.EqualTo,
+ enabled: equateStringsCaseInsensitive(checkJsDirectiveMatchResult[1], "@ts-check"),
end: range.end,
pos: range.pos
};
@@ -6177,7 +6288,7 @@ namespace ts {
export namespace JSDocParser {
export function parseJSDocTypeExpressionForTests(content: string, start: number, length: number): { jsDocTypeExpression: JSDocTypeExpression, diagnostics: Diagnostic[] } | undefined {
initializeState(content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS);
- sourceFile = createSourceFile("file.js", ScriptTarget.Latest, ScriptKind.JS);
+ sourceFile = createSourceFile("file.js", ScriptTarget.Latest, ScriptKind.JS, /*isDeclarationFile*/ false);
scanner.setText(content, start, length);
currentToken = scanner.scan();
const jsDocTypeExpression = parseJSDocTypeExpression();
@@ -7507,4 +7618,8 @@ namespace ts {
Value = -1
}
}
+
+ function isDeclarationFileName(fileName: string): boolean {
+ return fileExtensionIs(fileName, Extension.Dts);
+ }
}
diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts
index 8c24b3b9f1b..104148ed4ab 100644
--- a/src/compiler/performance.ts
+++ b/src/compiler/performance.ts
@@ -10,9 +10,8 @@ namespace ts {
namespace ts.performance {
declare const onProfilerEvent: { (markName: string): void; profiler: boolean; };
- const profilerEvent = typeof onProfilerEvent === "function" && onProfilerEvent.profiler === true
- ? onProfilerEvent
- : (_markName: string) => { };
+ // NOTE: cannot use ts.noop as core.ts loads after this
+ const profilerEvent: (markName: string) => void = typeof onProfilerEvent === "function" && onProfilerEvent.profiler === true ? onProfilerEvent : () => { /*empty*/ };
let enabled = false;
let profilerStart = 0;
diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index e00e803b489..cf7c03556f1 100755
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -7,18 +7,10 @@ namespace ts {
const ignoreDiagnosticCommentRegEx = /(^\s*$)|(^\s*\/\/\/?\s*(@ts-ignore)?)/;
export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "tsconfig.json"): string {
- while (true) {
- const fileName = combinePaths(searchPath, configName);
- if (fileExists(fileName)) {
- return fileName;
- }
- const parentPath = getDirectoryPath(searchPath);
- if (parentPath === searchPath) {
- break;
- }
- searchPath = parentPath;
- }
- return undefined;
+ return forEachAncestorDirectory(searchPath, ancestor => {
+ const fileName = combinePaths(ancestor, configName);
+ return fileExists(fileName) ? fileName : undefined;
+ });
}
export function resolveTripleslashReference(moduleName: string, containingFile: string): string {
@@ -1101,11 +1093,12 @@ namespace ts {
// If '--lib' is not specified, include default library file according to '--target'
// otherwise, using options specified in '--lib' instead of '--target' default library file
+ const equalityComparer = host.useCaseSensitiveFileNames() ? equateStringsCaseSensitive : equateStringsCaseInsensitive;
if (!options.lib) {
- return compareStrings(file.fileName, getDefaultLibraryFileName(), /*ignoreCase*/ !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
+ return equalityComparer(file.fileName, getDefaultLibraryFileName());
}
else {
- return forEach(options.lib, libFileName => compareStrings(file.fileName, combinePaths(defaultLibraryPath, libFileName), /*ignoreCase*/ !host.useCaseSensitiveFileNames()) === Comparison.EqualTo);
+ return forEach(options.lib, libFileName => equalityComparer(file.fileName, combinePaths(defaultLibraryPath, libFileName)));
}
}
@@ -1186,11 +1179,11 @@ namespace ts {
return emitResult;
}
- function getSourceFile(fileName: string): SourceFile {
+ function getSourceFile(fileName: string): SourceFile | undefined {
return getSourceFileByPath(toPath(fileName));
}
- function getSourceFileByPath(path: Path): SourceFile {
+ function getSourceFileByPath(path: Path): SourceFile | undefined {
return filesByName.get(path);
}
diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts
index b988da0fd5f..e21e81a2e88 100644
--- a/src/compiler/resolutionCache.ts
+++ b/src/compiler/resolutionCache.ts
@@ -69,9 +69,8 @@ namespace ts {
export const maxNumberOfFilesToIterateForInvalidation = 256;
- interface GetResolutionWithResolvedFileName {
- (resolution: T): R;
- }
+ type GetResolutionWithResolvedFileName =
+ (resolution: T) => R;
export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string): ResolutionCache {
let filesWithChangedSetOfUnresolvedImports: Path[] | undefined;
@@ -320,6 +319,10 @@ namespace ts {
return endsWith(dirPath, "/node_modules");
}
+ function isNodeModulesAtTypesDirectory(dirPath: Path) {
+ return endsWith(dirPath, "/node_modules/@types");
+ }
+
function isDirectoryAtleastAtLevelFromFSRoot(dirPath: Path, minLevels: number) {
for (let searchIndex = getRootLength(dirPath); minLevels > 0; minLevels--) {
searchIndex = dirPath.indexOf(directorySeparator, searchIndex) + 1;
@@ -560,11 +563,21 @@ namespace ts {
else {
// Some file or directory in the watching directory is created
// Return early if it does not have any of the watching extension or not the custom failed lookup path
- if (!isPathWithDefaultFailedLookupExtension(fileOrDirectoryPath) && !customFailedLookupPaths.has(fileOrDirectoryPath)) {
- return false;
+ const dirOfFileOrDirectory = getDirectoryPath(fileOrDirectoryPath);
+ if (isNodeModulesAtTypesDirectory(dirOfFileOrDirectory) || isNodeModulesDirectory(dirOfFileOrDirectory)) {
+ // Invalidate any resolution from this directory
+ isChangedFailedLookupLocation = location => {
+ const locationPath = resolutionHost.toPath(location);
+ return locationPath === fileOrDirectoryPath || startsWith(resolutionHost.toPath(location), fileOrDirectoryPath);
+ };
+ }
+ else {
+ if (!isPathWithDefaultFailedLookupExtension(fileOrDirectoryPath) && !customFailedLookupPaths.has(fileOrDirectoryPath)) {
+ return false;
+ }
+ // Resolution need to be invalidated if failed lookup location is same as the file or directory getting created
+ isChangedFailedLookupLocation = location => resolutionHost.toPath(location) === fileOrDirectoryPath;
}
- // Resolution need to be invalidated if failed lookup location is same as the file or directory getting created
- isChangedFailedLookupLocation = location => resolutionHost.toPath(location) === fileOrDirectoryPath;
}
const hasChangedFailedLookupLocation = (resolution: ResolutionWithFailedLookupLocations) => some(resolution.failedLookupLocations, isChangedFailedLookupLocation);
const invalidatedFilesCount = filesWithInvalidatedResolutions && filesWithInvalidatedResolutions.size;
diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts
index 521a1983254..9fddece11d0 100644
--- a/src/compiler/scanner.ts
+++ b/src/compiler/scanner.ts
@@ -2,9 +2,7 @@
///
namespace ts {
- export interface ErrorCallback {
- (message: DiagnosticMessage, length: number): void;
- }
+ export type ErrorCallback = (message: DiagnosticMessage, length: number) => void;
/* @internal */
export function tokenIsIdentifierOrKeyword(token: SyntaxKind): boolean {
@@ -296,7 +294,7 @@ namespace ts {
}
/* @internal */
- export function stringToToken(s: string): SyntaxKind {
+ export function stringToToken(s: string): SyntaxKind | undefined {
return textToToken.get(s);
}
@@ -357,7 +355,7 @@ namespace ts {
* We assume the first line starts at position 0 and 'position' is non-negative.
*/
export function computeLineAndCharacterOfPosition(lineStarts: ReadonlyArray, position: number): LineAndCharacter {
- let lineNumber = binarySearch(lineStarts, position);
+ let lineNumber = binarySearch(lineStarts, position, identity, compareValues);
if (lineNumber < 0) {
// If the actual position was not found,
// the binary search returns the 2's-complement of the next line start
diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts
index 1169af191b8..529ece36fa6 100644
--- a/src/compiler/sys.ts
+++ b/src/compiler/sys.ts
@@ -124,7 +124,7 @@ namespace ts {
getEnvironmentVariable?(name: string): string;
};
- export let sys: System = (function() {
+ export let sys: System = (() => {
function getNodeSystem(): System {
const _fs = require("fs");
const _path = require("path");
@@ -511,7 +511,7 @@ namespace ts {
return stat.size;
}
}
- catch (e) { }
+ catch { /*ignore*/ }
return 0;
},
exit(exitCode?: number): void {
@@ -525,7 +525,7 @@ namespace ts {
try {
require("source-map-support").install();
}
- catch (e) {
+ catch {
// Could not enable source maps.
}
},
@@ -594,7 +594,7 @@ namespace ts {
if (sys) {
// patch writefile to create folder before writing the file
const originalWriteFile = sys.writeFile;
- sys.writeFile = function(path, data, writeBom) {
+ sys.writeFile = (path, data, writeBom) => {
const directoryPath = getDirectoryPath(normalizeSlashes(path));
if (directoryPath && !sys.directoryExists(directoryPath)) {
recursiveCreateDirectory(directoryPath, sys);
diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts
index a1e52480172..989b0827570 100644
--- a/src/compiler/transformers/es2015.ts
+++ b/src/compiler/transformers/es2015.ts
@@ -787,9 +787,7 @@ 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 (getEmitFlags(node) & EmitFlags.Indented) {
- setEmitFlags(classFunction, EmitFlags.Indented);
- }
+ setEmitFlags(classFunction, (getEmitFlags(node) & EmitFlags.Indented) | EmitFlags.ReuseTempVariableScope);
// "inner" and "outer" below are added purely to preserve source map locations from
// the old emitter
@@ -1327,7 +1325,8 @@ namespace ts {
EmitFlags.SingleLine | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTokenSourceMaps
)
);
- statement.startsOnNewLine = true;
+
+ startOnNewLine(statement);
setTextRange(statement, parameter);
setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.CustomPrologue);
statements.push(statement);
@@ -1683,7 +1682,7 @@ namespace ts {
]
);
if (startsOnNewLine) {
- call.startsOnNewLine = true;
+ startOnNewLine(call);
}
exitSubtree(ancestorFacts, HierarchyFacts.PropagateNewTargetMask, hierarchyFacts & HierarchyFacts.PropagateNewTargetMask ? HierarchyFacts.NewTarget : HierarchyFacts.None);
@@ -2602,7 +2601,7 @@ namespace ts {
);
if (node.multiLine) {
- assignment.startsOnNewLine = true;
+ startOnNewLine(assignment);
}
expressions.push(assignment);
@@ -3083,7 +3082,7 @@ namespace ts {
);
setTextRange(expression, property);
if (startsOnNewLine) {
- expression.startsOnNewLine = true;
+ startOnNewLine(expression);
}
return expression;
}
@@ -3105,7 +3104,7 @@ namespace ts {
);
setTextRange(expression, property);
if (startsOnNewLine) {
- expression.startsOnNewLine = true;
+ startOnNewLine(expression);
}
return expression;
}
@@ -3128,7 +3127,7 @@ namespace ts {
);
setTextRange(expression, method);
if (startsOnNewLine) {
- expression.startsOnNewLine = true;
+ startOnNewLine(expression);
}
exitSubtree(ancestorFacts, HierarchyFacts.PropagateNewTargetMask, hierarchyFacts & HierarchyFacts.PropagateNewTargetMask ? HierarchyFacts.NewTarget : HierarchyFacts.None);
return expression;
diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts
index 7ede62b1540..bd2a4ef554d 100644
--- a/src/compiler/transformers/generators.ts
+++ b/src/compiler/transformers/generators.ts
@@ -1077,7 +1077,7 @@ namespace ts {
const visited = visitNode(expression, visitor, isExpression);
if (visited) {
if (multiLine) {
- visited.startsOnNewLine = true;
+ startOnNewLine(visited);
}
expressions.push(visited);
}
@@ -2683,8 +2683,7 @@ namespace ts {
if (clauses) {
const labelExpression = createPropertyAccess(state, "label");
const switchStatement = createSwitch(labelExpression, createCaseBlock(clauses));
- switchStatement.startsOnNewLine = true;
- return [switchStatement];
+ return [startOnNewLine(switchStatement)];
}
if (statements) {
diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts
index 2be6cdef0bc..ab44db8ef4b 100644
--- a/src/compiler/transformers/jsx.ts
+++ b/src/compiler/transformers/jsx.ts
@@ -309,258 +309,258 @@ namespace ts {
}
const entities = createMapFromTemplate({
- "quot": 0x0022,
- "amp": 0x0026,
- "apos": 0x0027,
- "lt": 0x003C,
- "gt": 0x003E,
- "nbsp": 0x00A0,
- "iexcl": 0x00A1,
- "cent": 0x00A2,
- "pound": 0x00A3,
- "curren": 0x00A4,
- "yen": 0x00A5,
- "brvbar": 0x00A6,
- "sect": 0x00A7,
- "uml": 0x00A8,
- "copy": 0x00A9,
- "ordf": 0x00AA,
- "laquo": 0x00AB,
- "not": 0x00AC,
- "shy": 0x00AD,
- "reg": 0x00AE,
- "macr": 0x00AF,
- "deg": 0x00B0,
- "plusmn": 0x00B1,
- "sup2": 0x00B2,
- "sup3": 0x00B3,
- "acute": 0x00B4,
- "micro": 0x00B5,
- "para": 0x00B6,
- "middot": 0x00B7,
- "cedil": 0x00B8,
- "sup1": 0x00B9,
- "ordm": 0x00BA,
- "raquo": 0x00BB,
- "frac14": 0x00BC,
- "frac12": 0x00BD,
- "frac34": 0x00BE,
- "iquest": 0x00BF,
- "Agrave": 0x00C0,
- "Aacute": 0x00C1,
- "Acirc": 0x00C2,
- "Atilde": 0x00C3,
- "Auml": 0x00C4,
- "Aring": 0x00C5,
- "AElig": 0x00C6,
- "Ccedil": 0x00C7,
- "Egrave": 0x00C8,
- "Eacute": 0x00C9,
- "Ecirc": 0x00CA,
- "Euml": 0x00CB,
- "Igrave": 0x00CC,
- "Iacute": 0x00CD,
- "Icirc": 0x00CE,
- "Iuml": 0x00CF,
- "ETH": 0x00D0,
- "Ntilde": 0x00D1,
- "Ograve": 0x00D2,
- "Oacute": 0x00D3,
- "Ocirc": 0x00D4,
- "Otilde": 0x00D5,
- "Ouml": 0x00D6,
- "times": 0x00D7,
- "Oslash": 0x00D8,
- "Ugrave": 0x00D9,
- "Uacute": 0x00DA,
- "Ucirc": 0x00DB,
- "Uuml": 0x00DC,
- "Yacute": 0x00DD,
- "THORN": 0x00DE,
- "szlig": 0x00DF,
- "agrave": 0x00E0,
- "aacute": 0x00E1,
- "acirc": 0x00E2,
- "atilde": 0x00E3,
- "auml": 0x00E4,
- "aring": 0x00E5,
- "aelig": 0x00E6,
- "ccedil": 0x00E7,
- "egrave": 0x00E8,
- "eacute": 0x00E9,
- "ecirc": 0x00EA,
- "euml": 0x00EB,
- "igrave": 0x00EC,
- "iacute": 0x00ED,
- "icirc": 0x00EE,
- "iuml": 0x00EF,
- "eth": 0x00F0,
- "ntilde": 0x00F1,
- "ograve": 0x00F2,
- "oacute": 0x00F3,
- "ocirc": 0x00F4,
- "otilde": 0x00F5,
- "ouml": 0x00F6,
- "divide": 0x00F7,
- "oslash": 0x00F8,
- "ugrave": 0x00F9,
- "uacute": 0x00FA,
- "ucirc": 0x00FB,
- "uuml": 0x00FC,
- "yacute": 0x00FD,
- "thorn": 0x00FE,
- "yuml": 0x00FF,
- "OElig": 0x0152,
- "oelig": 0x0153,
- "Scaron": 0x0160,
- "scaron": 0x0161,
- "Yuml": 0x0178,
- "fnof": 0x0192,
- "circ": 0x02C6,
- "tilde": 0x02DC,
- "Alpha": 0x0391,
- "Beta": 0x0392,
- "Gamma": 0x0393,
- "Delta": 0x0394,
- "Epsilon": 0x0395,
- "Zeta": 0x0396,
- "Eta": 0x0397,
- "Theta": 0x0398,
- "Iota": 0x0399,
- "Kappa": 0x039A,
- "Lambda": 0x039B,
- "Mu": 0x039C,
- "Nu": 0x039D,
- "Xi": 0x039E,
- "Omicron": 0x039F,
- "Pi": 0x03A0,
- "Rho": 0x03A1,
- "Sigma": 0x03A3,
- "Tau": 0x03A4,
- "Upsilon": 0x03A5,
- "Phi": 0x03A6,
- "Chi": 0x03A7,
- "Psi": 0x03A8,
- "Omega": 0x03A9,
- "alpha": 0x03B1,
- "beta": 0x03B2,
- "gamma": 0x03B3,
- "delta": 0x03B4,
- "epsilon": 0x03B5,
- "zeta": 0x03B6,
- "eta": 0x03B7,
- "theta": 0x03B8,
- "iota": 0x03B9,
- "kappa": 0x03BA,
- "lambda": 0x03BB,
- "mu": 0x03BC,
- "nu": 0x03BD,
- "xi": 0x03BE,
- "omicron": 0x03BF,
- "pi": 0x03C0,
- "rho": 0x03C1,
- "sigmaf": 0x03C2,
- "sigma": 0x03C3,
- "tau": 0x03C4,
- "upsilon": 0x03C5,
- "phi": 0x03C6,
- "chi": 0x03C7,
- "psi": 0x03C8,
- "omega": 0x03C9,
- "thetasym": 0x03D1,
- "upsih": 0x03D2,
- "piv": 0x03D6,
- "ensp": 0x2002,
- "emsp": 0x2003,
- "thinsp": 0x2009,
- "zwnj": 0x200C,
- "zwj": 0x200D,
- "lrm": 0x200E,
- "rlm": 0x200F,
- "ndash": 0x2013,
- "mdash": 0x2014,
- "lsquo": 0x2018,
- "rsquo": 0x2019,
- "sbquo": 0x201A,
- "ldquo": 0x201C,
- "rdquo": 0x201D,
- "bdquo": 0x201E,
- "dagger": 0x2020,
- "Dagger": 0x2021,
- "bull": 0x2022,
- "hellip": 0x2026,
- "permil": 0x2030,
- "prime": 0x2032,
- "Prime": 0x2033,
- "lsaquo": 0x2039,
- "rsaquo": 0x203A,
- "oline": 0x203E,
- "frasl": 0x2044,
- "euro": 0x20AC,
- "image": 0x2111,
- "weierp": 0x2118,
- "real": 0x211C,
- "trade": 0x2122,
- "alefsym": 0x2135,
- "larr": 0x2190,
- "uarr": 0x2191,
- "rarr": 0x2192,
- "darr": 0x2193,
- "harr": 0x2194,
- "crarr": 0x21B5,
- "lArr": 0x21D0,
- "uArr": 0x21D1,
- "rArr": 0x21D2,
- "dArr": 0x21D3,
- "hArr": 0x21D4,
- "forall": 0x2200,
- "part": 0x2202,
- "exist": 0x2203,
- "empty": 0x2205,
- "nabla": 0x2207,
- "isin": 0x2208,
- "notin": 0x2209,
- "ni": 0x220B,
- "prod": 0x220F,
- "sum": 0x2211,
- "minus": 0x2212,
- "lowast": 0x2217,
- "radic": 0x221A,
- "prop": 0x221D,
- "infin": 0x221E,
- "ang": 0x2220,
- "and": 0x2227,
- "or": 0x2228,
- "cap": 0x2229,
- "cup": 0x222A,
- "int": 0x222B,
- "there4": 0x2234,
- "sim": 0x223C,
- "cong": 0x2245,
- "asymp": 0x2248,
- "ne": 0x2260,
- "equiv": 0x2261,
- "le": 0x2264,
- "ge": 0x2265,
- "sub": 0x2282,
- "sup": 0x2283,
- "nsub": 0x2284,
- "sube": 0x2286,
- "supe": 0x2287,
- "oplus": 0x2295,
- "otimes": 0x2297,
- "perp": 0x22A5,
- "sdot": 0x22C5,
- "lceil": 0x2308,
- "rceil": 0x2309,
- "lfloor": 0x230A,
- "rfloor": 0x230B,
- "lang": 0x2329,
- "rang": 0x232A,
- "loz": 0x25CA,
- "spades": 0x2660,
- "clubs": 0x2663,
- "hearts": 0x2665,
- "diams": 0x2666
+ quot: 0x0022,
+ amp: 0x0026,
+ apos: 0x0027,
+ lt: 0x003C,
+ gt: 0x003E,
+ nbsp: 0x00A0,
+ iexcl: 0x00A1,
+ cent: 0x00A2,
+ pound: 0x00A3,
+ curren: 0x00A4,
+ yen: 0x00A5,
+ brvbar: 0x00A6,
+ sect: 0x00A7,
+ uml: 0x00A8,
+ copy: 0x00A9,
+ ordf: 0x00AA,
+ laquo: 0x00AB,
+ not: 0x00AC,
+ shy: 0x00AD,
+ reg: 0x00AE,
+ macr: 0x00AF,
+ deg: 0x00B0,
+ plusmn: 0x00B1,
+ sup2: 0x00B2,
+ sup3: 0x00B3,
+ acute: 0x00B4,
+ micro: 0x00B5,
+ para: 0x00B6,
+ middot: 0x00B7,
+ cedil: 0x00B8,
+ sup1: 0x00B9,
+ ordm: 0x00BA,
+ raquo: 0x00BB,
+ frac14: 0x00BC,
+ frac12: 0x00BD,
+ frac34: 0x00BE,
+ iquest: 0x00BF,
+ Agrave: 0x00C0,
+ Aacute: 0x00C1,
+ Acirc: 0x00C2,
+ Atilde: 0x00C3,
+ Auml: 0x00C4,
+ Aring: 0x00C5,
+ AElig: 0x00C6,
+ Ccedil: 0x00C7,
+ Egrave: 0x00C8,
+ Eacute: 0x00C9,
+ Ecirc: 0x00CA,
+ Euml: 0x00CB,
+ Igrave: 0x00CC,
+ Iacute: 0x00CD,
+ Icirc: 0x00CE,
+ Iuml: 0x00CF,
+ ETH: 0x00D0,
+ Ntilde: 0x00D1,
+ Ograve: 0x00D2,
+ Oacute: 0x00D3,
+ Ocirc: 0x00D4,
+ Otilde: 0x00D5,
+ Ouml: 0x00D6,
+ times: 0x00D7,
+ Oslash: 0x00D8,
+ Ugrave: 0x00D9,
+ Uacute: 0x00DA,
+ Ucirc: 0x00DB,
+ Uuml: 0x00DC,
+ Yacute: 0x00DD,
+ THORN: 0x00DE,
+ szlig: 0x00DF,
+ agrave: 0x00E0,
+ aacute: 0x00E1,
+ acirc: 0x00E2,
+ atilde: 0x00E3,
+ auml: 0x00E4,
+ aring: 0x00E5,
+ aelig: 0x00E6,
+ ccedil: 0x00E7,
+ egrave: 0x00E8,
+ eacute: 0x00E9,
+ ecirc: 0x00EA,
+ euml: 0x00EB,
+ igrave: 0x00EC,
+ iacute: 0x00ED,
+ icirc: 0x00EE,
+ iuml: 0x00EF,
+ eth: 0x00F0,
+ ntilde: 0x00F1,
+ ograve: 0x00F2,
+ oacute: 0x00F3,
+ ocirc: 0x00F4,
+ otilde: 0x00F5,
+ ouml: 0x00F6,
+ divide: 0x00F7,
+ oslash: 0x00F8,
+ ugrave: 0x00F9,
+ uacute: 0x00FA,
+ ucirc: 0x00FB,
+ uuml: 0x00FC,
+ yacute: 0x00FD,
+ thorn: 0x00FE,
+ yuml: 0x00FF,
+ OElig: 0x0152,
+ oelig: 0x0153,
+ Scaron: 0x0160,
+ scaron: 0x0161,
+ Yuml: 0x0178,
+ fnof: 0x0192,
+ circ: 0x02C6,
+ tilde: 0x02DC,
+ Alpha: 0x0391,
+ Beta: 0x0392,
+ Gamma: 0x0393,
+ Delta: 0x0394,
+ Epsilon: 0x0395,
+ Zeta: 0x0396,
+ Eta: 0x0397,
+ Theta: 0x0398,
+ Iota: 0x0399,
+ Kappa: 0x039A,
+ Lambda: 0x039B,
+ Mu: 0x039C,
+ Nu: 0x039D,
+ Xi: 0x039E,
+ Omicron: 0x039F,
+ Pi: 0x03A0,
+ Rho: 0x03A1,
+ Sigma: 0x03A3,
+ Tau: 0x03A4,
+ Upsilon: 0x03A5,
+ Phi: 0x03A6,
+ Chi: 0x03A7,
+ Psi: 0x03A8,
+ Omega: 0x03A9,
+ alpha: 0x03B1,
+ beta: 0x03B2,
+ gamma: 0x03B3,
+ delta: 0x03B4,
+ epsilon: 0x03B5,
+ zeta: 0x03B6,
+ eta: 0x03B7,
+ theta: 0x03B8,
+ iota: 0x03B9,
+ kappa: 0x03BA,
+ lambda: 0x03BB,
+ mu: 0x03BC,
+ nu: 0x03BD,
+ xi: 0x03BE,
+ omicron: 0x03BF,
+ pi: 0x03C0,
+ rho: 0x03C1,
+ sigmaf: 0x03C2,
+ sigma: 0x03C3,
+ tau: 0x03C4,
+ upsilon: 0x03C5,
+ phi: 0x03C6,
+ chi: 0x03C7,
+ psi: 0x03C8,
+ omega: 0x03C9,
+ thetasym: 0x03D1,
+ upsih: 0x03D2,
+ piv: 0x03D6,
+ ensp: 0x2002,
+ emsp: 0x2003,
+ thinsp: 0x2009,
+ zwnj: 0x200C,
+ zwj: 0x200D,
+ lrm: 0x200E,
+ rlm: 0x200F,
+ ndash: 0x2013,
+ mdash: 0x2014,
+ lsquo: 0x2018,
+ rsquo: 0x2019,
+ sbquo: 0x201A,
+ ldquo: 0x201C,
+ rdquo: 0x201D,
+ bdquo: 0x201E,
+ dagger: 0x2020,
+ Dagger: 0x2021,
+ bull: 0x2022,
+ hellip: 0x2026,
+ permil: 0x2030,
+ prime: 0x2032,
+ Prime: 0x2033,
+ lsaquo: 0x2039,
+ rsaquo: 0x203A,
+ oline: 0x203E,
+ frasl: 0x2044,
+ euro: 0x20AC,
+ image: 0x2111,
+ weierp: 0x2118,
+ real: 0x211C,
+ trade: 0x2122,
+ alefsym: 0x2135,
+ larr: 0x2190,
+ uarr: 0x2191,
+ rarr: 0x2192,
+ darr: 0x2193,
+ harr: 0x2194,
+ crarr: 0x21B5,
+ lArr: 0x21D0,
+ uArr: 0x21D1,
+ rArr: 0x21D2,
+ dArr: 0x21D3,
+ hArr: 0x21D4,
+ forall: 0x2200,
+ part: 0x2202,
+ exist: 0x2203,
+ empty: 0x2205,
+ nabla: 0x2207,
+ isin: 0x2208,
+ notin: 0x2209,
+ ni: 0x220B,
+ prod: 0x220F,
+ sum: 0x2211,
+ minus: 0x2212,
+ lowast: 0x2217,
+ radic: 0x221A,
+ prop: 0x221D,
+ infin: 0x221E,
+ ang: 0x2220,
+ and: 0x2227,
+ or: 0x2228,
+ cap: 0x2229,
+ cup: 0x222A,
+ int: 0x222B,
+ there4: 0x2234,
+ sim: 0x223C,
+ cong: 0x2245,
+ asymp: 0x2248,
+ ne: 0x2260,
+ equiv: 0x2261,
+ le: 0x2264,
+ ge: 0x2265,
+ sub: 0x2282,
+ sup: 0x2283,
+ nsub: 0x2284,
+ sube: 0x2286,
+ supe: 0x2287,
+ oplus: 0x2295,
+ otimes: 0x2297,
+ perp: 0x22A5,
+ sdot: 0x22C5,
+ lceil: 0x2308,
+ rceil: 0x2309,
+ lfloor: 0x230A,
+ rfloor: 0x230B,
+ lang: 0x2329,
+ rang: 0x232A,
+ loz: 0x25CA,
+ spades: 0x2660,
+ clubs: 0x2663,
+ hearts: 0x2665,
+ diams: 0x2666
});
}
\ No newline at end of file
diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts
index f60e50ea365..0c8e2cda0f5 100644
--- a/src/compiler/transformers/module/module.ts
+++ b/src/compiler/transformers/module/module.ts
@@ -57,7 +57,7 @@ namespace ts {
* @param node The SourceFile node.
*/
function transformSourceFile(node: SourceFile) {
- if (node.isDeclarationFile || !(isExternalModule(node) || compilerOptions.isolatedModules || node.transformFlags & TransformFlags.ContainsDynamicImport)) {
+ if (node.isDeclarationFile || !(isEffectiveExternalModule(node, compilerOptions) || node.transformFlags & TransformFlags.ContainsDynamicImport)) {
return node;
}
diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts
index a481f0c1d40..e5d638cf04d 100644
--- a/src/compiler/transformers/module/system.ts
+++ b/src/compiler/transformers/module/system.ts
@@ -146,8 +146,7 @@ namespace ts {
function collectDependencyGroups(externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]) {
const groupIndices = createMap();
const dependencyGroups: DependencyGroup[] = [];
- for (let i = 0; i < externalImports.length; i++) {
- const externalImport = externalImports[i];
+ for (const externalImport of externalImports) {
const externalModuleName = getExternalModuleNameLiteral(externalImport, currentSourceFile, host, resolver, compilerOptions);
if (externalModuleName) {
const text = externalModuleName.text;
diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts
index ffaecd77d71..39a4cc83bda 100644
--- a/src/compiler/transformers/ts.ts
+++ b/src/compiler/transformers/ts.ts
@@ -86,6 +86,12 @@ namespace ts {
*/
let applicableSubstitutions: TypeScriptSubstitutionFlags;
+ /**
+ * Tracks what computed name expressions originating from elided names must be inlined
+ * at the next execution site, in document order
+ */
+ let pendingExpressions: Expression[] | undefined;
+
return transformSourceFile;
/**
@@ -225,6 +231,13 @@ namespace ts {
if (parsed !== node) {
// If the node has been transformed by a `before` transformer, perform no ellision on it
// As the type information we would attempt to lookup to perform ellision is potentially unavailable for the synthesized nodes
+ // We do not reuse `visitorWorker`, as the ellidable statement syntax kinds are technically unrecognized by the switch-case in `visitTypeScript`,
+ // and will trigger debug failures when debug verbosity is turned up
+ if (node.transformFlags & TransformFlags.ContainsTypeScript) {
+ // This node contains TypeScript, so we should visit its children.
+ return visitEachChild(node, visitor, context);
+ }
+ // Otherwise, we can just return the node
return node;
}
switch (node.kind) {
@@ -388,9 +401,11 @@ namespace ts {
case SyntaxKind.TypeAliasDeclaration:
// TypeScript type-only declarations are elided.
+ return undefined;
case SyntaxKind.PropertyDeclaration:
- // TypeScript property declarations are elided.
+ // TypeScript property declarations are elided. However their names are still visited, and can potentially be retained if they could have sideeffects
+ return visitPropertyDeclaration(node as PropertyDeclaration);
case SyntaxKind.NamespaceExportDeclaration:
// TypeScript namespace export declarations are elided.
@@ -577,6 +592,9 @@ namespace ts {
* @param node The node to transform.
*/
function visitClassDeclaration(node: ClassDeclaration): VisitResult {
+ const savedPendingExpressions = pendingExpressions;
+ pendingExpressions = undefined;
+
const staticProperties = getInitializedProperties(node, /*isStatic*/ true);
const facts = getClassFacts(node, staticProperties);
@@ -591,6 +609,12 @@ namespace ts {
let statements: Statement[] = [classStatement];
+ // Write any pending expressions from elided or moved computed property names
+ if (some(pendingExpressions)) {
+ statements.push(createStatement(inlineExpressions(pendingExpressions)));
+ }
+ pendingExpressions = savedPendingExpressions;
+
// Emit static property assignment. Because classDeclaration is lexically evaluated,
// it is safe to emit static property assignment after classDeclaration
// From ES6 specification:
@@ -849,6 +873,9 @@ namespace ts {
* @param node The node to transform.
*/
function visitClassExpression(node: ClassExpression): Expression {
+ const savedPendingExpressions = pendingExpressions;
+ pendingExpressions = undefined;
+
const staticProperties = getInitializedProperties(node, /*isStatic*/ true);
const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause);
const members = transformClassMembers(node, some(heritageClauses, c => c.token === SyntaxKind.ExtendsKeyword));
@@ -864,7 +891,7 @@ namespace ts {
setOriginalNode(classExpression, node);
setTextRange(classExpression, node);
- if (staticProperties.length > 0) {
+ if (some(staticProperties) || some(pendingExpressions)) {
const expressions: Expression[] = [];
const temp = createTempVariable(hoistVariableDeclaration);
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) {
@@ -877,11 +904,15 @@ namespace ts {
// the body of a class with static initializers.
setEmitFlags(classExpression, EmitFlags.Indented | getEmitFlags(classExpression));
expressions.push(startOnNewLine(createAssignment(temp, classExpression)));
+ // Add any pending expressions leftover from elided or relocated computed property names
+ addRange(expressions, map(pendingExpressions, startOnNewLine));
+ pendingExpressions = savedPendingExpressions;
addRange(expressions, generateInitializedPropertyExpressions(staticProperties, temp));
expressions.push(startOnNewLine(temp));
return inlineExpressions(expressions);
}
+ pendingExpressions = savedPendingExpressions;
return classExpression;
}
@@ -1195,7 +1226,7 @@ namespace ts {
const expressions: Expression[] = [];
for (const property of properties) {
const expression = transformInitializedProperty(property, receiver);
- expression.startsOnNewLine = true;
+ startOnNewLine(expression);
setSourceMapRange(expression, moveRangePastModifiers(property));
setCommentRange(expression, property);
expressions.push(expression);
@@ -1211,7 +1242,10 @@ namespace ts {
* @param receiver The object receiving the property assignment.
*/
function transformInitializedProperty(property: PropertyDeclaration, receiver: LeftHandSideExpression) {
- const propertyName = visitPropertyNameOfClassElement(property);
+ // We generate a name here in order to reuse the value cached by the relocated computed name expression (which uses the same generated name)
+ const propertyName = isComputedPropertyName(property.name) && !isSimpleInlineableExpression(property.name.expression)
+ ? updateComputedPropertyName(property.name, getGeneratedNameForNode(property.name, !hasModifier(property, ModifierFlags.Static)))
+ : property.name;
const initializer = visitNode(property.initializer, visitor, isExpression);
const memberAccess = createMemberAccessForPropertyName(receiver, propertyName, /*location*/ propertyName);
@@ -2034,6 +2068,16 @@ namespace ts {
);
}
+ /**
+ * A simple inlinable expression is an expression which can be copied into multiple locations
+ * without risk of repeating any sideeffects and whose value could not possibly change between
+ * any such locations
+ */
+ function isSimpleInlineableExpression(expression: Expression) {
+ return !isIdentifier(expression) && isSimpleCopiableExpression(expression) ||
+ isWellKnownSymbolSyntactically(expression);
+ }
+
/**
* Gets an expression that represents a property name. For a computed property, a
* name is generated for the node.
@@ -2043,7 +2087,7 @@ namespace ts {
function getExpressionForPropertyName(member: ClassElement | EnumMember, generateNameForComputedPropertyName: boolean): Expression {
const name = member.name;
if (isComputedPropertyName(name)) {
- return generateNameForComputedPropertyName
+ return generateNameForComputedPropertyName && !isSimpleInlineableExpression((name).expression)
? getGeneratedNameForNode(name)
: (name).expression;
}
@@ -2055,6 +2099,26 @@ namespace ts {
}
}
+ /**
+ * If the name is a computed property, this function transforms it, then either returns an expression which caches the
+ * value of the result or the expression itself if the value is either unused or safe to inline into multiple locations
+ * @param shouldHoist Does the expression need to be reused? (ie, for an initializer or a decorator)
+ * @param omitSimple Should expressions with no observable side-effects be elided? (ie, the expression is not hoisted for a decorator or initializer and is a literal)
+ */
+ function getPropertyNameExpressionIfNeeded(name: PropertyName, shouldHoist: boolean, omitSimple: boolean): Expression {
+ if (isComputedPropertyName(name)) {
+ const expression = visitNode(name.expression, visitor, isExpression);
+ const innerExpression = skipPartiallyEmittedExpressions(expression);
+ const inlinable = isSimpleInlineableExpression(innerExpression);
+ if (!inlinable && shouldHoist) {
+ const generatedName = getGeneratedNameForNode(name);
+ hoistVariableDeclaration(generatedName);
+ return createAssignment(generatedName, expression);
+ }
+ return (omitSimple && (inlinable || isIdentifier(innerExpression))) ? undefined : expression;
+ }
+ }
+
/**
* Visits the property name of a class element, for use when emitting property
* initializers. For a computed property on a node with decorators, a temporary
@@ -2064,15 +2128,14 @@ namespace ts {
*/
function visitPropertyNameOfClassElement(member: ClassElement): PropertyName {
const name = member.name;
- if (isComputedPropertyName(name)) {
- let expression = visitNode(name.expression, visitor, isExpression);
- if (member.decorators) {
- const generatedName = getGeneratedNameForNode(name);
- hoistVariableDeclaration(generatedName);
- expression = createAssignment(generatedName, expression);
+ let expr = getPropertyNameExpressionIfNeeded(name, some(member.decorators), /*omitSimple*/ false);
+ if (expr) { // expr only exists if `name` is a computed property name
+ // Inline any pending expressions from previous elided or relocated computed property name expressions in order to preserve execution order
+ if (some(pendingExpressions)) {
+ expr = inlineExpressions([...pendingExpressions, expr]);
+ pendingExpressions.length = 0;
}
-
- return updateComputedPropertyName(name, expression);
+ return updateComputedPropertyName(name as ComputedPropertyName, expr);
}
else {
return name;
@@ -2129,6 +2192,14 @@ namespace ts {
return !nodeIsMissing(node.body);
}
+ function visitPropertyDeclaration(node: PropertyDeclaration): undefined {
+ const expr = getPropertyNameExpressionIfNeeded(node.name, some(node.decorators) || !!node.initializer, /*omitSimple*/ true);
+ if (expr && !isSimpleInlineableExpression(expr)) {
+ (pendingExpressions || (pendingExpressions = [])).push(expr);
+ }
+ return undefined;
+ }
+
function visitConstructor(node: ConstructorDeclaration) {
if (!shouldEmitFunctionLikeDeclaration(node)) {
return undefined;
@@ -2149,7 +2220,7 @@ namespace ts {
* This function will be called when one of the following conditions are met:
* - The node is an overload
* - The node is marked as abstract, public, private, protected, or readonly
- * - The node has both a decorator and a computed property name
+ * - The node has a computed property name
*
* @param node The method node.
*/
@@ -2193,7 +2264,7 @@ namespace ts {
*
* This function will be called when one of the following conditions are met:
* - The node is marked as abstract, public, private, or protected
- * - The node has both a decorator and a computed property name
+ * - The node has a computed property name
*
* @param node The get accessor node.
*/
@@ -2224,7 +2295,7 @@ namespace ts {
*
* This function will be called when one of the following conditions are met:
* - The node is marked as abstract, public, private, or protected
- * - The node has both a decorator and a computed property name
+ * - The node has a computed property name
*
* @param node The set accessor node.
*/
diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts
index 827dfa152c7..01fb45e4f7d 100644
--- a/src/compiler/tsc.ts
+++ b/src/compiler/tsc.ts
@@ -294,7 +294,7 @@ namespace ts {
// Sort our options by their names, (e.g. "--noImplicitAny" comes before "--watch")
const optsList = showAllOptions ?
- optionDeclarations.slice().sort((a, b) => compareValues(a.name.toLowerCase(), b.name.toLowerCase())) :
+ sort(optionDeclarations, (a, b) => compareStringsCaseInsensitive(a.name, b.name)) :
filter(optionDeclarations.slice(), v => v.showInSimplifiedHelpView);
// We want our descriptions to align at the same column in our output,
@@ -305,9 +305,7 @@ namespace ts {
const optionsDescriptionMap = createMap(); // Map between option.description and list of option.type if it is a kind
- for (let i = 0; i < optsList.length; i++) {
- const option = optsList[i];
-
+ for (const option of optsList) {
// If an option lacks a description,
// it is not officially supported.
if (!option.description) {
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 549c0f8d52f..3141e3f22d9 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -36,6 +36,19 @@ namespace ts {
push(...values: T[]): void;
}
+ /* @internal */
+ export type EqualityComparer = (a: T, b: T) => boolean;
+
+ /* @internal */
+ export type Comparer = (a: T, b: T) => Comparison;
+
+ /* @internal */
+ export const enum Comparison {
+ LessThan = -1,
+ EqualTo = 0,
+ GreaterThan = 1
+ }
+
// branded string type used to store absolute, normalized and canonicalized paths
// arbitrary file name can be converted to Path via toPath function
export type Path = string & { __pathBrand: any };
@@ -201,7 +214,7 @@ namespace ts {
UndefinedKeyword,
FromKeyword,
GlobalKeyword,
- OfKeyword, // LastKeyword and LastToken
+ OfKeyword, // LastKeyword and LastToken and LastContextualKeyword
// Parse tree nodes
@@ -418,7 +431,9 @@ namespace ts {
FirstJSDocNode = JSDocTypeExpression,
LastJSDocNode = JSDocPropertyTag,
FirstJSDocTagNode = JSDocTag,
- LastJSDocTagNode = JSDocPropertyTag
+ LastJSDocTagNode = JSDocPropertyTag,
+ /* @internal */ FirstContextualKeyword = AbstractKeyword,
+ /* @internal */ LastContextualKeyword = OfKeyword,
}
export const enum NodeFlags {
@@ -454,7 +469,8 @@ namespace ts {
/* @internal */
PossiblyContainsDynamicImport = 1 << 19,
JSDoc = 1 << 20, // If node was parsed inside jsdoc
- /* @internal */ InWithStatement = 1 << 21, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`)
+ /* @internal */ Ambient = 1 << 21, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier.
+ /* @internal */ InWithStatement = 1 << 22, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`)
BlockScoped = Let | Const,
@@ -462,7 +478,7 @@ namespace ts {
ReachabilityAndEmitFlags = ReachabilityCheckFlags | HasAsyncFunctions,
// Parsing context flags
- ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile | InWithStatement,
+ ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile | InWithStatement | Ambient,
// Exclude these flags when parsing a Type
TypeExcludesFlags = YieldContext | AwaitContext,
@@ -519,7 +535,6 @@ namespace ts {
/* @internal */ id?: number; // Unique id (used to look up NodeLinks)
parent?: Node; // Parent node (initialized by binding)
/* @internal */ original?: Node; // The original node if this is an updated node.
- /* @internal */ startsOnNewLine?: boolean; // Whether a synthesized node should start on a new line (used by transforms).
/* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding)
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
@@ -629,6 +644,7 @@ namespace ts {
isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace
/*@internal*/ typeArguments?: NodeArray; // Only defined on synthesized nodes. Though not syntactically valid, used in emitting diagnostics.
/*@internal*/ jsdocDotPos?: number; // Identifier occurs in JSDoc-style generic: Id.
+ /*@internal*/ skipNameGenerationScope?: boolean; // Should skip a name generation scope when generating the name for this identifier
}
// Transient identifier node (marked by id === -1)
@@ -2452,8 +2468,8 @@ namespace ts {
export interface ScriptReferenceHost {
getCompilerOptions(): CompilerOptions;
- getSourceFile(fileName: string): SourceFile;
- getSourceFileByPath(path: Path): SourceFile;
+ getSourceFile(fileName: string): SourceFile | undefined;
+ getSourceFileByPath(path: Path): SourceFile | undefined;
getCurrentDirectory(): string;
}
@@ -2471,9 +2487,13 @@ namespace ts {
readFile(path: string): string | undefined;
}
- export interface WriteFileCallback {
- (fileName: string, data: string, writeByteOrderMark: boolean, onError: ((message: string) => void) | undefined, sourceFiles: ReadonlyArray): void;
- }
+ export type WriteFileCallback = (
+ fileName: string,
+ data: string,
+ writeByteOrderMark: boolean,
+ onError: ((message: string) => void) | undefined,
+ sourceFiles: ReadonlyArray,
+ ) => void;
export class OperationCanceledException { }
@@ -3003,6 +3023,10 @@ namespace ts {
Optional = 1 << 24, // Optional property
Transient = 1 << 25, // Transient symbol (created during type check)
+ /* @internal */
+ All = FunctionScopedVariable | BlockScopedVariable | Property | EnumMember | Function | Class | Interface | ConstEnum | RegularEnum | ValueModule | NamespaceModule | TypeLiteral
+ | ObjectLiteral | Method | Constructor | GetAccessor | SetAccessor | Signature | TypeParameter | TypeAlias | ExportValue | Alias | Prototype | ExportStar | Optional | Transient,
+
Enum = RegularEnum | ConstEnum,
Variable = FunctionScopedVariable | BlockScopedVariable,
Value = Variable | Property | EnumMember | Function | Class | Enum | ValueModule | Method | GetAccessor | SetAccessor,
@@ -3582,15 +3606,14 @@ namespace ts {
}
/* @internal */
- export interface TypeMapper {
- (t: TypeParameter): Type;
- }
+ export type TypeMapper = (t: TypeParameter) => Type;
export const enum InferencePriority {
Contravariant = 1 << 0, // Inference from contravariant position
NakedTypeVariable = 1 << 1, // Naked type variable in union or intersection type
MappedType = 1 << 2, // Reverse inference for mapped type
ReturnType = 1 << 3, // Inference made from return type of generic function
+ NeverType = 1 << 4, // Inference made from the never type
}
export interface InferenceInfo {
@@ -4192,9 +4215,7 @@ namespace ts {
}
/* @internal */
- export interface HasInvalidatedResolution {
- (sourceFile: Path): boolean;
- }
+ export type HasInvalidatedResolution = (sourceFile: Path) => boolean;
export interface CompilerHost extends ModuleResolutionHost {
getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined;
@@ -4329,6 +4350,7 @@ namespace ts {
constantValue?: string | number; // The constant value of an expression
externalHelpersModuleName?: Identifier; // The local name for an imported helpers module
helpers?: EmitHelper[]; // Emit helpers for the node
+ startsOnNewLine?: boolean; // If the node should begin on a new line
}
export const enum EmitFlags {
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 080172b8106..e67635cba04 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -321,16 +321,16 @@ namespace ts {
return getSourceTextOfNodeFromSourceFile(getSourceFileOfNode(node), node, includeTrivia);
}
+ function getPos(range: Node) {
+ return range.pos;
+ }
+
/**
* Note: it is expected that the `nodeArray` and the `node` are within the same file.
* For example, searching for a `SourceFile` in a `SourceFile[]` wouldn't work.
*/
export function indexOfNode(nodeArray: ReadonlyArray, node: Node) {
- return binarySearch(nodeArray, node, compareNodePos);
- }
-
- function compareNodePos({ pos: aPos }: Node, { pos: bPos}: Node) {
- return aPos < bPos ? Comparison.LessThan : bPos < aPos ? Comparison.GreaterThan : Comparison.EqualTo;
+ return binarySearch(nodeArray, node, getPos, compareValues);
}
/**
@@ -363,8 +363,10 @@ namespace ts {
case SyntaxKind.NoSubstitutionTemplateLiteral:
return "`" + escapeText(node.text, CharacterCodes.backtick) + "`";
case SyntaxKind.TemplateHead:
+ // tslint:disable-next-line no-invalid-template-strings
return "`" + escapeText(node.text, CharacterCodes.backtick) + "${";
case SyntaxKind.TemplateMiddle:
+ // tslint:disable-next-line no-invalid-template-strings
return "}" + escapeText(node.text, CharacterCodes.backtick) + "${";
case SyntaxKind.TemplateTail:
return "}" + escapeText(node.text, CharacterCodes.backtick) + "`";
@@ -459,7 +461,7 @@ namespace ts {
}
export function isEffectiveExternalModule(node: SourceFile, compilerOptions: CompilerOptions) {
- return isExternalModule(node) || compilerOptions.isolatedModules;
+ return isExternalModule(node) || compilerOptions.isolatedModules || ((getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS) && !!node.commonJsModuleIndicator);
}
/* @internal */
@@ -1404,8 +1406,8 @@ namespace ts {
return charCode === CharacterCodes.singleQuote || charCode === CharacterCodes.doubleQuote;
}
- export function isStringDoubleQuoted(string: StringLiteral, sourceFile: SourceFile): boolean {
- return getSourceTextOfNodeFromSourceFile(sourceFile, string).charCodeAt(0) === CharacterCodes.doubleQuote;
+ export function isStringDoubleQuoted(str: StringLiteral, sourceFile: SourceFile): boolean {
+ return getSourceTextOfNodeFromSourceFile(sourceFile, str).charCodeAt(0) === CharacterCodes.doubleQuote;
}
/**
@@ -1728,16 +1730,6 @@ namespace ts {
return false;
}
- export function isInAmbientContext(node: Node): boolean {
- while (node) {
- if (hasModifier(node, ModifierFlags.Ambient) || (node.kind === SyntaxKind.SourceFile && (node as SourceFile).isDeclarationFile)) {
- return true;
- }
- node = node.parent;
- }
- return false;
- }
-
// True if the given identifier, string literal, or number literal is the name of a declaration node
export function isDeclarationName(name: Node): boolean {
switch (name.kind) {
@@ -1913,6 +1905,14 @@ namespace ts {
return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword;
}
+ export function isContextualKeyword(token: SyntaxKind): boolean {
+ return SyntaxKind.FirstContextualKeyword <= token && token <= SyntaxKind.LastContextualKeyword;
+ }
+
+ export function isNonContextualKeyword(token: SyntaxKind): boolean {
+ return isKeyword(token) && !isContextualKeyword(token);
+ }
+
export function isTrivia(token: SyntaxKind) {
return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken;
}
@@ -2302,6 +2302,7 @@ namespace ts {
let nonFileDiagnostics: Diagnostic[] = [];
const fileDiagnostics = createMap();
+ let hasReadNonFileDiagnostics = false;
let diagnosticsModified = false;
let modificationCount = 0;
@@ -2331,6 +2332,12 @@ namespace ts {
}
}
else {
+ // If we've already read the non-file diagnostics, do not modify the existing array.
+ if (hasReadNonFileDiagnostics) {
+ hasReadNonFileDiagnostics = false;
+ nonFileDiagnostics = nonFileDiagnostics.slice();
+ }
+
diagnostics = nonFileDiagnostics;
}
@@ -2341,6 +2348,7 @@ namespace ts {
function getGlobalDiagnostics(): Diagnostic[] {
sortAndDeduplicate();
+ hasReadNonFileDiagnostics = true;
return nonFileDiagnostics;
}
@@ -3602,7 +3610,7 @@ namespace ts {
}
/** Calls `callback` on `directory` and every ancestor directory it has, returning the first defined result. */
- export function forEachAncestorDirectory(directory: string, callback: (directory: string) => T): T {
+ export function forEachAncestorDirectory(directory: string, callback: (directory: string) => T | undefined): T | undefined {
while (true) {
const result = callback(directory);
if (result !== undefined) {
@@ -3960,6 +3968,9 @@ namespace ts {
trySetLanguageAndTerritory(language, /*territory*/ undefined, errors);
}
+ // Set the UI locale for string collation
+ setUILocale(locale);
+
function trySetLanguageAndTerritory(language: string, territory: string, errors?: Push): boolean {
const compilerFilePath = normalizePath(sys.getExecutingFilePath());
const containingDirectoryPath = getDirectoryPath(compilerFilePath);
diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts
index 0428d2d2d2e..0b40e20a7b7 100644
--- a/src/compiler/visitor.ts
+++ b/src/compiler/visitor.ts
@@ -1585,13 +1585,13 @@ namespace ts {
// Add additional properties in debug mode to assist with debugging.
Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, {
- "__debugFlags": { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
+ __debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
});
Object.defineProperties(objectAllocator.getTypeConstructor().prototype, {
- "__debugFlags": { get(this: Type) { return formatTypeFlags(this.flags); } },
- "__debugObjectFlags": { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((this).objectFlags) : ""; } },
- "__debugTypeToString": { value(this: Type) { return this.checker.typeToString(this); } },
+ __debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } },
+ __debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((this).objectFlags) : ""; } },
+ __debugTypeToString: { value(this: Type) { return this.checker.typeToString(this); } },
});
const nodeConstructors = [
@@ -1604,11 +1604,11 @@ namespace ts {
for (const ctor of nodeConstructors) {
if (!ctor.prototype.hasOwnProperty("__debugKind")) {
Object.defineProperties(ctor.prototype, {
- "__debugKind": { get(this: Node) { return formatSyntaxKind(this.kind); } },
- "__debugModifierFlags": { get(this: Node) { return formatModifierFlags(getModifierFlagsNoCache(this)); } },
- "__debugTransformFlags": { get(this: Node) { return formatTransformFlags(this.transformFlags); } },
- "__debugEmitFlags": { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } },
- "__debugGetText": {
+ __debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } },
+ __debugModifierFlags: { get(this: Node) { return formatModifierFlags(getModifierFlagsNoCache(this)); } },
+ __debugTransformFlags: { get(this: Node) { return formatTransformFlags(this.transformFlags); } },
+ __debugEmitFlags: { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } },
+ __debugGetText: {
value(this: Node, includeTrivia?: boolean) {
if (nodeIsSynthesized(this)) return "";
const parseNode = getParseTreeNode(this);
diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts
index 53692fe1e04..8e49bb71a15 100644
--- a/src/compiler/watch.ts
+++ b/src/compiler/watch.ts
@@ -230,7 +230,7 @@ namespace ts {
function createWatchMode(rootFileNames: string[], compilerOptions: CompilerOptions, watchingHost?: WatchingSystemHost, configFileName?: string, configFileSpecs?: ConfigFileSpecs, configFileWildCardDirectories?: MapLike, optionsToExtendForConfigFile?: CompilerOptions) {
let program: Program;
- let needsReload: boolean; // true if the config file changed and needs to reload it from the disk
+ let reloadLevel: ConfigFileProgramReloadLevel; // level to indicate if the program needs to be reloaded from config file/just filenames etc
let missingFilesMap: Map; // Map of file watchers for the missing files
let watchedWildcardDirectories: Map; // map of watchers for the wild card directories in the config file
let timerToUpdateProgram: any; // timer callback to recompile the program
@@ -488,7 +488,7 @@ namespace ts {
function scheduleProgramReload() {
Debug.assert(!!configFileName);
- needsReload = true;
+ reloadLevel = ConfigFileProgramReloadLevel.Full;
scheduleProgramUpdate();
}
@@ -496,17 +496,30 @@ namespace ts {
timerToUpdateProgram = undefined;
reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation));
- if (needsReload) {
- reloadConfigFile();
+ switch (reloadLevel) {
+ case ConfigFileProgramReloadLevel.Partial:
+ return reloadFileNamesFromConfigFile();
+ case ConfigFileProgramReloadLevel.Full:
+ return reloadConfigFile();
+ default:
+ return synchronizeProgram();
}
- else {
- synchronizeProgram();
+ }
+
+ function reloadFileNamesFromConfigFile() {
+ const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, directoryStructureHost);
+ if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
+ reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
}
+ rootFileNames = result.fileNames;
+
+ // Update the program
+ synchronizeProgram();
}
function reloadConfigFile() {
writeLog(`Reloading config file: ${configFileName}`);
- needsReload = false;
+ reloadLevel = ConfigFileProgramReloadLevel.None;
const cachedHost = directoryStructureHost as CachedDirectoryStructureHost;
cachedHost.clearCache();
@@ -611,18 +624,14 @@ namespace ts {
// If the the added or created file or directory is not supported file name, ignore the file
// But when watched directory is added/removed, we need to reload the file list
- if (fileOrDirectoryPath !== directory && !isSupportedSourceFileName(fileOrDirectory, compilerOptions)) {
+ if (fileOrDirectoryPath !== directory && hasExtension(fileOrDirectoryPath) && !isSupportedSourceFileName(fileOrDirectory, compilerOptions)) {
writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileOrDirectory}`);
return;
}
// Reload is pending, do the reload
- if (!needsReload) {
- const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, directoryStructureHost);
- if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
- reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
- }
- rootFileNames = result.fileNames;
+ if (reloadLevel !== ConfigFileProgramReloadLevel.Full) {
+ reloadLevel = ConfigFileProgramReloadLevel.Partial;
// Schedule Update the program
scheduleProgramUpdate();
diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts
index f39024d5a7c..0cf38f372d9 100644
--- a/src/compiler/watchUtilities.ts
+++ b/src/compiler/watchUtilities.ts
@@ -2,6 +2,14 @@
/* @internal */
namespace ts {
+ export enum ConfigFileProgramReloadLevel {
+ None,
+ /** Update the file name list from the disk */
+ Partial,
+ /** Reload completely by re-reading contents of config file from disk and updating program */
+ Full
+ }
+
/**
* Updates the existing missing file watches with the new set of missing files after new program is created
*/
diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts
index c58accb657c..30c5e47949f 100644
--- a/src/harness/compilerRunner.ts
+++ b/src/harness/compilerRunner.ts
@@ -211,8 +211,8 @@ class CompilerBaselineRunner extends RunnerBase {
this.emit = false;
const opts = this.options.split(",");
- for (let i = 0; i < opts.length; i++) {
- switch (opts[i]) {
+ for (const opt of opts) {
+ switch (opt) {
case "emit":
this.emit = true;
break;
diff --git a/src/harness/externalCompileRunner.ts b/src/harness/externalCompileRunner.ts
new file mode 100644
index 00000000000..2a44badd813
--- /dev/null
+++ b/src/harness/externalCompileRunner.ts
@@ -0,0 +1,162 @@
+///
+///
+const fs = require("fs");
+const path = require("path");
+
+interface ExecResult {
+ stdout: Buffer;
+ stderr: Buffer;
+ status: number;
+}
+
+interface UserConfig {
+ types: string[];
+}
+
+abstract class ExternalCompileRunnerBase extends RunnerBase {
+ abstract testDir: string;
+ abstract report(result: ExecResult, cwd: string): string;
+ enumerateTestFiles() {
+ return Harness.IO.getDirectories(this.testDir);
+ }
+ /** Setup the runner's tests so that they are ready to be executed by the harness
+ * The first test should be a describe/it block that sets up the harness's compiler instance appropriately
+ */
+ initializeTests(): void {
+ // Read in and evaluate the test list
+ const testList = this.tests && this.tests.length ? this.tests : this.enumerateTestFiles();
+
+ describe(`${this.kind()} code samples`, () => {
+ for (const test of testList) {
+ this.runTest(test);
+ }
+ });
+ }
+ private runTest(directoryName: string) {
+ describe(directoryName, () => {
+ const cp = require("child_process");
+
+ it("should build successfully", () => {
+ let cwd = path.join(__dirname, "../../", this.testDir, directoryName);
+ const timeout = 600000; // 600s = 10 minutes
+ const stdio = isWorker ? "pipe" : "inherit";
+ let types: string[];
+ if (fs.existsSync(path.join(cwd, "test.json"))) {
+ const update = cp.spawnSync("git", ["submodule", "update", "--remote"], { cwd, timeout, shell: true, stdio });
+ if (update.status !== 0) throw new Error(`git submodule update for ${directoryName} failed!`);
+
+ const config = JSON.parse(fs.readFileSync(path.join(cwd, "test.json"), { encoding: "utf8" })) as UserConfig;
+ ts.Debug.assert(!!config.types, "Bad format from test.json: Types field must be present.");
+ types = config.types;
+
+ cwd = path.join(cwd, directoryName);
+ }
+ if (fs.existsSync(path.join(cwd, "package.json"))) {
+ if (fs.existsSync(path.join(cwd, "package-lock.json"))) {
+ fs.unlinkSync(path.join(cwd, "package-lock.json"));
+ }
+ const install = cp.spawnSync(`npm`, ["i"], { cwd, timeout, shell: true, stdio });
+ if (install.status !== 0) throw new Error(`NPM Install for ${directoryName} failed!`);
+ }
+ const args = [path.join(__dirname, "tsc.js")];
+ if (types) {
+ args.push("--types", types.join(","));
+ }
+ args.push("--noEmit");
+ Harness.Baseline.runBaseline(`${this.kind()}/${directoryName}.log`, () => {
+ return this.report(cp.spawnSync(`node`, args, { cwd, timeout, shell: true }), cwd);
+ });
+ });
+ });
+ }
+}
+
+class UserCodeRunner extends ExternalCompileRunnerBase {
+ readonly testDir = "tests/cases/user/";
+ kind(): TestRunnerKind {
+ return "user";
+ }
+ report(result: ExecResult) {
+ // tslint:disable-next-line:no-null-keyword
+ return result.status === 0 && !result.stdout.length && !result.stderr.length ? null : `Exit Code: ${result.status}
+Standard output:
+${result.stdout.toString().replace(/\r\n/g, "\n")}
+
+
+Standard error:
+${result.stderr.toString().replace(/\r\n/g, "\n")}`;
+ }
+}
+
+class DefinitelyTypedRunner extends ExternalCompileRunnerBase {
+ readonly testDir = "../DefinitelyTyped/types/";
+ workingDirectory = this.testDir;
+ kind(): TestRunnerKind {
+ return "dt";
+ }
+ report(result: ExecResult, cwd: string) {
+ const stdout = removeExpectedErrors(result.stdout.toString(), cwd);
+ const stderr = result.stderr.toString();
+ // tslint:disable-next-line:no-null-keyword
+ return !stdout.length && !stderr.length ? null : `Exit Code: ${result.status}
+Standard output:
+${stdout.replace(/\r\n/g, "\n")}
+
+
+Standard error:
+${stderr.replace(/\r\n/g, "\n")}`;
+ }
+}
+
+function removeExpectedErrors(errors: string, cwd: string): string {
+ return ts.flatten(splitBy(errors.split("\n"), s => /^\S+/.test(s)).filter(isUnexpectedError(cwd))).join("\n");
+}
+/**
+ * Returns true if the line that caused the error contains '$ExpectError',
+ * or if the line before that one contains '$ExpectError'.
+ * '$ExpectError' is a marker used in Definitely Typed tests,
+ * meaning that the error should not contribute toward our error baslines.
+ */
+function isUnexpectedError(cwd: string) {
+ return (error: string[]) => {
+ ts.Debug.assertGreaterThanOrEqual(error.length, 1);
+ const match = error[0].match(/(.+\.ts)\((\d+),\d+\): error TS/);
+ if (!match) {
+ return true;
+ }
+ const [, errorFile, lineNumberString] = match;
+ const lines = fs.readFileSync(path.join(cwd, errorFile), { encoding: "utf8" }).split("\n");
+ const lineNumber = parseInt(lineNumberString);
+ ts.Debug.assertGreaterThanOrEqual(lineNumber, 0);
+ ts.Debug.assertLessThan(lineNumber, lines.length);
+ const previousLine = lineNumber - 1 > 0 ? lines[lineNumber - 1] : "";
+ return !ts.stringContains(lines[lineNumber], "$ExpectError") && !ts.stringContains(previousLine, "$ExpectError");
+ };
+}
+/**
+ * Split an array into multiple arrays whenever `isStart` returns true.
+ * @example
+ * splitBy([1,2,3,4,5,6], isOdd)
+ * ==> [[1, 2], [3, 4], [5, 6]]
+ * where
+ * const isOdd = n => !!(n % 2)
+ */
+function splitBy(xs: T[], isStart: (x: T) => boolean): T[][] {
+ const result = [];
+ let group: T[] = [];
+ for (const x of xs) {
+ if (isStart(x)) {
+ if (group.length) {
+ result.push(group);
+ }
+ group = [x];
+ }
+ else {
+ group.push(x);
+ }
+ }
+ if (group.length) {
+ result.push(group);
+ }
+ return result;
+}
diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts
index 288f4d73609..2687a660ca8 100644
--- a/src/harness/fourslash.ts
+++ b/src/harness/fourslash.ts
@@ -126,8 +126,8 @@ namespace FourSlash {
// 0 - cancelled
// >0 - not cancelled
// <0 - not cancelled and value denotes number of isCancellationRequested after which token become cancelled
- private static readonly NotCanceled: number = -1;
- private numberOfCallsBeforeCancellation: number = TestCancellationToken.NotCanceled;
+ private static readonly notCanceled = -1;
+ private numberOfCallsBeforeCancellation = TestCancellationToken.notCanceled;
public isCancellationRequested(): boolean {
if (this.numberOfCallsBeforeCancellation < 0) {
@@ -148,7 +148,7 @@ namespace FourSlash {
}
public resetCancelled(): void {
- this.numberOfCallsBeforeCancellation = TestCancellationToken.NotCanceled;
+ this.numberOfCallsBeforeCancellation = TestCancellationToken.notCanceled;
}
}
@@ -221,11 +221,9 @@ namespace FourSlash {
private addMatchedInputFile(referenceFilePath: string, extensions: ReadonlyArray) {
const inputFiles = this.inputFiles;
const languageServiceAdapterHost = this.languageServiceAdapterHost;
- if (!extensions) {
- tryAdd(referenceFilePath);
- }
- else {
- tryAdd(referenceFilePath) || ts.forEach(extensions, ext => tryAdd(referenceFilePath + ext));
+ const didAdd = tryAdd(referenceFilePath);
+ if (extensions && !didAdd) {
+ ts.forEach(extensions, ext => tryAdd(referenceFilePath + ext));
}
function tryAdd(path: string) {
@@ -470,6 +468,10 @@ namespace FourSlash {
public select(startMarker: string, endMarker: string) {
const start = this.getMarkerByName(startMarker), end = this.getMarkerByName(endMarker);
+ ts.Debug.assert(start.fileName === end.fileName);
+ if (this.activeFile.fileName !== start.fileName) {
+ this.openFile(start.fileName);
+ }
this.goToPosition(start.position);
this.selectionEnd = end.position;
}
@@ -481,9 +483,7 @@ namespace FourSlash {
}
// Opens a file given its 0-based index or fileName
- public openFile(index: number, content?: string, scriptKindName?: string): void;
- public openFile(name: string, content?: string, scriptKindName?: string): void;
- public openFile(indexOrName: any, content?: string, scriptKindName?: string) {
+ public openFile(indexOrName: number | string, content?: string, scriptKindName?: string): void {
const fileToOpen: FourSlashFile = this.findFile(indexOrName);
fileToOpen.fileName = ts.normalizeSlashes(fileToOpen.fileName);
this.activeFile = fileToOpen;
@@ -564,15 +564,31 @@ namespace FourSlash {
}
for (const { start, length, messageText, file } of errors) {
- Harness.IO.log(" from: " + showPosition(file, start) +
- ", to: " + showPosition(file, start + length) +
+ Harness.IO.log(" " + this.formatRange(file, start, length) +
", message: " + ts.flattenDiagnosticMessageText(messageText, Harness.IO.newLine()) + "\n");
}
+ }
- function showPosition(file: ts.SourceFile, pos: number) {
+ private formatRange(file: ts.SourceFile, start: number, length: number) {
+ if (file) {
+ return `from: ${this.formatLineAndCharacterOfPosition(file, start)}, to: ${this.formatLineAndCharacterOfPosition(file, start + length)}`;
+ }
+ return "global";
+ }
+
+ private formatLineAndCharacterOfPosition(file: ts.SourceFile, pos: number) {
+ if (file) {
const { line, character } = ts.getLineAndCharacterOfPosition(file, pos);
return `${line}:${character}`;
}
+ return "global";
+ }
+
+ private formatPosition(file: ts.SourceFile, pos: number) {
+ if (file) {
+ return file.fileName + "@" + pos;
+ }
+ return "global";
}
public verifyNoErrors() {
@@ -582,7 +598,7 @@ namespace FourSlash {
if (errors.length) {
this.printErrorLog(/*expectErrors*/ false, errors);
const error = errors[0];
- this.raiseError(`Found an error: ${error.file.fileName}@${error.start}: ${error.messageText}`);
+ this.raiseError(`Found an error: ${this.formatPosition(error.file, error.start)}: ${error.messageText}`);
}
});
}
@@ -616,19 +632,23 @@ namespace FourSlash {
}
public verifyGoToDefinition(arg0: any, endMarkerNames?: string | string[]) {
- this.verifyGoToX(arg0, endMarkerNames, () => this.getGoToDefinition());
+ this.verifyGoToX(arg0, endMarkerNames, () => this.getGoToDefinitionAndBoundSpan());
}
private getGoToDefinition(): ts.DefinitionInfo[] {
return this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
}
+ private getGoToDefinitionAndBoundSpan(): ts.DefinitionInfoAndBoundSpan {
+ return this.languageService.getDefinitionAndBoundSpan(this.activeFile.fileName, this.currentCaretPosition);
+ }
+
public verifyGoToType(arg0: any, endMarkerNames?: string | string[]) {
this.verifyGoToX(arg0, endMarkerNames, () =>
this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition));
}
- private verifyGoToX(arg0: any, endMarkerNames: string | string[] | undefined, getDefs: () => ts.DefinitionInfo[] | undefined) {
+ private verifyGoToX(arg0: any, endMarkerNames: string | string[] | undefined, getDefs: () => ts.DefinitionInfo[] | ts.DefinitionInfoAndBoundSpan | undefined) {
if (endMarkerNames) {
this.verifyGoToXPlain(arg0, endMarkerNames, getDefs);
}
@@ -648,7 +668,7 @@ namespace FourSlash {
}
}
- private verifyGoToXPlain(startMarkerNames: string | string[], endMarkerNames: string | string[], getDefs: () => ts.DefinitionInfo[] | undefined) {
+ private verifyGoToXPlain(startMarkerNames: string | string[], endMarkerNames: string | string[], getDefs: () => ts.DefinitionInfo[] | ts.DefinitionInfoAndBoundSpan | undefined) {
for (const start of toArray(startMarkerNames)) {
this.verifyGoToXSingle(start, endMarkerNames, getDefs);
}
@@ -660,26 +680,57 @@ namespace FourSlash {
}
}
- private verifyGoToXSingle(startMarkerName: string, endMarkerNames: string | string[], getDefs: () => ts.DefinitionInfo[] | undefined) {
+ private verifyGoToXSingle(startMarkerName: string, endMarkerNames: string | string[], getDefs: () => ts.DefinitionInfo[] | ts.DefinitionInfoAndBoundSpan | undefined) {
this.goToMarker(startMarkerName);
- this.verifyGoToXWorker(toArray(endMarkerNames), getDefs);
+ this.verifyGoToXWorker(toArray(endMarkerNames), getDefs, startMarkerName);
}
- private verifyGoToXWorker(endMarkers: string[], getDefs: () => ts.DefinitionInfo[] | undefined) {
- const definitions = getDefs() || [];
+ private verifyGoToXWorker(endMarkers: string[], getDefs: () => ts.DefinitionInfo[] | ts.DefinitionInfoAndBoundSpan | undefined, startMarkerName?: string) {
+ const defs = getDefs();
+ let definitions: ts.DefinitionInfo[] | ReadonlyArray;
+ let testName: string;
+
+ if (!defs || Array.isArray(defs)) {
+ definitions = defs as ts.DefinitionInfo[] || [];
+ testName = "goToDefinitions";
+ }
+ else {
+ this.verifyDefinitionTextSpan(defs, startMarkerName);
+
+ definitions = defs.definitions;
+ testName = "goToDefinitionsAndBoundSpan";
+ }
if (endMarkers.length !== definitions.length) {
- this.raiseError(`goToDefinitions failed - expected to find ${endMarkers.length} definitions but got ${definitions.length}`);
+ this.raiseError(`${testName} failed - expected to find ${endMarkers.length} definitions but got ${definitions.length}`);
}
ts.zipWith(endMarkers, definitions, (endMarker, definition, i) => {
const marker = this.getMarkerByName(endMarker);
if (marker.fileName !== definition.fileName || marker.position !== definition.textSpan.start) {
- this.raiseError(`goToDefinition failed for definition ${endMarker} (${i}): expected ${marker.fileName} at ${marker.position}, got ${definition.fileName} at ${definition.textSpan.start}`);
+ this.raiseError(`${testName} failed for definition ${endMarker} (${i}): expected ${marker.fileName} at ${marker.position}, got ${definition.fileName} at ${definition.textSpan.start}`);
}
});
}
+ private verifyDefinitionTextSpan(defs: ts.DefinitionInfoAndBoundSpan, startMarkerName: string) {
+ const range = this.testData.ranges.find(range => this.markerName(range.marker) === startMarkerName);
+
+ if (!range && !defs.textSpan) {
+ return;
+ }
+
+ if (!range) {
+ this.raiseError(`goToDefinitionsAndBoundSpan failed - found a TextSpan ${JSON.stringify(defs.textSpan)} when it wasn't expected.`);
+ }
+ else if (defs.textSpan.start !== range.start || defs.textSpan.length !== range.end - range.start) {
+ const expected: ts.TextSpan = {
+ start: range.start, length: range.end - range.start
+ };
+ this.raiseError(`goToDefinitionsAndBoundSpan failed - expected to find TextSpan ${JSON.stringify(expected)} but got ${JSON.stringify(defs.textSpan)}`);
+ }
+ }
+
public verifyGetEmitOutputForCurrentFile(expected: string): void {
const emit = this.languageService.getEmitOutput(this.activeFile.fileName);
if (emit.outputFiles.length !== 1) {
@@ -816,8 +867,8 @@ namespace FourSlash {
});
}
- public verifyCompletionListContains(entryId: ts.Completions.CompletionEntryIdentifier, text?: string, documentation?: string, kind?: string, spanIndex?: number, hasAction?: boolean) {
- const completions = this.getCompletionListAtCaret();
+ public verifyCompletionListContains(entryId: ts.Completions.CompletionEntryIdentifier, text?: string, documentation?: string, kind?: string, spanIndex?: number, hasAction?: boolean, options?: ts.GetCompletionsAtPositionOptions) {
+ const completions = this.getCompletionListAtCaret(options);
if (completions) {
this.assertItemInCompletionList(completions.entries, entryId, text, documentation, kind, spanIndex, hasAction);
}
@@ -837,38 +888,35 @@ namespace FourSlash {
* @param expectedKind the kind of symbol (see ScriptElementKind)
* @param spanIndex the index of the range that the completion item's replacement text span should match
*/
- public verifyCompletionListDoesNotContain(entryId: ts.Completions.CompletionEntryIdentifier, expectedText?: string, expectedDocumentation?: string, expectedKind?: string, spanIndex?: number) {
- const that = this;
+ public verifyCompletionListDoesNotContain(entryId: ts.Completions.CompletionEntryIdentifier, expectedText?: string, expectedDocumentation?: string, expectedKind?: string, spanIndex?: number, options?: ts.GetCompletionsAtPositionOptions) {
let replacementSpan: ts.TextSpan;
if (spanIndex !== undefined) {
replacementSpan = this.getTextSpanForRangeAtIndex(spanIndex);
}
- function filterByTextOrDocumentation(entry: ts.CompletionEntry) {
- const details = that.getCompletionEntryDetails(entry.name);
- const documentation = details && ts.displayPartsToString(details.documentation);
- const text = details && ts.displayPartsToString(details.displayParts);
-
- // If any of the expected values are undefined, assume that users don't
- // care about them.
- if (replacementSpan && !TestState.textSpansEqual(replacementSpan, entry.replacementSpan)) {
- return false;
- }
- else if (expectedText && text !== expectedText) {
- return false;
- }
- else if (expectedDocumentation && documentation !== expectedDocumentation) {
- return false;
- }
-
- return true;
- }
-
- const completions = this.getCompletionListAtCaret();
+ const completions = this.getCompletionListAtCaret(options);
if (completions) {
let filterCompletions = completions.entries.filter(e => e.name === entryId.name && e.source === entryId.source);
filterCompletions = expectedKind ? filterCompletions.filter(e => e.kind === expectedKind) : filterCompletions;
- filterCompletions = filterCompletions.filter(filterByTextOrDocumentation);
+ filterCompletions = filterCompletions.filter(entry => {
+ const details = this.getCompletionEntryDetails(entry.name);
+ const documentation = details && ts.displayPartsToString(details.documentation);
+ const text = details && ts.displayPartsToString(details.displayParts);
+
+ // If any of the expected values are undefined, assume that users don't
+ // care about them.
+ if (replacementSpan && !TestState.textSpansEqual(replacementSpan, entry.replacementSpan)) {
+ return false;
+ }
+ else if (expectedText && text !== expectedText) {
+ return false;
+ }
+ else if (expectedDocumentation && documentation !== expectedDocumentation) {
+ return false;
+ }
+
+ return true;
+ });
if (filterCompletions.length !== 0) {
// After filtered using all present criterion, if there are still symbol left in the list
// then these symbols must meet the criterion for Not supposed to be in the list. So we
@@ -1159,11 +1207,11 @@ Actual: ${stringify(fullActual)}`);
this.raiseError(`verifyReferencesAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(references)})`);
}
- private getCompletionListAtCaret() {
- return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition);
+ private getCompletionListAtCaret(options?: ts.GetCompletionsAtPositionOptions): ts.CompletionInfo {
+ return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options);
}
- private getCompletionEntryDetails(entryName: string, source?: string) {
+ private getCompletionEntryDetails(entryName: string, source?: string): ts.CompletionEntryDetails {
return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings, source);
}
@@ -1754,7 +1802,7 @@ Actual: ${stringify(fullActual)}`);
}
else if (prevChar === " " && /A-Za-z_/.test(ch)) {
/* Completions */
- this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset);
+ this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, { includeExternalModuleExports: false });
}
if (i % checkCadence === 0) {
@@ -2329,7 +2377,7 @@ Actual: ${stringify(fullActual)}`);
public applyCodeActionFromCompletion(markerName: string, options: FourSlashInterface.VerifyCompletionActionOptions) {
this.goToMarker(markerName);
- const actualCompletion = this.getCompletionListAtCaret().entries.find(e => e.name === options.name && e.source === options.source);
+ const actualCompletion = this.getCompletionListAtCaret({ includeExternalModuleExports: true }).entries.find(e => e.name === options.name && e.source === options.source);
if (!actualCompletion.hasAction) {
this.raiseError(`Completion for ${options.name} does not have an associated action.`);
@@ -3054,12 +3102,12 @@ Actual: ${stringify(fullActual)}`);
}
}
- const itemsString = items.map(item => stringify({ name: item.name, kind: item.kind })).join(",\n");
+ const itemsString = items.map(item => stringify({ name: item.name, source: item.source, kind: item.kind })).join(",\n");
this.raiseError(`Expected "${stringify({ entryId, text, documentation, kind })}" to be in list [${itemsString}]`);
}
- private findFile(indexOrName: any) {
+ private findFile(indexOrName: string | number) {
let result: FourSlashFile;
if (typeof indexOrName === "number") {
const index = indexOrName;
@@ -3711,9 +3759,7 @@ namespace FourSlashInterface {
this.state.goToImplementation();
}
- public position(position: number, fileIndex?: number): void;
- public position(position: number, fileName?: string): void;
- public position(position: number, fileNameOrIndex?: any): void {
+ public position(position: number, fileNameOrIndex?: string | number): void {
if (fileNameOrIndex !== undefined) {
this.file(fileNameOrIndex);
}
@@ -3723,9 +3769,7 @@ namespace FourSlashInterface {
// Opens a file, given either its index as it
// appears in the test source, or its filename
// as specified in the test metadata
- public file(index: number, content?: string, scriptKindName?: string): void;
- public file(name: string, content?: string, scriptKindName?: string): void;
- public file(indexOrName: any, content?: string, scriptKindName?: string): void {
+ public file(indexOrName: number | string, content?: string, scriptKindName?: string): void {
this.state.openFile(indexOrName, content, scriptKindName);
}
@@ -3767,15 +3811,15 @@ namespace FourSlashInterface {
// Verifies the completion list contains the specified symbol. The
// completion list is brought up if necessary
- public completionListContains(entryId: string | ts.Completions.CompletionEntryIdentifier, text?: string, documentation?: string, kind?: string, spanIndex?: number, hasAction?: boolean) {
+ public completionListContains(entryId: string | ts.Completions.CompletionEntryIdentifier, text?: string, documentation?: string, kind?: string, spanIndex?: number, hasAction?: boolean, options?: ts.GetCompletionsAtPositionOptions) {
if (typeof entryId === "string") {
entryId = { name: entryId, source: undefined };
}
if (this.negative) {
- this.state.verifyCompletionListDoesNotContain(entryId, text, documentation, kind, spanIndex);
+ this.state.verifyCompletionListDoesNotContain(entryId, text, documentation, kind, spanIndex, options);
}
else {
- this.state.verifyCompletionListContains(entryId, text, documentation, kind, spanIndex, hasAction);
+ this.state.verifyCompletionListContains(entryId, text, documentation, kind, spanIndex, hasAction, options);
}
}
@@ -3932,16 +3976,14 @@ namespace FourSlashInterface {
this.state.verifyGoToDefinitionIs(endMarkers);
}
- public goToDefinition(startMarkerName: string | string[], endMarkerName: string | string[]): void;
- public goToDefinition(startsAndEnds: [string | string[], string | string[]][]): void;
- public goToDefinition(startsAndEnds: { [startMarkerName: string]: string | string[] }): void;
+ public goToDefinition(startMarkerName: string | string[], endMarkerName: string | string[], range?: FourSlash.Range): void;
+ public goToDefinition(startsAndEnds: [string | string[], string | string[]][] | { [startMarkerName: string]: string | string[] }): void;
public goToDefinition(arg0: any, endMarkerName?: string | string[]) {
this.state.verifyGoToDefinition(arg0, endMarkerName);
}
public goToType(startMarkerName: string | string[], endMarkerName: string | string[]): void;
- public goToType(startsAndEnds: [string | string[], string | string[]][]): void;
- public goToType(startsAndEnds: { [startMarkerName: string]: string | string[] }): void;
+ public goToType(startsAndEnds: [string | string[], string | string[]][] | { [startMarkerName: string]: string | string[] }): void;
public goToType(arg0: any, endMarkerName?: string | string[]) {
this.state.verifyGoToType(arg0, endMarkerName);
}
diff --git a/src/harness/harness.ts b/src/harness/harness.ts
index c6caeff0dd9..8a3202b88be 100644
--- a/src/harness/harness.ts
+++ b/src/harness/harness.ts
@@ -479,7 +479,7 @@ namespace Utils {
}
namespace Harness {
- export interface IO {
+ export interface Io {
newLine(): string;
getCurrentDirectory(): string;
useCaseSensitiveFileNames(): boolean;
@@ -502,7 +502,7 @@ namespace Harness {
tryEnableSourceMapsForHost?(): void;
getEnvironmentVariable?(name: string): string;
}
- export let IO: IO;
+ export let IO: Io;
// harness always uses one kind of new line
// But note that `parseTestData` in `fourslash.ts` uses "\n"
@@ -555,8 +555,7 @@ namespace Harness {
try {
fs.unlinkSync(path);
}
- catch (e) {
- }
+ catch { /*ignore*/ }
}
export function directoryExists(path: string): boolean {
@@ -575,14 +574,13 @@ namespace Harness {
function filesInFolder(folder: string): string[] {
let paths: string[] = [];
- const files = fs.readdirSync(folder);
- for (let i = 0; i < files.length; i++) {
- const pathToFile = pathModule.join(folder, files[i]);
+ for (const file of fs.readdirSync(folder)) {
+ const pathToFile = pathModule.join(folder, file);
const stat = fs.statSync(pathToFile);
if (options.recursive && stat.isDirectory()) {
paths = paths.concat(filesInFolder(pathToFile));
}
- else if (stat.isFile() && (!spec || files[i].match(spec))) {
+ else if (stat.isFile() && (!spec || file.match(spec))) {
paths.push(pathToFile);
}
}
@@ -616,7 +614,7 @@ namespace Harness {
namespace Http {
function waitForXHR(xhr: XMLHttpRequest) {
- while (xhr.readyState !== 4) { }
+ while (xhr.readyState !== 4) { } // tslint:disable-line no-empty
return { status: xhr.status, responseText: xhr.responseText };
}
@@ -784,9 +782,15 @@ namespace Harness {
? IO.newLine() + `//# sourceURL=${IO.resolvePath(tcServicesFileName)}`
: "");
- export interface SourceMapEmitterCallback {
- (emittedFile: string, emittedLine: number, emittedColumn: number, sourceFile: string, sourceLine: number, sourceColumn: number, sourceName: string): void;
- }
+ export type SourceMapEmitterCallback = (
+ emittedFile: string,
+ emittedLine: number,
+ emittedColumn: number,
+ sourceFile: string,
+ sourceLine: number,
+ sourceColumn: number,
+ sourceName: string,
+ ) => void;
// Settings
export let userSpecifiedRoot = "";
@@ -1108,11 +1112,11 @@ namespace Harness {
case "string":
return value;
case "number": {
- const number = parseInt(value, 10);
- if (isNaN(number)) {
+ const numverValue = parseInt(value, 10);
+ if (isNaN(numverValue)) {
throw new Error(`Value must be a number, got: ${JSON.stringify(value)}`);
}
- return number;
+ return numverValue;
}
// If not a primitive, the possible types are specified in what is effectively a map of options.
case "list":
@@ -1319,7 +1323,7 @@ namespace Harness {
export const diagnosticSummaryMarker = "__diagnosticSummary";
export const globalErrorsMarker = "__globalErrors";
export function *iterateErrorBaseline(inputFiles: ReadonlyArray, diagnostics: ReadonlyArray, pretty?: boolean): IterableIterator<[string, string, number]> {
- diagnostics = diagnostics.slice().sort(ts.compareDiagnostics);
+ diagnostics = ts.sort(diagnostics, ts.compareDiagnostics);
let outputLines = "";
// Count up all errors that were found in files other than lib.d.ts so we don't miss any
let totalErrorsReportedInNonLibraryFiles = 0;
@@ -1575,10 +1579,8 @@ namespace Harness {
// Preserve legacy behavior
if (lastIndexWritten === undefined) {
- for (let i = 0; i < codeLines.length; i++) {
- const currentCodeLine = codeLines[i];
- typeLines += currentCodeLine + "\r\n";
- typeLines += "No type information for this code.";
+ for (const codeLine of codeLines) {
+ typeLines += codeLine + "\r\nNo type information for this code.";
}
}
else {
@@ -1704,7 +1706,7 @@ namespace Harness {
export function *iterateOutputs(outputFiles: Harness.Compiler.GeneratedFile[]): IterableIterator<[string, string]> {
// Collect, test, and sort the fileNames
- outputFiles.sort((a, b) => ts.compareStrings(cleanName(a.fileName), cleanName(b.fileName)));
+ outputFiles.sort((a, b) => ts.compareStringsCaseSensitive(cleanName(a.fileName), cleanName(b.fileName)));
const dupeCase = ts.createMap();
// Yield them
for (const outputFile of outputFiles) {
@@ -1864,8 +1866,7 @@ namespace Harness {
let currentFileName: any = undefined;
let refs: string[] = [];
- for (let i = 0; i < lines.length; i++) {
- const line = lines[i];
+ for (const line of lines) {
const testMetaData = optionRegex.exec(line);
if (testMetaData) {
// Comment line, check for global/file @options and record them
@@ -1962,7 +1963,7 @@ namespace Harness {
/** Support class for baseline files */
export namespace Baseline {
- const NoContent = "";
+ const noContent = "";
export interface BaselineOptions {
Subfolder?: string;
@@ -2021,7 +2022,7 @@ namespace Harness {
/* tslint:disable:no-null-keyword */
if (actual === null) {
/* tslint:enable:no-null-keyword */
- actual = NoContent;
+ actual = noContent;
}
let expected = "";
@@ -2058,13 +2059,13 @@ namespace Harness {
IO.deleteFile(actualFileName);
}
- const encoded_actual = Utils.encodeString(actual);
- if (expected !== encoded_actual) {
- if (actual === NoContent) {
+ const encodedActual = Utils.encodeString(actual);
+ if (expected !== encodedActual) {
+ if (actual === noContent) {
IO.writeFile(actualFileName + ".delete", "");
}
else {
- IO.writeFile(actualFileName, encoded_actual);
+ IO.writeFile(actualFileName, encodedActual);
}
throw new Error(`The baseline file ${relativeFileName} has changed.`);
}
@@ -2145,8 +2146,8 @@ namespace Harness {
return filePath.indexOf(Harness.libFolder) === 0;
}
- export function getDefaultLibraryFile(io: Harness.IO): Harness.Compiler.TestFile {
- const libFile = Harness.userSpecifiedRoot + Harness.libFolder + Harness.Compiler.defaultLibFileName;
+ export function getDefaultLibraryFile(filePath: string, io: Harness.Io): Harness.Compiler.TestFile {
+ const libFile = Harness.userSpecifiedRoot + Harness.libFolder + ts.getBaseFileName(ts.normalizeSlashes(filePath));
return { unitName: libFile, content: io.readFile(libFile) };
}
diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts
index 87827d0c14a..d4b5a9c2a16 100644
--- a/src/harness/harnessLanguageService.ts
+++ b/src/harness/harnessLanguageService.ts
@@ -108,7 +108,7 @@ namespace Harness.LanguageService {
}
class DefaultHostCancellationToken implements ts.HostCancellationToken {
- public static readonly Instance = new DefaultHostCancellationToken();
+ public static readonly instance = new DefaultHostCancellationToken();
public isCancellationRequested() {
return false;
@@ -126,7 +126,7 @@ namespace Harness.LanguageService {
public typesRegistry: ts.Map | undefined;
protected virtualFileSystem: Utils.VirtualFileSystem = new Utils.VirtualFileSystem(virtualFileSystemRoot, /*useCaseSensitiveFilenames*/false);
- constructor(protected cancellationToken = DefaultHostCancellationToken.Instance,
+ constructor(protected cancellationToken = DefaultHostCancellationToken.instance,
protected settings = ts.getDefaultCompilerOptions()) {
}
@@ -166,8 +166,7 @@ namespace Harness.LanguageService {
throw new Error("No script with name '" + fileName + "'");
}
- public openFile(_fileName: string, _content?: string, _scriptKindName?: string): void {
- }
+ public openFile(_fileName: string, _content?: string, _scriptKindName?: string): void { /*overridden*/ }
/**
* @param line 0 based index
@@ -237,9 +236,9 @@ namespace Harness.LanguageService {
}
- log(_: string): void { }
- trace(_: string): void { }
- error(_: string): void { }
+ log = ts.noop;
+ trace = ts.noop;
+ error = ts.noop;
}
export class NativeLanguageServiceAdapter implements LanguageServiceAdapter {
@@ -413,8 +412,8 @@ namespace Harness.LanguageService {
getEncodedSemanticClassifications(fileName: string, span: ts.TextSpan): ts.Classifications {
return unwrapJSONCallResult(this.shim.getEncodedSemanticClassifications(fileName, span.start, span.length));
}
- getCompletionsAtPosition(fileName: string, position: number): ts.CompletionInfo {
- return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position));
+ getCompletionsAtPosition(fileName: string, position: number, options: ts.GetCompletionsAtPositionOptions | undefined): ts.CompletionInfo {
+ return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, options));
}
getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: ts.FormatCodeOptions | undefined, source: string | undefined): ts.CompletionEntryDetails {
return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(options), source));
@@ -443,6 +442,9 @@ namespace Harness.LanguageService {
getDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] {
return unwrapJSONCallResult(this.shim.getDefinitionAtPosition(fileName, position));
}
+ getDefinitionAndBoundSpan(fileName: string, position: number): ts.DefinitionInfoAndBoundSpan {
+ return unwrapJSONCallResult(this.shim.getDefinitionAndBoundSpan(fileName, position));
+ }
getTypeDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] {
return unwrapJSONCallResult(this.shim.getTypeDefinitionAtPosition(fileName, position));
}
@@ -492,7 +494,7 @@ namespace Harness.LanguageService {
getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: ts.FormatCodeOptions): ts.TextChange[] {
return unwrapJSONCallResult(this.shim.getFormattingEditsAfterKeystroke(fileName, position, key, JSON.stringify(options)));
}
- getDocCommentTemplateAtPosition(fileName: string, position: number): ts.TextInsertion {
+ getDocCommentTemplateAtPosition(fileName: string, position: number): ts.TextInsertion | undefined {
return unwrapJSONCallResult(this.shim.getDocCommentTemplateAtPosition(fileName, position));
}
isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean {
@@ -541,9 +543,9 @@ namespace Harness.LanguageService {
getClassifier(): ts.Classifier { return new ClassifierShimProxy(this.factory.createClassifierShim(this.host)); }
getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo {
let shimResult: {
- referencedFiles: ts.IFileReference[];
- typeReferenceDirectives: ts.IFileReference[];
- importedFiles: ts.IFileReference[];
+ referencedFiles: ts.ShimsFileReference[];
+ typeReferenceDirectives: ts.ShimsFileReference[];
+ importedFiles: ts.ShimsFileReference[];
isLibFile: boolean;
};
@@ -593,13 +595,8 @@ namespace Harness.LanguageService {
super(cancellationToken, settings);
}
- onMessage(): void {
-
- }
-
- writeMessage(): void {
-
- }
+ onMessage = ts.noop;
+ writeMessage = ts.noop;
setClient(client: ts.server.SessionClient) {
this.client = client;
@@ -625,13 +622,8 @@ namespace Harness.LanguageService {
this.newLine = this.host.getNewLine();
}
- onMessage(): void {
-
- }
-
- writeMessage(_message: string): void {
- }
-
+ onMessage = ts.noop;
+ writeMessage = ts.noop; // overridden
write(message: string): void {
this.writeMessage(message);
}
@@ -645,8 +637,7 @@ namespace Harness.LanguageService {
return snapshot && snapshot.getText(0, snapshot.getLength());
}
- writeFile(): void {
- }
+ writeFile = ts.noop;
resolvePath(path: string): string {
return path;
@@ -665,8 +656,7 @@ namespace Harness.LanguageService {
return "";
}
- exit(): void {
- }
+ exit = ts.noop;
createDirectory(_directoryName: string): void {
return ts.notImplemented();
@@ -694,8 +684,7 @@ namespace Harness.LanguageService {
return { close: ts.noop };
}
- close(): void {
- }
+ close = ts.noop;
info(message: string): void {
this.host.log(message);
@@ -754,6 +743,7 @@ namespace Harness.LanguageService {
create(info: ts.server.PluginCreateInfo) {
const proxy = makeDefaultProxy(info);
const langSvc: any = info.languageService;
+ // tslint:disable-next-line only-arrow-functions
proxy.getQuickInfoAtPosition = function () {
const parts = langSvc.getQuickInfoAtPosition.apply(langSvc, arguments);
if (parts.displayParts.length > 0) {
@@ -786,7 +776,7 @@ namespace Harness.LanguageService {
module: () => ({
create(info: ts.server.PluginCreateInfo) {
const proxy = makeDefaultProxy(info);
- proxy.getSemanticDiagnostics = function (filename: string) {
+ proxy.getSemanticDiagnostics = filename => {
const prev = info.languageService.getSemanticDiagnostics(filename);
const sourceFile: ts.SourceFile = info.languageService.getSourceFile(filename);
prev.push({
@@ -812,11 +802,12 @@ namespace Harness.LanguageService {
};
}
- function makeDefaultProxy(info: ts.server.PluginCreateInfo) {
+ function makeDefaultProxy(info: ts.server.PluginCreateInfo): ts.LanguageService {
// tslint:disable-next-line:no-null-keyword
const proxy = Object.create(/*prototype*/ null);
const langSvc: any = info.languageService;
for (const k of Object.keys(langSvc)) {
+ // tslint:disable-next-line only-arrow-functions
proxy[k] = function () {
return langSvc[k].apply(langSvc, arguments);
};
diff --git a/src/harness/loggedIO.ts b/src/harness/loggedIO.ts
index 55be4068e83..37e8dddb3d7 100644
--- a/src/harness/loggedIO.ts
+++ b/src/harness/loggedIO.ts
@@ -14,19 +14,19 @@ interface FileInformation {
interface FindFileResult {
}
-interface IOLogFile {
+interface IoLogFile {
path: string;
codepage: number;
result?: FileInformation;
}
-interface IOLog {
+interface IoLog {
timestamp: string;
arguments: string[];
executingPath: string;
currentDirectory: string;
useCustomLibraryFile?: boolean;
- filesRead: IOLogFile[];
+ filesRead: IoLogFile[];
filesWritten: {
path: string;
contents?: string;
@@ -80,16 +80,16 @@ interface IOLog {
interface PlaybackControl {
startReplayFromFile(logFileName: string): void;
startReplayFromString(logContents: string): void;
- startReplayFromData(log: IOLog): void;
+ startReplayFromData(log: IoLog): void;
endReplay(): void;
startRecord(logFileName: string): void;
endRecord(): void;
}
namespace Playback {
- let recordLog: IOLog = undefined;
- let replayLog: IOLog = undefined;
- let replayFilesRead: ts.Map | undefined = undefined;
+ let recordLog: IoLog = undefined;
+ let replayLog: IoLog = undefined;
+ let replayFilesRead: ts.Map | undefined = undefined;
let recordLogFileNameBase = "";
interface Memoized {
@@ -110,11 +110,11 @@ namespace Playback {
return run;
}
- export interface PlaybackIO extends Harness.IO, PlaybackControl { }
+ export interface PlaybackIO extends Harness.Io, PlaybackControl { }
export interface PlaybackSystem extends ts.System, PlaybackControl { }
- function createEmptyLog(): IOLog {
+ function createEmptyLog(): IoLog {
return {
timestamp: (new Date()).toString(),
arguments: [],
@@ -134,7 +134,7 @@ namespace Playback {
};
}
- export function newStyleLogIntoOldStyleLog(log: IOLog, host: ts.System | Harness.IO, baseName: string) {
+ export function newStyleLogIntoOldStyleLog(log: IoLog, host: ts.System | Harness.Io, baseName: string) {
for (const file of log.filesAppended) {
if (file.contentsPath) {
file.contents = host.readFile(ts.combinePaths(baseName, file.contentsPath));
@@ -167,7 +167,7 @@ namespace Playback {
return path;
}
- export function oldStyleLogIntoNewStyleLog(log: IOLog, writeFile: typeof Harness.IO.writeFile, baseTestName: string) {
+ export function oldStyleLogIntoNewStyleLog(log: IoLog, writeFile: typeof Harness.IO.writeFile, baseTestName: string) {
if (log.filesAppended) {
for (const file of log.filesAppended) {
if (file.contents !== undefined) {
@@ -210,8 +210,8 @@ namespace Playback {
}
function initWrapper(wrapper: PlaybackSystem, underlying: ts.System): void;
- function initWrapper(wrapper: PlaybackIO, underlying: Harness.IO): void;
- function initWrapper(wrapper: PlaybackSystem | PlaybackIO, underlying: ts.System | Harness.IO): void {
+ function initWrapper(wrapper: PlaybackIO, underlying: Harness.Io): void;
+ function initWrapper(wrapper: PlaybackSystem | PlaybackIO, underlying: ts.System | Harness.Io): void {
ts.forEach(Object.keys(underlying), prop => {
(wrapper)[prop] = (underlying)[prop];
});
@@ -251,7 +251,7 @@ namespace Playback {
let i = 0;
const getBase = () => recordLogFileNameBase + i;
while (underlying.fileExists(ts.combinePaths(getBase(), "test.json"))) i++;
- const newLog = oldStyleLogIntoNewStyleLog(recordLog, (path, string) => underlying.writeFile(path, string), getBase());
+ const newLog = oldStyleLogIntoNewStyleLog(recordLog, (path, str) => underlying.writeFile(path, str), getBase());
underlying.writeFile(ts.combinePaths(getBase(), "test.json"), JSON.stringify(newLog, null, 4)); // tslint:disable-line:no-null-keyword
const syntheticTsconfig = generateTsconfig(newLog);
if (syntheticTsconfig) {
@@ -261,7 +261,7 @@ namespace Playback {
}
};
- function generateTsconfig(newLog: IOLog): undefined | { compilerOptions: ts.CompilerOptions, files: string[] } {
+ function generateTsconfig(newLog: IoLog): undefined | { compilerOptions: ts.CompilerOptions, files: string[] } {
if (newLog.filesRead.some(file => /tsconfig.+json$/.test(file.path))) {
return;
}
@@ -366,6 +366,7 @@ namespace Playback {
function recordReplay(original: T, underlying: any) {
function createWrapper(record: T, replay: T): T {
+ // tslint:disable-next-line only-arrow-functions
return (function () {
if (replayLog !== undefined) {
return replay.apply(undefined, arguments);
@@ -426,7 +427,7 @@ namespace Playback {
// console.log("Swallowed write operation during replay: " + name);
}
- export function wrapIO(underlying: Harness.IO): PlaybackIO {
+ export function wrapIO(underlying: Harness.Io): PlaybackIO {
const wrapper: PlaybackIO = {};
initWrapper(wrapper, underlying);
diff --git a/src/harness/parallel/host.ts b/src/harness/parallel/host.ts
index bf218c1b3a1..e278d267e1f 100644
--- a/src/harness/parallel/host.ts
+++ b/src/harness/parallel/host.ts
@@ -77,18 +77,18 @@ namespace Harness.Parallel.Host {
console.log("Discovering runner-based tests...");
const discoverStart = +(new Date());
const { statSync }: { statSync(path: string): { size: number }; } = require("fs");
+ const path: { join: (...args: string[]) => string } = require("path");
for (const runner of runners) {
- const files = runner.enumerateTestFiles();
- for (const file of files) {
+ for (const file of runner.enumerateTestFiles()) {
let size: number;
if (!perfData) {
try {
- size = statSync(file).size;
+ size = statSync(path.join(runner.workingDirectory, file)).size;
}
catch {
// May be a directory
try {
- size = Harness.IO.listFiles(file, /.*/g, { recursive: true }).reduce((acc, elem) => acc + statSync(elem).size, 0);
+ size = Harness.IO.listFiles(path.join(runner.workingDirectory, file), /.*/g, { recursive: true }).reduce((acc, elem) => acc + statSync(elem).size, 0);
}
catch {
// Unknown test kind, just return 0 and let the historical analysis take over after one run
@@ -274,15 +274,15 @@ namespace Harness.Parallel.Host {
function completeBar() {
const isPartitionFail = failingFiles !== 0;
const summaryColor = isPartitionFail ? "fail" : "green";
- const summarySymbol = isPartitionFail ? Base.symbols.err : Base.symbols.ok;
+ const summarySymbol = isPartitionFail ? base.symbols.err : base.symbols.ok;
const summaryTests = (isPartitionFail ? totalPassing + "/" + (errorResults.length + totalPassing) : totalPassing) + " passing";
const summaryDuration = "(" + ms(duration) + ")";
- const savedUseColors = Base.useColors;
- Base.useColors = !noColors;
+ const savedUseColors = base.useColors;
+ base.useColors = !noColors;
const summary = color(summaryColor, summarySymbol + " " + summaryTests) + " " + color("light", summaryDuration);
- Base.useColors = savedUseColors;
+ base.useColors = savedUseColors;
updateProgress(1, summary);
}
@@ -307,22 +307,21 @@ namespace Harness.Parallel.Host {
completeBar();
progressBars.disable();
- const reporter = new Base();
+ const reporter = new base();
const stats = reporter.stats;
const failures = reporter.failures;
stats.passes = totalPassing;
stats.failures = errorResults.length;
stats.tests = totalPassing + errorResults.length;
stats.duration = duration;
- for (let j = 0; j < errorResults.length; j++) {
- const failure = errorResults[j];
+ for (const failure of errorResults) {
failures.push(makeMochaTest(failure));
}
if (noColors) {
- const savedUseColors = Base.useColors;
- Base.useColors = false;
+ const savedUseColors = base.useColors;
+ base.useColors = false;
reporter.epilogue();
- Base.useColors = savedUseColors;
+ base.useColors = savedUseColors;
}
else {
reporter.epilogue();
@@ -353,8 +352,8 @@ namespace Harness.Parallel.Host {
return;
}
- let Mocha: any;
- let Base: any;
+ let mocha: any;
+ let base: any;
let color: any;
let cursor: any;
let readline: any;
@@ -395,10 +394,10 @@ namespace Harness.Parallel.Host {
}
function initializeProgressBarsDependencies() {
- Mocha = require("mocha");
- Base = Mocha.reporters.Base;
- color = Base.color;
- cursor = Base.cursor;
+ mocha = require("mocha");
+ base = mocha.reporters.Base;
+ color = base.color;
+ cursor = base.cursor;
readline = require("readline");
os = require("os");
tty = require("tty");
@@ -415,8 +414,8 @@ namespace Harness.Parallel.Host {
const open = options.open || "[";
const close = options.close || "]";
const complete = options.complete || "▬";
- const incomplete = options.incomplete || Base.symbols.dot;
- const maxWidth = Base.window.width - open.length - close.length - 34;
+ const incomplete = options.incomplete || base.symbols.dot;
+ const maxWidth = base.window.width - open.length - close.length - 34;
const width = minMax(options.width || maxWidth, 10, maxWidth);
this._options = {
open,
diff --git a/src/harness/parallel/worker.ts b/src/harness/parallel/worker.ts
index c32b9660a39..953ed5aeb21 100644
--- a/src/harness/parallel/worker.ts
+++ b/src/harness/parallel/worker.ts
@@ -57,7 +57,9 @@ namespace Harness.Parallel.Worker {
return cleanup();
}
try {
- beforeFunc && beforeFunc();
+ if (beforeFunc) {
+ beforeFunc();
+ }
}
catch (e) {
errors.push({ error: `Error executing before function: ${e.message}`, stack: e.stack, name: [...namestack] });
@@ -69,7 +71,9 @@ namespace Harness.Parallel.Worker {
testList.forEach(({ name, callback, kind }) => executeCallback(name, callback, kind));
try {
- afterFunc && afterFunc();
+ if (afterFunc) {
+ afterFunc();
+ }
}
catch (e) {
errors.push({ error: `Error executing after function: ${e.message}`, stack: e.stack, name: [...namestack] });
diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts
index cc68f8625f4..c617cc7a0a3 100644
--- a/src/harness/projectsRunner.ts
+++ b/src/harness/projectsRunner.ts
@@ -140,12 +140,12 @@ class ProjectRunner extends RunnerBase {
// Clean up source map data that will be used in baselining
if (sourceMapData) {
- for (let i = 0; i < sourceMapData.length; i++) {
- for (let j = 0; j < sourceMapData[i].sourceMapSources.length; j++) {
- sourceMapData[i].sourceMapSources[j] = cleanProjectUrl(sourceMapData[i].sourceMapSources[j]);
+ for (const data of sourceMapData) {
+ for (let j = 0; j < data.sourceMapSources.length; j++) {
+ data.sourceMapSources[j] = cleanProjectUrl(data.sourceMapSources[j]);
}
- sourceMapData[i].jsSourceMappingURL = cleanProjectUrl(sourceMapData[i].jsSourceMappingURL);
- sourceMapData[i].sourceMapSourceRoot = cleanProjectUrl(sourceMapData[i].sourceMapSourceRoot);
+ data.jsSourceMappingURL = cleanProjectUrl(data.jsSourceMappingURL);
+ data.sourceMapSourceRoot = cleanProjectUrl(data.sourceMapSourceRoot);
}
}
@@ -396,7 +396,7 @@ class ProjectRunner extends RunnerBase {
});
// Dont allow config files since we are compiling existing source options
- return compileProjectFiles(compilerResult.moduleKind, compilerResult.configFileSourceFiles, getInputFiles, getSourceFileText, writeFile, compilerResult.compilerOptions);
+ return compileProjectFiles(compilerResult.moduleKind, compilerResult.configFileSourceFiles, getInputFiles, getSourceFileText, /*writeFile*/ ts.noop, compilerResult.compilerOptions);
function findOutputDtsFile(fileName: string) {
return ts.forEach(compilerResult.outputFiles, outputFile => outputFile.emittedFileName === fileName ? outputFile : undefined);
@@ -416,9 +416,6 @@ class ProjectRunner extends RunnerBase {
}
return undefined;
}
-
- function writeFile() {
- }
}
function getErrorsBaseline(compilerResult: CompileProjectFilesResult) {
diff --git a/src/harness/runner.ts b/src/harness/runner.ts
index db807b976bb..2bd098b742d 100644
--- a/src/harness/runner.ts
+++ b/src/harness/runner.ts
@@ -18,6 +18,7 @@
///
///
///
+///
///
///
@@ -26,8 +27,8 @@ let iterations = 1;
function runTests(runners: RunnerBase[]) {
for (let i = iterations; i > 0; i--) {
- for (let j = 0; j < runners.length; j++) {
- runners[j].initializeTests();
+ for (const runner of runners) {
+ runner.initializeTests();
}
}
}
@@ -59,6 +60,10 @@ function createRunner(kind: TestRunnerKind): RunnerBase {
return new RWCRunner();
case "test262":
return new Test262BaselineRunner();
+ case "user":
+ return new UserCodeRunner();
+ case "dt":
+ return new DefinitelyTypedRunner();
}
ts.Debug.fail(`Unknown runner kind ${kind}`);
}
@@ -92,6 +97,7 @@ interface TestConfig {
workerCount?: number;
stackTraceLimit?: number | "full";
test?: string[];
+ runners?: string[];
runUnitTests?: boolean;
noColors?: boolean;
}
@@ -129,8 +135,9 @@ function handleTestConfig() {
return true;
}
- if (testConfig.test && testConfig.test.length > 0) {
- for (const option of testConfig.test) {
+ const runnerConfig = testConfig.runners || testConfig.test;
+ if (runnerConfig && runnerConfig.length > 0) {
+ for (const option of runnerConfig) {
if (!option) {
continue;
}
@@ -175,6 +182,12 @@ function handleTestConfig() {
case "test262":
runners.push(new Test262BaselineRunner());
break;
+ case "user":
+ runners.push(new UserCodeRunner());
+ break;
+ case "dt":
+ runners.push(new DefinitelyTypedRunner());
+ break;
}
}
}
@@ -196,6 +209,11 @@ function handleTestConfig() {
runners.push(new FourSlashRunner(FourSlashTestType.ShimsWithPreprocess));
runners.push(new FourSlashRunner(FourSlashTestType.Server));
// runners.push(new GeneratedFourslashRunner());
+
+ // CRON-only tests
+ if (Utils.getExecutionEnvironment() !== Utils.ExecutionEnvironment.Browser && process.env.TRAVIS_EVENT_TYPE === "cron") {
+ runners.push(new UserCodeRunner());
+ }
}
if (runUnitTests === undefined) {
runUnitTests = runners.length !== 1; // Don't run unit tests when running only one runner if unit tests were not explicitly asked for
@@ -207,6 +225,14 @@ function beginTests() {
ts.Debug.enableDebugInfo();
}
+ // run tests in en-US by default.
+ let savedUILocale: string | undefined;
+ beforeEach(() => {
+ savedUILocale = ts.getUILocale();
+ ts.setUILocale("en-US");
+ });
+ afterEach(() => ts.setUILocale(savedUILocale));
+
runTests(runners);
if (!runUnitTests) {
@@ -215,8 +241,9 @@ function beginTests() {
}
}
+let isWorker: boolean;
function startTestEnvironment() {
- const isWorker = handleTestConfig();
+ isWorker = handleTestConfig();
if (Utils.getExecutionEnvironment() !== Utils.ExecutionEnvironment.Browser) {
if (isWorker) {
return Harness.Parallel.Worker.start();
diff --git a/src/harness/runnerbase.ts b/src/harness/runnerbase.ts
index d50604803ed..80532d30b6f 100644
--- a/src/harness/runnerbase.ts
+++ b/src/harness/runnerbase.ts
@@ -1,13 +1,11 @@
///
-type TestRunnerKind = CompilerTestKind | FourslashTestKind | "project" | "rwc" | "test262";
+type TestRunnerKind = CompilerTestKind | FourslashTestKind | "project" | "rwc" | "test262" | "user" | "dt";
type CompilerTestKind = "conformance" | "compiler";
type FourslashTestKind = "fourslash" | "fourslash-shims" | "fourslash-shims-pp" | "fourslash-server";
abstract class RunnerBase {
- constructor() { }
-
// contains the tests to run
public tests: string[] = [];
@@ -24,6 +22,9 @@ abstract class RunnerBase {
abstract enumerateTestFiles(): string[];
+ /** The working directory where tests are found. Needed for batch testing where the input path will differ from the output path inside baselines */
+ public workingDirectory = "";
+
/** Setup the runner's tests so that they are ready to be executed by the harness
* The first test should be a describe/it block that sets up the harness's compiler instance appropriately
*/
diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts
index 2b4fc7b24e4..ef7a371b0c4 100644
--- a/src/harness/rwcRunner.ts
+++ b/src/harness/rwcRunner.ts
@@ -6,7 +6,7 @@
/* tslint:disable:no-null-keyword */
namespace RWC {
- function runWithIOLog(ioLog: IOLog, fn: (oldIO: Harness.IO) => void) {
+ function runWithIOLog(ioLog: IoLog, fn: (oldIO: Harness.Io) => void) {
const oldIO = Harness.IO;
const wrappedIO = Playback.wrapIO(oldIO);
@@ -58,7 +58,7 @@ namespace RWC {
this.timeout(800000); // Allow long timeouts for RWC compilations
let opts: ts.ParsedCommandLine;
- const ioLog: IOLog = Playback.newStyleLogIntoOldStyleLog(JSON.parse(Harness.IO.readFile(`internal/cases/rwc/${jsonPath}/test.json`)), Harness.IO, `internal/cases/rwc/${baseName}`);
+ const ioLog: IoLog = Playback.newStyleLogIntoOldStyleLog(JSON.parse(Harness.IO.readFile(`internal/cases/rwc/${jsonPath}/test.json`)), Harness.IO, `internal/cases/rwc/${baseName}`);
currentDirectory = ioLog.currentDirectory;
useCustomLibraryFile = ioLog.useCustomLibraryFile;
runWithIOLog(ioLog, () => {
@@ -131,13 +131,14 @@ namespace RWC {
}
else {
// set the flag to put default library to the beginning of the list
- inputFiles.unshift(Harness.getDefaultLibraryFile(oldIO));
+ inputFiles.unshift(Harness.getDefaultLibraryFile(fileRead.path, oldIO));
}
}
}
}
// do not use lib since we already read it in above
+ opts.options.lib = undefined;
opts.options.noLib = true;
// Emit the results
@@ -245,10 +246,8 @@ class RWCRunner extends RunnerBase {
*/
public initializeTests(): void {
// Read in and evaluate the test list
- const testList = this.tests && this.tests.length ? this.tests : this.enumerateTestFiles();
-
- for (let i = 0; i < testList.length; i++) {
- this.runTest(testList[i]);
+ for (const test of this.tests && this.tests.length ? this.tests : this.enumerateTestFiles()) {
+ this.runTest(test);
}
}
diff --git a/src/harness/sourceMapRecorder.ts b/src/harness/sourceMapRecorder.ts
index de068706fbe..606d4e67acd 100644
--- a/src/harness/sourceMapRecorder.ts
+++ b/src/harness/sourceMapRecorder.ts
@@ -386,9 +386,9 @@ namespace Harness.SourceMapRecorder {
if (currentSpan.decodeErrors) {
// If there are decode errors, write
- for (let i = 0; i < currentSpan.decodeErrors.length; i++) {
+ for (const decodeError of currentSpan.decodeErrors) {
writeSourceMapIndent(prevEmittedCol, markerIds[index]);
- sourceMapRecorder.WriteLine(currentSpan.decodeErrors[i]);
+ sourceMapRecorder.WriteLine(decodeError);
}
}
@@ -442,8 +442,7 @@ namespace Harness.SourceMapRecorder {
let prevSourceFile: ts.SourceFile;
SourceMapSpanWriter.initializeSourceMapSpanWriter(sourceMapRecorder, sourceMapData, jsFiles[i]);
- for (let j = 0; j < sourceMapData.sourceMapDecodedMappings.length; j++) {
- const decodedSourceMapping = sourceMapData.sourceMapDecodedMappings[j];
+ for (const decodedSourceMapping of sourceMapData.sourceMapDecodedMappings) {
const currentSourceFile = program.getSourceFile(sourceMapData.inputSourceFileNames[decodedSourceMapping.sourceIndex]);
if (currentSourceFile !== prevSourceFile) {
SourceMapSpanWriter.recordNewSourceFileSpan(decodedSourceMapping, currentSourceFile.text);
diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json
index 18baeb67c82..db38b95ba6a 100644
--- a/src/harness/tsconfig.json
+++ b/src/harness/tsconfig.json
@@ -60,20 +60,11 @@
"../services/jsTyping.ts",
"../services/formatting/formatting.ts",
"../services/formatting/formattingContext.ts",
- "../services/formatting/formattingRequestKind.ts",
"../services/formatting/formattingScanner.ts",
- "../services/formatting/references.ts",
"../services/formatting/rule.ts",
- "../services/formatting/ruleAction.ts",
- "../services/formatting/ruleDescriptor.ts",
- "../services/formatting/ruleFlag.ts",
- "../services/formatting/ruleOperation.ts",
- "../services/formatting/ruleOperationContext.ts",
"../services/formatting/rules.ts",
"../services/formatting/rulesMap.ts",
- "../services/formatting/rulesProvider.ts",
"../services/formatting/smartIndenter.ts",
- "../services/formatting/tokenRange.ts",
"../services/codeFixProvider.ts",
"../services/codefixes/fixes.ts",
"../services/codefixes/helpers.ts",
@@ -92,6 +83,7 @@
"projectsRunner.ts",
"loggedIO.ts",
"rwcRunner.ts",
+ "externalCompileRunner.ts",
"test262Runner.ts",
"./parallel/shared.ts",
"./parallel/host.ts",
diff --git a/src/harness/unittests/commandLineParsing.ts b/src/harness/unittests/commandLineParsing.ts
index 01a208aa330..18e867cb9ea 100644
--- a/src/harness/unittests/commandLineParsing.ts
+++ b/src/harness/unittests/commandLineParsing.ts
@@ -60,7 +60,7 @@ namespace ts {
assertParseResult(["--lib", "es5,invalidOption", "0.ts"],
{
errors: [{
- messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'esnext.asynciterable'.",
+ messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.",
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
@@ -263,7 +263,7 @@ namespace ts {
assertParseResult(["--lib", "es5,", "es7", "0.ts"],
{
errors: [{
- messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'esnext.asynciterable'.",
+ messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.",
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
@@ -283,7 +283,7 @@ namespace ts {
assertParseResult(["--lib", "es5, ", "es7", "0.ts"],
{
errors: [{
- messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'esnext.asynciterable'.",
+ messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.",
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
diff --git a/src/harness/unittests/compileOnSave.ts b/src/harness/unittests/compileOnSave.ts
index 7be6ab5b323..7ef3e2aa1af 100644
--- a/src/harness/unittests/compileOnSave.ts
+++ b/src/harness/unittests/compileOnSave.ts
@@ -13,8 +13,8 @@ namespace ts.projectSystem {
describe("CompileOnSave affected list", () => {
function sendAffectedFileRequestAndCheckResult(session: server.Session, request: server.protocol.Request, expectedFileList: { projectFileName: string, files: FileOrFolder[] }[]) {
const response = session.executeCommand(request).response as server.protocol.CompileOnSaveAffectedFileListSingleProject[];
- const actualResult = response.sort((list1, list2) => compareStrings(list1.projectFileName, list2.projectFileName));
- expectedFileList = expectedFileList.sort((list1, list2) => compareStrings(list1.projectFileName, list2.projectFileName));
+ const actualResult = response.sort((list1, list2) => ts.compareStringsCaseSensitive(list1.projectFileName, list2.projectFileName));
+ expectedFileList = expectedFileList.sort((list1, list2) => ts.compareStringsCaseSensitive(list1.projectFileName, list2.projectFileName));
assert.equal(actualResult.length, expectedFileList.length, `Actual result project number is different from the expected project number`);
@@ -627,8 +627,8 @@ namespace ts.projectSystem {
const mapFileContent = host.readFile(expectedMapFileName);
verifyContentHasString(mapFileContent, `"sources":["${inputFileName}"]`);
- function verifyContentHasString(content: string, string: string) {
- assert.isTrue(content.indexOf(string) !== -1, `Expected "${content}" to have "${string}"`);
+ function verifyContentHasString(content: string, str: string) {
+ assert.isTrue(stringContains(content, str), `Expected "${content}" to have "${str}"`);
}
});
});
diff --git a/src/harness/unittests/configurationExtension.ts b/src/harness/unittests/configurationExtension.ts
index 0032505aba1..5a1b155fbcf 100644
--- a/src/harness/unittests/configurationExtension.ts
+++ b/src/harness/unittests/configurationExtension.ts
@@ -25,9 +25,9 @@ namespace ts {
},
"/dev/configs/tests.json": {
compilerOptions: {
- "preserveConstEnums": true,
- "removeComments": false,
- "sourceMap": true
+ preserveConstEnums: true,
+ removeComments: false,
+ sourceMap: true
},
exclude: [
"../tests/baselines",
@@ -52,7 +52,7 @@ namespace ts {
"/dev/missing.json": {
extends: "./missing2",
compilerOptions: {
- "types": []
+ types: []
}
},
"/dev/failure.json": {
diff --git a/src/harness/unittests/convertCompilerOptionsFromJson.ts b/src/harness/unittests/convertCompilerOptionsFromJson.ts
index 09659762288..3a73971d7a3 100644
--- a/src/harness/unittests/convertCompilerOptionsFromJson.ts
+++ b/src/harness/unittests/convertCompilerOptionsFromJson.ts
@@ -58,12 +58,12 @@ namespace ts {
it("Convert correctly format tsconfig.json to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": false,
- "sourceMap": false,
- "lib": ["es5", "es2015.core", "es2015.symbol"]
+ compilerOptions: {
+ module: "commonjs",
+ target: "es5",
+ noImplicitAny: false,
+ sourceMap: false,
+ lib: ["es5", "es2015.core", "es2015.symbol"]
}
}, "tsconfig.json",
{
@@ -82,13 +82,13 @@ namespace ts {
it("Convert correctly format tsconfig.json with allowJs is false to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": false,
- "sourceMap": false,
- "allowJs": false,
- "lib": ["es5", "es2015.core", "es2015.symbol"]
+ compilerOptions: {
+ module: "commonjs",
+ target: "es5",
+ noImplicitAny: false,
+ sourceMap: false,
+ allowJs: false,
+ lib: ["es5", "es2015.core", "es2015.symbol"]
}
}, "tsconfig.json",
{
@@ -108,12 +108,12 @@ namespace ts {
it("Convert incorrect option of jsx to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": false,
- "sourceMap": false,
- "jsx": ""
+ compilerOptions: {
+ module: "commonjs",
+ target: "es5",
+ noImplicitAny: false,
+ sourceMap: false,
+ jsx: ""
}
}, "tsconfig.json",
{
@@ -138,11 +138,11 @@ namespace ts {
it("Convert incorrect option of module to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "module": "",
- "target": "es5",
- "noImplicitAny": false,
- "sourceMap": false,
+ compilerOptions: {
+ module: "",
+ target: "es5",
+ noImplicitAny: false,
+ sourceMap: false,
}
}, "tsconfig.json",
{
@@ -166,11 +166,11 @@ namespace ts {
it("Convert incorrect option of newLine to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "newLine": "",
- "target": "es5",
- "noImplicitAny": false,
- "sourceMap": false,
+ compilerOptions: {
+ newLine: "",
+ target: "es5",
+ noImplicitAny: false,
+ sourceMap: false,
}
}, "tsconfig.json",
{
@@ -194,10 +194,10 @@ namespace ts {
it("Convert incorrect option of target to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "target": "",
- "noImplicitAny": false,
- "sourceMap": false,
+ compilerOptions: {
+ target: "",
+ noImplicitAny: false,
+ sourceMap: false,
}
}, "tsconfig.json",
{
@@ -220,10 +220,10 @@ namespace ts {
it("Convert incorrect option of module-resolution to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "moduleResolution": "",
- "noImplicitAny": false,
- "sourceMap": false,
+ compilerOptions: {
+ moduleResolution: "",
+ noImplicitAny: false,
+ sourceMap: false,
}
}, "tsconfig.json",
{
@@ -246,12 +246,12 @@ namespace ts {
it("Convert incorrect option of libs to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": false,
- "sourceMap": false,
- "lib": ["es5", "es2015.core", "incorrectLib"]
+ compilerOptions: {
+ module: "commonjs",
+ target: "es5",
+ noImplicitAny: false,
+ sourceMap: false,
+ lib: ["es5", "es2015.core", "incorrectLib"]
}
}, "tsconfig.json",
{
@@ -266,7 +266,7 @@ namespace ts {
file: undefined,
start: 0,
length: 0,
- messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'esnext.asynciterable'.",
+ messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.",
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
@@ -277,12 +277,12 @@ namespace ts {
it("Convert empty string option of libs to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": false,
- "sourceMap": false,
- "lib": ["es5", ""]
+ compilerOptions: {
+ module: "commonjs",
+ target: "es5",
+ noImplicitAny: false,
+ sourceMap: false,
+ lib: ["es5", ""]
}
}, "tsconfig.json",
{
@@ -297,7 +297,7 @@ namespace ts {
file: undefined,
start: 0,
length: 0,
- messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'esnext.asynciterable'.",
+ messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.",
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
@@ -308,12 +308,12 @@ namespace ts {
it("Convert empty string option of libs to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": false,
- "sourceMap": false,
- "lib": [""]
+ compilerOptions: {
+ module: "commonjs",
+ target: "es5",
+ noImplicitAny: false,
+ sourceMap: false,
+ lib: [""]
}
}, "tsconfig.json",
{
@@ -328,7 +328,7 @@ namespace ts {
file: undefined,
start: 0,
length: 0,
- messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'esnext.asynciterable'.",
+ messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.",
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
@@ -339,12 +339,12 @@ namespace ts {
it("Convert trailing-whitespace string option of libs to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": false,
- "sourceMap": false,
- "lib": [" "]
+ compilerOptions: {
+ module: "commonjs",
+ target: "es5",
+ noImplicitAny: false,
+ sourceMap: false,
+ lib: [" "]
}
}, "tsconfig.json",
{
@@ -359,7 +359,7 @@ namespace ts {
file: undefined,
start: 0,
length: 0,
- messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'esnext.asynciterable'.",
+ messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'esnext', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'esnext.asynciterable'.",
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
@@ -370,12 +370,12 @@ namespace ts {
it("Convert empty option of libs to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": false,
- "sourceMap": false,
- "lib": []
+ compilerOptions: {
+ module: "commonjs",
+ target: "es5",
+ noImplicitAny: false,
+ sourceMap: false,
+ lib: []
}
}, "tsconfig.json",
{
@@ -394,8 +394,8 @@ namespace ts {
it("Convert incorrectly format tsconfig.json to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "modu": "commonjs",
+ compilerOptions: {
+ modu: "commonjs",
}
}, "tsconfig.json",
{
@@ -424,9 +424,9 @@ namespace ts {
it("Convert negative numbers in tsconfig.json ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "allowJs": true,
- "maxNodeModuleJsDepth": -1
+ compilerOptions: {
+ allowJs: true,
+ maxNodeModuleJsDepth: -1
}
}, "tsconfig.json",
{
@@ -443,12 +443,12 @@ namespace ts {
it("Convert correctly format jsconfig.json to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": false,
- "sourceMap": false,
- "lib": ["es5", "es2015.core", "es2015.symbol"]
+ compilerOptions: {
+ module: "commonjs",
+ target: "es5",
+ noImplicitAny: false,
+ sourceMap: false,
+ lib: ["es5", "es2015.core", "es2015.symbol"]
}
}, "jsconfig.json",
{
@@ -471,13 +471,13 @@ namespace ts {
it("Convert correctly format jsconfig.json with allowJs is false to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": false,
- "sourceMap": false,
- "allowJs": false,
- "lib": ["es5", "es2015.core", "es2015.symbol"]
+ compilerOptions: {
+ module: "commonjs",
+ target: "es5",
+ noImplicitAny: false,
+ sourceMap: false,
+ allowJs: false,
+ lib: ["es5", "es2015.core", "es2015.symbol"]
}
}, "jsconfig.json",
{
@@ -500,8 +500,8 @@ namespace ts {
it("Convert incorrectly format jsconfig.json to compiler-options ", () => {
assertCompilerOptions(
{
- "compilerOptions": {
- "modu": "commonjs",
+ compilerOptions: {
+ modu: "commonjs",
}
}, "jsconfig.json",
{
diff --git a/src/harness/unittests/convertTypeAcquisitionFromJson.ts b/src/harness/unittests/convertTypeAcquisitionFromJson.ts
index 5a367edebbb..be1ada10a97 100644
--- a/src/harness/unittests/convertTypeAcquisitionFromJson.ts
+++ b/src/harness/unittests/convertTypeAcquisitionFromJson.ts
@@ -55,11 +55,11 @@ namespace ts {
it("Convert deprecated typingOptions.enableAutoDiscovery format tsconfig.json to typeAcquisition ", () => {
assertTypeAcquisition(
{
- "typingOptions":
+ typingOptions:
{
- "enableAutoDiscovery": true,
- "include": ["0.d.ts", "1.d.ts"],
- "exclude": ["0.js", "1.js"]
+ enableAutoDiscovery: true,
+ include: ["0.d.ts", "1.d.ts"],
+ exclude: ["0.js", "1.js"]
}
},
"tsconfig.json",
@@ -77,11 +77,11 @@ namespace ts {
it("Convert correctly format tsconfig.json to typeAcquisition ", () => {
assertTypeAcquisition(
{
- "typeAcquisition":
+ typeAcquisition:
{
- "enable": true,
- "include": ["0.d.ts", "1.d.ts"],
- "exclude": ["0.js", "1.js"]
+ enable: true,
+ include: ["0.d.ts", "1.d.ts"],
+ exclude: ["0.js", "1.js"]
}
},
"tsconfig.json",
@@ -99,9 +99,9 @@ namespace ts {
it("Convert incorrect format tsconfig.json to typeAcquisition ", () => {
assertTypeAcquisition(
{
- "typeAcquisition":
+ typeAcquisition:
{
- "enableAutoDiscovy": true,
+ enableAutoDiscovy: true,
}
}, "tsconfig.json",
{
@@ -140,9 +140,9 @@ namespace ts {
it("Convert tsconfig.json with only enable property to typeAcquisition ", () => {
assertTypeAcquisition(
{
- "typeAcquisition":
+ typeAcquisition:
{
- "enable": true
+ enable: true
}
}, "tsconfig.json",
{
@@ -160,11 +160,11 @@ namespace ts {
it("Convert jsconfig.json to typeAcquisition ", () => {
assertTypeAcquisition(
{
- "typeAcquisition":
+ typeAcquisition:
{
- "enable": false,
- "include": ["0.d.ts"],
- "exclude": ["0.js"]
+ enable: false,
+ include: ["0.d.ts"],
+ exclude: ["0.js"]
}
}, "jsconfig.json",
{
@@ -194,9 +194,9 @@ namespace ts {
it("Convert incorrect format jsconfig.json to typeAcquisition ", () => {
assertTypeAcquisition(
{
- "typeAcquisition":
+ typeAcquisition:
{
- "enableAutoDiscovy": true,
+ enableAutoDiscovy: true,
}
}, "jsconfig.json",
{
@@ -222,9 +222,9 @@ namespace ts {
it("Convert jsconfig.json with only enable property to typeAcquisition ", () => {
assertTypeAcquisition(
{
- "typeAcquisition":
+ typeAcquisition:
{
- "enable": false
+ enable: false
}
}, "jsconfig.json",
{
diff --git a/src/harness/unittests/extractRanges.ts b/src/harness/unittests/extractRanges.ts
index e8e9a918f0d..493c9639c3d 100644
--- a/src/harness/unittests/extractRanges.ts
+++ b/src/harness/unittests/extractRanges.ts
@@ -191,7 +191,7 @@ function f() {
}
`,
[
- refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalReturnStatement.message
+ refactor.extractSymbol.Messages.cannotExtractRangeContainingConditionalReturnStatement.message
]);
testExtractRangeFailed("extractRangeFailed2",
@@ -210,7 +210,7 @@ function f() {
}
`,
[
- refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message
+ refactor.extractSymbol.Messages.cannotExtractRangeContainingConditionalBreakOrContinueStatements.message
]);
testExtractRangeFailed("extractRangeFailed3",
@@ -229,7 +229,7 @@ function f() {
}
`,
[
- refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message
+ refactor.extractSymbol.Messages.cannotExtractRangeContainingConditionalBreakOrContinueStatements.message
]);
testExtractRangeFailed("extractRangeFailed4",
@@ -248,7 +248,7 @@ function f() {
}
`,
[
- refactor.extractSymbol.Messages.CannotExtractRangeContainingLabeledBreakOrContinueStatementWithTargetOutsideOfTheRange.message
+ refactor.extractSymbol.Messages.cannotExtractRangeContainingLabeledBreakOrContinueStatementWithTargetOutsideOfTheRange.message
]);
testExtractRangeFailed("extractRangeFailed5",
@@ -269,7 +269,7 @@ function f2() {
}
`,
[
- refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalReturnStatement.message
+ refactor.extractSymbol.Messages.cannotExtractRangeContainingConditionalReturnStatement.message
]);
testExtractRangeFailed("extractRangeFailed6",
@@ -290,7 +290,7 @@ function f2() {
}
`,
[
- refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalReturnStatement.message
+ refactor.extractSymbol.Messages.cannotExtractRangeContainingConditionalReturnStatement.message
]);
testExtractRangeFailed("extractRangeFailed7",
@@ -303,7 +303,7 @@ while (x) {
}
`,
[
- refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message
+ refactor.extractSymbol.Messages.cannotExtractRangeContainingConditionalBreakOrContinueStatements.message
]);
testExtractRangeFailed("extractRangeFailed8",
@@ -316,13 +316,13 @@ switch (x) {
}
`,
[
- refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message
+ refactor.extractSymbol.Messages.cannotExtractRangeContainingConditionalBreakOrContinueStatements.message
]);
testExtractRangeFailed("extractRangeFailed9",
`var x = ([#||]1 + 2);`,
[
- refactor.extractSymbol.Messages.CannotExtractEmpty.message
+ refactor.extractSymbol.Messages.cannotExtractEmpty.message
]);
testExtractRangeFailed("extractRangeFailed10",
@@ -333,7 +333,7 @@ switch (x) {
}
`,
[
- refactor.extractSymbol.Messages.CannotExtractRange.message
+ refactor.extractSymbol.Messages.cannotExtractRange.message
]);
testExtractRangeFailed("extractRangeFailed11",
@@ -350,21 +350,21 @@ switch (x) {
}
`,
[
- refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message
+ refactor.extractSymbol.Messages.cannotExtractRangeContainingConditionalBreakOrContinueStatements.message
]);
testExtractRangeFailed("extractRangeFailed12",
`let [#|x|];`,
[
- refactor.extractSymbol.Messages.StatementOrExpressionExpected.message
+ refactor.extractSymbol.Messages.statementOrExpressionExpected.message
]);
testExtractRangeFailed("extractRangeFailed13",
`[#|return;|]`,
[
- refactor.extractSymbol.Messages.CannotExtractRange.message
+ refactor.extractSymbol.Messages.cannotExtractRange.message
]);
- testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, [refactor.extractSymbol.Messages.CannotExtractIdentifier.message]);
+ testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, [refactor.extractSymbol.Messages.cannotExtractIdentifier.message]);
});
}
\ No newline at end of file
diff --git a/src/harness/unittests/extractTestHelpers.ts b/src/harness/unittests/extractTestHelpers.ts
index b8974694cfe..a04f443c3c9 100644
--- a/src/harness/unittests/extractTestHelpers.ts
+++ b/src/harness/unittests/extractTestHelpers.ts
@@ -67,33 +67,27 @@ namespace ts {
}
export const newLineCharacter = "\n";
- export const getRuleProvider = memoize(getRuleProviderInternal);
- function getRuleProviderInternal() {
- const options = {
- indentSize: 4,
- tabSize: 4,
- newLineCharacter,
- convertTabsToSpaces: true,
- indentStyle: ts.IndentStyle.Smart,
- insertSpaceAfterConstructor: false,
- insertSpaceAfterCommaDelimiter: true,
- insertSpaceAfterSemicolonInForStatements: true,
- insertSpaceBeforeAndAfterBinaryOperators: true,
- insertSpaceAfterKeywordsInControlFlowStatements: true,
- insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
- insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
- insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
- insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
- insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
- insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
- insertSpaceBeforeFunctionParenthesis: false,
- placeOpenBraceOnNewLineForFunctions: false,
- placeOpenBraceOnNewLineForControlBlocks: false,
- };
- const rulesProvider = new formatting.RulesProvider();
- rulesProvider.ensureUpToDate(options);
- return rulesProvider;
- }
+ export const testFormatOptions: ts.FormatCodeSettings = {
+ indentSize: 4,
+ tabSize: 4,
+ newLineCharacter,
+ convertTabsToSpaces: true,
+ indentStyle: ts.IndentStyle.Smart,
+ insertSpaceAfterConstructor: false,
+ insertSpaceAfterCommaDelimiter: true,
+ insertSpaceAfterSemicolonInForStatements: true,
+ insertSpaceBeforeAndAfterBinaryOperators: true,
+ insertSpaceAfterKeywordsInControlFlowStatements: true,
+ insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
+ insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
+ insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
+ insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
+ insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
+ insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
+ insertSpaceBeforeFunctionParenthesis: false,
+ placeOpenBraceOnNewLineForFunctions: false,
+ placeOpenBraceOnNewLineForControlBlocks: false,
+ };
const notImplementedHost: LanguageServiceHost = {
getCompilationSettings: notImplemented,
@@ -126,14 +120,14 @@ namespace ts {
const sourceFile = program.getSourceFile(path);
const context: RefactorContext = {
- cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } },
+ cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse },
newLineCharacter,
program,
file: sourceFile,
startPosition: selectionRange.start,
endPosition: selectionRange.end,
host: notImplementedHost,
- rulesProvider: getRuleProvider()
+ formatContext: formatting.getFormatContext(testFormatOptions),
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText);
@@ -190,14 +184,14 @@ namespace ts {
const program = projectService.inferredProjects[0].getLanguageService().getProgram();
const sourceFile = program.getSourceFile(f.path);
const context: RefactorContext = {
- cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } },
+ cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse },
newLineCharacter,
program,
file: sourceFile,
startPosition: selectionRange.start,
endPosition: selectionRange.end,
host: notImplementedHost,
- rulesProvider: getRuleProvider()
+ formatContext: formatting.getFormatContext(testFormatOptions),
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
assert.isUndefined(rangeToExtract.errors, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText);
diff --git a/src/harness/unittests/hostNewLineSupport.ts b/src/harness/unittests/hostNewLineSupport.ts
index 9f6b09dfb72..95a05f101f7 100644
--- a/src/harness/unittests/hostNewLineSupport.ts
+++ b/src/harness/unittests/hostNewLineSupport.ts
@@ -5,10 +5,10 @@ namespace ts {
function snapFor(path: string): IScriptSnapshot {
if (path === "lib.d.ts") {
return {
- dispose() {},
+ dispose: noop,
getChangeRange() { return undefined; },
getLength() { return 0; },
- getText(_start, _end) {
+ getText() {
return "";
}
};
@@ -16,7 +16,7 @@ namespace ts {
const result = forEach(files, f => f.unitName === path ? f : undefined);
if (result) {
return {
- dispose() {},
+ dispose: noop,
getChangeRange() { return undefined; },
getLength() { return result.content.length; },
getText(start, end) {
diff --git a/src/harness/unittests/incrementalParser.ts b/src/harness/unittests/incrementalParser.ts
index fbd8a60da92..c71b89d3da6 100644
--- a/src/harness/unittests/incrementalParser.ts
+++ b/src/harness/unittests/incrementalParser.ts
@@ -591,7 +591,7 @@ module m3 { }\
const index = 0;
const newTextAndChange = withInsert(oldText, index, "declare ");
- compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 3);
+ compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 0);
});
it("Insert function above arrow function with comment", () => {
@@ -674,7 +674,7 @@ module m3 { }\
const oldText = ScriptSnapshot.fromString(source);
const newTextAndChange = withInsert(oldText, 0, "{");
- compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 9);
+ compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 4);
});
it("Removing block around function declarations", () => {
@@ -683,7 +683,7 @@ module m3 { }\
const oldText = ScriptSnapshot.fromString(source);
const newTextAndChange = withDelete(oldText, 0, "{".length);
- compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 9);
+ compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 4);
});
it("Moving methods from class to object literal", () => {
diff --git a/src/harness/unittests/languageService.ts b/src/harness/unittests/languageService.ts
index fd0a95c167f..1407a518617 100644
--- a/src/harness/unittests/languageService.ts
+++ b/src/harness/unittests/languageService.ts
@@ -45,7 +45,7 @@ export function Component(x: Config): any;`
readDirectory: noop as any,
});
const definitions = languageService.getDefinitionAtPosition("foo.ts", 160); // 160 is the latter `vueTemplateHtml` position
- expect(definitions).to.exist;
+ expect(definitions).to.exist; // tslint:disable-line no-unused-expression
});
});
}
\ No newline at end of file
diff --git a/src/harness/unittests/moduleResolution.ts b/src/harness/unittests/moduleResolution.ts
index 32301d6dccf..de79ba06cf9 100644
--- a/src/harness/unittests/moduleResolution.ts
+++ b/src/harness/unittests/moduleResolution.ts
@@ -127,7 +127,7 @@ namespace ts {
function test(hasDirectoryExists: boolean) {
const containingFile = { name: containingFileName };
- const packageJson = { name: packageJsonFileName, content: JSON.stringify({ "typings": fieldRef }) };
+ const packageJson = { name: packageJsonFileName, content: JSON.stringify({ typings: fieldRef }) };
const moduleFile = { name: moduleFileName };
const resolution = nodeModuleNameResolver(moduleName, containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, packageJson, moduleFile));
checkResolvedModule(resolution.resolvedModule, createResolvedModule(moduleFile.name));
@@ -149,7 +149,7 @@ namespace ts {
function test(hasDirectoryExists: boolean) {
const containingFile = { name: "/a/b.ts" };
- const packageJson = { name: "/node_modules/b/package.json", content: JSON.stringify({ "typings": typings }) };
+ const packageJson = { name: "/node_modules/b/package.json", content: JSON.stringify({ typings }) };
const moduleFile = { name: "/a/b.d.ts" };
const indexPath = "/node_modules/b/index.d.ts";
@@ -163,7 +163,7 @@ namespace ts {
it("module name as directory - handle invalid 'typings'", () => {
testTypingsIgnored(["a", "b"]);
- testTypingsIgnored({ "a": "b" });
+ testTypingsIgnored({ a: "b" });
testTypingsIgnored(/*typings*/ true);
testTypingsIgnored(/*typings*/ null); // tslint:disable-line no-null-keyword
testTypingsIgnored(/*typings*/ undefined);
@@ -803,7 +803,7 @@ import b = require("./moduleB");
function test(hasDirectoryExists: boolean) {
const file1: File = { name: "/root/folder1/file1.ts" };
- const file1_1: File = { name: "/root/folder1/file1_1/index.d.ts" };
+ const file1_1: File = { name: "/root/folder1/file1_1/index.d.ts" }; // tslint:disable-line variable-name
const file2: File = { name: "/root/generated/folder1/file2.ts" };
const file3: File = { name: "/root/generated/folder2/file3.ts" };
const host = createModuleResolutionHost(hasDirectoryExists, file1, file1_1, file2, file3);
diff --git a/src/harness/unittests/printer.ts b/src/harness/unittests/printer.ts
index 4aaddce5c00..ad60484d963 100644
--- a/src/harness/unittests/printer.ts
+++ b/src/harness/unittests/printer.ts
@@ -55,6 +55,7 @@ namespace ts {
printsCorrectly("removeComments", { removeComments: true }, printer => printer.printFile(sourceFile));
// github #14948
+ // tslint:disable-next-line no-invalid-template-strings
printsCorrectly("templateLiteral", {}, printer => printer.printFile(createSourceFile("source.ts", "let greeting = `Hi ${name}, how are you?`;", ScriptTarget.ES2017)));
// github #18071
diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts
index e0c0e6f80a8..fdccc8a7795 100644
--- a/src/harness/unittests/reuseProgramStructure.ts
+++ b/src/harness/unittests/reuseProgramStructure.ts
@@ -243,111 +243,111 @@ namespace ts {
];
it("successful if change does not affect imports", () => {
- const program_1 = newProgram(files, ["a.ts"], { target });
- const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
+ const program1 = newProgram(files, ["a.ts"], { target });
+ const program2 = updateProgram(program1, ["a.ts"], { target }, files => {
files[0].text = files[0].text.updateProgram("var x = 100");
});
- assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
- const program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
- const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
+ assert.equal(program1.structureIsReused, StructureIsReused.Completely);
+ const program1Diagnostics = program1.getSemanticDiagnostics(program1.getSourceFile("a.ts"));
+ const program2Diagnostics = program2.getSemanticDiagnostics(program1.getSourceFile("a.ts"));
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
});
it("successful if change does not affect type reference directives", () => {
- const program_1 = newProgram(files, ["a.ts"], { target });
- const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
+ const program1 = newProgram(files, ["a.ts"], { target });
+ const program2 = updateProgram(program1, ["a.ts"], { target }, files => {
files[0].text = files[0].text.updateProgram("var x = 100");
});
- assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
- const program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
- const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
+ assert.equal(program1.structureIsReused, StructureIsReused.Completely);
+ const program1Diagnostics = program1.getSemanticDiagnostics(program1.getSourceFile("a.ts"));
+ const program2Diagnostics = program2.getSemanticDiagnostics(program1.getSourceFile("a.ts"));
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
});
it("fails if change affects tripleslash references", () => {
- const program_1 = newProgram(files, ["a.ts"], { target });
- updateProgram(program_1, ["a.ts"], { target }, files => {
+ const program1 = newProgram(files, ["a.ts"], { target });
+ updateProgram(program1, ["a.ts"], { target }, files => {
const newReferences = `///
///
`;
files[0].text = files[0].text.updateReferences(newReferences);
});
- assert.equal(program_1.structureIsReused, StructureIsReused.SafeModules);
+ assert.equal(program1.structureIsReused, StructureIsReused.SafeModules);
});
it("fails if change affects type references", () => {
- const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
- updateProgram(program_1, ["a.ts"], { types: ["b"] }, noop);
- assert.equal(program_1.structureIsReused, StructureIsReused.Not);
+ const program1 = newProgram(files, ["a.ts"], { types: ["a"] });
+ updateProgram(program1, ["a.ts"], { types: ["b"] }, noop);
+ assert.equal(program1.structureIsReused, StructureIsReused.Not);
});
it("succeeds if change doesn't affect type references", () => {
- const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
- updateProgram(program_1, ["a.ts"], { types: ["a"] }, noop);
- assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
+ const program1 = newProgram(files, ["a.ts"], { types: ["a"] });
+ updateProgram(program1, ["a.ts"], { types: ["a"] }, noop);
+ assert.equal(program1.structureIsReused, StructureIsReused.Completely);
});
it("fails if change affects imports", () => {
- const program_1 = newProgram(files, ["a.ts"], { target });
- updateProgram(program_1, ["a.ts"], { target }, files => {
+ const program1 = newProgram(files, ["a.ts"], { target });
+ updateProgram(program1, ["a.ts"], { target }, files => {
files[2].text = files[2].text.updateImportsAndExports("import x from 'b'");
});
- assert.equal(program_1.structureIsReused, StructureIsReused.SafeModules);
+ assert.equal(program1.structureIsReused, StructureIsReused.SafeModules);
});
it("fails if change affects type directives", () => {
- const program_1 = newProgram(files, ["a.ts"], { target });
- updateProgram(program_1, ["a.ts"], { target }, files => {
+ const program1 = newProgram(files, ["a.ts"], { target });
+ updateProgram(program1, ["a.ts"], { target }, files => {
const newReferences = `
///
///
/// `;
files[0].text = files[0].text.updateReferences(newReferences);
});
- assert.equal(program_1.structureIsReused, StructureIsReused.SafeModules);
+ assert.equal(program1.structureIsReused, StructureIsReused.SafeModules);
});
it("fails if module kind changes", () => {
- const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS });
- updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.AMD }, noop);
- assert.equal(program_1.structureIsReused, StructureIsReused.Not);
+ const program1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS });
+ updateProgram(program1, ["a.ts"], { target, module: ModuleKind.AMD }, noop);
+ assert.equal(program1.structureIsReused, StructureIsReused.Not);
});
it("fails if rootdir changes", () => {
- const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/b" });
- updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/c" }, noop);
- assert.equal(program_1.structureIsReused, StructureIsReused.Not);
+ const program1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/b" });
+ updateProgram(program1, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/c" }, noop);
+ assert.equal(program1.structureIsReused, StructureIsReused.Not);
});
it("fails if config path changes", () => {
- const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/b/tsconfig.json" });
- updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/c/tsconfig.json" }, noop);
- assert.equal(program_1.structureIsReused, StructureIsReused.Not);
+ const program1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/b/tsconfig.json" });
+ updateProgram(program1, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/c/tsconfig.json" }, noop);
+ assert.equal(program1.structureIsReused, StructureIsReused.Not);
});
it("succeeds if missing files remain missing", () => {
const options: CompilerOptions = { target, noLib: true };
- const program_1 = newProgram(files, ["a.ts"], options);
- assert.notDeepEqual(emptyArray, program_1.getMissingFilePaths());
+ const program1 = newProgram(files, ["a.ts"], options);
+ assert.notDeepEqual(emptyArray, program1.getMissingFilePaths());
- const program_2 = updateProgram(program_1, ["a.ts"], options, noop);
- assert.deepEqual(program_1.getMissingFilePaths(), program_2.getMissingFilePaths());
+ const program2 = updateProgram(program1, ["a.ts"], options, noop);
+ assert.deepEqual(program1.getMissingFilePaths(), program2.getMissingFilePaths());
- assert.equal(StructureIsReused.Completely, program_1.structureIsReused);
+ assert.equal(StructureIsReused.Completely, program1.structureIsReused);
});
it("fails if missing file is created", () => {
const options: CompilerOptions = { target, noLib: true };
- const program_1 = newProgram(files, ["a.ts"], options);
- assert.notDeepEqual(emptyArray, program_1.getMissingFilePaths());
+ const program1 = newProgram(files, ["a.ts"], options);
+ assert.notDeepEqual(emptyArray, program1.getMissingFilePaths());
const newTexts: NamedSourceText[] = files.concat([{ name: "non-existing-file.ts", text: SourceText.New("", "", `var x = 1`) }]);
- const program_2 = updateProgram(program_1, ["a.ts"], options, noop, newTexts);
- assert.deepEqual(emptyArray, program_2.getMissingFilePaths());
+ const program2 = updateProgram(program1, ["a.ts"], options, noop, newTexts);
+ assert.deepEqual(emptyArray, program2.getMissingFilePaths());
- assert.equal(StructureIsReused.Not, program_1.structureIsReused);
+ assert.equal(StructureIsReused.Not, program1.structureIsReused);
});
it("resolution cache follows imports", () => {
@@ -359,34 +359,34 @@ namespace ts {
];
const options: CompilerOptions = { target };
- const program_1 = newProgram(files, ["a.ts"], options);
- checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") }));
- checkResolvedModulesCache(program_1, "b.ts", /*expectedContent*/ undefined);
+ const program1 = newProgram(files, ["a.ts"], options);
+ checkResolvedModulesCache(program1, "a.ts", createMapFromTemplate({ b: createResolvedModule("b.ts") }));
+ checkResolvedModulesCache(program1, "b.ts", /*expectedContent*/ undefined);
- const program_2 = updateProgram(program_1, ["a.ts"], options, files => {
+ const program2 = updateProgram(program1, ["a.ts"], options, files => {
files[0].text = files[0].text.updateProgram("var x = 2");
});
- assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
+ assert.equal(program1.structureIsReused, StructureIsReused.Completely);
// content of resolution cache should not change
- checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") }));
- checkResolvedModulesCache(program_1, "b.ts", /*expectedContent*/ undefined);
+ checkResolvedModulesCache(program1, "a.ts", createMapFromTemplate({ b: createResolvedModule("b.ts") }));
+ checkResolvedModulesCache(program1, "b.ts", /*expectedContent*/ undefined);
// imports has changed - program is not reused
- const program_3 = updateProgram(program_2, ["a.ts"], options, files => {
+ const program3 = updateProgram(program2, ["a.ts"], options, files => {
files[0].text = files[0].text.updateImportsAndExports("");
});
- assert.equal(program_2.structureIsReused, StructureIsReused.SafeModules);
- checkResolvedModulesCache(program_3, "a.ts", /*expectedContent*/ undefined);
+ assert.equal(program2.structureIsReused, StructureIsReused.SafeModules);
+ checkResolvedModulesCache(program3, "a.ts", /*expectedContent*/ undefined);
- const program_4 = updateProgram(program_3, ["a.ts"], options, files => {
+ const program4 = updateProgram(program3, ["a.ts"], options, files => {
const newImports = `import x from 'b'
import y from 'c'
`;
files[0].text = files[0].text.updateImportsAndExports(newImports);
});
- assert.equal(program_3.structureIsReused, StructureIsReused.SafeModules);
- checkResolvedModulesCache(program_4, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts"), "c": undefined }));
+ assert.equal(program3.structureIsReused, StructureIsReused.SafeModules);
+ checkResolvedModulesCache(program4, "a.ts", createMapFromTemplate({ b: createResolvedModule("b.ts"), c: undefined }));
});
it("resolved type directives cache follows type directives", () => {
@@ -396,35 +396,35 @@ namespace ts {
];
const options: CompilerOptions = { target, typeRoots: ["/types"] };
- const program_1 = newProgram(files, ["/a.ts"], options);
- checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
- checkResolvedTypeDirectivesCache(program_1, "/types/typedefs/index.d.ts", /*expectedContent*/ undefined);
+ const program1 = newProgram(files, ["/a.ts"], options);
+ checkResolvedTypeDirectivesCache(program1, "/a.ts", createMapFromTemplate({ typedefs: { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
+ checkResolvedTypeDirectivesCache(program1, "/types/typedefs/index.d.ts", /*expectedContent*/ undefined);
- const program_2 = updateProgram(program_1, ["/a.ts"], options, files => {
+ const program2 = updateProgram(program1, ["/a.ts"], options, files => {
files[0].text = files[0].text.updateProgram("var x = 2");
});
- assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
+ assert.equal(program1.structureIsReused, StructureIsReused.Completely);
// content of resolution cache should not change
- checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
- checkResolvedTypeDirectivesCache(program_1, "/types/typedefs/index.d.ts", /*expectedContent*/ undefined);
+ checkResolvedTypeDirectivesCache(program1, "/a.ts", createMapFromTemplate({ typedefs: { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
+ checkResolvedTypeDirectivesCache(program1, "/types/typedefs/index.d.ts", /*expectedContent*/ undefined);
// type reference directives has changed - program is not reused
- const program_3 = updateProgram(program_2, ["/a.ts"], options, files => {
+ const program3 = updateProgram(program2, ["/a.ts"], options, files => {
files[0].text = files[0].text.updateReferences("");
});
- assert.equal(program_2.structureIsReused, StructureIsReused.SafeModules);
- checkResolvedTypeDirectivesCache(program_3, "/a.ts", /*expectedContent*/ undefined);
+ assert.equal(program2.structureIsReused, StructureIsReused.SafeModules);
+ checkResolvedTypeDirectivesCache(program3, "/a.ts", /*expectedContent*/ undefined);
- updateProgram(program_3, ["/a.ts"], options, files => {
+ updateProgram(program3, ["/a.ts"], options, files => {
const newReferences = `///
///
`;
files[0].text = files[0].text.updateReferences(newReferences);
});
- assert.equal(program_3.structureIsReused, StructureIsReused.SafeModules);
- checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
+ assert.equal(program3.structureIsReused, StructureIsReused.SafeModules);
+ checkResolvedTypeDirectivesCache(program1, "/a.ts", createMapFromTemplate({ typedefs: { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
});
it("fetches imports after npm install", () => {
@@ -529,18 +529,18 @@ namespace ts {
"======== Module name 'fs' was not resolved. ========",
], "should look for 'fs'");
- const program_2 = updateProgram(program, program.getRootFileNames(), options, f => {
+ const program2 = updateProgram(program, program.getRootFileNames(), options, f => {
f[0].text = f[0].text.updateProgram("var x = 1;");
});
- assert.deepEqual(program_2.host.getTrace(), [
+ assert.deepEqual(program2.host.getTrace(), [
"Module 'fs' was resolved as ambient module declared in '/a/b/node.d.ts' since this file was not modified."
], "should reuse 'fs' since node.d.ts was not changed");
- const program_3 = updateProgram(program_2, program_2.getRootFileNames(), options, f => {
+ const program3 = updateProgram(program2, program2.getRootFileNames(), options, f => {
f[0].text = f[0].text.updateProgram("var y = 1;");
f[1].text = f[1].text.updateProgram("declare var process: any");
});
- assert.deepEqual(program_3.host.getTrace(),
+ assert.deepEqual(program3.host.getTrace(),
[
"======== Resolving module 'fs' from '/a/b/app.ts'. ========",
"Module resolution kind is not specified, using 'Classic'.",
@@ -598,10 +598,10 @@ namespace ts {
];
const options: CompilerOptions = { target: ScriptTarget.ES2015, traceResolution: true, moduleResolution: ModuleResolutionKind.Classic };
- const program_1 = newProgram(files, files.map(f => f.name), options);
+ const program1 = newProgram(files, files.map(f => f.name), options);
let expectedErrors = 0;
{
- assert.deepEqual(program_1.host.getTrace(),
+ assert.deepEqual(program1.host.getTrace(),
[
"======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
@@ -626,22 +626,22 @@ namespace ts {
"File 'f1.ts' exist - use it as a name resolution result.",
"======== Module name './f1' was successfully resolved to 'f1.ts'. ========"
],
- "program_1: execute module resolution normally.");
+ "program1: execute module resolution normally.");
- const program_1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("f2.ts"));
- assert.lengthOf(program_1Diagnostics, expectedErrors, `initial program should be well-formed`);
+ const program1Diagnostics = program1.getSemanticDiagnostics(program1.getSourceFile("f2.ts"));
+ assert.lengthOf(program1Diagnostics, expectedErrors, `initial program should be well-formed`);
}
const indexOfF1 = 6;
- const program_2 = updateProgram(program_1, program_1.getRootFileNames(), options, f => {
+ const program2 = updateProgram(program1, program1.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateReferences(`/// ${newLine}/// `);
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
- const program_2Diagnostics = program_2.getSemanticDiagnostics(program_2.getSourceFile("f2.ts"));
- assert.lengthOf(program_2Diagnostics, expectedErrors, `removing no-default-lib shouldn't affect any types used.`);
+ const program2Diagnostics = program2.getSemanticDiagnostics(program2.getSourceFile("f2.ts"));
+ assert.lengthOf(program2Diagnostics, expectedErrors, `removing no-default-lib shouldn't affect any types used.`);
- assert.deepEqual(program_2.host.getTrace(), [
+ assert.deepEqual(program2.host.getTrace(), [
"======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs1/package.json' does not exist.",
@@ -658,19 +658,19 @@ namespace ts {
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
- ], "program_2: reuse module resolutions in f2 since it is unchanged");
+ ], "program2: reuse module resolutions in f2 since it is unchanged");
}
- const program_3 = updateProgram(program_2, program_2.getRootFileNames(), options, f => {
+ const program3 = updateProgram(program2, program2.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateReferences(`/// `);
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
- const program_3Diagnostics = program_3.getSemanticDiagnostics(program_3.getSourceFile("f2.ts"));
- assert.lengthOf(program_3Diagnostics, expectedErrors, `typerefs2 was unused, so diagnostics should be unaffected.`);
+ const program3Diagnostics = program3.getSemanticDiagnostics(program3.getSourceFile("f2.ts"));
+ assert.lengthOf(program3Diagnostics, expectedErrors, `typerefs2 was unused, so diagnostics should be unaffected.`);
- assert.deepEqual(program_3.host.getTrace(), [
+ assert.deepEqual(program3.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
@@ -682,20 +682,20 @@ namespace ts {
"======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
"Reusing resolution of module './b2' to file 'f2.ts' from old program.",
"Reusing resolution of module './f1' to file 'f2.ts' from old program."
- ], "program_3: reuse module resolutions in f2 since it is unchanged");
+ ], "program3: reuse module resolutions in f2 since it is unchanged");
}
- const program_4 = updateProgram(program_3, program_3.getRootFileNames(), options, f => {
+ const program4 = updateProgram(program3, program3.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateReferences("");
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
- const program_4Diagnostics = program_4.getSemanticDiagnostics(program_4.getSourceFile("f2.ts"));
- assert.lengthOf(program_4Diagnostics, expectedErrors, `a1.ts was unused, so diagnostics should be unaffected.`);
+ const program4Diagnostics = program4.getSemanticDiagnostics(program4.getSourceFile("f2.ts"));
+ assert.lengthOf(program4Diagnostics, expectedErrors, `a1.ts was unused, so diagnostics should be unaffected.`);
- assert.deepEqual(program_4.host.getTrace(), [
+ assert.deepEqual(program4.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
@@ -710,16 +710,16 @@ namespace ts {
], "program_4: reuse module resolutions in f2 since it is unchanged");
}
- const program_5 = updateProgram(program_4, program_4.getRootFileNames(), options, f => {
+ const program5 = updateProgram(program4, program4.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateImportsAndExports(`import { B } from './b1';`);
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
- const program_5Diagnostics = program_5.getSemanticDiagnostics(program_5.getSourceFile("f2.ts"));
- assert.lengthOf(program_5Diagnostics, ++expectedErrors, `import of BB in f1 fails. BB is of type any. Add one error`);
+ const program5Diagnostics = program5.getSemanticDiagnostics(program5.getSourceFile("f2.ts"));
+ assert.lengthOf(program5Diagnostics, ++expectedErrors, `import of BB in f1 fails. BB is of type any. Add one error`);
- assert.deepEqual(program_5.host.getTrace(), [
+ assert.deepEqual(program5.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
@@ -727,16 +727,16 @@ namespace ts {
], "program_5: exports do not affect program structure, so f2's resolutions are silently reused.");
}
- const program_6 = updateProgram(program_5, program_5.getRootFileNames(), options, f => {
+ const program6 = updateProgram(program5, program5.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateProgram("");
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
- const program_6Diagnostics = program_6.getSemanticDiagnostics(program_6.getSourceFile("f2.ts"));
- assert.lengthOf(program_6Diagnostics, expectedErrors, `import of BB in f1 fails.`);
+ const program6Diagnostics = program6.getSemanticDiagnostics(program6.getSourceFile("f2.ts"));
+ assert.lengthOf(program6Diagnostics, expectedErrors, `import of BB in f1 fails.`);
- assert.deepEqual(program_6.host.getTrace(), [
+ assert.deepEqual(program6.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
"Explicitly specified module resolution kind: 'Classic'.",
"File 'b1.ts' exist - use it as a name resolution result.",
@@ -751,16 +751,16 @@ namespace ts {
], "program_6: reuse module resolutions in f2 since it is unchanged");
}
- const program_7 = updateProgram(program_6, program_6.getRootFileNames(), options, f => {
+ const program7 = updateProgram(program6, program6.getRootFileNames(), options, f => {
const newSourceText = f[indexOfF1].text.updateImportsAndExports("");
f[indexOfF1] = { name: "f1.ts", text: newSourceText };
});
{
- const program_7Diagnostics = program_7.getSemanticDiagnostics(program_7.getSourceFile("f2.ts"));
- assert.lengthOf(program_7Diagnostics, expectedErrors, `removing import is noop with respect to program, so no change in diagnostics.`);
+ const program7Diagnostics = program7.getSemanticDiagnostics(program7.getSourceFile("f2.ts"));
+ assert.lengthOf(program7Diagnostics, expectedErrors, `removing import is noop with respect to program, so no change in diagnostics.`);
- assert.deepEqual(program_7.host.getTrace(), [
+ assert.deepEqual(program7.host.getTrace(), [
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
"Resolving with primary search path 'node_modules/@types'.",
"File 'node_modules/@types/typerefs2/package.json' does not exist.",
@@ -820,47 +820,47 @@ namespace ts {
}
it("No changes -> redirect not broken", () => {
- const program_1 = createRedirectProgram();
+ const program1 = createRedirectProgram();
- const program_2 = updateRedirectProgram(program_1, files => {
+ const program2 = updateRedirectProgram(program1, files => {
updateProgramText(files, root, "const x = 1;");
});
- assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
- assert.deepEqual(program_2.getSemanticDiagnostics(), emptyArray);
+ assert.equal(program1.structureIsReused, StructureIsReused.Completely);
+ assert.deepEqual(program2.getSemanticDiagnostics(), emptyArray);
});
it("Target changes -> redirect broken", () => {
- const program_1 = createRedirectProgram();
- assert.deepEqual(program_1.getSemanticDiagnostics(), emptyArray);
+ const program1 = createRedirectProgram();
+ assert.deepEqual(program1.getSemanticDiagnostics(), emptyArray);
- const program_2 = updateRedirectProgram(program_1, files => {
+ const program2 = updateRedirectProgram(program1, files => {
updateProgramText(files, axIndex, "export default class X { private x: number; private y: number; }");
updateProgramText(files, axPackage, JSON.stringify('{ name: "x", version: "1.2.4" }'));
});
- assert.equal(program_1.structureIsReused, StructureIsReused.Not);
- assert.lengthOf(program_2.getSemanticDiagnostics(), 1);
+ assert.equal(program1.structureIsReused, StructureIsReused.Not);
+ assert.lengthOf(program2.getSemanticDiagnostics(), 1);
});
it("Underlying changes -> redirect broken", () => {
- const program_1 = createRedirectProgram();
+ const program1 = createRedirectProgram();
- const program_2 = updateRedirectProgram(program_1, files => {
+ const program2 = updateRedirectProgram(program1, files => {
updateProgramText(files, bxIndex, "export default class X { private x: number; private y: number; }");
updateProgramText(files, bxPackage, JSON.stringify({ name: "x", version: "1.2.4" }));
});
- assert.equal(program_1.structureIsReused, StructureIsReused.Not);
- assert.lengthOf(program_2.getSemanticDiagnostics(), 1);
+ assert.equal(program1.structureIsReused, StructureIsReused.Not);
+ assert.lengthOf(program2.getSemanticDiagnostics(), 1);
});
it("Previously duplicate packages -> program structure not reused", () => {
- const program_1 = createRedirectProgram({ bVersion: "1.2.4", bText: "export = class X { private x: number; }" });
+ const program1 = createRedirectProgram({ bVersion: "1.2.4", bText: "export = class X { private x: number; }" });
- const program_2 = updateRedirectProgram(program_1, files => {
+ const program2 = updateRedirectProgram(program1, files => {
updateProgramText(files, bxIndex, "export default class X { private x: number; }");
updateProgramText(files, bxPackage, JSON.stringify({ name: "x", version: "1.2.3" }));
});
- assert.equal(program_1.structureIsReused, StructureIsReused.Not);
- assert.deepEqual(program_2.getSemanticDiagnostics(), []);
+ assert.equal(program1.structureIsReused, StructureIsReused.Not);
+ assert.deepEqual(program2.getSemanticDiagnostics(), []);
});
});
});
diff --git a/src/harness/unittests/services/colorization.ts b/src/harness/unittests/services/colorization.ts
index eb2606bb08e..17d4132e9e5 100644
--- a/src/harness/unittests/services/colorization.ts
+++ b/src/harness/unittests/services/colorization.ts
@@ -1,12 +1,14 @@
///
+// tslint:disable no-invalid-template-strings (lots of tests use quoted code)
+
interface ClassificationEntry {
value: any;
classification: ts.TokenClass;
position?: number;
}
-describe("Colorization", function () {
+describe("Colorization", () => {
// Use the shim adapter to ensure test coverage of the shim layer for the classifier
const languageServiceAdapter = new Harness.LanguageService.ShimLanguageServiceAdapter(/*preprocessToResolve*/ false);
const classifier = languageServiceAdapter.getClassifier();
@@ -55,8 +57,8 @@ describe("Colorization", function () {
}
}
- describe("test getClassifications", function () {
- it("Returns correct token classes", function () {
+ describe("test getClassifications", () => {
+ it("Returns correct token classes", () => {
testLexicalClassification("var x: string = \"foo\"; //Hello",
ts.EndOfLineState.None,
keyword("var"),
@@ -70,7 +72,7 @@ describe("Colorization", function () {
punctuation(";"));
});
- it("correctly classifies a comment after a divide operator", function () {
+ it("correctly classifies a comment after a divide operator", () => {
testLexicalClassification("1 / 2 // comment",
ts.EndOfLineState.None,
numberLiteral("1"),
@@ -80,7 +82,7 @@ describe("Colorization", function () {
comment("// comment"));
});
- it("correctly classifies a literal after a divide operator", function () {
+ it("correctly classifies a literal after a divide operator", () => {
testLexicalClassification("1 / 2, 3 / 4",
ts.EndOfLineState.None,
numberLiteral("1"),
@@ -92,131 +94,131 @@ describe("Colorization", function () {
operator(","));
});
- it("correctly classifies a multi-line string with one backslash", function () {
+ it("correctly classifies a multi-line string with one backslash", () => {
testLexicalClassification("'line1\\",
ts.EndOfLineState.None,
stringLiteral("'line1\\"),
finalEndOfLineState(ts.EndOfLineState.InSingleQuoteStringLiteral));
});
- it("correctly classifies a multi-line string with three backslashes", function () {
+ it("correctly classifies a multi-line string with three backslashes", () => {
testLexicalClassification("'line1\\\\\\",
ts.EndOfLineState.None,
stringLiteral("'line1\\\\\\"),
finalEndOfLineState(ts.EndOfLineState.InSingleQuoteStringLiteral));
});
- it("correctly classifies an unterminated single-line string with no backslashes", function () {
+ it("correctly classifies an unterminated single-line string with no backslashes", () => {
testLexicalClassification("'line1",
ts.EndOfLineState.None,
stringLiteral("'line1"),
finalEndOfLineState(ts.EndOfLineState.None));
});
- it("correctly classifies an unterminated single-line string with two backslashes", function () {
+ it("correctly classifies an unterminated single-line string with two backslashes", () => {
testLexicalClassification("'line1\\\\",
ts.EndOfLineState.None,
stringLiteral("'line1\\\\"),
finalEndOfLineState(ts.EndOfLineState.None));
});
- it("correctly classifies an unterminated single-line string with four backslashes", function () {
+ it("correctly classifies an unterminated single-line string with four backslashes", () => {
testLexicalClassification("'line1\\\\\\\\",
ts.EndOfLineState.None,
stringLiteral("'line1\\\\\\\\"),
finalEndOfLineState(ts.EndOfLineState.None));
});
- it("correctly classifies the continuing line of a multi-line string ending in one backslash", function () {
+ it("correctly classifies the continuing line of a multi-line string ending in one backslash", () => {
testLexicalClassification("\\",
ts.EndOfLineState.InDoubleQuoteStringLiteral,
stringLiteral("\\"),
finalEndOfLineState(ts.EndOfLineState.InDoubleQuoteStringLiteral));
});
- it("correctly classifies the continuing line of a multi-line string ending in three backslashes", function () {
+ it("correctly classifies the continuing line of a multi-line string ending in three backslashes", () => {
testLexicalClassification("\\",
ts.EndOfLineState.InDoubleQuoteStringLiteral,
stringLiteral("\\"),
finalEndOfLineState(ts.EndOfLineState.InDoubleQuoteStringLiteral));
});
- it("correctly classifies the last line of an unterminated multi-line string ending in no backslashes", function () {
+ it("correctly classifies the last line of an unterminated multi-line string ending in no backslashes", () => {
testLexicalClassification(" ",
ts.EndOfLineState.InDoubleQuoteStringLiteral,
stringLiteral(" "),
finalEndOfLineState(ts.EndOfLineState.None));
});
- it("correctly classifies the last line of an unterminated multi-line string ending in two backslashes", function () {
+ it("correctly classifies the last line of an unterminated multi-line string ending in two backslashes", () => {
testLexicalClassification("\\\\",
ts.EndOfLineState.InDoubleQuoteStringLiteral,
stringLiteral("\\\\"),
finalEndOfLineState(ts.EndOfLineState.None));
});
- it("correctly classifies the last line of an unterminated multi-line string ending in four backslashes", function () {
+ it("correctly classifies the last line of an unterminated multi-line string ending in four backslashes", () => {
testLexicalClassification("\\\\\\\\",
ts.EndOfLineState.InDoubleQuoteStringLiteral,
stringLiteral("\\\\\\\\"),
finalEndOfLineState(ts.EndOfLineState.None));
});
- it("correctly classifies the last line of a multi-line string", function () {
+ it("correctly classifies the last line of a multi-line string", () => {
testLexicalClassification("'",
ts.EndOfLineState.InSingleQuoteStringLiteral,
stringLiteral("'"),
finalEndOfLineState(ts.EndOfLineState.None));
});
- it("correctly classifies an unterminated multiline comment", function () {
+ it("correctly classifies an unterminated multiline comment", () => {
testLexicalClassification("/*",
ts.EndOfLineState.None,
comment("/*"),
finalEndOfLineState(ts.EndOfLineState.InMultiLineCommentTrivia));
});
- it("correctly classifies the termination of a multiline comment", function () {
+ it("correctly classifies the termination of a multiline comment", () => {
testLexicalClassification(" */ ",
ts.EndOfLineState.InMultiLineCommentTrivia,
comment(" */"),
finalEndOfLineState(ts.EndOfLineState.None));
});
- it("correctly classifies the continuation of a multiline comment", function () {
+ it("correctly classifies the continuation of a multiline comment", () => {
testLexicalClassification("LOREM IPSUM DOLOR ",
ts.EndOfLineState.InMultiLineCommentTrivia,
comment("LOREM IPSUM DOLOR "),
finalEndOfLineState(ts.EndOfLineState.InMultiLineCommentTrivia));
});
- it("correctly classifies an unterminated multiline comment on a line ending in '/*/'", function () {
+ it("correctly classifies an unterminated multiline comment on a line ending in '/*/'", () => {
testLexicalClassification(" /*/",
ts.EndOfLineState.None,
comment("/*/"),
finalEndOfLineState(ts.EndOfLineState.InMultiLineCommentTrivia));
});
- it("correctly classifies an unterminated multiline comment with trailing space", function () {
+ it("correctly classifies an unterminated multiline comment with trailing space", () => {
testLexicalClassification("/* ",
ts.EndOfLineState.None,
comment("/* "),
finalEndOfLineState(ts.EndOfLineState.InMultiLineCommentTrivia));
});
- it("correctly classifies a keyword after a dot", function () {
+ it("correctly classifies a keyword after a dot", () => {
testLexicalClassification("a.var",
ts.EndOfLineState.None,
identifier("var"));
});
- it("correctly classifies a string literal after a dot", function () {
+ it("correctly classifies a string literal after a dot", () => {
testLexicalClassification("a.\"var\"",
ts.EndOfLineState.None,
stringLiteral("\"var\""));
});
- it("correctly classifies a keyword after a dot separated by comment trivia", function () {
+ it("correctly classifies a keyword after a dot separated by comment trivia", () => {
testLexicalClassification("a./*hello world*/ var",
ts.EndOfLineState.None,
identifier("a"),
@@ -225,21 +227,21 @@ describe("Colorization", function () {
identifier("var"));
});
- it("classifies a property access with whitespace around the dot", function () {
+ it("classifies a property access with whitespace around the dot", () => {
testLexicalClassification(" x .\tfoo ()",
ts.EndOfLineState.None,
identifier("x"),
identifier("foo"));
});
- it("classifies a keyword after a dot on previous line", function () {
+ it("classifies a keyword after a dot on previous line", () => {
testLexicalClassification("var",
ts.EndOfLineState.None,
keyword("var"),
finalEndOfLineState(ts.EndOfLineState.None));
});
- it("classifies multiple keywords properly", function () {
+ it("classifies multiple keywords properly", () => {
testLexicalClassification("public static",
ts.EndOfLineState.None,
keyword("public"),
@@ -353,7 +355,7 @@ describe("Colorization", function () {
}
});
- it("classifies partially written generics correctly.", function () {
+ it("classifies partially written generics correctly.", () => {
testLexicalClassification("Foo {
testLexicalClassification("for (var of of of) { }",
ts.EndOfLineState.None,
keyword("for"),
diff --git a/src/harness/unittests/services/patternMatcher.ts b/src/harness/unittests/services/patternMatcher.ts
index 728636e9af2..fef382e8a3b 100644
--- a/src/harness/unittests/services/patternMatcher.ts
+++ b/src/harness/unittests/services/patternMatcher.ts
@@ -1,7 +1,7 @@
///
-describe("PatternMatcher", function () {
- describe("BreakIntoCharacterSpans", function () {
+describe("PatternMatcher", () => {
+ describe("BreakIntoCharacterSpans", () => {
it("EmptyIdentifier", () => {
verifyBreakIntoCharacterSpans("");
});
@@ -55,7 +55,7 @@ describe("PatternMatcher", function () {
});
});
- describe("BreakIntoWordSpans", function () {
+ describe("BreakIntoWordSpans", () => {
it("VarbatimIdentifier", () => {
verifyBreakIntoWordSpans("@int:", "int");
});
diff --git a/src/harness/unittests/services/preProcessFile.ts b/src/harness/unittests/services/preProcessFile.ts
index 6e77c27e4a8..1e13bc3e345 100644
--- a/src/harness/unittests/services/preProcessFile.ts
+++ b/src/harness/unittests/services/preProcessFile.ts
@@ -1,6 +1,6 @@
///
-describe("PreProcessFile:", function () {
+describe("PreProcessFile:", () => {
function test(sourceText: string, readImportFile: boolean, detectJavaScriptImports: boolean, expectedPreProcess: ts.PreProcessedFileInfo): void {
const resultPreProcess = ts.preProcessFile(sourceText, readImportFile, detectJavaScriptImports);
@@ -31,8 +31,8 @@ describe("PreProcessFile:", function () {
}
}
- describe("Test preProcessFiles,", function () {
- it("Correctly return referenced files from triple slash", function () {
+ describe("Test preProcessFiles,", () => {
+ it("Correctly return referenced files from triple slash", () => {
test("///" + "\n" + "///" + "\n" + "///" + "\n" + "///",
/*readImportFile*/ true,
/*detectJavaScriptImports*/ false,
@@ -46,7 +46,7 @@ describe("PreProcessFile:", function () {
});
}),
- it("Do not return reference path because of invalid triple-slash syntax", function () {
+ it("Do not return reference path because of invalid triple-slash syntax", () => {
test("///" + "\n" + "///" + "\n" + "///" + "\n" + "///",
/*readImportFile*/ true,
/*detectJavaScriptImports*/ false,
@@ -59,7 +59,7 @@ describe("PreProcessFile:", function () {
});
}),
- it("Correctly return imported files", function () {
+ it("Correctly return imported files", () => {
test("import i1 = require(\"r1.ts\"); import i2 =require(\"r2.ts\"); import i3= require(\"r3.ts\"); import i4=require(\"r4.ts\"); import i5 = require (\"r5.ts\");",
/*readImportFile*/ true,
/*detectJavaScriptImports*/ false,
@@ -73,7 +73,7 @@ describe("PreProcessFile:", function () {
});
}),
- it("Do not return imported files if readImportFiles argument is false", function () {
+ it("Do not return imported files if readImportFiles argument is false", () => {
test("import i1 = require(\"r1.ts\"); import i2 =require(\"r2.ts\"); import i3= require(\"r3.ts\"); import i4=require(\"r4.ts\"); import i5 = require (\"r5.ts\");",
/*readImportFile*/ false,
/*detectJavaScriptImports*/ false,
@@ -86,7 +86,7 @@ describe("PreProcessFile:", function () {
});
}),
- it("Do not return import path because of invalid import syntax", function () {
+ it("Do not return import path because of invalid import syntax", () => {
test("import i1 require(\"r1.ts\"); import = require(\"r2.ts\") import i3= require(\"r3.ts\"); import i5",
/*readImportFile*/ true,
/*detectJavaScriptImports*/ false,
@@ -99,7 +99,7 @@ describe("PreProcessFile:", function () {
});
}),
- it("Correctly return referenced files and import files", function () {
+ it("Correctly return referenced files and import files", () => {
test("///" + "\n" + "///" + "\n" + "import i1 = require(\"r1.ts\"); import i2 =require(\"r2.ts\");",
/*readImportFile*/ true,
/*detectJavaScriptImports*/ false,
@@ -112,7 +112,7 @@ describe("PreProcessFile:", function () {
});
}),
- it("Correctly return referenced files and import files even with some invalid syntax", function () {
+ it("Correctly return referenced files and import files even with some invalid syntax", () => {
test("///" + "\n" + "///" + "\n" + "import i1 = require(\"r1.ts\"); import = require(\"r2.ts\"); import i2 = require(\"r3.ts\");",
/*readImportFile*/ true,
/*detectJavaScriptImports*/ false,
@@ -125,7 +125,7 @@ describe("PreProcessFile:", function () {
});
});
- it("Correctly return ES6 imports", function () {
+ it("Correctly return ES6 imports", () => {
test("import * as ns from \"m1\";" + "\n" +
"import def, * as ns from \"m2\";" + "\n" +
"import def from \"m3\";" + "\n" +
@@ -152,7 +152,7 @@ describe("PreProcessFile:", function () {
});
});
- it("Correctly return ES6 exports", function () {
+ it("Correctly return ES6 exports", () => {
test("export * from \"m1\";" + "\n" +
"export {a} from \"m2\";" + "\n" +
"export {a as A} from \"m3\";" + "\n" +
@@ -192,7 +192,7 @@ describe("PreProcessFile:", function () {
});
});
- it("Correctly handles export import declarations", function () {
+ it("Correctly handles export import declarations", () => {
test("export import a = require(\"m1\");",
/*readImportFile*/ true,
/*detectJavaScriptImports*/ false,
@@ -206,7 +206,7 @@ describe("PreProcessFile:", function () {
isLibFile: false
});
});
- it("Correctly handles export require calls in JavaScript files", function () {
+ it("Correctly handles export require calls in JavaScript files", () => {
test(`
export import a = require("m1");
var x = require('m2');
@@ -228,7 +228,7 @@ describe("PreProcessFile:", function () {
isLibFile: false
});
});
- it("Correctly handles dependency lists in define([deplist]) calls in JavaScript files", function () {
+ it("Correctly handles dependency lists in define([deplist]) calls in JavaScript files", () => {
test(`
define(["mod1", "mod2"], (m1, m2) => {
});
@@ -246,7 +246,7 @@ describe("PreProcessFile:", function () {
isLibFile: false
});
});
- it("Correctly handles dependency lists in define(modName, [deplist]) calls in JavaScript files", function () {
+ it("Correctly handles dependency lists in define(modName, [deplist]) calls in JavaScript files", () => {
test(`
define("mod", ["mod1", "mod2"], (m1, m2) => {
});
@@ -278,7 +278,7 @@ describe("PreProcessFile:", function () {
referencedFiles: [],
typeReferenceDirectives: [],
importedFiles: [
- { "fileName": "../Observable", "pos": 28, "end": 41 }
+ { fileName: "../Observable", pos: 28, end: 41 }
],
ambientExternalModules: undefined,
isLibFile: false
@@ -298,8 +298,8 @@ describe("PreProcessFile:", function () {
referencedFiles: [],
typeReferenceDirectives: [],
importedFiles: [
- { "fileName": "m", "pos": 123, "end": 124 },
- { "fileName": "../Observable", "pos": 28, "end": 41 }
+ { fileName: "m", pos: 123, end: 124 },
+ { fileName: "../Observable", pos: 28, end: 41 }
],
ambientExternalModules: undefined,
isLibFile: false
@@ -319,8 +319,8 @@ describe("PreProcessFile:", function () {
referencedFiles: [],
typeReferenceDirectives: [],
importedFiles: [
- { "fileName": "m", "pos": 123, "end": 124 },
- { "fileName": "../Observable", "pos": 28, "end": 41 }
+ { fileName: "m", pos: 123, end: 124 },
+ { fileName: "../Observable", pos: 28, end: 41 }
],
ambientExternalModules: undefined,
isLibFile: false
@@ -340,7 +340,7 @@ describe("PreProcessFile:", function () {
referencedFiles: [],
typeReferenceDirectives: [],
importedFiles: [
- { "fileName": "../Observable", "pos": 28, "end": 41 }
+ { fileName: "../Observable", pos: 28, end: 41 }
],
ambientExternalModules: undefined,
isLibFile: false
@@ -360,7 +360,7 @@ describe("PreProcessFile:", function () {
referencedFiles: [],
typeReferenceDirectives: [],
importedFiles: [
- { "fileName": "../Observable", "pos": 28, "end": 41 }
+ { fileName: "../Observable", pos: 28, end: 41 }
],
ambientExternalModules: undefined,
isLibFile: false
@@ -379,7 +379,7 @@ describe("PreProcessFile:", function () {
referencedFiles: [],
typeReferenceDirectives: [],
importedFiles: [
- { "fileName": "../Observable", "pos": 28, "end": 41 }
+ { fileName: "../Observable", pos: 28, end: 41 }
],
ambientExternalModules: undefined,
isLibFile: false
@@ -400,8 +400,8 @@ describe("PreProcessFile:", function () {
referencedFiles: [],
typeReferenceDirectives: [],
importedFiles: [
- { "fileName": "m2", "pos": 65, "end": 67 },
- { "fileName": "augmentation", "pos": 102, "end": 114 }
+ { fileName: "m2", pos: 65, end: 67 },
+ { fileName: "augmentation", pos: 102, end: 114 }
],
ambientExternalModules: ["m1"],
isLibFile: false
@@ -424,8 +424,8 @@ describe("PreProcessFile:", function () {
referencedFiles: [],
typeReferenceDirectives: [],
importedFiles: [
- { "fileName": "m2", "pos": 127, "end": 129 },
- { "fileName": "augmentation", "pos": 164, "end": 176 }
+ { fileName: "m2", pos: 127, end: 129 },
+ { fileName: "augmentation", pos: 164, end: 176 }
],
ambientExternalModules: ["m1"],
isLibFile: false
@@ -442,12 +442,12 @@ describe("PreProcessFile:", function () {
/*detectJavaScriptImports*/ false,
{
referencedFiles: [
- { "pos": 34, "end": 35, "fileName": "a" },
- { "pos": 112, "end": 114, "fileName": "a2" }
+ { pos: 34, end: 35, fileName: "a" },
+ { pos: 112, end: 114, fileName: "a2" }
],
typeReferenceDirectives: [
- { "pos": 73, "end": 75, "fileName": "a1" },
- { "pos": 152, "end": 154, "fileName": "a3" }
+ { pos: 73, end: 75, fileName: "a1" },
+ { pos: 152, end: 154, fileName: "a3" }
],
importedFiles: [],
ambientExternalModules: undefined,
diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts
index 3d9f782b2e0..871d4a37b9a 100644
--- a/src/harness/unittests/session.ts
+++ b/src/harness/unittests/session.ts
@@ -128,7 +128,7 @@ namespace ts.server {
body: undefined
});
});
- it ("should handle literal types in request", () => {
+ it("should handle literal types in request", () => {
const configureRequest: protocol.ConfigureRequest = {
command: CommandNames.Configure,
seq: 0,
@@ -186,6 +186,8 @@ namespace ts.server {
CommandNames.Configure,
CommandNames.Definition,
CommandNames.DefinitionFull,
+ CommandNames.DefinitionAndBoundSpan,
+ CommandNames.DefinitionAndBoundSpanFull,
CommandNames.Implementation,
CommandNames.ImplementationFull,
CommandNames.Exit,
@@ -315,7 +317,7 @@ namespace ts.server {
session.send = Session.prototype.send;
assert(session.send);
- expect(session.send(msg)).to.not.exist;
+ expect(session.send(msg)).to.not.exist; // tslint:disable-line no-unused-expression
expect(lastWrittenToHost).to.equal(resultMsg);
});
});
@@ -352,7 +354,7 @@ namespace ts.server {
session.addProtocolHandler(command, () => resp);
expect(() => session.addProtocolHandler(command, () => resp))
- .to.throw(`Protocol handler already exists for command "${command}"`);
+ .to.throw(`Protocol handler already exists for command "${command}"`);
});
});
@@ -522,14 +524,14 @@ namespace ts.server {
});
});
it("has access to the project service", () => {
- class ServiceSession extends TestSession {
+ // tslint:disable-next-line no-unused-expression
+ new class extends TestSession {
constructor() {
super();
assert(this.projectService);
expect(this.projectService).to.be.instanceOf(ProjectService);
}
- }
- new ServiceSession();
+ }();
});
});
diff --git a/src/harness/unittests/textChanges.ts b/src/harness/unittests/textChanges.ts
index c37e50a2565..ed571b37399 100644
--- a/src/harness/unittests/textChanges.ts
+++ b/src/harness/unittests/textChanges.ts
@@ -23,60 +23,8 @@ namespace ts {
const printerOptions = { newLine: NewLineKind.LineFeed };
const newLineCharacter = getNewLineCharacter(printerOptions);
- const getRuleProviderDefault = memoize(() => {
- const options = {
- indentSize: 4,
- tabSize: 4,
- newLineCharacter,
- convertTabsToSpaces: true,
- indentStyle: ts.IndentStyle.Smart,
- insertSpaceAfterConstructor: false,
- insertSpaceAfterCommaDelimiter: true,
- insertSpaceAfterSemicolonInForStatements: true,
- insertSpaceBeforeAndAfterBinaryOperators: true,
- insertSpaceAfterKeywordsInControlFlowStatements: true,
- insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
- insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
- insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
- insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
- insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
- insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
- insertSpaceBeforeFunctionParenthesis: false,
- placeOpenBraceOnNewLineForFunctions: false,
- placeOpenBraceOnNewLineForControlBlocks: false,
- };
- const rulesProvider = new formatting.RulesProvider();
- rulesProvider.ensureUpToDate(options);
- return rulesProvider;
- });
- const getRuleProviderNewlineBrace = memoize(() => {
- const options = {
- indentSize: 4,
- tabSize: 4,
- newLineCharacter,
- convertTabsToSpaces: true,
- indentStyle: ts.IndentStyle.Smart,
- insertSpaceAfterConstructor: false,
- insertSpaceAfterCommaDelimiter: true,
- insertSpaceAfterSemicolonInForStatements: true,
- insertSpaceBeforeAndAfterBinaryOperators: true,
- insertSpaceAfterKeywordsInControlFlowStatements: true,
- insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
- insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
- insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
- insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
- insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
- insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
- insertSpaceBeforeFunctionParenthesis: false,
- placeOpenBraceOnNewLineForFunctions: true,
- placeOpenBraceOnNewLineForControlBlocks: false,
- };
- const rulesProvider = new formatting.RulesProvider();
- rulesProvider.ensureUpToDate(options);
- return rulesProvider;
- });
- function getRuleProvider(placeOpenBraceOnNewLineForFunctions: boolean) {
- return placeOpenBraceOnNewLineForFunctions ? getRuleProviderNewlineBrace() : getRuleProviderDefault();
+ function getRuleProvider(placeOpenBraceOnNewLineForFunctions: boolean): formatting.FormatContext {
+ return formatting.getFormatContext(placeOpenBraceOnNewLineForFunctions ? { ...testFormatOptions, placeOpenBraceOnNewLineForFunctions: true } : testFormatOptions);
}
// validate that positions that were recovered from the printed text actually match positions that will be created if the same text is parsed.
@@ -122,9 +70,9 @@ namespace ts {
{
const text = `
-namespace M
+namespace M
{
- namespace M2
+ namespace M2
{
function foo() {
// comment 1
@@ -572,7 +520,7 @@ const x = 1;`;
}
{
const text = `
-const x = 1,
+const x = 1,
y = 2;`;
runSingleFileTest("insertNodeInListAfter6", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), createVariableDeclaration("z", /*type*/ undefined, createLiteral(1)));
@@ -583,7 +531,7 @@ const x = 1,
}
{
const text = `
-const /*x*/ x = 1,
+const /*x*/ x = 1,
/*y*/ y = 2;`;
runSingleFileTest("insertNodeInListAfter8", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), createVariableDeclaration("z", /*type*/ undefined, createLiteral(1)));
diff --git a/src/harness/unittests/transform.ts b/src/harness/unittests/transform.ts
index bcdca3e3b60..a39249bb225 100644
--- a/src/harness/unittests/transform.ts
+++ b/src/harness/unittests/transform.ts
@@ -20,6 +20,15 @@ namespace ts {
};
return (file: ts.SourceFile) => file;
}
+ function replaceNumberWith2(context: ts.TransformationContext) {
+ function visitor(node: Node): Node {
+ if (isNumericLiteral(node)) {
+ return createNumericLiteral("2");
+ }
+ return visitEachChild(node, visitor, context);
+ }
+ return (file: ts.SourceFile) => visitNode(file, visitor);
+ }
function replaceIdentifiersNamedOldNameWithNewName(context: ts.TransformationContext) {
const previousOnSubstituteNode = context.onSubstituteNode;
@@ -101,6 +110,20 @@ namespace ts {
}).outputText;
});
+ testBaseline("transformTypesInExportDefault", () => {
+ return ts.transpileModule(`
+ export default (foo: string) => { return 1; }
+ `, {
+ transformers: {
+ before: [replaceNumberWith2],
+ },
+ compilerOptions: {
+ target: ts.ScriptTarget.ESNext,
+ newLine: NewLineKind.CarriageReturnLineFeed,
+ }
+ }).outputText;
+ });
+
testBaseline("synthesizedClassAndNamespaceCombination", () => {
return ts.transpileModule("", {
transformers: {
@@ -169,6 +192,38 @@ namespace ts {
};
}
});
+
+ // https://github.com/Microsoft/TypeScript/issues/19618
+ testBaseline("transformAddImportStar", () => {
+ return ts.transpileModule("", {
+ transformers: {
+ before: [transformAddImportStar],
+ },
+ compilerOptions: {
+ target: ts.ScriptTarget.ES5,
+ module: ts.ModuleKind.System,
+ newLine: NewLineKind.CarriageReturnLineFeed,
+ }
+ }).outputText;
+
+ function transformAddImportStar(_context: ts.TransformationContext) {
+ return (sourceFile: ts.SourceFile): ts.SourceFile => {
+ return visitNode(sourceFile);
+ };
+ function visitNode(sf: ts.SourceFile) {
+ // produce `import * as i0 from './comp';
+ const importStar = ts.createImportDeclaration(
+ /*decorators*/ undefined,
+ /*modifiers*/ undefined,
+ /*importClause*/ ts.createImportClause(
+ /*name*/ undefined,
+ ts.createNamespaceImport(ts.createIdentifier("i0"))
+ ),
+ /*moduleSpecifier*/ ts.createLiteral("./comp1"));
+ return ts.updateSourceFileNode(sf, [importStar]);
+ }
+ }
+ });
});
}
diff --git a/src/harness/unittests/transpile.ts b/src/harness/unittests/transpile.ts
index 16bef6500f2..7c4af8be167 100644
--- a/src/harness/unittests/transpile.ts
+++ b/src/harness/unittests/transpile.ts
@@ -149,21 +149,21 @@ var x = 0;`, {
`import {foo} from "SomeName";\n` +
`declare function use(a: any);\n` +
`use(foo);`, {
- options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } }
+ options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, renamedDependencies: { SomeName: "SomeOtherName" } }
});
transpilesCorrectly("Rename dependencies - AMD",
`import {foo} from "SomeName";\n` +
`declare function use(a: any);\n` +
`use(foo);`, {
- options: { compilerOptions: { module: ModuleKind.AMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } }
+ options: { compilerOptions: { module: ModuleKind.AMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { SomeName: "SomeOtherName" } }
});
transpilesCorrectly("Rename dependencies - UMD",
`import {foo} from "SomeName";\n` +
`declare function use(a: any);\n` +
`use(foo);`, {
- options: { compilerOptions: { module: ModuleKind.UMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } }
+ options: { compilerOptions: { module: ModuleKind.UMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { SomeName: "SomeOtherName" } }
});
transpilesCorrectly("Transpile with emit decorators and emit metadata",
diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts
index c5aba73f2ac..4e2d63cec90 100644
--- a/src/harness/unittests/tscWatchMode.ts
+++ b/src/harness/unittests/tscWatchMode.ts
@@ -710,12 +710,12 @@ namespace ts.tscWatch {
path: "/src/tsconfig.json",
content: JSON.stringify(
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": true,
- "sourceMap": false,
- "lib": [
+ compilerOptions: {
+ module: "commonjs",
+ target: "es5",
+ noImplicitAny: true,
+ sourceMap: false,
+ lib: [
"es5"
]
}
@@ -725,12 +725,12 @@ namespace ts.tscWatch {
path: config1.path,
content: JSON.stringify(
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": true,
- "sourceMap": false,
- "lib": [
+ compilerOptions: {
+ module: "commonjs",
+ target: "es5",
+ noImplicitAny: true,
+ sourceMap: false,
+ lib: [
"es5",
"es2015.promise"
]
@@ -1616,6 +1616,36 @@ namespace ts.tscWatch {
return files.slice(0, 2);
}
});
+
+ it("Elides const enums correctly in incremental compilation", () => {
+ const currentDirectory = "/user/someone/projects/myproject";
+ const file1: FileOrFolder = {
+ path: `${currentDirectory}/file1.ts`,
+ content: "export const enum E1 { V = 1 }"
+ };
+ const file2: FileOrFolder = {
+ path: `${currentDirectory}/file2.ts`,
+ content: `import { E1 } from "./file1"; export const enum E2 { V = E1.V }`
+ };
+ const file3: FileOrFolder = {
+ path: `${currentDirectory}/file3.ts`,
+ content: `import { E2 } from "./file2"; const v: E2 = E2.V;`
+ };
+ const strictAndEsModule = `"use strict";\nexports.__esModule = true;\n`;
+ verifyEmittedFileContents("\n", [file3, file2, file1], [
+ `${strictAndEsModule}var v = 1 /* V */;\n`,
+ strictAndEsModule,
+ strictAndEsModule
+ ], modifyFiles);
+
+ function modifyFiles(files: FileOrFolderEmit[], emittedFiles: EmittedFile[]) {
+ files[0].content += `function foo2() { return 2; }`;
+ emittedFiles[0].content += `function foo2() { return 2; }\n`;
+ emittedFiles[1].shouldBeWritten = false;
+ emittedFiles[2].shouldBeWritten = false;
+ return [files[0]];
+ }
+ });
});
describe("tsc-watch module resolution caching", () => {
@@ -1963,12 +1993,12 @@ declare module "fs" {
const configFile: FileOrFolder = {
path: "/a/rootFolder/project/tsconfig.json",
content: JSON.stringify({
- "compilerOptions": {
- "module": "none",
- "allowJs": true,
- "outDir": "Static/scripts/"
+ compilerOptions: {
+ module: "none",
+ allowJs: true,
+ outDir: "Static/scripts/"
},
- "include": [
+ include: [
"Scripts/**/*"
],
})
@@ -1988,7 +2018,7 @@ declare module "fs" {
checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path));
file1.content = "var zz30 = 100;";
- host.reloadFS(files, /*invokeDirectoryWatcherInsteadOfFileChanged*/ true);
+ host.reloadFS(files, { invokeDirectoryWatcherInsteadOfFileChanged: true });
host.runQueuedTimeoutCallbacks();
checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path));
diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts
index bc8b476ceb9..ea573726eb8 100644
--- a/src/harness/unittests/tsserverProjectSystem.ts
+++ b/src/harness/unittests/tsserverProjectSystem.ts
@@ -85,8 +85,7 @@ namespace ts.projectSystem {
assert.equal(this.postExecActions.length, expectedCount, `Expected ${expectedCount} post install actions`);
}
- onProjectClosed() {
- }
+ onProjectClosed = noop;
attach(projectService: server.ProjectService) {
this.projectService = projectService;
@@ -437,6 +436,50 @@ namespace ts.projectSystem {
verifyDiagnostics(actual, []);
}
+ function assertEvent(actualOutput: string, expectedEvent: protocol.Event, host: TestServerHost) {
+ assert.equal(actualOutput, server.formatMessage(expectedEvent, nullLogger, Utils.byteLength, host.newLine));
+ }
+
+ function checkErrorMessage(host: TestServerHost, eventName: "syntaxDiag" | "semanticDiag", diagnostics: protocol.DiagnosticEventBody) {
+ const outputs = host.getOutput();
+ assert.isTrue(outputs.length >= 1, outputs.toString());
+ const event: protocol.Event = {
+ seq: 0,
+ type: "event",
+ event: eventName,
+ body: diagnostics
+ };
+ assertEvent(outputs[0], event, host);
+ }
+
+ function checkCompleteEvent(host: TestServerHost, numberOfCurrentEvents: number, expectedSequenceId: number) {
+ const outputs = host.getOutput();
+ assert.equal(outputs.length, numberOfCurrentEvents, outputs.toString());
+ const event: protocol.RequestCompletedEvent = {
+ seq: 0,
+ type: "event",
+ event: "requestCompleted",
+ body: {
+ request_seq: expectedSequenceId
+ }
+ };
+ assertEvent(outputs[numberOfCurrentEvents - 1], event, host);
+ }
+
+ function checkProjectUpdatedInBackgroundEvent(host: TestServerHost, openFiles: string[]) {
+ const outputs = host.getOutput();
+ assert.equal(outputs.length, 1, outputs.toString());
+ const event: protocol.ProjectsUpdatedInBackgroundEvent = {
+ seq: 0,
+ type: "event",
+ event: "projectsUpdatedInBackground",
+ body: {
+ openFiles
+ }
+ };
+ assertEvent(outputs[0], event, host);
+ }
+
describe("tsserverProjectSystem", () => {
const commonFile1: FileOrFolder = {
path: "/a/b/commonFile1.ts",
@@ -1248,13 +1291,13 @@ namespace ts.projectSystem {
service.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(service.externalProjects[0], [f1.path, f2.path, libFile.path]);
- const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2);
+ const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, { includeExternalModuleExports: false });
// should contain completions for string
assert.isTrue(completions1.entries.some(e => e.name === "charAt"), "should contain 'charAt'");
assert.isFalse(completions1.entries.some(e => e.name === "toExponential"), "should not contain 'toExponential'");
service.closeClientFile(f2.path);
- const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2);
+ const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, { includeExternalModuleExports: false });
// should contain completions for string
assert.isFalse(completions2.entries.some(e => e.name === "charAt"), "should not contain 'charAt'");
assert.isTrue(completions2.entries.some(e => e.name === "toExponential"), "should contain 'toExponential'");
@@ -1280,11 +1323,11 @@ namespace ts.projectSystem {
service.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(service.externalProjects[0], [f1.path, f2.path, libFile.path]);
- const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0);
+ const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, { includeExternalModuleExports: false });
assert.isTrue(completions1.entries.some(e => e.name === "somelongname"), "should contain 'somelongname'");
service.closeClientFile(f2.path);
- const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0);
+ const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, { includeExternalModuleExports: false });
assert.isFalse(completions2.entries.some(e => e.name === "somelongname"), "should not contain 'somelongname'");
const sf2 = service.externalProjects[0].getLanguageService().getProgram().getSourceFile(f2.path);
assert.equal(sf2.text, "");
@@ -1496,6 +1539,42 @@ namespace ts.projectSystem {
}
});
+ it("removes version numbers correctly", () => {
+ const testData: [string, string][] = [
+ ["jquery-max", "jquery-max"],
+ ["jquery.min", "jquery"],
+ ["jquery-min.4.2.3", "jquery"],
+ ["jquery.min.4.2.1", "jquery"],
+ ["minimum", "minimum"],
+ ["min", "min"],
+ ["min.3.2", "min"],
+ ["jquery", "jquery"]
+ ];
+ for (const t of testData) {
+ assert.equal(removeMinAndVersionNumbers(t[0]), t[1], t[0]);
+ }
+ });
+
+ it("ignores files excluded by a legacy safe type list", () => {
+ const file1 = {
+ path: "/a/b/bliss.js",
+ content: "let x = 5"
+ };
+ const file2 = {
+ path: "/a/b/foo.js",
+ content: ""
+ };
+ const host = createServerHost([file1, file2, customTypesMap]);
+ const projectService = createProjectService(host);
+ try {
+ projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, file2.path]), typeAcquisition: { enable: true } });
+ const proj = projectService.externalProjects[0];
+ assert.deepEqual(proj.getFileNames(), [file2.path]);
+ } finally {
+ projectService.resetSafeList();
+ }
+ });
+
it("open file become a part of configured project if it is referenced from root file", () => {
const file1 = {
path: "/a/b/f1.ts",
@@ -1826,7 +1905,7 @@ namespace ts.projectSystem {
// Specify .html extension as mixed content
const extraFileExtensions = [{ extension: ".html", scriptKind: ScriptKind.JS, isMixedContent: true }];
const configureHostRequest = makeSessionRequest(CommandNames.Configure, { extraFileExtensions });
- session.executeCommand(configureHostRequest).response;
+ session.executeCommand(configureHostRequest);
// The configured project should now be updated to include html file
checkNumberOfProjects(projectService, { configuredProjects: 1 });
@@ -1845,7 +1924,7 @@ namespace ts.projectSystem {
// Check identifiers defined in HTML content are available in .ts file
const project = configuredProjectAt(projectService, 0);
- let completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 1);
+ let completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 1, { includeExternalModuleExports: false });
assert(completions && completions.entries[0].name === "hello", `expected entry hello to be in completion list`);
// Close HTML file
@@ -1859,7 +1938,7 @@ namespace ts.projectSystem {
checkProjectActualFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path, config.path]);
// Check identifiers defined in HTML content are not available in .ts file
- completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 5);
+ completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 5, { includeExternalModuleExports: false });
assert(completions && completions.entries[0].name !== "hello", `unexpected hello entry in completion list`);
});
@@ -1885,7 +1964,7 @@ namespace ts.projectSystem {
// Specify .html extension as mixed content in a configure host request
const extraFileExtensions = [{ extension: ".html", scriptKind: ScriptKind.JS, isMixedContent: true }];
const configureHostRequest = makeSessionRequest(CommandNames.Configure, { extraFileExtensions });
- session.executeCommand(configureHostRequest).response;
+ session.executeCommand(configureHostRequest);
openFilesForSession([file1], session);
let projectService = session.getProjectService();
@@ -1904,7 +1983,7 @@ namespace ts.projectSystem {
host = createServerHost([file1, file2, config2, libFile], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") });
session = createSession(host);
- session.executeCommand(configureHostRequest).response;
+ session.executeCommand(configureHostRequest);
openFilesForSession([file1], session);
projectService = session.getProjectService();
@@ -1923,7 +2002,7 @@ namespace ts.projectSystem {
host = createServerHost([file1, file2, config3, libFile], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") });
session = createSession(host);
- session.executeCommand(configureHostRequest).response;
+ session.executeCommand(configureHostRequest);
openFilesForSession([file1], session);
projectService = session.getProjectService();
@@ -1942,7 +2021,7 @@ namespace ts.projectSystem {
host = createServerHost([file1, file2, config4, libFile], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") });
session = createSession(host);
- session.executeCommand(configureHostRequest).response;
+ session.executeCommand(configureHostRequest);
openFilesForSession([file1], session);
projectService = session.getProjectService();
@@ -1961,7 +2040,7 @@ namespace ts.projectSystem {
host = createServerHost([file1, file2, config5, libFile], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") });
session = createSession(host);
- session.executeCommand(configureHostRequest).response;
+ session.executeCommand(configureHostRequest);
openFilesForSession([file1], session);
projectService = session.getProjectService();
@@ -2563,7 +2642,7 @@ namespace ts.projectSystem {
projectFileName,
rootFiles: [toExternalFile(site.path), toExternalFile(configFile.path)],
options: { allowJs: false },
- typeAcquisition: { "include": [] }
+ typeAcquisition: { include: [] }
};
projectService.openExternalProjects([externalProject]);
@@ -2723,6 +2802,7 @@ namespace ts.projectSystem {
watchedRecursiveDirectories.push(`${root}/a/b/src`, `${root}/a/b/node_modules`);
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
});
+
});
describe("Proper errors", () => {
@@ -2744,6 +2824,139 @@ namespace ts.projectSystem {
const project = projectService.findProject(corruptedConfig.path);
checkProjectRootFiles(project, [file1.path]);
});
+
+ describe("when opening new file that doesnt exist on disk yet", () => {
+ function verifyNonExistentFile(useProjectRoot: boolean) {
+ const host = createServerHost([libFile]);
+ let hasError = false;
+ const errLogger: server.Logger = {
+ close: noop,
+ hasLevel: () => true,
+ loggingEnabled: () => true,
+ perftrc: noop,
+ info: noop,
+ msg: (_s, type) => {
+ if (type === server.Msg.Err) {
+ hasError = true;
+ }
+ },
+ startGroup: noop,
+ endGroup: noop,
+ getLogFileName: (): string => undefined
+ };
+ const session = createSession(host, { canUseEvents: true, logger: errLogger, useInferredProjectPerProjectRoot: true });
+
+ const folderPath = "/user/someuser/projects/someFolder";
+ const projectService = session.getProjectService();
+ const untitledFile = "untitled:Untitled-1";
+ session.executeCommandSeq({
+ command: server.CommandNames.Open,
+ arguments: {
+ file: untitledFile,
+ fileContent: "",
+ scriptKindName: "JS",
+ projectRootPath: useProjectRoot ? folderPath : undefined
+ }
+ });
+ checkNumberOfProjects(projectService, { inferredProjects: 1 });
+ const infoForUntitledAtProjectRoot = projectService.getScriptInfoForPath(`${folderPath.toLowerCase()}/${untitledFile.toLowerCase()}` as Path);
+ const infoForUnitiledAtRoot = projectService.getScriptInfoForPath(`/${untitledFile.toLowerCase()}` as Path);
+ if (useProjectRoot) {
+ assert.isDefined(infoForUntitledAtProjectRoot);
+ assert.isUndefined(infoForUnitiledAtRoot);
+ }
+ else {
+ assert.isDefined(infoForUnitiledAtRoot);
+ assert.isUndefined(infoForUntitledAtProjectRoot);
+ }
+ host.checkTimeoutQueueLength(2);
+
+ const newTimeoutId = host.getNextTimeoutId();
+ const expectedSequenceId = session.getNextSeq();
+ session.executeCommandSeq({
+ command: server.CommandNames.Geterr,
+ arguments: {
+ delay: 0,
+ files: [untitledFile]
+ }
+ });
+ host.checkTimeoutQueueLength(3);
+
+ // Run the last one = get error request
+ host.runQueuedTimeoutCallbacks(newTimeoutId);
+
+ assert.isFalse(hasError);
+ host.checkTimeoutQueueLength(2);
+ checkErrorMessage(host, "syntaxDiag", { file: untitledFile, diagnostics: [] });
+ host.clearOutput();
+
+ host.runQueuedImmediateCallbacks();
+ assert.isFalse(hasError);
+ checkErrorMessage(host, "semanticDiag", { file: untitledFile, diagnostics: [] });
+
+ checkCompleteEvent(host, 2, expectedSequenceId);
+ }
+
+ it("has projectRoot", () => {
+ verifyNonExistentFile(/*useProjectRoot*/ true);
+ });
+
+ it("does not have projectRoot", () => {
+ verifyNonExistentFile(/*useProjectRoot*/ false);
+ });
+ });
+
+ it("folder rename updates project structure and reports no errors", () => {
+ const projectDir = "/a/b/projects/myproject";
+ const app: FileOrFolder = {
+ path: `${projectDir}/bar/app.ts`,
+ content: "class Bar implements foo.Foo { getFoo() { return ''; } get2() { return 1; } }"
+ };
+ const foo: FileOrFolder = {
+ path: `${projectDir}/foo/foo.ts`,
+ content: "declare namespace foo { interface Foo { get2(): number; getFoo(): string; } }"
+ };
+ const configFile: FileOrFolder = {
+ path: `${projectDir}/tsconfig.json`,
+ content: JSON.stringify({ compilerOptions: { module: "none", targer: "es5" }, exclude: ["node_modules"] })
+ };
+ const host = createServerHost([app, foo, configFile]);
+ const session = createSession(host, { canUseEvents: true, });
+ const projectService = session.getProjectService();
+
+ session.executeCommandSeq