"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.IPCOperationRunnerPlugin = void 0;
const IPCOperationRunner_1 = require("./IPCOperationRunner");
const OperationStatus_1 = require("./OperationStatus");
const ShellOperationRunnerPlugin_1 = require("./ShellOperationRunnerPlugin");
const PLUGIN_NAME = 'IPCOperationRunnerPlugin';
/**
 * Plugin that implements compatible phases via IPC to a long-lived watch process.
 */
class IPCOperationRunnerPlugin {
    apply(hooks) {
        // Workaround until the operation graph persists for the lifetime of the watch process
        const runnerCache = new Map();
        const operationStatesByRunner = new WeakMap();
        let currentContext;
        hooks.createOperations.tapPromise({
            name: PLUGIN_NAME,
            before: ShellOperationRunnerPlugin_1.PLUGIN_NAME
        }, async (operations, context) => {
            var _a, _b;
            const { isWatch, isInitial } = context;
            if (!isWatch) {
                return operations;
            }
            currentContext = context;
            const getCustomParameterValues = (0, ShellOperationRunnerPlugin_1.getCustomParameterValuesByOperation)();
            for (const operation of operations) {
                const { associatedPhase: phase, associatedProject: project, runner } = operation;
                if (runner) {
                    continue;
                }
                const { scripts } = project.packageJson;
                if (!scripts) {
                    continue;
                }
                const { name: phaseName } = phase;
                const rawScript = (_a = (!isInitial ? scripts[`${phaseName}:incremental:ipc`] : undefined)) !== null && _a !== void 0 ? _a : scripts[`${phaseName}:ipc`];
                if (!rawScript) {
                    continue;
                }
                // This is the command that will be used to identify the cache entry for this operation, to allow
                // for this operation (or downstream operations) to be restored from the build cache.
                const commandForHash = (_b = phase.shellCommand) !== null && _b !== void 0 ? _b : scripts === null || scripts === void 0 ? void 0 : scripts[phaseName];
                const { parameterValues: customParameterValues, ignoredParameterValues } = getCustomParameterValues(operation);
                const commandToRun = (0, ShellOperationRunnerPlugin_1.formatCommand)(rawScript, customParameterValues);
                const operationName = (0, ShellOperationRunnerPlugin_1.getDisplayName)(phase, project);
                let maybeIpcOperationRunner = runnerCache.get(operationName);
                if (!maybeIpcOperationRunner) {
                    const ipcOperationRunner = (maybeIpcOperationRunner = new IPCOperationRunner_1.IPCOperationRunner({
                        phase,
                        project,
                        name: operationName,
                        commandToRun,
                        commandForHash,
                        persist: true,
                        ignoredParameterValues,
                        requestRun: (requestor, detail) => {
                            var _a;
                            const operationState = operationStatesByRunner.get(ipcOperationRunner);
                            if (!operationState) {
                                return;
                            }
                            const status = operationState.status;
                            if (status === OperationStatus_1.OperationStatus.Waiting ||
                                status === OperationStatus_1.OperationStatus.Ready ||
                                status === OperationStatus_1.OperationStatus.Queued) {
                                // Already pending. No-op.
                                return;
                            }
                            (_a = currentContext === null || currentContext === void 0 ? void 0 : currentContext.invalidateOperation) === null || _a === void 0 ? void 0 : _a.call(currentContext, operation, detail ? `${requestor}: ${detail}` : requestor);
                        }
                    }));
                    runnerCache.set(operationName, ipcOperationRunner);
                }
                operation.runner = maybeIpcOperationRunner;
            }
            return operations;
        });
        hooks.beforeExecuteOperations.tap(PLUGIN_NAME, (records, context) => {
            currentContext = context;
            for (const [{ runner }, result] of records) {
                if (runner instanceof IPCOperationRunner_1.IPCOperationRunner) {
                    operationStatesByRunner.set(runner, result);
                }
            }
        });
        hooks.shutdownAsync.tapPromise(PLUGIN_NAME, async () => {
            await Promise.all(Array.from(runnerCache.values(), (runner) => runner.shutdownAsync()));
        });
    }
}
exports.IPCOperationRunnerPlugin = IPCOperationRunnerPlugin;
//# sourceMappingURL=IPCOperationRunnerPlugin.js.map