From c8c042816266f4ccd2fc8495c70a1e86bd7642df Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 20 Jun 2018 18:24:12 -0700 Subject: [PATCH] Cleanup, remove some unneeded dependencies, make it easier to add gulp.watch support later --- Gulpfile.js | 122 +++-- package-lock.json | 111 ++--- package.json | 2 +- scripts/build/chalk.js | 5 + scripts/build/colors.js | 20 - scripts/build/diagnostics.js | 31 +- scripts/build/exec.js | 4 +- scripts/build/gulp.js | 7 + scripts/build/lib.js | 33 +- scripts/build/pass.js | 11 - scripts/build/prepend.js | 14 - scripts/build/project.js | 847 ++++++++++++++++------------------- scripts/build/upToDate.js | 4 +- src/compiler/tsbuild.ts | 6 - 14 files changed, 539 insertions(+), 678 deletions(-) create mode 100644 scripts/build/chalk.js delete mode 100644 scripts/build/colors.js delete mode 100644 scripts/build/pass.js delete mode 100644 scripts/build/prepend.js diff --git a/Gulpfile.js b/Gulpfile.js index c2b5627b749..1ba05048578 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -24,12 +24,13 @@ 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 makeLibraryTargets = require("./scripts/build/lib"); 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 _debugMode = require("./scripts/build/debugMode"); +const { libraryTargets, generateLibs } = require("./scripts/build/lib"); const { runConsoleTests, cleanTestDirs, writeTestConfigFile, refBaseline, localBaseline, refRwcBaseline, localRwcBaseline } = require("./scripts/build/tests"); Error.stackTraceLimit = 1000; @@ -37,11 +38,10 @@ Error.stackTraceLimit = 1000; // Constants const host = cmdLineOptions.host; const copyright = "CopyrightNotice.txt"; -const libraryTargets = makeLibraryTargets([copyright]); -// Compile using the LKG compiler project.addTypeScript("lkg", "./lib/typescript.js"); -project.addTypeScript("default", "lkg"); +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"; @@ -50,7 +50,7 @@ const generateLocalizedDiagnosticMessagesJs = "scripts/generateLocalizedDiagnost const buildProtocolJs = "scripts/buildProtocol.js"; const produceLKGJs = "scripts/produceLKG.js"; const word2mdJs = "scripts/word2md.js"; -gulp.task("scripts", /*help*/ false, [project(scriptsProject)], undefined, { +gulp.task("scripts", /*help*/ false, () => project.compile(scriptsProject), { aliases: [ configurePrereleaseJs, processDiagnosticMessagesJs, @@ -60,7 +60,7 @@ gulp.task("scripts", /*help*/ false, [project(scriptsProject)], undefined, { word2mdJs ] }); -gulp.task("clean-scripts", /*help*/ false, [project.clean(scriptsProject)]); +gulp.task("clean-scripts", /*help*/ false, () => project.clean(scriptsProject)); // Nightly management tasks gulp.task( @@ -78,8 +78,8 @@ gulp.task( const importDefinitelyTypedTestsProject = "scripts/importDefinitelyTypedTests/tsconfig.json"; const importDefinitelyTypedTestsJs = "scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js"; -gulp.task(importDefinitelyTypedTestsJs, /*help*/ false, [project(importDefinitelyTypedTestsProject)]); -gulp.task("clean:" + importDefinitelyTypedTestsJs, /*help*/ false, [project.clean(importDefinitelyTypedTestsProject)]); +gulp.task(importDefinitelyTypedTestsJs, /*help*/ false, () => project.compile(importDefinitelyTypedTestsProject)); +gulp.task("clean:" + importDefinitelyTypedTestsJs, /*help*/ false, () => project.clean(importDefinitelyTypedTestsProject)); gulp.task( "importDefinitelyTypedTests", @@ -90,7 +90,7 @@ gulp.task( gulp.task( "lib", "Builds the library targets", - libraryTargets); + () => generateLibs([copyright])); // The generated diagnostics map; built for the compiler and for the "generate-diagnostics" task const diagnosticInformationMapTs = "src/compiler/diagnosticInformationMap.generated.ts"; @@ -145,20 +145,18 @@ gulp.task(typescriptServicesProject, /*help*/ false, () => { // NOTE: flatten services so that we can properly strip @internal project.flatten("src/services/tsconfig.json", typescriptServicesProject, { compilerOptions: { + "removeComments": true, "stripInternal": true, "outFile": "typescriptServices.js" } }); -}) +}); const typescriptServicesJs = "built/local/typescriptServices.js"; const typescriptServicesDts = "built/local/typescriptServices.d.ts"; -const typescriptServicesProjectTask = project.defer(typescriptServicesProject, { - dts: files => files.pipe(convertConstEnums()), - release: { compilerOptions: { removeComments: true } } -}); - -gulp.task(typescriptServicesJs, /*help*/ false, ["lib", "generate-diagnostics", typescriptServicesProject], typescriptServicesProjectTask, { aliases: [typescriptServicesDts] }); +gulp.task(typescriptServicesJs, /*help*/ false, ["lib", "generate-diagnostics", typescriptServicesProject], () => + project.compile(typescriptServicesProject, { dts: convertConstEnums() }), + { aliases: [typescriptServicesDts] }); const typescriptJs = "built/local/typescript.js"; gulp.task(typescriptJs, /*help*/ false, [typescriptServicesJs], () => @@ -186,28 +184,25 @@ gulp.task(typescriptStandaloneDts, /*help*/ false, [typescriptServicesDts], () = // build all 'typescriptServices'-related outputs gulp.task("typescriptServices", /*help*/ false, [typescriptServicesJs, typescriptServicesDts, typescriptJs, typescriptDts, typescriptStandaloneDts]); -// Add the "built" compiler with a dependency on the built version of the compiler. -project.addTypeScript("built", "./built/local/typescriptServices.js", [typescriptServicesJs]); - const tscProject = "src/tsc/tsconfig.json"; const tscJs = "built/local/tsc.js"; -gulp.task(tscJs, /*help*/ false, [project(tscProject, { typescript: "built", release: { compilerOptions: { removeComments: true } } })]); +gulp.task(tscJs, /*help*/ false, [typescriptServicesJs], () => project.compile(tscProject, { typescript: "built" })); const cancellationTokenProject = "src/cancellationToken/tsconfig.json"; const cancellationTokenJs = "built/local/cancellationToken.js"; -gulp.task(cancellationTokenJs, /*help*/ false, [project(cancellationTokenProject, { typescript: "built", release: { compilerOptions: { removeComments: true } } })]); +gulp.task(cancellationTokenJs, /*help*/ false, [typescriptServicesJs], () => project.compile(cancellationTokenProject, { typescript: "built" })); const typingsInstallerProject = "src/typingsInstaller/tsconfig.json"; const typingsInstallerJs = "built/local/typingsInstaller.js"; -gulp.task(typingsInstallerJs, /*help*/ false, [project(typingsInstallerProject, { typescript: "built", release: { compilerOptions: { removeComments: true } } })]); +gulp.task(typingsInstallerJs, /*help*/ false, [typescriptServicesJs], () => project.compile(typingsInstallerProject, { typescript: "built" })); const tsserverProject = "src/tsserver/tsconfig.json"; const tsserverJs = "built/local/tsserver.js"; -gulp.task(tsserverJs, /*help*/ false, [project(tsserverProject, { typescript: "built", release: { compilerOptions: { removeComments: true } } })]); +gulp.task(tsserverJs, /*help*/ false, [typescriptServicesJs], () => project.compile(tsserverProject, { typescript: "built" })); const watchGuardProject = "src/watchGuard/tsconfig.json"; const watchGuardJs = "built/local/watchGuard.js"; -gulp.task(watchGuardJs, /*help*/ false, [project(watchGuardProject, { typescript: "built", release: { compilerOptions: { removeComments: true } } })]); +gulp.task(watchGuardJs, /*help*/ false, [typescriptServicesJs], () => project.compile(watchGuardProject, { typescript: "built" })); const typesMapJson = "built/local/typesMap.json"; gulp.task(typesMapJson, /*help*/ false, [], () => @@ -249,42 +244,43 @@ gulp.task( "Generates a Markdown version of the Language Specification", [specMd]); +gulp.task("produce-LKG", /*help*/ false, ["scripts", "local", cancellationTokenJs, typingsInstallerJs, watchGuardJs], () => { + const expectedFiles = [ + tscJs, + typescriptServicesJs, + tsserverJs, + typescriptJs, + typescriptDts, + typescriptServicesDts, + tsserverlibraryDts, + tsserverlibraryDts, + typingsInstallerJs, + cancellationTokenJs + ].concat(libraryTargets); + const missingFiles = expectedFiles + .concat(localizationTargets) + .filter(f => !fs.existsSync(f)); + if (missingFiles.length > 0) { + 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", "dontUseDebugMode", ["scripts", "local", cancellationTokenJs, typingsInstallerJs, watchGuardJs], - () => { - const expectedFiles = [ - tscJs, - typescriptServicesJs, - tsserverJs, - typescriptJs, - typescriptDts, - typescriptServicesDts, - tsserverlibraryDts, - tsserverlibraryDts, - typingsInstallerJs, - cancellationTokenJs - ].concat(libraryTargets); - const missingFiles = expectedFiles - .concat(localizationTargets) - .filter(f => !fs.existsSync(f)); - if (missingFiles.length > 0) { - 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."); - } - }); - })); + () => runSequence("clean-built", "dontUseDebugMode", "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, [project(testRunnerProject, { typescript: "built", deps: [tsserverlibraryDts] })]); +gulp.task(runJs, /*help*/ false, [typescriptServicesJs, tsserverlibraryDts], () => project.compile(testRunnerProject, { typescript: "built" })); gulp.task( "tests", @@ -310,8 +306,8 @@ gulp.task( const webTestServerProject = "tests/webTestServer.tsconfig.json"; const webTestServerJs = "tests/webTestServer.js"; -gulp.task(webTestServerJs, /*help*/ false, [project(webTestServerProject, { typescript: "built", release: { compilerOptions: { removeComments: true } } })]) -gulp.task("clean:" + webTestServerJs, /*help*/ false, [project.clean(webTestServerProject, { typescript: "built" })]) +gulp.task(webTestServerJs, /*help*/ false, [typescriptServicesJs], () => project.compile(webTestServerProject, { typescript: "built" })); +gulp.task("clean:" + webTestServerJs, /*help*/ false, () => project.clean(webTestServerProject)); const bundlePath = path.resolve("built/local/bundle.js"); @@ -447,8 +443,8 @@ gulp.task( // Webhost const webtscProject = "tests/webhost/webtsc.tsconfig.json"; const webtscJs = "tests/webhost/webtsc.js"; -gulp.task(webtscJs, /*help*/ false, [project(webtscProject, { typescript: "built", release: { compilerOptions: { removeComments: true } } })]); -gulp.task("clean:" + webtscJs, /*help*/ false, [project.clean(webtscProject, { typescript: "built" })]); +gulp.task(webtscJs, /*help*/ false, [typescriptServicesJs], () => project.compile(webtscProject, { typescript: "built" })); +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") @@ -457,8 +453,8 @@ gulp.task("webhost", "Builds the tsc web host", [webtscJs], () => // Perf compiler const perftscProject = "tests/perftsc.tsconfig.json"; const perftscJs = "built/local/perftsc.js"; -gulp.task(perftscJs, /*help*/ false, [project(perftscProject, { typescript: "built", release: { compilerOptions: { removeComments: true } } })]); -gulp.task("clean:" + perftscJs, /*help*/ false, [project.clean(perftscProject, { typescript: "built" })]); +gulp.task(perftscJs, /*help*/ false, [typescriptServicesJs], () => project.compile(perftscProject, { typescript: "built" })); +gulp.task("clean:" + perftscJs, /*help*/ false, () => project.clean(perftscProject)); gulp.task( "perftsc", @@ -477,8 +473,8 @@ gulp.task(loggedIOJs, /*help*/ false, [], (done) => { const instrumenterProject = "src/instrumenter/tsconfig.json"; const instrumenterJs = "built/local/instrumenter.js"; -gulp.task(instrumenterJs, /*help*/ false, [project(instrumenterProject)]); -gulp.task("clean:" + instrumenterJs, /*help*/ false, [project.clean(instrumenterProject)]); +gulp.task(instrumenterJs, /*help*/ false, () => project.compile(instrumenterProject)); +gulp.task("clean:" + instrumenterJs, /*help*/ false, () => project.clean(instrumenterProject)); gulp.task( "tsc-instrumented", @@ -497,9 +493,9 @@ gulp.task( gulp.task( "build-rules", "Compiles tslint rules to js", - [project("scripts/tslint/tsconfig.json")]); + () => project.compile("scripts/tslint/tsconfig.json")); -gulp.task("clean-rules", /*help*/ false, [project.clean("scripts/tslint/tsconfig.json")]); +gulp.task("clean-rules", /*help*/ false, () => project.clean("scripts/tslint/tsconfig.json")); gulp.task( "lint", diff --git a/package-lock.json b/package-lock.json index 53cf762c995..ff52eb7be20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dev": true, "requires": { "acorn": "5.7.1", - "css": "2.2.1", + "css": "2.2.3", "normalize-path": "2.1.1", "source-map": "0.5.7", "through2": "2.0.3" @@ -408,6 +408,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "dev": true, "requires": { "ansi-wrap": "0.1.0" } @@ -439,7 +440,8 @@ "ansi-wrap": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "dev": true }, "append-buffer": { "version": "1.0.2", @@ -468,7 +470,8 @@ "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true }, "arr-flatten": { "version": "1.1.0", @@ -479,7 +482,8 @@ "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true }, "array-differ": { "version": "1.0.0", @@ -584,7 +588,8 @@ "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true }, "astw": { "version": "2.2.0", @@ -1171,7 +1176,8 @@ "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true }, "combine-source-map": { "version": "0.8.0", @@ -1353,23 +1359,17 @@ } }, "css": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.1.tgz", - "integrity": "sha1-c6TIHehdtmTU7mdPfUcIXjstVdw=", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.3.tgz", + "integrity": "sha512-0W171WccAjQGGTKLhw4m2nnl0zPHUlTO/I8td4XzJgIB8Hg3ZZx71qT4G4eX8OVsSiaAKiUMy73E3nsbPlg2DQ==", "dev": true, "requires": { "inherits": "2.0.3", "source-map": "0.1.43", - "source-map-resolve": "0.3.1", + "source-map-resolve": "0.5.1", "urix": "0.1.0" }, "dependencies": { - "atob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/atob/-/atob-1.1.3.tgz", - "integrity": "sha1-lfE2KbEsOlGl0hWr3OKqnzL4B3M=", - "dev": true - }, "source-map": { "version": "0.1.43", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", @@ -1378,24 +1378,6 @@ "requires": { "amdefine": "1.0.1" } - }, - "source-map-resolve": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.3.1.tgz", - "integrity": "sha1-YQ9hIqRFuN1RU1oqcbeD38Ekh2E=", - "dev": true, - "requires": { - "atob": "1.1.3", - "resolve-url": "0.2.1", - "source-map-url": "0.3.0", - "urix": "0.1.0" - } - }, - "source-map-url": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.3.0.tgz", - "integrity": "sha1-fsrxO1e80J2opAxdJp2zN5nUqvk=", - "dev": true } } }, @@ -1405,7 +1387,7 @@ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { - "es5-ext": "0.10.42" + "es5-ext": "0.10.45" } }, "date-now": { @@ -1632,12 +1614,6 @@ "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", "dev": true }, - "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", - "dev": true - }, "duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -1706,9 +1682,9 @@ } }, "es5-ext": { - "version": "0.10.42", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.42.tgz", - "integrity": "sha512-AJxO1rmPe1bDEfSR6TJ/FgMFYuTBhR5R57KW58iCkYACMyFbrkqVyzXSurYoScDGvgyMpk7uRF/lPUPPTmsRSA==", + "version": "0.10.45", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", + "integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", "dev": true, "requires": { "es6-iterator": "2.0.3", @@ -1723,7 +1699,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.42", + "es5-ext": "0.10.45", "es6-symbol": "3.1.1" } }, @@ -1757,7 +1733,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.42" + "es5-ext": "0.10.45" } }, "es6-weak-map": { @@ -1767,7 +1743,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.42", + "es5-ext": "0.10.45", "es6-iterator": "2.0.3", "es6-symbol": "3.1.1" } @@ -1828,7 +1804,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.42" + "es5-ext": "0.10.45" } }, "events": { @@ -1916,6 +1892,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, "requires": { "assign-symbols": "1.0.0", "is-extendable": "1.0.1" @@ -1925,6 +1902,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, "requires": { "is-plain-object": "2.0.4" } @@ -2000,6 +1978,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", + "dev": true, "requires": { "ansi-gray": "0.1.1", "color-support": "1.1.3", @@ -2760,7 +2739,7 @@ "@gulp-sourcemaps/map-sources": "1.0.0", "acorn": "5.7.1", "convert-source-map": "1.5.1", - "css": "2.2.1", + "css": "2.2.3", "debug-fabulous": "1.1.0", "detect-newline": "2.1.0", "graceful-fs": "4.1.11", @@ -3406,6 +3385,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, "requires": { "isobject": "3.0.1" } @@ -3473,7 +3453,8 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true }, "istanbul": { "version": "0.4.5", @@ -3672,15 +3653,6 @@ "dev": true, "optional": true }, - "lazypipe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazypipe/-/lazypipe-1.0.1.tgz", - "integrity": "sha1-FHGu9rN6NA1Rw030Rpnc7wZMGUA=", - "dev": true, - "requires": { - "stream-combiner": "0.2.2" - } - }, "lazystream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", @@ -3902,7 +3874,7 @@ "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", "dev": true, "requires": { - "es5-ext": "0.10.42" + "es5-ext": "0.10.45" } }, "make-iterator": { @@ -3955,7 +3927,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.42", + "es5-ext": "0.10.45", "es6-weak-map": "2.0.2", "event-emitter": "0.3.5", "is-promise": "2.1.0", @@ -4657,6 +4629,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, "requires": { "ansi-colors": "1.1.0", "arr-diff": "4.0.0", @@ -4668,6 +4641,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, "requires": { "ansi-wrap": "0.1.0" } @@ -5478,16 +5452,6 @@ "readable-stream": "2.3.6" } }, - "stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", - "dev": true, - "requires": { - "duplexer": "0.1.1", - "through": "2.3.8" - } - }, "stream-combiner2": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", @@ -5712,7 +5676,8 @@ "time-stamp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true }, "timers-browserify": { "version": "1.4.2", @@ -5729,7 +5694,7 @@ "integrity": "sha512-tsEStd7kmACHENhsUPaxb8Jf8/+GZZxyNFQbZD07HQOyooOa6At1rQqjffgvg7n+dxscQa9cjjMdWhJtsP2sxg==", "dev": true, "requires": { - "es5-ext": "0.10.42", + "es5-ext": "0.10.45", "next-tick": "1.0.0" } }, diff --git a/package.json b/package.json index ae87651d6c1..37e4cc663d6 100644 --- a/package.json +++ b/package.json @@ -61,8 +61,8 @@ "chalk": "latest", "convert-source-map": "latest", "del": "latest", - "fs-extra": "^6.0.1", "fancy-log": "latest", + "fs-extra": "^6.0.1", "gulp": "3.X", "gulp-clone": "latest", "gulp-concat": "latest", diff --git a/scripts/build/chalk.js b/scripts/build/chalk.js new file mode 100644 index 00000000000..149c8ea1533 --- /dev/null +++ b/scripts/build/chalk.js @@ -0,0 +1,5 @@ +// @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/colors.js b/scripts/build/colors.js deleted file mode 100644 index 91619364120..00000000000 --- a/scripts/build/colors.js +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-check -const chalk = /**@type {import("chalk").Chalk}*/(require("chalk").default || require("chalk")); - -exports.useColors = process.stdout.isTTY; -for (const arg of process.argv) { - if (arg === "--no-color") exports["useColors"] = false; - else if (arg === "--color") exports["useColors"] = true; -} - -/** - * @param {string} message - * @param {import("chalk").Chalk} color - */ -function addColor(message, color) { - return exports.useColors ? color(message) : message; -} -exports.addColor = addColor; - -/** @type {{[P in Exclude]: import("chalk").Chalk[P]}} */ -exports.color = chalk; \ No newline at end of file diff --git a/scripts/build/diagnostics.js b/scripts/build/diagnostics.js index dee74192875..5ca51c572ea 100644 --- a/scripts/build/diagnostics.js +++ b/scripts/build/diagnostics.js @@ -11,20 +11,37 @@ const formatDiagnosticsHost = exports.formatDiagnosticsHost = { /** * @param {Diagnostic[]} diagnostics - * @param {boolean} [pretty] + * @param {{ cwd?: string, pretty?: boolean }} [options] */ -function formatDiagnostics(diagnostics, pretty) { - return pretty ? ts.formatDiagnosticsWithColorAndContext(diagnostics, formatDiagnosticsHost) : - ts.formatDiagnostics(diagnostics, formatDiagnosticsHost); +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 */ -function reportDiagnostics(diagnostics) { - log(formatDiagnostics(diagnostics, process.stdout.isTTY)); +/** + * @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 diff --git a/scripts/build/exec.js b/scripts/build/exec.js index a7839cf1cb4..ca7c6e3f6c9 100644 --- a/scripts/build/exec.js +++ b/scripts/build/exec.js @@ -2,7 +2,7 @@ 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 { addColor, color } = require("./colors"); +const chalk = require("./chalk"); module.exports = exec; @@ -15,7 +15,7 @@ module.exports = exec; */ function exec(cmd, args, options = {}) { return /**@type {Promise<{exitCode: number}>}*/(new Promise((resolve, reject) => { - log(addColor(`${cmd} ${args.join(" ")}`, color.gray)); + log(`> ${chalk.green(cmd)} ${args.join(" ")}`); // 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(" ")}`]; diff --git a/scripts/build/gulp.js b/scripts/build/gulp.js index 9aaa70d4eef..40f27b9526e 100644 --- a/scripts/build/gulp.js +++ b/scripts/build/gulp.js @@ -1 +1,8 @@ +// @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 index 297b1db9bdd..f8dac7529e0 100644 --- a/scripts/build/lib.js +++ b/scripts/build/lib.js @@ -4,26 +4,27 @@ const path = require("path"); const gulp = require("./gulp"); const newer = require("gulp-newer"); const concat = require("gulp-concat"); - -module.exports = exports = makeLibraryTargets; +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 makeLibraryTargets(prepends) { - return 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 = prepends.concat(relativeSources.map(s => path.posix.join("src/lib", s))); - const target = `built/local/${relativeTarget}`; - gulp.task(target, /*help*/ false, [], () => - gulp.src(sources) - .pipe(newer(target)) - .pipe(concat(relativeTarget, { newLine: "\n\n" })) - .pipe(gulp.dest("built/local"))); - return target; - }); -} \ No newline at end of file +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/pass.js b/scripts/build/pass.js deleted file mode 100644 index bc3c5ee84d8..00000000000 --- a/scripts/build/pass.js +++ /dev/null @@ -1,11 +0,0 @@ -// @ts-check -const { PassThrough } = require("stream"); - -/** - * @returns {NodeJS.ReadWriteStream} - */ -function pass() { - return new PassThrough({ objectMode: true }); -} - -module.exports = pass; \ No newline at end of file diff --git a/scripts/build/prepend.js b/scripts/build/prepend.js deleted file mode 100644 index 8c44d329112..00000000000 --- a/scripts/build/prepend.js +++ /dev/null @@ -1,14 +0,0 @@ -// @ts-check -const fs = require("fs"); -const insert = require("gulp-insert"); -const pass = require("./pass"); - -/** - * @param {(() => string[] | undefined) | string[]} [prepends] - * @returns {NodeJS.ReadWriteStream} - */ -function prepend(prepends) { - if (typeof prepends === "function") prepends = prepends(); - return (prepends || []).reduce((stream, file) => stream.pipe(insert.prepend(fs.readFileSync(file))), pass()); -} -module.exports = prepend; \ No newline at end of file diff --git a/scripts/build/project.js b/scripts/build/project.js index 69a45e9d32e..f4c2b819c17 100644 --- a/scripts/build/project.js +++ b/scripts/build/project.js @@ -2,8 +2,6 @@ const path = require("path"); const fs = require("fs"); const gulp = require("./gulp"); -const PluginError = require("plugin-error"); -const log = require("fancy-log"); // was `require("gulp-util").log (see https://github.com/gulpjs/gulp-util) const gulpif = require("gulp-if"); const sourcemaps = require("gulp-sourcemaps"); const merge2 = require("merge2"); @@ -11,105 +9,140 @@ const tsc = require("gulp-typescript"); const tsc_oop = require("./gulp-typescript-oop"); const upToDate = require("./upToDate"); const ts = require("../../lib/typescript"); -const debugMode = require("./debugMode"); const del = require("del"); const needsUpdate = require("./needsUpdate"); const mkdirp = require("./mkdirp"); +const { reportDiagnostics } = require("./diagnostics"); +const { PassThrough } = require("stream"); -module.exports = exports = project; +class CompilationGulp extends gulp.Gulp { + /** + * @param {import("gulp-help").GulpHelp | import("gulp").Gulp} gulp + */ + constructor(gulp) { + super(); + // forward notifications to the outer gulp. + this.on("task_start", e => gulp.emit("task_start", e)); + this.on("task_stop", e => gulp.emit("task_stop", e)); + this.on("task_err", e => gulp.emit("task_err", e)); + this.on("task_not_found", e => gulp.emit("task_not_found", e)); + this.on("task_recursion", e => gulp.emit("task_recursion", e)); + this.on("err", e => gulp.emit("err", e)); + } -/** @type {Map>} */ -const typescriptProjects = new Map(); - -/** @type {Record} */ -const typescriptRegistry = { }; - -/** - * Defines a series of gulp tasks for a TypeScript project, returning the root task name. - * @param {string} projectSpec - * @param {ProjectOptions} [options] - * @param {(project: ParsedCommandLine, destPath: string, options: ResolvedProjectOptions) => NodeJS.ReadWriteStream} [task] - */ -function project(projectSpec, options, task) { - return makeProject(projectSpec, resolveProjectOptions(options), /*referer*/ undefined, task).taskName; + dispose() { + this.removeAllListeners(); + this.reset(); + } + + // Do not reset tasks when `gulp.start()` is called + _resetAllTasks() {} + _resetSpecificTasks() {} + _resetTask() {} } -exports.project = project; + +// internal `Gulp` instance for compilation artifacts. +const compilationGulp = new CompilationGulp(gulp); + +/** @type {Map} */ +const projectGraphCache = new Map(); + +/** @type {Map} */ +const typescriptAliasMap = new Map(); /** - * Defines a deferred gulp pipeline for a TypeScript project. - * @param {string} projectSpec - * @param {ProjectOptions} [options] - * @param {(project: ParsedCommandLine, destPath: string, options: ResolvedProjectOptions) => NodeJS.ReadWriteStream} [task] + * 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 {ProjectOptions} [options] Project compilation options. + * @returns {() => Promise} */ -function defer(projectSpec, options, task) { +function createCompiler(projectSpec, options) { const resolvedOptions = resolveProjectOptions(options); - resolvedOptions.defer = true; - const resolvedProject = prepareProject(projectSpec, resolvedOptions, /*referer*/ undefined, task); - return getProjectTaskFunction(resolvedProject); + const resolvedProjectSpec = resolveProjectSpec(projectSpec, resolvedOptions.paths, /*referrer*/ undefined); + const taskName = compileTaskName(ensureCompileTask(getOrCreateProjectGraph(resolvedProjectSpec, resolvedOptions.paths), resolvedOptions), resolvedOptions.typescript); + return () => new Promise((resolve, reject) => compilationGulp.start(taskName, err => err ? reject(err) : resolve(err))); } -exports.defer = defer; +exports.createCompiler = createCompiler; /** - * Defines a series of gulp tasks to clean the outputs of a TypeScript project, returning the root task name. - * @param {string} projectSpec - * @param {CleanOptions} [options] + * 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 {ProjectOptions} [options] Project compilation options. + * @returns {Promise} + * + * @typedef ProjectOptions + * @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. For multiple steps, use `stream-combiner`. + * @property {Hook} [dts] Pipeline hook for .d.ts file outputs. For multiple steps, use `stream-combiner`. + * @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 {NodeJS.ReadWriteStream | (() => 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 taskName = cleanTaskName(ensureCleanTask(getOrCreateProjectGraph(resolvedProjectSpec, paths))); + return () => new Promise((resolve, reject) => compilationGulp.start(taskName, err => err ? reject(err) : resolve(err))); +} +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) { - return makeClean(projectSpec, resolveCleanOptions(options), /*referer*/ undefined); + const cleaner = createCleaner(projectSpec, options); + return cleaner(); } exports.clean = clean; -/** - * Defines the default task behavior. - * @param {ParsedCommandLine} parsedProject - * @param {string} destPath - * @param {ResolvedTaskConfiguration} options - */ -function defaultTask(parsedProject, destPath, options) { - const { sourceMap, inlineSourceMap, inlineSources = false, sourceRoot, declarationMap } = parsedProject.options; - const configFilePath = parsedProject.options.configFilePath; - const sourceMapPath = inlineSourceMap ? undefined : "."; - const sourceMapOptions = { includeContent: inlineSources, sourceRoot, destPath }; - const project = options.inProcess - ? tsc.createProject(configFilePath, Object.assign({}, options.compilerOptions, { typescript: require(options.typescript)})) - : tsc_oop.createProject(configFilePath, Object.assign({}, options.compilerOptions), { typescript: options.typescript }); - const stream = project.src() - .pipe(gulpif(!options.force, upToDate(parsedProject, { verbose: options.verbose }))) - .pipe(gulpif(sourceMap || inlineSourceMap, sourcemaps.init())) - .pipe(project()); - 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([options.js ? options.js(js) : js, options.dts ? options.dts(dts) : dts]) - .pipe(gulp.dest(destPath)); -} -exports.defaultTask = defaultTask; - /** * Adds a named alias for a TypeScript language service path - * @param {string} alias - * @param {string} typescript - * @param {string[]} [deps] + * @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, deps) { - typescriptRegistry[alias] = { typescript, deps }; +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 - * @param {string} flattenedProjectSpec - * @param {FlattenOptions} [options] + * @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. */ function flatten(projectSpec, flattenedProjectSpec, options = {}) { + const paths = resolvePathOptions(options); const files = []; - const resolvedOutputSpec = path.resolve(flattenedProjectSpec); + const resolvedOutputSpec = path.resolve(paths.cwd, flattenedProjectSpec); const resolvedOutputDirectory = path.dirname(resolvedOutputSpec); - const resolvedProjectSpec = resolveProjectSpec(projectSpec); - const parsedProject = ts.getParsedCommandLineOfConfigFile(resolvedProjectSpec, {}, parseConfigFileHost); - recur(parsedProject); + const resolvedProjectSpec = resolveProjectSpec(projectSpec, paths, /*referrer*/ undefined); + const projectGraph = getOrCreateProjectGraph(resolvedProjectSpec, paths); + recur(projectGraph); const config = { extends: normalizeSlashes(path.relative(resolvedOutputDirectory, resolvedProjectSpec)), @@ -123,418 +156,384 @@ function flatten(projectSpec, flattenedProjectSpec, options = {}) { } /** - * @param {ParsedCommandLine} parsedProject + * @param {ProjectGraph} projectGraph */ - function recur(parsedProject) { - if (parsedProject.projectReferences) { - for (const ref of parsedProject.projectReferences) { - const resolvedProjectSpec = resolveProjectSpec(ref.path, parsedProject); - const referencedProject = ts.getParsedCommandLineOfConfigFile(resolvedProjectSpec, {}, parseConfigFileHost); - recur(referencedProject); - } + function recur(projectGraph) { + for (const ref of projectGraph.references) { + recur(ref.target); } - for (const file of parsedProject.fileNames) { - files.push(normalizeSlashes(path.relative(resolvedOutputDirectory, path.resolve(file)))); + for (const file of projectGraph.project.fileNames) { + files.push(normalizeSlashes(path.relative(resolvedOutputDirectory, path.resolve(projectGraph.projectDirectory, file)))); } } } exports.flatten = flatten; /** - * @param {string} typescript + * 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) { - /** @type {string[] | undefined} */ - let deps; - while (typescript in typescriptRegistry) { - const entry = typescriptRegistry[typescript]; - typescript = entry.typescript; - if (!deps) deps = entry.deps; +function resolveTypeScript(typescript, 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(process.cwd(), typescript); + typescript = path.resolve(paths.cwd, typescript); } - return { typescript, deps }; + 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 = { cwd: process.cwd(), base: process.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 {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`. + */ +function resolvePathOptions(options) { + const cwd = options && options.cwd ? path.resolve(process.cwd(), options.cwd) : process.cwd(); + const base = options && options.base ? path.resolve(cwd, options.base) : cwd; + return cwd === defaultPaths.cwd && base === defaultPaths.base ? defaultPaths : { cwd, base }; } /** * @param {ProjectOptions} [options] * @returns {ResolvedProjectOptions} + * + * @typedef ResolvedProjectOptions + * @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). */ function resolveProjectOptions(options = {}) { - const resolvedTypeScript = resolveTypeScript(options.typescript || "default"); + const paths = resolvePathOptions(options); + const typescript = resolveTypeScript(options.typescript || "default", paths); return { - typescript: resolvedTypeScript.typescript, - deps: concat(options.deps, resolvedTypeScript.deps), - compilerOptions: options.compilerOptions || {}, + paths, + typescript, js: options.js, dts: options.dts, - debug: resolveProjectConfiguration(options.debug), - release: resolveProjectConfiguration(options.release), verbose: options.verbose || false, force: options.force || false, - inProcess: options.inProcess || false, - defer: false + inProcess: options.inProcess || false }; } /** - * @param {CleanOptions} [options] - * @returns {ResolvedCleanOptions} + * @param {Hook} hook + * @returns {NodeJS.ReadWriteStream} */ -function resolveCleanOptions(options = {}) { - const resolvedTypeScript = resolveTypeScript(options.typescript || "default"); - return { typescript: resolvedTypeScript.typescript }; +function evaluateHook(hook) { + return (typeof hook === "function" ? hook() : hook) || new PassThrough({ objectMode: true }); } /** - * @param {ProjectConfiguration} [config] - * @returns {ResolvedProjectConfiguration} + * @param {ResolvedProjectOptions} left + * @param {ResolvedProjectOptions} right + * @returns {ResolvedProjectOptions} */ -function resolveProjectConfiguration(config = {}) { +function mergeProjectOptions(left, right) { + if (left.typescript !== right.typescript) throw new Error("Cannot merge project options targeting different TypeScript packages"); + if (tryReuseProjectOptions(left, right)) return left; return { - compilerOptions: config.compilerOptions || {}, - deps: config.deps, - js: config.js, - dts: config.dts, - force: config.force, - inProcess: config.inProcess + 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 }; } /** - * @param {ResolvedProjectOptions} a - * @param {ResolvedProjectOptions} b + * @param {ResolvedProjectOptions} left + * @param {ResolvedProjectOptions} right */ -function mergeProjectOptions(a, b) { - return { - typescript: a.typescript || b.typescript, - deps: distinct(concat(a.deps, b.deps)), - compilerOptions: Object.assign({}, a.compilerOptions, b.compilerOptions), - js: b.js || a.js, - dts: b.dts || a.dts, - debug: mergeProjectConfigurations(a.debug, b.debug), - release: mergeProjectConfigurations(a.release, b.release), - verbose: b.verbose || a.verbose, - force: b.force || a.force, - inProcess: b.inProcess || a.inProcess, - defer: a.defer || b.defer - }; +function tryReuseProjectOptions(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 {ResolvedProjectConfiguration} a - * @param {ResolvedProjectConfiguration} b + * @param {ResolvedProjectSpec} projectSpec + * @param {ResolvedPathOptions} paths + * @returns {UnqualifiedProjectName} + * + * @typedef {string & {_isUnqualifiedProjectName:never}} UnqualifiedProjectName */ -function mergeProjectConfigurations(a, b) { - return { - compilerOptions: Object.assign({}, a.compilerOptions, b.compilerOptions), - deps: concat(a.deps, b.deps), - js: b.js || a.js, - dts: b.dts || a.dts, - force: b.force !== undefined ? b.force : a.force, - inProcess: b.inProcess !== undefined ? b.inProcess : a.inProcess - }; +function getUnqualifiedProjectName(projectSpec, paths) { + return /**@type {UnqualifiedProjectName}*/(normalizeSlashes(path.relative(paths.base, projectSpec))); } /** - * @param {ResolvedProjectOptions} options - * @returns {ResolvedTaskConfiguration} + * @param {UnqualifiedProjectName} projectName + * @param {ResolvedPathOptions} paths + * @param {ResolvedTypeScript} typescript + * @returns {QualifiedProjectName} + * + * @typedef {string & {_isQualifiedProjectName:never}} QualifiedProjectName */ -function getResolvedTaskConfiguration(options) { - const config = debugMode.useDebugMode ? options.debug : options.release - return { - typescript: options.typescript, - compilerOptions: Object.assign({}, options.compilerOptions, config.compilerOptions), - deps: concat(options.deps, config.deps), - dts: config.dts || options.dts, - js: config.js || options.js, - verbose: options.verbose, - force: config.force !== undefined ? config.force : options.force, - inProcess: config.inProcess !== undefined ? config.inProcess : options.inProcess - }; +function getQualifiedProjectName(projectName, paths, typescript) { + return /**@type {QualifiedProjectName}*/(projectName + getTaskNameSuffix(typescript, paths)); } -/** - * @template T - * @param {T[] | undefined} a - * @param {T[] | undefined} b - * @returns {T[] | undefined} +/** + * @typedef {import("../../lib/typescript").ParseConfigFileHost} ParseConfigFileHost + * @type {ParseConfigFileHost} */ -function concat(a, b) { - return a ? b ? a.concat(b) : a : b; -} - -/** - * @template T - * @param {T[] | undefined} ar - * @returns {T[] | undefined} - */ - -function distinct(ar) { - return ar && [...new Set(ar)]; -} - -/** - * @param {string} projectSpec - * @param {ResolvedProjectOptions} resolvedOptions - * @param {ParsedCommandLine} [referer] - * @param {Task} [task] - */ -function prepareProject(projectSpec, resolvedOptions, referer, task) { - const resolvedProject = resolveProject(projectSpec, resolvedOptions, referer); - if (resolvedProject.resolvedOptions !== resolvedOptions) { - resolvedProject.resolvedOptions = mergeProjectOptions(resolvedProject.resolvedOptions, resolvedOptions); - } - if (task) resolvedProject.task = task; - return resolvedProject; -} - -/** - * @param {string} projectSpec - * @param {ResolvedProjectOptions} resolvedOptions - * @param {ParsedCommandLine} [referer] - * @param {Task} [task] - */ -function makeProject(projectSpec, resolvedOptions, referer, task) { - const resolvedProject = prepareProject(projectSpec, resolvedOptions, referer, task); - if (!resolvedProject.projectTaskCreated || resolvedProject.task) { - createProjectTask(resolvedProject); - } - return resolvedProject; -} - -/** - * @param {string} projectSpec - * @param {ResolvedCleanOptions} resolvedOptions - * @param {ParsedCommandLine} [referer] - */ -function makeClean(projectSpec, resolvedOptions, referer) { - const projects = getProjects(resolvedOptions); - const resolvedProjectSpec = resolveProjectSpec(projectSpec, referer, projects); - const resolvedProject = projects.get(resolvedProjectSpec); - if (!resolvedProject) throw new PluginError("clean", `Project not found: '${projectSpec}'. Add project via 'project()' first.`); - if (!resolvedProject.cleanTaskCreated) { - createCleanTask(resolvedProject); - } - return "clean:" + resolvedProject.projectName; -} - -/** - * @param {ResolvedProjectOptions | ResolvedCleanOptions} resolvedOptions - */ -function getProjects(resolvedOptions) { - let projects = typescriptProjects.get(resolvedOptions.typescript); - if (!projects) typescriptProjects.set(resolvedOptions.typescript, projects = new Map()); - return projects; -} - -/** - * @param {string} resolvedProjectSpec - * @param {ResolvedProjectOptions} resolvedOptions - */ -function getProjectName(resolvedProjectSpec, resolvedOptions) { - let projectName = path.relative(process.cwd(), resolvedProjectSpec); - if (resolvedOptions.typescript !== resolveTypeScript("default").typescript) { - projectName += `@${isPath(resolvedOptions.typescript) ? path.relative(process.cwd(), resolvedOptions.typescript) : resolvedOptions.typescript}`; - } - return normalizeSlashes(projectName); -} - -/** @type {FormatDiagnosticsHost} */ -const formatDiagnosticsHost = { - getCanonicalFileName: fileName => fileName, - getCurrentDirectory: () => process.cwd(), - getNewLine: () => ts.sys.newLine -}; - -/** @type {ParseConfigFileHost} */ const parseConfigFileHost = { useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames, fileExists: fileName => ts.sys.fileExists(fileName), readFile: fileName => ts.sys.readFile(fileName), - getCurrentDirectory: () => ts.sys.getCurrentDirectory(), + getCurrentDirectory: () => process.cwd(), readDirectory: (rootDir, extensions, exclude, include, depth) => ts.sys.readDirectory(rootDir, extensions, exclude, include, depth), - onUnRecoverableConfigFileDiagnostic: diagnostic => { - log.warn(ts.formatDiagnosticsWithColorAndContext([diagnostic], formatDiagnosticsHost)); - } + onUnRecoverableConfigFileDiagnostic: diagnostic => reportDiagnostics([diagnostic]) }; /** - * @param {string} projectSpec - * @param {ResolvedProjectOptions} resolvedOptions - * @param {ParsedCommandLine} [referer] + * @param {string} [cwd] + * @returns {ParseConfigFileHost} */ -function resolveProject(projectSpec, resolvedOptions, referer) { - const projects = getProjects(resolvedOptions); - const resolvedProjectSpec = resolveProjectSpec(projectSpec, referer, projects); - let resolvedProject = projects.get(resolvedProjectSpec); - if (!resolvedProject) { - const projectName = getProjectName(resolvedProjectSpec, resolvedOptions); - resolvedProject = { - resolvedProjectSpec, - projectName, - taskName: "project:" + projectName, - parsedProject: undefined, - resolvedOptions, - destPath: undefined, - projectTaskCreated: false, - cleanTaskCreated: false, - task: undefined, - projectTask: undefined +function getParseConfigFileHost(cwd) { + if (!cwd || cwd === process.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 {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 {string} 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 {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). + * + * @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 = ts.getParsedCommandLineOfConfigFile(projectSpec, {}, getParseConfigFileHost(paths.cwd)); + projectGraph = { + projectSpec, + projectName: getUnqualifiedProjectName(projectSpec, paths), + projectDirectory: path.dirname(projectSpec), + project, + references: [], + configurations: new Map(), + cleanTaskCreated: false }; - if (!resolvedOptions.defer) { - getParsedProject(resolvedProject); - getDestPath(resolvedProject); + 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); + } } - projects.set(resolvedProjectSpec, resolvedProject); } - return resolvedProject; + return projectGraph; } /** - * @param {ResolvedProject} resolvedProject + * @param {ProjectGraph} projectGraph + * @param {ResolvedProjectOptions} resolvedOptions + * @returns {ProjectGraphConfiguration} + * + * @typedef ProjectGraphConfiguration + * @property {QualifiedProjectName} projectName + * @property {ResolvedProjectOptions} resolvedOptions + * @property {boolean} compileTaskCreated */ -function getParsedProject(resolvedProject) { - if (!resolvedProject.parsedProject) { - resolvedProject.parsedProject = ts.getParsedCommandLineOfConfigFile( - resolvedProject.resolvedProjectSpec, - resolvedProject.resolvedOptions.compilerOptions, - parseConfigFileHost); +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 resolvedProject.parsedProject; -} - -/** - * @param {ResolvedProject} resolvedProject - */ -function getDestPath(resolvedProject) { - if (!resolvedProject.destPath) { - resolvedProject.destPath = resolveDestPath(getParsedProject(resolvedProject)); - } - return resolvedProject.destPath; + return projectGraphConfig; } /** * @param {string} projectSpec - * @param {ParsedCommandLine} [referer] - * @param {Map} [projects] + * @param {ResolvedPathOptions} paths + * @param {ProjectGraph | undefined} referrer + * @returns {ResolvedProjectSpec} + * + * @typedef {string & {_isResolvedProjectSpec: never}} ResolvedProjectSpec */ -function resolveProjectSpec(projectSpec, referer, projects) { - projectSpec = referer ? path.resolve(path.dirname(referer.options.configFilePath), projectSpec) : path.resolve(projectSpec); - projectSpec = normalizeSlashes(projectSpec); - - // quick checks for existing project in cache - if (projects && projects.has(projectSpec)) return projectSpec; - - const configSpec = normalizeSlashes(path.join(projectSpec, "tsconfig.json")); - if (projects && projects.has(configSpec)) return configSpec; - - // slower checks against file system - if (fs.existsSync(projectSpec) && fs.statSync(projectSpec).isDirectory()) { - return configSpec; - } - - return projectSpec; +function resolveProjectSpec(projectSpec, paths, referrer) { + projectSpec = path.resolve(paths.cwd, referrer && referrer.projectDirectory || "", projectSpec); + if (!ts.sys.fileExists(projectSpec)) projectSpec = path.join(projectSpec, "tsconfig.json"); + return /**@type {ResolvedProjectSpec}*/(normalizeSlashes(projectSpec)); } /** - * @param {ParsedCommandLine} parsedProject + * @param {ProjectGraph} projectGraph + * @param {ResolvedPathOptions} paths */ -function resolveDestPath(parsedProject) { +function resolveDestPath(projectGraph, paths) { /** @type {string} */ - let destPath = path.dirname(parsedProject.options.configFilePath); - if (parsedProject.options.outDir) { - destPath = path.resolve(destPath, parsedProject.options.outDir); + let destPath = projectGraph.projectDirectory; + if (projectGraph.project.options.outDir) { + destPath = path.resolve(paths.cwd, destPath, projectGraph.project.options.outDir); } - else if (parsedProject.options.outFile || parsedProject.options.out) { - destPath = path.dirname(path.resolve(destPath, parsedProject.options.outFile || parsedProject.options.out)); + else if (projectGraph.project.options.outFile || projectGraph.project.options.out) { + destPath = path.dirname(path.resolve(paths.cwd, destPath, projectGraph.project.options.outFile || projectGraph.project.options.out)); } - return normalizeSlashes(path.relative(process.cwd(), destPath)); + return normalizeSlashes(path.relative(paths.base, destPath)); } /** - * @param {ResolvedProject} resolvedProject - */ -function createProjectTask(resolvedProject) { - const { parsedProject, resolvedOptions, taskName } = resolvedProject; - const deps = makeProjectDeps(parsedProject, resolvedOptions); - gulp.task(taskName, /*help*/ false, (resolvedOptions.deps || []).concat(deps), getProjectTaskFunction(resolvedProject)); - resolvedProject.projectTaskCreated = true; -} - -/** - * @param {ResolvedProject} resolvedProject - */ -function getProjectTaskFunction(resolvedProject) { - if (!resolvedProject.projectTask) { - resolvedProject.projectTask = () => (resolvedProject.task || exports.defaultTask)( - getParsedProject(resolvedProject), - getDestPath(resolvedProject), - getResolvedTaskConfiguration(resolvedProject.resolvedOptions)); - } - return resolvedProject.projectTask; -} - -/** - * @param {ParsedCommandLine | undefined} parsedProject + * @param {ProjectGraph} projectGraph * @param {ResolvedProjectOptions} resolvedOptions */ -function makeProjectDeps(parsedProject, resolvedOptions) { - /** @type {string[]} */ - const deps = []; - if (parsedProject && parsedProject.projectReferences) { - for (const projectReference of parsedProject.projectReferences) { - const projectReferenceOptions = resolveProjectOptions({ - typescript: resolvedOptions.typescript, - deps: resolvedOptions.deps - }); - deps.push(makeProject(projectReference.path, projectReferenceOptions, parsedProject).taskName); - } +function ensureCompileTask(projectGraph, resolvedOptions) { + const projectGraphConfig = getOrCreateProjectGraphConfiguration(projectGraph, resolvedOptions); + projectGraphConfig.resolvedOptions = resolvedOptions = mergeProjectOptions(resolvedOptions, resolvedOptions); + if (!projectGraphConfig.compileTaskCreated) { + const deps = makeProjectReferenceCompileTasks(projectGraph, resolvedOptions.typescript, resolvedOptions.paths); + compilationGulp.task(compileTaskName(projectGraph, resolvedOptions.typescript), deps, () => { + const destPath = resolveDestPath(projectGraph, 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 = resolvedOptions.inProcess + ? tsc.createProject(configFilePath, { typescript: require(resolvedOptions.typescript.typescript) }) + : tsc_oop.createProject(configFilePath, {}, { typescript: resolvedOptions.typescript.typescript }); + const stream = project.src() + .pipe(gulpif(!resolvedOptions.force, upToDate(projectGraph.project, { verbose: resolvedOptions.verbose }))) + .pipe(gulpif(sourceMap || inlineSourceMap, sourcemaps.init())) + .pipe(project()); + const js = stream.js + .pipe(evaluateHook(resolvedOptions.js)) + .pipe(gulpif(sourceMap || inlineSourceMap, sourcemaps.write(sourceMapPath, sourceMapOptions))); + const dts = stream.dts + .pipe(evaluateHook(resolvedOptions.dts)) + .pipe(gulpif(declarationMap, sourcemaps.write(sourceMapPath, sourceMapOptions))); + return merge2([js, dts]) + .pipe(gulp.dest(destPath)); + }); + projectGraphConfig.compileTaskCreated = true; } - return deps; + return projectGraph; } /** - * @param {ResolvedProject} resolvedProject + * @param {ProjectGraph} projectGraph + * @param {ResolvedTypeScript} typescript + * @param {ResolvedPathOptions} paths */ -function createCleanTask(resolvedProject) { - const { parsedProject, resolvedOptions, projectName } = resolvedProject; - const deps = makeCleanDeps(parsedProject, resolvedOptions); - gulp.task("clean:" + projectName, /*help*/ false, deps, () => { - let outputs = ts.getAllProjectOutputs(parsedProject); - if (!parsedProject.options.inlineSourceMap) { - if (parsedProject.options.sourceMap) { - outputs = outputs.concat(outputs.filter(file => /\.jsx?$/.test(file)).map(file => file + ".map")); - } - if (parsedProject.options.declarationMap) { - outputs = outputs.concat(outputs.filter(file => /\.d.ts$/.test(file)).map(file => file + ".map")); - } - } - return del(outputs); - }); - resolvedProject.cleanTaskCreated = true; +function makeProjectReferenceCompileTasks(projectGraph, typescript, paths) { + return projectGraph.references.map(({target}) => compileTaskName(ensureCompileTask(target, { paths, typescript }), typescript)); } /** - * @param {ParsedCommandLine} parsedProject - * @param {ResolvedCleanOptions} resolvedOptions + * @param {ProjectGraph} projectGraph */ -function makeCleanDeps(parsedProject, resolvedOptions) { - /** @type {string[]} */ - const deps = []; - if (parsedProject.projectReferences) { - for (const projectReference of parsedProject.projectReferences) { - const projectReferenceOptions = resolveCleanOptions({ typescript: resolvedOptions.typescript }); - deps.push(makeClean(projectReference.path, projectReferenceOptions, parsedProject)); - } +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 deps; + return projectGraph; +} + +/** + * @param {ProjectGraph} projectGraph + */ +function makeProjectReferenceCleanTasks(projectGraph) { + return projectGraph.references.map(({target}) => cleanTaskName(ensureCleanTask(target))); +} + +/** + * @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}`; } /** @@ -555,83 +554,5 @@ function isPath(moduleSpec) { /** * @typedef {import("../../lib/typescript").ParsedCommandLine & { options: CompilerOptions }} ParsedCommandLine * @typedef {import("../../lib/typescript").CompilerOptions & { configFilePath?: string }} CompilerOptions - * @typedef {import("../../lib/typescript").ParseConfigFileHost} ParseConfigFileHost - * @typedef {import("../../lib/typescript").FormatDiagnosticsHost} FormatDiagnosticsHost - * @typedef {(project: ParsedCommandLine, destPath: string, options: ResolvedTaskConfiguration) => NodeJS.ReadWriteStream} Task - * - * @typedef ProjectOptions - * @property {string} [typescript] A module specifier or path (relative to gulpfile.js) to the version of TypeScript to use. - * @property {string[]} [deps] Gulp task dependencies for all tasks created for a project. - * @property {CompilerOptions} [compilerOptions] Default options to pass to the compiler. - * @property {(stream: NodeJS.ReadWriteStream) => NodeJS.ReadWriteStream} [js] Pipeline hook for .js file outputs. - * @property {(stream: NodeJS.ReadWriteStream) => NodeJS.ReadWriteStream} [dts] Pipeline hook for .d.ts file outputs. - * @property {ProjectConfiguration} [debug] Project option overrides when building a "debug" build. - * @property {ProjectConfiguration} [release] Project option overrides when building a "release" build. - * @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 ProjectConfiguration - * @property {CompilerOptions} [compilerOptions] Default options to pass to the compiler. - * @property {string[]} [deps] Gulp task dependencies for all tasks created for a project. - * @property {(stream: NodeJS.ReadWriteStream) => NodeJS.ReadWriteStream} [js] Pipeline hook for .js file outputs. - * @property {(stream: NodeJS.ReadWriteStream) => NodeJS.ReadWriteStream} [dts] Pipeline hook for .d.ts file outputs. - * @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 ResolvedProjectOptions - * @property {string} typescript A module specifier or path (relative to gulpfile.js) to the version of TypeScript to use. - * @property {CompilerOptions} compilerOptions Default options to pass to the compiler. - * @property {string[] | undefined} deps Gulp task dependencies for all tasks created for a project. - * @property {(stream: NodeJS.ReadWriteStream) => NodeJS.ReadWriteStream | undefined} js Pipeline hook for .js file outputs. - * @property {(stream: NodeJS.ReadWriteStream) => NodeJS.ReadWriteStream | undefined} dts Pipeline hook for .d.ts file outputs. - * @property {ResolvedProjectConfiguration} debug Project option overrides when building a "debug" build. - * @property {ResolvedProjectConfiguration} release Project option overrides when building a "release" build. - * @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} defer Defer loading the project until later (not supported for projects with project references). - * - * @typedef ResolvedProjectConfiguration - * @property {CompilerOptions} compilerOptions Project configuration overrides to pass to the compiler. - * @property {string[] | undefined} deps Gulp task dependencies for all tasks created for a project. - * @property {(stream: NodeJS.ReadWriteStream) => NodeJS.ReadWriteStream | undefined} js Pipeline hook for .js file outputs. - * @property {(stream: NodeJS.ReadWriteStream) => NodeJS.ReadWriteStream | undefined} dts Pipeline hook for .d.ts file outputs. - * @property {boolean | undefined} force Force recompilation (no up-to-date check). - * @property {boolean | undefined} inProcess Indicates whether to run gulp-typescript in-process or out-of-process (default). - * - * @typedef ResolvedTaskConfiguration - * @property {string} typescript - * @property {CompilerOptions} compilerOptions Project configuration overrides to pass to the compiler. - * @property {string[] | undefined} deps Gulp task dependencies for all tasks created for a project. - * @property {(stream: NodeJS.ReadWriteStream) => NodeJS.ReadWriteStream | undefined} js Pipeline hook for .js file outputs. - * @property {(stream: NodeJS.ReadWriteStream) => NodeJS.ReadWriteStream | undefined} 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 ResolvedProject - * @property {string} resolvedProjectSpec - * @property {string} projectName - * @property {string} taskName - * @property {ParsedCommandLine | undefined} parsedProject - * @property {string | undefined} destPath - * @property {ResolvedProjectOptions} resolvedOptions - * @property {Task | undefined} task - * @property {(() => NodeJS.ReadWriteStream) | undefined} projectTask - * @property {boolean} projectTaskCreated - * @property {boolean} cleanTaskCreated - * - * @typedef CleanOptions - * @property {string} [typescript] A module specifier or path (relative to gulpfile.js) to the version of TypeScript to use. - * - * @typedef ResolvedCleanOptions - * @property {string | undefined} typescript A module specifier or path (relative to gulpfile.js) to the version of TypeScript to use. - * - * @typedef FlattenOptions - * @property {CompilerOptions} [compilerOptions] - * @property {string} [base] - * @property {boolean} [force] */ -void 0; - +void 0; \ No newline at end of file diff --git a/scripts/build/upToDate.js b/scripts/build/upToDate.js index 5ba0c80e034..07e5080b576 100644 --- a/scripts/build/upToDate.js +++ b/scripts/build/upToDate.js @@ -3,7 +3,7 @@ const path = require("path"); 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 { addColor, color } = require("./colors"); +const chalk = require("./chalk"); const Vinyl = require("vinyl"); /** @@ -86,7 +86,7 @@ function normalizeSlashes(file) { * @private */ function fileName(file) { - return addColor(normalizeSlashes(path.relative(process.cwd(), path.resolve(file))), color.cyan); + return chalk.cyan(normalizeSlashes(path.relative(process.cwd(), path.resolve(file)))); } /** diff --git a/src/compiler/tsbuild.ts b/src/compiler/tsbuild.ts index 9055fc1030c..007ec1afc7c 100644 --- a/src/compiler/tsbuild.ts +++ b/src/compiler/tsbuild.ts @@ -1016,12 +1016,6 @@ namespace ts { * Gets the UpToDateStatus for a project */ export function getUpToDateStatus(host: UpToDateHost, project: ParsedCommandLine | undefined): UpToDateStatus { - if (project === undefined) { - return { - type: UpToDateStatusType.Unbuildable, reason: "File deleted mid-build" - }; - } - if (project === undefined) { return { type: UpToDateStatusType.Unbuildable, reason: "File deleted mid-build" }; }