poku
Version:
🐷 Poku makes testing easy for Node.js, Bun, Deno, and you at the same time.
144 lines (143 loc) • 5.79 kB
JavaScript
;
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);
});