diff --git a/Gulpfile.js b/Gulpfile.js
index 3aed88d3a7070..fb10563067b25 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 = [];
+
+const buildScripts = () => buildProject("scripts");
+const cleanScripts = () => cleanProject("scripts");
+cleanTasks.push(cleanScripts);
+
+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 };
+});
+
+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";
+
+const cleanLib = () => del(libs.map(lib => lib.target));
+cleanTasks.push(cleanLib);
+
+const watchLib = () => watch(["src/lib/**/*"], generateLibs);
-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 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}`)});
-
-// 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"]));
-
-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 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));
-
-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,183 +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 });
}
-});
+};
+
+// Pre-build steps when targeting the LKG compiler
+const lkgPreBuild = parallel(generateLibs, series(buildScripts, generateDiagnostics));
+
+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));
-gulp.task("localize", /*help*/ false, [generatedLCGFile]);
+// Pre-build steps to use based on supplied options.
+const preBuild = cmdLineOptions.lkg ? lkgPreBuild : localPreBuild;
-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 buildServices = (() => {
+ // flatten the services project
+ const flattenServicesConfig = async () => flatten("src/services/tsconfig.json", "built/local/typescriptServices.tsconfig.json", {
compilerOptions: {
"removeComments": false,
"stripInternal": true,
- "declaration": true,
- "outFile": "typescriptServices.out.js" // must align with same task in jakefile. We fix this name below.
+ "declarationMap": false,
+ "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")));
-
-// build all 'typescriptServices'-related outputs
-gulp.task("services", /*help*/ false, [typescriptServicesJs, typescriptServicesDts, typescriptJs, typescriptDts, typescriptStandaloneDts]);
-
-const useCompiler = cmdLineOptions.lkg ? "lkg" : "built";
-const useCompilerDeps = cmdLineOptions.lkg ? ["lib", "generate-diagnostics"] : [typescriptServicesJs];
-
-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 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 cancellationTokenProject = "src/cancellationToken/tsconfig.json";
-const cancellationTokenJs = "built/local/cancellationToken.js";
-gulp.task(cancellationTokenJs, /*help*/ false, useCompilerDeps, () => project.compile(cancellationTokenProject, { typescript: useCompiler }));
-
-const typingsInstallerProject = "src/typingsInstaller/tsconfig.json";
-const typingsInstallerJs = "built/local/typingsInstaller.js";
-gulp.task(typingsInstallerJs, /*help*/ false, useCompilerDeps, () => project.compile(typingsInstallerProject, { typescript: useCompiler }));
-
-const tsserverProject = "src/tsserver/tsconfig.json";
-const tsserverJs = "built/local/tsserver.js";
-gulp.task(tsserverJs, /*help*/ false, useCompilerDeps, () => project.compile(tsserverProject, { typescript: useCompiler }));
-
-gulp.task(
- "tsserver",
- "Builds the language server",
- [tsserverJs]);
-
-const watchGuardProject = "src/watchGuard/tsconfig.json";
-const watchGuardJs = "built/local/watchGuard.js";
-gulp.task(watchGuardJs, /*help*/ false, useCompilerDeps, () => project.compile(watchGuardProject, { typescript: useCompiler }));
-
-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")));
-
-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, {
+ .pipe(dest("built/local"));
+
+ 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 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 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 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 cleanServer = () => cleanProject("src/tsserver");
+cleanTasks.push(cleanServer);
+task("clean-tsserver", cleanServer);
+task("clean-tsserver").description = "Cleans outputs for the language server";
+
+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."
+}
+
+task("min", series(lkgPreBuild, buildTsc, buildServer));
+task("min").description = "Builds only tsc and tsserver";
+task("min").flags = {
+ " --built": "Compile using the built version of the compiler."
+}
+
+task("clean-min", series(cleanTsc, cleanServer));
+task("clean-min").description = "Cleans outputs for tsc and tsserver";
+
+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 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] });
-
-gulp.task(
- "lssl",
- "Builds language service server library",
- [tsserverlibraryDts]);
-
-gulp.task(
- "local",
- "Builds the full compiler and services",
- [tscJs, "services", tsserverJs, builtGeneratedDiagnosticMessagesJson, tsserverlibraryDts, "localize"]);
-
-gulp.task(
- "tsc",
- "Builds only the compiler",
- [tscJs]);
-
-// 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)]));
-
-gulp.task(
- "generate-spec",
- "Generates a Markdown version of the Language Specification",
- [specMd]);
-
-gulp.task("produce-LKG", /*help*/ false, ["scripts", "local", cancellationTokenJs, typingsInstallerJs, watchGuardJs, tscReleaseJs], () => {
+ // build tsserverlibrary.out.js
+ const buildServerLibraryOut = () => buildProject("built/local/tsserverlibrary.tsconfig.json", cmdLineOptions);
+
+ // 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"));
+
+ // 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"));
+
+ 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."
+}
+
+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";
+
+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."
+}
+
+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));
@@ -306,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.");
- }
- });
-});
-
-gulp.task(
- "LKG",
- "Makes a new LKG out of the built js files",
- () => runSequence("clean-built", "produce-LKG"));
-
-// 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"));
+ 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);
+ }
});
-
-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"
- ]);
+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.",
+};
+
+task("default", series("local"));
+task("default").description = "Runs 'local'";
+
+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 1a38e4d09e2e7..ea81881e7d9c2 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;
diff --git a/package.json b/package.json
index d8c514363e532..351d1b3af6b4f 100644
--- a/package.json
+++ b/package.json
@@ -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 df61b87cd3267..0000000000000
--- 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 1fa5bbdeaf368..ba8ccd158243d 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 149c8ea1533c6..0000000000000
--- 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 00d48fdcc3ff0..0000000000000
--- 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 7020cb61bbd16..0000000000000
--- 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 5ca51c572ea5b..0000000000000
--- 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 8e0a058fed036..0000000000000
--- 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 e642c06419325..0000000000000
--- 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 13a30b8933754..0000000000000
--- 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 58a65907e4d41..0000000000000
--- 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 ed7a7d64a0337..0000000000000
--- 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 714b94f22a16c..0000000000000
--- 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 5d68bc591e85f..0000000000000
--- 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 40f27b9526eb1..0000000000000
--- 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 f8dac7529e053..0000000000000
--- 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 8a918a387d4ce..0000000000000
--- 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 436003eab8756..0000000000000
--- 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 ba1b669188dc5..b7233ba70b0f1 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 6e7b794e79bea..51c8aa84a8ab5 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 fb193907bbe11..0000000000000
--- 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 0000000000000..954f37af84eee
--- /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 c9e63419692a8..0000000000000
--- 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 1287d082287e5..0000000000000
--- 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 0922954b49ddb..0000000000000
--- 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 66e488287b2b7..2c85b897b09c1 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 46c31ed8913c7..36a9ea54cb9b5 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 c3abb5d1013fb..0000000000000
--- 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 06f55d7288ac3..170c36adef6b4 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 1dfb631499194..0000000000000
--- 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 6cf68f3cd26ce..0000000000000
--- a/scripts/build/vinyl.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require("vinyl");
\ No newline at end of file
diff --git a/scripts/types/ambient.d.ts b/scripts/types/ambient.d.ts
index 4f9112d236ea9..52ddca6d7c1b9 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