myst-cli
Version:
Command line tools for MyST
113 lines (112 loc) • 3.95 kB
JavaScript
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) {
var _a, _b, _c;
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: ((_a = spec.directives) !== null && _a !== void 0 ? _a : []).map((directive) => wrapDirective(session, directive, path)),
roles: ((_b = spec.roles) !== null && _b !== void 0 ? _b : []).map((role) => wrapRole(session, role, path)),
transforms: ((_c = spec.transforms) !== null && _c !== void 0 ? _c : []).map((transform) => wrapTransform(session, transform, path)),
};
}