From 625b17a410f9e26d6c1ab0b81e475848d70af236 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Feb 2024 17:46:34 +1100 Subject: [PATCH] webassembly: Implement runCLI() for a Node-based CLI. This allows running MicroPython webassembly from the command line using: node micropython.mjs Signed-off-by: Damien George --- ports/webassembly/api.js | 92 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/ports/webassembly/api.js b/ports/webassembly/api.js index ec0601c612..2a5522dfb0 100644 --- a/ports/webassembly/api.js +++ b/ports/webassembly/api.js @@ -154,3 +154,95 @@ export async function loadMicroPython(options) { } globalThis.loadMicroPython = loadMicroPython; + +async function runCLI() { + const fs = await import("fs"); + let heap_size = 128 * 1024; + let contents = ""; + let repl = true; + + for (let i = 2; i < process.argv.length; i++) { + if (process.argv[i] === "-X" && i < process.argv.length - 1) { + if (process.argv[i + 1].includes("heapsize=")) { + heap_size = parseInt(process.argv[i + 1].split("heapsize=")[1]); + const suffix = process.argv[i + 1].substr(-1).toLowerCase(); + if (suffix === "k") { + heap_size *= 1024; + } else if (suffix === "m") { + heap_size *= 1024 * 1024; + } + ++i; + } + } else { + contents += fs.readFileSync(process.argv[i], "utf8"); + repl = false; + } + } + + if (process.stdin.isTTY === false) { + contents = fs.readFileSync(0, "utf8"); + repl = false; + } + + const mp = await loadMicroPython({ + heapsize: heap_size, + stdout: (data) => process.stdout.write(data), + linebuffer: false, + }); + + if (repl) { + mp_js_init_repl(); + process.stdin.setRawMode(true); + process.stdin.on("data", (data) => { + for (let i = 0; i < data.length; i++) { + mp_js_process_char(data[i]).then((result) => { + if (result) { + process.exit(); + } + }); + } + }); + } else { + try { + mp.runPython(contents); + } catch (error) { + if (error.name === "PythonError") { + if (error.type === "SystemExit") { + // SystemExit, this is a valid exception to successfully end a script. + } else { + // An unhandled Python exception, print in out. + console.error(error.message); + } + } else { + // A non-Python exception. Re-raise it. + throw error; + } + } + } +} + +// Check if Node is running (equivalent to ENVIRONMENT_IS_NODE). +if ( + typeof process === "object" && + typeof process.versions === "object" && + typeof process.versions.node === "string" +) { + // Check if this module is ron from the command line. + // + // See https://stackoverflow.com/questions/6398196/detect-if-called-through-require-or-directly-by-command-line/66309132#66309132 + // + // Note: + // - `resolve()` is used to handle symlinks + // - `includes()` is used to handle cases where the file extension was omitted when passed to node + + const path = await import("path"); + const url = await import("url"); + + const pathToThisFile = path.resolve(url.fileURLToPath(import.meta.url)); + const pathPassedToNode = path.resolve(process.argv[1]); + const isThisFileBeingRunViaCLI = pathToThisFile.includes(pathPassedToNode); + + if (isThisFileBeingRunViaCLI) { + runCLI(); + } +}