"use strict";
/**
 * @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;
//# sourceMappingURL=parallel-compilation.js.map