diff --git a/Gulpfile.ts b/Gulpfile.ts
index d677b042250..1b902562832 100644
--- a/Gulpfile.ts
+++ b/Gulpfile.ts
@@ -17,7 +17,7 @@ declare module "gulp-typescript" {
stripInternal?: boolean;
types?: string[];
}
- interface CompileStream extends NodeJS.ReadWriteStream {} // Either gulp or gulp-typescript has some odd typings which don't reflect reality, making this required
+ interface CompileStream extends NodeJS.ReadWriteStream { } // Either gulp or gulp-typescript has some odd typings which don't reflect reality, making this required
}
import * as insert from "gulp-insert";
import * as sourcemaps from "gulp-sourcemaps";
@@ -34,7 +34,6 @@ import through2 = require("through2");
import merge2 = require("merge2");
import intoStream = require("into-stream");
import * as os from "os";
-import Linter = require("tslint");
import fold = require("travis-fold");
const gulp = helpMaker(originalGulp);
const mochaParallel = require("./scripts/mocha-parallel.js");
@@ -66,7 +65,7 @@ const cmdLineOptions = minimist(process.argv.slice(2), {
}
});
-function exec(cmd: string, args: string[], complete: () => void = (() => {}), error: (e: any, status: number) => void = (() => {})) {
+function exec(cmd: string, args: string[], complete: () => void = (() => { }), error: (e: any, status: number) => void = (() => { })) {
console.log(`${cmd} ${args.join(" ")}`);
// TODO (weswig): Update child_process types to add windowsVerbatimArguments to the type definition
const subshellFlag = isWin ? "/c" : "-c";
@@ -117,12 +116,12 @@ const es2015LibrarySources = [
];
const es2015LibrarySourceMap = es2015LibrarySources.map(function(source) {
- return { target: "lib." + source, sources: ["header.d.ts", source] };
+ return { target: "lib." + source, sources: ["header.d.ts", source] };
});
-const es2016LibrarySource = [ "es2016.array.include.d.ts" ];
+const es2016LibrarySource = ["es2016.array.include.d.ts"];
-const es2016LibrarySourceMap = es2016LibrarySource.map(function (source) {
+const es2016LibrarySourceMap = es2016LibrarySource.map(function(source) {
return { target: "lib." + source, sources: ["header.d.ts", source] };
});
@@ -131,38 +130,38 @@ const es2017LibrarySource = [
"es2017.sharedmemory.d.ts"
];
-const es2017LibrarySourceMap = es2017LibrarySource.map(function (source) {
+const es2017LibrarySourceMap = es2017LibrarySource.map(function(source) {
return { target: "lib." + source, sources: ["header.d.ts", source] };
});
const hostsLibrarySources = ["dom.generated.d.ts", "webworker.importscripts.d.ts", "scripthost.d.ts"];
const librarySourceMap = [
- // Host library
- { target: "lib.dom.d.ts", sources: ["header.d.ts", "dom.generated.d.ts"] },
- { target: "lib.dom.iterable.d.ts", sources: ["header.d.ts", "dom.iterable.d.ts"] },
- { target: "lib.webworker.d.ts", sources: ["header.d.ts", "webworker.generated.d.ts"] },
- { target: "lib.scripthost.d.ts", sources: ["header.d.ts", "scripthost.d.ts"] },
+ // Host library
+ { target: "lib.dom.d.ts", sources: ["header.d.ts", "dom.generated.d.ts"] },
+ { target: "lib.dom.iterable.d.ts", sources: ["header.d.ts", "dom.iterable.d.ts"] },
+ { target: "lib.webworker.d.ts", sources: ["header.d.ts", "webworker.generated.d.ts"] },
+ { target: "lib.scripthost.d.ts", sources: ["header.d.ts", "scripthost.d.ts"] },
- // JavaScript library
- { target: "lib.es5.d.ts", sources: ["header.d.ts", "es5.d.ts"] },
- { target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] },
- { target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] },
- { target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] },
+ // JavaScript library
+ { target: "lib.es5.d.ts", sources: ["header.d.ts", "es5.d.ts"] },
+ { target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] },
+ { target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] },
+ { target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] },
- // JavaScript + all host library
- { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) },
- { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }
+ // JavaScript + all host library
+ { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) },
+ { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }
].concat(es2015LibrarySourceMap, es2016LibrarySourceMap, es2017LibrarySourceMap);
-const libraryTargets = librarySourceMap.map(function (f) {
+const libraryTargets = librarySourceMap.map(function(f) {
return path.join(builtLocalDirectory, f.target);
});
for (const i in libraryTargets) {
const entry = librarySourceMap[i];
const target = libraryTargets[i];
- const sources = [copyright].concat(entry.sources.map(function (s) {
+ const sources = [copyright].concat(entry.sources.map(function(s) {
return path.join(libraryDirectory, s);
}));
gulp.task(target, false, [], function() {
@@ -392,7 +391,7 @@ gulp.task(servicesFile, false, ["lib", "generate-diagnostics"], () => {
.pipe(sourcemaps.init())
.pipe(tsc(servicesProject));
const completedJs = js.pipe(prependCopyright())
- .pipe(sourcemaps.write("."));
+ .pipe(sourcemaps.write("."));
const completedDts = dts.pipe(prependCopyright(/*outputCopyright*/true))
.pipe(insert.transform((contents, file) => {
file.path = standaloneDefinitionsFile;
@@ -435,17 +434,17 @@ const tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverli
gulp.task(tsserverLibraryFile, false, [servicesFile], (done) => {
const serverLibraryProject = tsc.createProject("src/server/tsconfig.library.json", getCompilerSettings({}, /*useBuiltCompiler*/ true));
- const {js, dts}: {js: NodeJS.ReadableStream, dts: NodeJS.ReadableStream} = serverLibraryProject.src()
+ const {js, dts}: { js: NodeJS.ReadableStream, dts: NodeJS.ReadableStream } = serverLibraryProject.src()
.pipe(sourcemaps.init())
.pipe(newer(tsserverLibraryFile))
.pipe(tsc(serverLibraryProject));
return merge2([
js.pipe(prependCopyright())
- .pipe(sourcemaps.write("."))
- .pipe(gulp.dest(builtLocalDirectory)),
+ .pipe(sourcemaps.write("."))
+ .pipe(gulp.dest(builtLocalDirectory)),
dts.pipe(prependCopyright())
- .pipe(gulp.dest(builtLocalDirectory))
+ .pipe(gulp.dest(builtLocalDirectory))
]);
});
@@ -477,7 +476,7 @@ gulp.task(specMd, false, [word2mdJs], (done) => {
const specMDFullPath = path.resolve(specMd);
const cmd = "cscript //nologo " + word2mdJs + " \"" + specWordFullPath + "\" " + "\"" + specMDFullPath + "\"";
console.log(cmd);
- cp.exec(cmd, function () {
+ cp.exec(cmd, function() {
done();
});
});
@@ -493,12 +492,12 @@ gulp.task("dontUseDebugMode", false, [], (done) => { useDebugMode = false; done(
gulp.task("VerifyLKG", false, [], () => {
const expectedFiles = [builtLocalCompiler, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile].concat(libraryTargets);
- const missingFiles = expectedFiles.filter(function (f) {
+ const missingFiles = expectedFiles.filter(function(f) {
return !fs.existsSync(f);
});
if (missingFiles.length > 0) {
throw new Error("Cannot replace the LKG unless all built targets are present in directory " + builtLocalDirectory +
- ". The following files are missing:\n" + missingFiles.join("\n"));
+ ". The following files are missing:\n" + missingFiles.join("\n"));
}
// Copy all the targets into the LKG directory
return gulp.src(expectedFiles).pipe(gulp.dest(LKGDirectory));
@@ -532,8 +531,6 @@ const localRwcBaseline = path.join(internalTests, "baselines/rwc/local");
const refRwcBaseline = path.join(internalTests, "baselines/rwc/reference");
const localTest262Baseline = path.join(internalTests, "baselines/test262/local");
-const refTest262Baseline = path.join(internalTests, "baselines/test262/reference");
-
gulp.task("tests", "Builds the test infrastructure using the built compiler", [run]);
gulp.task("tests-debug", "Builds the test sources and automation in debug mode", () => {
@@ -628,7 +625,7 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
}
args.push(run);
setNodeEnvToDevelopment();
- runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: colors === " --no-colors " }, function (err) {
+ runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: colors === " --no-colors " }, function(err) {
// last worker clean everything and runs linter in case if there were no errors
del(taskConfigsFolder).then(() => {
if (!err) {
@@ -680,7 +677,7 @@ gulp.task("runtests",
["build-rules", "tests"],
(done) => {
runConsoleTests("mocha-fivemat-progress-reporter", /*runInParallel*/ false, done);
-});
+ });
const nodeServerOutFile = "tests/webTestServer.js";
const nodeServerInFile = "tests/webTestServer.ts";
@@ -812,32 +809,36 @@ gulp.task("diff-rwc", "Diffs the RWC baselines using the diff tool specified by
exec(getDiffTool(), [refRwcBaseline, localRwcBaseline], done, done);
});
+gulp.task("baseline-accept", "Makes the most recent test results the new baseline, overwriting the old baseline", () => {
+ return baselineAccept("");
+});
+
+function baselineAccept(subfolder = "") {
+ return merge2(baselineCopy(subfolder), baselineDelete(subfolder));
+}
+
+function baselineCopy(subfolder = "") {
+ return gulp.src([`tests/baselines/local/${subfolder}/**`, `!tests/baselines/local/${subfolder}/**/*.delete`])
+ .pipe(gulp.dest(refBaseline));
+}
+
+function baselineDelete(subfolder = "") {
+ return gulp.src(["tests/baselines/local/**/*.delete"])
+ .pipe(insert.transform((content, fileObj) => {
+ const target = path.join(refBaseline, fileObj.relative.substr(0, fileObj.relative.length - ".delete".length));
+ del.sync(target);
+ del.sync(fileObj.path);
+ return "";
+ }));
+}
-gulp.task("baseline-accept", "Makes the most recent test results the new baseline, overwriting the old baseline", (done) => {
- const softAccept = cmdLineOptions["soft"];
- if (!softAccept) {
- del(refBaseline).then(() => {
- fs.renameSync(localBaseline, refBaseline);
- done();
- }, done);
- }
- else {
- gulp.src(localBaseline)
- .pipe(gulp.dest(refBaseline))
- .on("end", () => {
- del(path.join(refBaseline, "local")).then(() => done(), done);
- });
- }
-});
gulp.task("baseline-accept-rwc", "Makes the most recent rwc test results the new baseline, overwriting the old baseline", () => {
- return del(refRwcBaseline).then(() => {
- fs.renameSync(localRwcBaseline, refRwcBaseline);
- });
+ return baselineAccept("rwc");
});
+
+
gulp.task("baseline-accept-test262", "Makes the most recent test262 test results the new baseline, overwriting the old baseline", () => {
- return del(refTest262Baseline).then(() => {
- fs.renameSync(localTest262Baseline, refTest262Baseline);
- });
+ return baselineAccept("test262");
});
@@ -929,26 +930,6 @@ gulp.task("build-rules", "Compiles tslint rules to js", () => {
.pipe(gulp.dest(dest));
});
-function getLinterOptions() {
- return {
- configuration: require("./tslint.json"),
- formatter: "prose",
- formattersDirectory: undefined,
- rulesDirectory: "built/local/tslint"
- };
-}
-
-function lintFileContents(options, path, contents) {
- const ll = new Linter(path, contents, options);
- console.log("Linting '" + path + "'.");
- return ll.lint();
-}
-
-function lintFile(options, path) {
- const contents = fs.readFileSync(path, "utf8");
- return lintFileContents(options, path, contents);
-}
-
const lintTargets = [
"Gulpfile.ts",
"src/compiler/**/*.ts",
@@ -960,29 +941,72 @@ const lintTargets = [
"tests/*.ts", "tests/webhost/*.ts" // Note: does *not* descend recursively
];
+function sendNextFile(files: {path: string}[], child: cp.ChildProcess, callback: (failures: number) => void, failures: number) {
+ const file = files.pop();
+ if (file) {
+ console.log(`Linting '${file.path}'.`);
+ child.send({ kind: "file", name: file.path });
+ }
+ else {
+ child.send({ kind: "close" });
+ callback(failures);
+ }
+}
+
+function spawnLintWorker(files: {path: string}[], callback: (failures: number) => void) {
+ const child = cp.fork("./scripts/parallel-lint");
+ let failures = 0;
+ child.on("message", function(data) {
+ switch (data.kind) {
+ case "result":
+ if (data.failures > 0) {
+ failures += data.failures;
+ console.log(data.output);
+ }
+ sendNextFile(files, child, callback, failures);
+ break;
+ case "error":
+ console.error(data.error);
+ failures++;
+ sendNextFile(files, child, callback, failures);
+ break;
+ }
+ });
+ sendNextFile(files, child, callback, failures);
+}
gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are: --f[iles]=regex", ["build-rules"], () => {
const fileMatcher = RegExp(cmdLineOptions["files"]);
- const lintOptions = getLinterOptions();
- let failed = 0;
if (fold.isTravis()) console.log(fold.start("lint"));
- return gulp.src(lintTargets)
- .pipe(insert.transform((contents, file) => {
- if (!fileMatcher.test(file.path)) return contents;
- const result = lintFile(lintOptions, file.path);
- if (result.failureCount > 0) {
- console.log(result.output);
- failed += result.failureCount;
+
+ let files: {stat: fs.Stats, path: string}[] = [];
+ return gulp.src(lintTargets, { read: false })
+ .pipe(through2.obj((chunk, enc, cb) => {
+ files.push(chunk);
+ cb();
+ }, (cb) => {
+ files = files.filter(file => fileMatcher.test(file.path)).sort((filea, fileb) => filea.stat.size - fileb.stat.size);
+ const workerCount = (process.env.workerCount && +process.env.workerCount) || os.cpus().length;
+ for (let i = 0; i < workerCount; i++) {
+ spawnLintWorker(files, finished);
}
- return contents; // TODO (weswig): Automatically apply fixes? :3
- }))
- .on("end", () => {
- if (fold.isTravis()) console.log(fold.end("lint"));
- if (failed > 0) {
- console.error("Linter errors.");
- process.exit(1);
+
+ let completed = 0;
+ let failures = 0;
+ function finished(fails) {
+ completed++;
+ failures += fails;
+ if (completed === workerCount) {
+ if (fold.isTravis()) console.log(fold.end("lint"));
+ if (failures > 0) {
+ throw new Error(`Linter errors: ${failures}`);
+ }
+ else {
+ cb();
+ }
+ }
}
- });
+ }));
});
diff --git a/Jakefile.js b/Jakefile.js
index 01aac82a47a..441f6aef4f9 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -4,7 +4,6 @@ var fs = require("fs");
var os = require("os");
var path = require("path");
var child_process = require("child_process");
-var Linter = require("tslint");
var fold = require("travis-fold");
var runTestsInParallel = require("./scripts/mocha-parallel").runTestsInParallel;
@@ -181,7 +180,8 @@ var harnessSources = harnessCoreSources.concat([
"convertCompilerOptionsFromJson.ts",
"convertTypingOptionsFromJson.ts",
"tsserverProjectSystem.ts",
- "matchFiles.ts"
+ "matchFiles.ts",
+ "initializeTSConfig.ts",
].map(function (f) {
return path.join(unittestsDirectory, f);
})).concat([
@@ -938,16 +938,16 @@ task("tests-debug", ["setDebugMode", "tests"]);
// Makes the test results the new baseline
desc("Makes the most recent test results the new baseline, overwriting the old baseline");
task("baseline-accept", function(hardOrSoft) {
- if (!hardOrSoft || hardOrSoft === "hard") {
- jake.rmRf(refBaseline);
- fs.renameSync(localBaseline, refBaseline);
- }
- else if (hardOrSoft === "soft") {
- var files = jake.readdirR(localBaseline);
- for (var i in files) {
+ var files = jake.readdirR(localBaseline);
+ var deleteEnding = '.delete';
+ for (var i in files) {
+ if (files[i].substr(files[i].length - deleteEnding.length) === deleteEnding) {
+ var filename = path.basename(files[i]);
+ filename = filename.substr(0, filename.length - deleteEnding.length);
+ fs.unlink(path.join(refBaseline, filename));
+ } else {
jake.cpR(files[i], refBaseline);
}
- jake.rmRf(path.join(refBaseline, "local"));
}
});
@@ -1054,36 +1054,6 @@ task("build-rules-end", [] , function() {
if (fold.isTravis()) console.log(fold.end("build-rules"));
});
-function getLinterOptions() {
- return {
- configuration: require("./tslint.json"),
- formatter: "prose",
- formattersDirectory: undefined,
- rulesDirectory: "built/local/tslint"
- };
-}
-
-function lintFileContents(options, path, contents) {
- var ll = new Linter(path, contents, options);
- console.log("Linting '" + path + "'.");
- return ll.lint();
-}
-
-function lintFile(options, path) {
- var contents = fs.readFileSync(path, "utf8");
- return lintFileContents(options, path, contents);
-}
-
-function lintFileAsync(options, path, cb) {
- fs.readFile(path, "utf8", function(err, contents) {
- if (err) {
- return cb(err);
- }
- var result = lintFileContents(options, path, contents);
- cb(undefined, result);
- });
-}
-
var lintTargets = compilerSources
.concat(harnessSources)
// Other harness sources
@@ -1094,75 +1064,78 @@ var lintTargets = compilerSources
.concat(["Gulpfile.ts"])
.concat([nodeServerInFile, perftscPath, "tests/perfsys.ts", webhostPath]);
+function sendNextFile(files, child, callback, failures) {
+ var file = files.pop();
+ if (file) {
+ console.log("Linting '" + file + "'.");
+ child.send({kind: "file", name: file});
+ }
+ else {
+ child.send({kind: "close"});
+ callback(failures);
+ }
+}
+
+function spawnLintWorker(files, callback) {
+ var child = child_process.fork("./scripts/parallel-lint");
+ var failures = 0;
+ child.on("message", function(data) {
+ switch (data.kind) {
+ case "result":
+ if (data.failures > 0) {
+ failures += data.failures;
+ console.log(data.output);
+ }
+ sendNextFile(files, child, callback, failures);
+ break;
+ case "error":
+ console.error(data.error);
+ failures++;
+ sendNextFile(files, child, callback, failures);
+ break;
+ }
+ });
+ sendNextFile(files, child, callback, failures);
+}
desc("Runs tslint on the compiler sources. Optional arguments are: f[iles]=regex");
task("lint", ["build-rules"], function() {
if (fold.isTravis()) console.log(fold.start("lint"));
var startTime = mark();
- var lintOptions = getLinterOptions();
var failed = 0;
var fileMatcher = RegExp(process.env.f || process.env.file || process.env.files || "");
var done = {};
for (var i in lintTargets) {
var target = lintTargets[i];
if (!done[target] && fileMatcher.test(target)) {
- var result = lintFile(lintOptions, target);
- if (result.failureCount > 0) {
- console.log(result.output);
- failed += result.failureCount;
- }
- done[target] = true;
+ done[target] = fs.statSync(target).size;
}
}
- measure(startTime);
- if (fold.isTravis()) console.log(fold.end("lint"));
- if (failed > 0) {
- fail('Linter errors.', failed);
- }
-});
-/**
- * This is required because file watches on Windows get fires _twice_
- * when a file changes on some node/windows version configuations
- * (node v4 and win 10, for example). By not running a lint for a file
- * which already has a pending lint, we avoid duplicating our work.
- * (And avoid printing duplicate results!)
- */
-var lintSemaphores = {};
+ var workerCount = (process.env.workerCount && +process.env.workerCount) || os.cpus().length;
-function lintWatchFile(filename) {
- fs.watch(filename, {persistent: true}, function(event) {
- if (event !== "change") {
- return;
- }
-
- if (!lintSemaphores[filename]) {
- lintSemaphores[filename] = true;
- lintFileAsync(getLinterOptions(), filename, function(err, result) {
- delete lintSemaphores[filename];
- if (err) {
- console.log(err);
- return;
- }
- if (result.failureCount > 0) {
- console.log("***Lint failure***");
- for (var i = 0; i < result.failures.length; i++) {
- var failure = result.failures[i];
- var start = failure.startPosition.lineAndCharacter;
- var end = failure.endPosition.lineAndCharacter;
- console.log("warning " + filename + " (" + (start.line + 1) + "," + (start.character + 1) + "," + (end.line + 1) + "," + (end.character + 1) + "): " + failure.failure);
- }
- console.log("*** Total " + result.failureCount + " failures.");
- }
- });
- }
+ var names = Object.keys(done).sort(function(namea, nameb) {
+ return done[namea] - done[nameb];
});
-}
-desc("Watches files for changes to rerun a lint pass");
-task("lint-server", ["build-rules"], function() {
- console.log("Watching ./src for changes to linted files");
- for (var i = 0; i < lintTargets.length; i++) {
- lintWatchFile(lintTargets[i]);
+ for (var i = 0; i < workerCount; i++) {
+ spawnLintWorker(names, finished);
}
-});
+
+ var completed = 0;
+ var failures = 0;
+ function finished(fails) {
+ completed++;
+ failures += fails;
+ if (completed === workerCount) {
+ measure(startTime);
+ if (fold.isTravis()) console.log(fold.end("lint"));
+ if (failures > 0) {
+ fail('Linter errors.', failed);
+ }
+ else {
+ complete();
+ }
+ }
+ }
+}, {async: true});
diff --git a/README.md b/README.md
index fca2890bc77..d16bc363b26 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
[](https://gitter.im/Microsoft/TypeScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[TypeScript](http://www.typescriptlang.org/) is a language for application-scale JavaScript. TypeScript adds optional types, classes, and modules to JavaScript. TypeScript supports tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript. Try it out at the [playground](http://www.typescriptlang.org/Playground), and stay up to date via [our blog](http://blogs.msdn.com/typescript) and [Twitter account](https://twitter.com/typescriptlang).
+[TypeScript](http://www.typescriptlang.org/) is a language for application-scale JavaScript. TypeScript adds optional types, classes, and modules to JavaScript. TypeScript supports tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript. Try it out at the [playground](http://www.typescriptlang.org/Playground), and stay up to date via [our blog](https://blogs.msdn.microsoft.com/typescript) and [Twitter account](https://twitter.com/typescriptlang).
## Installing
diff --git a/package.json b/package.json
index 25d8f0c42ea..eb34b6bffbb 100644
--- a/package.json
+++ b/package.json
@@ -69,6 +69,7 @@
"mkdirp": "latest",
"mocha": "latest",
"mocha-fivemat-progress-reporter": "latest",
+ "q": "latest",
"run-sequence": "latest",
"sorcery": "latest",
"through2": "latest",
diff --git a/scripts/ior.ts b/scripts/ior.ts
index eb67e62a275..91580203350 100644
--- a/scripts/ior.ts
+++ b/scripts/ior.ts
@@ -1,4 +1,4 @@
-///
+///
import fs = require('fs');
import path = require('path');
diff --git a/scripts/parallel-lint.js b/scripts/parallel-lint.js
new file mode 100644
index 00000000000..a9aec06c2df
--- /dev/null
+++ b/scripts/parallel-lint.js
@@ -0,0 +1,45 @@
+var Linter = require("tslint");
+var fs = require("fs");
+
+function getLinterOptions() {
+ return {
+ configuration: require("../tslint.json"),
+ formatter: "prose",
+ formattersDirectory: undefined,
+ rulesDirectory: "built/local/tslint"
+ };
+}
+
+function lintFileContents(options, path, contents) {
+ var ll = new Linter(path, contents, options);
+ return ll.lint();
+}
+
+function lintFileAsync(options, path, cb) {
+ fs.readFile(path, "utf8", function (err, contents) {
+ if (err) {
+ return cb(err);
+ }
+ var result = lintFileContents(options, path, contents);
+ cb(undefined, result);
+ });
+}
+
+process.on("message", function (data) {
+ switch (data.kind) {
+ case "file":
+ var target = data.name;
+ var lintOptions = getLinterOptions();
+ lintFileAsync(lintOptions, target, function (err, result) {
+ if (err) {
+ process.send({ kind: "error", error: err.toString() });
+ return;
+ }
+ process.send({ kind: "result", failures: result.failureCount, output: result.output });
+ });
+ break;
+ case "close":
+ process.exit(0);
+ break;
+ }
+});
\ No newline at end of file
diff --git a/scripts/types/ambient.d.ts b/scripts/types/ambient.d.ts
index e83489801d7..4f4b118c432 100644
--- a/scripts/types/ambient.d.ts
+++ b/scripts/types/ambient.d.ts
@@ -10,7 +10,7 @@ declare module "gulp-insert" {
export function append(text: string | Buffer): NodeJS.ReadWriteStream;
export function prepend(text: string | Buffer): NodeJS.ReadWriteStream;
export function wrap(text: string | Buffer, tail: string | Buffer): NodeJS.ReadWriteStream;
- export function transform(cb: (contents: string, file: {path: string}) => string): NodeJS.ReadWriteStream; // file is a vinyl file
+ export function transform(cb: (contents: string, file: {path: string, relative: string}) => string): NodeJS.ReadWriteStream; // file is a vinyl file
}
declare module "into-stream" {
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index c355d6c9186..d8017d601ad 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -89,9 +89,10 @@ namespace ts {
const binder = createBinder();
export function bindSourceFile(file: SourceFile, options: CompilerOptions) {
- const start = performance.mark();
+ performance.mark("beforeBind");
binder(file, options);
- performance.measure("Bind", start);
+ performance.mark("afterBind");
+ performance.measure("Bind", "beforeBind", "afterBind");
}
function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
@@ -298,8 +299,10 @@ namespace ts {
const name = isDefaultExport && parent ? "default" : getDeclarationName(node);
let symbol: Symbol;
- if (name !== undefined) {
-
+ if (name === undefined) {
+ symbol = createSymbol(SymbolFlags.None, "__missing");
+ }
+ else {
// Check and see if the symbol table already has a symbol with this name. If not,
// create a new symbol with this name and add it to the table. Note that we don't
// give the new symbol any flags *yet*. This ensures that it will not conflict
@@ -311,6 +314,11 @@ namespace ts {
// declaration we have for this symbol, and then create a new symbol for this
// declaration.
//
+ // Note that when properties declared in Javascript constructors
+ // (marked by isReplaceableByMethod) conflict with another symbol, the property loses.
+ // Always. This allows the common Javascript pattern of overwriting a prototype method
+ // with an bound instance method of the same type: `this.method = this.method.bind(this)`
+ //
// If we created a new symbol, either because we didn't have a symbol with this name
// in the symbol table, or we conflicted with an existing symbol, then just add this
// node as the sole declaration of the new symbol.
@@ -325,33 +333,37 @@ namespace ts {
}
if (symbol.flags & excludes) {
- if (node.name) {
- node.name.parent = node;
+ if (symbol.isReplaceableByMethod) {
+ // Javascript constructor-declared symbols can be discarded in favor of
+ // prototype symbols like methods.
+ symbol = symbolTable[name] = createSymbol(SymbolFlags.None, name);
}
-
- // Report errors every position with duplicate declaration
- // Report errors on previous encountered declarations
- let message = symbol.flags & SymbolFlags.BlockScopedVariable
- ? Diagnostics.Cannot_redeclare_block_scoped_variable_0
- : Diagnostics.Duplicate_identifier_0;
-
- forEach(symbol.declarations, declaration => {
- if (declaration.flags & NodeFlags.Default) {
- message = Diagnostics.A_module_cannot_have_multiple_default_exports;
+ else {
+ if (node.name) {
+ node.name.parent = node;
}
- });
- forEach(symbol.declarations, declaration => {
- file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration)));
- });
- file.bindDiagnostics.push(createDiagnosticForNode(node.name || node, message, getDisplayName(node)));
+ // Report errors every position with duplicate declaration
+ // Report errors on previous encountered declarations
+ let message = symbol.flags & SymbolFlags.BlockScopedVariable
+ ? Diagnostics.Cannot_redeclare_block_scoped_variable_0
+ : Diagnostics.Duplicate_identifier_0;
- symbol = createSymbol(SymbolFlags.None, name);
+ forEach(symbol.declarations, declaration => {
+ if (declaration.flags & NodeFlags.Default) {
+ message = Diagnostics.A_module_cannot_have_multiple_default_exports;
+ }
+ });
+
+ forEach(symbol.declarations, declaration => {
+ file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration)));
+ });
+ file.bindDiagnostics.push(createDiagnosticForNode(node.name || node, message, getDisplayName(node)));
+
+ symbol = createSymbol(SymbolFlags.None, name);
+ }
}
}
- else {
- symbol = createSymbol(SymbolFlags.None, "__missing");
- }
addDeclarationToSymbol(symbol, node, includes);
symbol.parent = parent;
@@ -1965,20 +1977,25 @@ namespace ts {
}
function bindThisPropertyAssignment(node: BinaryExpression) {
- // Declare a 'member' in case it turns out the container was an ES5 class or ES6 constructor
- let assignee: Node;
+ Debug.assert(isInJavaScriptFile(node));
+ // Declare a 'member' if the container is an ES5 class or ES6 constructor
if (container.kind === SyntaxKind.FunctionDeclaration || container.kind === SyntaxKind.FunctionExpression) {
- assignee = container;
+ container.symbol.members = container.symbol.members || createMap();
+ // It's acceptable for multiple 'this' assignments of the same identifier to occur
+ declareSymbol(container.symbol.members, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
}
else if (container.kind === SyntaxKind.Constructor) {
- assignee = container.parent;
+ // this.foo assignment in a JavaScript class
+ // Bind this property to the containing class
+ const saveContainer = container;
+ container = container.parent;
+ const symbol = bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.None);
+ if (symbol) {
+ // constructor-declared symbols can be overwritten by subsequent method declarations
+ (symbol as Symbol).isReplaceableByMethod = true;
+ }
+ container = saveContainer;
}
- else {
- return;
- }
- assignee.symbol.members = assignee.symbol.members || createMap();
- // It's acceptable for multiple 'this' assignments of the same identifier to occur
- declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
}
function bindPrototypePropertyAssignment(node: BinaryExpression) {
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 37f13dbf807..d954ea51170 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -284,7 +284,7 @@ namespace ts {
NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy,
}
- const typeofEQFacts: MapLike = {
+ const typeofEQFacts = createMap({
"string": TypeFacts.TypeofEQString,
"number": TypeFacts.TypeofEQNumber,
"boolean": TypeFacts.TypeofEQBoolean,
@@ -292,9 +292,9 @@ namespace ts {
"undefined": TypeFacts.EQUndefined,
"object": TypeFacts.TypeofEQObject,
"function": TypeFacts.TypeofEQFunction
- };
+ });
- const typeofNEFacts: MapLike = {
+ const typeofNEFacts = createMap({
"string": TypeFacts.TypeofNEString,
"number": TypeFacts.TypeofNENumber,
"boolean": TypeFacts.TypeofNEBoolean,
@@ -302,15 +302,15 @@ namespace ts {
"undefined": TypeFacts.NEUndefined,
"object": TypeFacts.TypeofNEObject,
"function": TypeFacts.TypeofNEFunction
- };
+ });
- const typeofTypesByName: MapLike = {
+ const typeofTypesByName = createMap({
"string": stringType,
"number": numberType,
"boolean": booleanType,
"symbol": esSymbolType,
"undefined": undefinedType
- };
+ });
let jsxElementType: ObjectType;
/** Things we lazy load from the JSX namespace */
@@ -403,8 +403,8 @@ namespace ts {
result.parent = symbol.parent;
if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration;
if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true;
- if (symbol.members) result.members = cloneSymbolTable(symbol.members);
- if (symbol.exports) result.exports = cloneSymbolTable(symbol.exports);
+ if (symbol.members) result.members = cloneMap(symbol.members);
+ if (symbol.exports) result.exports = cloneMap(symbol.exports);
recordMergedSymbol(result, symbol);
return result;
}
@@ -447,14 +447,6 @@ namespace ts {
}
}
- function cloneSymbolTable(symbolTable: SymbolTable): SymbolTable {
- const result = createMap();
- for (const id in symbolTable) {
- result[id] = symbolTable[id];
- }
- return result;
- }
-
function mergeSymbolTable(target: SymbolTable, source: SymbolTable) {
for (const id in source) {
let targetSymbol = target[id];
@@ -531,7 +523,7 @@ namespace ts {
function getNodeLinks(node: Node): NodeLinks {
const nodeId = getNodeId(node);
- return nodeLinks[nodeId] || (nodeLinks[nodeId] = {});
+ return nodeLinks[nodeId] || (nodeLinks[nodeId] = { flags: 0 });
}
function isGlobalSourceFile(node: Node) {
@@ -1457,7 +1449,7 @@ namespace ts {
return;
}
visitedSymbols.push(symbol);
- const symbols = cloneSymbolTable(symbol.exports);
+ const symbols = cloneMap(symbol.exports);
// All export * declarations are collected in an __export symbol by the binder
const exportStars = symbol.exports["__export"];
if (exportStars) {
@@ -1662,12 +1654,12 @@ namespace ts {
}
// If symbol is directly available by its name in the symbol table
- if (isAccessible(lookUp(symbols, symbol.name))) {
+ if (isAccessible(symbols[symbol.name])) {
return [symbol];
}
// Check if symbol is any of the alias
- return forEachValue(symbols, symbolFromSymbolTable => {
+ return forEachProperty(symbols, symbolFromSymbolTable => {
if (symbolFromSymbolTable.flags & SymbolFlags.Alias
&& symbolFromSymbolTable.name !== "export="
&& !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) {
@@ -3098,7 +3090,7 @@ namespace ts {
// If the declaration specifies a binding pattern, use the type implied by the binding pattern
if (isBindingPattern(declaration.name)) {
- return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ false);
+ return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ false, /*reportErrors*/ true);
}
// No type specified and nothing can be inferred
@@ -3108,23 +3100,21 @@ namespace ts {
// Return the type implied by a binding pattern element. This is the type of the initializer of the element if
// one is present. Otherwise, if the element is itself a binding pattern, it is the type implied by the binding
// pattern. Otherwise, it is the type any.
- function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean): Type {
+ function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean, reportErrors?: boolean): Type {
if (element.initializer) {
- const type = checkExpressionCached(element.initializer);
- reportErrorsFromWidening(element, type);
- return getWidenedType(type);
+ return checkExpressionCached(element.initializer);
}
if (isBindingPattern(element.name)) {
- return getTypeFromBindingPattern(element.name, includePatternInType);
+ return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors);
}
- if (compilerOptions.noImplicitAny && !declarationBelongsToPrivateAmbientMember(element)) {
+ if (reportErrors && compilerOptions.noImplicitAny && !declarationBelongsToPrivateAmbientMember(element)) {
reportImplicitAnyError(element, anyType);
}
return anyType;
}
// Return the type implied by an object binding pattern
- function getTypeFromObjectBindingPattern(pattern: BindingPattern, includePatternInType: boolean): Type {
+ function getTypeFromObjectBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
const members = createMap();
let hasComputedProperties = false;
forEach(pattern.elements, e => {
@@ -3138,7 +3128,7 @@ namespace ts {
const text = getTextOfPropertyName(name);
const flags = SymbolFlags.Property | SymbolFlags.Transient | (e.initializer ? SymbolFlags.Optional : 0);
const symbol = createSymbol(flags, text);
- symbol.type = getTypeFromBindingElement(e, includePatternInType);
+ symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors);
symbol.bindingElement = e;
members[symbol.name] = symbol;
});
@@ -3153,13 +3143,13 @@ namespace ts {
}
// Return the type implied by an array binding pattern
- function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean): Type {
+ function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
const elements = pattern.elements;
if (elements.length === 0 || elements[elements.length - 1].dotDotDotToken) {
return languageVersion >= ScriptTarget.ES6 ? createIterableType(anyType) : anyArrayType;
}
// If the pattern has at least one element, and no rest element, then it should imply a tuple type.
- const elementTypes = map(elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e, includePatternInType));
+ const elementTypes = map(elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors));
if (includePatternInType) {
const result = createNewTupleType(elementTypes);
result.pattern = pattern;
@@ -3175,10 +3165,10 @@ namespace ts {
// used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring
// parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of
// the parameter.
- function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType?: boolean): Type {
+ function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType?: boolean, reportErrors?: boolean): Type {
return pattern.kind === SyntaxKind.ObjectBindingPattern
- ? getTypeFromObjectBindingPattern(pattern, includePatternInType)
- : getTypeFromArrayBindingPattern(pattern, includePatternInType);
+ ? getTypeFromObjectBindingPattern(pattern, includePatternInType, reportErrors)
+ : getTypeFromArrayBindingPattern(pattern, includePatternInType, reportErrors);
}
// Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type
@@ -3255,6 +3245,13 @@ namespace ts {
// * className.prototype.method = expr
if (declaration.kind === SyntaxKind.BinaryExpression ||
declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) {
+ // Use JS Doc type if present on parent expression statement
+ if (declaration.flags & NodeFlags.JavaScriptFile) {
+ const typeTag = getJSDocTypeTag(declaration.parent);
+ if (typeTag && typeTag.typeExpression) {
+ return links.type = getTypeFromTypeNode(typeTag.typeExpression.type);
+ }
+ }
const declaredTypes = map(symbol.declarations,
decl => decl.kind === SyntaxKind.BinaryExpression ?
checkExpressionCached((decl).right) :
@@ -5560,6 +5557,8 @@ namespace ts {
return getTypeFromThisTypeNode(node);
case SyntaxKind.LiteralType:
return getTypeFromLiteralTypeNode(node);
+ case SyntaxKind.JSDocLiteralType:
+ return getTypeFromLiteralTypeNode((node).literal);
case SyntaxKind.TypeReference:
case SyntaxKind.JSDocTypeReference:
return getTypeFromTypeReference(node);
@@ -6629,7 +6628,7 @@ namespace ts {
const maybeCache = maybeStack[depth];
// If result is definitely true, copy assumptions to global cache, else copy to next level up
const destinationCache = (result === Ternary.True || depth === 0) ? relation : maybeStack[depth - 1];
- copyMap(maybeCache, destinationCache);
+ copyProperties(maybeCache, destinationCache);
}
else {
// A false result goes straight into global cache (when something is false under assumptions it
@@ -7941,7 +7940,7 @@ namespace ts {
// check. This gives us a quicker out in the common case where an object type is not a function.
const resolved = resolveStructuredTypeMembers(type);
return !!(resolved.callSignatures.length || resolved.constructSignatures.length ||
- hasProperty(resolved.members, "bind") && isTypeSubtypeOf(type, globalFunctionType));
+ resolved.members["bind"] && isTypeSubtypeOf(type, globalFunctionType));
}
function getTypeFacts(type: Type): TypeFacts {
@@ -8192,7 +8191,7 @@ namespace ts {
return incomplete ? { flags: 0, type } : type;
}
- function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) {
+ function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, flowContainer: Node) {
let key: string;
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
return declaredType;
@@ -8244,7 +8243,7 @@ namespace ts {
else if (flow.flags & FlowFlags.Start) {
// Check if we should continue with the control flow of the containing function.
const container = (flow).container;
- if (container && includeOuterFunctions) {
+ if (container && container !== flowContainer && reference.kind !== SyntaxKind.PropertyAccessExpression) {
flow = container.flowNode;
continue;
}
@@ -8372,13 +8371,18 @@ namespace ts {
// each antecedent code path.
const antecedentTypes: Type[] = [];
let subtypeReduction = false;
+ let firstAntecedentType: FlowType;
flowLoopNodes[flowLoopCount] = flow;
flowLoopKeys[flowLoopCount] = key;
flowLoopTypes[flowLoopCount] = antecedentTypes;
for (const antecedent of flow.antecedents) {
flowLoopCount++;
- const type = getTypeFromFlowType(getTypeAtFlowNode(antecedent));
+ const flowType = getTypeAtFlowNode(antecedent);
flowLoopCount--;
+ if (!firstAntecedentType) {
+ firstAntecedentType = flowType;
+ }
+ const type = getTypeFromFlowType(flowType);
// If we see a value appear in the cache it is a sign that control flow analysis
// was restarted and completed by checkExpressionCached. We can simply pick up
// the resulting type and bail out.
@@ -8401,7 +8405,13 @@ namespace ts {
break;
}
}
- return cache[key] = getUnionType(antecedentTypes, subtypeReduction);
+ // The result is incomplete if the first antecedent (the non-looping control flow path)
+ // is incomplete.
+ const result = getUnionType(antecedentTypes, subtypeReduction);
+ if (isIncomplete(firstAntecedentType)) {
+ return createFlowType(result, /*incomplete*/ true);
+ }
+ return cache[key] = result;
}
function isMatchingReferenceDiscriminant(expr: Expression) {
@@ -8517,14 +8527,14 @@ namespace ts {
// We narrow a non-union type to an exact primitive type if the non-union type
// is a supertype of that primitive type. For example, type 'any' can be narrowed
// to one of the primitive types.
- const targetType = getProperty(typeofTypesByName, literal.text);
+ const targetType = typeofTypesByName[literal.text];
if (targetType && isTypeSubtypeOf(targetType, type)) {
return targetType;
}
}
const facts = assumeTrue ?
- getProperty(typeofEQFacts, literal.text) || TypeFacts.TypeofEQHostObject :
- getProperty(typeofNEFacts, literal.text) || TypeFacts.TypeofNEHostObject;
+ typeofEQFacts[literal.text] || TypeFacts.TypeofEQHostObject :
+ typeofNEFacts[literal.text] || TypeFacts.TypeofNEHostObject;
return getTypeWithFacts(type, facts);
}
@@ -8722,21 +8732,52 @@ namespace ts {
function getControlFlowContainer(node: Node): Node {
while (true) {
node = node.parent;
- if (isFunctionLike(node) || node.kind === SyntaxKind.ModuleBlock || node.kind === SyntaxKind.SourceFile || node.kind === SyntaxKind.PropertyDeclaration) {
+ if (isFunctionLike(node) && !getImmediatelyInvokedFunctionExpression(node) ||
+ node.kind === SyntaxKind.ModuleBlock ||
+ node.kind === SyntaxKind.SourceFile ||
+ node.kind === SyntaxKind.PropertyDeclaration) {
return node;
}
}
}
- function isDeclarationIncludedInFlow(reference: Node, declaration: Declaration, includeOuterFunctions: boolean) {
- const declarationContainer = getControlFlowContainer(declaration);
- let container = getControlFlowContainer(reference);
- while (container !== declarationContainer &&
- (container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.ArrowFunction) &&
- (includeOuterFunctions || getImmediatelyInvokedFunctionExpression(container))) {
- container = getControlFlowContainer(container);
+ // Check if a parameter is assigned anywhere within its declaring function.
+ function isParameterAssigned(symbol: Symbol) {
+ const func = getRootDeclaration(symbol.valueDeclaration).parent;
+ const links = getNodeLinks(func);
+ if (!(links.flags & NodeCheckFlags.AssignmentsMarked)) {
+ links.flags |= NodeCheckFlags.AssignmentsMarked;
+ if (!hasParentWithAssignmentsMarked(func)) {
+ markParameterAssignments(func);
+ }
+ }
+ return symbol.isAssigned || false;
+ }
+
+ function hasParentWithAssignmentsMarked(node: Node) {
+ while (true) {
+ node = node.parent;
+ if (!node) {
+ return false;
+ }
+ if (isFunctionLike(node) && getNodeLinks(node).flags & NodeCheckFlags.AssignmentsMarked) {
+ return true;
+ }
+ }
+ }
+
+ function markParameterAssignments(node: Node) {
+ if (node.kind === SyntaxKind.Identifier) {
+ if (isAssignmentTarget(node)) {
+ const symbol = getResolvedSymbol(node);
+ if (symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration).kind === SyntaxKind.Parameter) {
+ symbol.isAssigned = true;
+ }
+ }
+ }
+ else {
+ forEachChild(node, markParameterAssignments);
}
- return container === declarationContainer;
}
function checkIdentifier(node: Identifier): Type {
@@ -8791,15 +8832,35 @@ namespace ts {
checkNestedBlockScopedBinding(node, symbol);
const type = getTypeOfSymbol(localOrExportSymbol);
- if (!(localOrExportSymbol.flags & SymbolFlags.Variable) || isAssignmentTarget(node)) {
+ const declaration = localOrExportSymbol.valueDeclaration;
+ // We only narrow variables and parameters occurring in a non-assignment position. For all other
+ // entities we simply return the declared type.
+ if (!(localOrExportSymbol.flags & SymbolFlags.Variable) || isAssignmentTarget(node) || !declaration) {
return type;
}
- const declaration = localOrExportSymbol.valueDeclaration;
- const includeOuterFunctions = isReadonlySymbol(localOrExportSymbol);
- const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || !declaration ||
- getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) ||
- !isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions);
- const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions);
+ // The declaration container is the innermost function that encloses the declaration of the variable
+ // or parameter. The flow container is the innermost function starting with which we analyze the control
+ // flow graph to determine the control flow based type.
+ const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter;
+ const declarationContainer = getControlFlowContainer(declaration);
+ let flowContainer = getControlFlowContainer(node);
+ // When the control flow originates in a function expression or arrow function and we are referencing
+ // a const variable or parameter from an outer function, we extend the origin of the control flow
+ // analysis to include the immediately enclosing function.
+ while (flowContainer !== declarationContainer &&
+ (flowContainer.kind === SyntaxKind.FunctionExpression || flowContainer.kind === SyntaxKind.ArrowFunction) &&
+ (isReadonlySymbol(localOrExportSymbol) || isParameter && !isParameterAssigned(localOrExportSymbol))) {
+ flowContainer = getControlFlowContainer(flowContainer);
+ }
+ // We only look for uninitialized variables in strict null checking mode, and only when we can analyze
+ // the entire control flow graph from the variable's declaration (i.e. when the flow container and
+ // declaration container are the same).
+ const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isParameter ||
+ flowContainer !== declarationContainer || isInAmbientContext(declaration);
+ const flowType = getFlowTypeOfReference(node, type, assumeInitialized, flowContainer);
+ // A variable is considered uninitialized when it is possible to analyze the entire control flow graph
+ // from declaration to use, and when the variable's declared type doesn't include undefined but the
+ // control flow based type does include undefined.
if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
// Return the declared type to reduce follow-on errors
@@ -9052,7 +9113,7 @@ namespace ts {
if (isClassLike(container.parent)) {
const symbol = getSymbolOfNode(container.parent);
const type = container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol)).thisType;
- return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*includeOuterFunctions*/ true);
+ return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*flowContainer*/ undefined);
}
if (isInJavaScriptFile(node)) {
@@ -9358,7 +9419,7 @@ namespace ts {
}
}
if (isBindingPattern(declaration.name)) {
- return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ true);
+ return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ true, /*reportErrors*/ false);
}
if (isBindingPattern(declaration.parent)) {
const parentDeclaration = declaration.parent.parent;
@@ -9463,6 +9524,11 @@ namespace ts {
const binaryExpression = node.parent;
const operator = binaryExpression.operatorToken.kind;
if (operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) {
+ // Don't do this for special property assignments to avoid circularity
+ if (getSpecialPropertyAssignmentKind(binaryExpression) !== SpecialPropertyAssignmentKind.None) {
+ return undefined;
+ }
+
// In an assignment expression, the right operand is contextually typed by the type of the left operand.
if (node === binaryExpression.right) {
return checkExpression(binaryExpression.left);
@@ -10135,10 +10201,9 @@ namespace ts {
const correspondingPropSymbol = getPropertyOfType(elementAttributesType, node.name.text);
correspondingPropType = correspondingPropSymbol && getTypeOfSymbol(correspondingPropSymbol);
if (isUnhyphenatedJsxName(node.name.text)) {
- // Maybe there's a string indexer?
- const indexerType = getIndexTypeOfType(elementAttributesType, IndexKind.String);
- if (indexerType) {
- correspondingPropType = indexerType;
+ const attributeType = getTypeOfPropertyOfType(elementAttributesType, getTextOfPropertyName(node.name)) || getIndexTypeOfType(elementAttributesType, IndexKind.String);
+ if (attributeType) {
+ correspondingPropType = attributeType;
}
else {
// If there's no corresponding property with this name, error
@@ -10713,7 +10778,7 @@ namespace ts {
!(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
return propType;
}
- return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*includeOuterFunctions*/ false);
+ return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*flowContainer*/ undefined);
}
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean {
@@ -10838,10 +10903,11 @@ namespace ts {
}
// Check for compatible indexer types.
- if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
+ const allowedNullableFlags = strictNullChecks ? 0 : TypeFlags.Nullable;
+ if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol | allowedNullableFlags)) {
// Try to use a number indexer.
- if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike) || isForInVariableForNumericPropertyNames(node.argumentExpression)) {
+ if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike | allowedNullableFlags) || isForInVariableForNumericPropertyNames(node.argumentExpression)) {
const numberIndexInfo = getIndexInfoOfType(objectType, IndexKind.Number);
if (numberIndexInfo) {
getNodeLinks(node).resolvedIndexInfo = numberIndexInfo;
@@ -13801,15 +13867,19 @@ namespace ts {
}
function checkClassForDuplicateDeclarations(node: ClassLikeDeclaration) {
- const getter = 1, setter = 2, property = getter | setter;
+ const enum Accessor {
+ Getter = 1,
+ Setter = 2,
+ Property = Getter | Setter
+ }
- const instanceNames = createMap();
- const staticNames = createMap();
+ const instanceNames = createMap();
+ const staticNames = createMap();
for (const member of node.members) {
if (member.kind === SyntaxKind.Constructor) {
for (const param of (member as ConstructorDeclaration).parameters) {
if (isParameterPropertyDeclaration(param)) {
- addName(instanceNames, param.name, (param.name as Identifier).text, property);
+ addName(instanceNames, param.name, (param.name as Identifier).text, Accessor.Property);
}
}
}
@@ -13821,22 +13891,22 @@ namespace ts {
if (memberName) {
switch (member.kind) {
case SyntaxKind.GetAccessor:
- addName(names, member.name, memberName, getter);
+ addName(names, member.name, memberName, Accessor.Getter);
break;
case SyntaxKind.SetAccessor:
- addName(names, member.name, memberName, setter);
+ addName(names, member.name, memberName, Accessor.Setter);
break;
case SyntaxKind.PropertyDeclaration:
- addName(names, member.name, memberName, property);
+ addName(names, member.name, memberName, Accessor.Property);
break;
}
}
}
}
- function addName(names: Map, location: Node, name: string, meaning: number) {
+ function addName(names: Map, location: Node, name: string, meaning: Accessor) {
const prev = names[name];
if (prev) {
if (prev & meaning) {
@@ -15118,7 +15188,7 @@ namespace ts {
else if (member.kind === SyntaxKind.Constructor) {
for (const parameter of (member).parameters) {
if (!parameter.symbol.isReferenced && parameter.flags & NodeFlags.Private) {
- error(parameter.name, Diagnostics._0_is_declared_but_never_used, parameter.symbol.name);
+ error(parameter.name, Diagnostics.Property_0_is_declared_but_never_used, parameter.symbol.name);
}
}
}
@@ -17041,11 +17111,6 @@ namespace ts {
}
}
- if (compilerOptions.noImplicitAny && !node.body) {
- // Ambient shorthand module is an implicit any
- reportImplicitAnyError(node, anyType);
- }
-
if (node.body) {
checkSourceElement(node.body);
if (!isGlobalScopeAugmentation(node)) {
@@ -17549,11 +17614,10 @@ namespace ts {
}
function checkSourceFile(node: SourceFile) {
- const start = performance.mark();
-
+ performance.mark("beforeCheck");
checkSourceFileWorker(node);
-
- performance.measure("Check", start);
+ performance.mark("afterCheck");
+ performance.measure("Check", "beforeCheck", "afterCheck");
}
// Fully type check a source file and collect the relevant diagnostics.
@@ -18207,7 +18271,7 @@ namespace ts {
// otherwise - check if at least one export is value
symbolLinks.exportsSomeValue = hasExportAssignment
? !!(moduleSymbol.flags & SymbolFlags.Value)
- : forEachValue(getExportsOfModule(moduleSymbol), isValue);
+ : forEachProperty(getExportsOfModule(moduleSymbol), isValue);
}
return symbolLinks.exportsSomeValue;
diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts
index eaf17f00dd0..6406455d713 100644
--- a/src/compiler/commandLineParser.ts
+++ b/src/compiler/commandLineParser.ts
@@ -61,10 +61,10 @@ namespace ts {
},
{
name: "jsx",
- type: {
+ type: createMap({
"preserve": JsxEmit.Preserve,
"react": JsxEmit.React
- },
+ }),
paramType: Diagnostics.KIND,
description: Diagnostics.Specify_JSX_code_generation_Colon_preserve_or_react,
},
@@ -91,7 +91,7 @@ namespace ts {
{
name: "module",
shortName: "m",
- type: {
+ type: createMap({
"none": ModuleKind.None,
"commonjs": ModuleKind.CommonJS,
"amd": ModuleKind.AMD,
@@ -99,16 +99,16 @@ namespace ts {
"umd": ModuleKind.UMD,
"es6": ModuleKind.ES6,
"es2015": ModuleKind.ES2015,
- },
+ }),
description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_or_es2015,
paramType: Diagnostics.KIND,
},
{
name: "newLine",
- type: {
+ type: createMap({
"crlf": NewLineKind.CarriageReturnLineFeed,
"lf": NewLineKind.LineFeed
- },
+ }),
description: Diagnostics.Specify_the_end_of_line_sequence_to_be_used_when_emitting_files_Colon_CRLF_dos_or_LF_unix,
paramType: Diagnostics.NEWLINE,
},
@@ -126,6 +126,10 @@ namespace ts {
type: "boolean",
description: Diagnostics.Do_not_emit_outputs_if_any_errors_were_reported,
},
+ {
+ name: "noErrorTruncation",
+ type: "boolean"
+ },
{
name: "noImplicitAny",
type: "boolean",
@@ -250,12 +254,12 @@ namespace ts {
{
name: "target",
shortName: "t",
- type: {
+ type: createMap({
"es3": ScriptTarget.ES3,
"es5": ScriptTarget.ES5,
"es6": ScriptTarget.ES6,
"es2015": ScriptTarget.ES2015,
- },
+ }),
description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES2015,
paramType: Diagnostics.VERSION,
},
@@ -284,10 +288,10 @@ namespace ts {
},
{
name: "moduleResolution",
- type: {
+ type: createMap({
"node": ModuleResolutionKind.NodeJs,
"classic": ModuleResolutionKind.Classic,
- },
+ }),
description: Diagnostics.Specify_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6,
},
{
@@ -392,7 +396,7 @@ namespace ts {
type: "list",
element: {
name: "lib",
- type: {
+ type: createMap({
// JavaScript only
"es5": "lib.es5.d.ts",
"es6": "lib.es2015.d.ts",
@@ -417,7 +421,7 @@ namespace ts {
"es2016.array.include": "lib.es2016.array.include.d.ts",
"es2017.object": "lib.es2017.object.d.ts",
"es2017.sharedmemory": "lib.es2017.sharedmemory.d.ts"
- },
+ }),
},
description: Diagnostics.Specify_library_files_to_be_included_in_the_compilation_Colon
},
@@ -462,6 +466,14 @@ namespace ts {
shortOptionNames: Map;
}
+ /* @internal */
+ export const defaultInitCompilerOptions: CompilerOptions = {
+ module: ModuleKind.CommonJS,
+ target: ScriptTarget.ES5,
+ noImplicitAny: false,
+ sourceMap: false,
+ };
+
let optionNameMapCache: OptionNameMap;
/* @internal */
@@ -486,10 +498,9 @@ namespace ts {
/* @internal */
export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType): Diagnostic {
const namesOfType: string[] = [];
- forEachKey(opt.type, key => {
+ for (const key in opt.type) {
namesOfType.push(` '${key}'`);
- });
-
+ }
return createCompilerDiagnostic(Diagnostics.Argument_for_0_option_must_be_Colon_1, `--${opt.name}`, namesOfType);
}
@@ -497,7 +508,7 @@ namespace ts {
export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) {
const key = trimString((value || "")).toLowerCase();
const map = opt.type;
- if (hasProperty(map, key)) {
+ if (key in map) {
return map[key];
}
else {
@@ -551,11 +562,11 @@ namespace ts {
s = s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase();
// Try to translate short option names to their full equivalents.
- if (hasProperty(shortOptionNames, s)) {
+ if (s in shortOptionNames) {
s = shortOptionNames[s];
}
- if (hasProperty(optionNameMap, s)) {
+ if (s in optionNameMap) {
const opt = optionNameMap[s];
if (opt.isTSConfigOnly) {
@@ -668,6 +679,94 @@ namespace ts {
}
}
+ /**
+ * Generate tsconfig configuration when running command line "--init"
+ * @param options commandlineOptions to be generated into tsconfig.json
+ * @param fileNames array of filenames to be generated into tsconfig.json
+ */
+ /* @internal */
+ export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: Map } {
+ const compilerOptions = extend(options, defaultInitCompilerOptions);
+ const configurations: any = {
+ compilerOptions: serializeCompilerOptions(compilerOptions)
+ };
+ if (fileNames && fileNames.length) {
+ // only set the files property if we have at least one file
+ configurations.files = fileNames;
+ }
+
+ return configurations;
+
+ function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map | undefined {
+ if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean") {
+ // this is of a type CommandLineOptionOfPrimitiveType
+ return undefined;
+ }
+ else if (optionDefinition.type === "list") {
+ return getCustomTypeMapOfCommandLineOption((optionDefinition).element);
+ }
+ else {
+ return (optionDefinition).type;
+ }
+ }
+
+ function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: MapLike): string | undefined {
+ // There is a typeMap associated with this command-line option so use it to map value back to its name
+ for (const key in customTypeMap) {
+ if (customTypeMap[key] === value) {
+ return key;
+ }
+ }
+ return undefined;
+ }
+
+ function serializeCompilerOptions(options: CompilerOptions): Map {
+ const result = createMap();
+ const optionsNameMap = getOptionNameMap().optionNameMap;
+
+ for (const name in options) {
+ if (hasProperty(options, name)) {
+ // tsconfig only options cannot be specified via command line,
+ // so we can assume that only types that can appear here string | number | boolean
+ switch (name) {
+ case "init":
+ case "watch":
+ case "version":
+ case "help":
+ case "project":
+ break;
+ default:
+ const value = options[name];
+ let optionDefinition = optionsNameMap[name.toLowerCase()];
+ if (optionDefinition) {
+ const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition);
+ if (!customTypeMap) {
+ // There is no map associated with this compiler option then use the value as-is
+ // This is the case if the value is expect to be string, number, boolean or list of string
+ result[name] = value;
+ }
+ else {
+ if (optionDefinition.type === "list") {
+ const convertedValue: string[] = [];
+ for (const element of value as (string | number)[]) {
+ convertedValue.push(getNameOfCompilerOptionValue(element, customTypeMap));
+ }
+ result[name] = convertedValue;
+ }
+ else {
+ // There is a typeMap associated with this command-line option so use it to map value back to its name
+ result[name] = getNameOfCompilerOptionValue(value, customTypeMap);
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ return result;
+ }
+ }
+
/**
* Remove the comments from a json like text.
* Comments can be single line comments (starting with # or //) or multiline comments using / * * /
@@ -811,7 +910,7 @@ namespace ts {
const optionNameMap = arrayToMap(optionDeclarations, opt => opt.name);
for (const id in jsonOptions) {
- if (hasProperty(optionNameMap, id)) {
+ if (id in optionNameMap) {
const opt = optionNameMap[id];
defaultOptions[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors);
}
@@ -848,7 +947,7 @@ namespace ts {
function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) {
const key = value.toLowerCase();
- if (hasProperty(opt.type, key)) {
+ if (key in opt.type) {
return opt.type[key];
}
else {
@@ -1011,7 +1110,7 @@ namespace ts {
removeWildcardFilesWithLowerPriorityExtension(file, wildcardFileMap, supportedExtensions, keyMapper);
const key = keyMapper(file);
- if (!hasProperty(literalFileMap, key) && !hasProperty(wildcardFileMap, key)) {
+ if (!(key in literalFileMap) && !(key in wildcardFileMap)) {
wildcardFileMap[key] = file;
}
}
@@ -1076,7 +1175,7 @@ namespace ts {
if (match) {
const key = useCaseSensitiveFileNames ? match[0] : match[0].toLowerCase();
const flags = watchRecursivePattern.test(name) ? WatchDirectoryFlags.Recursive : WatchDirectoryFlags.None;
- const existingFlags = getProperty(wildcardDirectories, key);
+ const existingFlags = wildcardDirectories[key];
if (existingFlags === undefined || existingFlags < flags) {
wildcardDirectories[key] = flags;
if (flags === WatchDirectoryFlags.Recursive) {
@@ -1088,11 +1187,9 @@ namespace ts {
// Remove any subpaths under an existing recursively watched directory.
for (const key in wildcardDirectories) {
- if (hasProperty(wildcardDirectories, key)) {
- for (const recursiveKey of recursiveKeys) {
- if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) {
- delete wildcardDirectories[key];
- }
+ for (const recursiveKey of recursiveKeys) {
+ if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) {
+ delete wildcardDirectories[key];
}
}
}
@@ -1115,7 +1212,7 @@ namespace ts {
for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) {
const higherPriorityExtension = extensions[i];
const higherPriorityPath = keyMapper(changeExtension(file, higherPriorityExtension));
- if (hasProperty(literalFiles, higherPriorityPath) || hasProperty(wildcardFiles, higherPriorityPath)) {
+ if (higherPriorityPath in literalFiles || higherPriorityPath in wildcardFiles) {
return true;
}
}
diff --git a/src/compiler/core.ts b/src/compiler/core.ts
index 808f8a538df..27b27bc6532 100644
--- a/src/compiler/core.ts
+++ b/src/compiler/core.ts
@@ -21,10 +21,8 @@ namespace ts {
const createObject = Object.create;
- export function createMap(): Map {
- /* tslint:disable:no-null-keyword */
- const map: Map = createObject(null);
- /* tslint:enable:no-null-keyword */
+ export function createMap(template?: MapLike): Map {
+ const map: Map = createObject(null); // tslint:disable-line:no-null-keyword
// Using 'delete' on an object causes V8 to put the object in dictionary mode.
// This disables creation of hidden classes, which are expensive when an object is
@@ -32,6 +30,12 @@ namespace ts {
map["__"] = undefined;
delete map["__"];
+ // Copies keys/values from template. Note that for..in will not throw if
+ // template is undefined, and instead will just exit the loop.
+ for (const key in template) if (hasOwnProperty.call(template, key)) {
+ map[key] = template[key];
+ }
+
return map;
}
@@ -62,7 +66,7 @@ namespace ts {
}
function contains(path: Path) {
- return hasProperty(files, toKey(path));
+ return toKey(path) in files;
}
function remove(path: Path) {
@@ -202,6 +206,21 @@ namespace ts {
return array;
}
+ export function removeWhere(array: T[], f: (x: T) => boolean): boolean {
+ let outIndex = 0;
+ for (const item of array) {
+ if (!f(item)) {
+ array[outIndex] = item;
+ outIndex++;
+ }
+ }
+ if (outIndex !== array.length) {
+ array.length = outIndex;
+ return true;
+ }
+ return false;
+ }
+
export function filterMutate(array: T[], f: (x: T) => boolean): void {
let outIndex = 0;
for (const item of array) {
@@ -364,82 +383,142 @@ namespace ts {
const hasOwnProperty = Object.prototype.hasOwnProperty;
+ /**
+ * Indicates whether a map-like contains an own property with the specified key.
+ *
+ * NOTE: This is intended for use only with MapLike objects. For Map objects, use
+ * the 'in' operator.
+ *
+ * @param map A map-like.
+ * @param key A property key.
+ */
export function hasProperty(map: MapLike, key: string): boolean {
return hasOwnProperty.call(map, key);
}
- export function getKeys(map: MapLike): string[] {
+ /**
+ * Gets the value of an owned property in a map-like.
+ *
+ * NOTE: This is intended for use only with MapLike objects. For Map objects, use
+ * an indexer.
+ *
+ * @param map A map-like.
+ * @param key A property key.
+ */
+ export function getProperty(map: MapLike, key: string): T | undefined {
+ return hasOwnProperty.call(map, key) ? map[key] : undefined;
+ }
+
+ /**
+ * Gets the owned, enumerable property keys of a map-like.
+ *
+ * NOTE: This is intended for use with MapLike objects. For Map objects, use
+ * Object.keys instead as it offers better performance.
+ *
+ * @param map A map-like.
+ */
+ export function getOwnKeys(map: MapLike): string[] {
const keys: string[] = [];
- for (const key in map) {
+ for (const key in map) if (hasOwnProperty.call(map, key)) {
keys.push(key);
}
return keys;
}
- export function getProperty(map: MapLike, key: string): T | undefined {
- return hasProperty(map, key) ? map[key] : undefined;
+ /**
+ * Enumerates the properties of a Map, invoking a callback and returning the first truthy result.
+ *
+ * @param map A map for which properties should be enumerated.
+ * @param callback A callback to invoke for each property.
+ */
+ export function forEachProperty(map: Map, callback: (value: T, key: string) => U): U {
+ let result: U;
+ for (const key in map) {
+ if (result = callback(map[key], key)) break;
+ }
+ return result;
}
- export function getOrUpdateProperty(map: MapLike, key: string, makeValue: () => T): T {
- return hasProperty(map, key) ? map[key] : map[key] = makeValue();
+ /**
+ * Returns true if a Map has some matching property.
+ *
+ * @param map A map whose properties should be tested.
+ * @param predicate An optional callback used to test each property.
+ */
+ export function someProperties(map: Map, predicate?: (value: T, key: string) => boolean) {
+ for (const key in map) {
+ if (!predicate || predicate(map[key], key)) return true;
+ }
+ return false;
}
- export function isEmpty(map: MapLike) {
- for (const id in map) {
- if (hasProperty(map, id)) {
- return false;
- }
+ /**
+ * Performs a shallow copy of the properties from a source Map to a target MapLike
+ *
+ * @param source A map from which properties should be copied.
+ * @param target A map to which properties should be copied.
+ */
+ export function copyProperties(source: Map, target: MapLike): void {
+ for (const key in source) {
+ target[key] = source[key];
+ }
+ }
+
+ /**
+ * Reduce the properties of a map.
+ *
+ * NOTE: This is intended for use with Map objects. For MapLike objects, use
+ * reduceOwnProperties instead as it offers better runtime safety.
+ *
+ * @param map The map to reduce
+ * @param callback An aggregation function that is called for each entry in the map
+ * @param initial The initial value for the reduction.
+ */
+ export function reduceProperties(map: Map, callback: (aggregate: U, value: T, key: string) => U, initial: U): U {
+ let result = initial;
+ for (const key in map) {
+ result = callback(result, map[key], String(key));
+ }
+ return result;
+ }
+
+ /**
+ * Reduce the properties defined on a map-like (but not from its prototype chain).
+ *
+ * NOTE: This is intended for use with MapLike objects. For Map objects, use
+ * reduceProperties instead as it offers better performance.
+ *
+ * @param map The map-like to reduce
+ * @param callback An aggregation function that is called for each entry in the map
+ * @param initial The initial value for the reduction.
+ */
+ export function reduceOwnProperties(map: MapLike, callback: (aggregate: U, value: T, key: string) => U, initial: U): U {
+ let result = initial;
+ for (const key in map) if (hasOwnProperty.call(map, key)) {
+ result = callback(result, map[key], String(key));
+ }
+ return result;
+ }
+
+ /**
+ * Performs a shallow equality comparison of the contents of two map-likes.
+ *
+ * @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) {
+ 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;
+ }
+ for (const key in right) if (hasOwnProperty.call(right, key)) {
+ if (!hasOwnProperty.call(left, key)) return false;
}
return true;
}
- export function clone(object: T): T {
- const result: any = {};
- for (const id in object) {
- result[id] = (object)[id];
- }
- return result;
- }
-
- export function extend, T2 extends MapLike<{}>>(first: T1 , second: T2): T1 & T2 {
- const result: T1 & T2 = {};
- for (const id in first) {
- (result as any)[id] = first[id];
- }
- for (const id in second) {
- if (!hasProperty(result, id)) {
- (result as any)[id] = second[id];
- }
- }
- return result;
- }
-
- export function forEachValue(map: MapLike, callback: (value: T) => U): U {
- let result: U;
- for (const id in map) {
- if (result = callback(map[id])) break;
- }
- return result;
- }
-
- export function forEachKey(map: MapLike, callback: (key: string) => U): U {
- let result: U;
- for (const id in map) {
- if (result = callback(id)) break;
- }
- return result;
- }
-
- export function lookUp(map: MapLike, key: string): T {
- return hasProperty(map, key) ? map[key] : undefined;
- }
-
- export function copyMap(source: MapLike, target: MapLike): void {
- for (const p in source) {
- target[p] = source[p];
- }
- }
-
/**
* Creates a map from the elements of an array.
*
@@ -450,33 +529,40 @@ namespace ts {
* the same key with the given 'makeKey' function, then the element with the higher
* index in the array will be the one associated with the produced key.
*/
- export function arrayToMap(array: T[], makeKey: (value: T) => string): Map {
- const result = createMap();
-
- forEach(array, value => {
- result[makeKey(value)] = value;
- });
-
+ export function arrayToMap(array: T[], makeKey: (value: T) => string): Map;
+ export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue: (value: T) => U): Map;
+ export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map {
+ const result = createMap();
+ for (const value of array) {
+ result[makeKey(value)] = makeValue ? makeValue(value) : value;
+ }
return result;
}
- /**
- * Reduce the properties of a map.
- *
- * @param map The map to reduce
- * @param callback An aggregation function that is called for each entry in the map
- * @param initial The initial value for the reduction.
- */
- export function reduceProperties(map: MapLike, callback: (aggregate: U, value: T, key: string) => U, initial: U): U {
- let result = initial;
- if (map) {
- for (const key in map) {
- if (hasProperty(map, key)) {
- result = callback(result, map[key], String(key));
- }
+ export function cloneMap(map: Map) {
+ const clone = createMap();
+ copyProperties(map, clone);
+ return clone;
+ }
+
+ export function clone(object: T): T {
+ const result: any = {};
+ for (const id in object) {
+ if (hasOwnProperty.call(object, id)) {
+ result[id] = (object)[id];
}
}
+ return result;
+ }
+ export function extend(first: T1 , second: T2): T1 & T2 {
+ const result: T1 & T2 = {};
+ for (const id in second) if (hasOwnProperty.call(second, id)) {
+ (result as any)[id] = (second as any)[id];
+ }
+ for (const id in first) if (hasOwnProperty.call(first, id)) {
+ (result as any)[id] = (first as any)[id];
+ }
return result;
}
diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts
index 56ff206ab01..3642ee7f10a 100644
--- a/src/compiler/declarationEmitter.ts
+++ b/src/compiler/declarationEmitter.ts
@@ -157,9 +157,7 @@ namespace ts {
if (usedTypeDirectiveReferences) {
for (const directive in usedTypeDirectiveReferences) {
- if (hasProperty(usedTypeDirectiveReferences, directive)) {
- referencesOutput += `/// ${newLine}`;
- }
+ referencesOutput += `/// ${newLine}`;
}
}
@@ -272,7 +270,7 @@ namespace ts {
usedTypeDirectiveReferences = createMap();
}
for (const directive of typeReferenceDirectives) {
- if (!hasProperty(usedTypeDirectiveReferences, directive)) {
+ if (!(directive in usedTypeDirectiveReferences)) {
usedTypeDirectiveReferences[directive] = directive;
}
}
@@ -537,14 +535,14 @@ namespace ts {
// do not need to keep track of created temp names.
function getExportDefaultTempVariableName(): string {
const baseName = "_default";
- if (!hasProperty(currentIdentifiers, baseName)) {
+ if (!(baseName in currentIdentifiers)) {
return baseName;
}
let count = 0;
while (true) {
count++;
const name = baseName + "_" + count;
- if (!hasProperty(currentIdentifiers, name)) {
+ if (!(name in currentIdentifiers)) {
return name;
}
}
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 8975e5daa04..6210a20afa6 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -2235,7 +2235,7 @@
"category": "Error",
"code": 4082
},
- "Conflicting library definitions for '{0}' found at '{1}' and '{2}'. Copy the correct file to the 'typings' folder to resolve this conflict.": {
+ "Conflicting definitions for '{0}' found at '{1}' and '{2}'. Consider installing a specific version of this library to resolve the conflict.": {
"category": "Message",
"code": 4090
},
@@ -2832,6 +2832,10 @@
"category": "Message",
"code": 6137
},
+ "Property '{0}' is declared but never used.": {
+ "category": "Error",
+ "code": 6138
+ },
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts
index 1d62a4a4f46..357a15507a4 100644
--- a/src/compiler/emitter.ts
+++ b/src/compiler/emitter.ts
@@ -24,7 +24,7 @@ namespace ts {
Return = 1 << 3
}
- const entities: MapLike = {
+ const entities = createMap({
"quot": 0x0022,
"amp": 0x0026,
"apos": 0x0027,
@@ -278,7 +278,7 @@ namespace ts {
"clubs": 0x2663,
"hearts": 0x2665,
"diams": 0x2666
- };
+ });
// Flags enum to track count of temp variables and a few dedicated names
const enum TempFlags {
@@ -407,7 +407,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
function isUniqueLocalName(name: string, container: Node): boolean {
for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) {
- if (node.locals && hasProperty(node.locals, name)) {
+ if (node.locals && name in node.locals) {
// We conservatively include alias symbols to cover cases where they're emitted as locals
if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) {
return false;
@@ -531,7 +531,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
let currentText: string;
let currentLineMap: number[];
let currentFileIdentifiers: Map;
- let renamedDependencies: MapLike;
+ let renamedDependencies: Map;
let isEs6Module: boolean;
let isCurrentFileExternalModule: boolean;
@@ -577,21 +577,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
const setSourceMapWriterEmit = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? changeSourceMapEmit : function (writer: SourceMapWriter) { };
- const moduleEmitDelegates: MapLike<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void> = {
+ const moduleEmitDelegates = createMap<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void>({
[ModuleKind.ES6]: emitES6Module,
[ModuleKind.AMD]: emitAMDModule,
[ModuleKind.System]: emitSystemModule,
[ModuleKind.UMD]: emitUMDModule,
[ModuleKind.CommonJS]: emitCommonJSModule,
- };
+ });
- const bundleEmitDelegates: MapLike<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void> = {
+ const bundleEmitDelegates = createMap<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void>({
[ModuleKind.ES6]() {},
[ModuleKind.AMD]: emitAMDModule,
[ModuleKind.System]: emitSystemModule,
[ModuleKind.UMD]() {},
[ModuleKind.CommonJS]() {},
- };
+ });
return doEmit;
@@ -669,8 +669,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
function isUniqueName(name: string): boolean {
return !resolver.hasGlobalName(name) &&
- !hasProperty(currentFileIdentifiers, name) &&
- !hasProperty(generatedNameSet, name);
+ !(name in currentFileIdentifiers) &&
+ !(name in generatedNameSet);
}
// Return the next available name in the pattern _a ... _z, _0, _1, ...
@@ -2646,7 +2646,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
return false;
}
- return !exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, (node).text);
+ return !exportEquals && exportSpecifiers && (node).text in exportSpecifiers;
}
function emitPrefixUnaryExpression(node: PrefixUnaryExpression) {
@@ -3263,7 +3263,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
write(", ");
}
- if (!hasProperty(seen, id.text)) {
+ if (!(id.text in seen)) {
emit(id);
seen[id.text] = id.text;
}
@@ -3970,7 +3970,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
return;
}
- if (!exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, name.text)) {
+ if (!exportEquals && exportSpecifiers && name.text in exportSpecifiers) {
for (const specifier of exportSpecifiers[name.text]) {
writeLine();
emitStart(specifier.name);
@@ -5311,18 +5311,22 @@ const _super = (function (geti, seti) {
emitSignatureParameters(ctor);
}
else {
- // Based on EcmaScript6 section 14.5.14: Runtime Semantics: ClassDefinitionEvaluation.
- // If constructor is empty, then,
- // If ClassHeritageopt is present, then
- // Let constructor be the result of parsing the String "constructor(... args){ super (...args);}" using the syntactic grammar with the goal symbol MethodDefinition.
+ // The ES2015 spec specifies in 14.5.14. Runtime Semantics: ClassDefinitionEvaluation:
+ // If constructor is empty, then
+ // If ClassHeritag_eopt is present and protoParent is not null, then
+ // Let constructor be the result of parsing the source text
+ // constructor(...args) { super (...args);}
+ // using the syntactic grammar with the goal symbol MethodDefinition[~Yield].
// Else,
- // Let constructor be the result of parsing the String "constructor( ){ }" using the syntactic grammar with the goal symbol MethodDefinition
- if (baseTypeElement) {
- write("(...args)");
- }
- else {
- write("()");
- }
+ // Let constructor be the result of parsing the source text
+ // constructor( ){ }
+ // using the syntactic grammar with the goal symbol MethodDefinition[~Yield].
+ //
+ // While we could emit the '...args' rest parameter, certain later tools in the pipeline might
+ // downlevel the '...args' portion less efficiently by naively copying the contents of 'arguments' to an array.
+ // Instead, we'll avoid using a rest parameter and spread into the super call as
+ // 'super(...arguments)' instead of 'super(...args)', as you can see below.
+ write("()");
}
}
@@ -5360,7 +5364,8 @@ const _super = (function (geti, seti) {
write("_super.apply(this, arguments);");
}
else {
- write("super(...args);");
+ // See comment above on using '...arguments' instead of '...args'.
+ write("super(...arguments);");
}
emitEnd(baseTypeElement);
}
@@ -6461,7 +6466,7 @@ const _super = (function (geti, seti) {
* Here we check if alternative name was provided for a given moduleName and return it if possible.
*/
function tryRenameExternalModule(moduleName: LiteralExpression): string {
- if (renamedDependencies && hasProperty(renamedDependencies, moduleName.text)) {
+ if (renamedDependencies && moduleName.text in renamedDependencies) {
return `"${renamedDependencies[moduleName.text]}"`;
}
return undefined;
@@ -6842,7 +6847,7 @@ const _super = (function (geti, seti) {
// export { x, y }
for (const specifier of (node).exportClause.elements) {
const name = (specifier.propertyName || specifier.name).text;
- getOrUpdateProperty(exportSpecifiers, name, () => []).push(specifier);
+ (exportSpecifiers[name] || (exportSpecifiers[name] = [])).push(specifier);
}
}
break;
@@ -6941,7 +6946,7 @@ const _super = (function (geti, seti) {
}
// local names set should only be added if we have anything exported
- if (!exportedDeclarations && isEmpty(exportSpecifiers)) {
+ if (!exportedDeclarations && !someProperties(exportSpecifiers)) {
// no exported declarations (export var ...) or export specifiers (export {x})
// check if we have any non star export declarations.
let hasExportDeclarationWithExportClause = false;
@@ -7091,7 +7096,7 @@ const _super = (function (geti, seti) {
if (name) {
// do not emit duplicate entries (in case of declaration merging) in the list of hoisted variables
const text = unescapeIdentifier(name.text);
- if (hasProperty(seen, text)) {
+ if (text in seen) {
continue;
}
else {
@@ -7460,7 +7465,7 @@ const _super = (function (geti, seti) {
// for deduplication purposes in key remove leading and trailing quotes so 'a' and "a" will be considered the same
const key = text.substr(1, text.length - 2);
- if (hasProperty(groupIndices, key)) {
+ if (key in groupIndices) {
// deduplicate/group entries in dependency list by the dependency name
const groupIndex = groupIndices[key];
dependencyGroups[groupIndex].push(externalImports[i]);
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 6f38783d5ed..b05451340d1 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -417,14 +417,16 @@ namespace ts {
case SyntaxKind.JSDocPropertyTag:
return visitNode(cbNode, (node).typeExpression) ||
visitNode(cbNode, (node).name);
+ case SyntaxKind.JSDocLiteralType:
+ return visitNode(cbNode, (node).literal);
}
}
export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile {
- const start = performance.mark();
+ performance.mark("beforeParse");
const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind);
-
- performance.measure("Parse", start);
+ performance.mark("afterParse");
+ performance.measure("Parse", "beforeParse", "afterParse");
return result;
}
@@ -5889,9 +5891,13 @@ namespace ts {
case SyntaxKind.SymbolKeyword:
case SyntaxKind.VoidKeyword:
return parseTokenNode();
+ case SyntaxKind.StringLiteral:
+ case SyntaxKind.NumericLiteral:
+ case SyntaxKind.TrueKeyword:
+ case SyntaxKind.FalseKeyword:
+ return parseJSDocLiteralType();
}
- // TODO (drosen): Parse string literal types in JSDoc as well.
return parseJSDocTypeReference();
}
@@ -6070,6 +6076,12 @@ namespace ts {
return finishNode(result);
}
+ function parseJSDocLiteralType(): JSDocLiteralType {
+ const result = createNode(SyntaxKind.JSDocLiteralType);
+ result.literal = parseLiteralTypeNode();
+ return finishNode(result);
+ }
+
function parseJSDocUnknownOrNullableType(): JSDocUnknownType | JSDocNullableType {
const pos = scanner.getStartPos();
// skip the ?
diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts
index e496353fde8..e8f064e81cd 100644
--- a/src/compiler/performance.ts
+++ b/src/compiler/performance.ts
@@ -6,71 +6,57 @@ namespace ts {
}
/*@internal*/
+/** Performance measurements for the compiler. */
namespace ts.performance {
- /** Performance measurements for the compiler. */
declare const onProfilerEvent: { (markName: string): void; profiler: boolean; };
- let profilerEvent: (markName: string) => void;
- let counters: MapLike;
- let measures: MapLike;
+
+ const profilerEvent = typeof onProfilerEvent === "function" && onProfilerEvent.profiler === true
+ ? onProfilerEvent
+ : (markName: string) => { };
+
+ let enabled = false;
+ let profilerStart = 0;
+ let counts: Map;
+ let marks: Map;
+ let measures: Map;
/**
- * Emit a performance event if ts-profiler is connected. This is primarily used
- * to generate heap snapshots.
+ * Marks a performance event.
*
- * @param eventName A name for the event.
+ * @param markName The name of the mark.
*/
- export function emit(eventName: string) {
- if (profilerEvent) {
- profilerEvent(eventName);
+ export function mark(markName: string) {
+ if (enabled) {
+ marks[markName] = timestamp();
+ counts[markName] = (counts[markName] || 0) + 1;
+ profilerEvent(markName);
}
}
- /**
- * Increments a counter with the specified name.
- *
- * @param counterName The name of the counter.
- */
- export function increment(counterName: string) {
- if (counters) {
- counters[counterName] = (getProperty(counters, counterName) || 0) + 1;
- }
- }
-
- /**
- * Gets the value of the counter with the specified name.
- *
- * @param counterName The name of the counter.
- */
- export function getCount(counterName: string) {
- return counters && getProperty(counters, counterName) || 0;
- }
-
- /**
- * Marks the start of a performance measurement.
- */
- export function mark() {
- return measures ? timestamp() : 0;
- }
-
/**
* Adds a performance measurement with the specified name.
*
* @param measureName The name of the performance measurement.
- * @param marker The timestamp of the starting mark.
+ * @param startMarkName The name of the starting mark. If not supplied, the point at which the
+ * profiler was enabled is used.
+ * @param endMarkName The name of the ending mark. If not supplied, the current timestamp is
+ * used.
*/
- export function measure(measureName: string, marker: number) {
- if (measures) {
- measures[measureName] = (getProperty(measures, measureName) || 0) + (timestamp() - marker);
+ export function measure(measureName: string, startMarkName?: string, endMarkName?: string) {
+ if (enabled) {
+ const end = endMarkName && marks[endMarkName] || timestamp();
+ const start = startMarkName && marks[startMarkName] || profilerStart;
+ measures[measureName] = (measures[measureName] || 0) + (end - start);
}
}
/**
- * Iterate over each measure, performing some action
- *
- * @param cb The action to perform for each measure
+ * Gets the number of times a marker was encountered.
+ *
+ * @param markName The name of the mark.
*/
- export function forEachMeasure(cb: (measureName: string, duration: number) => void) {
- return forEachKey(measures, key => cb(key, measures[key]));
+ export function getCount(markName: string) {
+ return counts && counts[markName] || 0;
}
/**
@@ -79,31 +65,31 @@ namespace ts.performance {
* @param measureName The name of the measure whose durations should be accumulated.
*/
export function getDuration(measureName: string) {
- return measures && getProperty(measures, measureName) || 0;
+ return measures && measures[measureName] || 0;
+ }
+
+ /**
+ * Iterate over each measure, performing some action
+ *
+ * @param cb The action to perform for each measure
+ */
+ export function forEachMeasure(cb: (measureName: string, duration: number) => void) {
+ for (const key in measures) {
+ cb(key, measures[key]);
+ }
}
/** Enables (and resets) performance measurements for the compiler. */
export function enable() {
- counters = { };
- measures = {
- "I/O Read": 0,
- "I/O Write": 0,
- "Program": 0,
- "Parse": 0,
- "Bind": 0,
- "Check": 0,
- "Emit": 0,
- };
-
- profilerEvent = typeof onProfilerEvent === "function" && onProfilerEvent.profiler === true
- ? onProfilerEvent
- : undefined;
+ counts = createMap();
+ marks = createMap();
+ measures = createMap();
+ enabled = true;
+ profilerStart = timestamp();
}
- /** Disables (and clears) performance measurements for the compiler. */
+ /** Disables performance measurements for the compiler. */
export function disable() {
- counters = undefined;
- measures = undefined;
- profilerEvent = undefined;
+ enabled = false;
}
}
diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index cd9f2c1f232..afd2ddad9e1 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -501,7 +501,7 @@ namespace ts {
if (state.traceEnabled) {
trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName);
}
- matchedPattern = matchPatternOrExact(getKeys(state.compilerOptions.paths), moduleName);
+ matchedPattern = matchPatternOrExact(getOwnKeys(state.compilerOptions.paths), moduleName);
}
if (matchedPattern) {
@@ -831,14 +831,6 @@ namespace ts {
: { resolvedModule: undefined, failedLookupLocations };
}
- /* @internal */
- export const defaultInitCompilerOptions: CompilerOptions = {
- module: ModuleKind.CommonJS,
- target: ScriptTarget.ES5,
- noImplicitAny: false,
- sourceMap: false,
- };
-
interface OutputFingerprint {
hash: string;
byteOrderMark: boolean;
@@ -860,9 +852,10 @@ namespace ts {
function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile {
let text: string;
try {
- const start = performance.mark();
+ performance.mark("beforeIORead");
text = sys.readFile(fileName, options.charset);
- performance.measure("I/O Read", start);
+ performance.mark("afterIORead");
+ performance.measure("I/O Read", "beforeIORead", "afterIORead");
}
catch (e) {
if (onError) {
@@ -877,7 +870,7 @@ namespace ts {
}
function directoryExists(directoryPath: string): boolean {
- if (hasProperty(existingDirectories, directoryPath)) {
+ if (directoryPath in existingDirectories) {
return true;
}
if (sys.directoryExists(directoryPath)) {
@@ -905,7 +898,7 @@ namespace ts {
const hash = sys.createHash(data);
const mtimeBefore = sys.getModifiedTime(fileName);
- if (mtimeBefore && hasProperty(outputFingerprints, fileName)) {
+ if (mtimeBefore && fileName in outputFingerprints) {
const fingerprint = outputFingerprints[fileName];
// If output has not been changed, and the file has no external modification
@@ -929,7 +922,7 @@ namespace ts {
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
try {
- const start = performance.mark();
+ performance.mark("beforeIOWrite");
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
if (isWatchSet(options) && sys.createHash && sys.getModifiedTime) {
@@ -939,7 +932,8 @@ namespace ts {
sys.writeFile(fileName, data, writeByteOrderMark);
}
- performance.measure("I/O Write", start);
+ performance.mark("afterIOWrite");
+ performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
}
catch (e) {
if (onError) {
@@ -1042,14 +1036,9 @@ namespace ts {
const resolutions: T[] = [];
const cache = createMap();
for (const name of names) {
- let result: T;
- if (hasProperty(cache, name)) {
- result = cache[name];
- }
- else {
- result = loader(name, containingFile);
- cache[name] = result;
- }
+ const result = name in cache
+ ? cache[name]
+ : cache[name] = loader(name, containingFile);
resolutions.push(result);
}
return resolutions;
@@ -1121,7 +1110,7 @@ namespace ts {
// Track source files that are source files found by searching under node_modules, as these shouldn't be compiled.
const sourceFilesFoundSearchingNodeModules = createMap();
- const start = performance.mark();
+ performance.mark("beforeProgram");
host = host || createCompilerHost(options);
@@ -1219,8 +1208,8 @@ namespace ts {
};
verifyCompilerOptions();
-
- performance.measure("Program", start);
+ performance.mark("afterProgram");
+ performance.measure("Program", "beforeProgram", "afterProgram");
return program;
@@ -1250,7 +1239,7 @@ namespace ts {
classifiableNames = createMap();
for (const sourceFile of files) {
- copyMap(sourceFile.classifiableNames, classifiableNames);
+ copyProperties(sourceFile.classifiableNames, classifiableNames);
}
}
@@ -1278,7 +1267,7 @@ namespace ts {
(oldOptions.maxNodeModuleJsDepth !== options.maxNodeModuleJsDepth) ||
!arrayIsEqualTo(oldOptions.typeRoots, oldOptions.typeRoots) ||
!arrayIsEqualTo(oldOptions.rootDirs, options.rootDirs) ||
- !mapIsEqualTo(oldOptions.paths, options.paths)) {
+ !equalOwnProperties(oldOptions.paths, options.paths)) {
return false;
}
@@ -1400,7 +1389,7 @@ namespace ts {
getSourceFile: program.getSourceFile,
getSourceFileByPath: program.getSourceFileByPath,
getSourceFiles: program.getSourceFiles,
- isSourceFileFromExternalLibrary: (file: SourceFile) => !!lookUp(sourceFilesFoundSearchingNodeModules, file.path),
+ isSourceFileFromExternalLibrary: (file: SourceFile) => !!sourceFilesFoundSearchingNodeModules[file.path],
writeFile: writeFileCallback || (
(fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
isEmitBlocked,
@@ -1463,14 +1452,15 @@ namespace ts {
// checked is to not pass the file to getEmitResolver.
const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile);
- const start = performance.mark();
+ performance.mark("beforeEmit");
const emitResult = emitFiles(
emitResolver,
getEmitHost(writeFileCallback),
sourceFile);
- performance.measure("Emit", start);
+ performance.mark("afterEmit");
+ performance.measure("Emit", "beforeEmit", "afterEmit");
return emitResult;
}
@@ -1937,7 +1927,7 @@ namespace ts {
// If the file was previously found via a node_modules search, but is now being processed as a root file,
// then everything it sucks in may also be marked incorrectly, and needs to be checked again.
- if (file && lookUp(sourceFilesFoundSearchingNodeModules, file.path) && currentNodeModulesDepth == 0) {
+ if (file && sourceFilesFoundSearchingNodeModules[file.path] && currentNodeModulesDepth == 0) {
sourceFilesFoundSearchingNodeModules[file.path] = false;
if (!options.noResolve) {
processReferencedFiles(file, getDirectoryPath(fileName), isDefaultLib);
@@ -1948,7 +1938,7 @@ namespace ts {
processImportedModules(file, getDirectoryPath(fileName));
}
// See if we need to reprocess the imports due to prior skipped imports
- else if (file && lookUp(modulesWithElidedImports, file.path)) {
+ else if (file && modulesWithElidedImports[file.path]) {
if (currentNodeModulesDepth < maxNodeModulesJsDepth) {
modulesWithElidedImports[file.path] = false;
processImportedModules(file, getDirectoryPath(fileName));
@@ -2015,15 +2005,17 @@ namespace ts {
}
function processTypeReferenceDirectives(file: SourceFile) {
- const typeDirectives = map(file.typeReferenceDirectives, l => l.fileName);
+ // We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
+ const typeDirectives = map(file.typeReferenceDirectives, ref => ref.fileName.toLocaleLowerCase());
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file.fileName);
for (let i = 0; i < typeDirectives.length; i++) {
const ref = file.typeReferenceDirectives[i];
const resolvedTypeReferenceDirective = resolutions[i];
// store resolved type directive on the file
- setResolvedTypeReferenceDirective(file, ref.fileName, resolvedTypeReferenceDirective);
- processTypeReferenceDirective(ref.fileName, resolvedTypeReferenceDirective, file, ref.pos, ref.end);
+ const fileName = ref.fileName.toLocaleLowerCase();
+ setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective);
+ processTypeReferenceDirective(fileName, resolvedTypeReferenceDirective, file, ref.pos, ref.end);
}
}
@@ -2048,7 +2040,7 @@ namespace ts {
const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName);
if (otherFileText !== getSourceFile(previousResolution.resolvedFileName).text) {
fileProcessingDiagnostics.add(createDiagnostic(refFile, refPos, refEnd,
- Diagnostics.Conflicting_library_definitions_for_0_found_at_1_and_2_Copy_the_correct_file_to_the_typings_folder_to_resolve_this_conflict,
+ Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict,
typeReferenceDirective,
resolvedTypeReferenceDirective.resolvedFileName,
previousResolution.resolvedFileName
diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts
index 134dc489b2a..c1431ca23ed 100644
--- a/src/compiler/scanner.ts
+++ b/src/compiler/scanner.ts
@@ -55,7 +55,7 @@ namespace ts {
tryScan(callback: () => T): T;
}
- const textToToken: MapLike = {
+ const textToToken = createMap({
"abstract": SyntaxKind.AbstractKeyword,
"any": SyntaxKind.AnyKeyword,
"as": SyntaxKind.AsKeyword,
@@ -179,7 +179,7 @@ namespace ts {
"|=": SyntaxKind.BarEqualsToken,
"^=": SyntaxKind.CaretEqualsToken,
"@": SyntaxKind.AtToken,
- };
+ });
/*
As per ECMAScript Language Specification 3th Edition, Section 7.6: Identifiers
@@ -271,12 +271,10 @@ namespace ts {
lookupInUnicodeMap(code, unicodeES3IdentifierPart);
}
- function makeReverseMap(source: MapLike): string[] {
+ function makeReverseMap(source: Map): string[] {
const result: string[] = [];
for (const name in source) {
- if (source.hasOwnProperty(name)) {
- result[source[name]] = name;
- }
+ result[source[name]] = name;
}
return result;
}
diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts
index 2d8c36a3d02..4046867faf1 100644
--- a/src/compiler/sourcemap.ts
+++ b/src/compiler/sourcemap.ts
@@ -46,6 +46,7 @@ namespace ts {
export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
const compilerOptions = host.getCompilerOptions();
+ const extendedDiagnostics = compilerOptions.extendedDiagnostics;
let currentSourceFile: SourceFile;
let sourceMapDir: string; // The directory in which sourcemap will be
let stopOverridingSpan = false;
@@ -240,7 +241,9 @@ namespace ts {
return;
}
- const start = performance.mark();
+ if (extendedDiagnostics) {
+ performance.mark("beforeSourcemap");
+ }
const sourceLinePos = getLineAndCharacterOfPosition(currentSourceFile, pos);
@@ -282,7 +285,10 @@ namespace ts {
updateLastEncodedAndRecordedSpans();
- performance.measure("Source Map", start);
+ if (extendedDiagnostics) {
+ performance.mark("afterSourcemap");
+ performance.measure("Source Map", "beforeSourcemap", "afterSourcemap");
+ }
}
function getStartPos(range: TextRange) {
diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts
index 3b588edd101..62b58609086 100644
--- a/src/compiler/tsc.ts
+++ b/src/compiler/tsc.ts
@@ -122,11 +122,11 @@ namespace ts {
const gutterSeparator = " ";
const resetEscapeSequence = "\u001b[0m";
const ellipsis = "...";
- const categoryFormatMap: MapLike = {
+ const categoryFormatMap = createMap({
[DiagnosticCategory.Warning]: yellowForegroundEscapeSequence,
[DiagnosticCategory.Error]: redForegroundEscapeSequence,
[DiagnosticCategory.Message]: blueForegroundEscapeSequence,
- };
+ });
function formatAndReset(text: string, formatStyle: string) {
return formatStyle + text + resetEscapeSequence;
@@ -445,10 +445,9 @@ namespace ts {
}
function cachedFileExists(fileName: string): boolean {
- if (hasProperty(cachedExistingFiles, fileName)) {
- return cachedExistingFiles[fileName];
- }
- return cachedExistingFiles[fileName] = hostFileExists(fileName);
+ return fileName in cachedExistingFiles
+ ? cachedExistingFiles[fileName]
+ : cachedExistingFiles[fileName] = hostFileExists(fileName);
}
function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void) {
@@ -704,9 +703,10 @@ namespace ts {
description = getDiagnosticText(option.description);
const options: string[] = [];
const element = (option).element;
- forEachKey(