diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md
index 5711c8f950..89da312503 100644
--- a/.github/ISSUE_TEMPLATE/Bug_report.md
+++ b/.github/ISSUE_TEMPLATE/Bug_report.md
@@ -16,7 +16,7 @@ Please fill in the *entire* template below.
-->
-**TypeScript Version:** 3.3.0-dev.201xxxxx
+**TypeScript Version:** 3.4.0-dev.201xxxxx
**Search Terms:**
diff --git a/Gulpfile.js b/Gulpfile.js
index c272caf2b6..68a2b79f22 100644
--- a/Gulpfile.js
+++ b/Gulpfile.js
@@ -1,107 +1,70 @@
-///
// @ts-check
const path = require("path");
-const log = require("fancy-log"); // was `require("gulp-util").log (see https://github.com/gulpjs/gulp-util)
const fs = require("fs");
-const child_process = require("child_process");
-const runSequence = require("run-sequence");
+const log = require("fancy-log");
const newer = require("gulp-newer");
-const insert = require("gulp-insert");
-const { append } = require("gulp-insert");
const sourcemaps = require("gulp-sourcemaps");
const del = require("del");
const fold = require("travis-fold");
const rename = require("gulp-rename");
-const mkdirp = require("./scripts/build/mkdirp");
-const gulp = require("./scripts/build/gulp");
-const getDirSize = require("./scripts/build/getDirSize");
-const project = require("./scripts/build/project");
-const replace = require("./scripts/build/replace");
-const convertConstEnums = require("./scripts/build/convertConstEnum");
-const needsUpdate = require("./scripts/build/needsUpdate");
-const getDiffTool = require("./scripts/build/getDiffTool");
-const baselineAccept = require("./scripts/build/baselineAccept");
-const cmdLineOptions = require("./scripts/build/options");
-const exec = require("./scripts/build/exec");
-const browserify = require("./scripts/build/browserify");
-const prepend = require("./scripts/build/prepend");
-const { removeSourceMaps } = require("./scripts/build/sourcemaps");
-const { CancellationTokenSource, CancelError, delay, Semaphore } = require("prex");
-const { libraryTargets, generateLibs } = require("./scripts/build/lib");
+const concat = require("gulp-concat");
+const merge2 = require("merge2");
+const mkdirp = require("mkdirp");
+const { src, dest, task, parallel, series, watch } = require("gulp");
+const { append, transform } = require("gulp-insert");
+const { browserify } = require("./scripts/build/browserify");
+const { prependFile } = require("./scripts/build/prepend");
+const { exec, readJson, needsUpdate, getDiffTool, getDirSize, flatten, rm } = require("./scripts/build/utils");
const { runConsoleTests, cleanTestDirs, writeTestConfigFile, refBaseline, localBaseline, refRwcBaseline, localRwcBaseline } = require("./scripts/build/tests");
+const { buildProject, cleanProject, watchProject } = require("./scripts/build/projects");
+const cmdLineOptions = require("./scripts/build/options");
-Error.stackTraceLimit = 1000;
-
-// Constants
-const host = cmdLineOptions.host;
const copyright = "CopyrightNotice.txt";
+const cleanTasks = [];
-project.addTypeScript("lkg", "./lib/typescript.js");
-project.addTypeScript("built", "./built/local/typescriptServices.js");
-project.addTypeScript("default", "lkg"); // Compile using the LKG compiler by default
+const buildScripts = () => buildProject("scripts");
+const cleanScripts = () => cleanProject("scripts");
+cleanTasks.push(cleanScripts);
-const scriptsProject = "scripts/tsconfig.json";
-const configurePrereleaseJs = "scripts/configurePrerelease.js";
-const processDiagnosticMessagesJs = "scripts/processDiagnosticMessages.js";
-const generateLocalizedDiagnosticMessagesJs = "scripts/generateLocalizedDiagnosticMessages.js";
-const buildProtocolJs = "scripts/buildProtocol.js";
-const produceLKGJs = "scripts/produceLKG.js";
-const word2mdJs = "scripts/word2md.js";
-const scriptsTaskAliases = [configurePrereleaseJs, processDiagnosticMessagesJs, generateLocalizedDiagnosticMessagesJs, produceLKGJs, buildProtocolJs, word2mdJs];
-gulp.task("scripts", /*help*/ false, () => project.compile(scriptsProject), { aliases: scriptsTaskAliases });
-gulp.task("clean:scripts", /*help*/ false, () => project.clean(scriptsProject), { aliases: scriptsTaskAliases.map(alias => `clean:${alias}`)});
+const libraries = readJson("./src/lib/libs.json");
+const libs = libraries.libs.map(lib => {
+ const relativeSources = ["header.d.ts"].concat(libraries.sources && libraries.sources[lib] || [lib + ".d.ts"]);
+ const relativeTarget = libraries.paths && libraries.paths[lib] || ("lib." + lib + ".d.ts");
+ const sources = relativeSources.map(s => path.posix.join("src/lib", s));
+ const target = `built/local/${relativeTarget}`;
+ return { target, relativeTarget, sources };
+});
-// Nightly management tasks
-gulp.task(
- "configure-nightly",
- "Runs scripts/configurePrerelease.ts to prepare a build for nightly publishing",
- [configurePrereleaseJs],
- () => exec(host, [configurePrereleaseJs, "dev", "package.json", "src/compiler/core.ts"]));
+const generateLibs = () => {
+ return merge2(libs.map(({ sources, target, relativeTarget }) =>
+ src([copyright, ...sources])
+ .pipe(newer(target))
+ .pipe(concat(relativeTarget, { newLine: "\n\n" }))
+ .pipe(dest("built/local"))));
+};
+task("lib", generateLibs)
+task("lib").description = "Builds the library targets";
-gulp.task(
- "publish-nightly",
- "Runs `npm publish --tag next` to create a new nightly build on npm",
- ["LKG"],
- () => runSequence("clean", "runtests-parallel",
- () => exec("npm", ["publish", "--tag", "next"])));
+const cleanLib = () => del(libs.map(lib => lib.target));
+cleanTasks.push(cleanLib);
-const importDefinitelyTypedTestsProject = "scripts/importDefinitelyTypedTests/tsconfig.json";
-const importDefinitelyTypedTestsJs = "scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js";
-gulp.task(importDefinitelyTypedTestsJs, /*help*/ false, () => project.compile(importDefinitelyTypedTestsProject));
-gulp.task(`clean:${importDefinitelyTypedTestsJs}`, /*help*/ false, () => project.clean(importDefinitelyTypedTestsProject));
+const watchLib = () => watch(["src/lib/**/*"], generateLibs);
-gulp.task(
- "importDefinitelyTypedTests",
- "Runs scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.ts to copy DT's tests to the TS-internal RWC tests",
- [importDefinitelyTypedTestsJs],
- () => exec(host, [importDefinitelyTypedTestsJs, "./", "../DefinitelyTyped"]));
-
-gulp.task(
- "lib",
- "Builds the library targets",
- () => generateLibs([copyright]));
-
-// The generated diagnostics map; built for the compiler and for the "generate-diagnostics" task
const diagnosticInformationMapTs = "src/compiler/diagnosticInformationMap.generated.ts";
const diagnosticMessagesJson = "src/compiler/diagnosticMessages.json";
const diagnosticMessagesGeneratedJson = "src/compiler/diagnosticMessages.generated.json";
-gulp.task(diagnosticInformationMapTs, /*help*/ false, [processDiagnosticMessagesJs], () => {
+const generateDiagnostics = async () => {
if (needsUpdate(diagnosticMessagesJson, [diagnosticMessagesGeneratedJson, diagnosticInformationMapTs])) {
- return exec(host, [processDiagnosticMessagesJs, diagnosticMessagesJson]);
+ await exec(process.execPath, ["scripts/processDiagnosticMessages.js", diagnosticMessagesJson]);
}
-});
-gulp.task(`clean:${diagnosticInformationMapTs}`, /*help*/ false, () => del([diagnosticInformationMapTs, diagnosticMessagesGeneratedJson]));
+};
+task("generate-diagnostics", series(buildScripts, generateDiagnostics));
+task("generate-diagnostics").description = "Generates a diagnostic file in TypeScript based on an input JSON file";
-const builtGeneratedDiagnosticMessagesJson = "built/local/diagnosticMessages.generated.json";
-gulp.task(builtGeneratedDiagnosticMessagesJson, /*help*/ false, [diagnosticInformationMapTs], () =>
- gulp.src([diagnosticMessagesGeneratedJson], { base: "src/compiler" })
- .pipe(newer(builtGeneratedDiagnosticMessagesJson))
- .pipe(gulp.dest("built/local")));
+const cleanDiagnostics = () => del([diagnosticInformationMapTs, diagnosticMessagesGeneratedJson]);
+cleanTasks.push(cleanDiagnostics);
-gulp.task(
- "generate-diagnostics",
- "Generates a diagnostic file in TypeScript based on an input JSON file",
- [diagnosticInformationMapTs]);
+const watchDiagnostics = () => watch(["src/compiler/diagnosticMessages.json"], task("generate-diagnostics"));
// Localize diagnostics
/**
@@ -122,178 +85,508 @@ const localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt
.map(f => `built/local/${f}/diagnosticMessages.generated.json`)
.concat(generatedLCGFile);
-gulp.task(generatedLCGFile, /*help*/ false, [generateLocalizedDiagnosticMessagesJs, diagnosticInformationMapTs], (done) => {
+const localize = async () => {
if (needsUpdate(diagnosticMessagesGeneratedJson, generatedLCGFile)) {
- return exec(host, [generateLocalizedDiagnosticMessagesJs, "src/loc/lcl", "built/local", diagnosticMessagesGeneratedJson], { ignoreExitCode: true });
+ return exec(process.execPath, ["scripts/generateLocalizedDiagnosticMessages.js", "src/loc/lcl", "built/local", diagnosticMessagesGeneratedJson], { ignoreExitCode: true });
}
-});
+};
-gulp.task("localize", /*help*/ false, [generatedLCGFile]);
+// Pre-build steps when targeting the LKG compiler
+const lkgPreBuild = parallel(generateLibs, series(buildScripts, generateDiagnostics));
-const servicesProject = "src/services/tsconfig.json";
-const typescriptServicesProject = "built/local/typescriptServices.tsconfig.json";
-gulp.task(typescriptServicesProject, /*help*/ false, () => {
- // NOTE: flatten services so that we can properly strip @internal
- project.flatten(servicesProject, typescriptServicesProject, {
+const buildTsc = () => buildProject("src/tsc");
+task("tsc", series(lkgPreBuild, buildTsc));
+task("tsc").description = "Builds the command-line compiler";
+
+const cleanTsc = () => cleanProject("src/tsc");
+cleanTasks.push(cleanTsc);
+task("clean-tsc", cleanTsc);
+task("clean-tsc").description = "Cleans outputs for the command-line compiler";
+
+const watchTsc = () => watchProject("src/tsc");
+task("watch-tsc", series(lkgPreBuild, parallel(watchLib, watchDiagnostics, watchTsc)));
+task("watch-tsc").description = "Watch for changes and rebuild the command-line compiler only.";
+
+// Pre-build steps when targeting the built/local compiler.
+const localPreBuild = parallel(generateLibs, series(buildScripts, generateDiagnostics, buildTsc));
+
+// Pre-build steps to use based on supplied options.
+const preBuild = cmdLineOptions.lkg ? lkgPreBuild : localPreBuild;
+
+const buildServices = (() => {
+ // flatten the services project
+ const flattenServicesConfig = async () => flatten("src/services/tsconfig.json", "built/local/typescriptServices.tsconfig.json", {
compilerOptions: {
"removeComments": false,
"stripInternal": true,
"declarationMap": false,
- "outFile": "typescriptServices.out.js" // must align with same task in jakefile. We fix this name below.
+ "outFile": "typescriptServices.out.js"
}
});
-});
-const typescriptServicesJs = "built/local/typescriptServices.js";
-const typescriptServicesDts = "built/local/typescriptServices.d.ts";
-gulp.task(typescriptServicesJs, /*help*/ false, ["lib", "generate-diagnostics", typescriptServicesProject], () =>
- project.compile(typescriptServicesProject, {
- js: files => files
- .pipe(prepend.file(copyright))
- .pipe(rename("typescriptServices.js")),
- dts: files => files
- .pipe(removeSourceMaps())
- .pipe(prepend.file(copyright))
- .pipe(convertConstEnums())
- .pipe(rename("typescriptServices.d.ts"))
- }),
- { aliases: [typescriptServicesDts] });
+ // build typescriptServices.out.js
+ const buildTypescriptServicesOut = () => buildProject("built/local/typescriptServices.tsconfig.json", cmdLineOptions);
-const typescriptJs = "built/local/typescript.js";
-gulp.task(typescriptJs, /*help*/ false, [typescriptServicesJs], () =>
- gulp.src([typescriptServicesJs], { base: "built/local" })
- .pipe(newer(typescriptJs))
+ // create typescriptServices.js
+ const createTypescriptServicesJs = () => src("built/local/typescriptServices.out.js")
+ .pipe(newer("built/local/typescriptServices.js"))
+ .pipe(sourcemaps.init({ loadMaps: true }))
+ .pipe(prependFile(copyright))
+ .pipe(rename("typescriptServices.js"))
+ .pipe(sourcemaps.write(".", { includeContent: false, destPath: "built/local" }))
+ .pipe(dest("built/local"));
+
+ // create typescriptServices.d.ts
+ const createTypescriptServicesDts = () => src("built/local/typescriptServices.out.d.ts")
+ .pipe(newer("built/local/typescriptServices.d.ts"))
+ .pipe(prependFile(copyright))
+ .pipe(transform(content => content.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, "$1$2enum $3 {$4")))
+ .pipe(rename("typescriptServices.d.ts"))
+ .pipe(dest("built/local"));
+
+ // create typescript.js
+ const createTypescriptJs = () => src("built/local/typescriptServices.js")
+ .pipe(newer("built/local/typescript.js"))
+ .pipe(sourcemaps.init({ loadMaps: true }))
.pipe(rename("typescript.js"))
- .pipe(gulp.dest("built/local")))
+ .pipe(sourcemaps.write(".", { includeContent: false, destPath: "built/local" }))
+ .pipe(dest("built/local"));
-const typescriptDts = "built/local/typescript.d.ts";
-gulp.task(typescriptDts, /*help*/ false, [typescriptServicesDts], () =>
- gulp.src([typescriptServicesDts], { base: "built/local" })
- .pipe(newer(typescriptDts))
+ // create typescript.d.ts
+ const createTypescriptDts = () => src("built/local/typescriptServices.d.ts")
+ .pipe(newer("built/local/typescript.d.ts"))
.pipe(append("\nexport = ts;"))
.pipe(rename("typescript.d.ts"))
- .pipe(gulp.dest("built/local")));
+ .pipe(dest("built/local"));
-const typescriptStandaloneDts = "built/local/typescript_standalone.d.ts";
-gulp.task(typescriptStandaloneDts, /*help*/ false, [typescriptServicesDts], () =>
- gulp.src([typescriptServicesDts], { base: "built/local" })
- .pipe(newer(typescriptStandaloneDts))
- .pipe(replace(/declare (namespace|module) ts/g, 'declare module "typescript"'))
+ // create typescript_standalone.d.ts
+ const createTypescriptStandaloneDts = () => src("built/local/typescriptServices.d.ts")
+ .pipe(newer("built/local/typescript_standalone.d.ts"))
+ .pipe(transform(content => content.replace(/declare (namespace|module) ts/g, 'declare module "typescript"')))
.pipe(rename("typescript_standalone.d.ts"))
- .pipe(gulp.dest("built/local")));
+ .pipe(dest("built/local"));
-// build all 'typescriptServices'-related outputs
-gulp.task("services", /*help*/ false, [typescriptServicesJs, typescriptServicesDts, typescriptJs, typescriptDts, typescriptStandaloneDts]);
+ return series(
+ flattenServicesConfig,
+ buildTypescriptServicesOut,
+ createTypescriptServicesJs,
+ createTypescriptServicesDts,
+ createTypescriptJs,
+ createTypescriptDts,
+ createTypescriptStandaloneDts);
+})();
+task("services", series(preBuild, buildServices));
+task("services").description = "Builds the language service";
+task("services").flags = {
+ " --built": "Compile using the built version of the compiler."
+}
-const useCompiler = cmdLineOptions.lkg ? "lkg" : "built";
-const useCompilerDeps = cmdLineOptions.lkg ? ["lib", "generate-diagnostics"] : [typescriptServicesJs];
+const cleanServices = async () => {
+ if (fs.existsSync("built/local/typescriptServices.tsconfig.json")) {
+ await cleanProject("built/local/typescriptServices.tsconfig.json");
+ }
+ await del([
+ "built/local/typescriptServices.tsconfig.json",
+ "built/local/typescriptServices.out.js",
+ "built/local/typescriptServices.out.d.ts",
+ "built/local/typescriptServices.js",
+ "built/local/typescript.js",
+ "built/local/typescript.d.ts",
+ "built/local/typescript_standalone.d.ts",
+ ]);
+};
+cleanTasks.push(cleanServices);
+task("clean-services", cleanServices);
+task("clean-services").description = "Cleans outputs for the language service";
-const tscProject = "src/tsc/tsconfig.json";
-const tscJs = "built/local/tsc.js";
-gulp.task(tscJs, /*help*/ false, useCompilerDeps, () =>
- project.compile(tscProject, {
- typescript: useCompiler,
- js: files => files.pipe(prepend.file(copyright))
- }));
+const watchServices = () => watch([
+ "src/compiler/tsconfig.json",
+ "src/compiler/**/*.ts",
+ "src/jsTyping/tsconfig.json",
+ "src/jsTyping/**/*.ts",
+ "src/services/tsconfig.json",
+ "src/services/**/*.ts",
+], series(preBuild, buildServices));
+task("watch-services", series(preBuild, parallel(watchLib, watchDiagnostics, watchServices)));
+task("watch-services").description = "Watches for changes and rebuild language service only";
+task("watch-services").flags = {
+ " --built": "Compile using the built version of the compiler."
+}
-const tscReleaseProject = "src/tsc/tsconfig.release.json";
-const tscReleaseJs = "built/local/tsc.release.js";
-gulp.task(tscReleaseJs, /*help*/ false, () =>
- project.compile(tscReleaseProject, {
- js: files => files.pipe(prepend.file(copyright))
- }));
+const buildServer = () => buildProject("src/tsserver", cmdLineOptions);
+task("tsserver", series(preBuild, buildServer));
+task("tsserver").description = "Builds the language server";
+task("tsserver").flags = {
+ " --built": "Compile using the built version of the compiler."
+}
-const cancellationTokenProject = "src/cancellationToken/tsconfig.json";
-const cancellationTokenJs = "built/local/cancellationToken.js";
-gulp.task(cancellationTokenJs, /*help*/ false, useCompilerDeps, () => project.compile(cancellationTokenProject, { typescript: useCompiler }));
+const cleanServer = () => cleanProject("src/tsserver");
+cleanTasks.push(cleanServer);
+task("clean-tsserver", cleanServer);
+task("clean-tsserver").description = "Cleans outputs for the language server";
-const typingsInstallerProject = "src/typingsInstaller/tsconfig.json";
-const typingsInstallerJs = "built/local/typingsInstaller.js";
-gulp.task(typingsInstallerJs, /*help*/ false, useCompilerDeps, () => project.compile(typingsInstallerProject, { typescript: useCompiler }));
+const watchServer = () => watchProject("src/tsserver", cmdLineOptions);
+task("watch-tsserver", series(preBuild, parallel(watchLib, watchDiagnostics, watchServer)));
+task("watch-tsserver").description = "Watch for changes and rebuild the language server only";
+task("watch-tsserver").flags = {
+ " --built": "Compile using the built version of the compiler."
+}
-const tsserverProject = "src/tsserver/tsconfig.json";
-const tsserverJs = "built/local/tsserver.js";
-gulp.task(tsserverJs, /*help*/ false, useCompilerDeps, () => project.compile(tsserverProject, { typescript: useCompiler }));
+task("min", series(lkgPreBuild, parallel(buildTsc, buildServer)));
+task("min").description = "Builds only tsc and tsserver";
+task("min").flags = {
+ " --built": "Compile using the built version of the compiler."
+}
-const watchGuardProject = "src/watchGuard/tsconfig.json";
-const watchGuardJs = "built/local/watchGuard.js";
-gulp.task(watchGuardJs, /*help*/ false, useCompilerDeps, () => project.compile(watchGuardProject, { typescript: useCompiler }));
+task("clean-min", series(cleanTsc, cleanServer));
+task("clean-min").description = "Cleans outputs for tsc and tsserver";
-const typesMapJson = "built/local/typesMap.json";
-gulp.task(typesMapJson, /*help*/ false, [], () =>
- gulp.src("src/server/typesMap.json")
- .pipe(newer(typesMapJson))
- .pipe(insert.transform(contents => (JSON.parse(contents), contents)))
- .pipe(gulp.dest("built/local")));
+task("watch-min", series(preBuild, parallel(watchLib, watchDiagnostics, watchTsc, watchServer)));
+task("watch-min").description = "Watches for changes to a tsc and tsserver only";
+task("watch-min").flags = {
+ " --built": "Compile using the built version of the compiler."
+}
-const tsserverlibraryProject = "built/local/tsserverlibrary.tsconfig.json";
-gulp.task(tsserverlibraryProject, /*help*/ false, () => {
- // NOTE: flatten tsserverlibrary so that we can properly strip @internal
- project.flatten("src/tsserver/tsconfig.json", tsserverlibraryProject, {
+const buildLssl = (() => {
+ // flatten the server project
+ const flattenTsServerProject = async () => flatten("src/tsserver/tsconfig.json", "built/local/tsserverlibrary.tsconfig.json", {
exclude: ["src/tsserver/server.ts"],
compilerOptions: {
"removeComments": false,
"stripInternal": true,
+ "declaration": true,
"declarationMap": false,
- "outFile": "tsserverlibrary.out.js" // must align with same task in jakefile. We fix this name below.
+ "outFile": "tsserverlibrary.out.js"
}
});
-});
-const tsserverlibraryJs = "built/local/tsserverlibrary.js";
-const tsserverlibraryDts = "built/local/tsserverlibrary.d.ts";
-gulp.task(tsserverlibraryJs, /*help*/ false, useCompilerDeps.concat([tsserverlibraryProject]), () =>
- project.compile(tsserverlibraryProject, {
- js: files => files
- .pipe(prepend.file(copyright))
- .pipe(rename("tsserverlibrary.js")),
- dts: files => files
- .pipe(removeSourceMaps())
- .pipe(prepend.file(copyright))
- .pipe(convertConstEnums())
- .pipe(append("\nexport = ts;\nexport as namespace ts;"))
- .pipe(rename("tsserverlibrary.d.ts")),
- typescript: useCompiler
- }), { aliases: [tsserverlibraryDts] });
+ // build tsserverlibrary.out.js
+ const buildServerLibraryOut = () => buildProject("built/local/tsserverlibrary.tsconfig.json", cmdLineOptions);
-gulp.task(
- "lssl",
- "Builds language service server library",
- [tsserverlibraryDts]);
+ // create tsserverlibrary.js
+ const createServerLibraryJs = () => src("built/local/tsserverlibrary.out.js")
+ .pipe(newer("built/local/tsserverlibrary.js"))
+ .pipe(sourcemaps.init({ loadMaps: true }))
+ .pipe(prependFile(copyright))
+ .pipe(rename("tsserverlibrary.js"))
+ .pipe(sourcemaps.write(".", { includeContent: false, destPath: "built/local" }))
+ .pipe(dest("built/local"));
-gulp.task(
- "local",
- "Builds the full compiler and services",
- [tscJs, "services", tsserverJs, builtGeneratedDiagnosticMessagesJson, tsserverlibraryDts, "localize"]);
+ // create tsserverlibrary.d.ts
+ const createServerLibraryDts = () => src("built/local/tsserverlibrary.out.d.ts")
+ .pipe(newer("built/local/tsserverlibrary.d.ts"))
+ .pipe(prependFile(copyright))
+ .pipe(transform(content => content.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, "$1$2enum $3 {$4")))
+ .pipe(append("\nexport = ts;\nexport as namespace ts;"))
+ .pipe(rename("tsserverlibrary.d.ts"))
+ .pipe(dest("built/local"));
-gulp.task(
- "tsc",
- "Builds only the compiler",
- [tscJs]);
+ return series(
+ flattenTsServerProject,
+ buildServerLibraryOut,
+ createServerLibraryJs,
+ createServerLibraryDts);
+})();
+task("lssl", series(preBuild, buildLssl));
+task("lssl").description = "Builds language service server library";
+task("lssl").flags = {
+ " --built": "Compile using the built version of the compiler."
+}
-// Generate Markdown spec
-const specMd = "doc/spec.md";
-gulp.task(specMd, /*help*/ false, [word2mdJs], () =>
- exec("cscript", ["//nologo", word2mdJs, path.resolve("doc/TypeScript Language Specification.docx"), path.resolve(specMd)]));
+const cleanLssl = async () => {
+ if (fs.existsSync("built/local/tsserverlibrary.tsconfig.json")) {
+ await cleanProject("built/local/tsserverlibrary.tsconfig.json");
+ }
+ await del([
+ "built/local/tsserverlibrary.tsconfig.json",
+ "built/local/tsserverlibrary.out.js",
+ "built/local/tsserverlibrary.out.d.ts",
+ "built/local/tsserverlibrary.js",
+ "built/local/tsserverlibrary.d.ts",
+ ]);
+};
+cleanTasks.push(cleanLssl);
+task("clean-lssl", cleanLssl);
+task("clean-lssl").description = "Clean outputs for the language service server library";
-gulp.task(
- "generate-spec",
- "Generates a Markdown version of the Language Specification",
- [specMd]);
+const watchLssl = () => watch([
+ "src/compiler/tsconfig.json",
+ "src/compiler/**/*.ts",
+ "src/jsTyping/tsconfig.json",
+ "src/jsTyping/**/*.ts",
+ "src/services/tsconfig.json",
+ "src/services/**/*.ts",
+ "src/server/tsconfig.json",
+ "src/server/**/*.ts",
+ "src/tsserver/tsconfig.json",
+ "src/tsserver/**/*.ts",
+], buildLssl);
+task("watch-lssl", series(preBuild, parallel(watchLib, watchDiagnostics, watchLssl)));
+task("watch-lssl").description = "Watch for changes and rebuild tsserverlibrary only";
+task("watch-lssl").flags = {
+ " --built": "Compile using the built version of the compiler."
+}
-gulp.task("produce-LKG", /*help*/ false, ["scripts", "local", cancellationTokenJs, typingsInstallerJs, watchGuardJs, tscReleaseJs], () => {
+const buildTests = () => buildProject("src/testRunner");
+task("tests", series(preBuild, parallel(buildLssl, buildTests)));
+task("tests").description = "Builds the test infrastructure";
+task("tests").flags = {
+ " --built": "Compile using the built version of the compiler."
+}
+
+const cleanTests = () => cleanProject("src/testRunner");
+cleanTasks.push(cleanTests);
+task("clean-tests", cleanTests);
+task("clean-tests").description = "Cleans the outputs for the test infrastructure";
+
+const watchTests = () => watchProject("src/testRunner", cmdLineOptions);
+
+const buildRules = () => buildProject("scripts/tslint");
+task("build-rules", buildRules);
+task("build-rules").description = "Compiles tslint rules to js";
+
+const cleanRules = () => cleanProject("scripts/tslint");
+cleanTasks.push(cleanRules);
+task("clean-rules", cleanRules);
+task("clean-rules").description = "Cleans the outputs for the lint rules";
+
+const lintFoldStart = async () => { if (fold.isTravis()) console.log(fold.start("lint")); };
+const lintFoldEnd = async () => { if (fold.isTravis()) console.log(fold.end("lint")); };
+const lint = series([
+ lintFoldStart,
+ ...["scripts/tslint/tsconfig.json", "src/tsconfig-base.json"].map(project => {
+ const lintOne = () => {
+ const args = ["node_modules/tslint/bin/tslint", "--project", project, "--formatters-dir", "./built/local/tslint/formatters", "--format", "autolinkableStylish"];
+ if (cmdLineOptions.fix) args.push("--fix");
+ log(`Linting: node ${args.join(" ")}`);
+ return exec(process.execPath, args);
+ };
+ lintOne.dispayName = `lint(${project})`;
+ return lintOne;
+ }),
+ lintFoldEnd
+]);
+lint.displayName = "lint";
+task("lint", series(buildRules, lint));
+task("lint").description = "Runs tslint on the compiler sources.";
+task("lint").flags = {
+ " --f[iles]=": "pattern to match files to lint",
+};
+
+const buildFoldStart = async () => { if (fold.isTravis()) console.log(fold.start("build")); };
+const buildFoldEnd = async () => { if (fold.isTravis()) console.log(fold.end("build")); };
+task("local", series(buildFoldStart, lkgPreBuild, parallel(localize, buildTsc, buildServer, buildServices, buildLssl), buildFoldEnd));
+task("local").description = "Builds the full compiler and services";
+task("local").flags = {
+ " --built": "Compile using the built version of the compiler."
+}
+
+task("watch-local", series(preBuild, parallel(watchLib, watchDiagnostics, watchTsc, watchServices, watchServer, watchLssl)));
+task("watch-local").description = "Watches for changes to projects in src/ (but does not execute tests).";
+task("watch-local").flags = {
+ " --built": "Compile using the built version of the compiler."
+}
+
+const generateCodeCoverage = () => exec("istanbul", ["cover", "node_modules/mocha/bin/_mocha", "--", "-R", "min", "-t", "" + cmdLineOptions.testTimeout, "built/local/run.js"]);
+task("generate-code-coverage", series(preBuild, buildTests, generateCodeCoverage));
+task("generate-code-coverage").description = "Generates code coverage data via istanbul";
+
+const preTest = parallel(buildRules, buildTests, buildServices, buildLssl);
+preTest.displayName = "preTest";
+
+const postTest = (done) => cmdLineOptions.lint ? lint(done) : done();
+
+const runTests = () => runConsoleTests("built/local/run.js", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false);
+task("runtests", series(preBuild, preTest, runTests, postTest));
+task("runtests").description = "Runs the tests using the built run.js file.";
+task("runtests").flags = {
+ "-t --tests=": "Pattern for tests to run.",
+ " --failed": "Runs tests listed in '.failed-tests'.",
+ "-r --reporter=": "The mocha reporter to use.",
+ "-d --debug": "Runs tests in debug mode (NodeJS 6 and earlier)",
+ "-i --inspect": "Runs tests in inspector mode (NodeJS 8 and later)",
+ " --keepFailed": "Keep tests in .failed-tests even if they pass",
+ " --light": "Run tests in light mode (fewer verifications, but tests run faster)",
+ " --dirty": "Run tests without first cleaning test output directories",
+ " --stackTraceLimit=": "Sets the maximum number of stack frames to display. Use 'full' to show all frames.",
+ " --no-color": "Disables color",
+ " --no-lint": "Disables lint",
+ " --timeout=": "Overrides the default test timeout.",
+ " --built": "Compile using the built version of the compiler.",
+}
+
+const runTestsParallel = () => runConsoleTests("built/local/run.js", "min", /*runInParallel*/ true, /*watchMode*/ false);
+task("runtests-parallel", series(preBuild, preTest, runTestsParallel, postTest));
+task("runtests-parallel").description = "Runs all the tests in parallel using the built run.js file.";
+task("runtests-parallel").flags = {
+ " --no-lint": "disables lint.",
+ " --light": "Run tests in light mode (fewer verifications, but tests run faster).",
+ " --keepFailed": "Keep tests in .failed-tests even if they pass.",
+ " --dirty": "Run tests without first cleaning test output directories.",
+ " --stackTraceLimit=": "Sets the maximum number of stack frames to display. Use 'full' to show all frames.",
+ " --workers=": "The number of parallel workers to use.",
+ " --timeout=": "Overrides the default test timeout.",
+ " --built": "Compile using the built version of the compiler.",
+};
+
+const buildWebTestServer = () => buildProject("tests/webTestServer.tsconfig.json");
+const cleanWebTestServer = () => cleanProject("tests/webTestServer.tsconfig.json");
+cleanTasks.push(cleanWebTestServer);
+
+const browserifyTests = () => src(["built/local/run.js"], { base: "built/local" })
+ .pipe(newer("built/local/bundle.js"))
+ .pipe(sourcemaps.init({ loadMaps: true }))
+ .pipe(browserify())
+ .pipe(rename("bundle.js"))
+ .pipe(sourcemaps.write(".", /**@type {*}*/({ includeContent: false, destPath: "built/local" })))
+ .pipe(dest("built/local"));
+
+const runtestsBrowser = async () => {
+ await cleanTestDirs();
+ const { tests, runners, light } = cmdLineOptions;
+ const testConfigFile = "test.config";
+ await del([testConfigFile]);
+ if (tests || runners || light) {
+ writeTestConfigFile(tests, runners, light);
+ }
+ const args = ["tests/webTestServer.js"];
+ if (cmdLineOptions.browser) {
+ args.push(cmdLineOptions.browser);
+ }
+ if (tests) {
+ args.push(JSON.stringify(tests));
+ }
+ await exec(process.execPath, args);
+};
+
+task("runtests-browser", series(preBuild, parallel(buildTests, buildServices, buildLssl, buildWebTestServer), browserifyTests, runtestsBrowser));
+task("runtests-browser").description = "Runs the tests using the built run.js file like 'gulp runtests'.";
+task("runtests-browser").flags = {
+ "-t --tests=": "pattern for tests to run",
+ "-b --browser=": "Either 'IE' or 'chrome'",
+ " --built": "Compile using the built version of the compiler.",
+};
+
+task("diff", () => exec(getDiffTool(), [refBaseline, localBaseline], { ignoreExitCode: true }));
+task("diff").description = "Diffs the compiler baselines using the diff tool specified by the 'DIFF' environment variable";
+
+task("diff-rwc", () => exec(getDiffTool(), [refRwcBaseline, localRwcBaseline], { ignoreExitCode: true }));
+task("diff-rwc").description = "Diffs the RWC baselines using the diff tool specified by the 'DIFF' environment variable";
+
+const baselineAccept = subfolder => merge2(
+ src([`${localBaseline}${subfolder ? `${subfolder}/` : ``}**`, `!${localBaseline}${subfolder}/**/*.delete`], { base: localBaseline })
+ .pipe(dest(refBaseline)),
+ src([`${localBaseline}${subfolder ? `${subfolder}/` : ``}**/*.delete`], { base: localBaseline, read: false })
+ .pipe(rm())
+ .pipe(rename({ extname: "" }))
+ .pipe(rm(refBaseline)));
+task("baseline-accept", () => baselineAccept(""));
+task("baseline-accept").description = "Makes the most recent test results the new baseline, overwriting the old baseline";
+
+task("baseline-accept-rwc", () => baselineAccept("rwc"));
+task("baseline-accept-rwc").description = "Makes the most recent rwc test results the new baseline, overwriting the old baseline";
+
+task("baseline-accept-test262", () => baselineAccept("test262"));
+task("baseline-accept-test262").description = "Makes the most recent test262 test results the new baseline, overwriting the old baseline";
+
+// TODO(rbuckton): Determine if 'webhost' is still in use.
+const buildWebHost = () => buildProject("tests/webhost/webtsc.tsconfig.json");
+task("webhost", series(lkgPreBuild, buildWebHost));
+task("webhost").description = "Builds the tsc web host";
+
+const cleanWebHost = () => cleanProject("tests/webhost/webtsc.tsconfig.json");
+cleanTasks.push(cleanWebHost);
+task("clean-webhost", cleanWebHost);
+task("clean-webhost").description = "Cleans the outputs of the tsc web host";
+
+// TODO(rbuckton): Determine if 'perftsc' is still in use.
+const buildPerfTsc = () => buildProject("tests/perftsc.tsconfig.json");
+task("perftsc", series(lkgPreBuild, buildPerfTsc));
+task("perftsc").description = "Builds augmented version of the compiler for perf tests";
+
+const cleanPerfTsc = () => cleanProject("tests/perftsc.tsconfig.json");
+cleanTasks.push(cleanPerfTsc);
+task("clean-perftsc", cleanPerfTsc);
+task("clean-perftsc").description = "Cleans the outputs of the perftsc project";
+
+const buildLoggedIO = async () => {
+ mkdirp.sync("built/local/temp");
+ await exec(process.execPath, ["lib/tsc", "--types", "--target", "es5", "--lib", "es5", "--outdir", "built/local/temp", "src/harness/loggedIO.ts"]);
+ fs.renameSync("built/local/temp/harness/loggedIO.js", "built/local/loggedIO.js");
+ await del("built/local/temp");
+};
+
+const cleanLoggedIO = () => del("built/local/temp/loggedIO.js");
+cleanTasks.push(cleanLoggedIO);
+
+const buildInstrumenter = () => buildProject("src/instrumenter");
+const cleanInstrumenter = () => cleanProject("src/instrumenter");
+cleanTasks.push(cleanInstrumenter);
+
+const tscInstrumented = () => exec(process.execPath, ["built/local/instrumenter.js", "record", cmdLineOptions.tests || "iocapture", "built/local"]);
+task("tsc-instrumented", series(lkgPreBuild, parallel(localize, buildTsc, buildServer, buildServices, buildLssl, buildLoggedIO, buildInstrumenter), tscInstrumented));
+task("tsc-instrumented").description = "Builds an instrumented tsc.js";
+task("tsc-instrumented").flags = {
+ "-t --tests=": "The test to run."
+}
+
+// TODO(rbuckton): Determine if we still need this task. Depending on a relative
+// path here seems like a bad idea.
+const updateSublime = () => src(["built/local/tsserver.js", "built/local/tsserver.js.map"])
+ .pipe(dest("../TypeScript-Sublime-Plugin/tsserver/"));
+task("update-sublime", updateSublime);
+task("update-sublime").description = "Updates the sublime plugin's tsserver";
+
+const buildImportDefinitelyTypedTests = () => buildProject("scripts/importDefinitelyTypedTests");
+const cleanImportDefinitelyTypedTests = () => cleanProject("scripts/importDefinitelyTypedTests");
+cleanTasks.push(cleanImportDefinitelyTypedTests);
+
+// TODO(rbuckton): Should the path to DefinitelyTyped be configurable via an environment variable?
+const importDefinitelyTypedTests = () => exec(process.execPath, ["scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js", "./", "../DefinitelyTyped"]);
+task("importDefinitelyTypedTests", series(buildImportDefinitelyTypedTests, importDefinitelyTypedTests));
+task("importDefinitelyTypedTests").description = "Runs the importDefinitelyTypedTests script to copy DT's tests to the TS-internal RWC tests";
+
+const buildReleaseTsc = () => buildProject("src/tsc/tsconfig.release.json");
+const cleanReleaseTsc = () => cleanProject("src/tsc/tsconfig.release.json");
+cleanTasks.push(cleanReleaseTsc);
+
+const buildCancellationToken = () => buildProject("src/cancellationToken");
+const cleanCancellationToken = () => cleanProject("src/cancellationToken");
+cleanTasks.push(cleanCancellationToken);
+
+const buildTypingsInstaller = () => buildProject("src/typingsInstaller");
+const cleanTypingsInstaller = () => cleanProject("src/typingsInstaller");
+cleanTasks.push(cleanTypingsInstaller);
+
+const buildWatchGuard = () => buildProject("src/watchGuard");
+const cleanWatchGuard = () => cleanProject("src/watchGuard");
+cleanTasks.push(cleanWatchGuard);
+
+// TODO(rbuckton): This task isn't triggered by any other task. Is it still needed?
+const generateTypesMap = () => src("src/server/typesMap.json")
+ .pipe(newer("built/local/typesMap.json"))
+ .pipe(transform(contents => (JSON.parse(contents), contents))) // validates typesMap.json is valid JSON
+ .pipe(dest("built/local"));
+task("generate-types-map", generateTypesMap);
+
+const cleanTypesMap = () => del("built/local/typesMap.json");
+cleanTasks.push(cleanTypesMap);
+
+const cleanBuilt = () => del("built");
+
+const produceLKG = async () => {
const expectedFiles = [
- tscReleaseJs,
- typescriptServicesJs,
- tsserverJs,
- typescriptJs,
- typescriptDts,
- typescriptServicesDts,
- tsserverlibraryDts,
- tsserverlibraryDts,
- typingsInstallerJs,
- cancellationTokenJs
- ].concat(libraryTargets);
+ "built/local/tsc.release.js",
+ "built/local/typescriptServices.js",
+ "built/local/typescriptServices.d.ts",
+ "built/local/tsserver.js",
+ "built/local/typescript.js",
+ "built/local/typescript.d.ts",
+ "built/local/tsserverlibrary.js",
+ "built/local/tsserverlibrary.d.ts",
+ "built/local/typingsInstaller.js",
+ "built/local/cancellationToken.js"
+ ].concat(libs.map(lib => lib.target));
const missingFiles = expectedFiles
.concat(localizationTargets)
.filter(f => !fs.existsSync(f));
@@ -301,335 +594,66 @@ gulp.task("produce-LKG", /*help*/ false, ["scripts", "local", cancellationTokenJ
throw new Error("Cannot replace the LKG unless all built targets are present in directory 'built/local/'. The following files are missing:\n" + missingFiles.join("\n"));
}
const sizeBefore = getDirSize("lib");
- return exec(host, [produceLKGJs]).then(() => {
- const sizeAfter = getDirSize("lib");
- if (sizeAfter > (sizeBefore * 1.10)) {
- throw new Error("The lib folder increased by 10% or more. This likely indicates a bug.");
- }
- });
+ await exec(process.execPath, ["scripts/produceLKG.js"]);
+ const sizeAfter = getDirSize("lib");
+ if (sizeAfter > (sizeBefore * 1.10)) {
+ throw new Error("The lib folder increased by 10% or more. This likely indicates a bug.");
+ }
+};
+
+task("LKG", series(lkgPreBuild, parallel(localize, buildTsc, buildServer, buildServices, buildLssl, buildCancellationToken, buildTypingsInstaller, buildWatchGuard, buildReleaseTsc), produceLKG));
+task("LKG").description = "Makes a new LKG out of the built js files";
+task("LKG").flags = {
+ " --built": "Compile using the built version of the compiler.",
+}
+
+const generateSpec = () => exec("cscript", ["//nologo", "scripts/word2md.js", path.resolve("doc/TypeScript Language Specification.docx"), path.resolve("doc/spec.md")]);
+task("generate-spec", series(buildScripts, generateSpec));
+task("generate-spec").description = "Generates a Markdown version of the Language Specification";
+
+task("clean", series(parallel(cleanTasks), cleanBuilt));
+task("clean").description = "Cleans build outputs";
+
+const configureNightly = () => exec(process.execPath, ["scripts/configurePrerelease.js", "dev", "package.json", "src/compiler/core.ts"])
+task("configure-nightly", series(buildScripts, configureNightly));
+task("configure-nightly").description = "Runs scripts/configurePrerelease.ts to prepare a build for nightly publishing";
+
+const publishNightly = () => exec("npm", ["publish", "--tag", "next"]);
+task("publish-nightly", series(task("clean"), task("LKG"), task("clean"), task("runtests-parallel"), publishNightly));
+task("publish-nightly").description = "Runs `npm publish --tag next` to create a new nightly build on npm";
+
+// TODO(rbuckton): The problem with watching in this way is that a change in compiler/ will result
+// in cascading changes in other projects that may take differing amounts of times to complete. As
+// a result, the watch may accidentally trigger early, so we have to set a significant delay. An
+// alternative approach would be to leverage a builder API, or to have 'tsc -b' have an option to
+// write some kind of trigger file that indicates build completion that we could listen for instead.
+const watchRuntests = () => watch(["built/local/*.js", "tests/cases/**/*.ts", "tests/cases/**/tsconfig.json"], { delay: 5000 }, async () => {
+ if (cmdLineOptions.tests || cmdLineOptions.failed) {
+ await runConsoleTests("built/local/run.js", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ true);
+ }
+ else {
+ await runConsoleTests("built/local/run.js", "min", /*runInParallel*/ true, /*watchMode*/ true);
+ }
});
+task("watch", series(preBuild, preTest, parallel(watchLib, watchDiagnostics, watchServices, watchLssl, watchTests, watchRuntests)));
+task("watch").description = "Watches for changes and rebuilds and runs tests in parallel.";
+task("watch").flags = {
+ "-t --tests=": "Pattern for tests to run. Forces tests to be run in a single worker.",
+ " --failed": "Runs tests listed in '.failed-tests'. Forces tests to be run in a single worker.",
+ "-r --reporter=": "The mocha reporter to use.",
+ " --keepFailed": "Keep tests in .failed-tests even if they pass",
+ " --light": "Run tests in light mode (fewer verifications, but tests run faster)",
+ " --dirty": "Run tests without first cleaning test output directories",
+ " --stackTraceLimit=": "Sets the maximum number of stack frames to display. Use 'full' to show all frames.",
+ " --no-color": "Disables color",
+ " --no-lint": "Disables lint",
+ " --timeout=": "Overrides the default test timeout.",
+ " --workers=": "The number of parallel workers to use.",
+ " --built": "Compile using the built version of the compiler.",
+};
-gulp.task(
- "LKG",
- "Makes a new LKG out of the built js files",
- () => runSequence("clean-built", "produce-LKG"));
+task("default", series("local"));
+task("default").description = "Runs 'local'";
-// Task to build the tests infrastructure using the built compiler
-const testRunnerProject = "src/testRunner/tsconfig.json";
-const runJs = "built/local/run.js";
-gulp.task(runJs, /*help*/ false, useCompilerDeps, () => project.compile(testRunnerProject, { typescript: useCompiler }));
-
-gulp.task(
- "tests",
- "Builds the test infrastructure using the built compiler",
- [runJs, tsserverlibraryDts]);
-
-gulp.task(
- "runtests-parallel",
- "Runs all the tests in parallel using the built run.js file. Optional arguments are: --t[ests]=category1|category2|... --d[ebug]=true.",
- ["build-rules", "tests", "services", tsserverlibraryDts],
- () => runConsoleTests(runJs, "min", /*runInParallel*/ true, /*watchMode*/ false));
-
-gulp.task(
- "runtests",
- "Runs the tests using the built run.js file. Optional arguments are: --t[ests]=regex --r[eporter]=[list|spec|json|] --d[ebug]=true --color[s]=false --lint=true.",
- ["build-rules", "tests", "services", tsserverlibraryDts],
- () => runConsoleTests(runJs, "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false));
-
-const webTestServerProject = "tests/webTestServer.tsconfig.json";
-const webTestServerJs = "tests/webTestServer.js";
-gulp.task(webTestServerJs, /*help*/ false, useCompilerDeps, () => project.compile(webTestServerProject, { typescript: useCompiler }));
-gulp.task(`clean:${webTestServerJs}`, /*help*/ false, () => project.clean(webTestServerProject));
-
-const bundlePath = path.resolve("built/local/bundle.js");
-
-gulp.task(
- "browserify",
- "Runs browserify on run.js to produce a file suitable for running tests in the browser",
- [runJs],
- () => gulp.src([runJs], { base: "built/local" })
- .pipe(newer(bundlePath))
- .pipe(sourcemaps.init({ loadMaps: true }))
- .pipe(browserify())
- .pipe(rename("bundle.js"))
- .pipe(sourcemaps.write(".", /**@type {*}*/({ includeContent: false, destPath: "built/local" })))
- .pipe(gulp.dest("built/local")));
-
-gulp.task(
- "runtests-browser",
- "Runs the tests using the built run.js file like 'gulp runtests'. Syntax is gulp runtests-browser. Additional optional parameters --tests=[regex], --browser=[chrome|IE]",
- ["browserify", webTestServerJs],
- () => cleanTestDirs().then(() => {
- const tests = cmdLineOptions.tests;
- const runners = cmdLineOptions.runners;
- const light = cmdLineOptions.light;
- const testConfigFile = "test.config";
- if (fs.existsSync(testConfigFile)) {
- fs.unlinkSync(testConfigFile);
- }
- if (tests || runners || light) {
- writeTestConfigFile(tests, runners, light);
- }
- const args = [webTestServerJs];
- if (cmdLineOptions.browser) {
- args.push(cmdLineOptions.browser);
- }
- if (tests) {
- args.push(JSON.stringify(tests));
- }
- return exec("node", args);
- }));
-
-gulp.task(
- "generate-code-coverage",
- "Generates code coverage data via istanbul",
- ["tests"],
- () => exec("istanbul", ["cover", "node_modules/mocha/bin/_mocha", "--", "-R", "min", "-t", "" + cmdLineOptions.testTimeout, runJs]));
-
-
-gulp.task(
- "diff",
- "Diffs the compiler baselines using the diff tool specified by the 'DIFF' environment variable",
- () => exec(getDiffTool(), [refBaseline, localBaseline], { ignoreExitCode: true }));
-
-gulp.task(
- "diff-rwc",
- "Diffs the RWC baselines using the diff tool specified by the 'DIFF' environment variable",
- () => exec(getDiffTool(), [refRwcBaseline, localRwcBaseline], { ignoreExitCode: true }));
-
-gulp.task(
- "baseline-accept",
- "Makes the most recent test results the new baseline, overwriting the old baseline",
- () => baselineAccept());
-
-gulp.task(
- "baseline-accept-rwc",
- "Makes the most recent rwc test results the new baseline, overwriting the old baseline",
- () => baselineAccept("rwc"));
-
-gulp.task(
- "baseline-accept-test262",
- "Makes the most recent test262 test results the new baseline, overwriting the old baseline",
- () => baselineAccept("test262"));
-
-// Webhost
-const webtscProject = "tests/webhost/webtsc.tsconfig.json";
-const webtscJs = "tests/webhost/webtsc.js";
-gulp.task(webtscJs, /*help*/ false, useCompilerDeps, () => project.compile(webtscProject, { typescript: useCompiler }));
-gulp.task(`clean:${webtscJs}`, /*help*/ false, () => project.clean(webtscProject));
-
-gulp.task("webhost", "Builds the tsc web host", [webtscJs], () =>
- gulp.src("built/local/lib.d.ts")
- .pipe(gulp.dest("tests/webhost/")));
-
-// Perf compiler
-const perftscProject = "tests/perftsc.tsconfig.json";
-const perftscJs = "built/local/perftsc.js";
-gulp.task(perftscJs, /*help*/ false, useCompilerDeps, () => project.compile(perftscProject, { typescript: useCompiler }));
-gulp.task(`clean:${perftscJs}`, /*help*/ false, () => project.clean(perftscProject));
-
-gulp.task(
- "perftsc",
- "Builds augmented version of the compiler for perf tests",
- [perftscJs]);
-
-// Instrumented compiler
-const loggedIOTs = "src/harness/loggedIO.ts";
-const loggedIOJs = "built/local/loggedIO.js";
-gulp.task(loggedIOJs, /*help*/ false, [], (done) => {
- return mkdirp("built/local/temp")
- .then(() => exec(host, ["lib/tsc.js", "--types", "--target es5", "--lib es5", "--outdir", "built/local/temp", loggedIOTs]))
- .then(() => { fs.renameSync(path.join("built/local/temp", "/harness/loggedIO.js"), loggedIOJs); })
- .then(() => del("built/local/temp"));
-});
-
-const instrumenterProject = "src/instrumenter/tsconfig.json";
-const instrumenterJs = "built/local/instrumenter.js";
-gulp.task(instrumenterJs, /*help*/ false, () => project.compile(instrumenterProject));
-gulp.task(`clean:${instrumenterJs}`, /*help*/ false, () => project.clean(instrumenterProject));
-
-gulp.task(
- "tsc-instrumented",
- "Builds an instrumented tsc.js - run with --test=[testname]",
- ["local", loggedIOJs, instrumenterJs, typescriptServicesJs],
- () => exec(host, [instrumenterJs, "record", cmdLineOptions.tests || "iocapture", "built/local"]));
-
-gulp.task(
- "update-sublime",
- "Updates the sublime plugin's tsserver",
- ["local", tsserverJs],
- () =>
- gulp.src([tsserverJs, tsserverJs + ".map"])
- .pipe(gulp.dest("../TypeScript-Sublime-Plugin/tsserver/")));
-
-gulp.task(
- "build-rules",
- "Compiles tslint rules to js",
- () => project.compile("scripts/tslint/tsconfig.json"));
-
-gulp.task("clean-rules", /*help*/ false, () => project.clean("scripts/tslint/tsconfig.json"));
-
-gulp.task(
- "lint",
- "Runs tslint on the compiler sources. Optional arguments are: --f[iles]=regex",
- ["build-rules"],
- () => {
- if (fold.isTravis()) console.log(fold.start("lint"));
- for (const project of ["scripts/tslint/tsconfig.json", "src/tsconfig-base.json"]) {
- const cmd = `node node_modules/tslint/bin/tslint --project ${project} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish${cmdLineOptions.fix ? " --fix" : ""}`;
- log("Linting: " + cmd);
- child_process.execSync(cmd, { stdio: [0, 1, 2] });
- }
- if (fold.isTravis()) console.log(fold.end("lint"));
- });
-
-gulp.task(
- "default",
- "Runs 'local'",
- ["local"]);
-
-gulp.task(
- "watch-lib",
- /*help*/ false,
- () => gulp.watch(["src/lib/**/*"], ["lib"]));
-
-const watchTscPatterns = [
- "src/tsconfig-base.json",
- "src/lib/**/*",
- "src/compiler/**/*",
- "src/tsc/**/*",
-];
-gulp.task(
- "watch-tsc",
- /*help*/ false,
- useCompilerDeps,
- () => gulp.watch(watchTscPatterns, ["tsc"]));
-
-const watchServicesPatterns = [
- "src/compiler/**/*",
- "src/jsTypings/**/*",
- "src/services/**/*"
-];
-gulp.task(
- "watch-services",
- /*help*/ false,
- ["watch-diagnostics", "watch-lib"],
- () => gulp.watch(watchServicesPatterns, ["services"]));
-
-const watchLsslPatterns = [
- ...watchServicesPatterns,
- "src/server/**/*",
- "src/tsserver/tsconfig.json"
-];
-gulp.task(
- "watch-lssl",
- /*help*/ false,
- () => gulp.watch(watchLsslPatterns, ["lssl"]));
-
-const watchLocalPatterns = [
- "src/tsconfig-base.json",
- "src/lib/**/*",
- "src/compiler/**/*",
- "src/tsc/**/*",
- "src/services/**/*",
- "src/jsTyping/**/*",
- "src/server/**/*",
- "src/tsserver/**/*",
- "src/typingsInstallerCore/**/*",
- "src/harness/**/*",
- "src/testRunner/**/*",
-];
-gulp.task(
- "watch-local",
- "Watches for changes to projects in src/ (but does not execute tests).",
- () => gulp.watch(watchLocalPatterns, "local"));
-
-const watchPatterns = [
- "src/tsconfig-base.json",
- "src/lib/**/*",
- "src/compiler/**/*",
- "src/services/**/*",
- "src/jsTyping/**/*",
- "src/server/**/*",
- "src/tsserver/**/*",
- "src/typingsInstallerCore/**/*",
- "src/harness/**/*",
- "src/testRunner/**/*",
-];
-gulp.task(
- "watch",
- "Watches for changes to the build inputs for built/local/run.js, then runs tests.",
- ["build-rules"],
- () => {
- const sem = new Semaphore(1);
-
- gulp.watch(watchPatterns, () => { runTests(); });
-
- // NOTE: gulp.watch is far too slow when watching tests/cases/**/* as it first enumerates *every* file
- const testFilePattern = /(\.ts|[\\/]tsconfig\.json)$/;
- fs.watch("tests/cases", { recursive: true }, (_, file) => {
- if (testFilePattern.test(file)) runTests();
- });
-
- async function runTests() {
- try {
- // Ensure only one instance of the test runner is running at any given time.
- if (sem.count > 0) {
- await sem.wait();
- try {
- // Wait for any concurrent recompilations to complete...
- try {
- await delay(100);
- while (project.hasRemainingWork()) {
- await project.waitForWorkToComplete();
- await delay(500);
- }
- }
- catch (e) {
- if (e instanceof CancelError) return;
- throw e;
- }
-
- // cancel any pending or active test run if a new recompilation is triggered
- const source = new CancellationTokenSource();
- project.waitForWorkToStart().then(() => {
- source.cancel();
- });
-
- if (cmdLineOptions.tests || cmdLineOptions.failed) {
- await runConsoleTests(runJs, "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ true, source.token);
- }
- else {
- await runConsoleTests(runJs, "min", /*runInParallel*/ true, /*watchMode*/ true, source.token);
- }
- }
- finally {
- sem.release();
- }
- }
- }
- catch (e) {
- if (e instanceof CancelError) {
- log.warn("Operation was canceled");
- }
- else {
- log.error(e);
- }
- }
- };
- });
-
-gulp.task("clean-built", /*help*/ false, [`clean:${diagnosticInformationMapTs}`], () => del(["built"]));
-gulp.task(
- "clean",
- "Cleans the compiler output, declare files, and tests",
- [
- `clean:${importDefinitelyTypedTestsJs}`,
- `clean:${webtscJs}`,
- `clean:${perftscJs}`,
- `clean:${instrumenterJs}`,
- `clean:${webTestServerJs}`,
- "clean:scripts",
- "clean-rules",
- "clean-built"
- ]);
\ No newline at end of file
+task("help", () => exec("gulp", ["--tasks", "--depth", "1", "--sort-tasks"], { hidePrompt: true }));
+task("help").description = "Prints the top-level tasks.";
diff --git a/Jakefile.js b/Jakefile.js
index 1413230c07..ea81881e7d 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -8,10 +8,8 @@ const path = require("path");
const fold = require("travis-fold");
const ts = require("./lib/typescript");
const del = require("del");
-const getDirSize = require("./scripts/build/getDirSize");
+const { getDirSize, needsUpdate, flatten } = require("./scripts/build/utils");
const { base64VLQFormatEncode } = require("./scripts/build/sourcemaps");
-const needsUpdate = require("./scripts/build/needsUpdate");
-const { flatten } = require("./scripts/build/project");
// add node_modules to path so we don't need global modules, prefer the modules by adding them first
var nodeModulesPathPrefix = path.resolve("./node_modules/.bin/") + path.delimiter;
@@ -361,7 +359,7 @@ file(ConfigFileFor.tsserverLibrary, [], function () {
compilerOptions: {
"removeComments": false,
"stripInternal": true,
- "declarationMap": false,
+ "declaration": true,
"outFile": "tsserverlibrary.out.js"
}
})
diff --git a/README.md b/README.md
index 57c2a54385..2826db8aec 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
[![Build Status](https://travis-ci.org/Microsoft/TypeScript.svg?branch=master)](https://travis-ci.org/Microsoft/TypeScript)
-[![VSTS Build Status](https://dev.azure.com/typescript/TypeScript/_apis/build/status/Typescript/node10)](https://https://dev.azure.com/typescript/TypeScript/_build/latest?definitionId=4&view=logs)
+[![VSTS Build Status](https://dev.azure.com/typescript/TypeScript/_apis/build/status/Typescript/node10)](https://dev.azure.com/typescript/TypeScript/_build/latest?definitionId=4&view=logs)
[![npm version](https://badge.fury.io/js/typescript.svg)](https://www.npmjs.com/package/typescript)
[![Downloads](https://img.shields.io/npm/dm/typescript.svg)](https://www.npmjs.com/package/typescript)
diff --git a/doc/spec.md b/doc/spec.md
index edf8455ac5..741d4f29ce 100644
--- a/doc/spec.md
+++ b/doc/spec.md
@@ -3715,7 +3715,7 @@ the array literal initializer expression is contextually typed by the implied ty
## 5.3 Let and Const Declarations
-Let and const declarations are exended to include optional type annotations.
+Let and const declarations are extended to include optional type annotations.
*LexicalBinding:* *( Modified )*
*SimpleLexicalBinding*
diff --git a/package.json b/package.json
index d8c514363e..4f4f7db571 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "typescript",
"author": "Microsoft Corp.",
"homepage": "https://www.typescriptlang.org/",
- "version": "3.3.0",
+ "version": "3.4.0",
"license": "Apache-2.0",
"description": "TypeScript is a language for application scale JavaScript development",
"keywords": [
@@ -35,10 +35,8 @@
"@types/convert-source-map": "latest",
"@types/del": "latest",
"@types/glob": "latest",
- "@types/gulp": "3.X",
+ "@types/gulp": "^4.0.5",
"@types/gulp-concat": "latest",
- "@types/gulp-help": "latest",
- "@types/gulp-if": "0.0.33",
"@types/gulp-newer": "latest",
"@types/gulp-rename": "0.0.33",
"@types/gulp-sourcemaps": "0.0.32",
@@ -50,7 +48,6 @@
"@types/mocha": "latest",
"@types/node": "8.5.5",
"@types/q": "latest",
- "@types/run-sequence": "latest",
"@types/source-map-support": "latest",
"@types/through2": "latest",
"@types/travis-fold": "latest",
@@ -63,16 +60,12 @@
"del": "latest",
"fancy-log": "latest",
"fs-extra": "^6.0.1",
- "gulp": "3.X",
- "gulp-clone": "latest",
+ "gulp": "^4.0.0",
"gulp-concat": "latest",
- "gulp-help": "latest",
- "gulp-if": "latest",
"gulp-insert": "latest",
"gulp-newer": "latest",
"gulp-rename": "latest",
"gulp-sourcemaps": "latest",
- "gulp-typescript": "latest",
"istanbul": "latest",
"jake": "latest",
"lodash": "4.17.10",
@@ -86,7 +79,6 @@
"prex": "^0.4.3",
"q": "latest",
"remove-internal": "^2.9.2",
- "run-sequence": "latest",
"source-map-support": "latest",
"through2": "latest",
"travis-fold": "latest",
diff --git a/scripts/build/baselineAccept.js b/scripts/build/baselineAccept.js
deleted file mode 100644
index df61b87cd3..0000000000
--- a/scripts/build/baselineAccept.js
+++ /dev/null
@@ -1,24 +0,0 @@
-// @ts-check
-const merge2 = require("merge2");
-const gulp = require("./gulp");
-const rename = require("gulp-rename");
-const rm = require("./rm");
-const { localBaseline, refBaseline } = require("./tests");
-
-module.exports = baselineAccept;
-
-function baselineAccept(subfolder = "") {
- return merge2(baselineCopy(subfolder), baselineDelete(subfolder));
-}
-
-function baselineCopy(subfolder = "") {
- return gulp.src([`${localBaseline}${subfolder ? `${subfolder}/` : ``}**`, `!${localBaseline}${subfolder}/**/*.delete`], { base: localBaseline })
- .pipe(gulp.dest(refBaseline));
-}
-
-function baselineDelete(subfolder = "") {
- return gulp.src([`${localBaseline}${subfolder ? `${subfolder}/` : ``}**/*.delete`], { base: localBaseline, read: false })
- .pipe(rm())
- .pipe(rename({ extname: "" }))
- .pipe(rm(refBaseline));
-}
diff --git a/scripts/build/browserify.js b/scripts/build/browserify.js
index 1fa5bbdeaf..ba8ccd1582 100644
--- a/scripts/build/browserify.js
+++ b/scripts/build/browserify.js
@@ -1,12 +1,10 @@
// @ts-check
const browserify = require("browserify");
-const Vinyl = require("./vinyl");
+const Vinyl = require("vinyl");
const { Transform } = require("stream");
const { streamFromFile } = require("./utils");
const { replaceContents } = require("./sourcemaps");
-module.exports = browserifyFile;
-
/**
* @param {import("browserify").Options} [opts]
*/
@@ -31,4 +29,5 @@ function browserifyFile(opts) {
}
}
});
-}
\ No newline at end of file
+}
+exports.browserify = browserifyFile;
\ No newline at end of file
diff --git a/scripts/build/chalk.js b/scripts/build/chalk.js
deleted file mode 100644
index 149c8ea153..0000000000
--- a/scripts/build/chalk.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// @ts-check
-
-// this just fixes the incorrect types for chalk :/
-const chalk = /**@type {import("chalk").Chalk}*/(require("chalk").default || require("chalk"));
-module.exports = chalk;
\ No newline at end of file
diff --git a/scripts/build/convertConstEnum.js b/scripts/build/convertConstEnum.js
deleted file mode 100644
index 00d48fdcc3..0000000000
--- a/scripts/build/convertConstEnum.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// @ts-check
-const replace = require("./replace");
-
-module.exports = exports = convertConstEnum;
-
-/**
- * This regexp exists to capture our const enums and replace them with normal enums in our public API
- * - this is fine since we compile with preserveConstEnums, and ensures our consumers are not locked
- * to the TS version they compile with.
- */
-const constEnumCaptureRegexp = /^(\s*)(export )?const enum (\S+) {(\s*)$/gm;
-const constEnumReplacement = "$1$2enum $3 {$4";
-
-/**
- * Converts `const enum` declarations in a .d.ts file into non-const `enum` declarations.
- */
-function convertConstEnum() {
- return replace(constEnumCaptureRegexp, constEnumReplacement);
-}
\ No newline at end of file
diff --git a/scripts/build/debounce.js b/scripts/build/debounce.js
deleted file mode 100644
index 7020cb61bb..0000000000
--- a/scripts/build/debounce.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// @ts-check
-module.exports = debounce;
-
-/**
- * @param {() => void} cb
- * @param {number} timeout
- * @param {DebounceOptions} [opts]
- *
- * @typedef DebounceOptions
- * @property {number} [max]
- */
-function debounce(cb, timeout, opts = {}) {
- if (timeout < 10) timeout = 10;
- let max = opts.max || 10;
- if (max < timeout) max = timeout;
- let minTimer;
- let maxTimer;
- return trigger;
-
- function trigger() {
- if (max > timeout && !maxTimer) maxTimer = setTimeout(done, max);
- if (minTimer) clearTimeout(minTimer);
- minTimer = setTimeout(done, timeout);
- }
-
- function done() {
- if (maxTimer) maxTimer = void clearTimeout(maxTimer);
- if (minTimer) minTimer = void clearTimeout(minTimer);
- cb();
- }
-}
\ No newline at end of file
diff --git a/scripts/build/diagnostics.js b/scripts/build/diagnostics.js
deleted file mode 100644
index 5ca51c572e..0000000000
--- a/scripts/build/diagnostics.js
+++ /dev/null
@@ -1,49 +0,0 @@
-// @ts-check
-const ts = require("../../lib/typescript");
-const log = require("fancy-log"); // was `require("gulp-util").log (see https://github.com/gulpjs/gulp-util)
-
-/** @type {FormatDiagnosticsHost} */
-const formatDiagnosticsHost = exports.formatDiagnosticsHost = {
- getCanonicalFileName: fileName => fileName,
- getCurrentDirectory: () => process.cwd(),
- getNewLine: () => ts.sys.newLine
-};
-
-/**
- * @param {Diagnostic[]} diagnostics
- * @param {{ cwd?: string, pretty?: boolean }} [options]
- */
-function formatDiagnostics(diagnostics, options) {
- return options && options.pretty
- ? ts.formatDiagnosticsWithColorAndContext(diagnostics, getFormatDiagnosticsHost(options && options.cwd))
- : ts.formatDiagnostics(diagnostics, getFormatDiagnosticsHost(options && options.cwd));
-}
-exports.formatDiagnostics = formatDiagnostics;
-
-/**
- * @param {Diagnostic[]} diagnostics
- * @param {{ cwd?: string }} [options]
- */
-function reportDiagnostics(diagnostics, options) {
- log(formatDiagnostics(diagnostics, { cwd: options && options.cwd, pretty: process.stdout.isTTY }));
-}
-exports.reportDiagnostics = reportDiagnostics;
-
-/**
- * @param {string | undefined} cwd
- * @returns {FormatDiagnosticsHost}
- */
-function getFormatDiagnosticsHost(cwd) {
- if (!cwd || cwd === process.cwd()) return formatDiagnosticsHost;
- return {
- getCanonicalFileName: formatDiagnosticsHost.getCanonicalFileName,
- getCurrentDirectory: () => cwd,
- getNewLine: formatDiagnosticsHost.getNewLine
- };
-}
-
-/**
- * @typedef {import("../../lib/typescript").FormatDiagnosticsHost} FormatDiagnosticsHost
- * @typedef {import("../../lib/typescript").Diagnostic} Diagnostic
- */
-void 0;
\ No newline at end of file
diff --git a/scripts/build/exec.js b/scripts/build/exec.js
deleted file mode 100644
index 8e0a058fed..0000000000
--- a/scripts/build/exec.js
+++ /dev/null
@@ -1,58 +0,0 @@
-// @ts-check
-const cp = require("child_process");
-const log = require("fancy-log"); // was `require("gulp-util").log (see https://github.com/gulpjs/gulp-util)
-const isWin = /^win/.test(process.platform);
-const chalk = require("./chalk");
-const { CancellationToken, CancelError } = require("prex");
-
-module.exports = exec;
-
-/**
- * Executes the provided command once with the supplied arguments.
- * @param {string} cmd
- * @param {string[]} args
- * @param {ExecOptions} [options]
- *
- * @typedef ExecOptions
- * @property {boolean} [ignoreExitCode]
- * @property {import("prex").CancellationToken} [cancelToken]
- */
-function exec(cmd, args, options = {}) {
- return /**@type {Promise<{exitCode: number}>}*/(new Promise((resolve, reject) => {
- const { ignoreExitCode, cancelToken = CancellationToken.none } = options;
- cancelToken.throwIfCancellationRequested();
-
- // TODO (weswig): Update child_process types to add windowsVerbatimArguments to the type definition
- const subshellFlag = isWin ? "/c" : "-c";
- const command = isWin ? [possiblyQuote(cmd), ...args] : [`${cmd} ${args.join(" ")}`];
-
- log(`> ${chalk.green(cmd)} ${args.join(" ")}`);
- const proc = cp.spawn(isWin ? "cmd" : "/bin/sh", [subshellFlag, ...command], { stdio: "inherit", windowsVerbatimArguments: true });
- const registration = cancelToken.register(() => {
- log(`${chalk.red("killing")} '${chalk.green(cmd)} ${args.join(" ")}'...`);
- proc.kill("SIGINT");
- proc.kill("SIGTERM");
- reject(new CancelError());
- });
- proc.on("exit", exitCode => {
- registration.unregister();
- if (exitCode === 0 || ignoreExitCode) {
- resolve({ exitCode });
- }
- else {
- reject(new Error(`Process exited with code: ${exitCode}`));
- }
- });
- proc.on("error", error => {
- registration.unregister();
- reject(error);
- });
- }));
-}
-
-/**
- * @param {string} cmd
- */
-function possiblyQuote(cmd) {
- return cmd.indexOf(" ") >= 0 ? `"${cmd}"` : cmd;
-}
diff --git a/scripts/build/finished.js b/scripts/build/finished.js
deleted file mode 100644
index e642c06419..0000000000
--- a/scripts/build/finished.js
+++ /dev/null
@@ -1,46 +0,0 @@
-// @ts-check
-module.exports = finished;
-
-/**
- * @param {NodeJS.ReadableStream | NodeJS.WritableStream} stream
- * @returns {Promise}
- */
-function finished(stream) {
- return new Promise((resolve, reject) => {
- const readable = "readable" in stream && stream.readable;
- const writable = "writable" in stream && stream.writable;
-
- let countdown = 0;
- const cleanup = () => {
- if (readable) stream.removeListener("end", signal);
- if (writable) stream.removeListener("finish", signal);
- stream.removeListener("error", onerror);
- };
- const signal = () => {
- if (countdown > 0) {
- countdown--;
- if (countdown === 0) {
- cleanup();
- resolve();
- }
- }
- };
- const onerror = (error) => {
- if (countdown > 0) {
- countdown = 0;
- cleanup();
- reject(error);
- }
- };
- stream.once("error", onerror);
- if (readable) {
- countdown++;
- stream.once("end", signal);
- }
- if (writable) {
- countdown++;
- stream.once("finish", signal);
- }
- if (countdown === 0) signal();
- });
-}
\ No newline at end of file
diff --git a/scripts/build/getDiffTool.js b/scripts/build/getDiffTool.js
deleted file mode 100644
index 13a30b8933..0000000000
--- a/scripts/build/getDiffTool.js
+++ /dev/null
@@ -1,12 +0,0 @@
-// @ts-check
-const log = require("fancy-log"); // was `require("gulp-util").log (see https://github.com/gulpjs/gulp-util)
-module.exports = getDiffTool;
-
-function getDiffTool() {
- const program = process.env.DIFF;
- if (!program) {
- log.warn("Add the 'DIFF' environment variable to the path of the program you want to use.");
- process.exit(1);
- }
- return program;
-}
\ No newline at end of file
diff --git a/scripts/build/getDirSize.js b/scripts/build/getDirSize.js
deleted file mode 100644
index 58a65907e4..0000000000
--- a/scripts/build/getDirSize.js
+++ /dev/null
@@ -1,23 +0,0 @@
-// @ts-check
-const { lstatSync, readdirSync } = require("fs");
-const { join } = require("path");
-
-/**
- * Find the size of a directory recursively.
- * Symbolic links can cause a loop.
- * @param {string} root
- * @returns {number} bytes
- */
-function getDirSize(root) {
- const stats = lstatSync(root);
-
- if (!stats.isDirectory()) {
- return stats.size;
- }
-
- return readdirSync(root)
- .map(file => getDirSize(join(root, file)))
- .reduce((acc, num) => acc + num, 0);
-}
-
-module.exports = getDirSize;
diff --git a/scripts/build/gulp-typescript-oop/index.js b/scripts/build/gulp-typescript-oop/index.js
deleted file mode 100644
index ed7a7d64a0..0000000000
--- a/scripts/build/gulp-typescript-oop/index.js
+++ /dev/null
@@ -1,149 +0,0 @@
-// @ts-check
-const path = require("path");
-const child_process = require("child_process");
-const fs = require("fs");
-const tsc = require("gulp-typescript");
-const Vinyl = require("vinyl");
-const { Duplex, Readable } = require("stream");
-const protocol = require("./protocol");
-
-/**
- * @param {string | undefined} tsConfigFileName
- * @param {tsc.Settings} settings
- * @param {CreateProjectOptions} options
- *
- * @typedef CreateProjectOptions
- * @property {string} [typescript]
- * @property {boolean} [parse]
- */
-function createProject(tsConfigFileName, settings, options) {
- settings = Object.assign({}, settings);
- options = Object.assign({}, options);
- if (settings.typescript) throw new Error();
-
- const localSettings = Object.assign({}, settings);
- if (options.typescript) {
- options.typescript = path.resolve(options.typescript);
- localSettings.typescript = require(options.typescript);
- }
-
- const project = tsConfigFileName === undefined ? tsc.createProject(localSettings) : tsc.createProject(tsConfigFileName, localSettings);
- const wrappedProject = /** @type {tsc.Project} */((reporter = tsc.reporter.defaultReporter()) => {
- const ts = project.typescript;
- const proc = child_process.fork(require.resolve("./worker.js"), [], {
- // Prevent errors when debugging gulpfile due to the same debug port being passed to forked children.
- execArgv: []
- });
- /** @type {Map} */
- const inputs = new Map();
- /** @type {Map} */
- const sourceFiles = new Map();
- /** @type {protocol.SourceFileHost & protocol.VinylHost} */
- const host = {
- getVinyl(path) { return inputs.get(path); },
- getSourceFile(fileName) { return sourceFiles.get(fileName); },
- createSourceFile(fileName, text, languageVersion) {
- if (text === undefined) {
- text = fs.readFileSync(fileName, "utf8");
- }
-
- /** @type {protocol.SourceFile} */
- let file;
- if (options.parse) {
- file = ts.createSourceFile(fileName, text, languageVersion, /*setParentNodes*/ true);
- }
- else {
- // NOTE: the built-in reporters in gulp-typescript don't actually need a full
- // source file, so save time by faking one unless requested.
- file = /**@type {protocol.SourceFile}*/({
- pos: 0,
- end: text.length,
- kind: ts.SyntaxKind.SourceFile,
- fileName,
- text,
- languageVersion,
- statements: /**@type {*} */([]),
- endOfFileToken: { pos: text.length, end: text.length, kind: ts.SyntaxKind.EndOfFileToken },
- amdDependencies: /**@type {*} */([]),
- referencedFiles: /**@type {*} */([]),
- typeReferenceDirectives: /**@type {*} */([]),
- libReferenceDirectives: /**@type {*} */([]),
- languageVariant: ts.LanguageVariant.Standard,
- isDeclarationFile: /\.d\.ts$/.test(fileName),
- hasNoDefaultLib: /[\\/]lib\.[^\\/]+\.d\.ts$/.test(fileName)
- });
- }
- sourceFiles.set(fileName, file);
- return file;
- }
- };
- /** @type {Duplex & { js?: Readable, dts?: Readable }} */
- const compileStream = new Duplex({
- objectMode: true,
- read() {},
- /** @param {*} file */
- write(file, _encoding, callback) {
- inputs.set(file.path, file);
- proc.send(protocol.message.write(file));
- callback();
- },
- final(callback) {
- proc.send(protocol.message.final());
- callback();
- }
- });
- const jsStream = compileStream.js = new Readable({
- objectMode: true,
- read() {}
- });
- const dtsStream = compileStream.dts = new Readable({
- objectMode: true,
- read() {}
- });
- proc.send(protocol.message.createProject(tsConfigFileName, settings, options));
- proc.on("message", (/**@type {protocol.WorkerMessage}*/ message) => {
- switch (message.method) {
- case "write": {
- const file = protocol.vinylFromJson(message.params);
- compileStream.push(file);
- if (file.path.endsWith(".d.ts")) {
- dtsStream.push(file);
- }
- else {
- jsStream.push(file);
- }
- break;
- }
- case "final": {
- compileStream.push(null);
- jsStream.push(null);
- dtsStream.push(null);
- proc.kill(); // TODO(rbuckton): pool workers? may not be feasible due to gulp-typescript holding onto memory
- break;
- }
- case "error": {
- const error = protocol.errorFromJson(message.params);
- compileStream.emit("error", error);
- proc.kill(); // TODO(rbuckton): pool workers? may not be feasible due to gulp-typescript holding onto memory
- break;
- }
- case "reporter.error": {
- if (reporter.error) {
- const error = protocol.typeScriptErrorFromJson(message.params, host);
- reporter.error(error, project.typescript);
- }
- break;
- }
- case "reporter.finish": {
- if (reporter.finish) {
- reporter.finish(message.params);
- }
- }
- }
- });
- return /** @type {*} */(compileStream);
- });
- return Object.assign(wrappedProject, project);
-}
-
-exports.createProject = createProject;
\ No newline at end of file
diff --git a/scripts/build/gulp-typescript-oop/protocol.js b/scripts/build/gulp-typescript-oop/protocol.js
deleted file mode 100644
index 714b94f22a..0000000000
--- a/scripts/build/gulp-typescript-oop/protocol.js
+++ /dev/null
@@ -1,281 +0,0 @@
-// @ts-check
-const Vinyl = require("vinyl");
-
-/**
- * @param {File} file
- * @returns {*}
- */
-function vinylToJson(file) {
- if (file.isStream()) throw new TypeError("Streams not supported.");
- return {
- path: file.path,
- cwd: file.cwd,
- base: file.base,
- contents: file.isBuffer() ? file.contents.toString("utf8") : undefined,
- sourceMap: file.sourceMap
- };
-}
-exports.vinylToJson = vinylToJson;
-
-/**
- * @param {*} json
- * @returns {File}
- */
-function vinylFromJson(json) {
- return new Vinyl({
- path: json.path,
- cwd: json.cwd,
- base: json.base,
- contents: typeof json.contents === "string" ? Buffer.from(json.contents, "utf8") : undefined,
- sourceMap: json.sourceMap
- });
-}
-exports.vinylFromJson = vinylFromJson;
-
-/**
- * @param {Error} error
- * @returns {*}
- */
-function errorToJson(error) {
- return {
- name: error.name,
- message: error.message,
- stack: error.stack
- };
-}
-exports.errorToJson = errorToJson;
-
-/**
- * @param {*} json
- * @returns {Error}
- */
-function errorFromJson(json) {
- const error = new Error();
- error.name = json.name;
- error.message = json.message;
- error.stack = json.stack;
- return error;
-}
-exports.errorFromJson = errorFromJson;
-
-/**
- * @param {TypeScriptError} error
- * @returns {*}
- */
-function typeScriptErrorToJson(error) {
- return Object.assign({}, errorToJson(error), {
- fullFilename: error.fullFilename,
- relativeFilename: error.relativeFilename,
- file: error.file && { path: error.file.path },
- tsFile: error.tsFile && sourceFileToJson(error.tsFile),
- diagnostic: diagnosticToJson(error.diagnostic),
- startPosition: error.startPosition,
- endPosition: error.endPosition
- });
-}
-exports.typeScriptErrorToJson = typeScriptErrorToJson;
-
-/**
- * @param {*} json
- * @param {SourceFileHost & VinylHost} host
- * @returns {TypeScriptError}
- */
-function typeScriptErrorFromJson(json, host) {
- const error = /**@type {TypeScriptError}*/(errorFromJson(json));
- error.fullFilename = json.fullFilename;
- error.relativeFilename = json.relativeFilename;
- error.file = json.file && host.getVinyl(json.file.path);
- error.tsFile = json.tsFile && sourceFileFromJson(json.tsFile, host);
- error.diagnostic = diagnosticFromJson(json.diagnostic, host);
- error.startPosition = json.startPosition;
- error.endPosition = json.endPosition;
- return error;
-}
-exports.typeScriptErrorFromJson = typeScriptErrorFromJson;
-
-/**
- * @param {SourceFile} file
- * @returns {*}
- */
-function sourceFileToJson(file) {
- return {
- fileName: file.fileName,
- text: file.text,
- languageVersion: file.languageVersion
- };
-}
-exports.sourceFileToJson = sourceFileToJson;
-
-/**
- * @param {*} json
- * @param {SourceFileHost} host
- */
-function sourceFileFromJson(json, host) {
- return host.getSourceFile(json.fileName)
- || host.createSourceFile(json.fileName, json.text, json.languageVersion);
-}
-exports.sourceFileFromJson = sourceFileFromJson;
-
-/**
- * @param {Diagnostic} diagnostic
- * @returns {*}
- */
-function diagnosticToJson(diagnostic) {
- return Object.assign({}, diagnosticRelatedInformationToJson(diagnostic), {
- category: diagnostic.category,
- code: diagnostic.code,
- source: diagnostic.source,
- relatedInformation: diagnostic.relatedInformation && diagnostic.relatedInformation.map(diagnosticRelatedInformationToJson)
- });
-}
-exports.diagnosticToJson = diagnosticToJson;
-
-/**
- * @param {*} json
- * @param {SourceFileHost} host
- * @returns {Diagnostic}
- */
-function diagnosticFromJson(json, host) {
- return Object.assign({}, diagnosticRelatedInformationFromJson(json, host), {
- category: json.category,
- code: json.code,
- source: json.source,
- relatedInformation: json.relatedInformation && json.relatedInformation.map(json => diagnosticRelatedInformationFromJson(json, host))
- });
-}
-exports.diagnosticFromJson = diagnosticFromJson;
-
-/**
- * @param {DiagnosticRelatedInformation} diagnostic
- * @returns {*}
- */
-function diagnosticRelatedInformationToJson(diagnostic) {
- return {
- file: diagnostic.file && { fileName: diagnostic.file.fileName },
- start: diagnostic.start,
- length: diagnostic.length,
- messageText: diagnostic.messageText
- };
-}
-exports.diagnosticRelatedInformationToJson = diagnosticRelatedInformationToJson;
-
-/**
- * @param {*} json
- * @param {SourceFileHost} host
- * @returns {DiagnosticRelatedInformation}
- */
-function diagnosticRelatedInformationFromJson(json, host) {
- return {
- file: json.file && sourceFileFromJson(json.file, host),
- start: json.start,
- length: json.length,
- messageText: json.messageText,
- category: json.category,
- code: json.code
- };
-}
-exports.diagnosticRelatedInformationFromJson = diagnosticRelatedInformationFromJson;
-
-exports.message = {};
-
-/**
- * @param {string | undefined} tsConfigFileName
- * @param {import("gulp-typescript").Settings} settings
- * @param {Object} options
- * @param {string} [options.typescript]
- * @returns {CreateProjectMessage}
- *
- * @typedef CreateProjectMessage
- * @property {"createProject"} method
- * @property {CreateProjectParams} params
- *
- * @typedef CreateProjectParams
- * @property {string | undefined} tsConfigFileName
- * @property {import("gulp-typescript").Settings} settings
- * @property {CreateProjectOptions} options
- *
- * @typedef CreateProjectOptions
- * @property {string} [typescript]
- */
-exports.message.createProject = function(tsConfigFileName, settings, options) {
- return { method: "createProject", params: { tsConfigFileName, settings, options } };
-};
-
-/**
- * @param {File} file
- * @returns {WriteMessage}
- *
- * @typedef WriteMessage
- * @property {"write"} method
- * @property {*} params
- */
-exports.message.write = function(file) {
- return { method: "write", params: vinylToJson(file) };
-};
-
-/**
- * @returns {FinalMessage}
- *
- * @typedef FinalMessage
- * @property {"final"} method
- */
-exports.message.final = function() {
- return { method: "final" };
-};
-
-/**
- * @param {Error} error
- * @returns {ErrorMessage}
- *
- * @typedef ErrorMessage
- * @property {"error"} method
- * @property {*} params
- */
-exports.message.error = function(error) {
- return { method: "error", params: errorToJson(error) };
-};
-
-exports.message.reporter = {};
-
-/**
- * @param {TypeScriptError} error
- * @returns {reporter.ErrorMessage}
- *
- * @typedef reporter.ErrorMessage
- * @property {"reporter.error"} method
- * @property {*} params
- */
-exports.message.reporter.error = function(error) {
- return { method: "reporter.error", params: typeScriptErrorToJson(error) };
-};
-
-/**
- * @param {*} results
- * @returns {reporter.FinishMessage}
- *
- * @typedef reporter.FinishMessage
- * @property {"reporter.finish"} method
- * @property {*} params
- */
-exports.message.reporter.finish = function(results) {
- return { method: "reporter.finish", params: results };
-};
-
-/**
- * @typedef {import("vinyl")} File
- * @typedef {typeof import("typescript")} TypeScriptModule
- * @typedef {import("typescript").SourceFile} SourceFile
- * @typedef {import("typescript").Diagnostic} Diagnostic
- * @typedef {import("typescript").DiagnosticRelatedInformation} DiagnosticRelatedInformation
- * @typedef {import("gulp-typescript").reporter.TypeScriptError} TypeScriptError
- * @typedef {WriteMessage | FinalMessage | CreateProjectMessage} HostMessage
- * @typedef {WriteMessage | FinalMessage | ErrorMessage | reporter.ErrorMessage | reporter.FinishMessage} WorkerMessage
- *
- * @typedef SourceFileHost
- * @property {(fileName: string) => SourceFile | undefined} getSourceFile
- * @property {(fileName: string, text: string, languageVersion: number) => SourceFile} createSourceFile
- *
- * @typedef VinylHost
- * @property {(path: string) => File | undefined} getVinyl
- */
-void 0;
\ No newline at end of file
diff --git a/scripts/build/gulp-typescript-oop/worker.js b/scripts/build/gulp-typescript-oop/worker.js
deleted file mode 100644
index 5d68bc591e..0000000000
--- a/scripts/build/gulp-typescript-oop/worker.js
+++ /dev/null
@@ -1,79 +0,0 @@
-// @ts-check
-const fs = require("fs");
-const tsc = require("gulp-typescript");
-const { Readable, Writable } = require("stream");
-const protocol = require("./protocol");
-
-/** @type {tsc.Project} */
-let project;
-
-/** @type {Readable} */
-let inputStream;
-
-/** @type {Writable} */
-let outputStream;
-
-/** @type {tsc.CompileStream} */
-let compileStream;
-
-process.on("message", (/**@type {protocol.HostMessage}*/ message) => {
- try {
- switch (message.method) {
- case "createProject": {
- const { tsConfigFileName, settings, options } = message.params;
- if (options.typescript) {
- settings.typescript = require(options.typescript);
- }
-
- project = tsConfigFileName === undefined
- ? tsc.createProject(settings)
- : tsc.createProject(tsConfigFileName, settings);
-
- inputStream = new Readable({
- objectMode: true,
- read() {}
- });
-
- outputStream = new Writable({
- objectMode: true,
- /**
- * @param {*} file
- */
- write(file, _, callback) {
- process.send(protocol.message.write(file));
- callback();
- },
- final(callback) {
- process.send(protocol.message.final());
- callback();
- }
- });
- compileStream = project({
- error(error) { process.send(protocol.message.reporter.error(error)); },
- finish(results) { process.send(protocol.message.reporter.finish(results)); }
- });
- compileStream.on("error", error => {
- process.send(protocol.message.error(error));
- });
- outputStream.on("error", () => {
- /* do nothing */
- });
- inputStream.pipe(compileStream).pipe(outputStream);
- break;
- }
- case "write": {
- const file = protocol.vinylFromJson(message.params);
- if (!file.isBuffer()) file.contents = fs.readFileSync(file.path);
- inputStream.push(file);
- break;
- }
- case "final": {
- inputStream.push(null);
- break;
- }
- }
- }
- catch (e) {
- process.send(protocol.message.error(e));
- }
-});
\ No newline at end of file
diff --git a/scripts/build/gulp.js b/scripts/build/gulp.js
deleted file mode 100644
index 40f27b9526..0000000000
--- a/scripts/build/gulp.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// @ts-check
-/**
- * @typedef {import("gulp").Gulp} Gulp
- * @typedef {import("gulp-help").GulpHelp} GulpHelp
- * @typedef {GulpHelp & { Gulp: new () => Gulp }} DotGulpModule
- * @type {DotGulpModule}
- */
-module.exports = require("gulp-help")(require("gulp"));
\ No newline at end of file
diff --git a/scripts/build/lib.js b/scripts/build/lib.js
deleted file mode 100644
index f8dac7529e..0000000000
--- a/scripts/build/lib.js
+++ /dev/null
@@ -1,30 +0,0 @@
-// @ts-check
-const readJson = require("./readJson");
-const path = require("path");
-const gulp = require("./gulp");
-const newer = require("gulp-newer");
-const concat = require("gulp-concat");
-const merge2 = require("merge2");
-
-/** @type {{ libs: string[], paths?: Record, sources?: Record }} */
-const libraries = readJson("./src/lib/libs.json");
-const libs = libraries.libs.map(lib => {
- const relativeSources = ["header.d.ts"].concat(libraries.sources && libraries.sources[lib] || [lib + ".d.ts"]);
- const relativeTarget = libraries.paths && libraries.paths[lib] || ("lib." + lib + ".d.ts");
- const sources = relativeSources.map(s => path.posix.join("src/lib", s));
- const target = `built/local/${relativeTarget}`;
- return { target, relativeTarget, sources };
-});
-exports.libraryTargets = libs.map(lib => lib.target);
-
-/**
- * @param {string[]} prepends
- */
-function generateLibs(prepends) {
- return merge2(libs.map(({ sources, target, relativeTarget }) =>
- gulp.src(prepends.concat(sources))
- .pipe(newer(target))
- .pipe(concat(relativeTarget, { newLine: "\n\n" }))
- .pipe(gulp.dest("built/local"))));
-}
-exports.generateLibs = generateLibs;
\ No newline at end of file
diff --git a/scripts/build/mkdirp.js b/scripts/build/mkdirp.js
deleted file mode 100644
index 8a918a387d..0000000000
--- a/scripts/build/mkdirp.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// @ts-check
-const mkdirp = require("mkdirp");
-
-module.exports = exports = mkdirpAsync;
-
-/**
- * @param {string} dir
- * @param {mkdirp.Mode | mkdirp.Options} [opts]
- */
-function mkdirpAsync(dir, opts) {
- return new Promise((resolve, reject) => mkdirp(dir, opts, (err, made) => err ? reject(err) : resolve(made)));
-}
-
-exports.sync = mkdirp.sync;
\ No newline at end of file
diff --git a/scripts/build/needsUpdate.js b/scripts/build/needsUpdate.js
deleted file mode 100644
index 436003eab8..0000000000
--- a/scripts/build/needsUpdate.js
+++ /dev/null
@@ -1,72 +0,0 @@
-// @ts-check
-const fs = require("fs");
-
-module.exports = needsUpdate;
-
-/**
- * @param {string | string[]} source
- * @param {string | string[]} dest
- * @returns {boolean}
- */
-function needsUpdate(source, dest) {
- if (typeof source === "string" && typeof dest === "string") {
- if (fs.existsSync(dest)) {
- const {mtime: outTime} = fs.statSync(dest);
- const {mtime: inTime} = fs.statSync(source);
- if (+inTime <= +outTime) {
- return false;
- }
- }
- }
- else if (typeof source === "string" && typeof dest !== "string") {
- const {mtime: inTime} = fs.statSync(source);
- for (const filepath of dest) {
- if (fs.existsSync(filepath)) {
- const {mtime: outTime} = fs.statSync(filepath);
- if (+inTime > +outTime) {
- return true;
- }
- }
- else {
- return true;
- }
- }
- return false;
- }
- else if (typeof source !== "string" && typeof dest === "string") {
- if (fs.existsSync(dest)) {
- const {mtime: outTime} = fs.statSync(dest);
- for (const filepath of source) {
- if (fs.existsSync(filepath)) {
- const {mtime: inTime} = fs.statSync(filepath);
- if (+inTime > +outTime) {
- return true;
- }
- }
- else {
- return true;
- }
- }
- return false;
- }
- }
- else if (typeof source !== "string" && typeof dest !== "string") {
- for (let i = 0; i < source.length; i++) {
- if (!dest[i]) {
- continue;
- }
- if (fs.existsSync(dest[i])) {
- const {mtime: outTime} = fs.statSync(dest[i]);
- const {mtime: inTime} = fs.statSync(source[i]);
- if (+inTime > +outTime) {
- return true;
- }
- }
- else {
- return true;
- }
- }
- return false;
- }
- return true;
-}
\ No newline at end of file
diff --git a/scripts/build/options.js b/scripts/build/options.js
index ba1b669188..b7233ba70b 100644
--- a/scripts/build/options.js
+++ b/scripts/build/options.js
@@ -4,7 +4,7 @@ const os = require("os");
/** @type {CommandLineOptions} */
module.exports = minimist(process.argv.slice(2), {
- boolean: ["debug", "dirty", "inspect", "light", "colors", "lint", "lkg", "soft", "fix", "failed", "keepFailed"],
+ boolean: ["debug", "dirty", "inspect", "light", "colors", "lint", "lkg", "soft", "fix", "failed", "keepFailed", "force", "built"],
string: ["browser", "tests", "host", "reporter", "stackTraceLimit", "timeout"],
alias: {
"b": "browser",
@@ -15,7 +15,7 @@ module.exports = minimist(process.argv.slice(2), {
"r": "reporter",
"c": "colors", "color": "colors",
"w": "workers",
- "f": "fix",
+ "f": "fix"
},
default: {
soft: false,
@@ -35,10 +35,15 @@ module.exports = minimist(process.argv.slice(2), {
failed: false,
keepFailed: false,
lkg: true,
- dirty: false
+ dirty: false,
+ built: false
}
});
+if (module.exports.built) {
+ module.exports.lkg = false;
+}
+
/**
* @typedef TypedOptions
* @property {boolean} debug
@@ -48,6 +53,7 @@ module.exports = minimist(process.argv.slice(2), {
* @property {boolean} colors
* @property {boolean} lint
* @property {boolean} lkg
+ * @property {boolean} built
* @property {boolean} soft
* @property {boolean} fix
* @property {string} browser
diff --git a/scripts/build/prepend.js b/scripts/build/prepend.js
index 6e7b794e79..51c8aa84a8 100644
--- a/scripts/build/prepend.js
+++ b/scripts/build/prepend.js
@@ -1,20 +1,18 @@
// @ts-check
const stream = require("stream");
-const Vinyl = require("./vinyl");
+const Vinyl = require("vinyl");
const ts = require("../../lib/typescript");
const fs = require("fs");
const { base64VLQFormatEncode } = require("./sourcemaps");
-module.exports = exports = prepend;
-
/**
- * @param {string | ((file: Vinyl) => string)} data
+ * @param {string | ((file: import("vinyl")) => string)} data
*/
function prepend(data) {
return new stream.Transform({
objectMode: true,
/**
- * @param {string | Buffer | Vinyl} input
+ * @param {string | Buffer | import("vinyl")} input
* @param {(error: Error, data?: any) => void} cb
*/
transform(input, _, cb) {
@@ -56,11 +54,11 @@ function prepend(data) {
exports.prepend = prepend;
/**
- * @param {string | ((file: Vinyl) => string)} file
+ * @param {string | ((file: import("vinyl")) => string)} file
*/
function prependFile(file) {
const data = typeof file === "string" ? fs.readFileSync(file, "utf8") :
vinyl => fs.readFileSync(file(vinyl), "utf8");
return prepend(data)
}
-exports.file = prependFile;
\ No newline at end of file
+exports.prependFile = prependFile;
\ No newline at end of file
diff --git a/scripts/build/project.js b/scripts/build/project.js
deleted file mode 100644
index fb193907bb..0000000000
--- a/scripts/build/project.js
+++ /dev/null
@@ -1,1067 +0,0 @@
-// @ts-check
-const path = require("path");
-const fs = require("fs");
-const gulp = require("./gulp");
-const gulpif = require("gulp-if");
-const log = require("fancy-log"); // was `require("gulp-util").log (see https://github.com/gulpjs/gulp-util)
-const chalk = require("./chalk");
-const sourcemaps = require("gulp-sourcemaps");
-const merge2 = require("merge2");
-const tsc = require("gulp-typescript");
-const tsc_oop = require("./gulp-typescript-oop");
-const upToDate = require("./upToDate");
-const ts = require("../../lib/typescript");
-const del = require("del");
-const needsUpdate = require("./needsUpdate");
-const mkdirp = require("./mkdirp");
-const { reportDiagnostics } = require("./diagnostics");
-const { CountdownEvent, ManualResetEvent, Semaphore } = require("prex");
-
-const workStartedEvent = new ManualResetEvent();
-const countdown = new CountdownEvent(0);
-
-// internal `Gulp` instance for compilation artifacts.
-const compilationGulp = new gulp.Gulp();
-
-/** @type {Map} */
-const projectGraphCache = new Map();
-
-/** @type {Map} */
-const typescriptAliasMap = new Map();
-
-// TODO: allow concurrent outer builds to be run in parallel
-const sem = new Semaphore(1);
-
-/**
- * @param {string|string[]} taskName
- * @param {() => any} [cb]
- */
-function start(taskName, cb) {
- return sem.wait().then(() => new Promise((resolve, reject) => {
- compilationGulp.start(taskName, err => {
- if (err) {
- reject(err);
- }
- else if (cb) {
- try {
- resolve(cb());
- }
- catch (e) {
- reject(err);
- }
- }
- else {
- resolve();
- }
- });
- })).then(() => {
- sem.release()
- }, e => {
- sem.release();
- throw e;
- });
-}
-
-/**
- * Defines a gulp orchestration for a TypeScript project, returning a callback that can be used to trigger compilation.
- * @param {string} projectSpec The path to a tsconfig.json file or its containing directory.
- * @param {CompileOptions} [options] Project compilation options.
- * @returns {() => Promise}
- */
-function createCompiler(projectSpec, options) {
- const resolvedOptions = resolveCompileOptions(options);
- const resolvedProjectSpec = resolveProjectSpec(projectSpec, resolvedOptions.paths, /*referrer*/ undefined);
- const projectGraph = getOrCreateProjectGraph(resolvedProjectSpec, resolvedOptions.paths);
- projectGraph.isRoot = true;
- const taskName = compileTaskName(ensureCompileTask(projectGraph, resolvedOptions), resolvedOptions.typescript);
- return () => start(taskName);
-}
-exports.createCompiler = createCompiler;
-
-/**
- * Defines and executes a gulp orchestration for a TypeScript project.
- * @param {string} projectSpec The path to a tsconfig.json file or its containing directory.
- * @param {CompileOptions} [options] Project compilation options.
- * @returns {Promise}
- *
- * @typedef CompileOptions
- * @property {string} [cwd] The path to use for the current working directory. Defaults to `process.cwd()`.
- * @property {string} [base] The path to use as the base for relative paths. Defaults to `cwd`.
- * @property {string} [typescript] A module specifier or path (relative to gulpfile.js) to the version of TypeScript to use.
- * @property {Hook} [js] Pipeline hook for .js file outputs.
- * @property {Hook} [dts] Pipeline hook for .d.ts file outputs.
- * @property {boolean} [verbose] Indicates whether verbose logging is enabled.
- * @property {boolean} [force] Force recompilation (no up-to-date check).
- * @property {boolean} [inProcess] Indicates whether to run gulp-typescript in-process or out-of-process (default).
- *
- * @typedef {(stream: NodeJS.ReadableStream) => NodeJS.ReadWriteStream} Hook
- */
-function compile(projectSpec, options) {
- const compiler = createCompiler(projectSpec, options);
- return compiler();
-}
-exports.compile = compile;
-
-/**
- * Defines a gulp orchestration to clean the outputs of a TypeScript project, returning a callback that can be used to trigger compilation.
- * @param {string} projectSpec The path to a tsconfig.json file or its containing directory.
- * @param {PathOptions} [options] Project clean options.
- */
-function createCleaner(projectSpec, options) {
- const paths = resolvePathOptions(options);
- const resolvedProjectSpec = resolveProjectSpec(projectSpec, paths, /*referrer*/ undefined);
- const projectGraph = getOrCreateProjectGraph(resolvedProjectSpec, paths);
- projectGraph.isRoot = true;
- const taskName = cleanTaskName(ensureCleanTask(projectGraph));
- return () => start(taskName);
-}
-exports.createCleaner = createCleaner;
-
-/**
- * Defines and executes a gulp orchestration to clean the outputs of a TypeScript project.
- * @param {string} projectSpec The path to a tsconfig.json file or its containing directory.
- * @param {PathOptions} [options] Project clean options.
- */
-function clean(projectSpec, options) {
- const cleaner = createCleaner(projectSpec, options);
- return cleaner();
-}
-exports.clean = clean;
-
-/**
- * Defines a watcher to execute a gulp orchestration to recompile a TypeScript project.
- * @param {string} projectSpec
- * @param {WatchCallback | string[] | CompileOptions} [options]
- * @param {WatchCallback | string[]} [tasks]
- * @param {WatchCallback} [callback]
- */
-function watch(projectSpec, options, tasks, callback) {
- if (typeof tasks === "function") callback = tasks, tasks = /**@type {string[] | undefined}*/(undefined);
- if (typeof options === "function") callback = options, tasks = /**@type {string[] | undefined}*/(undefined), options = /**@type {CompileOptions | undefined}*/(undefined);
- if (Array.isArray(options)) tasks = options, options = /**@type {CompileOptions | undefined}*/(undefined);
- const resolvedOptions = resolveCompileOptions(options);
- resolvedOptions.watch = true;
- const resolvedProjectSpec = resolveProjectSpec(projectSpec, resolvedOptions.paths, /*referrer*/ undefined);
- const projectGraph = getOrCreateProjectGraph(resolvedProjectSpec, resolvedOptions.paths);
- projectGraph.isRoot = true;
- ensureWatcher(projectGraph, resolvedOptions, tasks, callback);
-}
-exports.watch = watch;
-
-/**
- * Adds a named alias for a TypeScript language service path
- * @param {string} alias An alias for a TypeScript version.
- * @param {string} typescript An alias or module specifier for a TypeScript version.
- * @param {PathOptions} [options] Options used to resolve the path to `typescript`.
- */
-function addTypeScript(alias, typescript, options) {
- const paths = resolvePathOptions(options);
- typescriptAliasMap.set(alias, { typescript, alias, paths });
-}
-exports.addTypeScript = addTypeScript;
-
-/**
- * Flattens a project with project references into a single project.
- * @param {string} projectSpec The path to a tsconfig.json file or its containing directory.
- * @param {string} flattenedProjectSpec The output path for the flattened tsconfig.json file.
- * @param {FlattenOptions} [options] Options used to flatten a project hierarchy.
- *
- * @typedef FlattenOptions
- * @property {string} [cwd] The path to use for the current working directory. Defaults to `process.cwd()`.
- * @property {CompilerOptions} [compilerOptions] Compiler option overrides.
- * @property {boolean} [force] Forces creation of the output project.
- * @property {string[]} [exclude] Files to exclude (relative to `cwd`)
- */
-function flatten(projectSpec, flattenedProjectSpec, options = {}) {
- const paths = resolvePathOptions(options);
- const files = [];
- const resolvedOutputSpec = path.resolve(paths.cwd, flattenedProjectSpec);
- const resolvedOutputDirectory = path.dirname(resolvedOutputSpec);
- const resolvedProjectSpec = resolveProjectSpec(projectSpec, paths, /*referrer*/ undefined);
- const projectGraph = getOrCreateProjectGraph(resolvedProjectSpec, paths);
- const skipProjects = /**@type {Set}*/(new Set());
- const skipFiles = new Set(options && options.exclude && options.exclude.map(file => path.resolve(paths.cwd, file)));
- recur(projectGraph);
-
- if (options.force || needsUpdate(files, resolvedOutputSpec)) {
- const config = {
- extends: normalizeSlashes(path.relative(resolvedOutputDirectory, resolvedProjectSpec)),
- compilerOptions: options.compilerOptions || {},
- files: files.map(file => normalizeSlashes(path.relative(resolvedOutputDirectory, file)))
- };
- mkdirp.sync(resolvedOutputDirectory);
- fs.writeFileSync(resolvedOutputSpec, JSON.stringify(config, undefined, 2), "utf8");
- }
-
- /**
- * @param {ProjectGraph} projectGraph
- */
- function recur(projectGraph) {
- if (skipProjects.has(projectGraph)) return;
- skipProjects.add(projectGraph);
- for (const ref of projectGraph.references) {
- recur(ref.target);
- }
- for (let file of projectGraph.project.fileNames) {
- file = path.resolve(projectGraph.projectDirectory, file);
- if (skipFiles.has(file)) continue;
- skipFiles.add(file);
- files.push(file);
- }
- }
-}
-exports.flatten = flatten;
-
-/**
- * Returns a Promise that resolves when all pending build tasks have completed
- * @param {import("prex").CancellationToken} [token]
- */
-function waitForWorkToComplete(token) {
- return countdown.wait(token);
-}
-exports.waitForWorkToComplete = waitForWorkToComplete;
-
-/**
- * Returns a Promise that resolves when all pending build tasks have completed
- * @param {import("prex").CancellationToken} [token]
- */
-function waitForWorkToStart(token) {
- return workStartedEvent.wait(token);
-}
-exports.waitForWorkToStart = waitForWorkToStart;
-
-function getRemainingWork() {
- return countdown.remainingCount > 0;
-}
-exports.hasRemainingWork = getRemainingWork;
-
-/**
- * Resolve a TypeScript specifier into a fully-qualified module specifier and any requisite dependencies.
- * @param {string} typescript An unresolved module specifier to a TypeScript version.
- * @param {ResolvedPathOptions} paths Paths used to resolve `typescript`.
- * @returns {ResolvedTypeScript}
- *
- * @typedef {string & {_isResolvedTypeScript: never}} ResolvedTypeScriptSpec
- *
- * @typedef ResolvedTypeScript
- * @property {ResolvedTypeScriptSpec} typescript
- * @property {string} [alias]
- */
-function resolveTypeScript(typescript = "default", paths) {
- let alias;
- while (typescriptAliasMap.has(typescript)) {
- ({ typescript, alias, paths } = typescriptAliasMap.get(typescript));
- }
-
- if (typescript === "default") {
- typescript = require.resolve("../../lib/typescript");
- }
- else if (isPath(typescript)) {
- typescript = path.resolve(paths.cwd, typescript);
- }
-
- return { typescript: /**@type {ResolvedTypeScriptSpec}*/(normalizeSlashes(typescript)), alias };
-}
-
-/**
- * Gets a suffix to append to Gulp task names that vary by TypeScript version.
- * @param {ResolvedTypeScript} typescript A resolved module specifier to a TypeScript version.
- * @param {ResolvedPathOptions} paths Paths used to resolve a relative reference to `typescript`.
- */
-function getTaskNameSuffix(typescript, paths) {
- return typescript.typescript === resolveTypeScript("default", paths).typescript ? "" :
- typescript.alias ? `@${typescript.alias}` :
- isPath(typescript.typescript) ? `@${normalizeSlashes(path.relative(paths.base, typescript.typescript))}` :
- `@${typescript}`;
-}
-
-/** @type {ResolvedPathOptions} */
-const defaultPaths = (() => {
- const cwd = /**@type {AbsolutePath}*/(normalizeSlashes(process.cwd()));
- return { cwd, base: cwd };
-})();
-
-/**
- * @param {PathOptions | undefined} options Path options to resolve and normalize.
- * @returns {ResolvedPathOptions}
- *
- * @typedef PathOptions
- * @property {string} [cwd] The path to use for the current working directory. Defaults to `process.cwd()`.
- * @property {string} [base] The path to use as the base for relative paths. Defaults to `cwd`.
- *
- * @typedef ResolvedPathOptions
- * @property {AbsolutePath} cwd The path to use for the current working directory. Defaults to `process.cwd()`.
- * @property {AbsolutePath} base The path to use as the base for relative paths. Defaults to `cwd`.
- */
-function resolvePathOptions(options) {
- const cwd = options && options.cwd ? resolvePath(defaultPaths.cwd, options.cwd) : defaultPaths.cwd;
- const base = options && options.base ? resolvePath(cwd, options.base) : cwd;
- return cwd === defaultPaths.cwd && base === defaultPaths.base ? defaultPaths : { cwd, base };
-}
-
-/**
- * @param {CompileOptions} [options]
- * @returns {ResolvedCompileOptions}
- *
- * @typedef ResolvedCompileOptions
- * @property {ResolvedPathOptions} paths
- * @property {ResolvedTypeScript} typescript A resolved reference to a TypeScript implementation.
- * @property {Hook} [js] Pipeline hook for .js file outputs.
- * @property {Hook} [dts] Pipeline hook for .d.ts file outputs.
- * @property {boolean} [verbose] Indicates whether verbose logging is enabled.
- * @property {boolean} [force] Force recompilation (no up-to-date check).
- * @property {boolean} [inProcess] Indicates whether to run gulp-typescript in-process or out-of-process (default).
- * @property {boolean} [watch] Indicates the project was created in watch mode
- */
-function resolveCompileOptions(options = {}) {
- const paths = resolvePathOptions(options);
- const typescript = resolveTypeScript(options.typescript, paths);
- return {
- paths,
- typescript,
- js: options.js,
- dts: options.dts,
- verbose: options.verbose || false,
- force: options.force || false,
- inProcess: options.inProcess || false
- };
-}
-
-/**
- * @param {ResolvedCompileOptions} left
- * @param {ResolvedCompileOptions} right
- * @returns {ResolvedCompileOptions}
- */
-function mergeCompileOptions(left, right) {
- if (left.typescript.typescript !== right.typescript.typescript) throw new Error("Cannot merge project options targeting different TypeScript packages");
- if (tryReuseCompileOptions(left, right)) return left;
- return {
- paths: left.paths,
- typescript: left.typescript,
- js: right.js || left.js,
- dts: right.dts || left.dts,
- verbose: right.verbose || left.verbose,
- force: right.force || left.force,
- inProcess: right.inProcess || left.inProcess,
- watch: right.watch || left.watch
- };
-}
-
-/**
- * @param {ResolvedCompileOptions} left
- * @param {ResolvedCompileOptions} right
- */
-function tryReuseCompileOptions(left, right) {
- return left === right
- || left.js === (right.js || left.js)
- && left.dts === (right.dts || left.dts)
- && !left.verbose === !(right.verbose || left.verbose)
- && !left.force === !(right.force || left.force)
- && !left.inProcess === !(right.inProcess || left.inProcess);
-}
-
-/**
- * @param {ResolvedProjectSpec} projectSpec
- * @param {ResolvedPathOptions} paths
- * @returns {UnqualifiedProjectName}
- *
- * @typedef {string & {_isUnqualifiedProjectName:never}} UnqualifiedProjectName
- */
-function getUnqualifiedProjectName(projectSpec, paths) {
- let projectName = path.relative(paths.base, projectSpec);
- if (path.basename(projectName) === "tsconfig.json") projectName = path.dirname(projectName);
- return /**@type {UnqualifiedProjectName}*/(normalizeSlashes(projectName));
-}
-
-/**
- * @param {UnqualifiedProjectName} projectName
- * @param {ResolvedPathOptions} paths
- * @param {ResolvedTypeScript} typescript
- * @returns {QualifiedProjectName}
- *
- * @typedef {string & {_isQualifiedProjectName:never}} QualifiedProjectName
- */
-function getQualifiedProjectName(projectName, paths, typescript) {
- return /**@type {QualifiedProjectName}*/(projectName + getTaskNameSuffix(typescript, paths));
-}
-
-/**
- * @typedef {import("../../lib/typescript").ParseConfigFileHost} ParseConfigFileHost
- * @type {ParseConfigFileHost}
- */
-const parseConfigFileHost = {
- useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
- fileExists: fileName => ts.sys.fileExists(fileName),
- readFile: fileName => ts.sys.readFile(fileName),
- getCurrentDirectory: () => process.cwd(),
- readDirectory: (rootDir, extensions, exclude, include, depth) => ts.sys.readDirectory(rootDir, extensions, exclude, include, depth),
- onUnRecoverableConfigFileDiagnostic: diagnostic => reportDiagnostics([diagnostic])
-};
-
-/**
- * @param {AbsolutePath} [cwd]
- * @returns {ParseConfigFileHost}
- */
-function getParseConfigFileHost(cwd) {
- if (!cwd || cwd === defaultPaths.cwd) return parseConfigFileHost;
- return {
- useCaseSensitiveFileNames: parseConfigFileHost.useCaseSensitiveFileNames,
- fileExists: parseConfigFileHost.fileExists,
- readFile: parseConfigFileHost.readFile,
- getCurrentDirectory: () => cwd,
- readDirectory: parseConfigFileHost.readDirectory,
- onUnRecoverableConfigFileDiagnostic: diagnostic => reportDiagnostics([diagnostic], { cwd })
- };
-}
-
-/**
- * @param {ResolvedProjectSpec} projectSpec
- * @param {ResolvedPathOptions} paths
- * @returns {ProjectGraph}
- *
- * @typedef ProjectGraph
- * @property {ResolvedPathOptions} paths
- * @property {ResolvedProjectSpec} projectSpec The fully qualified path to the tsconfig.json of the project
- * @property {UnqualifiedProjectName} projectName The relative project name, excluding any TypeScript suffix.
- * @property {AbsolutePath} projectDirectory The fully qualified path to the project directory.
- * @property {ParsedCommandLine} project The parsed tsconfig.json file.
- * @property {ProjectGraphReference[]} references An array of project references.
- * @property {Set} referrers An array of referring projects.
- * @property {Set} inputs A set of compilation inputs.
- * @property {Set} outputs A set of compilation outputs.
- * @property {Map} configurations TypeScript-specific configurations for the project.
- * @property {boolean} cleanTaskCreated A value indicating whether a `clean:` task has been created for this project (not dependent on TypeScript version).
- * @property {boolean} watcherCreated A value indicating whether a watcher has been created for this project.
- * @property {boolean} isRoot The project graph is a root project reference.
- * @property {Set} [allWatchers] Tasks to execute when the compilation has completed after being triggered by a watcher.
- *
- * @typedef ProjectGraphReference
- * @property {ProjectGraph} source The referring project.
- * @property {ProjectGraph} target The referenced project.
- */
-function getOrCreateProjectGraph(projectSpec, paths) {
- let projectGraph = projectGraphCache.get(projectSpec);
- if (!projectGraph) {
- const project = parseProject(projectSpec, paths);
- const projectDirectory = parentDirectory(projectSpec);
- projectGraph = {
- paths,
- projectSpec,
- projectName: getUnqualifiedProjectName(projectSpec, paths),
- projectDirectory,
- project,
- references: [],
- referrers: new Set(),
- inputs: new Set(project.fileNames.map(file => resolvePath(projectDirectory, file))),
- outputs: new Set(ts.getAllProjectOutputs(project).map(file => resolvePath(projectDirectory, file))),
- configurations: new Map(),
- cleanTaskCreated: false,
- watcherCreated: false,
- isRoot: false
- };
- projectGraphCache.set(projectSpec, projectGraph);
- if (project.projectReferences) {
- for (const projectReference of project.projectReferences) {
- const resolvedProjectSpec = resolveProjectSpec(projectReference.path, paths, projectGraph);
- const referencedProject = getOrCreateProjectGraph(resolvedProjectSpec, paths);
- const reference = { source: projectGraph, target: referencedProject };
- projectGraph.references.push(reference);
- referencedProject.referrers.add(projectGraph);
- }
- }
- }
- return projectGraph;
-}
-
-/**
- * @param {ResolvedPathOptions} paths
- */
-function createParseProject(paths) {
- /**
- * @param {string} configFilePath
- */
- function getProject(configFilePath) {
- const projectSpec = resolveProjectSpec(configFilePath, paths, /*referrer*/ undefined);
- const projectGraph = getOrCreateProjectGraph(projectSpec, defaultPaths);
- return projectGraph && projectGraph.project;
- }
- return getProject;
-}
-
-/**
- * @param {ProjectGraph} projectGraph
- * @param {ParsedCommandLine} parsedProject
- */
-function updateProjectGraph(projectGraph, parsedProject) {
- projectGraph.project = parsedProject;
- projectGraph.inputs = new Set(projectGraph.project.fileNames.map(file => resolvePath(projectGraph.projectDirectory, file)));
- projectGraph.outputs = new Set(ts.getAllProjectOutputs(projectGraph.project).map(file => resolvePath(projectGraph.projectDirectory, file)));
-
- // Update project references.
- const oldReferences = new Set(projectGraph.references.map(ref => ref.target));
- projectGraph.references = [];
- if (projectGraph.project.projectReferences) {
- for (const projectReference of projectGraph.project.projectReferences) {
- const resolvedProjectSpec = resolveProjectSpec(projectReference.path, projectGraph.paths, projectGraph);
- const referencedProject = getOrCreateProjectGraph(resolvedProjectSpec, projectGraph.paths);
- const reference = { source: projectGraph, target: referencedProject };
- projectGraph.references.push(reference);
- referencedProject.referrers.add(projectGraph);
- oldReferences.delete(referencedProject);
- }
- }
-
- // Remove project references that have been removed from the project
- for (const referencedProject of oldReferences) {
- referencedProject.referrers.delete(projectGraph);
- // If there are no more references to this project and the project was not directly requested,
- // remove it from the cache.
- if (referencedProject.referrers.size === 0 && !referencedProject.isRoot) {
- projectGraphCache.delete(referencedProject.projectSpec);
- }
- }
-}
-
-/**
- * @param {ResolvedProjectSpec} projectSpec
- * @param {ResolvedPathOptions} paths
- */
-function parseProject(projectSpec, paths) {
- return ts.getParsedCommandLineOfConfigFile(projectSpec, {}, getParseConfigFileHost(paths.cwd));
-}
-
-/**
- * @param {ProjectGraph} projectGraph
- * @param {ResolvedCompileOptions} resolvedOptions
- * @returns {ProjectGraphConfiguration}
- *
- * @typedef ProjectGraphConfiguration
- * @property {QualifiedProjectName} projectName
- * @property {ResolvedCompileOptions} resolvedOptions
- * @property {boolean} compileTaskCreated A value indicating whether a `compile:` task has been created for this project.
- * @property {Set} [watchers] Tasks to execute when the compilation has completed after being triggered by a watcher.
- */
-function getOrCreateProjectGraphConfiguration(projectGraph, resolvedOptions) {
- let projectGraphConfig = projectGraph.configurations.get(resolvedOptions.typescript.typescript);
- if (!projectGraphConfig) {
- projectGraphConfig = {
- projectName: getQualifiedProjectName(projectGraph.projectName, resolvedOptions.paths, resolvedOptions.typescript),
- resolvedOptions,
- compileTaskCreated: false
- };
- projectGraph.configurations.set(resolvedOptions.typescript.typescript, projectGraphConfig);
- }
- return projectGraphConfig;
-}
-
-/**
- * Resolves a series of path steps as a normalized, canonical, and absolute path.
- * @param {AbsolutePath} basePath
- * @param {...string} paths
- * @returns {AbsolutePath}
- *
- * @typedef {string & {_isResolvedPath:never}} AbsolutePath
- */
-function resolvePath(basePath, ...paths) {
- return /**@type {AbsolutePath}*/(normalizeSlashes(path.resolve(basePath, ...paths)));
-}
-
-/**
- * @param {AbsolutePath} from
- * @param {AbsolutePath} to
- * @returns {Path}
- *
- * @typedef {string & {_isRelativePath:never}} RelativePath
- * @typedef {RelativePath | AbsolutePath} Path
- */
-function relativePath(from, to) {
- let relativePath = normalizeSlashes(path.relative(from, to));
- if (!relativePath) relativePath = ".";
- if (path.isAbsolute(relativePath)) return /**@type {AbsolutePath}*/(relativePath);
- if (relativePath.charAt(0) !== ".") relativePath = "./" + relativePath;
- return /**@type {RelativePath}*/(relativePath);
-}
-
-/**
- * @param {AbsolutePath} file
- * @returns {AbsolutePath}
- */
-function parentDirectory(file) {
- const dirname = path.dirname(file);
- if (!dirname || dirname === file) return file;
- return /**@type {AbsolutePath}*/(normalizeSlashes(dirname));
-}
-
-/**
- * @param {string} projectSpec
- * @param {ResolvedPathOptions} paths
- * @param {ProjectGraph | undefined} referrer
- * @returns {ResolvedProjectSpec}
- *
- * @typedef {AbsolutePath & {_isResolvedProjectSpec: never}} ResolvedProjectSpec
- */
-function resolveProjectSpec(projectSpec, paths, referrer) {
- let projectPath = resolvePath(paths.cwd, referrer && referrer.projectDirectory || "", projectSpec);
- if (!ts.sys.fileExists(projectPath)) projectPath = resolvePath(paths.cwd, projectPath, "tsconfig.json");
- return /**@type {ResolvedProjectSpec}*/(normalizeSlashes(projectPath));
-}
-
-/**
- * @param {ProjectGraph} projectGraph
- * @param {ResolvedPathOptions} paths
- */
-function resolveDestPath(projectGraph, paths) {
- /** @type {AbsolutePath} */
- let destPath = projectGraph.projectDirectory;
- if (projectGraph.project.options.outDir) {
- destPath = resolvePath(paths.cwd, destPath, projectGraph.project.options.outDir);
- }
- else if (projectGraph.project.options.outFile || projectGraph.project.options.out) {
- destPath = parentDirectory(resolvePath(paths.cwd, destPath, projectGraph.project.options.outFile || projectGraph.project.options.out));
- }
- return relativePath(paths.base, destPath);
-}
-
-/**
- * @param {ProjectGraph} projectGraph
- * @param {ResolvedCompileOptions} options
- */
-function ensureCompileTask(projectGraph, options) {
- const projectGraphConfig = getOrCreateProjectGraphConfiguration(projectGraph, options);
- projectGraphConfig.resolvedOptions = mergeCompileOptions(projectGraphConfig.resolvedOptions, options);
- const hasCompileTask = projectGraphConfig.compileTaskCreated;
- projectGraphConfig.compileTaskCreated = true;
- const deps = makeProjectReferenceCompileTasks(projectGraph, projectGraphConfig.resolvedOptions.typescript, projectGraphConfig.resolvedOptions.paths, projectGraphConfig.resolvedOptions.watch);
- if (!hasCompileTask) {
- compilationGulp.task(compileTaskName(projectGraph, projectGraphConfig.resolvedOptions.typescript), deps, () => {
- const destPath = resolveDestPath(projectGraph, projectGraphConfig.resolvedOptions.paths);
- const { sourceMap, inlineSourceMap, inlineSources = false, sourceRoot, declarationMap } = projectGraph.project.options;
- const configFilePath = projectGraph.project.options.configFilePath;
- const sourceMapPath = inlineSourceMap ? undefined : ".";
- const sourceMapOptions = { includeContent: inlineSources, sourceRoot, destPath };
- const project = projectGraphConfig.resolvedOptions.inProcess
- ? tsc.createProject(configFilePath, { typescript: require(projectGraphConfig.resolvedOptions.typescript.typescript) })
- : tsc_oop.createProject(configFilePath, {}, { typescript: projectGraphConfig.resolvedOptions.typescript.typescript });
- const stream = project.src()
- .pipe(gulpif(!projectGraphConfig.resolvedOptions.force, upToDate(projectGraph.project, { verbose: projectGraphConfig.resolvedOptions.verbose, parseProject: createParseProject(projectGraphConfig.resolvedOptions.paths) })))
- .pipe(gulpif(sourceMap || inlineSourceMap, sourcemaps.init()))
- .pipe(project());
- if (projectGraphConfig.resolvedOptions.watch) {
- stream.on("error", error => {
- if (error.message === "TypeScript: Compilation failed") {
- stream.emit("end");
- stream.js.emit("end");
- stream.dts.emit("end");
- }
- });
- }
-
- const additionalJsOutputs = projectGraphConfig.resolvedOptions.js ? projectGraphConfig.resolvedOptions.js(stream.js)
- .pipe(gulpif(sourceMap || inlineSourceMap, sourcemaps.write(sourceMapPath, sourceMapOptions))) : undefined;
- const additionalDtsOutputs = projectGraphConfig.resolvedOptions.dts ? projectGraphConfig.resolvedOptions.dts(stream.dts)
- .pipe(gulpif(declarationMap, sourcemaps.write(sourceMapPath, sourceMapOptions))) : undefined;
-
- const js = stream.js
- .pipe(gulpif(sourceMap || inlineSourceMap, sourcemaps.write(sourceMapPath, sourceMapOptions)));
- const dts = stream.dts
- .pipe(gulpif(declarationMap, sourcemaps.write(sourceMapPath, sourceMapOptions)));
- return merge2([js, dts, additionalJsOutputs, additionalDtsOutputs].filter(x => !!x))
- .pipe(gulp.dest(destPath));
- });
- }
- return projectGraph;
-}
-
-/**
- * @param {ProjectGraph} projectGraph
- * @param {ResolvedTypeScript} typescript
- * @param {ResolvedPathOptions} paths
- * @param {boolean} watch
- */
-function makeProjectReferenceCompileTasks(projectGraph, typescript, paths, watch) {
- return projectGraph.references.map(({target}) => compileTaskName(ensureCompileTask(target, { paths, typescript, watch }), typescript));
-}
-
-/**
- * @param {ProjectGraph} projectGraph
- */
-function ensureCleanTask(projectGraph) {
- if (!projectGraph.cleanTaskCreated) {
- const deps = makeProjectReferenceCleanTasks(projectGraph);
- compilationGulp.task(cleanTaskName(projectGraph), deps, () => {
- let outputs = ts.getAllProjectOutputs(projectGraph.project);
- if (!projectGraph.project.options.inlineSourceMap) {
- if (projectGraph.project.options.sourceMap) {
- outputs = outputs.concat(outputs.filter(file => /\.jsx?$/.test(file)).map(file => file + ".map"));
- }
- if (projectGraph.project.options.declarationMap) {
- outputs = outputs.concat(outputs.filter(file => /\.d.ts$/.test(file)).map(file => file + ".map"));
- }
- }
- return del(outputs);
- });
- projectGraph.cleanTaskCreated = true;
- }
- return projectGraph;
-}
-
-/**
- * @param {ProjectGraph} projectGraph
- */
-function makeProjectReferenceCleanTasks(projectGraph) {
- return projectGraph.references.map(({target}) => cleanTaskName(ensureCleanTask(target)));
-}
-
-/**
- * @param {ProjectGraph} projectGraph
- * @param {ResolvedCompileOptions} options
- * @param {string[]} [tasks]
- * @param {(err?: any) => void} [callback]
- *
- * @typedef Watcher
- * @property {string[]} [tasks]
- * @property {(err?: any) => void} [callback]
- *
- * @typedef WatcherRegistration
- * @property {() => void} end
- */
-function ensureWatcher(projectGraph, options, tasks, callback) {
- ensureCompileTask(projectGraph, options);
- if (!projectGraph.watcherCreated) {
- projectGraph.watcherCreated = true;
- makeProjectReferenceWatchers(projectGraph, options.typescript, options.paths);
- createWatcher(projectGraph, options, () => {
- for (const config of projectGraph.configurations.values()) {
- const taskName = compileTaskName(projectGraph, config.resolvedOptions.typescript);
- const task = compilationGulp.tasks[taskName];
- if (!task) continue;
- possiblyTriggerRecompilation(config, task);
- }
- });
- }
- if ((tasks && tasks.length) || callback) {
- const projectGraphConfig = getOrCreateProjectGraphConfiguration(projectGraph, options);
- if (!projectGraphConfig.watchers) projectGraphConfig.watchers = new Set();
- if (!projectGraph.allWatchers) projectGraph.allWatchers = new Set();
-
- /** @type {Watcher} */
- const watcher = { tasks, callback };
- projectGraphConfig.watchers.add(watcher);
- projectGraph.allWatchers.add(watcher);
-
- /** @type {WatcherRegistration} */
- const registration = {
- end() {
- projectGraphConfig.watchers.delete(watcher);
- projectGraph.allWatchers.delete(watcher);
- }
- };
- return registration;
- }
-}
-
-/**
- * @param {ProjectGraphConfiguration} config
- * @param {import("orchestrator").Task} task
- */
-function possiblyTriggerRecompilation(config, task) {
- // if any of the task's dependencies are still running, wait until they are complete.
- for (const dep of task.dep) {
- if (compilationGulp.tasks[dep].running) {
- setTimeout(possiblyTriggerRecompilation, 50, config, task);
- return;
- }
- }
-
- triggerRecompilation(task, config);
-}
-
-/**
- * @param {import("orchestrator").Task} task
- * @param {ProjectGraphConfiguration} config
- */
-function triggerRecompilation(task, config) {
- compilationGulp._resetTask(task);
- if (config.watchers && config.watchers.size) {
- start(task.name, () => {
- /** @type {Set} */
- const taskNames = new Set();
- /** @type {((err?: any) => void)[]} */
- const callbacks = [];
- for (const { tasks, callback } of config.watchers) {
- if (tasks) for (const task of tasks) taskNames.add(task);
- if (callback) callbacks.push(callback);
- }
- if (taskNames.size) {
- gulp.start([...taskNames], error => {
- for (const callback of callbacks) callback(error);
- });
- }
- else {
- for (const callback of callbacks) callback();
- }
- });
- }
- else {
- start(task.name);
- }
-}
-
-/**
- * @param {ProjectGraph} projectGraph
- * @param {ResolvedTypeScript} typescript
- * @param {ResolvedPathOptions} paths
- */
-function makeProjectReferenceWatchers(projectGraph, typescript, paths) {
- for (const { target } of projectGraph.references) {
- ensureWatcher(target, { paths, typescript });
- }
-}
-
-/**
- * @param {ProjectGraph} projectGraph
- * @param {ResolvedCompileOptions} options
- * @param {() => void} callback
- */
-function createWatcher(projectGraph, options, callback) {
- let projectRemoved = false;
- let patterns = collectWatcherPatterns(projectGraph.projectSpec, projectGraph.project, projectGraph);
- let watcher = /**@type {GulpWatcher}*/ (gulp.watch(patterns, { cwd: projectGraph.projectDirectory }, onWatchEvent));
-
- /**
- * @param {WatchEvent} event
- */
- function onWatchEvent(event) {
- const file = resolvePath(options.paths.cwd, event.path);
- if (file === projectGraph.projectSpec) {
- onProjectWatchEvent(event);
- }
- else {
- onInputOrOutputChanged(file);
- }
- }
-
- /**
- * @param {WatchEvent} event
- */
- function onProjectWatchEvent(event) {
- if (event.type === "renamed" || event.type === "deleted") {
- onProjectRenamedOrDeleted();
- }
- else {
- onProjectCreatedOrModified();
- }
- }
-
- function onProjectRenamedOrDeleted() {
- // stop listening for file changes and wait for the project to be created again
- projectRemoved = true;
- watcher.end();
- watcher = /**@type {GulpWatcher}*/ (gulp.watch([projectGraph.projectSpec], onWatchEvent));
- }
-
- function onProjectCreatedOrModified() {
- const newParsedProject = parseProject(projectGraph.projectSpec, options.paths);
- const newPatterns = collectWatcherPatterns(projectGraph.projectSpec, newParsedProject, projectGraph);
- if (projectRemoved || !sameValues(patterns, newPatterns)) {
- projectRemoved = false;
- watcher.end();
- updateProjectGraph(projectGraph, newParsedProject);
- // Ensure we catch up with any added projects
- for (const config of projectGraph.configurations.values()) {
- if (config.watchers) {
- makeProjectReferenceWatchers(projectGraph, config.resolvedOptions.typescript, config.resolvedOptions.paths);
- }
- }
- patterns = newPatterns;
- watcher = /**@type {GulpWatcher}*/ (gulp.watch(patterns, onWatchEvent));
- }
- onProjectInvalidated();
- }
-
- function onProjectInvalidated() {
- callback();
- }
-
- /**
- * @param {AbsolutePath} file
- */
- function onInputOrOutputChanged(file) {
- if (projectGraph.inputs.has(file) ||
- projectGraph.references.some(ref => ref.target.outputs.has(file))) {
- onProjectInvalidated();
- }
- }
-}
-
-/**
- * @param {ResolvedProjectSpec} projectSpec
- * @param {ParsedCommandLine} parsedProject
- * @param {ProjectGraph} projectGraph
- */
-function collectWatcherPatterns(projectSpec, parsedProject, projectGraph) {
- const configFileSpecs = parsedProject.configFileSpecs;
-
- // NOTE: we do not currently handle files from `/// ` tags
- const patterns = /**@type {string[]} */([]);
-
- // Add the project contents.
- if (configFileSpecs) {
- addIncludeSpecs(patterns, configFileSpecs.validatedIncludeSpecs);
- addExcludeSpecs(patterns, configFileSpecs.validatedExcludeSpecs);
- addIncludeSpecs(patterns, configFileSpecs.filesSpecs);
- }
- else {
- addWildcardDirectories(patterns, parsedProject.wildcardDirectories);
- addIncludeSpecs(patterns, parsedProject.fileNames);
- }
-
- // Add the project itself.
- addIncludeSpec(patterns, projectSpec);
-
- // TODO: Add the project base.
- // addExtendsSpec(patterns, project.raw && project.raw.extends);
-
- // Add project reference outputs.
- addProjectReferences(patterns, parsedProject.projectReferences);
-
- return patterns;
-
- /**
- * @param {string[]} patterns
- * @param {string | undefined} includeSpec
- */
- function addIncludeSpec(patterns, includeSpec) {
- if (!includeSpec) return;
- patterns.push(includeSpec);
- }
-
- /**
- * @param {string[]} patterns
- * @param {ReadonlyArray | undefined} includeSpecs
- */
- function addIncludeSpecs(patterns, includeSpecs) {
- if (!includeSpecs) return;
- for (const includeSpec of includeSpecs) {
- addIncludeSpec(patterns, includeSpec);
- }
- }
-
- /**
- * @param {string[]} patterns
- * @param {string | undefined} excludeSpec
- */
- function addExcludeSpec(patterns, excludeSpec) {
- if (!excludeSpec) return;
- patterns.push("!" + excludeSpec);
- }
-
- /**
- * @param {string[]} patterns
- * @param {ReadonlyArray | undefined} excludeSpecs
- */
- function addExcludeSpecs(patterns, excludeSpecs) {
- if (!excludeSpecs) return;
- for (const excludeSpec of excludeSpecs) {
- addExcludeSpec(patterns, excludeSpec);
- }
- }
-
- /**
- * @param {string[]} patterns
- * @param {ts.MapLike | undefined} wildcardDirectories
- */
- function addWildcardDirectories(patterns, wildcardDirectories) {
- if (!wildcardDirectories) return;
- for (const dirname of Object.keys(wildcardDirectories)) {
- const flags = wildcardDirectories[dirname];
- patterns.push(path.join(dirname, flags & ts.WatchDirectoryFlags.Recursive ? "**" : "", "*"));
- }
- }
-
- // TODO: Add the project base
- // /**
- // * @param {string[]} patterns
- // * @param {string | undefined} base
- // */
- // function addExtendsSpec(patterns, base) {
- // if (!base) return;
- // addIncludeSpec(patterns, base);
- // }
-
- /**
- * @param {string[]} patterns
- * @param {ReadonlyArray} projectReferences
- */
- function addProjectReferences(patterns, projectReferences) {
- if (!projectReferences) return;
- for (const projectReference of projectReferences) {
- const resolvedProjectSpec = resolveProjectSpec(projectReference.path, projectGraph.paths, projectGraph);
- const referencedProject = getOrCreateProjectGraph(resolvedProjectSpec, projectGraph.paths);
- for (const output of referencedProject.outputs) {
- patterns.push(output);
- }
- }
- }
-}
-
-/**
- * @param {ProjectGraph} projectGraph
- * @param {ResolvedTypeScript} typescript
- */
-function compileTaskName(projectGraph, typescript) {
- return `compile:${projectGraph.configurations.get(typescript.typescript).projectName}`;
-}
-
-/**
- * @param {ProjectGraph} projectGraph
- */
-function cleanTaskName(projectGraph) {
- return `clean:${projectGraph.projectName}`;
-}
-
-/**
- * @param {string} file
- */
-function normalizeSlashes(file) {
- return file.replace(/\\/g, "/");
-}
-
-/**
- * Determines whether a module specifier is a path
- * @param {string} moduleSpec
- */
-function isPath(moduleSpec) {
- return path.isAbsolute(moduleSpec) || /^\.\.?([\\/]|$)/.test(moduleSpec);
-}
-
-/**
- * @template T
- * @param {ReadonlyArray} left
- * @param {ReadonlyArray} right
- */
-function sameValues(left, right) {
- if (left === right) return true;
- if (left.length !== right.length) return false;
- for (let i = 0; i < left.length; i++) {
- if (left[i] !== right[i]) return false;
- }
- return true;
-}
-
-/**
- * @typedef {import("../../lib/typescript").ParsedCommandLine & { options: CompilerOptions, configFileSpecs?: ConfigFileSpecs }} ParsedCommandLine
- * @typedef {import("../../lib/typescript").CompilerOptions & { configFilePath?: string }} CompilerOptions
- * @typedef {import("../../lib/typescript").ProjectReference} ProjectReference
- * @typedef {import("gulp").WatchEvent} WatchEvent
- * @typedef {import("gulp").WatchCallback} WatchCallback
- * @typedef {NodeJS.EventEmitter & { end(): void, add(files: string | string[], done?: () => void): void, remove(file: string): void }} GulpWatcher
- *
- * @typedef ConfigFileSpecs
- * @property {ReadonlyArray | undefined} filesSpecs
- * @property {ReadonlyArray | undefined} referenceSpecs
- * @property {ReadonlyArray | undefined} validatedIncludeSpecs
- * @property {ReadonlyArray | undefined} validatedExcludeSpecs
- * @property {ts.MapLike} wildcardDirectories
- */
-void 0;
\ No newline at end of file
diff --git a/scripts/build/projects.js b/scripts/build/projects.js
new file mode 100644
index 0000000000..954f37af84
--- /dev/null
+++ b/scripts/build/projects.js
@@ -0,0 +1,60 @@
+// @ts-check
+const { exec, Debouncer } = require("./utils");
+
+class ProjectQueue {
+ /**
+ * @param {(projects: string[], lkg: boolean, force: boolean) => Promise} action
+ */
+ constructor(action) {
+ /** @type {{ lkg: boolean, force: boolean, projects?: string[], debouncer: Debouncer }[]} */
+ this._debouncers = [];
+ this._action = action;
+ }
+
+ /**
+ * @param {string} project
+ * @param {object} options
+ */
+ enqueue(project, { lkg = true, force = false } = {}) {
+ let entry = this._debouncers.find(entry => entry.lkg === lkg && entry.force === force);
+ if (!entry) {
+ const debouncer = new Debouncer(100, async () => {
+ const projects = entry.projects;
+ if (projects) {
+ entry.projects = undefined;
+ await this._action(projects, lkg, force);
+ }
+ });
+ this._debouncers.push(entry = { lkg, force, debouncer });
+ }
+ if (!entry.projects) entry.projects = [];
+ entry.projects.push(project);
+ return entry.debouncer.enqueue();
+ }
+}
+
+const projectBuilder = new ProjectQueue((projects, lkg, force) => exec(process.execPath, [lkg ? "./lib/tsc" : "./built/local/tsc", "-b", ...(force ? ["--force"] : []), ...projects], { hidePrompt: true }));
+
+/**
+ * @param {string} project
+ * @param {object} [options]
+ * @param {boolean} [options.lkg=true]
+ * @param {boolean} [options.force=false]
+ */
+exports.buildProject = (project, { lkg, force } = {}) => projectBuilder.enqueue(project, { lkg, force });
+
+const projectCleaner = new ProjectQueue((projects, lkg) => exec(process.execPath, [lkg ? "./lib/tsc" : "./built/local/tsc", "-b", "--clean", ...projects], { hidePrompt: true }));
+
+/**
+ * @param {string} project
+ */
+exports.cleanProject = (project) => projectCleaner.enqueue(project);
+
+const projectWatcher = new ProjectQueue((projects) => exec(process.execPath, ["./lib/tsc", "-b", "--watch", ...projects], { hidePrompt: true }));
+
+/**
+ * @param {string} project
+ * @param {object} [options]
+ * @param {boolean} [options.lkg=true]
+ */
+exports.watchProject = (project, { lkg } = {}) => projectWatcher.enqueue(project, { lkg });
diff --git a/scripts/build/readJson.js b/scripts/build/readJson.js
deleted file mode 100644
index c9e6341969..0000000000
--- a/scripts/build/readJson.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// @ts-check
-const ts = require("../../lib/typescript");
-const fs = require("fs");
-const { reportDiagnostics } = require("./diagnostics");
-
-module.exports = exports = readJson;
-
-/** @param {string} jsonPath */
-function readJson(jsonPath) {
- const jsonText = fs.readFileSync(jsonPath, "utf8");
- const result = ts.parseConfigFileTextToJson(jsonPath, jsonText);
- if (result.error) {
- reportDiagnostics([result.error]);
- throw new Error("An error occurred during parse.");
- }
- return result.config;
-}
\ No newline at end of file
diff --git a/scripts/build/replace.js b/scripts/build/replace.js
deleted file mode 100644
index 1287d08228..0000000000
--- a/scripts/build/replace.js
+++ /dev/null
@@ -1,12 +0,0 @@
-// @ts-check
-const insert = require("gulp-insert");
-
-/**
- * @param {string | RegExp} searchValue
- * @param {string | ((...args: string[]) => string)} replacer
- */
-function replace(searchValue, replacer) {
- return insert.transform(content => content.replace(searchValue, /**@type {string}*/(replacer)));
-}
-
-module.exports = replace;
\ No newline at end of file
diff --git a/scripts/build/rm.js b/scripts/build/rm.js
deleted file mode 100644
index 0922954b49..0000000000
--- a/scripts/build/rm.js
+++ /dev/null
@@ -1,84 +0,0 @@
-// @ts-check
-const { Duplex } = require("stream");
-const path = require("path");
-const Vinyl = require("vinyl");
-const del = require("del");
-
-module.exports = rm;
-
-/**
- * @param {string | ((file: File) => string) | Options} [dest]
- * @param {Options} [opts]
- */
-function rm(dest, opts) {
- if (dest && typeof dest === "object") opts = dest, dest = undefined;
- let failed = false;
-
- const cwd = path.resolve(opts && opts.cwd || process.cwd());
-
- /** @type {{ file: File, deleted: boolean, promise: Promise, cb: Function }[]} */
- const pending = [];
-
- const processDeleted = () => {
- if (failed) return;
- while (pending.length && pending[0].deleted) {
- const { file, cb } = pending.shift();
- duplex.push(file);
- cb();
- }
- };
-
- const duplex = new Duplex({
- objectMode: true,
- /**
- * @param {string|Buffer|File} file
- */
- write(file, _, cb) {
- if (failed) return;
- if (typeof file === "string" || Buffer.isBuffer(file)) return cb(new Error("Only Vinyl files are supported."));
- const basePath = typeof dest === "string" ? path.resolve(cwd, dest) :
- typeof dest === "function" ? path.resolve(cwd, dest(file)) :
- file.base;
- const filePath = path.resolve(basePath, file.relative);
- file.cwd = cwd;
- file.base = basePath;
- file.path = filePath;
- const entry = {
- file,
- deleted: false,
- cb,
- promise: del(file.path).then(() => {
- entry.deleted = true;
- processDeleted();
- }, err => {
- failed = true;
- pending.length = 0;
- cb(err);
- })
- };
- pending.push(entry);
- },
- final(cb) {
- processDeleted();
- if (pending.length) {
- Promise
- .all(pending.map(entry => entry.promise))
- .then(() => processDeleted())
- .then(() => cb(), cb);
- return;
- }
- cb();
- },
- read() {
- }
- });
- return duplex;
-}
-
-/**
- * @typedef {import("vinyl")} File
- *
- * @typedef Options
- * @property {string} [cwd]
- */
-void 0;
\ No newline at end of file
diff --git a/scripts/build/sourcemaps.js b/scripts/build/sourcemaps.js
index 66e488287b..2c85b897b0 100644
--- a/scripts/build/sourcemaps.js
+++ b/scripts/build/sourcemaps.js
@@ -1,12 +1,13 @@
// @ts-check
+///
+
const path = require("path");
-const Vinyl = require("./vinyl");
const convertMap = require("convert-source-map");
const applySourceMap = require("vinyl-sourcemaps-apply");
const through2 = require("through2");
/**
- * @param {Vinyl} input
+ * @param {import("vinyl")} input
* @param {string | Buffer} contents
* @param {string | RawSourceMap} [sourceMap]
*/
@@ -16,13 +17,13 @@ function replaceContents(input, contents, sourceMap) {
if (input.sourceMap) {
output.sourceMap = typeof input.sourceMap === "string" ? /**@type {RawSourceMap}*/(JSON.parse(input.sourceMap)) : input.sourceMap;
if (typeof sourceMap === "string") {
- sourceMap = /**@type {RawSourceMap}*/(JSON.parse(sourceMap));
+ sourceMap = /** @type {RawSourceMap} */(JSON.parse(sourceMap));
}
else if (sourceMap === undefined) {
const stringContents = typeof contents === "string" ? contents : contents.toString("utf8");
const newSourceMapConverter = convertMap.fromSource(stringContents);
if (newSourceMapConverter) {
- sourceMap = /**@type {RawSourceMap}*/(newSourceMapConverter.toObject());
+ sourceMap = /** @type {RawSourceMap} */(newSourceMapConverter.toObject());
output.contents = new Buffer(convertMap.removeMapFileComments(stringContents), "utf8");
}
}
@@ -31,7 +32,7 @@ function replaceContents(input, contents, sourceMap) {
const base = input.base || cwd;
const sourceRoot = output.sourceMap.sourceRoot;
makeAbsoluteSourceMap(cwd, base, output.sourceMap);
- makeAbsoluteSourceMap(cwd, base, sourceMap);
+ makeAbsoluteSourceMap(cwd, base, /** @type {RawSourceMap} */(sourceMap));
applySourceMap(output, sourceMap);
makeRelativeSourceMap(cwd, base, sourceRoot, output.sourceMap);
}
@@ -44,10 +45,12 @@ function replaceContents(input, contents, sourceMap) {
exports.replaceContents = replaceContents;
function removeSourceMaps() {
- return through2.obj((/**@type {Vinyl}*/file, _, cb) => {
- if (file.sourceMap && file.isBuffer()) {
+ return through2.obj((/**@type {import("vinyl")}*/file, _, cb) => {
+ if (file.isBuffer()) {
file.contents = Buffer.from(convertMap.removeMapFileComments(file.contents.toString("utf8")), "utf8");
- file.sourceMap = undefined;
+ if (file.sourceMap) {
+ file.sourceMap = undefined;
+ }
}
cb(null, file);
});
@@ -59,7 +62,7 @@ exports.removeSourceMaps = removeSourceMaps;
* @param {string | undefined} base
* @param {RawSourceMap} sourceMap
*
- * @typedef RawSourceMap
+ * @typedef {object} RawSourceMap
* @property {string} version
* @property {string} file
* @property {string} [sourceRoot]
diff --git a/scripts/build/tests.js b/scripts/build/tests.js
index 46c31ed891..36a9ea54cb 100644
--- a/scripts/build/tests.js
+++ b/scripts/build/tests.js
@@ -1,16 +1,16 @@
// @ts-check
-const gulp = require("./gulp");
+const gulp = require("gulp");
const del = require("del");
const fs = require("fs");
const os = require("os");
const path = require("path");
-const mkdirP = require("./mkdirp");
+const mkdirP = require("mkdirp");
+const log = require("fancy-log");
const cmdLineOptions = require("./options");
-const exec = require("./exec");
-const log = require("fancy-log"); // was `require("gulp-util").log (see https://github.com/gulpjs/gulp-util)
const { CancellationToken } = require("prex");
-const mochaJs = require.resolve("mocha/bin/_mocha");
+const { exec } = require("./utils");
+const mochaJs = require.resolve("mocha/bin/_mocha");
exports.localBaseline = "tests/baselines/local/";
exports.refBaseline = "tests/baselines/reference/";
exports.localRwcBaseline = "internal/baselines/rwc/local";
@@ -27,7 +27,6 @@ exports.localTest262Baseline = "internal/baselines/test262/local";
async function runConsoleTests(runJs, defaultReporter, runInParallel, watchMode, cancelToken = CancellationToken.none) {
let testTimeout = cmdLineOptions.timeout;
let tests = cmdLineOptions.tests;
- const lintFlag = cmdLineOptions.lint;
const debug = cmdLineOptions.debug;
const inspect = cmdLineOptions.inspect;
const runners = cmdLineOptions.runners;
@@ -117,9 +116,6 @@ async function runConsoleTests(runJs, defaultReporter, runInParallel, watchMode,
errorStatus = exitCode;
error = new Error(`Process exited with status code ${errorStatus}.`);
}
- else if (lintFlag) {
- await new Promise((resolve, reject) => gulp.start(["lint"], error => error ? reject(error) : resolve()));
- }
}
catch (e) {
errorStatus = undefined;
@@ -144,10 +140,10 @@ async function runConsoleTests(runJs, defaultReporter, runInParallel, watchMode,
}
exports.runConsoleTests = runConsoleTests;
-function cleanTestDirs() {
- return del([exports.localBaseline, exports.localRwcBaseline])
- .then(() => mkdirP(exports.localRwcBaseline))
- .then(() => mkdirP(exports.localBaseline));
+async function cleanTestDirs() {
+ await del([exports.localBaseline, exports.localRwcBaseline])
+ mkdirP.sync(exports.localRwcBaseline);
+ mkdirP.sync(exports.localBaseline);
}
exports.cleanTestDirs = cleanTestDirs;
diff --git a/scripts/build/upToDate.js b/scripts/build/upToDate.js
deleted file mode 100644
index c3abb5d101..0000000000
--- a/scripts/build/upToDate.js
+++ /dev/null
@@ -1,435 +0,0 @@
-// @ts-check
-const path = require("path");
-const fs = require("fs");
-const log = require("fancy-log"); // was `require("gulp-util").log (see https://github.com/gulpjs/gulp-util)
-const ts = require("../../lib/typescript");
-const { Duplex } = require("stream");
-const chalk = /**@type {*} */(require("chalk"));
-const Vinyl = require("vinyl");
-
-/**
- * Creates a stream that passes through its inputs only if the project outputs are not up to date
- * with respect to the inputs.
- * @param {ParsedCommandLine} parsedProject
- * @param {UpToDateOptions} [options]
- *
- * @typedef UpToDateOptions
- * @property {boolean | "minimal"} [verbose]
- * @property {(configFilePath: string) => ParsedCommandLine | undefined} [parseProject]
- */
-function upToDate(parsedProject, options) {
- /** @type {File[]} */
- const inputs = [];
- /** @type {Map} */
- const inputMap = new Map();
- /** @type {Map} */
- const statCache = new Map();
- /** @type {UpToDateHost} */
- const upToDateHost = {
- fileExists(fileName) {
- const stats = getStat(fileName);
- return stats ? stats.isFile() : false;
- },
- getModifiedTime(fileName) {
- return getStat(fileName).mtime;
- },
- parseConfigFile: options && options.parseProject
- };
- const duplex = new Duplex({
- objectMode: true,
- /**
- * @param {string|Buffer|File} file
- */
- write(file, _, cb) {
- if (typeof file === "string" || Buffer.isBuffer(file)) return cb(new Error("Only Vinyl files are supported."));
- inputs.push(file);
- inputMap.set(path.resolve(file.path), file);
- cb();
- },
- final(cb) {
- const status = getUpToDateStatus(upToDateHost, parsedProject);
- reportStatus(parsedProject, status, options);
- if (status.type !== UpToDateStatusType.UpToDate) {
- for (const input of inputs) duplex.push(input);
- }
- duplex.push(null);
- inputMap.clear();
- statCache.clear();
- cb();
- },
- read() {
- }
- });
- return duplex;
-
- function getStat(fileName) {
- fileName = path.resolve(fileName);
- const inputFile = inputMap.get(fileName);
- if (inputFile && inputFile.stat) return inputFile.stat;
-
- let stats = statCache.get(fileName);
- if (!stats && fs.existsSync(fileName)) {
- stats = fs.statSync(fileName);
- statCache.set(fileName, stats);
- }
- return stats;
- }
-}
-module.exports = exports = upToDate;
-
-/**
- * @param {DiagnosticMessage} message
- * @param {...string} args
- */
-function formatMessage(message, ...args) {
- log.info(formatStringFromArgs(message.message, args));
-}
-
-/**
- * @param {ParsedCommandLine} project
- * @param {UpToDateStatus} status
- * @param {{verbose?: boolean | "minimal"}} options
- */
-function reportStatus(project, status, options) {
- switch (options.verbose) {
- case "minimal":
- switch (status.type) {
- case UpToDateStatusType.UpToDate:
- log.info(`Project '${fileName(project.options.configFilePath)}' is up to date.`);
- break;
- default:
- log.info(`Project '${fileName(project.options.configFilePath)}' is out of date, rebuilding...`);
- break;
- }
- break;
- case true:
- /**@type {*}*/(ts).formatUpToDateStatus(project.options.configFilePath, status, fileName, formatMessage);
- break;
- }
- if (!options.verbose) return;
-}
-
-/**
- * @param {string} file
- * @private
- */
-function normalizeSlashes(file) {
- return file.replace(/\\/g, "/");
-}
-
-/**
- * @param {string} file
- * @private
- */
-function fileName(file) {
- return chalk.cyan(normalizeSlashes(path.relative(process.cwd(), path.resolve(file))));
-}
-
-/**
- * @param {string} text
- * @param {string[]} args
- * @param {number} [baseIndex]
- */
-function formatStringFromArgs(text, args, baseIndex = 0) {
- return text.replace(/{(\d+)}/g, (_match, index) => args[+index + baseIndex]);
-}
-
-const minimumDate = new Date(-8640000000000000);
-const maximumDate = new Date(8640000000000000);
-const missingFileModifiedTime = new Date(0);
-
-/**
- * @typedef {0} UpToDateStatusType.Unbuildable
- * @typedef {1} UpToDateStatusType.UpToDate
- * @typedef {2} UpToDateStatusType.UpToDateWithUpstreamTypes
- * @typedef {3} UpToDateStatusType.OutputMissing
- * @typedef {4} UpToDateStatusType.OutOfDateWithSelf
- * @typedef {5} UpToDateStatusType.OutOfDateWithUpstream
- * @typedef {6} UpToDateStatusType.UpstreamOutOfDate
- * @typedef {7} UpToDateStatusType.UpstreamBlocked
- * @typedef {8} UpToDateStatusType.ComputingUpstream
- * @typedef {9} UpToDateStatusType.ContainerOnly
- * @enum {UpToDateStatusType.Unbuildable | UpToDateStatusType.UpToDate | UpToDateStatusType.UpToDateWithUpstreamTypes | UpToDateStatusType.OutputMissing | UpToDateStatusType.OutOfDateWithSelf | UpToDateStatusType.OutOfDateWithUpstream | UpToDateStatusType.UpstreamOutOfDate | UpToDateStatusType.UpstreamBlocked | UpToDateStatusType.ComputingUpstream | UpToDateStatusType.ContainerOnly}
- */
-const UpToDateStatusType = {
- Unbuildable: /** @type {0} */(0),
- UpToDate: /** @type {1} */(1),
- UpToDateWithUpstreamTypes: /** @type {2} */(2),
- OutputMissing: /** @type {3} */(3),
- OutOfDateWithSelf: /** @type {4} */(4),
- OutOfDateWithUpstream: /** @type {5} */(5),
- UpstreamOutOfDate: /** @type {6} */(6),
- UpstreamBlocked: /** @type {7} */(7),
- ComputingUpstream: /** @type {8} */(8),
- ContainerOnly: /** @type {9} */(9),
-};
-
-/**
- * @param {Date} date1
- * @param {Date} date2
- * @returns {Date}
- */
-function newer(date1, date2) {
- return date2 > date1 ? date2 : date1;
-}
-
-/**
- * @param {UpToDateHost} host
- * @param {ParsedCommandLine | undefined} project
- * @returns {UpToDateStatus}
- */
-function getUpToDateStatus(host, project) {
- if (project === undefined) return { type: UpToDateStatusType.Unbuildable, reason: "File deleted mid-build" };
- const prior = host.getLastStatus ? host.getLastStatus(project.options.configFilePath) : undefined;
- if (prior !== undefined) {
- return prior;
- }
- const actual = getUpToDateStatusWorker(host, project);
- if (host.setLastStatus) {
- host.setLastStatus(project.options.configFilePath, actual);
- }
- return actual;
-}
-
-/**
- * @param {UpToDateHost} host
- * @param {ParsedCommandLine | undefined} project
- * @returns {UpToDateStatus}
- */
-function getUpToDateStatusWorker(host, project) {
- /** @type {string} */
- let newestInputFileName = undefined;
- let newestInputFileTime = minimumDate;
- // Get timestamps of input files
- for (const inputFile of project.fileNames) {
- if (!host.fileExists(inputFile)) {
- return {
- type: UpToDateStatusType.Unbuildable,
- reason: `${inputFile} does not exist`
- };
- }
-
- const inputTime = host.getModifiedTime(inputFile) || missingFileModifiedTime;
- if (inputTime > newestInputFileTime) {
- newestInputFileName = inputFile;
- newestInputFileTime = inputTime;
- }
- }
-
- // Collect the expected outputs of this project
- const outputs = /**@type {string[]}*/(/**@type {*}*/(ts).getAllProjectOutputs(project));
-
- if (outputs.length === 0) {
- return {
- type: UpToDateStatusType.ContainerOnly
- };
- }
-
- // Now see if all outputs are newer than the newest input
- let oldestOutputFileName = "(none)";
- let oldestOutputFileTime = maximumDate;
- let newestOutputFileName = "(none)";
- let newestOutputFileTime = minimumDate;
- /** @type {string | undefined} */
- let missingOutputFileName;
- let newestDeclarationFileContentChangedTime = minimumDate;
- let isOutOfDateWithInputs = false;
- for (const output of outputs) {
- // Output is missing; can stop checking
- // Don't immediately return because we can still be upstream-blocked, which is a higher-priority status
- if (!host.fileExists(output)) {
- missingOutputFileName = output;
- break;
- }
-
- const outputTime = host.getModifiedTime(output) || missingFileModifiedTime;
- if (outputTime < oldestOutputFileTime) {
- oldestOutputFileTime = outputTime;
- oldestOutputFileName = output;
- }
-
- // If an output is older than the newest input, we can stop checking
- // Don't immediately return because we can still be upstream-blocked, which is a higher-priority status
- if (outputTime < newestInputFileTime) {
- isOutOfDateWithInputs = true;
- break;
- }
-
- if (outputTime > newestOutputFileTime) {
- newestOutputFileTime = outputTime;
- newestOutputFileName = output;
- }
-
- // Keep track of when the most recent time a .d.ts file was changed.
- // In addition to file timestamps, we also keep track of when a .d.ts file
- // had its file touched but not had its contents changed - this allows us
- // to skip a downstream typecheck
- if (path.extname(output) === ".d.ts") {
- const unchangedTime = host.getUnchangedTime ? host.getUnchangedTime(output) : undefined;
- if (unchangedTime !== undefined) {
- newestDeclarationFileContentChangedTime = newer(unchangedTime, newestDeclarationFileContentChangedTime);
- }
- else {
- const outputModifiedTime = host.getModifiedTime(output) || missingFileModifiedTime;
- newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, outputModifiedTime);
- }
- }
- }
-
- let pseudoUpToDate = false;
- let usesPrepend = false;
- /** @type {string | undefined} */
- let upstreamChangedProject;
- if (project.projectReferences) {
- if (host.setLastStatus) host.setLastStatus(project.options.configFilePath, { type: UpToDateStatusType.ComputingUpstream });
- for (const ref of project.projectReferences) {
- usesPrepend = usesPrepend || !!(ref.prepend);
- const resolvedRef = ts.resolveProjectReferencePath(host, ref);
- const parsedRef = host.parseConfigFile ? host.parseConfigFile(resolvedRef) : ts.getParsedCommandLineOfConfigFile(resolvedRef, {}, parseConfigHost);
- const refStatus = getUpToDateStatus(host, parsedRef);
-
- // Its a circular reference ignore the status of this project
- if (refStatus.type === UpToDateStatusType.ComputingUpstream) {
- continue;
- }
-
- // An upstream project is blocked
- if (refStatus.type === UpToDateStatusType.Unbuildable) {
- return {
- type: UpToDateStatusType.UpstreamBlocked,
- upstreamProjectName: ref.path
- };
- }
-
- // If the upstream project is out of date, then so are we (someone shouldn't have asked, though?)
- if (refStatus.type !== UpToDateStatusType.UpToDate) {
- return {
- type: UpToDateStatusType.UpstreamOutOfDate,
- upstreamProjectName: ref.path
- };
- }
-
- // If the upstream project's newest file is older than our oldest output, we
- // can't be out of date because of it
- if (refStatus.newestInputFileTime && refStatus.newestInputFileTime <= oldestOutputFileTime) {
- continue;
- }
-
- // If the upstream project has only change .d.ts files, and we've built
- // *after* those files, then we're "psuedo up to date" and eligible for a fast rebuild
- if (refStatus.newestDeclarationFileContentChangedTime && refStatus.newestDeclarationFileContentChangedTime <= oldestOutputFileTime) {
- pseudoUpToDate = true;
- upstreamChangedProject = ref.path;
- continue;
- }
-
- // We have an output older than an upstream output - we are out of date
- return {
- type: UpToDateStatusType.OutOfDateWithUpstream,
- outOfDateOutputFileName: oldestOutputFileName,
- newerProjectName: ref.path
- };
- }
- }
-
- if (missingOutputFileName !== undefined) {
- return {
- type: UpToDateStatusType.OutputMissing,
- missingOutputFileName
- };
- }
-
- if (isOutOfDateWithInputs) {
- return {
- type: UpToDateStatusType.OutOfDateWithSelf,
- outOfDateOutputFileName: oldestOutputFileName,
- newerInputFileName: newestInputFileName
- };
- }
-
- if (usesPrepend && pseudoUpToDate) {
- return {
- type: UpToDateStatusType.OutOfDateWithUpstream,
- outOfDateOutputFileName: oldestOutputFileName,
- newerProjectName: upstreamChangedProject
- };
- }
-
- // Up to date
- return {
- type: pseudoUpToDate ? UpToDateStatusType.UpToDateWithUpstreamTypes : UpToDateStatusType.UpToDate,
- newestDeclarationFileContentChangedTime,
- newestInputFileTime,
- newestOutputFileTime,
- newestInputFileName,
- newestOutputFileName,
- oldestOutputFileName
- };
-}
-
-const parseConfigHost = {
- useCaseSensitiveFileNames: true,
- getCurrentDirectory: () => process.cwd(),
- readDirectory: (file) => fs.readdirSync(file),
- fileExists: file => fs.existsSync(file) && fs.statSync(file).isFile(),
- readFile: file => fs.readFileSync(file, "utf8"),
- onUnRecoverableConfigFileDiagnostic: () => undefined
-};
-
-/**
- * @typedef {import("vinyl")} File
- * @typedef {import("../../lib/typescript").ParsedCommandLine & { options: CompilerOptions }} ParsedCommandLine
- * @typedef {import("../../lib/typescript").CompilerOptions & { configFilePath?: string }} CompilerOptions
- * @typedef {import("../../lib/typescript").DiagnosticMessage} DiagnosticMessage
- * @typedef UpToDateHost
- * @property {(fileName: string) => boolean} fileExists
- * @property {(fileName: string) => Date} getModifiedTime
- * @property {(fileName: string) => Date} [getUnchangedTime]
- * @property {(configFilePath: string) => ParsedCommandLine | undefined} parseConfigFile
- * @property {(configFilePath: string) => UpToDateStatus} [getLastStatus]
- * @property {(configFilePath: string, status: UpToDateStatus) => void} [setLastStatus]
- *
- * @typedef Status.Unbuildable
- * @property {UpToDateStatusType.Unbuildable} type
- * @property {string} reason
- *
- * @typedef Status.ContainerOnly
- * @property {UpToDateStatusType.ContainerOnly} type
- *
- * @typedef Status.UpToDate
- * @property {UpToDateStatusType.UpToDate | UpToDateStatusType.UpToDateWithUpstreamTypes} type
- * @property {Date} [newestInputFileTime]
- * @property {string} [newestInputFileName]
- * @property {Date} [newestDeclarationFileContentChangedTime]
- * @property {Date} [newestOutputFileTime]
- * @property {string} [newestOutputFileName]
- * @property {string} [oldestOutputFileName]
- *
- * @typedef Status.OutputMissing
- * @property {UpToDateStatusType.OutputMissing} type
- * @property {string} missingOutputFileName
- *
- * @typedef Status.OutOfDateWithSelf
- * @property {UpToDateStatusType.OutOfDateWithSelf} type
- * @property {string} outOfDateOutputFileName
- * @property {string} newerInputFileName
- *
- * @typedef Status.UpstreamOutOfDate
- * @property {UpToDateStatusType.UpstreamOutOfDate} type
- * @property {string} upstreamProjectName
- *
- * @typedef Status.UpstreamBlocked
- * @property {UpToDateStatusType.UpstreamBlocked} type
- * @property {string} upstreamProjectName
- *
- * @typedef Status.ComputingUpstream
- * @property {UpToDateStatusType.ComputingUpstream} type
- *
- * @typedef Status.OutOfDateWithUpstream
- * @property {UpToDateStatusType.OutOfDateWithUpstream} type
- * @property {string} outOfDateOutputFileName
- * @property {string} newerProjectName
-
- * @typedef {Status.Unbuildable | Status.ContainerOnly | Status.UpToDate | Status.OutputMissing | Status.OutOfDateWithSelf | Status.UpstreamOutOfDate | Status.UpstreamBlocked | Status.ComputingUpstream | Status.OutOfDateWithUpstream} UpToDateStatus
- */
-void 0;
\ No newline at end of file
diff --git a/scripts/build/utils.js b/scripts/build/utils.js
index 06f55d7288..170c36adef 100644
--- a/scripts/build/utils.js
+++ b/scripts/build/utils.js
@@ -1,7 +1,119 @@
// @ts-check
+///
+
const fs = require("fs");
-const File = require("./vinyl");
-const { Readable } = require("stream");
+const path = require("path");
+const log = require("fancy-log");
+const mkdirp = require("mkdirp");
+const del = require("del");
+const File = require("vinyl");
+const ts = require("../../lib/typescript");
+const { default: chalk } = require("chalk");
+const { spawn } = require("child_process");
+const { CancellationToken, CancelError, Deferred } = require("prex");
+const { Readable, Duplex } = require("stream");
+
+const isWindows = /^win/.test(process.platform);
+
+/**
+ * Executes the provided command once with the supplied arguments.
+ * @param {string} cmd
+ * @param {string[]} args
+ * @param {ExecOptions} [options]
+ *
+ * @typedef ExecOptions
+ * @property {boolean} [ignoreExitCode]
+ * @property {import("prex").CancellationToken} [cancelToken]
+ * @property {boolean} [hidePrompt]
+ */
+function exec(cmd, args, options = {}) {
+ return /**@type {Promise<{exitCode: number}>}*/(new Promise((resolve, reject) => {
+ const { ignoreExitCode, cancelToken = CancellationToken.none } = options;
+ cancelToken.throwIfCancellationRequested();
+
+ // TODO (weswig): Update child_process types to add windowsVerbatimArguments to the type definition
+ const subshellFlag = isWindows ? "/c" : "-c";
+ const command = isWindows ? [possiblyQuote(cmd), ...args] : [`${cmd} ${args.join(" ")}`];
+
+ if (!options.hidePrompt) log(`> ${chalk.green(cmd)} ${args.join(" ")}`);
+ const proc = spawn(isWindows ? "cmd" : "/bin/sh", [subshellFlag, ...command], { stdio: "inherit", windowsVerbatimArguments: true });
+ const registration = cancelToken.register(() => {
+ log(`${chalk.red("killing")} '${chalk.green(cmd)} ${args.join(" ")}'...`);
+ proc.kill("SIGINT");
+ proc.kill("SIGTERM");
+ reject(new CancelError());
+ });
+ proc.on("exit", exitCode => {
+ registration.unregister();
+ if (exitCode === 0 || ignoreExitCode) {
+ resolve({ exitCode });
+ }
+ else {
+ reject(new Error(`Process exited with code: ${exitCode}`));
+ }
+ });
+ proc.on("error", error => {
+ registration.unregister();
+ reject(error);
+ });
+ }));
+}
+exports.exec = exec;
+
+/**
+ * @param {string} cmd
+ */
+function possiblyQuote(cmd) {
+ return cmd.indexOf(" ") >= 0 ? `"${cmd}"` : cmd;
+}
+
+/**
+ * @param {ts.Diagnostic[]} diagnostics
+ * @param {{ cwd?: string, pretty?: boolean }} [options]
+ */
+function formatDiagnostics(diagnostics, options) {
+ return options && options.pretty
+ ? ts.formatDiagnosticsWithColorAndContext(diagnostics, getFormatDiagnosticsHost(options && options.cwd))
+ : ts.formatDiagnostics(diagnostics, getFormatDiagnosticsHost(options && options.cwd));
+}
+exports.formatDiagnostics = formatDiagnostics;
+
+/**
+ * @param {ts.Diagnostic[]} diagnostics
+ * @param {{ cwd?: string }} [options]
+ */
+function reportDiagnostics(diagnostics, options) {
+ log(formatDiagnostics(diagnostics, { cwd: options && options.cwd, pretty: process.stdout.isTTY }));
+}
+exports.reportDiagnostics = reportDiagnostics;
+
+/**
+ * @param {string | undefined} cwd
+ * @returns {ts.FormatDiagnosticsHost}
+ */
+function getFormatDiagnosticsHost(cwd) {
+ return {
+ getCanonicalFileName: fileName => fileName,
+ getCurrentDirectory: () => cwd,
+ getNewLine: () => ts.sys.newLine,
+ };
+}
+exports.getFormatDiagnosticsHost = getFormatDiagnosticsHost;
+
+/**
+ * Reads JSON data with optional comments using the LKG TypeScript compiler
+ * @param {string} jsonPath
+ */
+function readJson(jsonPath) {
+ const jsonText = fs.readFileSync(jsonPath, "utf8");
+ const result = ts.parseConfigFileTextToJson(jsonPath, jsonText);
+ if (result.error) {
+ reportDiagnostics([result.error]);
+ throw new Error("An error occurred during parse.");
+ }
+ return result.config;
+}
+exports.readJson = readJson;
/**
* @param {File} file
@@ -24,4 +136,299 @@ function streamFromBuffer(buffer) {
}
});
}
-exports.streamFromBuffer = streamFromBuffer;
\ No newline at end of file
+exports.streamFromBuffer = streamFromBuffer;
+
+/**
+ * @param {string | string[]} source
+ * @param {string | string[]} dest
+ * @returns {boolean}
+ */
+function needsUpdate(source, dest) {
+ if (typeof source === "string" && typeof dest === "string") {
+ if (fs.existsSync(dest)) {
+ const {mtime: outTime} = fs.statSync(dest);
+ const {mtime: inTime} = fs.statSync(source);
+ if (+inTime <= +outTime) {
+ return false;
+ }
+ }
+ }
+ else if (typeof source === "string" && typeof dest !== "string") {
+ const {mtime: inTime} = fs.statSync(source);
+ for (const filepath of dest) {
+ if (fs.existsSync(filepath)) {
+ const {mtime: outTime} = fs.statSync(filepath);
+ if (+inTime > +outTime) {
+ return true;
+ }
+ }
+ else {
+ return true;
+ }
+ }
+ return false;
+ }
+ else if (typeof source !== "string" && typeof dest === "string") {
+ if (fs.existsSync(dest)) {
+ const {mtime: outTime} = fs.statSync(dest);
+ for (const filepath of source) {
+ if (fs.existsSync(filepath)) {
+ const {mtime: inTime} = fs.statSync(filepath);
+ if (+inTime > +outTime) {
+ return true;
+ }
+ }
+ else {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ else if (typeof source !== "string" && typeof dest !== "string") {
+ for (let i = 0; i < source.length; i++) {
+ if (!dest[i]) {
+ continue;
+ }
+ if (fs.existsSync(dest[i])) {
+ const {mtime: outTime} = fs.statSync(dest[i]);
+ const {mtime: inTime} = fs.statSync(source[i]);
+ if (+inTime > +outTime) {
+ return true;
+ }
+ }
+ else {
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+}
+exports.needsUpdate = needsUpdate;
+
+function getDiffTool() {
+ const program = process.env.DIFF;
+ if (!program) {
+ log.warn("Add the 'DIFF' environment variable to the path of the program you want to use.");
+ process.exit(1);
+ }
+ return program;
+}
+exports.getDiffTool = getDiffTool;
+
+/**
+ * Find the size of a directory recursively.
+ * Symbolic links can cause a loop.
+ * @param {string} root
+ * @returns {number} bytes
+ */
+function getDirSize(root) {
+ const stats = fs.lstatSync(root);
+
+ if (!stats.isDirectory()) {
+ return stats.size;
+ }
+
+ return fs.readdirSync(root)
+ .map(file => getDirSize(path.join(root, file)))
+ .reduce((acc, num) => acc + num, 0);
+}
+exports.getDirSize = getDirSize;
+
+/**
+ * Flattens a project with project references into a single project.
+ * @param {string} projectSpec The path to a tsconfig.json file or its containing directory.
+ * @param {string} flattenedProjectSpec The output path for the flattened tsconfig.json file.
+ * @param {FlattenOptions} [options] Options used to flatten a project hierarchy.
+ *
+ * @typedef FlattenOptions
+ * @property {string} [cwd] The path to use for the current working directory. Defaults to `process.cwd()`.
+ * @property {import("../../lib/typescript").CompilerOptions} [compilerOptions] Compiler option overrides.
+ * @property {boolean} [force] Forces creation of the output project.
+ * @property {string[]} [exclude] Files to exclude (relative to `cwd`)
+ */
+function flatten(projectSpec, flattenedProjectSpec, options = {}) {
+ const cwd = normalizeSlashes(options.cwd ? path.resolve(options.cwd) : process.cwd());
+ const files = [];
+ const resolvedOutputSpec = path.resolve(cwd, flattenedProjectSpec);
+ const resolvedOutputDirectory = path.dirname(resolvedOutputSpec);
+ const resolvedProjectSpec = resolveProjectSpec(projectSpec, cwd, undefined);
+ const project = readJson(resolvedProjectSpec);
+ const skipProjects = /**@type {Set}*/(new Set());
+ const skipFiles = new Set(options && options.exclude && options.exclude.map(file => normalizeSlashes(path.resolve(cwd, file))));
+ recur(resolvedProjectSpec, project);
+
+ if (options.force || needsUpdate(files, resolvedOutputSpec)) {
+ const config = {
+ extends: normalizeSlashes(path.relative(resolvedOutputDirectory, resolvedProjectSpec)),
+ compilerOptions: options.compilerOptions || {},
+ files: files.map(file => normalizeSlashes(path.relative(resolvedOutputDirectory, file)))
+ };
+ mkdirp.sync(resolvedOutputDirectory);
+ fs.writeFileSync(resolvedOutputSpec, JSON.stringify(config, undefined, 2), "utf8");
+ }
+
+ /**
+ * @param {string} projectSpec
+ * @param {object} project
+ */
+ function recur(projectSpec, project) {
+ if (skipProjects.has(projectSpec)) return;
+ skipProjects.add(project);
+ if (project.references) {
+ for (const ref of project.references) {
+ const referencedSpec = resolveProjectSpec(ref.path, cwd, projectSpec);
+ const referencedProject = readJson(referencedSpec);
+ recur(referencedSpec, referencedProject);
+ }
+ }
+ if (project.include) {
+ throw new Error("Flattened project may not have an 'include' list.");
+ }
+ if (!project.files) {
+ throw new Error("Flattened project must have an explicit 'files' list.");
+ }
+ const projectDirectory = path.dirname(projectSpec);
+ for (let file of project.files) {
+ file = normalizeSlashes(path.resolve(projectDirectory, file));
+ if (skipFiles.has(file)) continue;
+ skipFiles.add(file);
+ files.push(file);
+ }
+ }
+}
+exports.flatten = flatten;
+
+/**
+ * @param {string} file
+ */
+function normalizeSlashes(file) {
+ return file.replace(/\\/g, "/");
+}
+
+/**
+ * @param {string} projectSpec
+ * @param {string} cwd
+ * @param {string | undefined} referrer
+ * @returns {string}
+ */
+function resolveProjectSpec(projectSpec, cwd, referrer) {
+ let projectPath = normalizeSlashes(path.resolve(cwd, referrer ? path.dirname(referrer) : "", projectSpec));
+ const stats = fs.statSync(projectPath);
+ if (stats.isFile()) return normalizeSlashes(projectPath);
+ return normalizeSlashes(path.resolve(cwd, projectPath, "tsconfig.json"));
+}
+
+/**
+ * @param {string | ((file: File) => string) | { cwd?: string }} [dest]
+ * @param {{ cwd?: string }} [opts]
+ */
+function rm(dest, opts) {
+ if (dest && typeof dest === "object") opts = dest, dest = undefined;
+ let failed = false;
+
+ const cwd = path.resolve(opts && opts.cwd || process.cwd());
+
+ /** @type {{ file: File, deleted: boolean, promise: Promise, cb: Function }[]} */
+ const pending = [];
+
+ const processDeleted = () => {
+ if (failed) return;
+ while (pending.length && pending[0].deleted) {
+ const { file, cb } = pending.shift();
+ duplex.push(file);
+ cb();
+ }
+ };
+
+ const duplex = new Duplex({
+ objectMode: true,
+ /**
+ * @param {string|Buffer|File} file
+ */
+ write(file, _, cb) {
+ if (failed) return;
+ if (typeof file === "string" || Buffer.isBuffer(file)) return cb(new Error("Only Vinyl files are supported."));
+ const basePath = typeof dest === "string" ? path.resolve(cwd, dest) :
+ typeof dest === "function" ? path.resolve(cwd, dest(file)) :
+ file.base;
+ const filePath = path.resolve(basePath, file.relative);
+ file.cwd = cwd;
+ file.base = basePath;
+ file.path = filePath;
+ const entry = {
+ file,
+ deleted: false,
+ cb,
+ promise: del(file.path).then(() => {
+ entry.deleted = true;
+ processDeleted();
+ }, err => {
+ failed = true;
+ pending.length = 0;
+ cb(err);
+ })
+ };
+ pending.push(entry);
+ },
+ final(cb) {
+ processDeleted();
+ if (pending.length) {
+ Promise
+ .all(pending.map(entry => entry.promise))
+ .then(() => processDeleted())
+ .then(() => cb(), cb);
+ return;
+ }
+ cb();
+ },
+ read() {
+ }
+ });
+ return duplex;
+}
+exports.rm = rm;
+
+class Debouncer {
+ /**
+ * @param {number} timeout
+ * @param {() => Promise} action
+ */
+ constructor(timeout, action) {
+ this._timeout = timeout;
+ this._action = action;
+ }
+
+ enqueue() {
+ if (this._timer) {
+ clearTimeout(this._timer);
+ this._timer = undefined;
+ }
+
+ if (!this._deferred) {
+ this._deferred = new Deferred();
+ }
+
+ this._timer = setTimeout(() => this.run(), 100);
+ return this._deferred.promise;
+ }
+
+ run() {
+ if (this._timer) {
+ clearTimeout(this._timer);
+ this._timer = undefined;
+ }
+
+ const deferred = this._deferred;
+ this._deferred = undefined;
+ this._projects = undefined;
+ try {
+ deferred.resolve(this._action());
+ }
+ catch (e) {
+ deferred.reject(e);
+ }
+ }
+}
+exports.Debouncer = Debouncer;
\ No newline at end of file
diff --git a/scripts/build/vinyl.d.ts b/scripts/build/vinyl.d.ts
deleted file mode 100644
index 1dfb631499..0000000000
--- a/scripts/build/vinyl.d.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-// NOTE: This makes it possible to correctly type vinyl Files under @ts-check.
-export = File;
-
-declare class File {
- constructor(options?: File.VinylOptions);
-
- cwd: string;
- base: string;
- path: string;
- readonly history: ReadonlyArray;
- contents: T;
- relative: string;
- dirname: string;
- basename: string;
- stem: string;
- extname: string;
- symlink: string | null;
- stat: import("fs").Stats | null;
- sourceMap?: import("./sourcemaps").RawSourceMap | string;
-
- [custom: string]: any;
-
- isBuffer(): this is T extends Buffer ? File : never;
- isStream(): this is T extends NodeJS.ReadableStream ? File : never;
- isNull(): this is T extends null ? File : never;
- isDirectory(): this is T extends null ? File.Directory : never;
- isSymbolic(): this is T extends null ? File.Symbolic : never;
- clone(opts?: { contents?: boolean, deep?: boolean }): this;
-}
-
-namespace File {
- export interface VinylOptions {
- cwd?: string;
- base?: string;
- path?: string;
- history?: ReadonlyArray;
- stat?: import("fs").Stats;
- contents?: T;
- sourceMap?: import("./sourcemaps").RawSourceMap | string;
- [custom: string]: any;
- }
-
- export type Contents = Buffer | NodeJS.ReadableStream | null;
- export type File = import("./vinyl");
- export type NullFile = File;
- export type BufferFile = File;
- export type StreamFile = File;
-
- export interface Directory extends NullFile {
- isNull(): true;
- isDirectory(): true;
- isSymbolic(): this is never;
- }
-
- export interface Symbolic extends NullFile {
- isNull(): true;
- isDirectory(): this is never;
- isSymbolic(): true;
- }
-}
\ No newline at end of file
diff --git a/scripts/build/vinyl.js b/scripts/build/vinyl.js
deleted file mode 100644
index 6cf68f3cd2..0000000000
--- a/scripts/build/vinyl.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require("vinyl");
\ No newline at end of file
diff --git a/scripts/open-user-pr.ts b/scripts/open-user-pr.ts
index 68f691235d..f510c63d4e 100644
--- a/scripts/open-user-pr.ts
+++ b/scripts/open-user-pr.ts
@@ -1,3 +1,5 @@
+///
+// Must reference esnext.asynciterable lib, since octokit uses AsyncIterable internally
import cp = require("child_process");
import Octokit = require("@octokit/rest");
@@ -35,7 +37,7 @@ gh.authenticate({
type: "token",
token: process.argv[2]
});
-gh.pullRequests.create({
+gh.pulls.create({
owner: process.env.TARGET_FORK,
repo: "TypeScript",
maintainer_can_modify: true,
@@ -50,7 +52,7 @@ cc ${reviewers.map(r => "@" + r).join(" ")}`,
}).then(r => {
const num = r.data.number;
console.log(`Pull request ${num} created.`);
- return gh.pullRequests.createReviewRequest({
+ return gh.pulls.createReviewRequest({
owner: process.env.TARGET_FORK,
repo: "TypeScript",
number: num,
diff --git a/scripts/types/ambient.d.ts b/scripts/types/ambient.d.ts
index 4f9112d236..52ddca6d7c 100644
--- a/scripts/types/ambient.d.ts
+++ b/scripts/types/ambient.d.ts
@@ -1,3 +1,5 @@
+import { TaskFunction } from "gulp";
+
declare module "gulp-clone" {
function Clone(): NodeJS.ReadWriteStream;
namespace Clone {
@@ -14,3 +16,78 @@ declare module "gulp-insert" {
}
declare module "sorcery";
+
+declare module "vinyl" {
+ // NOTE: This makes it possible to correctly type vinyl Files under @ts-check.
+ export = File;
+
+ declare class File {
+ constructor(options?: File.VinylOptions);
+
+ cwd: string;
+ base: string;
+ path: string;
+ readonly history: ReadonlyArray;
+ contents: T;
+ relative: string;
+ dirname: string;
+ basename: string;
+ stem: string;
+ extname: string;
+ symlink: string | null;
+ stat: import("fs").Stats | null;
+ sourceMap?: import("./sourcemaps").RawSourceMap | string;
+
+ [custom: string]: any;
+
+ isBuffer(): this is T extends Buffer ? File : never;
+ isStream(): this is T extends NodeJS.ReadableStream ? File : never;
+ isNull(): this is T extends null ? File : never;
+ isDirectory(): this is T extends null ? File.Directory : never;
+ isSymbolic(): this is T extends null ? File.Symbolic : never;
+ clone(opts?: { contents?: boolean, deep?: boolean }): this;
+ }
+
+ namespace File {
+ export interface VinylOptions {
+ cwd?: string;
+ base?: string;
+ path?: string;
+ history?: ReadonlyArray;
+ stat?: import("fs").Stats;
+ contents?: T;
+ sourceMap?: import("./sourcemaps").RawSourceMap | string;
+ [custom: string]: any;
+ }
+
+ export type Contents = Buffer | NodeJS.ReadableStream | null;
+ export type File = import("./vinyl");
+ export type NullFile = File;
+ export type BufferFile = File;
+ export type StreamFile = File;
+
+ export interface Directory extends NullFile {
+ isNull(): true;
+ isDirectory(): true;
+ isSymbolic(): this is never;
+ }
+
+ export interface Symbolic extends NullFile {
+ isNull(): true;
+ isDirectory(): this is never;
+ isSymbolic(): true;
+ }
+ }
+}
+
+declare module "undertaker" {
+ interface TaskFunctionParams {
+ flags?: Record;
+ }
+}
+
+declare module "gulp-sourcemaps" {
+ interface WriteOptions {
+ destPath?: string;
+ }
+}
\ No newline at end of file
diff --git a/src/cancellationToken/cancellationToken.ts b/src/cancellationToken/cancellationToken.ts
index 9b74ebb713..059ef37c8f 100644
--- a/src/cancellationToken/cancellationToken.ts
+++ b/src/cancellationToken/cancellationToken.ts
@@ -35,8 +35,8 @@ function createCancellationToken(args: string[]): ServerCancellationToken {
}
// cancellationPipeName is a string without '*' inside that can optionally end with '*'
// when client wants to signal cancellation it should create a named pipe with name=
- // server will synchronously check the presence of the pipe and treat its existance as indicator that current request should be canceled.
- // in case if client prefers to use more fine-grained schema than one name for all request it can add '*' to the end of cancelellationPipeName.
+ // server will synchronously check the presence of the pipe and treat its existence as indicator that current request should be canceled.
+ // in case if client prefers to use more fine-grained schema than one name for all request it can add '*' to the end of cancellationPipeName.
// in this case pipe name will be build dynamically as .
if (cancellationPipeName.charAt(cancellationPipeName.length - 1) === "*") {
const namePrefix = cancellationPipeName.slice(0, -1);
diff --git a/src/cancellationToken/tsconfig.json b/src/cancellationToken/tsconfig.json
index e16ce22120..6d9e0af772 100644
--- a/src/cancellationToken/tsconfig.json
+++ b/src/cancellationToken/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "../tsconfig-base",
+ "extends": "../tsconfig-noncomposite-base",
"compilerOptions": {
"outDir": "../../built/local/",
"rootDir": ".",
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 27f85896a3..6781f6f170 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -550,7 +550,7 @@ namespace ts {
}
}
// We create a return control flow graph for IIFEs and constructors. For constructors
- // we use the return control flow graph in strict property intialization checks.
+ // we use the return control flow graph in strict property initialization checks.
currentReturnTarget = isIIFE || node.kind === SyntaxKind.Constructor ? createBranchLabel() : undefined;
currentBreakTarget = undefined;
currentContinueTarget = undefined;
@@ -3445,7 +3445,7 @@ namespace ts {
// If a FunctionDeclaration is generator function and is the body of a
// transformed async function, then this node can be transformed to a
// down-level generator.
- // Currently we do not support transforming any other generator fucntions
+ // Currently we do not support transforming any other generator functions
// down level.
if (node.asteriskToken) {
transformFlags |= TransformFlags.AssertGenerator;
diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts
index 44ae6a299d..c95a827005 100644
--- a/src/compiler/builder.ts
+++ b/src/compiler/builder.ts
@@ -27,7 +27,7 @@ namespace ts {
currentChangedFilePath: Path | undefined;
/**
* Map of file signatures, with key being file path, calculated while getting current changed file's affected files
- * These will be commited whenever the iteration through affected files of current changed file is complete
+ * These will be committed whenever the iteration through affected files of current changed file is complete
*/
currentAffectedFilesSignatures: Map | undefined;
/**
diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts
index 552f46c378..a394ff5620 100644
--- a/src/compiler/builderState.ts
+++ b/src/compiler/builderState.ts
@@ -37,7 +37,7 @@ namespace ts {
*/
readonly referencedMap: ReadonlyMap | undefined;
/**
- * Contains the map of exported modules ReferencedSet=exorted module files from the file if module emit is enabled
+ * Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled
* Otherwise undefined
*/
readonly exportedModulesMap: Map | undefined;
@@ -268,9 +268,9 @@ namespace ts.BuilderState {
*/
export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: Map, exportedModulesMapCache?: ComputingExportedModulesMap): ReadonlyArray {
// Since the operation could be cancelled, the signatures are always stored in the cache
- // They will be commited once it is safe to use them
+ // They will be committed once it is safe to use them
// eg when calling this api from tsserver, if there is no cancellation of the operation
- // In the other cases the affected files signatures are commited only after the iteration through the result is complete
+ // In the other cases the affected files signatures are committed only after the iteration through the result is complete
const signatureCache = cacheToUpdateSignature || createMap();
const sourceFile = programOfThisState.getSourceFileByPath(path);
if (!sourceFile) {
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index c2c151b259..0df78d82c6 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -67,6 +67,7 @@ namespace ts {
let enumCount = 0;
let instantiationDepth = 0;
let constraintDepth = 0;
+ let currentNode: Node | undefined;
const emptySymbols = createSymbolTable();
const identityMapper: (type: Type) => Type = identity;
@@ -3592,14 +3593,15 @@ namespace ts {
function typeReferenceToTypeNode(type: TypeReference) {
const typeArguments: ReadonlyArray = type.typeArguments || emptyArray;
- if (type.target === globalArrayType) {
+ if (type.target === globalArrayType || type.target === globalReadonlyArrayType) {
if (context.flags & NodeBuilderFlags.WriteArrayAsGenericType) {
const typeArgumentNode = typeToTypeNodeHelper(typeArguments[0], context);
- return createTypeReferenceNode("Array", [typeArgumentNode]);
+ return createTypeReferenceNode(type.target === globalArrayType ? "Array" : "ReadonlyArray", [typeArgumentNode]);
}
const elementType = typeToTypeNodeHelper(typeArguments[0], context);
- return createArrayTypeNode(elementType);
+ const arrayType = createArrayTypeNode(elementType);
+ return type.target === globalArrayType ? arrayType : createTypeOperatorNode(SyntaxKind.ReadonlyKeyword, arrayType);
}
else if (type.target.objectFlags & ObjectFlags.Tuple) {
if (typeArguments.length > 0) {
@@ -3612,11 +3614,13 @@ namespace ts {
createRestTypeNode(createArrayTypeNode(tupleConstituentNodes[i])) :
createOptionalTypeNode(tupleConstituentNodes[i]);
}
- return createTupleTypeNode(tupleConstituentNodes);
+ const tupleTypeNode = createTupleTypeNode(tupleConstituentNodes);
+ return (type.target).readonly ? createTypeOperatorNode(SyntaxKind.ReadonlyKeyword, tupleTypeNode) : tupleTypeNode;
}
}
if (context.encounteredError || (context.flags & NodeBuilderFlags.AllowEmptyTuple)) {
- return createTupleTypeNode([]);
+ const tupleTypeNode = createTupleTypeNode([]);
+ return (type.target).readonly ? createTypeOperatorNode(SyntaxKind.ReadonlyKeyword, tupleTypeNode) : tupleTypeNode;
}
context.encounteredError = true;
return undefined!; // TODO: GH#18217
@@ -3785,7 +3789,7 @@ namespace ts {
context.approximateLength += (symbolName(propertySymbol).length + 1);
context.enclosingDeclaration = saveEnclosingDeclaration;
const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined;
- if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) {
+ if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length && !isReadonlySymbol(propertySymbol)) {
const signatures = getSignaturesOfType(propertyType, SignatureKind.Call);
for (const signature of signatures) {
const methodDeclaration = signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature, context);
@@ -4787,7 +4791,7 @@ namespace ts {
if (!isTypeAssignableTo(getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique), omitKeyType)
&& !(getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected))
&& isSpreadableProperty(prop)) {
- members.set(prop.escapedName, getSpreadSymbol(prop));
+ members.set(prop.escapedName, getSpreadSymbol(prop, /*readonly*/ false));
}
}
const stringIndexInfo = getIndexInfoOfType(source, IndexKind.String);
@@ -4862,17 +4866,8 @@ namespace ts {
function getTypeForBindingElement(declaration: BindingElement): Type | undefined {
const pattern = declaration.parent;
let parentType = getTypeForBindingElementParent(pattern.parent);
- // If parent has the unknown (error) type, then so does this binding element
- if (parentType === errorType) {
- return errorType;
- }
- // If no type was specified or inferred for parent,
- // infer from the initializer of the binding element if one is present.
- // Otherwise, go with the undefined type of the parent.
- if (!parentType) {
- return declaration.initializer ? checkDeclarationInitializer(declaration) : parentType;
- }
- if (isTypeAny(parentType)) {
+ // If no type or an any type was inferred for parent, infer that for the binding element
+ if (!parentType || isTypeAny(parentType)) {
return parentType;
}
// Relax null check on ambient destructuring parameters, since the parameters have no implementation and are just documentation
@@ -4958,6 +4953,12 @@ namespace ts {
return strictNullChecks && optional ? getOptionalType(type) : type;
}
+ function isParameterOfContextuallyTypedFunction(node: Declaration) {
+ return node.kind === SyntaxKind.Parameter &&
+ (node.parent.kind === SyntaxKind.FunctionExpression || node.parent.kind === SyntaxKind.ArrowFunction) &&
+ !!getContextualType(node.parent);
+ }
+
// Return the inferred type for a variable, parameter, or property declaration
function getTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement, includeOptionality: boolean): Type | undefined {
// A variable declared in a for..in statement is of type string, or of type keyof T when the
@@ -5041,8 +5042,9 @@ namespace ts {
}
}
- // Use the type of the initializer expression if one is present
- if (declaration.initializer) {
+ // Use the type of the initializer expression if one is present and the declaration is
+ // not a parameter of a contextually typed function
+ if (declaration.initializer && !isParameterOfContextuallyTypedFunction(declaration)) {
const type = checkDeclarationInitializer(declaration);
return addOptionality(type, isOptional);
}
@@ -5053,8 +5055,9 @@ namespace ts {
return trueType;
}
- // If the declaration specifies a binding pattern, use the type implied by the binding pattern
- if (isBindingPattern(declaration.name)) {
+ // If the declaration specifies a binding pattern and is not a parameter of a contextually
+ // typed function, use the type implied by the binding pattern
+ if (isBindingPattern(declaration.name) && !isParameterOfContextuallyTypedFunction(declaration)) {
return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ false, /*reportErrors*/ true);
}
@@ -5280,17 +5283,18 @@ namespace ts {
let objectFlags = ObjectFlags.ObjectLiteral;
forEach(pattern.elements, e => {
const name = e.propertyName || e.name;
- if (isComputedNonLiteralName(name)) {
- // do not include computed properties in the implied type
- objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties;
- return;
- }
if (e.dotDotDotToken) {
stringIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
return;
}
- const text = getTextOfPropertyName(name);
+ const exprType = getLiteralTypeFromPropertyName(name);
+ if (!isTypeUsableAsPropertyName(exprType)) {
+ // do not include computed properties in the implied type
+ objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties;
+ return;
+ }
+ const text = getPropertyNameFromType(exprType);
const flags = SymbolFlags.Property | (e.initializer ? SymbolFlags.Optional : 0);
const symbol = createSymbol(flags, text);
symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors);
@@ -5690,17 +5694,21 @@ namespace ts {
}
function reportCircularityError(symbol: Symbol) {
+ const declaration = symbol.valueDeclaration;
// Check if variable has type annotation that circularly references the variable itself
- if (getEffectiveTypeAnnotationNode(symbol.valueDeclaration)) {
+ if (getEffectiveTypeAnnotationNode(declaration)) {
error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation,
symbolToString(symbol));
return errorType;
}
- // Otherwise variable has initializer that circularly references the variable itself
- if (noImplicitAny) {
+ // Check if variable has initializer that circularly references the variable itself
+ if (noImplicitAny && (declaration.kind !== SyntaxKind.Parameter || (declaration).initializer)) {
error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer,
symbolToString(symbol));
}
+ // Circularities could also result from parameters in function expressions that end up
+ // having themselves as contextual types following type argument inference. In those cases
+ // we have already reported an implicit any error so we don't report anything here.
return anyType;
}
@@ -5931,7 +5939,7 @@ namespace ts {
function getBaseTypes(type: InterfaceType): BaseType[] {
if (!type.resolvedBaseTypes) {
if (type.objectFlags & ObjectFlags.Tuple) {
- type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters || emptyArray))];
+ type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters || emptyArray), (type).readonly)];
}
else if (type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
if (type.symbol.flags & SymbolFlags.Class) {
@@ -6310,7 +6318,7 @@ namespace ts {
return false;
}
- /** A type parameter is thisless if its contraint is thisless, or if it has no constraint. */
+ /** A type parameter is thisless if its constraint is thisless, or if it has no constraint. */
function isThislessTypeParameter(node: TypeParameterDeclaration) {
const constraint = getEffectiveConstraintOfTypeParameter(node);
return !constraint || isThislessType(constraint);
@@ -6356,7 +6364,9 @@ namespace ts {
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
- return isThislessFunctionLikeDeclaration(declaration);
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ return isThislessFunctionLikeDeclaration(declaration);
}
}
}
@@ -6399,9 +6409,9 @@ namespace ts {
}
/**
- * Indicates whether a type can be used as a late-bound name.
+ * Indicates whether a type can be used as a property name.
*/
- function isTypeUsableAsLateBoundName(type: Type): type is LiteralType | UniqueESSymbolType {
+ function isTypeUsableAsPropertyName(type: Type): type is StringLiteralType | NumberLiteralType | UniqueESSymbolType {
return !!(type.flags & TypeFlags.StringOrNumberLiteralOrUnique);
}
@@ -6416,7 +6426,7 @@ namespace ts {
function isLateBindableName(node: DeclarationName): node is LateBoundName {
return isComputedPropertyName(node)
&& isEntityNameExpression(node.expression)
- && isTypeUsableAsLateBoundName(checkComputedPropertyName(node));
+ && isTypeUsableAsPropertyName(checkComputedPropertyName(node));
}
function isLateBoundName(name: __String): boolean {
@@ -6448,11 +6458,11 @@ namespace ts {
}
/**
- * Gets the symbolic name for a late-bound member from its type.
+ * Gets the symbolic name for a member from its type.
*/
- function getLateBoundNameFromType(type: StringLiteralType | NumberLiteralType | UniqueESSymbolType): __String {
+ function getPropertyNameFromType(type: StringLiteralType | NumberLiteralType | UniqueESSymbolType): __String {
if (type.flags & TypeFlags.UniqueESSymbol) {
- return `__@${type.symbol.escapedName}@${getSymbolId(type.symbol)}` as __String;
+ return (type).escapedName;
}
if (type.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) {
return escapeLeadingUnderscores("" + (type).value);
@@ -6518,8 +6528,8 @@ namespace ts {
// fall back to the early-bound name of this member.
links.resolvedSymbol = decl.symbol;
const type = checkComputedPropertyName(decl.name);
- if (isTypeUsableAsLateBoundName(type)) {
- const memberName = getLateBoundNameFromType(type);
+ if (isTypeUsableAsPropertyName(type)) {
+ const memberName = getPropertyNameFromType(type);
const symbolFlags = decl.symbol.flags;
// Get or add a late-bound symbol for the member. This allows us to merge late-bound accessor declarations.
@@ -7168,8 +7178,8 @@ namespace ts {
const propType = instantiateType(templateType, templateMapper);
// If the current iteration type constituent is a string literal type, create a property.
// Otherwise, for type string create a string index signature.
- if (t.flags & TypeFlags.StringOrNumberLiteralOrUnique) {
- const propName = getLateBoundNameFromType(t as LiteralType);
+ if (isTypeUsableAsPropertyName(t)) {
+ const propName = getPropertyNameFromType(t);
const modifiersProp = getPropertyOfType(modifiersType, propName);
const isOptional = !!(templateModifiers & MappedTypeModifiers.IncludeOptional ||
!(templateModifiers & MappedTypeModifiers.ExcludeOptional) && modifiersProp && modifiersProp.flags & SymbolFlags.Optional);
@@ -7354,7 +7364,8 @@ namespace ts {
function isTypeInvalidDueToUnionDiscriminant(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes): boolean {
const list = obj.properties as NodeArray;
return list.some(property => {
- const name = property.name && getTextOfPropertyName(property.name);
+ const nameType = property.name && getLiteralTypeFromPropertyName(property.name);
+ const name = nameType && isTypeUsableAsPropertyName(nameType) ? getPropertyNameFromType(nameType) : undefined;
const expected = name === undefined ? undefined : getTypeOfPropertyOfType(contextualType, name);
return !!expected && isLiteralType(expected) && !isTypeIdenticalTo(getTypeOfNode(property), expected);
});
@@ -7520,6 +7531,7 @@ namespace ts {
// very high likelyhood we're dealing with an infinite generic type that perpetually generates
// new type identities as we descend into it. We stop the recursion here and mark this type
// and the outer types as having circular constraints.
+ error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite);
nonTerminating = true;
return t.immediateBaseConstraint = noConstraintType;
}
@@ -7638,7 +7650,7 @@ namespace ts {
const typeVariable = getHomomorphicTypeVariable(type);
if (typeVariable) {
const constraint = getConstraintOfTypeParameter(typeVariable);
- if (constraint && (isArrayType(constraint) || isReadonlyArrayType(constraint) || isTupleType(constraint))) {
+ if (constraint && (isArrayType(constraint) || isTupleType(constraint))) {
const mapper = makeUnaryTypeMapper(typeVariable, constraint);
return instantiateType(type, combineTypeMappers(mapper, type.mapper));
}
@@ -9035,22 +9047,22 @@ namespace ts {
return createTypeFromGenericGlobalType(getGlobalIterableIteratorType(/*reportErrors*/ true), [iteratedType]);
}
- function createArrayType(elementType: Type): ObjectType {
- return createTypeFromGenericGlobalType(globalArrayType, [elementType]);
- }
-
- function createReadonlyArrayType(elementType: Type): ObjectType {
- return createTypeFromGenericGlobalType(globalReadonlyArrayType, [elementType]);
+ function createArrayType(elementType: Type, readonly?: boolean): ObjectType {
+ return createTypeFromGenericGlobalType(readonly ? globalReadonlyArrayType : globalArrayType, [elementType]);
}
function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
- links.resolvedType = createArrayType(getTypeFromTypeNode(node.elementType));
+ links.resolvedType = createArrayType(getTypeFromTypeNode(node.elementType), isReadonlyTypeOperator(node.parent));
}
return links.resolvedType;
}
+ function isReadonlyTypeOperator(node: Node) {
+ return isTypeOperatorNode(node) && node.operator === SyntaxKind.ReadonlyKeyword;
+ }
+
// We represent tuple types as type references to synthesized generic interface types created by
// this function. The types are of the form:
//
@@ -9058,7 +9070,7 @@ namespace ts {
//
// Note that the generic type created by this function has no symbol associated with it. The same
// is true for each of the synthesized type parameters.
- function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, associatedNames: __String[] | undefined): TupleType {
+ function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames: __String[] | undefined): TupleType {
let typeParameters: TypeParameter[] | undefined;
const properties: Symbol[] = [];
const maxLength = hasRestElement ? arity - 1 : arity;
@@ -9067,7 +9079,8 @@ namespace ts {
for (let i = 0; i < arity; i++) {
const typeParameter = typeParameters[i] = createTypeParameter();
if (i < maxLength) {
- const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0), "" + i as __String);
+ const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0),
+ "" + i as __String, readonly ? CheckFlags.Readonly : 0);
property.type = typeParameter;
properties.push(property);
}
@@ -9096,25 +9109,26 @@ namespace ts {
type.declaredNumberIndexInfo = undefined;
type.minLength = minLength;
type.hasRestElement = hasRestElement;
+ type.readonly = readonly;
type.associatedNames = associatedNames;
return type;
}
- function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, associatedNames?: __String[]): GenericType {
- const key = arity + (hasRestElement ? "+" : ",") + minLength + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : "");
+ function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames?: __String[]): GenericType {
+ const key = arity + (hasRestElement ? "+" : ",") + minLength + (readonly ? "R" : "") + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : "");
let type = tupleTypes.get(key);
if (!type) {
- tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, associatedNames));
+ tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, readonly, associatedNames));
}
return type;
}
- function createTupleType(elementTypes: ReadonlyArray, minLength = elementTypes.length, hasRestElement = false, associatedNames?: __String[]) {
+ function createTupleType(elementTypes: ReadonlyArray, minLength = elementTypes.length, hasRestElement = false, readonly = false, associatedNames?: __String[]) {
const arity = elementTypes.length;
if (arity === 1 && hasRestElement) {
- return createArrayType(elementTypes[0]);
+ return createArrayType(elementTypes[0], readonly);
}
- const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, associatedNames);
+ const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, readonly, associatedNames);
return elementTypes.length ? createTypeReference(tupleType, elementTypes) : tupleType;
}
@@ -9128,7 +9142,7 @@ namespace ts {
const type = getTypeFromTypeNode(n);
return n === restElement && getIndexTypeOfType(type, IndexKind.Number) || type;
});
- links.resolvedType = createTupleType(elementTypes, minLength, !!restElement);
+ links.resolvedType = createTupleType(elementTypes, minLength, !!restElement, isReadonlyTypeOperator(node.parent));
}
return links.resolvedType;
}
@@ -9143,6 +9157,7 @@ namespace ts {
(type.typeArguments || emptyArray).slice(index),
Math.max(0, tuple.minLength - index),
tuple.hasRestElement,
+ tuple.readonly,
tuple.associatedNames && tuple.associatedNames.slice(index),
);
}
@@ -9232,18 +9247,6 @@ namespace ts {
return includes;
}
- function isSubtypeOfAny(source: Type, targets: ReadonlyArray): boolean {
- for (const target of targets) {
- if (source !== target && isTypeSubtypeOf(source, target) && (
- !(getObjectFlags(getTargetType(source)) & ObjectFlags.Class) ||
- !(getObjectFlags(getTargetType(target)) & ObjectFlags.Class) ||
- isTypeDerivedFrom(source, target))) {
- return true;
- }
- }
- return false;
- }
-
function isSetOfLiteralsFromSameEnum(types: ReadonlyArray): boolean {
const first = types[0];
if (first.flags & TypeFlags.EnumLiteral) {
@@ -9260,17 +9263,42 @@ namespace ts {
return false;
}
- function removeSubtypes(types: Type[]) {
- if (types.length === 0 || isSetOfLiteralsFromSameEnum(types)) {
- return;
+ function removeSubtypes(types: Type[], primitivesOnly: boolean): boolean {
+ const len = types.length;
+ if (len === 0 || isSetOfLiteralsFromSameEnum(types)) {
+ return true;
}
- let i = types.length;
+ let i = len;
+ let count = 0;
while (i > 0) {
i--;
- if (isSubtypeOfAny(types[i], types)) {
- orderedRemoveItemAt(types, i);
+ const source = types[i];
+ for (const target of types) {
+ if (source !== target) {
+ if (count === 100000) {
+ // After 100000 subtype checks we estimate the remaining amount of work by assuming the
+ // same ratio of checks per element. If the estimated number of remaining type checks is
+ // greater than an upper limit we deem the union type too complex to represent. The
+ // upper limit is 25M for unions of primitives only, and 1M otherwise. This for example
+ // caps union types at 5000 unique literal types and 1000 unique object types.
+ const estimatedCount = (count / (len - i)) * len;
+ if (estimatedCount > (primitivesOnly ? 25000000 : 1000000)) {
+ error(currentNode, Diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent);
+ return false;
+ }
+ }
+ count++;
+ if (isTypeSubtypeOf(source, target) && (
+ !(getObjectFlags(getTargetType(source)) & ObjectFlags.Class) ||
+ !(getObjectFlags(getTargetType(target)) & ObjectFlags.Class) ||
+ isTypeDerivedFrom(source, target))) {
+ orderedRemoveItemAt(types, i);
+ break;
+ }
+ }
}
}
+ return true;
}
function removeRedundantLiteralTypes(types: Type[], includes: TypeFlags) {
@@ -9317,7 +9345,9 @@ namespace ts {
}
break;
case UnionReduction.Subtype:
- removeSubtypes(typeSet);
+ if (!removeSubtypes(typeSet, !(includes & TypeFlags.StructuredOrInstantiable))) {
+ return errorType;
+ }
break;
}
if (typeSet.length === 0) {
@@ -9680,6 +9710,9 @@ namespace ts {
? getESSymbolLikeTypeForNode(walkUpParenthesizedTypes(node.parent))
: errorType;
break;
+ case SyntaxKind.ReadonlyKeyword:
+ links.resolvedType = getTypeFromTypeNode(node.type);
+ break;
}
}
return links.resolvedType!; // TODO: GH#18217
@@ -9722,8 +9755,8 @@ namespace ts {
function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, cacheSymbol: boolean, missingType: Type) {
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
- const propName = isTypeUsableAsLateBoundName(indexType) ?
- getLateBoundNameFromType(indexType) :
+ const propName = isTypeUsableAsPropertyName(indexType) ?
+ getPropertyNameFromType(indexType) :
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
getPropertyNameForKnownSymbolName(idText((accessExpression.argumentExpression).name)) :
accessNode && isPropertyName(accessNode) ?
@@ -10010,10 +10043,10 @@ namespace ts {
if (checkType.flags & TypeFlags.Any) {
return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), instantiateType(root.falseType, mapper)]);
}
- // Return falseType for a definitely false extends check. We check an instantations of the two
+ // Return falseType for a definitely false extends check. We check an instantiations of the two
// types with type parameters mapped to the wildcard type, the most permissive instantiations
// possible (the wildcard type is assignable to and from all types). If those are not related,
- // then no instatiations will be and we can just return the false branch type.
+ // then no instantiations will be and we can just return the false branch type.
if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) {
return instantiateType(root.falseType, mapper);
}
@@ -10227,7 +10260,7 @@ namespace ts {
* this function should be called in a left folding style, with left = previous result of getSpreadType
* and right = the new element to be spread.
*/
- function getSpreadType(left: Type, right: Type, symbol: Symbol | undefined, typeFlags: TypeFlags, objectFlags: ObjectFlags): Type {
+ function getSpreadType(left: Type, right: Type, symbol: Symbol | undefined, typeFlags: TypeFlags, objectFlags: ObjectFlags, readonly: boolean): Type {
if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) {
return anyType;
}
@@ -10241,10 +10274,10 @@ namespace ts {
return left;
}
if (left.flags & TypeFlags.Union) {
- return mapType(left, t => getSpreadType(t, right, symbol, typeFlags, objectFlags));
+ return mapType(left, t => getSpreadType(t, right, symbol, typeFlags, objectFlags, readonly));
}
if (right.flags & TypeFlags.Union) {
- return mapType(right, t => getSpreadType(left, t, symbol, typeFlags, objectFlags));
+ return mapType(right, t => getSpreadType(left, t, symbol, typeFlags, objectFlags, readonly));
}
if (right.flags & (TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive | TypeFlags.Index)) {
return left;
@@ -10261,7 +10294,7 @@ namespace ts {
const types = (left).types;
const lastLeft = types[types.length - 1];
if (isNonGenericObjectType(lastLeft) && isNonGenericObjectType(right)) {
- return getIntersectionType(concatenate(types.slice(0, types.length - 1), [getSpreadType(lastLeft, right, symbol, typeFlags, objectFlags)]));
+ return getIntersectionType(concatenate(types.slice(0, types.length - 1), [getSpreadType(lastLeft, right, symbol, typeFlags, objectFlags, readonly)]));
}
}
return getIntersectionType([left, right]);
@@ -10286,7 +10319,7 @@ namespace ts {
skippedPrivateMembers.set(rightProp.escapedName, true);
}
else if (isSpreadableProperty(rightProp)) {
- members.set(rightProp.escapedName, getSpreadSymbol(rightProp));
+ members.set(rightProp.escapedName, getSpreadSymbol(rightProp, readonly));
}
}
@@ -10310,7 +10343,7 @@ namespace ts {
}
}
else {
- members.set(leftProp.escapedName, getSpreadSymbol(leftProp));
+ members.set(leftProp.escapedName, getSpreadSymbol(leftProp, readonly));
}
}
@@ -10319,8 +10352,8 @@ namespace ts {
members,
emptyArray,
emptyArray,
- getNonReadonlyIndexSignature(stringIndexInfo),
- getNonReadonlyIndexSignature(numberIndexInfo));
+ getIndexInfoWithReadonly(stringIndexInfo, readonly),
+ getIndexInfoWithReadonly(numberIndexInfo, readonly));
spread.flags |= TypeFlags.ContainsObjectLiteral | typeFlags;
spread.objectFlags |= ObjectFlags.ObjectLiteral | ObjectFlags.ContainsSpread | objectFlags;
return spread;
@@ -10332,14 +10365,13 @@ namespace ts {
!prop.declarations.some(decl => isClassLike(decl.parent));
}
- function getSpreadSymbol(prop: Symbol) {
- const isReadonly = isReadonlySymbol(prop);
+ function getSpreadSymbol(prop: Symbol, readonly: boolean) {
const isSetonlyAccessor = prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor);
- if (!isReadonly && !isSetonlyAccessor) {
+ if (!isSetonlyAccessor && readonly === isReadonlySymbol(prop)) {
return prop;
}
const flags = SymbolFlags.Property | (prop.flags & SymbolFlags.Optional);
- const result = createSymbol(flags, prop.escapedName);
+ const result = createSymbol(flags, prop.escapedName, readonly ? CheckFlags.Readonly : 0);
result.type = isSetonlyAccessor ? undefinedType : getTypeOfSymbol(prop);
result.declarations = prop.declarations;
result.nameType = prop.nameType;
@@ -10347,11 +10379,8 @@ namespace ts {
return result;
}
- function getNonReadonlyIndexSignature(index: IndexInfo | undefined) {
- if (index && index.isReadonly) {
- return createIndexInfo(index.type, /*isReadonly*/ false, index.declaration);
- }
- return index;
+ function getIndexInfoWithReadonly(info: IndexInfo | undefined, readonly: boolean) {
+ return info && info.isReadonly !== readonly ? createIndexInfo(info.type, readonly, info.declaration) : info;
}
function createLiteralType(flags: TypeFlags, value: string | number | PseudoBigInt, symbol: Symbol | undefined) {
@@ -10413,6 +10442,7 @@ namespace ts {
function createUniqueESSymbolType(symbol: Symbol) {
const type = createType(TypeFlags.UniqueESSymbol);
type.symbol = symbol;
+ type.escapedName = `__@${type.symbol.escapedName}@${getSymbolId(type.symbol)}` as __String;
return type;
}
@@ -10633,7 +10663,11 @@ namespace ts {
}
function getRestrictiveTypeParameter(tp: TypeParameter) {
- return !tp.constraint ? tp : tp.restrictiveInstantiation || (tp.restrictiveInstantiation = createTypeParameter(tp.symbol));
+ return tp.constraint === unknownType ? tp : tp.restrictiveInstantiation || (
+ tp.restrictiveInstantiation = createTypeParameter(tp.symbol),
+ (tp.restrictiveInstantiation as TypeParameter).constraint = unknownType,
+ tp.restrictiveInstantiation
+ );
}
function restrictiveMapper(type: Type) {
@@ -10707,7 +10741,7 @@ namespace ts {
}
// Keep the flags from the symbol we're instantiating. Mark that is instantiated, and
// also transient so that we can just store data on it directly.
- const result = createSymbol(symbol.flags, symbol.escapedName, CheckFlags.Instantiated | getCheckFlags(symbol) & (CheckFlags.Late | CheckFlags.OptionalParameter | CheckFlags.RestParameter));
+ const result = createSymbol(symbol.flags, symbol.escapedName, CheckFlags.Instantiated | getCheckFlags(symbol) & (CheckFlags.Readonly | CheckFlags.Late | CheckFlags.OptionalParameter | CheckFlags.RestParameter));
result.declarations = symbol.declarations;
result.parent = symbol.parent;
result.target = symbol;
@@ -10838,11 +10872,11 @@ namespace ts {
return errorType;
}
type.instantiating = true;
+ const modifiers = getMappedTypeModifiers(type);
const result = mapType(mappedTypeVariable, t => {
if (t.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection) && t !== wildcardType) {
const replacementMapper = createReplacementMapper(typeVariable, t, mapper);
- return isArrayType(t) ? createArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper)) :
- isReadonlyArrayType(t) ? createReadonlyArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper)) :
+ return isArrayType(t) ? createArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper), getModifiedReadonlyState(isReadonlyArrayType(t), modifiers)) :
isTupleType(t) ? instantiateMappedTupleType(t, type, replacementMapper) :
instantiateAnonymousType(type, replacementMapper);
}
@@ -10855,6 +10889,10 @@ namespace ts {
return instantiateAnonymousType(type, mapper);
}
+ function getModifiedReadonlyState(state: boolean, modifiers: MappedTypeModifiers) {
+ return modifiers & MappedTypeModifiers.IncludeReadonly ? true : modifiers & MappedTypeModifiers.ExcludeReadonly ? false : state;
+ }
+
function instantiateMappedTupleType(tupleType: TupleTypeReference, mappedType: MappedType, mapper: TypeMapper) {
const minLength = tupleType.target.minLength;
const elementTypes = map(tupleType.typeArguments || emptyArray, (_, i) =>
@@ -10863,7 +10901,8 @@ namespace ts {
const newMinLength = modifiers & MappedTypeModifiers.IncludeOptional ? 0 :
modifiers & MappedTypeModifiers.ExcludeOptional ? getTypeReferenceArity(tupleType) - (tupleType.target.hasRestElement ? 1 : 0) :
minLength;
- return createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, tupleType.target.associatedNames);
+ const newReadonly = getModifiedReadonlyState(tupleType.target.readonly, modifiers);
+ return createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, newReadonly, tupleType.target.associatedNames);
}
function instantiateMappedTypeTemplate(type: MappedType, key: Type, isOptional: boolean, mapper: TypeMapper) {
@@ -10936,6 +10975,7 @@ namespace ts {
// We have reached 50 recursive type instantiations and there is a very high likelyhood we're dealing
// with a combination of infinite generic types that perpetually generate new type identities. We stop
// the recursion here by yielding the error type.
+ error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite);
return errorType;
}
instantiationDepth++;
@@ -11300,7 +11340,7 @@ namespace ts {
}
if (resultObj.error) {
const reportedDiag = resultObj.error;
- const propertyName = isTypeUsableAsLateBoundName(nameType) ? getLateBoundNameFromType(nameType) : undefined;
+ const propertyName = isTypeUsableAsPropertyName(nameType) ? getPropertyNameFromType(nameType) : undefined;
const targetProp = propertyName !== undefined ? getPropertyOfType(target, propertyName) : undefined;
let issuedElaboration = false;
@@ -12725,7 +12765,7 @@ namespace ts {
errorInfo = saveErrorInfo;
}
}
- else if (isTupleType(source) && (isArrayType(target) || isReadonlyArrayType(target)) || isArrayType(source) && isReadonlyArrayType(target)) {
+ else if (isReadonlyArrayType(target) ? isArrayType(source) || isTupleType(source) : isArrayType(target) && isTupleType(source) && !source.target.readonly) {
return isRelatedTo(getIndexTypeOfType(source, IndexKind.Number) || anyType, getIndexTypeOfType(target, IndexKind.Number) || anyType, reportErrors);
}
// Even if relationship doesn't hold for unions, intersections, or generic type references,
@@ -13266,11 +13306,8 @@ namespace ts {
}
function getVariances(type: GenericType): Variance[] {
- if (!strictFunctionTypes) {
- return emptyArray;
- }
- if (type === globalArrayType || type === globalReadonlyArrayType) {
- // Arrays are known to be covariant, no need to spend time computing this (emptyArray implies covariance for all parameters)
+ // Arrays and tuples are known to be covariant, no need to spend time computing this (emptyArray implies covariance for all parameters)
+ if (!strictFunctionTypes || type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple) {
return emptyArray;
}
return getVariancesWorker(type.typeParameters, type, getMarkerTypeReference);
@@ -13560,7 +13597,7 @@ namespace ts {
}
function isArrayType(type: Type): boolean {
- return !!(getObjectFlags(type) & ObjectFlags.Reference) && (type).target === globalArrayType;
+ return !!(getObjectFlags(type) & ObjectFlags.Reference) && ((type).target === globalArrayType || (type).target === globalReadonlyArrayType);
}
function isReadonlyArrayType(type: Type): boolean {
@@ -13574,8 +13611,7 @@ namespace ts {
function isArrayLikeType(type: Type): boolean {
// A type is array-like if it is a reference to the global Array or global ReadonlyArray type,
// or if it is not the undefined or null type and if it is assignable to ReadonlyArray
- return getObjectFlags(type) & ObjectFlags.Reference && ((type).target === globalArrayType || (type).target === globalReadonlyArrayType) ||
- !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType);
+ return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType);
}
function isEmptyArrayLiteralType(type: Type): boolean {
@@ -14077,7 +14113,9 @@ namespace ts {
function mapper(t: Type): Type {
for (let i = 0; i < inferences.length; i++) {
if (t === inferences[i].typeParameter) {
- inferences[i].isFixed = true;
+ if (!(context.flags & InferenceFlags.NoFixing)) {
+ inferences[i].isFixed = true;
+ }
return getInferredType(context, i);
}
}
@@ -14184,16 +14222,13 @@ namespace ts {
// For arrays and tuples we infer new arrays and tuples where the reverse mapping has been
// applied to the element type(s).
if (isArrayType(source)) {
- return createArrayType(inferReverseMappedType((source).typeArguments![0], target, constraint));
- }
- if (isReadonlyArrayType(source)) {
- return createReadonlyArrayType(inferReverseMappedType((source).typeArguments![0], target, constraint));
+ return createArrayType(inferReverseMappedType((source).typeArguments![0], target, constraint), isReadonlyArrayType(source));
}
if (isTupleType(source)) {
const elementTypes = map(source.typeArguments || emptyArray, t => inferReverseMappedType(t, target, constraint));
const minLength = getMappedTypeModifiers(target) & MappedTypeModifiers.IncludeOptional ?
getTypeReferenceArity(source) - (source.target.hasRestElement ? 1 : 0) : source.target.minLength;
- return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.associatedNames);
+ return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.readonly, source.target.associatedNames);
}
// For all other object types we infer a new object type where the reverse mapping has been
// applied to the type of each property.
@@ -14470,7 +14505,7 @@ namespace ts {
// that is _too good_ in projects with complicated constraints (eg, fp-ts). In such cases, if we did not limit ourselves
// here, we might produce more valid inferences for types, causing us to do more checks and perform more instantiations
// (in addition to the extra stack depth here) which, in turn, can push the already close process over its limit.
- // TL;DR: If we ever become generally more memory efficienct (or our resource budget ever increases), we should just
+ // TL;DR: If we ever become generally more memory efficient (or our resource budget ever increases), we should just
// remove this `allowComplexConstraintInference` flag.
allowComplexConstraintInference = false;
return inferFromTypes(apparentSource, target);
@@ -14567,7 +14602,12 @@ namespace ts {
priority |= InferencePriority.MappedTypeConstraint;
inferFromTypes(getIndexType(source), constraintType);
priority = savePriority;
- inferFromTypes(getUnionType(map(getPropertiesOfType(source), getTypeOfSymbol)), getTemplateTypeFromMappedType(target));
+ const valueTypes = compact([
+ getIndexTypeOfType(source, IndexKind.String),
+ getIndexTypeOfType(source, IndexKind.Number),
+ ...map(getPropertiesOfType(source), getTypeOfSymbol)
+ ]);
+ inferFromTypes(getUnionType(valueTypes), getTemplateTypeFromMappedType(target));
return true;
}
return false;
@@ -14596,18 +14636,18 @@ namespace ts {
}
function inferFromProperties(source: Type, target: Type) {
- if (isTupleType(source)) {
+ if (isArrayType(source) || isTupleType(source)) {
if (isTupleType(target)) {
- const sourceLength = getLengthOfTupleType(source);
+ const sourceLength = isTupleType(source) ? getLengthOfTupleType(source) : 0;
const targetLength = getLengthOfTupleType(target);
- const sourceRestType = getRestTypeOfTupleType(source);
+ const sourceRestType = isTupleType(source) ? getRestTypeOfTupleType(source) : getElementTypeOfArrayType(source);
const targetRestType = getRestTypeOfTupleType(target);
const fixedLength = targetLength < sourceLength || sourceRestType ? targetLength : sourceLength;
for (let i = 0; i < fixedLength; i++) {
- inferFromTypes(i < sourceLength ? source.typeArguments![i] : sourceRestType!, target.typeArguments![i]);
+ inferFromTypes(i < sourceLength ? (source).typeArguments![i] : sourceRestType!, target.typeArguments![i]);
}
if (targetRestType) {
- const types = fixedLength < sourceLength ? source.typeArguments!.slice(fixedLength, sourceLength) : [];
+ const types = fixedLength < sourceLength ? (source).typeArguments!.slice(fixedLength, sourceLength) : [];
if (sourceRestType) {
types.push(sourceRestType);
}
@@ -14801,10 +14841,12 @@ namespace ts {
const constraint = getConstraintOfTypeParameter(inference.typeParameter);
if (constraint) {
+ context.flags |= InferenceFlags.NoFixing;
const instantiatedConstraint = instantiateType(constraint, context);
if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
inference.inferredType = inferredType = instantiatedConstraint;
}
+ context.flags &= ~InferenceFlags.NoFixing;
}
}
@@ -14831,17 +14873,23 @@ namespace ts {
case "console":
return Diagnostics.Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_include_dom;
case "$":
- return Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_types_Slashjquery_and_then_add_jquery_to_the_types_field_in_your_tsconfig;
+ return compilerOptions.types
+ ? Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_types_Slashjquery_and_then_add_jquery_to_the_types_field_in_your_tsconfig
+ : Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_types_Slashjquery;
case "describe":
case "suite":
case "it":
case "test":
- return Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_types_Slashjest_or_npm_i_types_Slashmocha_and_then_add_jest_or_mocha_to_the_types_field_in_your_tsconfig;
+ return compilerOptions.types
+ ? Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_types_Slashjest_or_npm_i_types_Slashmocha_and_then_add_jest_or_mocha_to_the_types_field_in_your_tsconfig
+ : Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_types_Slashjest_or_npm_i_types_Slashmocha;
case "process":
case "require":
case "Buffer":
case "module":
- return Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_types_Slashnode_and_then_add_node_to_the_types_field_in_your_tsconfig;
+ return compilerOptions.types
+ ? Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_types_Slashnode_and_then_add_node_to_the_types_field_in_your_tsconfig
+ : Diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_types_Slashnode;
case "Map":
case "Set":
case "Promise":
@@ -15172,7 +15220,9 @@ namespace ts {
}
function getTypeOfDestructuredProperty(type: Type, name: PropertyName) {
- const text = getTextOfPropertyName(name);
+ const nameType = getLiteralTypeFromPropertyName(name);
+ if (!isTypeUsableAsPropertyName(nameType)) return errorType;
+ const text = getPropertyNameFromType(nameType);
return getConstraintForLocation(getTypeOfPropertyOfType(type, text), name) ||
isNumericLiteralName(text) && getIndexTypeOfType(type, IndexKind.Number) ||
getIndexTypeOfType(type, IndexKind.String) ||
@@ -16018,6 +16068,15 @@ namespace ts {
assumeTrue = !assumeTrue;
}
const valueType = getTypeOfExpression(value);
+ if ((type.flags & TypeFlags.Unknown) && (operator === SyntaxKind.EqualsEqualsEqualsToken) && assumeTrue) {
+ if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) {
+ return valueType;
+ }
+ if (valueType.flags & TypeFlags.Object) {
+ return nonPrimitiveType;
+ }
+ return type;
+ }
if (valueType.flags & TypeFlags.Nullable) {
if (!strictNullChecks) {
return type;
@@ -16972,7 +17031,7 @@ namespace ts {
}
function isInConstructorArgumentInitializer(node: Node, constructorDecl: Node): boolean {
- return !!findAncestor(node, n => n === constructorDecl ? "quit" : n.kind === SyntaxKind.Parameter);
+ return !!findAncestor(node, n => isFunctionLikeDeclaration(n) ? "quit" : n.kind === SyntaxKind.Parameter && n.parent === constructorDecl);
}
function checkSuperExpression(node: Node): Type {
@@ -17304,9 +17363,10 @@ namespace ts {
const parentDeclaration = declaration.parent.parent;
const name = declaration.propertyName || declaration.name;
const parentType = getContextualTypeForVariableLikeDeclaration(parentDeclaration);
- if (parentType && !isBindingPattern(name)) {
- const text = getTextOfPropertyName(name);
- if (text !== undefined) {
+ if (parentType && !isBindingPattern(name) && !isComputedNonLiteralName(name)) {
+ const nameType = getLiteralTypeFromPropertyName(name);
+ if (isTypeUsableAsPropertyName(nameType)) {
+ const text = getPropertyNameFromType(nameType);
return getTypeOfPropertyOfType(parentType, text);
}
}
@@ -17699,19 +17759,49 @@ namespace ts {
// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
// be "pushed" onto a node using the contextualType property.
function getApparentTypeOfContextualType(node: Expression): Type | undefined {
- let contextualType = getContextualType(node);
- contextualType = contextualType && mapType(contextualType, getApparentType);
- if (contextualType && contextualType.flags & TypeFlags.Union) {
- if (isObjectLiteralExpression(node)) {
- return discriminateContextualTypeByObjectMembers(node, contextualType as UnionType);
+ const contextualType = instantiateContextualType(getContextualType(node), node);
+ if (contextualType) {
+ const apparentType = mapType(contextualType, getApparentType, /*noReductions*/ true);
+ if (apparentType.flags & TypeFlags.Union) {
+ if (isObjectLiteralExpression(node)) {
+ return discriminateContextualTypeByObjectMembers(node, apparentType as UnionType);
+ }
+ else if (isJsxAttributes(node)) {
+ return discriminateContextualTypeByJSXAttributes(node, apparentType as UnionType);
+ }
}
- else if (isJsxAttributes(node)) {
- return discriminateContextualTypeByJSXAttributes(node, contextualType as UnionType);
+ return apparentType;
+ }
+ }
+
+ // If the given contextual type contains instantiable types and if a mapper representing
+ // return type inferences is available, instantiate those types using that mapper.
+ function instantiateContextualType(contextualType: Type | undefined, node: Expression): Type | undefined {
+ if (contextualType && maybeTypeOfKind(contextualType, TypeFlags.Instantiable)) {
+ const returnMapper = (getContextualMapper(node)).returnMapper;
+ if (returnMapper) {
+ return instantiateInstantiableTypes(contextualType, returnMapper);
}
}
return contextualType;
}
+ // This function is similar to instantiateType, except that (a) it only instantiates types that
+ // are classified as instantiable (i.e. it doesn't instantiate object types), and (b) it performs
+ // no reductions on instantiated union types.
+ function instantiateInstantiableTypes(type: Type, mapper: TypeMapper): Type {
+ if (type.flags & TypeFlags.Instantiable) {
+ return instantiateType(type, mapper);
+ }
+ if (type.flags & TypeFlags.Union) {
+ return getUnionType(map((type).types, t => instantiateInstantiableTypes(t, mapper)), UnionReduction.None);
+ }
+ if (type.flags & TypeFlags.Intersection) {
+ return getIntersectionType(map((type).types, t => instantiateInstantiableTypes(t, mapper)));
+ }
+ return type;
+ }
+
/**
* Woah! Do you really want to use this function?
*
@@ -17757,7 +17847,7 @@ namespace ts {
return getContextualTypeForArgument(parent, node);
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.AsExpression:
- return getTypeFromTypeNode((parent).type);
+ return isConstTypeReference((parent).type) ? undefined : getTypeFromTypeNode((parent).type);
case SyntaxKind.BinaryExpression:
return getContextualTypeForBinaryOperand(node);
case SyntaxKind.PropertyAssignment:
@@ -18043,6 +18133,7 @@ namespace ts {
const elementTypes: Type[] = [];
const inDestructuringPattern = isAssignmentTarget(node);
const contextualType = getApparentTypeOfContextualType(node);
+ const inConstContext = isConstContext(node);
for (let index = 0; index < elementCount; index++) {
const e = elements[index];
if (inDestructuringPattern && e.kind === SyntaxKind.SpreadElement) {
@@ -18085,21 +18176,23 @@ namespace ts {
type.pattern = node;
return type;
}
- else if (tupleResult = getArrayLiteralTupleTypeIfApplicable(elementTypes, contextualType, hasRestElement, elementCount)) {
+ else if (tupleResult = getArrayLiteralTupleTypeIfApplicable(elementTypes, contextualType, hasRestElement, elementCount, inConstContext)) {
return tupleResult;
}
else if (forceTuple) {
return createTupleType(elementTypes, minLength, hasRestElement);
}
}
- return getArrayLiteralType(elementTypes, UnionReduction.Subtype);
+ return createArrayType(elementTypes.length ?
+ getUnionType(elementTypes, UnionReduction.Subtype) :
+ strictNullChecks ? implicitNeverType : undefinedWideningType, inConstContext);
}
- function getArrayLiteralTupleTypeIfApplicable(elementTypes: Type[], contextualType: Type | undefined, hasRestElement: boolean, elementCount = elementTypes.length) {
+ function getArrayLiteralTupleTypeIfApplicable(elementTypes: Type[], contextualType: Type | undefined, hasRestElement: boolean, elementCount = elementTypes.length, readonly = false) {
// Infer a tuple type when the contextual type is or contains a tuple-like type
- if (contextualType && forEachType(contextualType, isTupleLikeType)) {
+ if (readonly || (contextualType && forEachType(contextualType, isTupleLikeType))) {
const minLength = elementCount - (hasRestElement ? 1 : 0);
- const pattern = contextualType.pattern;
+ const pattern = contextualType && contextualType.pattern;
// If array literal is contextually typed by a binding pattern or an assignment pattern, pad the resulting
// tuple type with the corresponding binding or assignment element types to make the lengths equal.
if (!hasRestElement && pattern && (pattern.kind === SyntaxKind.ArrayBindingPattern || pattern.kind === SyntaxKind.ArrayLiteralExpression)) {
@@ -18117,16 +18210,10 @@ namespace ts {
}
}
}
- return createTupleType(elementTypes, minLength, hasRestElement);
+ return createTupleType(elementTypes, minLength, hasRestElement, readonly);
}
}
- function getArrayLiteralType(elementTypes: Type[], unionReduction = UnionReduction.Literal) {
- return createArrayType(elementTypes.length ?
- getUnionType(elementTypes, unionReduction) :
- strictNullChecks ? implicitNeverType : undefinedWideningType);
- }
-
function isNumericName(name: DeclarationName): boolean {
switch (name.kind) {
case SyntaxKind.ComputedPropertyName:
@@ -18195,15 +18282,15 @@ namespace ts {
return links.resolvedType;
}
- function getObjectLiteralIndexInfo(propertyNodes: NodeArray, offset: number, properties: Symbol[], kind: IndexKind): IndexInfo {
+ function getObjectLiteralIndexInfo(node: ObjectLiteralExpression, offset: number, properties: Symbol[], kind: IndexKind): IndexInfo {
const propTypes: Type[] = [];
for (let i = 0; i < properties.length; i++) {
- if (kind === IndexKind.String || isNumericName(propertyNodes[i + offset].name!)) {
+ if (kind === IndexKind.String || isNumericName(node.properties[i + offset].name!)) {
propTypes.push(getTypeOfSymbol(properties[i]));
}
}
const unionType = propTypes.length ? getUnionType(propTypes, UnionReduction.Subtype) : undefinedType;
- return createIndexInfo(unionType, /*isReadonly*/ false);
+ return createIndexInfo(unionType, isConstContext(node));
}
function getImmediateAliasedSymbol(symbol: Symbol): Symbol | undefined {
@@ -18231,6 +18318,8 @@ namespace ts {
const contextualType = getApparentTypeOfContextualType(node);
const contextualTypeHasPattern = contextualType && contextualType.pattern &&
(contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression);
+ const inConstContext = isConstContext(node);
+ const checkFlags = inConstContext ? CheckFlags.Readonly : 0;
const isInJavascript = isInJSFile(node) && !isInJsonFile(node);
const enumTag = getJSDocEnumTag(node);
const isJSObjectLiteral = !contextualType && isInJavascript && !enumTag;
@@ -18263,11 +18352,10 @@ namespace ts {
}
}
typeFlags |= type.flags;
- const nameType = computedNameType && computedNameType.flags & TypeFlags.StringOrNumberLiteralOrUnique ?
- computedNameType : undefined;
+ const nameType = computedNameType && isTypeUsableAsPropertyName(computedNameType) ? computedNameType : undefined;
const prop = nameType ?
- createSymbol(SymbolFlags.Property | member.flags, getLateBoundNameFromType(nameType), CheckFlags.Late) :
- createSymbol(SymbolFlags.Property | member.flags, member.escapedName);
+ createSymbol(SymbolFlags.Property | member.flags, getPropertyNameFromType(nameType), checkFlags | CheckFlags.Late) :
+ createSymbol(SymbolFlags.Property | member.flags, member.escapedName, checkFlags);
if (nameType) {
prop.nameType = nameType;
}
@@ -18311,7 +18399,7 @@ namespace ts {
checkExternalEmitHelpers(memberDecl, ExternalEmitHelpers.Assign);
}
if (propertiesArray.length > 0) {
- spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, propagatedFlags, ObjectFlags.FreshLiteral);
+ spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, propagatedFlags, ObjectFlags.FreshLiteral, inConstContext);
propertiesArray = [];
propertiesTable = createSymbolTable();
hasComputedStringProperty = false;
@@ -18323,7 +18411,7 @@ namespace ts {
error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
return errorType;
}
- spread = getSpreadType(spread, type, node.symbol, propagatedFlags, ObjectFlags.FreshLiteral);
+ spread = getSpreadType(spread, type, node.symbol, propagatedFlags, ObjectFlags.FreshLiteral, inConstContext);
offset = i + 1;
continue;
}
@@ -18373,7 +18461,7 @@ namespace ts {
if (spread !== emptyObjectType) {
if (propertiesArray.length > 0) {
- spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, propagatedFlags, ObjectFlags.FreshLiteral);
+ spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, propagatedFlags, ObjectFlags.FreshLiteral, inConstContext);
}
return spread;
}
@@ -18381,8 +18469,8 @@ namespace ts {
return createObjectLiteralType();
function createObjectLiteralType() {
- const stringIndexInfo = hasComputedStringProperty ? getObjectLiteralIndexInfo(node.properties, offset, propertiesArray, IndexKind.String) : undefined;
- const numberIndexInfo = hasComputedNumberProperty ? getObjectLiteralIndexInfo(node.properties, offset, propertiesArray, IndexKind.Number) : undefined;
+ const stringIndexInfo = hasComputedStringProperty ? getObjectLiteralIndexInfo(node, offset, propertiesArray, IndexKind.String) : undefined;
+ const numberIndexInfo = hasComputedNumberProperty ? getObjectLiteralIndexInfo(node, offset, propertiesArray, IndexKind.Number) : undefined;
const result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
result.flags |= TypeFlags.ContainsObjectLiteral | typeFlags & TypeFlags.PropagatingFlags;
result.objectFlags |= ObjectFlags.ObjectLiteral | freshObjectLiteralFlag;
@@ -18512,7 +18600,7 @@ namespace ts {
else {
Debug.assert(attributeDecl.kind === SyntaxKind.JsxSpreadAttribute);
if (attributesTable.size > 0) {
- spread = getSpreadType(spread, createJsxAttributesType(), attributes.symbol, typeFlags, objectFlags);
+ spread = getSpreadType(spread, createJsxAttributesType(), attributes.symbol, typeFlags, objectFlags, /*readonly*/ false);
attributesTable = createSymbolTable();
}
const exprType = checkExpressionCached(attributeDecl.expression, checkMode);
@@ -18520,7 +18608,7 @@ namespace ts {
hasSpreadAnyType = true;
}
if (isValidSpreadType(exprType)) {
- spread = getSpreadType(spread, exprType, attributes.symbol, typeFlags, objectFlags);
+ spread = getSpreadType(spread, exprType, attributes.symbol, typeFlags, objectFlags, /*readonly*/ false);
}
else {
typeToIntersect = typeToIntersect ? getIntersectionType([typeToIntersect, exprType]) : exprType;
@@ -18530,7 +18618,7 @@ namespace ts {
if (!hasSpreadAnyType) {
if (attributesTable.size > 0) {
- spread = getSpreadType(spread, createJsxAttributesType(), attributes.symbol, typeFlags, objectFlags);
+ spread = getSpreadType(spread, createJsxAttributesType(), attributes.symbol, typeFlags, objectFlags, /*readonly*/ false);
}
}
@@ -18562,7 +18650,7 @@ namespace ts {
const childPropMap = createSymbolTable();
childPropMap.set(jsxChildrenPropertyName, childrenPropSymbol);
spread = getSpreadType(spread, createAnonymousType(attributes.symbol, childPropMap, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined),
- attributes.symbol, typeFlags, objectFlags);
+ attributes.symbol, typeFlags, objectFlags, /*readonly*/ false);
}
}
@@ -18892,7 +18980,7 @@ namespace ts {
// if jsx emit was not react as there wont be error being emitted
reactSym.isReferenced = SymbolFlags.All;
- // If react symbol is alias, mark it as refereced
+ // If react symbol is alias, mark it as referenced
if (reactSym.flags & SymbolFlags.Alias && !isConstEnumOrConstEnumOnlyModule(resolveAlias(reactSym))) {
markAliasSymbolAsReferenced(reactSym);
}
@@ -19267,7 +19355,19 @@ namespace ts {
case SyntaxKind.PropertyDeclaration:
return true;
case SyntaxKind.PropertyAssignment:
- // We might be in `a = { b: this.b }`, so keep looking. See `tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts`.
+ case SyntaxKind.MethodDeclaration:
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ case SyntaxKind.SpreadAssignment:
+ case SyntaxKind.ComputedPropertyName:
+ case SyntaxKind.TemplateSpan:
+ case SyntaxKind.JsxExpression:
+ case SyntaxKind.JsxAttribute:
+ case SyntaxKind.JsxAttributes:
+ case SyntaxKind.JsxSpreadAttribute:
+ case SyntaxKind.JsxOpeningElement:
+ case SyntaxKind.ExpressionWithTypeArguments:
+ case SyntaxKind.HeritageClause:
return false;
default:
return isExpressionNode(node) ? false : "quit";
@@ -19852,6 +19952,9 @@ namespace ts {
const inferenceTargetType = getReturnTypeOfSignature(signature);
// Inferences made from return types have lower priority than all other inferences.
inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType);
+ // Create a type mapper for instantiating generic contextual types using the inferences made
+ // from the return type.
+ context.returnMapper = cloneTypeMapper(context);
}
}
@@ -21234,7 +21337,7 @@ namespace ts {
const anonymousSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type);
const defaultContainingObject = createAnonymousType(anonymousSymbol, memberTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined);
anonymousSymbol.type = defaultContainingObject;
- synthType.syntheticType = isValidSpreadType(type) ? getSpreadType(type, defaultContainingObject, anonymousSymbol, /*typeFLags*/ 0, /*objectFlags*/ 0) : defaultContainingObject;
+ synthType.syntheticType = isValidSpreadType(type) ? getSpreadType(type, defaultContainingObject, anonymousSymbol, /*typeFLags*/ 0, /*objectFlags*/ 0, /*readonly*/ false) : defaultContainingObject;
}
else {
synthType.syntheticType = type;
@@ -21286,12 +21389,39 @@ namespace ts {
return checkAssertionWorker(node, node.type, node.expression);
}
+ function isValidConstAssertionArgument(node: Node): boolean {
+ switch (node.kind) {
+ case SyntaxKind.StringLiteral:
+ case SyntaxKind.NoSubstitutionTemplateLiteral:
+ case SyntaxKind.NumericLiteral:
+ case SyntaxKind.BigIntLiteral:
+ case SyntaxKind.TrueKeyword:
+ case SyntaxKind.FalseKeyword:
+ case SyntaxKind.ArrayLiteralExpression:
+ case SyntaxKind.ObjectLiteralExpression:
+ return true;
+ case SyntaxKind.ParenthesizedExpression:
+ return isValidConstAssertionArgument((node).expression);
+ case SyntaxKind.PrefixUnaryExpression:
+ const op = (node).operator;
+ const arg = (node).operand;
+ return op === SyntaxKind.MinusToken && (arg.kind === SyntaxKind.NumericLiteral || arg.kind === SyntaxKind.BigIntLiteral) ||
+ op === SyntaxKind.PlusToken && arg.kind === SyntaxKind.NumericLiteral;
+ }
+ return false;
+ }
+
function checkAssertionWorker(errNode: Node, type: TypeNode, expression: UnaryExpression | Expression, checkMode?: CheckMode) {
- const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpression(expression, checkMode)));
-
+ let exprType = checkExpression(expression, checkMode);
+ if (isConstTypeReference(type)) {
+ if (!isValidConstAssertionArgument(expression)) {
+ error(expression, Diagnostics.A_const_assertion_can_only_be_applied_to_a_string_number_boolean_array_or_object_literal);
+ }
+ return getRegularTypeOfLiteralType(exprType);
+ }
checkSourceElement(type);
+ exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(exprType));
const targetType = getTypeFromTypeNode(type);
-
if (produceDiagnostics && targetType !== errorType) {
const widenedType = getWidenedType(exprType);
if (!isTypeComparableTo(targetType, widenedType)) {
@@ -21409,7 +21539,7 @@ namespace ts {
}
const minArgumentCount = getMinArgumentCount(source);
const minLength = minArgumentCount < start ? 0 : minArgumentCount - start;
- return createTupleType(types, minLength, !!restType, names);
+ return createTupleType(types, minLength, !!restType, /*readonly*/ false, names);
}
function getParameterCount(signature: Signature) {
@@ -22315,15 +22445,15 @@ namespace ts {
function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike, allProperties?: NodeArray, rightIsThis = false) {
if (property.kind === SyntaxKind.PropertyAssignment || property.kind === SyntaxKind.ShorthandPropertyAssignment) {
const name = property.name;
- const text = getTextOfPropertyName(name);
- if (text) {
+ const exprType = getLiteralTypeFromPropertyName(name);
+ if (isTypeUsableAsPropertyName(exprType)) {
+ const text = getPropertyNameFromType(exprType);
const prop = getPropertyOfType(objectLiteralType, text);
if (prop) {
markPropertyAsReferenced(prop, property, rightIsThis);
checkPropertyAccessibility(property, /*isSuper*/ false, objectLiteralType, prop);
}
}
- const exprType = getLiteralTypeFromPropertyName(name);
const elementType = getIndexedAccessType(objectLiteralType, exprType, name);
const type = getFlowTypeOfDestructuring(property, elementType);
return checkDestructuringAssignment(property.kind === SyntaxKind.ShorthandPropertyAssignment ? property : property.initializer, type);
@@ -22935,7 +23065,12 @@ namespace ts {
context.contextualMapper = contextualMapper;
const checkMode = contextualMapper === identityMapper ? CheckMode.SkipContextSensitive :
contextualMapper ? CheckMode.Inferential : CheckMode.Contextual;
- const result = checkExpression(node, checkMode);
+ const type = checkExpression(node, checkMode);
+ // We strip literal freshness when an appropriate contextual type is present such that contextually typed
+ // literals always preserve their literal types (otherwise they might widen during type inference). An alternative
+ // here would be to not mark contextually typed literals as fresh in the first place.
+ const result = maybeTypeOfKind(type, TypeFlags.Literal) && isLiteralOfContextualType(type, instantiateContextualType(contextualType, node)) ?
+ getRegularTypeOfLiteralType(type) : type;
context.contextualType = saveContextualType;
context.contextualMapper = saveContextualMapper;
return result;
@@ -23011,13 +23146,18 @@ namespace ts {
return false;
}
+ function isConstContext(node: Expression): boolean {
+ const parent = node.parent;
+ return isAssertionExpression(parent) && isConstTypeReference(parent.type) ||
+ (isParenthesizedExpression(parent) || isArrayLiteralExpression(parent) || isSpreadElement(parent)) && isConstContext(parent) ||
+ (isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent)) && isConstContext(parent.parent);
+ }
+
function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, contextualType?: Type, forceTuple?: boolean): Type {
- if (arguments.length === 2) {
- contextualType = getContextualType(node);
- }
const type = checkExpression(node, checkMode, forceTuple);
- return isTypeAssertion(node) ? type :
- getWidenedLiteralLikeTypeForContextualType(type, contextualType);
+ return isConstContext(node) ? getRegularTypeOfLiteralType(type) :
+ isTypeAssertion(node) ? type :
+ getWidenedLiteralLikeTypeForContextualType(type, instantiateContextualType(arguments.length === 2 ? getContextualType(node) : contextualType, node));
}
function checkPropertyAssignment(node: PropertyAssignment, checkMode?: CheckMode): Type {
@@ -23046,7 +23186,7 @@ namespace ts {
return instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode);
}
- function instantiateTypeWithSingleGenericCallSignature(node: Expression | MethodDeclaration, type: Type, checkMode?: CheckMode) {
+ function instantiateTypeWithSingleGenericCallSignature(node: Expression | MethodDeclaration | QualifiedName, type: Type, checkMode?: CheckMode) {
if (checkMode === CheckMode.Inferential) {
const signature = getSingleCallSignature(type);
if (signature && signature.typeParameters) {
@@ -23080,7 +23220,7 @@ namespace ts {
return getReturnTypeOfSignature(signature);
}
}
- else if (expr.kind === SyntaxKind.TypeAssertionExpression || expr.kind === SyntaxKind.AsExpression) {
+ else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) {
return getTypeFromTypeNode((expr).type);
}
// Otherwise simply call checkExpression. Ideally, the entire family of checkXXX functions
@@ -23116,18 +23256,14 @@ namespace ts {
// have the wildcard function type; this form of type check is used during overload resolution to exclude
// contextually typed function and arrow expressions in the initial phase.
function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type {
- let type: Type;
- if (node.kind === SyntaxKind.QualifiedName) {
- type = checkQualifiedName(node);
- }
- else {
- const uninstantiatedType = checkExpressionWorker(node, checkMode, forceTuple);
- type = instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode);
- }
-
+ const saveCurrentNode = currentNode;
+ currentNode = node;
+ const uninstantiatedType = checkExpressionWorker(node, checkMode, forceTuple);
+ const type = instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode);
if (isConstEnumObjectType(type)) {
checkConstEnumAccess(node, type);
}
+ currentNode = saveCurrentNode;
return type;
}
@@ -23163,7 +23299,7 @@ namespace ts {
return checkExpression(node.expression, checkMode);
}
- function checkExpressionWorker(node: Expression, checkMode: CheckMode | undefined, forceTuple?: boolean): Type {
+ function checkExpressionWorker(node: Expression | QualifiedName, checkMode: CheckMode | undefined, forceTuple?: boolean): Type {
switch (node.kind) {
case SyntaxKind.Identifier:
return checkIdentifier(node);
@@ -23196,6 +23332,8 @@ namespace ts {
return checkObjectLiteral(node, checkMode);
case SyntaxKind.PropertyAccessExpression:
return checkPropertyAccessExpression(node);
+ case SyntaxKind.QualifiedName:
+ return checkQualifiedName(node);
case SyntaxKind.ElementAccessExpression:
return checkIndexedAccess(node);
case SyntaxKind.CallExpression:
@@ -23319,7 +23457,7 @@ namespace ts {
// Only check rest parameter type if it's not a binding pattern. Since binding patterns are
// not allowed in a rest parameter, we already have an error from checkGrammarParameterList.
- if (node.dotDotDotToken && !isBindingPattern(node.name) && !isTypeAssignableTo(getTypeOfSymbol(node.symbol), anyArrayType)) {
+ if (node.dotDotDotToken && !isBindingPattern(node.name) && !isTypeAssignableTo(getTypeOfSymbol(node.symbol), anyReadonlyArrayType)) {
error(node, Diagnostics.A_rest_parameter_must_be_of_an_array_type);
}
}
@@ -25650,13 +25788,14 @@ namespace ts {
const parent = node.parent.parent;
const parentType = getTypeForBindingElementParent(parent);
const name = node.propertyName || node.name;
- if (!isBindingPattern(name)) {
- const nameText = getTextOfPropertyName(name);
- if (nameText) {
- const property = getPropertyOfType(parentType!, nameText); // TODO: GH#18217
+ if (parentType && !isBindingPattern(name)) {
+ const exprType = getLiteralTypeFromPropertyName(name);
+ if (isTypeUsableAsPropertyName(exprType)) {
+ const nameText = getPropertyNameFromType(exprType);
+ const property = getPropertyOfType(parentType, nameText);
if (property) {
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference.
- checkPropertyAccessibility(parent, !!parent.initializer && parent.initializer.kind === SyntaxKind.SuperKeyword, parentType!, property);
+ checkPropertyAccessibility(parent, !!parent.initializer && parent.initializer.kind === SyntaxKind.SuperKeyword, parentType, property);
}
}
}
@@ -27872,10 +28011,15 @@ namespace ts {
}
function checkSourceElement(node: Node | undefined): void {
- if (!node) {
- return;
+ if (node) {
+ const saveCurrentNode = currentNode;
+ currentNode = node;
+ checkSourceElementWorker(node);
+ currentNode = saveCurrentNode;
}
+ }
+ function checkSourceElementWorker(node: Node): void {
if (isInJSFile(node)) {
forEach((node as JSDocContainer).jsDoc, ({ tags }) => forEach(tags, checkSourceElement));
}
@@ -28133,32 +28277,36 @@ namespace ts {
function checkDeferredNodes(context: SourceFile) {
const links = getNodeLinks(context);
- if (!links.deferredNodes) {
- return;
+ if (links.deferredNodes) {
+ links.deferredNodes.forEach(checkDeferredNode);
}
- links.deferredNodes.forEach(node => {
- switch (node.kind) {
- case SyntaxKind.FunctionExpression:
- case SyntaxKind.ArrowFunction:
- case SyntaxKind.MethodDeclaration:
- case SyntaxKind.MethodSignature:
- checkFunctionExpressionOrObjectLiteralMethodDeferred(node);
- break;
- case SyntaxKind.GetAccessor:
- case SyntaxKind.SetAccessor:
- checkAccessorDeclaration(node);
- break;
- case SyntaxKind.ClassExpression:
- checkClassExpressionDeferred(node);
- break;
- case SyntaxKind.JsxSelfClosingElement:
- checkJsxSelfClosingElementDeferred(node);
- break;
- case SyntaxKind.JsxElement:
- checkJsxElementDeferred(node);
- break;
- }
- });
+ }
+
+ function checkDeferredNode(node: Node) {
+ const saveCurrentNode = currentNode;
+ currentNode = node;
+ switch (node.kind) {
+ case SyntaxKind.FunctionExpression:
+ case SyntaxKind.ArrowFunction:
+ case SyntaxKind.MethodDeclaration:
+ case SyntaxKind.MethodSignature:
+ checkFunctionExpressionOrObjectLiteralMethodDeferred(node);
+ break;
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ checkAccessorDeclaration(node);
+ break;
+ case SyntaxKind.ClassExpression:
+ checkClassExpressionDeferred(node);
+ break;
+ case SyntaxKind.JsxSelfClosingElement:
+ checkJsxSelfClosingElementDeferred(node);
+ break;
+ case SyntaxKind.JsxElement:
+ checkJsxElementDeferred(node);
+ break;
+ }
+ currentNode = saveCurrentNode;
}
function checkSourceFile(node: SourceFile) {
@@ -28249,7 +28397,7 @@ namespace ts {
throwIfNonDiagnosticsProducing();
if (sourceFile) {
// Some global diagnostics are deferred until they are needed and
- // may not be reported in the firt call to getGlobalDiagnostics.
+ // may not be reported in the first call to getGlobalDiagnostics.
// We should catch these changes and report them.
const previousGlobalDiagnostics = diagnostics.getGlobalDiagnostics();
const previousGlobalDiagnosticsSize = previousGlobalDiagnostics.length;
@@ -30661,6 +30809,11 @@ namespace ts {
return grammarErrorOnNode(node, Diagnostics.unique_symbol_types_are_not_allowed_here);
}
}
+ else if (node.operator === SyntaxKind.ReadonlyKeyword) {
+ if (node.type.kind !== SyntaxKind.ArrayType && node.type.kind !== SyntaxKind.TupleType) {
+ return grammarErrorOnFirstToken(node, Diagnostics.readonly_type_modifier_is_only_permitted_on_array_and_tuple_literal_types, tokenToString(SyntaxKind.SymbolKeyword));
+ }
+ }
}
function checkGrammarForInvalidDynamicName(node: DeclarationName, message: DiagnosticMessage) {
diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts
index 43275605e2..5f01e66e07 100644
--- a/src/compiler/commandLineParser.ts
+++ b/src/compiler/commandLineParser.ts
@@ -1935,7 +1935,7 @@ namespace ts {
function directoryOfCombinedPath(fileName: string, basePath: string) {
// Use the `getNormalizedAbsolutePath` function to avoid canonicalizing the path, as it must remain noncanonical
- // until consistient casing errors are reported
+ // until consistent casing errors are reported
return getDirectoryPath(getNormalizedAbsolutePath(fileName, basePath));
}
diff --git a/src/compiler/core.ts b/src/compiler/core.ts
index d4f5fe9664..a2f2b24550 100644
--- a/src/compiler/core.ts
+++ b/src/compiler/core.ts
@@ -1,7 +1,7 @@
namespace ts {
// WARNING: The script `configureNightly.ts` uses a regexp to parse out these values.
// If changing the text in this section, be sure to test `configureNightly` too.
- export const versionMajorMinor = "3.3";
+ export const versionMajorMinor = "3.4";
/** The version of the TypeScript compiler release */
export const version = `${versionMajorMinor}.0-dev`;
}
@@ -884,8 +884,11 @@ namespace ts {
/**
* Compacts an array, removing any falsey elements.
*/
- export function compact(array: T[]): T[];
- export function compact(array: ReadonlyArray): ReadonlyArray;
+ export function compact(array: (T | undefined | null | false | 0 | "")[]): T[];
+ export function compact(array: ReadonlyArray): ReadonlyArray;
+ // TSLint thinks these can be combined with the above - they cannot; they'd produce higher-priority inferences and prevent the falsey types from being stripped
+ export function compact(array: T[]): T[]; // tslint:disable-line unified-signatures
+ export function compact(array: ReadonlyArray): ReadonlyArray; // tslint:disable-line unified-signatures
export function compact(array: T[]): T[] {
let result: T[] | undefined;
if (array) {
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 65a1dc73fd..8607d16715 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -1019,6 +1019,14 @@
"category": "Error",
"code": 1353
},
+ "'readonly' type modifier is only permitted on array and tuple literal types.": {
+ "category": "Error",
+ "code": 1354
+ },
+ "A 'const' assertion can only be applied to a string, number, boolean, array, or object literal.": {
+ "category": "Error",
+ "code": 1355
+ },
"Duplicate identifier '{0}'.": {
"category": "Error",
@@ -2088,15 +2096,15 @@
"category": "Error",
"code": 2577
},
- "Cannot find name '{0}'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.": {
+ "Cannot find name '{0}'. Do you need to install type definitions for node? Try `npm i @types/node`.": {
"category": "Error",
"code": 2580
},
- "Cannot find name '{0}'. Do you need to install type definitions for jQuery? Try `npm i @types/jquery` and then add `jquery` to the types field in your tsconfig.": {
+ "Cannot find name '{0}'. Do you need to install type definitions for jQuery? Try `npm i @types/jquery`.": {
"category": "Error",
"code": 2581
},
- "Cannot find name '{0}'. Do you need to install type definitions for a test runner? Try `npm i @types/jest` or `npm i @types/mocha` and then add `jest` or `mocha` to the types field in your tsconfig.": {
+ "Cannot find name '{0}'. Do you need to install type definitions for a test runner? Try `npm i @types/jest` or `npm i @types/mocha`.": {
"category": "Error",
"code": 2582
},
@@ -2124,6 +2132,26 @@
"category": "Error",
"code": 2588
},
+ "Type instantiation is excessively deep and possibly infinite.": {
+ "category": "Error",
+ "code": 2589
+ },
+ "Expression produces a union type that is too complex to represent.": {
+ "category": "Error",
+ "code": 2590
+ },
+ "Cannot find name '{0}'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.": {
+ "category": "Error",
+ "code": 2591
+ },
+ "Cannot find name '{0}'. Do you need to install type definitions for jQuery? Try `npm i @types/jquery` and then add `jquery` to the types field in your tsconfig.": {
+ "category": "Error",
+ "code": 2592
+ },
+ "Cannot find name '{0}'. Do you need to install type definitions for a test runner? Try `npm i @types/jest` or `npm i @types/mocha` and then add `jest` or `mocha` to the types field in your tsconfig.": {
+ "category": "Error",
+ "code": 2593
+ },
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600
diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts
index ab15bccafb..00d8419205 100644
--- a/src/compiler/factory.ts
+++ b/src/compiler/factory.ts
@@ -876,8 +876,8 @@ namespace ts {
}
export function createTypeOperatorNode(type: TypeNode): TypeOperatorNode;
- export function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword, type: TypeNode): TypeOperatorNode;
- export function createTypeOperatorNode(operatorOrType: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | TypeNode, type?: TypeNode) {
+ export function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword, type: TypeNode): TypeOperatorNode;
+ export function createTypeOperatorNode(operatorOrType: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | TypeNode, type?: TypeNode) {
const node = createSynthesizedNode(SyntaxKind.TypeOperator) as TypeOperatorNode;
node.operator = typeof operatorOrType === "number" ? operatorOrType : SyntaxKind.KeyOfKeyword;
node.type = parenthesizeElementTypeMember(typeof operatorOrType === "number" ? type! : operatorOrType);
@@ -2874,7 +2874,7 @@ namespace ts {
return node.emitNode = { annotatedNodes: [node] } as EmitNode;
}
- const sourceFile = getSourceFileOfNode(node);
+ const sourceFile = getSourceFileOfNode(getParseTreeNode(getSourceFileOfNode(node)));
getOrCreateEmitNode(sourceFile).annotatedNodes!.push(node);
}
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 92792c7a1a..4aec4f5962 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -2966,6 +2966,7 @@ namespace ts {
case SyntaxKind.NumberKeyword:
case SyntaxKind.BigIntKeyword:
case SyntaxKind.BooleanKeyword:
+ case SyntaxKind.ReadonlyKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.UniqueKeyword:
case SyntaxKind.VoidKeyword:
@@ -3055,7 +3056,7 @@ namespace ts {
return finishNode(postfix);
}
- function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword) {
+ function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword) {
const node = createNode(SyntaxKind.TypeOperator);
parseExpected(operator);
node.operator = operator;
@@ -3077,6 +3078,7 @@ namespace ts {
switch (operator) {
case SyntaxKind.KeyOfKeyword:
case SyntaxKind.UniqueKeyword:
+ case SyntaxKind.ReadonlyKeyword:
return parseTypeOperator(operator);
case SyntaxKind.InferKeyword:
return parseInferType();
diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index e7c2295a0c..280f46fa44 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -230,18 +230,18 @@ namespace ts {
const readFileWithCache = (fileName: string): string | undefined => {
const key = toPath(fileName);
const value = readFileCache.get(key);
- if (value !== undefined) return value || undefined;
+ if (value !== undefined) return value !== false ? value : undefined;
return setReadFileCache(key, fileName);
};
const setReadFileCache = (key: Path, fileName: string) => {
const newValue = originalReadFile.call(host, fileName);
- readFileCache.set(key, newValue || false);
+ readFileCache.set(key, newValue !== undefined ? newValue : false);
return newValue;
};
host.readFile = fileName => {
const key = toPath(fileName);
const value = readFileCache.get(key);
- if (value !== undefined) return value; // could be .d.ts from output
+ if (value !== undefined) return value !== false ? value : undefined; // could be .d.ts from output
if (!fileExtensionIs(fileName, Extension.Json)) {
return originalReadFile.call(host, fileName);
}
diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts
index 4f3eba3729..d0ecc12985 100644
--- a/src/compiler/scanner.ts
+++ b/src/compiler/scanner.ts
@@ -661,8 +661,15 @@ namespace ts {
let pendingKind!: CommentKind;
let pendingHasTrailingNewLine!: boolean;
let hasPendingCommentRange = false;
- let collecting = trailing || pos === 0;
+ let collecting = trailing;
let accumulator = initial;
+ if (pos === 0) {
+ collecting = true;
+ const shebang = getShebang(text);
+ if (shebang) {
+ pos = shebang.length;
+ }
+ }
scan: while (pos >= 0 && pos < text.length) {
const ch = text.charCodeAt(pos);
switch (ch) {
diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts
index 7aebed5958..473f3ed0d1 100644
--- a/src/compiler/transformers/ts.ts
+++ b/src/compiler/transformers/ts.ts
@@ -1934,8 +1934,13 @@ namespace ts {
case SyntaxKind.ConditionalType:
return serializeTypeList([(node).trueType, (node).falseType]);
- case SyntaxKind.TypeQuery:
case SyntaxKind.TypeOperator:
+ if ((node).operator === SyntaxKind.ReadonlyKeyword) {
+ return serializeTypeNode((node).type);
+ }
+ break;
+
+ case SyntaxKind.TypeQuery:
case SyntaxKind.IndexedAccessType:
case SyntaxKind.MappedType:
case SyntaxKind.TypeLiteral:
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index c460b94834..ff5ce74d31 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -1233,7 +1233,7 @@ namespace ts {
export interface TypeOperatorNode extends TypeNode {
kind: SyntaxKind.TypeOperator;
- operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword;
+ operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword;
type: TypeNode;
}
@@ -3955,6 +3955,7 @@ namespace ts {
// Unique symbol types (TypeFlags.UniqueESSymbol)
export interface UniqueESSymbolType extends Type {
symbol: Symbol;
+ escapedName: __String;
}
export interface StringLiteralType extends LiteralType {
@@ -4062,6 +4063,7 @@ namespace ts {
export interface TupleType extends GenericType {
minLength: number;
hasRestElement: boolean;
+ readonly: boolean;
associatedNames?: __String[];
}
@@ -4339,6 +4341,7 @@ namespace ts {
None = 0, // No special inference behaviors
NoDefault = 1 << 0, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType)
AnyDefault = 1 << 1, // Infer anyType for no inferences (otherwise emptyObjectType)
+ NoFixing = 1 << 2, // Disable type parameter fixing
}
/**
@@ -4367,6 +4370,7 @@ namespace ts {
inferences: InferenceInfo[]; // Inferences made for each type parameter
flags: InferenceFlags; // Inference flags
compareTypes: TypeComparer; // Type comparer function
+ returnMapper?: TypeMapper; // Type mapper for inferences from return types (if any)
}
/* @internal */
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 33a902b803..bcaa4d5428 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -778,7 +778,8 @@ namespace ts {
case SyntaxKind.NoSubstitutionTemplateLiteral:
return escapeLeadingUnderscores(name.text);
case SyntaxKind.ComputedPropertyName:
- return isStringOrNumericLiteralLike(name.expression) ? escapeLeadingUnderscores(name.expression.text) : undefined!; // TODO: GH#18217 Almost all uses of this assume the result to be defined!
+ if (isStringOrNumericLiteralLike(name.expression)) return escapeLeadingUnderscores(name.expression.text);
+ return Debug.fail("Text of property name cannot be read from non-literal-valued ComputedPropertyNames");
default:
return Debug.assertNever(name);
}
@@ -5606,6 +5607,11 @@ namespace ts {
return node.kind === SyntaxKind.TypeAssertionExpression;
}
+ export function isConstTypeReference(node: Node) {
+ return isTypeReferenceNode(node) && isIdentifier(node.typeName) &&
+ node.typeName.escapedText === "const" && !node.typeArguments;
+ }
+
export function isParenthesizedExpression(node: Node): node is ParenthesizedExpression {
return node.kind === SyntaxKind.ParenthesizedExpression;
}
diff --git a/src/harness/fakes.ts b/src/harness/fakes.ts
index e8c8161705..093f57a112 100644
--- a/src/harness/fakes.ts
+++ b/src/harness/fakes.ts
@@ -375,6 +375,15 @@ namespace fakes {
}
}
+ export type ExpectedDiagnostic = [ts.DiagnosticMessage, ...(string | number)[]];
+ function expectedDiagnosticToText([message, ...args]: ExpectedDiagnostic) {
+ let text = ts.getLocaleSpecificMessage(message);
+ if (args.length) {
+ text = ts.formatStringFromArgs(text, args);
+ }
+ return text;
+ }
+
export class SolutionBuilderHost extends CompilerHost implements ts.SolutionBuilderHost {
createProgram = ts.createAbstractBuilder;
now() {
@@ -395,16 +404,10 @@ namespace fakes {
this.diagnostics.length = 0;
}
- assertDiagnosticMessages(...expected: ts.DiagnosticMessage[]) {
- const actual = this.diagnostics.slice();
- if (actual.length !== expected.length) {
- assert.fail(actual, expected, `Diagnostic arrays did not match - got\r\n${actual.map(a => " " + a.messageText).join("\r\n")}\r\nexpected\r\n${expected.map(e => " " + e.message).join("\r\n")}`);
- }
- for (let i = 0; i < actual.length; i++) {
- if (actual[i].code !== expected[i].code) {
- assert.fail(actual[i].messageText, expected[i].message, `Mismatched error code - expected diagnostic ${i} "${actual[i].messageText}" to match ${expected[i].message}`);
- }
- }
+ assertDiagnosticMessages(...expectedDiagnostics: ExpectedDiagnostic[]) {
+ const actual = this.diagnostics.slice().map(d => d.messageText as string);
+ const expected = expectedDiagnostics.map(expectedDiagnosticToText);
+ assert.deepEqual(actual, expected, "Diagnostic arrays did not match");
}
printDiagnostics(header = "== Diagnostics ==") {
diff --git a/src/lib/es2017.sharedmemory.d.ts b/src/lib/es2017.sharedmemory.d.ts
index 018a2f162a..1d052d6a98 100644
--- a/src/lib/es2017.sharedmemory.d.ts
+++ b/src/lib/es2017.sharedmemory.d.ts
@@ -103,7 +103,7 @@ interface Atomics {
* Wakes up sleeping agents that are waiting on the given index of the array, returning the
* number of agents that were awoken.
*/
- wake(typedArray: Int32Array, index: number, count: number): number;
+ notify(typedArray: Int32Array, index: number, count: number): number;
/**
* Stores the bitwise XOR of a value with the value at the given position in the array,
@@ -115,4 +115,4 @@ interface Atomics {
readonly [Symbol.toStringTag]: "Atomics";
}
-declare var Atomics: Atomics;
\ No newline at end of file
+declare var Atomics: Atomics;
diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts
index 51833bb2e6..6dccc08c4b 100644
--- a/src/server/editorServices.ts
+++ b/src/server/editorServices.ts
@@ -904,7 +904,7 @@ namespace ts.server {
getPreferences(file: NormalizedPath): protocol.UserPreferences {
const info = this.getScriptInfoForNormalizedPath(file);
- return info && info.getPreferences() || this.hostConfiguration.preferences;
+ return { ...this.hostConfiguration.preferences, ...info && info.getPreferences() };
}
getHostFormatCodeOptions(): FormatCodeSettings {
diff --git a/src/server/session.ts b/src/server/session.ts
index 2257c6740f..e5b128c984 100644
--- a/src/server/session.ts
+++ b/src/server/session.ts
@@ -1178,8 +1178,7 @@ namespace ts.server {
private getRenameInfo(args: protocol.FileLocationRequestArgs): RenameInfo {
const { file, project } = this.getFileAndProject(args);
const position = this.getPositionInFile(args, file);
- const preferences = this.getHostPreferences();
- return project.getLanguageService().getRenameInfo(file, position, { allowRenameOfImportPath: preferences.allowRenameOfImportPath });
+ return project.getLanguageService().getRenameInfo(file, position, { allowRenameOfImportPath: this.getPreferences(file).allowRenameOfImportPath });
}
private getProjects(args: protocol.FileRequestArgs, getScriptInfoEnsuringProjectsUptoDate?: boolean, ignoreNoProjectError?: boolean): Projects {
@@ -1234,12 +1233,12 @@ namespace ts.server {
{ fileName: args.file, pos: position },
!!args.findInStrings,
!!args.findInComments,
- this.getHostPreferences()
+ this.getPreferences(file)
);
if (!simplifiedResult) return locations;
const defaultProject = this.getDefaultProject(args);
- const renameInfo: protocol.RenameInfo = this.mapRenameInfo(defaultProject.getLanguageService().getRenameInfo(file, position, { allowRenameOfImportPath: this.getHostPreferences().allowRenameOfImportPath }), Debug.assertDefined(this.projectService.getScriptInfo(file)));
+ const renameInfo: protocol.RenameInfo = this.mapRenameInfo(defaultProject.getLanguageService().getRenameInfo(file, position, { allowRenameOfImportPath: this.getPreferences(file).allowRenameOfImportPath }), Debug.assertDefined(this.projectService.getScriptInfo(file)));
return { info: renameInfo, locs: this.toSpanGroups(locations) };
}
diff --git a/src/services/codefixes/inferFromUsage.ts b/src/services/codefixes/inferFromUsage.ts
index 8e5b962116..f47f618f79 100644
--- a/src/services/codefixes/inferFromUsage.ts
+++ b/src/services/codefixes/inferFromUsage.ts
@@ -274,7 +274,17 @@ namespace ts.codefix {
return !!merged;
}));
const tag = createJSDocComment(comments.join("\n"), createNodeArray([...(oldTags || emptyArray), ...unmergedNewTags]));
- changes.insertJsdocCommentBefore(sourceFile, parent, tag);
+ const jsDocNode = parent.kind === SyntaxKind.ArrowFunction ? getJsDocNodeForArrowFunction(parent) : parent;
+ jsDocNode.jsDoc = parent.jsDoc;
+ jsDocNode.jsDocCache = parent.jsDocCache;
+ changes.insertJsdocCommentBefore(sourceFile, jsDocNode, tag);
+ }
+
+ function getJsDocNodeForArrowFunction(signature: ArrowFunction): HasJSDoc {
+ if (signature.parent.kind === SyntaxKind.PropertyDeclaration) {
+ return signature.parent;
+ }
+ return signature.parent.parent;
}
function tryMergeJsdocTags(oldTag: JSDocTag, newTag: JSDocTag): JSDocTag | undefined {
diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts
index 52f3277574..f96c53ee20 100644
--- a/src/services/findAllReferences.ts
+++ b/src/services/findAllReferences.ts
@@ -41,6 +41,7 @@ namespace ts.FindAllReferences {
readonly implementations?: boolean;
/**
* True to opt in for enhanced renaming of shorthand properties and import/export specifiers.
+ * The options controls the behavior for the whole rename operation; it cannot be changed on a per-file basis.
* Default is false for backwards compatibility.
*/
readonly providePrefixAndSuffixTextForRename?: boolean;
diff --git a/src/testRunner/tsconfig.json b/src/testRunner/tsconfig.json
index 2e5341732b..849906e24c 100644
--- a/src/testRunner/tsconfig.json
+++ b/src/testRunner/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "../tsconfig-base",
+ "extends": "../tsconfig-noncomposite-base",
"compilerOptions": {
"outFile": "../../built/local/run.js",
"composite": false,
@@ -43,6 +43,7 @@
"unittests/asserts.ts",
"unittests/base64.ts",
"unittests/builder.ts",
+ "unittests/comments.ts",
"unittests/compilerCore.ts",
"unittests/convertToBase64.ts",
"unittests/customTransforms.ts",
diff --git a/src/testRunner/unittests/comments.ts b/src/testRunner/unittests/comments.ts
new file mode 100644
index 0000000000..59f3020c11
--- /dev/null
+++ b/src/testRunner/unittests/comments.ts
@@ -0,0 +1,32 @@
+namespace ts {
+ describe("comment parsing", () => {
+ const withShebang = `#! node
+/** comment */
+// another one
+;`;
+ const noShebang = `/** comment */
+// another one
+;`;
+ const withTrailing = `;/* comment */
+// another one
+`;
+ it("skips shebang", () => {
+ const result = getLeadingCommentRanges(withShebang, 0);
+ assert.isDefined(result);
+ assert.strictEqual(result!.length, 2);
+ });
+
+ it("treats all comments at start of file as leading comments", () => {
+ const result = getLeadingCommentRanges(noShebang, 0);
+ assert.isDefined(result);
+ assert.strictEqual(result!.length, 2);
+ });
+
+ it("returns leading comments if position is not 0", () => {
+ const result = getLeadingCommentRanges(withTrailing, 1);
+ assert.isDefined(result);
+ assert.strictEqual(result!.length, 1);
+ assert.strictEqual(result![0].kind, SyntaxKind.SingleLineCommentTrivia);
+ });
+ });
+}
diff --git a/src/testRunner/unittests/transform.ts b/src/testRunner/unittests/transform.ts
index 98c29025d5..376b7d16ec 100644
--- a/src/testRunner/unittests/transform.ts
+++ b/src/testRunner/unittests/transform.ts
@@ -400,6 +400,46 @@ namespace Foo {
}
}).outputText;
});
+
+ // https://github.com/Microsoft/TypeScript/issues/24709
+ testBaseline("issue24709", () => {
+ const fs = vfs.createFromFileSystem(Harness.IO, /*caseSensitive*/ true);
+ const transformed = transform(createSourceFile("source.ts", "class X { echo(x: string) { return x; } }", ScriptTarget.ES3), [transformSourceFile]);
+ const transformedSourceFile = transformed.transformed[0];
+ transformed.dispose();
+ const host = new fakes.CompilerHost(fs);
+ host.getSourceFile = () => transformedSourceFile;
+ const program = createProgram(["source.ts"], {
+ target: ScriptTarget.ES3,
+ module: ModuleKind.None,
+ noLib: true
+ }, host);
+ program.emit(transformedSourceFile, (_p, s, b) => host.writeFile("source.js", s, b));
+ return host.readFile("source.js")!.toString();
+
+ function transformSourceFile(context: TransformationContext) {
+ const visitor: Visitor = (node) => {
+ if (isMethodDeclaration(node)) {
+ return updateMethod(
+ node,
+ node.decorators,
+ node.modifiers,
+ node.asteriskToken,
+ createIdentifier("foobar"),
+ node.questionToken,
+ node.typeParameters,
+ node.parameters,
+ node.type,
+ node.body,
+ );
+ }
+ return visitEachChild(node, visitor, context);
+ };
+ return (node: SourceFile) => visitNode(node, visitor);
+ }
+
+ });
+
});
}
diff --git a/src/testRunner/unittests/tsbuild.ts b/src/testRunner/unittests/tsbuild.ts
index e1bc78c712..707a853400 100644
--- a/src/testRunner/unittests/tsbuild.ts
+++ b/src/testRunner/unittests/tsbuild.ts
@@ -1,5 +1,10 @@
namespace ts {
let currentTime = 100;
+
+ function getExpectedDiagnosticForProjectsInBuild(...projects: string[]): fakes.ExpectedDiagnostic {
+ return [Diagnostics.Projects_in_this_build_Colon_0, projects.map(p => "\r\n * " + p).join("")];
+ }
+
export namespace Sample1 {
tick();
const projFs = loadProjectFromDisk("tests/projects/sample1");
@@ -67,7 +72,11 @@ namespace ts {
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
builder.buildAllProjects();
- host.assertDiagnosticMessages(Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0);
+ host.assertDiagnosticMessages(
+ [Diagnostics.A_non_dry_build_would_build_project_0, "/src/core/tsconfig.json"],
+ [Diagnostics.A_non_dry_build_would_build_project_0, "/src/logic/tsconfig.json"],
+ [Diagnostics.A_non_dry_build_would_build_project_0, "/src/tests/tsconfig.json"]
+ );
// Check for outputs to not be written. Not an exhaustive list
for (const output of allExpectedOutputs) {
@@ -86,7 +95,11 @@ namespace ts {
host.clearDiagnostics();
builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
builder.buildAllProjects();
- host.assertDiagnosticMessages(Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date);
+ host.assertDiagnosticMessages(
+ [Diagnostics.Project_0_is_up_to_date, "/src/core/tsconfig.json"],
+ [Diagnostics.Project_0_is_up_to_date, "/src/logic/tsconfig.json"],
+ [Diagnostics.Project_0_is_up_to_date, "/src/tests/tsconfig.json"]
+ );
});
});
@@ -146,13 +159,15 @@ namespace ts {
host.clearDiagnostics();
builder.resetBuildContext();
builder.buildAllProjects();
- host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
- Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
- Diagnostics.Building_project_0,
- Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
- Diagnostics.Building_project_0,
- Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
- Diagnostics.Building_project_0);
+ host.assertDiagnosticMessages(
+ getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
+ [Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
+ [Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
+ [Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/logic/tsconfig.json", "src/logic/index.js"],
+ [Diagnostics.Building_project_0, "/src/logic/tsconfig.json"],
+ [Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tests/tsconfig.json", "src/tests/index.js"],
+ [Diagnostics.Building_project_0, "/src/tests/tsconfig.json"]
+ );
tick();
});
@@ -161,10 +176,12 @@ namespace ts {
host.clearDiagnostics();
builder.resetBuildContext();
builder.buildAllProjects();
- host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
- Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
- Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
- Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2);
+ host.assertDiagnosticMessages(
+ getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
+ [Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/core/tsconfig.json", "src/core/anotherModule.ts", "src/core/anotherModule.js"],
+ [Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/logic/tsconfig.json", "src/logic/index.ts", "src/logic/index.js"],
+ [Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/tests/tsconfig.json", "src/tests/index.ts", "src/tests/index.js"]
+ );
tick();
});
@@ -175,11 +192,13 @@ namespace ts {
builder.resetBuildContext();
builder.buildAllProjects();
- host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
- Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
- Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
- Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2,
- Diagnostics.Building_project_0);
+ host.assertDiagnosticMessages(
+ getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
+ [Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/core/tsconfig.json", "src/core/anotherModule.ts", "src/core/anotherModule.js"],
+ [Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/logic/tsconfig.json", "src/logic/index.ts", "src/logic/index.js"],
+ [Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tests/tsconfig.json", "src/tests/index.js", "src/tests/index.ts"],
+ [Diagnostics.Building_project_0, "/src/tests/tsconfig.json"]
+ );
tick();
});
@@ -190,13 +209,15 @@ namespace ts {
builder.resetBuildContext();
builder.buildAllProjects();
- host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
- Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2,
- Diagnostics.Building_project_0,
- Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies,
- Diagnostics.Updating_output_timestamps_of_project_0,
- Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies,
- Diagnostics.Updating_output_timestamps_of_project_0);
+ host.assertDiagnosticMessages(
+ getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
+ [Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/core/tsconfig.json", "src/core/anotherModule.js", "src/core/index.ts"],
+ [Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
+ [Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies, "src/logic/tsconfig.json"],
+ [Diagnostics.Updating_output_timestamps_of_project_0, "/src/logic/tsconfig.json"],
+ [Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies, "src/tests/tsconfig.json"],
+ [Diagnostics.Updating_output_timestamps_of_project_0, "/src/tests/tsconfig.json"]
+ );
});
});
@@ -210,14 +231,14 @@ namespace ts {
replaceText(fs, "/src/logic/index.ts", "c.multiply(10, 15)", `c.muitply()`);
builder.buildAllProjects();
host.assertDiagnosticMessages(
- Diagnostics.Projects_in_this_build_Colon_0,
- Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
- Diagnostics.Building_project_0,
- Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
- Diagnostics.Building_project_0,
- Diagnostics.Property_0_does_not_exist_on_type_1,
- Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_has_errors,
- Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors
+ getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
+ [Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
+ [Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
+ [Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/logic/tsconfig.json", "src/logic/index.js"],
+ [Diagnostics.Building_project_0, "/src/logic/tsconfig.json"],
+ [Diagnostics.Property_0_does_not_exist_on_type_1, "muitply", `typeof import("/src/core/index")`],
+ [Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_has_errors, "src/tests/tsconfig.json", "src/logic"],
+ [Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, "/src/tests/tsconfig.json", "/src/logic"]
);
});
});
@@ -281,12 +302,12 @@ export class cNew {}`);
const projFs = loadProjectFromDisk("tests/projects/resolveJsonModuleAndComposite");
const allExpectedOutputs = ["/src/tests/dist/src/index.js", "/src/tests/dist/src/index.d.ts", "/src/tests/dist/src/hello.json"];
- function verifyProjectWithResolveJsonModule(configFile: string, ...expectedDiagnosticMessages: DiagnosticMessage[]) {
+ function verifyProjectWithResolveJsonModule(configFile: string, ...expectedDiagnosticMessages: fakes.ExpectedDiagnostic[]) {
const fs = projFs.shadow();
verifyProjectWithResolveJsonModuleWithFs(fs, configFile, allExpectedOutputs, ...expectedDiagnosticMessages);
}
- function verifyProjectWithResolveJsonModuleWithFs(fs: vfs.FileSystem, configFile: string, allExpectedOutputs: ReadonlyArray, ...expectedDiagnosticMessages: DiagnosticMessage[]) {
+ function verifyProjectWithResolveJsonModuleWithFs(fs: vfs.FileSystem, configFile: string, allExpectedOutputs: ReadonlyArray, ...expectedDiagnosticMessages: fakes.ExpectedDiagnostic[]) {
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, [configFile], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
@@ -300,7 +321,10 @@ export class cNew {}`);
}
it("with resolveJsonModule and include only", () => {
- verifyProjectWithResolveJsonModule("/src/tests/tsconfig_withInclude.json", Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern);
+ verifyProjectWithResolveJsonModule("/src/tests/tsconfig_withInclude.json", [
+ Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern,
+ "/src/tests/src/hello.json"
+ ]);
});
it("with resolveJsonModule and include of *.json along with other include", () => {
@@ -415,7 +439,7 @@ export default hello.hello`);
"/src/c.ts"
];
- function verifyBuild(modifyDiskLayout: (fs: vfs.FileSystem) => void, allExpectedOutputs: ReadonlyArray, expectedDiagnostics: DiagnosticMessage[], expectedFileTraces: ReadonlyArray) {
+ function verifyBuild(modifyDiskLayout: (fs: vfs.FileSystem) => void, allExpectedOutputs: ReadonlyArray, expectedFileTraces: ReadonlyArray, ...expectedDiagnostics: fakes.ExpectedDiagnostic[]) {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
modifyDiskLayout(fs);
@@ -442,11 +466,11 @@ export const b = new A();`);
}
it("verify that it builds correctly", () => {
- verifyBuild(noop, allExpectedOutputs, emptyArray, expectedFileTraces);
+ verifyBuild(noop, allExpectedOutputs, expectedFileTraces);
});
it("verify that it builds correctly when the referenced project uses different module resolution", () => {
- verifyBuild(fs => modifyFsBTsToNonRelativeImport(fs, "classic"), allExpectedOutputs, emptyArray, expectedFileTraces);
+ verifyBuild(fs => modifyFsBTsToNonRelativeImport(fs, "classic"), allExpectedOutputs, expectedFileTraces);
});
it("verify that it build reports error about module not found with node resolution with external module name", () => {
@@ -458,10 +482,25 @@ export const b = new A();`);
];
verifyBuild(fs => modifyFsBTsToNonRelativeImport(fs, "node"),
allExpectedOutputs,
- [Diagnostics.Cannot_find_module_0],
- expectedFileTraces);
+ expectedFileTraces,
+ [Diagnostics.Cannot_find_module_0, "a"],
+ );
});
});
+
+ it("unittests:: tsbuild - when tsconfig extends the missing file", () => {
+ const projFs = loadProjectFromDisk("tests/projects/missingExtendedConfig");
+ const fs = projFs.shadow();
+ const host = new fakes.SolutionBuilderHost(fs);
+ const builder = createSolutionBuilder(host, ["/src/tsconfig.json"], {});
+ builder.buildAllProjects();
+ host.assertDiagnosticMessages(
+ [Diagnostics.The_specified_path_does_not_exist_Colon_0, "/src/foobar.json"],
+ [Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, "/src/tsconfig.first.json", "[\"**/*\"]", "[]"],
+ [Diagnostics.The_specified_path_does_not_exist_Colon_0, "/src/foobar.json"],
+ [Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, "/src/tsconfig.second.json", "[\"**/*\"]", "[]"]
+ );
+ });
}
export namespace OutFile {
@@ -564,7 +603,7 @@ export const b = new A();`);
host.clearDiagnostics();
builder.buildAllProjects();
- host.assertDiagnosticMessages(Diagnostics.The_files_list_in_config_file_0_is_empty);
+ host.assertDiagnosticMessages([Diagnostics.The_files_list_in_config_file_0_is_empty, "/src/no-references/tsconfig.json"]);
// Check for outputs to not be written.
for (const output of allExpectedOutputs) {
diff --git a/src/testRunner/unittests/tsserver/rename.ts b/src/testRunner/unittests/tsserver/rename.ts
index 571db235a6..f565524ade 100644
--- a/src/testRunner/unittests/tsserver/rename.ts
+++ b/src/testRunner/unittests/tsserver/rename.ts
@@ -7,6 +7,7 @@ namespace ts.projectSystem {
const session = createSession(createServerHost([aTs, bTs]));
openFilesForSession([bTs], session);
+ // rename fails with allowRenameOfImportPath disabled
const response1 = executeSessionRequest(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, 'a";'));
assert.deepEqual(response1, {
info: {
@@ -16,6 +17,7 @@ namespace ts.projectSystem {
locs: [{ file: bTs.path, locs: [protocolRenameSpanFromSubstring(bTs.content, "./a")] }],
});
+ // rename succeeds with allowRenameOfImportPath enabled in host
session.getProjectService().setHostConfiguration({ preferences: { allowRenameOfImportPath: true } });
const response2 = executeSessionRequest(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, 'a";'));
assert.deepEqual(response2, {
@@ -30,6 +32,23 @@ namespace ts.projectSystem {
},
locs: [{ file: bTs.path, locs: [protocolRenameSpanFromSubstring(bTs.content, "./a")] }],
});
+
+ // rename succeeds with allowRenameOfImportPath enabled in file
+ session.getProjectService().setHostConfiguration({ preferences: { allowRenameOfImportPath: false } });
+ session.getProjectService().setHostConfiguration({ file: "/b.ts", formatOptions: {}, preferences: { allowRenameOfImportPath: true } });
+ const response3 = executeSessionRequest(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, 'a";'));
+ assert.deepEqual(response3, {
+ info: {
+ canRename: true,
+ fileToRename: aTs.path,
+ displayName: aTs.path,
+ fullDisplayName: aTs.path,
+ kind: ScriptElementKind.moduleElement,
+ kindModifiers: "",
+ triggerSpan: protocolTextSpanFromSubstring(bTs.content, "a", { index: 1 }),
+ },
+ locs: [{ file: bTs.path, locs: [protocolRenameSpanFromSubstring(bTs.content, "./a")] }],
+ });
});
it("works with prefixText and suffixText when enabled", () => {
@@ -61,7 +80,7 @@ namespace ts.projectSystem {
],
});
- // rename with prefixText and suffixText enabled
+ // rename with prefixText and suffixText enabled in host
session.getProjectService().setHostConfiguration({ preferences: { providePrefixAndSuffixTextForRename: true } });
const response2 = executeSessionRequest(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(aTs, "x"));
assert.deepEqual(response2, {
@@ -84,6 +103,93 @@ namespace ts.projectSystem {
},
],
});
+
+ // rename with prefixText and suffixText enabled for file
+ session.getProjectService().setHostConfiguration({ preferences: { providePrefixAndSuffixTextForRename: false } });
+ session.getProjectService().setHostConfiguration({ file: "/a.ts", formatOptions: {}, preferences: { providePrefixAndSuffixTextForRename: true } });
+ const response3 = executeSessionRequest(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(aTs, "x"));
+ assert.deepEqual(response3, {
+ info: {
+ canRename: true,
+ fileToRename: undefined,
+ displayName: "x",
+ fullDisplayName: "x",
+ kind: ScriptElementKind.constElement,
+ kindModifiers: ScriptElementKindModifier.none,
+ triggerSpan: protocolTextSpanFromSubstring(aTs.content, "x"),
+ },
+ locs: [
+ {
+ file: aTs.path,
+ locs: [
+ protocolRenameSpanFromSubstring(aTs.content, "x"),
+ protocolRenameSpanFromSubstring(aTs.content, "x", { index: 1 }, { prefixText: "x: " }),
+ ],
+ },
+ ],
+ });
+ });
+
+ it("rename behavior is based on file of rename initiation", () => {
+ const aTs: File = { path: "/a.ts", content: "const x = 1; export { x };" };
+ const bTs: File = { path: "/b.ts", content: `import { x } from "./a"; const y = x + 1;` };
+ const host = createServerHost([aTs, bTs]);
+ const session = createSession(host);
+ openFilesForSession([aTs, bTs], session);
+
+ // rename from file with prefixText and suffixText enabled
+ session.getProjectService().setHostConfiguration({ file: "/a.ts", formatOptions: {}, preferences: { providePrefixAndSuffixTextForRename: true } });
+ const response1 = executeSessionRequest(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(aTs, "x"));
+ assert.deepEqual(response1, {
+ info: {
+ canRename: true,
+ fileToRename: undefined,
+ displayName: "x",
+ fullDisplayName: "x",
+ kind: ScriptElementKind.constElement,
+ kindModifiers: ScriptElementKindModifier.none,
+ triggerSpan: protocolTextSpanFromSubstring(aTs.content, "x"),
+ },
+ locs: [
+ {
+ file: aTs.path,
+ locs: [
+ protocolRenameSpanFromSubstring(aTs.content, "x"),
+ protocolRenameSpanFromSubstring(aTs.content, "x", { index: 2 }, { suffixText: " as x" }),
+ ],
+ },
+ ],
+ });
+
+ // rename from file with prefixText and suffixText disabled
+ const response2 = executeSessionRequest(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, "x"));
+ assert.deepEqual(response2, {
+ info: {
+ canRename: true,
+ fileToRename: undefined,
+ displayName: "x",
+ fullDisplayName: "x",
+ kind: ScriptElementKind.alias,
+ kindModifiers: ScriptElementKindModifier.none,
+ triggerSpan: protocolTextSpanFromSubstring(bTs.content, "x"),
+ },
+ locs: [
+ {
+ file: bTs.path,
+ locs: [
+ protocolRenameSpanFromSubstring(bTs.content, "x"),
+ protocolRenameSpanFromSubstring(bTs.content, "x", { index: 1 })
+ ]
+ },
+ {
+ file: aTs.path,
+ locs: [
+ protocolRenameSpanFromSubstring(aTs.content, "x"),
+ protocolRenameSpanFromSubstring(aTs.content, "x", { index: 2 }),
+ ],
+ },
+ ],
+ });
});
});
}
diff --git a/src/tsc/tsconfig.json b/src/tsc/tsconfig.json
index 8bc0227993..e97cedc5de 100644
--- a/src/tsc/tsconfig.json
+++ b/src/tsc/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "../tsconfig-base",
+ "extends": "../tsconfig-noncomposite-base",
"compilerOptions": {
"outFile": "../../built/local/tsc.js"
},
diff --git a/src/tsconfig-noncomposite-base.json b/src/tsconfig-noncomposite-base.json
new file mode 100644
index 0000000000..569269f756
--- /dev/null
+++ b/src/tsconfig-noncomposite-base.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./tsconfig-base",
+ "compilerOptions": {
+ "declaration": false,
+ "declarationMap": false,
+ "composite": false
+ }
+}
diff --git a/src/tsserver/tsconfig.json b/src/tsserver/tsconfig.json
index 8f14785cd4..16ffd722f2 100644
--- a/src/tsserver/tsconfig.json
+++ b/src/tsserver/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "../tsconfig-base",
+ "extends": "../tsconfig-noncomposite-base",
"compilerOptions": {
"outFile": "../../built/local/tsserver.js",
diff --git a/src/typingsInstaller/tsconfig.json b/src/typingsInstaller/tsconfig.json
index 675c045ba0..143409e9e9 100644
--- a/src/typingsInstaller/tsconfig.json
+++ b/src/typingsInstaller/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "../tsconfig-base",
+ "extends": "../tsconfig-noncomposite-base",
"compilerOptions": {
"removeComments": true,
"outFile": "../../built/local/typingsInstaller.js",
diff --git a/src/watchGuard/tsconfig.json b/src/watchGuard/tsconfig.json
index 7262a5db58..aafa227029 100644
--- a/src/watchGuard/tsconfig.json
+++ b/src/watchGuard/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "../tsconfig-base",
+ "extends": "../tsconfig-noncomposite-base",
"compilerOptions": {
"removeComments": true,
"outFile": "../../built/local/watchGuard.js",
@@ -13,4 +13,4 @@
"files": [
"watchGuard.ts"
]
-}
\ No newline at end of file
+}
diff --git a/tests/baselines/reference/anonymousModules.errors.txt b/tests/baselines/reference/anonymousModules.errors.txt
index 24468352d0..b81513cfbb 100644
--- a/tests/baselines/reference/anonymousModules.errors.txt
+++ b/tests/baselines/reference/anonymousModules.errors.txt
@@ -1,22 +1,22 @@
-tests/cases/compiler/anonymousModules.ts(1,1): error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.
+tests/cases/compiler/anonymousModules.ts(1,1): error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node`.
tests/cases/compiler/anonymousModules.ts(1,8): error TS1005: ';' expected.
-tests/cases/compiler/anonymousModules.ts(4,2): error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.
+tests/cases/compiler/anonymousModules.ts(4,2): error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node`.
tests/cases/compiler/anonymousModules.ts(4,9): error TS1005: ';' expected.
-tests/cases/compiler/anonymousModules.ts(10,2): error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.
+tests/cases/compiler/anonymousModules.ts(10,2): error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node`.
tests/cases/compiler/anonymousModules.ts(10,9): error TS1005: ';' expected.
==== tests/cases/compiler/anonymousModules.ts (6 errors) ====
module {
~~~~~~
-!!! error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.
+!!! error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node`.
~
!!! error TS1005: ';' expected.
export var foo = 1;
module {
~~~~~~
-!!! error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.
+!!! error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node`.
~
!!! error TS1005: ';' expected.
export var bar = 1;
@@ -26,7 +26,7 @@ tests/cases/compiler/anonymousModules.ts(10,9): error TS1005: ';' expected.
module {
~~~~~~
-!!! error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.
+!!! error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node`.
~
!!! error TS1005: ';' expected.
var x = bar;
diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts
index a384375b24..ee13a377f5 100644
--- a/tests/baselines/reference/api/tsserverlibrary.d.ts
+++ b/tests/baselines/reference/api/tsserverlibrary.d.ts
@@ -14,7 +14,7 @@ and limitations under the License.
***************************************************************************** */
declare namespace ts {
- const versionMajorMinor = "3.3";
+ const versionMajorMinor = "3.4";
/** The version of the TypeScript compiler release */
const version: string;
}
@@ -814,7 +814,7 @@ declare namespace ts {
}
interface TypeOperatorNode extends TypeNode {
kind: SyntaxKind.TypeOperator;
- operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword;
+ operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword;
type: TypeNode;
}
interface IndexedAccessTypeNode extends TypeNode {
@@ -2217,6 +2217,7 @@ declare namespace ts {
}
interface UniqueESSymbolType extends Type {
symbol: Symbol;
+ escapedName: __String;
}
interface StringLiteralType extends LiteralType {
value: string;
@@ -2285,6 +2286,7 @@ declare namespace ts {
interface TupleType extends GenericType {
minLength: number;
hasRestElement: boolean;
+ readonly: boolean;
associatedNames?: __String[];
}
interface TupleTypeReference extends TypeReference {
@@ -3358,6 +3360,7 @@ declare namespace ts {
function isNewExpression(node: Node): node is NewExpression;
function isTaggedTemplateExpression(node: Node): node is TaggedTemplateExpression;
function isTypeAssertion(node: Node): node is TypeAssertion;
+ function isConstTypeReference(node: Node): boolean;
function isParenthesizedExpression(node: Node): node is ParenthesizedExpression;
function skipPartiallyEmittedExpressions(node: Expression): Expression;
function skipPartiallyEmittedExpressions(node: Node): Node;
@@ -3758,7 +3761,7 @@ declare namespace ts {
function updateParenthesizedType(node: ParenthesizedTypeNode, type: TypeNode): ParenthesizedTypeNode;
function createThisTypeNode(): ThisTypeNode;
function createTypeOperatorNode(type: TypeNode): TypeOperatorNode;
- function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword, type: TypeNode): TypeOperatorNode;
+ function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword, type: TypeNode): TypeOperatorNode;
function updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode): TypeOperatorNode;
function createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode;
function updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode;
@@ -8343,7 +8346,7 @@ declare namespace ts.server {
excludedFiles: ReadonlyArray;
private typeAcquisition;
updateGraph(): boolean;
- getExcludedFiles(): ReadonlyArray;
+ getExcludedFiles(): readonly NormalizedPath[];
getTypeAcquisition(): TypeAcquisition;
setTypeAcquisition(newTypeAcquisition: TypeAcquisition): void;
}
diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts
index 5ae4960835..49238bd3d6 100644
--- a/tests/baselines/reference/api/typescript.d.ts
+++ b/tests/baselines/reference/api/typescript.d.ts
@@ -14,7 +14,7 @@ and limitations under the License.
***************************************************************************** */
declare namespace ts {
- const versionMajorMinor = "3.3";
+ const versionMajorMinor = "3.4";
/** The version of the TypeScript compiler release */
const version: string;
}
@@ -814,7 +814,7 @@ declare namespace ts {
}
interface TypeOperatorNode extends TypeNode {
kind: SyntaxKind.TypeOperator;
- operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword;
+ operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword;
type: TypeNode;
}
interface IndexedAccessTypeNode extends TypeNode {
@@ -2217,6 +2217,7 @@ declare namespace ts {
}
interface UniqueESSymbolType extends Type {
symbol: Symbol;
+ escapedName: __String;
}
interface StringLiteralType extends LiteralType {
value: string;
@@ -2285,6 +2286,7 @@ declare namespace ts {
interface TupleType extends GenericType {
minLength: number;
hasRestElement: boolean;
+ readonly: boolean;
associatedNames?: __String[];
}
interface TupleTypeReference extends TypeReference {
@@ -3358,6 +3360,7 @@ declare namespace ts {
function isNewExpression(node: Node): node is NewExpression;
function isTaggedTemplateExpression(node: Node): node is TaggedTemplateExpression;
function isTypeAssertion(node: Node): node is TypeAssertion;
+ function isConstTypeReference(node: Node): boolean;
function isParenthesizedExpression(node: Node): node is ParenthesizedExpression;
function skipPartiallyEmittedExpressions(node: Expression): Expression;
function skipPartiallyEmittedExpressions(node: Node): Node;
@@ -3758,7 +3761,7 @@ declare namespace ts {
function updateParenthesizedType(node: ParenthesizedTypeNode, type: TypeNode): ParenthesizedTypeNode;
function createThisTypeNode(): ThisTypeNode;
function createTypeOperatorNode(type: TypeNode): TypeOperatorNode;
- function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword, type: TypeNode): TypeOperatorNode;
+ function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword, type: TypeNode): TypeOperatorNode;
function updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode): TypeOperatorNode;
function createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode;
function updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode;
diff --git a/tests/baselines/reference/arrayFind.types b/tests/baselines/reference/arrayFind.types
index c523718370..530f07be3f 100644
--- a/tests/baselines/reference/arrayFind.types
+++ b/tests/baselines/reference/arrayFind.types
@@ -30,15 +30,15 @@ const foundNumber: number | undefined = arrayOfStringsNumbersAndBooleans.find(is
>isNumber : (x: any) => x is number
const readonlyArrayOfStringsNumbersAndBooleans = arrayOfStringsNumbersAndBooleans as ReadonlyArray;
->readonlyArrayOfStringsNumbersAndBooleans : ReadonlyArray
->arrayOfStringsNumbersAndBooleans as ReadonlyArray : ReadonlyArray
+>readonlyArrayOfStringsNumbersAndBooleans : readonly (string | number | boolean)[]
+>arrayOfStringsNumbersAndBooleans as ReadonlyArray : readonly (string | number | boolean)[]
>arrayOfStringsNumbersAndBooleans : (string | number | boolean)[]
const readonlyFoundNumber: number | undefined = readonlyArrayOfStringsNumbersAndBooleans.find(isNumber);
>readonlyFoundNumber : number
>readonlyArrayOfStringsNumbersAndBooleans.find(isNumber) : number
->readonlyArrayOfStringsNumbersAndBooleans.find : { (predicate: (this: void, value: string | number | boolean, index: number, obj: ReadonlyArray) => value is S, thisArg?: any): S; (predicate: (value: string | number | boolean, index: number, obj: ReadonlyArray) => boolean, thisArg?: any): string | number | boolean; }
->readonlyArrayOfStringsNumbersAndBooleans : ReadonlyArray
->find : { (predicate: (this: void, value: string | number | boolean, index: number, obj: ReadonlyArray) => value is S, thisArg?: any): S; (predicate: (value: string | number | boolean, index: number, obj: ReadonlyArray) => boolean, thisArg?: any): string | number | boolean; }
+>readonlyArrayOfStringsNumbersAndBooleans.find : { (predicate: (this: void, value: string | number | boolean, index: number, obj: readonly (string | number | boolean)[]) => value is S, thisArg?: any): S; (predicate: (value: string | number | boolean, index: number, obj: readonly (string | number | boolean)[]) => boolean, thisArg?: any): string | number | boolean; }
+>readonlyArrayOfStringsNumbersAndBooleans : readonly (string | number | boolean)[]
+>find : { (predicate: (this: void, value: string | number | boolean, index: number, obj: readonly (string | number | boolean)[]) => value is S, thisArg?: any): S; (predicate: (value: string | number | boolean, index: number, obj: readonly (string | number | boolean)[]) => boolean, thisArg?: any): string | number | boolean; }
>isNumber : (x: any) => x is number
diff --git a/tests/baselines/reference/arrayFlatMap.types b/tests/baselines/reference/arrayFlatMap.types
index 7eb4d444ec..631ae04ef9 100644
--- a/tests/baselines/reference/arrayFlatMap.types
+++ b/tests/baselines/reference/arrayFlatMap.types
@@ -4,22 +4,22 @@ const array: number[] = [];
>[] : undefined[]
const readonlyArray: ReadonlyArray = [];
->readonlyArray : ReadonlyArray
+>readonlyArray : readonly number[]
>[] : undefined[]
array.flatMap((): ReadonlyArray => []); // ok
>array.flatMap((): ReadonlyArray => []) : number[]
->array.flatMap : (callback: (this: This, value: number, index: number, array: number[]) => U | ReadonlyArray, thisArg?: This) => U[]
+>array.flatMap : (callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]
>array : number[]
->flatMap : (callback: (this: This, value: number, index: number, array: number[]) => U | ReadonlyArray, thisArg?: This) => U[]
->(): ReadonlyArray => [] : () => ReadonlyArray
+>flatMap : (callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]
+>(): ReadonlyArray => [] : () => readonly number[]
>[] : undefined[]
readonlyArray.flatMap((): ReadonlyArray => []); // ok
>readonlyArray.flatMap((): ReadonlyArray => []) : number[]
->readonlyArray.flatMap : (callback: (this: This, value: number, index: number, array: number[]) => U | ReadonlyArray, thisArg?: This) => U[]
->readonlyArray : ReadonlyArray
->flatMap : (callback: (this: This, value: number, index: number, array: number[]) => U | ReadonlyArray, thisArg?: This) => U[]
->(): ReadonlyArray => [] : () => ReadonlyArray
+>readonlyArray.flatMap : (callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]
+>readonlyArray : readonly number[]
+>flatMap : (callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]
+>(): ReadonlyArray => [] : () => readonly number[]
>[] : undefined[]
diff --git a/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.errors.txt b/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.errors.txt
index 319cc4310d..63d3a6f7b6 100644
--- a/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.errors.txt
+++ b/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.errors.txt
@@ -1,6 +1,6 @@
-tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(13,1): error TS2322: Type 'A[]' is not assignable to type 'ReadonlyArray'.
+tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(13,1): error TS2322: Type 'A[]' is not assignable to type 'readonly B[]'.
Property 'b' is missing in type 'A' but required in type 'B'.
-tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error TS2322: Type 'C' is not assignable to type 'ReadonlyArray'.
+tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error TS2322: Type 'C' is not assignable to type 'readonly B[]'.
Types of property 'concat' are incompatible.
Type '{ (...items: ConcatArray[]): A[]; (...items: (A | ConcatArray)[]): A[]; }' is not assignable to type '{ (...items: ConcatArray[]): B[]; (...items: (B | ConcatArray)[]): B[]; }'.
Type 'A[]' is not assignable to type 'B[]'.
@@ -22,7 +22,7 @@ tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error T
rra = arb;
rrb = ara; // error: 'A' is not assignable to 'B'
~~~
-!!! error TS2322: Type 'A[]' is not assignable to type 'ReadonlyArray'.
+!!! error TS2322: Type 'A[]' is not assignable to type 'readonly B[]'.
!!! error TS2322: Property 'b' is missing in type 'A' but required in type 'B'.
!!! related TS2728 tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts:2:21: 'b' is declared here.
@@ -31,7 +31,7 @@ tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error T
rrb = crb;
rrb = cra; // error: 'A' is not assignable to 'B'
~~~
-!!! error TS2322: Type 'C' is not assignable to type 'ReadonlyArray'.
+!!! error TS2322: Type 'C' is not assignable to type 'readonly B[]'.
!!! error TS2322: Types of property 'concat' are incompatible.
!!! error TS2322: Type '{ (...items: ConcatArray[]): A[]; (...items: (A | ConcatArray)[]): A[]; }' is not assignable to type '{ (...items: ConcatArray[]): B[]; (...items: (B | ConcatArray)[]): B[]; }'.
!!! error TS2322: Type 'A[]' is not assignable to type 'B[]'.
diff --git a/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.types b/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.types
index 535522182f..f5b639f486 100644
--- a/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.types
+++ b/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.types
@@ -26,48 +26,48 @@ declare var crb: C;
>crb : C
declare var rra: ReadonlyArray;
->rra : ReadonlyArray
+>rra : readonly A[]
declare var rrb: ReadonlyArray;
->rrb : ReadonlyArray
+>rrb : readonly B[]
rra = ara;
>rra = ara : A[]
->rra : ReadonlyArray
+>rra : readonly A[]
>ara : A[]
rrb = arb; // OK, Array is assignable to ReadonlyArray
>rrb = arb : B[]
->rrb : ReadonlyArray
+>rrb : readonly B[]
>arb : B[]
rra = arb;
>rra = arb : B[]
->rra : ReadonlyArray
+>rra : readonly A[]
>arb : B[]
rrb = ara; // error: 'A' is not assignable to 'B'
>rrb = ara : A[]
->rrb : ReadonlyArray
+>rrb : readonly B[]
>ara : A[]
rra = cra;
>rra = cra : C