UNPKG

myst-cli

Version:
112 lines (111 loc) 3.81 kB
import { spawn, spawnSync } from 'node:child_process'; function executeSyncParser(session, name, data, path, kind) { const { stdout, status, stderr } = spawnSync(path, [`--${kind}`, name], { input: JSON.stringify(data), encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], }); if (status) { throw new Error(`Non-zero error code after running executable "${path}" during execution of ${kind} '${name}'\n\n${stderr}`); } else { if (stderr.length) { session.log.debug(`\n\n${stderr}\n\n`); } return JSON.parse(stdout); } } async function executeAsyncParser(session, name, data, path, kind) { const proc = spawn(path, [`--${kind}`, name]); // Pass in the data proc.stdin.write(JSON.stringify(data)); proc.stdin.end(); // Read out the stdout in chunks const stdoutBuffers = []; proc.stdout.on('data', (chunk) => { stdoutBuffers.push(chunk); }); // Read out the stderr in chunks const stderrBuffers = []; proc.stderr.on('data', (chunk) => { stderrBuffers.push(chunk); }); // Await exit const exitCode = await new Promise((resolve) => { proc.on('close', (code) => resolve(code)); }); // Did the process fail? if (exitCode) { const stderr = Buffer.concat(stderrBuffers).toString(); throw new Error(`Non-zero error code after running executable "${path}" during execution of ${kind} '${name}'\n\n${stderr}`); } else if (stderrBuffers.length) { const stderr = Buffer.concat(stderrBuffers).toString(); // Log the error session.log.debug(`\n\n${stderr}\n\n`); } // Concatenate the responses const stdout = Buffer.concat(stdoutBuffers).toString(); return JSON.parse(stdout); } function wrapDirective(session, directive, path) { return { ...directive, run: (data) => { return executeSyncParser(session, directive.name, data, path, 'directive'); }, }; } function wrapRole(session, role, path) { return { ...role, run: (data) => { return executeSyncParser(session, role.name, data, path, 'role'); }, }; } function wrapTransform(session, transform, path) { return { ...transform, plugin: () => { return async (node) => { // Modify the tree in-place const result = await executeAsyncParser(session, transform.name, node, path, 'transform'); Object.assign(node, result); }; }, }; } export async function loadExecutablePlugin(session, path) { const proc = spawn(path, []); // Read out the stdout in chunks const stdoutBuffers = []; proc.stdout.on('data', (chunk) => { stdoutBuffers.push(chunk); }); // Read out the stderr in chunks const stderrBuffers = []; proc.stderr.on('data', (chunk) => { stderrBuffers.push(chunk); }); // Await exit const exitCode = await new Promise((resolve) => { proc.on('close', (code) => resolve(code)); }); if (exitCode) { // Log the error const stderr = Buffer.concat(stderrBuffers).toString(); session.log.debug(`\n\n${stderr}\n\n`); return undefined; } // Concatenate the responses const stdout = Buffer.concat(stdoutBuffers).toString(); // Return the wrapped specification const spec = JSON.parse(stdout); return { ...spec, directives: (spec.directives ?? []).map((directive) => wrapDirective(session, directive, path)), roles: (spec.roles ?? []).map((role) => wrapRole(session, role, path)), transforms: (spec.transforms ?? []).map((transform) => wrapTransform(session, transform, path)), }; }