From f4ca7ee7676128c410cafbab9ce8525a36e016d1 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 4 May 2018 10:45:16 -0700 Subject: [PATCH] Fork gulp-typescript tasks to run out-of-process --- Gulpfile.js | 11 +++- scripts/build/gulp-typescript-oop.js | 88 +++++++++++++++++++++++++++ scripts/build/main.js | 91 ++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 scripts/build/gulp-typescript-oop.js create mode 100644 scripts/build/main.js diff --git a/Gulpfile.js b/Gulpfile.js index 9a082bb4479..08392070099 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -11,6 +11,7 @@ const concat = require("gulp-concat"); const clone = require("gulp-clone"); const newer = require("gulp-newer"); const tsc = require("gulp-typescript"); +const tsc_oop = require("./scripts/build/gulp-typescript-oop"); const insert = require("gulp-insert"); const sourcemaps = require("gulp-sourcemaps"); const Q = require("q"); @@ -409,6 +410,10 @@ function prependCopyright(outputCopyright = !useDebugMode) { return insert.prepend(outputCopyright ? (copyrightContent || (copyrightContent = fs.readFileSync(copyright).toString())) : ""); } +function getCompilerPath(useBuiltCompiler) { + return useBuiltCompiler ? "./built/local/typescript.js" : "./lib/typescript.js"; +} + gulp.task(builtLocalCompiler, /*help*/ false, [servicesFile], () => { const localCompilerProject = tsc.createProject("src/compiler/tsconfig.json", getCompilerSettings({}, /*useBuiltCompiler*/ true)); return localCompilerProject.src() @@ -421,7 +426,7 @@ gulp.task(builtLocalCompiler, /*help*/ false, [servicesFile], () => { }); gulp.task(servicesFile, /*help*/ false, ["lib", "generate-diagnostics"], () => { - const servicesProject = tsc.createProject("src/services/tsconfig.json", getCompilerSettings({ removeComments: false }, /*useBuiltCompiler*/ false)); + const servicesProject = tsc_oop.createProject("src/services/tsconfig.json", getCompilerSettings({ removeComments: false }), { typescript: getCompilerPath(/*useBuiltCompiler*/ false) }); const {js, dts} = servicesProject.src() .pipe(newer(servicesFile)) .pipe(sourcemaps.init()) @@ -496,7 +501,7 @@ const tsserverLibraryFile = path.join(builtLocalDirectory, "tsserverlibrary.js") const tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibrary.d.ts"); gulp.task(tsserverLibraryFile, /*help*/ false, [servicesFile, typesMapJson], (done) => { - const serverLibraryProject = tsc.createProject("src/server/tsconfig.library.json", getCompilerSettings({ removeComments: false }, /*useBuiltCompiler*/ true)); + const serverLibraryProject = tsc_oop.createProject("src/server/tsconfig.library.json", getCompilerSettings({ removeComments: false }), { typescript: getCompilerPath(/*useBuiltCompiler*/ true) }); /** @type {{ js: NodeJS.ReadableStream, dts: NodeJS.ReadableStream }} */ const {js, dts} = serverLibraryProject.src() .pipe(sourcemaps.init()) @@ -587,7 +592,7 @@ gulp.task("LKG", "Makes a new LKG out of the built js files", ["clean", "dontUse // Task to build the tests infrastructure using the built compiler const run = path.join(builtLocalDirectory, "run.js"); gulp.task(run, /*help*/ false, [servicesFile, tsserverLibraryFile], () => { - const testProject = tsc.createProject("src/harness/tsconfig.json", getCompilerSettings({}, /*useBuiltCompiler*/ true)); + const testProject = tsc_oop.createProject("src/harness/tsconfig.json", getCompilerSettings({}), { typescript: getCompilerPath(/*useBuiltCompiler*/ true) }); return testProject.src() .pipe(newer(run)) .pipe(sourcemaps.init()) diff --git a/scripts/build/gulp-typescript-oop.js b/scripts/build/gulp-typescript-oop.js new file mode 100644 index 00000000000..d4b494e6435 --- /dev/null +++ b/scripts/build/gulp-typescript-oop.js @@ -0,0 +1,88 @@ +// @ts-check +const path = require("path"); +const child_process = require("child_process"); +const tsc = require("gulp-typescript"); +const Vinyl = require("vinyl"); +const { Duplex, Readable } = require("stream"); + +/** + * @param {string} tsConfigFileName + * @param {tsc.Settings} settings + * @param {Object} options + * @param {string} [options.typescript] + */ +function createProject(tsConfigFileName, settings, options) { + settings = { ...settings }; + options = { ...options }; + if (settings.typescript) throw new Error(); + + const localSettings = { ...settings }; + if (options.typescript) { + options.typescript = path.resolve(options.typescript); + localSettings.typescript = require(options.typescript); + } + + const project = tsc.createProject(tsConfigFileName, localSettings); + const wrappedProject = /** @type {tsc.Project} */(() => { + const proc = child_process.fork(require.resolve("./main.js")); + /** @type {Duplex & { js?: Readable, dts?: Readable }} */ + const compileStream = new Duplex({ + objectMode: true, + read() {}, + /** @param {*} file */ + write(file, encoding, callback) { + proc.send({ method: "write", params: { path: file.path, cwd: file.cwd, base: file.base }}); + callback(); + }, + final(callback) { + proc.send({ method: "final" }); + callback(); + } + }); + const jsStream = compileStream.js = new Readable({ + objectMode: true, + read() {} + }); + const dtsStream = compileStream.dts = new Readable({ + objectMode: true, + read() {} + }); + proc.send({ method: "createProject", params: { tsConfigFileName, settings, options } }); + proc.on("message", ({ method, params }) => { + if (method === "write") { + const file = new Vinyl({ + path: params.path, + cwd: params.cwd, + base: params.base, + contents: Buffer.from(params.contents, "utf8") + }); + if (params.sourceMap) file.sourceMap = params.sourceMap + compileStream.push(file);; + if (file.path.endsWith(".d.ts")) { + dtsStream.push(file); + } + else { + jsStream.push(file); + } + } + else if (method === "final") { + compileStream.push(null); + jsStream.push(null); + dtsStream.push(null); + proc.kill(); + } + else if (method === "error") { + const error = new Error(); + error.name = params.name; + error.message = params.message; + error.stack = params.stack; + compileStream.emit("error", error); + proc.kill(); + } + }); + return /** @type {*} */(compileStream); + }); + return Object.assign(wrappedProject, project); +} + +exports.createProject = createProject; \ No newline at end of file diff --git a/scripts/build/main.js b/scripts/build/main.js new file mode 100644 index 00000000000..3dcec4880d7 --- /dev/null +++ b/scripts/build/main.js @@ -0,0 +1,91 @@ +// @ts-check +const path = require("path"); +const fs = require("fs"); +const tsc = require("gulp-typescript"); +const Vinyl = require("vinyl"); +const { Readable, Writable } = require("stream"); + +/** @type {tsc.Project} */ +let project; + +/** @type {Readable} */ +let inputStream; + +/** @type {Writable} */ +let outputStream; + +/** @type {tsc.CompileStream} */ +let compileStream; + +process.on("message", ({ method, params }) => { + try { + if (method === "createProject") { + const { tsConfigFileName, settings, options } = params; + if (options.typescript) { + settings.typescript = require(options.typescript); + } + project = tsc.createProject(tsConfigFileName, settings); + inputStream = new Readable({ + objectMode: true, + read() {} + }); + outputStream = new Writable({ + objectMode: true, + /** + * @param {*} file + */ + write(file, encoding, callback) { + process.send({ + method: "write", + params: { + path: file.path, + cwd: file.cwd, + base: file.base, + contents: file.contents.toString(), + sourceMap: file.sourceMap + } + }); + callback(); + }, + final(callback) { + process.send({ method: "final" }); + callback(); + } + }); + outputStream.on("error", error => { + process.send({ + method: "error", + params: { + name: error.name, + message: error.message, + stack: error.stack + } + }); + }); + compileStream = project(); + inputStream.pipe(compileStream).pipe(outputStream); + } + else if (method === "write") { + const file = new Vinyl({ + path: params.path, + cwd: params.cwd, + base: params.base + }); + file.contents = fs.readFileSync(file.path); + inputStream.push(/** @type {*} */(file)); + } + else if (method === "final") { + inputStream.push(null); + } + } + catch (e) { + process.send({ + method: "error", + params: { + name: e.name, + message: e.message, + stack: e.stack + } + }); + } +}); \ No newline at end of file