UNPKG

@plugjs/plug

Version:
225 lines (223 loc) 9.38 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // fork.ts var fork_exports = {}; __export(fork_exports, { ForkingPlug: () => ForkingPlug, installForking: () => installForking }); module.exports = __toCommonJS(fork_exports); var import_node_child_process = require("node:child_process"); var import_node_console = require("node:console"); var import_node_stream = require("node:stream"); var import_asserts = require("./asserts.cjs"); var import_async = require("./async.cjs"); var import_files = require("./files.cjs"); var import_logging = require("./logging.cjs"); var import_emit = require("./logging/emit.cjs"); var import_paths = require("./paths.cjs"); var import_pipe = require("./pipe.cjs"); var ForkingPlug = class { constructor(_scriptFile, _arguments, _exportName) { this._scriptFile = _scriptFile; this._arguments = _arguments; this._exportName = _exportName; } _scriptFile; _arguments; _exportName; pipe(files, context) { const request = { scriptFile: this._scriptFile, exportName: this._exportName, constructorArgs: this._arguments, taskName: context.taskName, buildFile: context.buildFile, filesDir: files.directory, filesList: [...files.absolutePaths()], logIndent: context.log.indent }; const script = (0, import_paths.requireFilename)(__filename); context.log.debug("About to fork plug from", (0, import_logging.$p)(this._scriptFile)); const env = { ...process.env, ...import_logging.logOptions.forkEnv(context.taskName) }; for (let i = this._arguments.length - 1; i >= 0; i--) { if (this._arguments[i] == null) continue; if (typeof this._arguments[i] === "object") { if (typeof this._arguments[i].coverageDir === "string") { const dir = env.NODE_V8_COVERAGE = context.resolve(this._arguments[i].coverageDir); context.log.debug("Forked process will produce coverage in", (0, import_logging.$p)(dir)); } if (typeof this._arguments[i].forceModule === "string") { const force = env.__TS_LOADER_FORCE_TYPE = this._arguments[i].forceModule; context.log.debug("Forked process will force module type as", (0, import_logging.$p)(force)); } } } const child = (0, import_node_child_process.fork)(script, { stdio: ["ignore", "inherit", "inherit", "ipc"], serialization: "advanced", env }); context.log.info("Running", (0, import_logging.$p)(script), (0, import_logging.$gry)(`(pid=${child.pid})`)); let done = false; return new Promise((resolve, reject) => { let response = void 0; child.on("error", (error) => { context.log.error("Forked plug process error", error); return done || reject(import_asserts.BuildFailure.fail()); }); child.on("message", (message) => { if ("logLevel" in message) { const { logLevel, taskName, lines } = message; lines.forEach((line) => { context.log._emit(logLevel, [line], taskName); }); } else { context.log.debug("Message from forked plug process with PID", child.pid, message); response = message; } }); child.on("exit", (code, signal) => { if (signal) { context.log.error(`Forked plug process exited with signal ${signal}`, (0, import_logging.$gry)(`(pid=${child.pid})`)); return done || reject(import_asserts.BuildFailure.fail()); } else if (code !== 0) { context.log.error(`Forked plug process exited with code ${code}`, (0, import_logging.$gry)(`(pid=${child.pid})`)); return done || reject(import_asserts.BuildFailure.fail()); } else if (!response) { context.log.error("Forked plug process exited with no result", (0, import_logging.$gry)(`(pid=${child.pid})`)); return done || reject(import_asserts.BuildFailure.fail()); } else if (response.failed) { return done || reject(import_asserts.BuildFailure.fail()); } return done || resolve(response.filesDir && response.filesList ? import_files.Files.builder(response.filesDir).add(...response.filesList).build() : void 0); }); child.on("spawn", () => { try { child.send(request, (error) => { if (error) { context.log.error("Error sending message to forked plug process (callback failure)", error); reject(import_asserts.BuildFailure.fail()); } }); } catch (error) { context.log.error("Error sending message to forked plug process (exception caught)", error); reject(import_asserts.BuildFailure.fail()); } }); }).finally(() => done = true); } }; if (process.argv[1] === (0, import_paths.requireFilename)(__filename) && process.send) { const originalConsole = globalThis.console; process.on("uncaughtException", (error, origin) => { originalConsole.error( (0, import_logging.$red)("\n= UNCAUGHT EXCEPTION ========================================="), ` Error (${origin}):`, error, ` Node.js ${process.version} (pid=${process.pid}) ` ); process.nextTick(() => process.exit(3)); }); const timeout = setTimeout(() => { originalConsole.error("Fork not initialized in 5 seconds"); process.exit(2); }, 5e3).unref(); process.on("message", (message) => { clearTimeout(timeout); const { scriptFile, exportName, constructorArgs, taskName, buildFile, filesDir, filesList, logIndent } = message; import_emit.emit.emitter = import_emit.emitForked; const makeWritable = (level) => new class extends import_node_stream.Writable { _write(chunk, _, callback) { const string = chunk.toString(); const message2 = string.endsWith("\n") ? string.slice(0, -1) : string; import_emit.emit.emitter({ level, taskName }, [message2]); callback(); } }(); globalThis.console = new import_node_console.Console(makeWritable(import_logging.NOTICE), makeWritable(import_logging.WARN)); const context = new import_pipe.Context(buildFile, taskName); context.log.indent = logIndent; context.log.debug("Message from parent process for PID", process.pid, message); process.exitCode = 0; const result = (0, import_async.runAsync)(context, async () => { (0, import_asserts.assert)((0, import_paths.resolveFile)(scriptFile), `Script file ${(0, import_logging.$p)(scriptFile)} not found`); const script = await import(scriptFile); let Ctor; if (exportName === "default") { Ctor = script; while (Ctor && typeof Ctor !== "function") Ctor = Ctor.default; (0, import_asserts.assert)(typeof Ctor === "function", `Script ${(0, import_logging.$p)(scriptFile)} does not export a default constructor`); } else { Ctor = script[exportName]; if (!Ctor && script.default) Ctor = script.default[exportName]; (0, import_asserts.assert)(typeof Ctor === "function", `Script ${(0, import_logging.$p)(scriptFile)} does not export "${exportName}"`); } const plug = new Ctor(...constructorArgs); const files = import_files.Files.builder(filesDir).add(...filesList).build(); return plug.pipe(files, context); }); const promise = result.then((result2) => { const message2 = result2 ? { failed: false, filesDir: result2.directory, filesList: [...result2.absolutePaths()] } : { failed: false }; return new Promise((resolve, reject) => { process.send(message2, (err) => err ? reject(err) : resolve()); }); }, (error) => { context.log.error(error); return new Promise((resolve, reject) => { process.send({ failed: true }, (err) => err ? reject(err) : resolve()); }); }); promise.then(() => { context.log.debug("Forked plug with pid", process.pid, "exiting"); }, (error) => { originalConsole.error("\n\nError sending message back to parent process", error); process.exitCode = 1; }).finally(() => { process.disconnect(); process.exit(process.exitCode); }); }); } function installForking(plugName, scriptFile, exportName = "default") { const ctor = class extends ForkingPlug { constructor(...args) { super(scriptFile, args, exportName); } }; (0, import_pipe.install)(plugName, ctor); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ForkingPlug, installForking }); //# sourceMappingURL=fork.cjs.map