Skip to content

Instantly share code, notes, and snippets.

@mdashlw
Last active August 7, 2023 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mdashlw/425e3313a2f772faf69d66633c02d04a to your computer and use it in GitHub Desktop.
Save mdashlw/425e3313a2f772faf69d66633c02d04a to your computer and use it in GitHub Desktop.
Node.js async eval that accepts a context object with code and returns `util.inspect` of the result or the stringified error, along with stdout and stderr - acts exactly like Node.js REPL
const events = require("node:events");
const stream = require("node:stream");
const repl = require("node:repl");
/**
* @param {object} contextObject
* @param {string} code
* @returns {Promise<string>}
*/
async function executeAsyncCode(contextObject, code) {
let output = "";
const replServer = repl.start({
prompt: "",
input: stream.Readable.from([code], {
highWaterMark: 0,
}),
output: new stream.Writable({
highWaterMark: 0,
decodeStrings: false,
write(chunk, _encoding, callback) {
output += chunk;
callback();
},
}),
terminal: false,
});
for (const [key, value] of Object.entries(contextObject)) {
replServer.context[key] = value;
}
await events.once(replServer, "exit");
return output.slice(0, -1);
}
await executeAsyncCode({}, "42"); // '42'
await executeAsyncCode({}, "42;"); // '42'
await executeAsyncCode({}, "console.log('test')"); // 'test\nundefined'
await executeAsyncCode({}, "console.log('test'); await new Promise(resolve => { resolve(42); })"); // 'test\n42'
await executeAsyncCode({}, "new Error('error')"); /*
* 'Error: error\n' +
* ' at REPL1:1:1\n' +
* ' at Script.runInContext (node:vm:140:12)\n' +
* ' at REPLServer.defaultEval (node:repl:572:29)\n' +
* ' at bound (node:domain:433:15)\n' +
* ' at REPLServer.runBound [as eval] (node:domain:444:12)\n' +
* ' at REPLServer.onLine (node:repl:900:10)\n' +
* ' at REPLServer.emit (node:events:513:28)\n' +
* ' at REPLServer.emit (node:domain:489:12)\n' +
* ' at Readable.onend (node:internal/readline/interface:258:12)\n' +
* ' at Readable.emit (node:events:513:28)'
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment