summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien George <damien@micropython.org>2024-02-01 17:46:34 +1100
committerDamien George <damien@micropython.org>2024-03-22 14:31:25 +1100
commit625b17a410f9e26d6c1ab0b81e475848d70af236 (patch)
treed852616d1880e9d5fc59f5a3271926b70ff3644b
parent9b090603a04e69ec710e14f6c8bd93011516c5a1 (diff)
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 <damien@micropython.org>
-rw-r--r--ports/webassembly/api.js92
1 files changed, 92 insertions, 0 deletions
diff --git a/ports/webassembly/api.js b/ports/webassembly/api.js
index ec0601c61..2a5522dfb 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();
+ }
+}