"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const messaging_1 = require("./messaging");
const serializable_error_1 = require("../../../utils/serializable-error");
const consume_messages_from_socket_1 = require("../../../utils/consume-messages-from-socket");
const net_1 = require("net");
const fs_1 = require("fs");
if (process.env.NX_PERF_LOGGING === 'true') {
    require('../../../utils/perf-logging');
}
global.NX_GRAPH_CREATION = true;
global.NX_PLUGIN_WORKER = true;
let connected = false;
let plugin;
let start;
const socketPath = process.argv[2];
const server = (0, net_1.createServer)((socket) => {
    connected = true;
    const startupTime = Date.now() - start;
    if (startupTime > 5000) {
        console.warn(`Plugin Worker took ${startupTime}ms to start up.`);
    }
    // This handles cases where the host process was killed
    // after the worker connected but before the worker was
    // instructed to load the plugin.
    const loadTimeout = setTimeout(() => {
        console.error(`Plugin Worker exited because no plugin was loaded within 10 seconds of starting up.`);
        process.exit(1);
    }, 10000).unref();
    socket.on('data', (0, consume_messages_from_socket_1.consumeMessagesFromSocket)((raw) => {
        const message = JSON.parse(raw.toString());
        if (!(0, messaging_1.isPluginWorkerMessage)(message)) {
            return;
        }
        return (0, messaging_1.consumeMessage)(socket, message, {
            load: async ({ plugin: pluginConfiguration, root, name, pluginPath, shouldRegisterTSTranspiler, }) => {
                if (loadTimeout)
                    clearTimeout(loadTimeout);
                process.chdir(root);
                try {
                    const { loadResolvedNxPluginAsync } = await Promise.resolve().then(() => require('../load-resolved-plugin'));
                    // Register the ts-transpiler if we are pointing to a
                    // plain ts file that's not part of a plugin project
                    if (shouldRegisterTSTranspiler) {
                        require('../transpiler').registerPluginTSTranspiler();
                    }
                    plugin = await loadResolvedNxPluginAsync(pluginConfiguration, pluginPath, name);
                    return {
                        type: 'load-result',
                        payload: {
                            name: plugin.name,
                            include: plugin.include,
                            exclude: plugin.exclude,
                            createNodesPattern: plugin.createNodes?.[0],
                            hasCreateDependencies: 'createDependencies' in plugin && !!plugin.createDependencies,
                            hasProcessProjectGraph: 'processProjectGraph' in plugin &&
                                !!plugin.processProjectGraph,
                            hasCreateMetadata: 'createMetadata' in plugin && !!plugin.createMetadata,
                            hasPreTasksExecution: 'preTasksExecution' in plugin && !!plugin.preTasksExecution,
                            hasPostTasksExecution: 'postTasksExecution' in plugin && !!plugin.postTasksExecution,
                            success: true,
                        },
                    };
                }
                catch (e) {
                    return {
                        type: 'load-result',
                        payload: {
                            success: false,
                            error: (0, serializable_error_1.createSerializableError)(e),
                        },
                    };
                }
            },
            createNodes: async ({ configFiles, context, tx }) => {
                try {
                    const result = await plugin.createNodes[1](configFiles, context);
                    return {
                        type: 'createNodesResult',
                        payload: { result, success: true, tx },
                    };
                }
                catch (e) {
                    return {
                        type: 'createNodesResult',
                        payload: {
                            success: false,
                            error: (0, serializable_error_1.createSerializableError)(e),
                            tx,
                        },
                    };
                }
            },
            createDependencies: async ({ context, tx }) => {
                try {
                    const result = await plugin.createDependencies(context);
                    return {
                        type: 'createDependenciesResult',
                        payload: { dependencies: result, success: true, tx },
                    };
                }
                catch (e) {
                    return {
                        type: 'createDependenciesResult',
                        payload: {
                            success: false,
                            error: (0, serializable_error_1.createSerializableError)(e),
                            tx,
                        },
                    };
                }
            },
            createMetadata: async ({ graph, context, tx }) => {
                try {
                    const result = await plugin.createMetadata(graph, context);
                    return {
                        type: 'createMetadataResult',
                        payload: { metadata: result, success: true, tx },
                    };
                }
                catch (e) {
                    return {
                        type: 'createMetadataResult',
                        payload: {
                            success: false,
                            error: (0, serializable_error_1.createSerializableError)(e),
                            tx,
                        },
                    };
                }
            },
            preTasksExecution: async ({ tx, context }) => {
                try {
                    const mutations = await plugin.preTasksExecution?.(context);
                    return {
                        type: 'preTasksExecutionResult',
                        payload: { success: true, tx, mutations },
                    };
                }
                catch (e) {
                    return {
                        type: 'preTasksExecutionResult',
                        payload: {
                            success: false,
                            error: (0, serializable_error_1.createSerializableError)(e),
                            tx,
                        },
                    };
                }
            },
            postTasksExecution: async ({ tx, context }) => {
                try {
                    await plugin.postTasksExecution?.(context);
                    return {
                        type: 'postTasksExecutionResult',
                        payload: { success: true, tx },
                    };
                }
                catch (e) {
                    return {
                        type: 'postTasksExecutionResult',
                        payload: {
                            success: false,
                            error: (0, serializable_error_1.createSerializableError)(e),
                            tx,
                        },
                    };
                }
            },
        });
    }));
    // There should only ever be one host -> worker connection
    // since the worker is spawned per host process. As such,
    // we can safely close the worker when the host disconnects.
    socket.on('end', () => {
        // Destroys the socket once it's fully closed.
        socket.destroySoon();
        // Stops accepting new connections, but existing connections are
        // not closed immediately.
        server.close(() => {
            try {
                (0, fs_1.unlinkSync)(socketPath);
            }
            catch (e) { }
            process.exit(0);
        });
    });
});
server.listen(socketPath);
start = Date.now();
setTimeout(() => {
    if (!connected) {
        console.error('The plugin worker is exiting as it was not connected to within 5 seconds.');
        // process.exit(1);
    }
}, 5000).unref();
const exitHandler = (exitCode) => () => {
    server.close();
    try {
        (0, fs_1.unlinkSync)(socketPath);
    }
    catch (e) { }
    process.exit(exitCode);
};
const events = ['SIGINT', 'SIGTERM', 'SIGQUIT', 'exit'];
events.forEach((event) => process.once(event, exitHandler(0)));
process.once('uncaughtException', exitHandler(1));
process.once('unhandledRejection', exitHandler(1));
