import { fileURLToPath } from 'node:url'; import { Worker, parentPort } from 'node:worker_threads'; /** * Runs a task in a subprocess so any dangling stuff gets killed upon completion. * The subprocess needs to be the file `forked` is called in, and `forked` needs to be called eagerly at the top level. * @template T * @template U * @param {string} module `import.meta.url` of the file * @param {(opts: T) => Promise} callback The function that is invoked in the subprocess * @returns {(opts: T) => Promise} A function that when called starts the subprocess */ export function forked(module, callback) { if (process.env.SVELTEKIT_FORK && parentPort) { parentPort.on( 'message', /** @param {any} data */ async (data) => { if (data?.type === 'args' && data.module === module) { parentPort?.postMessage({ type: 'result', module, payload: await callback(data.payload) }); } } ); parentPort.postMessage({ type: 'ready', module }); } /** * @param {T} opts * @returns {Promise} */ return function (opts) { return new Promise((fulfil, reject) => { const worker = new Worker(fileURLToPath(module), { env: { ...process.env, SVELTEKIT_FORK: 'true' } }); worker.on( 'message', /** @param {any} data */ (data) => { if (data?.type === 'ready' && data.module === module) { worker.postMessage({ type: 'args', module, payload: opts }); } if (data?.type === 'result' && data.module === module) { worker.unref(); fulfil(data.payload); } } ); worker.on('exit', (code) => { if (code) { reject(new Error(`Failed with code ${code}`)); } }); }); }; }