UNPKG

function-exec-sync

Version:

Run a function in a node process

104 lines (103 loc) 3.51 kB
import './polyfills.cjs'; import cp from 'child_process'; import fs from 'fs'; import mkdirp from 'mkdirp-classic'; import os from 'os'; import osShim from 'os-shim'; import path from 'path'; import serialize from 'serialize-javascript'; import shortHash from 'short-hash'; import suffix from 'temp-suffix'; import sleep from 'thread-sleep-compat'; import url from 'url'; const tmpdir = os.tmpdir || osShim.tmpdir; const DEFAULT_SLEEP_MS = 100; const NODES = [ 'node', 'node.exe', 'node.cmd' ]; const isWindows = process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE); const __dirname = path.dirname(typeof __filename === 'undefined' ? url.fileURLToPath(import.meta.url) : __filename); const worker = path.join(__dirname, 'workers', 'runFunction.cjs'); import unlinkSafe from './unlinkSafe.js'; const existsSync = (test)=>{ try { (fs.accessSync || fs.statSync)(test); return true; } catch (_) { return false; } }; export default function functionExecSync(options, filePath, ...args) { if (typeof options === 'string') { args.unshift(filePath); filePath = options; options = null; } if (!filePath) throw new Error('function-exec-sync missing file'); options = options || {}; const env = { ...options.env || process.env }; delete env.NODE_OPTIONS; const workerData = { filePath, args, callbacks: options.callbacks === undefined ? false : options.callbacks, env, cwd: options.cwd === undefined ? process.cwd() : options.cwd }; const name = options.name === undefined ? 'function-exec-sync' : options.name; const temp = path.join(tmpdir(), name, shortHash(workerData.cwd)); const input = path.join(temp, suffix('input')); const output = path.join(temp, suffix('output')); const done = path.join(temp, suffix('done')); // store data to a file mkdirp.sync(path.dirname(input)); fs.writeFileSync(input, serialize(workerData), 'utf8'); unlinkSafe(output); // call the function const execPath = options.execPath || process.execPath; // only node if (NODES.indexOf(path.basename(execPath).toLowerCase()) < 0) throw new Error(`Expecting node executable. Received: ${path.basename(execPath)}`); // exec and start polling if (!cp.execFileSync) { const sleepMS = options.sleep === undefined ? DEFAULT_SLEEP_MS : options.sleep; let cmd = `"${execPath}" "${worker}" "${input}" "${output}"`; cmd += `${isWindows ? '&' : ';'} echo "done" > ${done}`; cp.exec(cmd, { env }); while(!existsSync(done)){ sleep(sleepMS); } } else { cp.execFileSync(execPath, [ worker, input, output ], { env }); } unlinkSafe(input); unlinkSafe(done); // get data and clean up let res; try { // biome-ignore lint/security/noGlobalEval: Serialize res = eval(`(${fs.readFileSync(output, 'utf8')})`); unlinkSafe(output); } catch (err) { throw new Error(`function-exec-sync: Error: ${err.message}. Function: ${filePath}. Exec path: ${execPath}`); } // throw error from the worker if (res.error) { const error = new Error(res.error.message); for(const key in res.error)error[key] = res.error[key]; throw error; } // return the result return res.value; }