@angular/build
Version:
Official build system for Angular
119 lines (118 loc) • 5.41 kB
JavaScript
;
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParallelCompilation = void 0;
const node_module_1 = require("node:module");
const node_worker_threads_1 = require("node:worker_threads");
const worker_pool_1 = require("../../../utils/worker-pool");
const angular_compilation_1 = require("./angular-compilation");
/**
* An Angular compilation which uses a Node.js Worker thread to load and execute
* the TypeScript and Angular compilers. This allows for longer synchronous actions
* such as semantic and template diagnostics to be calculated in parallel to the
* other aspects of the application bundling process. The worker thread also has
* a separate memory pool which significantly reduces the need for adjusting the
* main Node.js CLI process memory settings with large application code sizes.
*/
class ParallelCompilation extends angular_compilation_1.AngularCompilation {
jit;
browserOnlyBuild;
#worker;
constructor(jit, browserOnlyBuild) {
super();
this.jit = jit;
this.browserOnlyBuild = browserOnlyBuild;
// TODO: Convert to import.meta usage during ESM transition
const localRequire = (0, node_module_1.createRequire)(__filename);
this.#worker = new worker_pool_1.WorkerPool({
maxThreads: 1,
idleTimeout: Infinity,
filename: localRequire.resolve('./parallel-worker'),
});
}
initialize(tsconfig, hostOptions, compilerOptionsTransformer) {
const stylesheetChannel = new node_worker_threads_1.MessageChannel();
// The request identifier is required because Angular can issue multiple concurrent requests
stylesheetChannel.port1.on('message', ({ requestId, data, containingFile, stylesheetFile, order, className }) => {
hostOptions
.transformStylesheet(data, containingFile, stylesheetFile, order, className)
.then((value) => stylesheetChannel.port1.postMessage({ requestId, value }))
.catch((error) => stylesheetChannel.port1.postMessage({ requestId, error }));
});
// The web worker processing is a synchronous operation and uses shared memory combined with
// the Atomics API to block execution here until a response is received.
const webWorkerChannel = new node_worker_threads_1.MessageChannel();
const webWorkerSignal = new Int32Array(new SharedArrayBuffer(4));
webWorkerChannel.port1.on('message', ({ workerFile, containingFile }) => {
try {
const workerCodeFile = hostOptions.processWebWorker(workerFile, containingFile);
webWorkerChannel.port1.postMessage({ workerCodeFile });
}
catch (error) {
webWorkerChannel.port1.postMessage({ error });
}
finally {
Atomics.store(webWorkerSignal, 0, 1);
Atomics.notify(webWorkerSignal, 0);
}
});
// The compiler options transformation is a synchronous operation and uses shared memory combined
// with the Atomics API to block execution here until a response is received.
const optionsChannel = new node_worker_threads_1.MessageChannel();
const optionsSignal = new Int32Array(new SharedArrayBuffer(4));
optionsChannel.port1.on('message', (compilerOptions) => {
try {
const transformedOptions = compilerOptionsTransformer?.(compilerOptions) ?? compilerOptions;
optionsChannel.port1.postMessage({ transformedOptions });
}
catch (error) {
webWorkerChannel.port1.postMessage({ error });
}
finally {
Atomics.store(optionsSignal, 0, 1);
Atomics.notify(optionsSignal, 0);
}
});
// Execute the initialize function in the worker thread
return this.#worker.run({
fileReplacements: hostOptions.fileReplacements,
tsconfig,
jit: this.jit,
browserOnlyBuild: this.browserOnlyBuild,
stylesheetPort: stylesheetChannel.port2,
optionsPort: optionsChannel.port2,
optionsSignal,
webWorkerPort: webWorkerChannel.port2,
webWorkerSignal,
}, {
name: 'initialize',
transferList: [stylesheetChannel.port2, optionsChannel.port2, webWorkerChannel.port2],
});
}
/**
* This is not needed with this compilation type since the worker will already send a response
* with the serializable esbuild compatible diagnostics.
*/
collectDiagnostics() {
throw new Error('Not implemented in ParallelCompilation.');
}
diagnoseFiles(modes = angular_compilation_1.DiagnosticModes.All) {
return this.#worker.run(modes, { name: 'diagnose' });
}
emitAffectedFiles() {
return this.#worker.run(undefined, { name: 'emit' });
}
update(files) {
return this.#worker.run(files, { name: 'update' });
}
close() {
return this.#worker.destroy();
}
}
exports.ParallelCompilation = ParallelCompilation;