UNPKG

poku

Version:

🐷 Poku makes testing easy for Node.js, Bun, Deno, and you at the same time.

144 lines (143 loc) 5.79 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.startScript = exports.startService = void 0; const node_child_process_1 = require("child_process"); const node_path_1 = require("path"); const node_process_1 = __importDefault(require("process")); const get_runner_js_1 = require("../../parsers/get-runner.js"); const os_js_1 = require("../../parsers/os.js"); const write_js_1 = require("../../services/write.js"); const kill_js_1 = require("./kill.js"); const list_files_js_1 = require("./list-files.js"); const runningProcesses = new Map(); const backgroundProcess = (runtime, args, file, options) => new Promise((resolve, reject) => { try { let isResolved = false; const service = (0, node_child_process_1.spawn)(runtime, args, { stdio: ['inherit', 'pipe', 'pipe'], env: node_process_1.default.env, timeout: options?.timeout, cwd: options?.cwd ? (0, list_files_js_1.sanitizePath)((0, node_path_1.normalize)(options.cwd)) : undefined, shell: os_js_1.isWindows, detached: !os_js_1.isWindows, windowsHide: os_js_1.isWindows, }); const PID = service.pid; service.stdout.setEncoding('utf8'); service.stderr.setEncoding('utf8'); let portBackup; const end = (port) => new Promise((resolve) => { try { runningProcesses.delete(PID); if (os_js_1.isWindows) { kill_js_1.kill.pid(PID); return; } if (['bun', 'deno'].includes(runtime) || ['bun', 'deno'].includes(String(options?.runner))) node_process_1.default.kill(PID); else node_process_1.default.kill(-PID, 'SIGKILL'); if (port && ['bun', 'deno'].includes(runtime)) { setTimeout(async () => { await kill_js_1.kill.port(port); resolve(undefined); return; }); } else { resolve(undefined); return; } } catch { resolve(undefined); return; } }); runningProcesses.set(PID, { end, port: portBackup }); service.stdout.on('data', (data) => { if (!isResolved && typeof options?.startAfter !== 'number') { const stringData = JSON.stringify(String(data)); if (typeof options?.startAfter === 'undefined' || (typeof options?.startAfter === 'string' && stringData.includes(options?.startAfter))) { resolve({ end }); clearTimeout(timeout); isResolved = true; } } options?.verbose && (0, write_js_1.log)(data); }); service.stderr.on('data', (data) => { if (!isResolved && typeof options?.startAfter !== 'number') { const stringData = JSON.stringify(String(data)); if (typeof options?.startAfter === 'undefined' || (typeof options?.startAfter === 'string' && stringData.includes(options?.startAfter))) { resolve({ end }); clearTimeout(timeout); isResolved = true; } } options?.verbose && (0, write_js_1.log)(data); }); service.on('error', (err) => { end(portBackup); reject(`Service failed to start: ${err}`); }); service.on('close', (code) => { if (code !== 0) reject(`Service exited with code ${code}`); }); const timeout = setTimeout(() => { if (!isResolved) { end(portBackup); reject(`createService: Timeout\nFile: ${file}`); } }, options?.timeout || 60000); if (typeof options?.startAfter === 'number') setTimeout(() => { if (!isResolved) { resolve({ end }); clearTimeout(timeout); isResolved = true; } }, options.startAfter); } catch { } }); /** Starts a file in a background process (useful for servers, APIs, etc.) */ const startService = (file, options) => { const runtimeOptions = (0, get_runner_js_1.runner)(file); const runtime = runtimeOptions.shift(); const runtimeArgs = [...runtimeOptions, file]; return backgroundProcess(runtime, runtimeArgs, (0, node_path_1.normalize)((0, list_files_js_1.sanitizePath)(file)), options); }; exports.startService = startService; /** * * Starts a script (package.json) or task (deno.json) in a background process (useful for servers, APIs, etc.). * * --- * * By default it uses **npm**, but you can costumize it using the `runner` option. */ const startScript = (script, options) => { const runner = options?.runner ?? 'npm'; const runtimeOptions = (0, get_runner_js_1.scriptRunner)(runner); const runtime = runtimeOptions.shift(); const runtimeArgs = [...runtimeOptions, script]; return backgroundProcess(runtime, runtimeArgs, script, { ...options, runner, }); }; exports.startScript = startScript; node_process_1.default.once('SIGINT', async () => { for (const { end, port } of runningProcesses.values()) await end(port); });