@pyscript/core 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +14 -2
  2. package/cjs/custom/pyscript.js +3 -3
  3. package/cjs/custom-types.js +0 -2
  4. package/cjs/custom.js +6 -6
  5. package/cjs/interpreter/_python.js +4 -2
  6. package/cjs/interpreter/micropython.js +10 -12
  7. package/cjs/interpreter/webr.js +48 -0
  8. package/cjs/plugins.js +89 -0
  9. package/cjs/runtime/_python.js +34 -0
  10. package/cjs/runtime/_utils.js +141 -0
  11. package/cjs/runtime/micropython.js +40 -0
  12. package/cjs/runtime/pyodide.js +40 -0
  13. package/cjs/runtime/ruby.js +50 -0
  14. package/cjs/runtime/wasmoon.js +46 -0
  15. package/cjs/runtimes.js +63 -0
  16. package/cjs/worker/class.js +11 -2
  17. package/cjs/worker/hooks.js +11 -2
  18. package/cjs/worker/xworker.js +1 -1
  19. package/core.js +2 -2
  20. package/docs/README.md +495 -0
  21. package/esm/custom/pyscript.js +3 -3
  22. package/esm/custom.js +6 -6
  23. package/esm/interpreter/_python.js +4 -2
  24. package/esm/interpreter/micropython.js +10 -12
  25. package/esm/worker/class.js +11 -2
  26. package/esm/worker/hooks.js +11 -2
  27. package/esm/worker/xworker.js +1 -1
  28. package/package.json +12 -4
  29. package/pyscript.js +2 -2
  30. package/types/coincident/window.d.ts +4 -4
  31. package/types/custom.d.ts +54 -0
  32. package/types/index.d.ts +1 -1
  33. package/types/interpreter/_python.d.ts +7 -0
  34. package/types/interpreter/_utils.d.ts +11 -0
  35. package/types/interpreter/micropython.d.ts +18 -0
  36. package/types/interpreter/pyodide.d.ts +19 -0
  37. package/types/interpreter/ruby-wasm-wasi.d.ts +15 -0
  38. package/types/interpreter/wasmoon.d.ts +21 -0
  39. package/types/interpreters.d.ts +9 -0
  40. package/types/listeners.d.ts +2 -0
  41. package/types/pyscript/pyscript.core/esm/custom.d.ts +3 -3
  42. package/types/pyscript/pyscript.core/esm/interpreter/_python.d.ts +2 -2
  43. package/types/pyscript/pyscript.core/esm/interpreter/micropython.d.ts +1 -2
  44. package/types/pyscript/pyscript.core/esm/interpreter/webr.d.ts +14 -0
  45. package/types/pyscript/pyscript.core/esm/plugins.d.ts +21 -14
  46. package/types/pyscript/pyscript.core/esm/worker/hooks.d.ts +4 -1
  47. package/types/script-handler.d.ts +2 -1
  48. package/types/worker/class.d.ts +4 -4
  49. package/types/worker/hooks.d.ts +6 -2
package/README.md CHANGED
@@ -4,9 +4,13 @@
4
4
 
5
5
  ---
6
6
 
7
+ ## Documentation
8
+
9
+ Please read [the documentation page](./docs/README.md) to know all the user-facing details around this module.
10
+
7
11
  ## Development
8
12
 
9
- The working folder (source code of truth) is the `./esm` one, while the `./cjs` is populated as dual module and to test (but it's 1:1 code, no trnaspilation except for imports/exports).
13
+ The working folder (source code of truth) is the `./esm` one, while the `./cjs` is populated as dual module and to test (but it's 1:1 code, no transpilation except for imports/exports).
10
14
 
11
15
  ```sh
12
16
  # install all dependencies needed by core
@@ -19,7 +23,7 @@ This project requires some automatic artifact creation to:
19
23
 
20
24
  * create a _Worker_ as a _Blob_ based on the same code used by this repo
21
25
  * create automatically the list of runtimes available via the module
22
- * create the `core.js` file used by most integration tests
26
+ * create the `core.js` or the `pyscript.js` file used by most integration tests
23
27
  * create a sha256 version of the Blob content for CSP cases
24
28
 
25
29
  Accordingly, to build latest project:
@@ -31,3 +35,11 @@ npm run build
31
35
  # optionally spin a server with CORS, COOP, and COEP enabled
32
36
  npm run server
33
37
  ```
38
+
39
+ If **no minification** is desired or helpful while debugging potential issues, please use `NO_MIN=1` in front of the _build_ step:
40
+
41
+ ```sh
42
+ NO_MIN=1 npm run build
43
+
44
+ npm run server
45
+ ```
@@ -115,10 +115,10 @@ document.head.appendChild(document.createElement("style")).textContent = `
115
115
  onAfterRunAsync(pyodide, element) {
116
116
  bootstrapNodeAndPlugins(pyodide, element, after, "onAfterRunAsync");
117
117
  },
118
- async onRuntimeReady(pyodide, element) {
118
+ async onInterpreterReady(pyodide, element) {
119
119
  // allows plugins to do whatever they want with the element
120
120
  // before regular stuff happens in here
121
- for (const callback of hooks.onRuntimeReady)
121
+ for (const callback of hooks.onInterpreterReady)
122
122
  callback(pyodide, element);
123
123
  if (isScript(element)) {
124
124
  const {
@@ -179,7 +179,7 @@ const hooks = {
179
179
  /** @type {Set<function>} */
180
180
  onAfterRunAsync: new Set(),
181
181
  /** @type {Set<function>} */
182
- onRuntimeReady: new Set(),
182
+ onInterpreterReady: new Set(),
183
183
 
184
184
  /** @type {Set<string>} */
185
185
  codeBeforeRunWorker: new Set(),
@@ -126,8 +126,6 @@ const handleCustomType = (node) => {
126
126
  afterRunAsync: codeAfterRunWorkerAsync,
127
127
  });
128
128
 
129
- module.setGlobal(interpreter, "XWorker", XWorker);
130
-
131
129
  const resolved = {
132
130
  type,
133
131
  interpreter,
package/cjs/custom.js CHANGED
@@ -48,7 +48,7 @@ const handleCustomType = (node) => {
48
48
  version,
49
49
  config,
50
50
  env,
51
- onRuntimeReady,
51
+ onInterpreterReady,
52
52
  } = options;
53
53
  const name = getRuntimeID(runtime, version);
54
54
  const id = env || `${name}${config ? `|${config}` : ""}`;
@@ -69,7 +69,7 @@ const handleCustomType = (node) => {
69
69
  onAfterRunAsync,
70
70
  } = options;
71
71
 
72
- const hooks = new Hook(options);
72
+ const hooks = new Hook(interpreter, options);
73
73
 
74
74
  const XWorker = function XWorker(...args) {
75
75
  return Worker.apply(hooks, args);
@@ -123,7 +123,7 @@ const handleCustomType = (node) => {
123
123
 
124
124
  resolve(resolved);
125
125
 
126
- onRuntimeReady?.(resolved, node);
126
+ onInterpreterReady?.(resolved, node);
127
127
  });
128
128
  }
129
129
  }
@@ -137,17 +137,17 @@ exports.handleCustomType = handleCustomType;
137
137
  const registry = new Map();
138
138
 
139
139
  /**
140
- * @typedef {Object} PluginOptions custom configuration
140
+ * @typedef {Object} CustomOptions custom configuration
141
141
  * @prop {'pyodide' | 'micropython' | 'wasmoon' | 'ruby-wasm-wasi'} interpreter the interpreter to use
142
142
  * @prop {string} [version] the optional interpreter version to use
143
143
  * @prop {string} [config] the optional config to use within such interpreter
144
- * @prop {(environment: object, node: Element) => void} [onRuntimeReady] the callback that will be invoked once
144
+ * @prop {(environment: object, node: Element) => void} [onInterpreterReady] the callback that will be invoked once
145
145
  */
146
146
 
147
147
  /**
148
148
  * Allows custom types and components on the page to receive interpreters to execute any code
149
149
  * @param {string} type the unique `<script type="...">` identifier
150
- * @param {PluginOptions} options the custom type configuration
150
+ * @param {CustomOptions} options the custom type configuration
151
151
  */
152
152
  const define = (type, options) => {
153
153
  if (defaultRegistry.has(type) || registry.has(type))
@@ -10,12 +10,14 @@ const runAsync = (interpreter, code) =>
10
10
  interpreter.runPythonAsync(clean(code));
11
11
  exports.runAsync = runAsync;
12
12
 
13
- const setGlobal = (interpreter, name, value) =>
13
+ const setGlobal = (interpreter, name, value) => {
14
14
  interpreter.globals.set(name, value);
15
+ };
15
16
  exports.setGlobal = setGlobal;
16
17
 
17
- const deleteGlobal = (interpreter, name) =>
18
+ const deleteGlobal = (interpreter, name) => {
18
19
  interpreter.globals.delete(name);
20
+ };
19
21
  exports.deleteGlobal = deleteGlobal;
20
22
 
21
23
  const writeFile = ({ FS }, path, buffer) =>
@@ -1,12 +1,6 @@
1
1
  'use strict';
2
2
  const { fetchPaths, stdio } = require("./_utils.js");
3
- const {
4
- run,
5
- runAsync,
6
- setGlobal,
7
- deleteGlobal,
8
- writeFile
9
- } = require("./_python.js");
3
+ const { run, setGlobal, deleteGlobal, writeFile } = require("./_python.js");
10
4
 
11
5
  const type = "micropython";
12
6
 
@@ -14,19 +8,23 @@ const type = "micropython";
14
8
  /* c8 ignore start */
15
9
  module.exports = {
16
10
  type,
17
- module: (version = "1.20.0-253") =>
11
+ module: (version = "1.20.0-268") =>
18
12
  `https://cdn.jsdelivr.net/npm/@micropython/micropython-webassembly-pyscript@${version}/micropython.mjs`,
19
13
  async engine({ loadMicroPython }, config, url) {
20
14
  const { stderr, stdout, get } = stdio();
21
15
  url = url.replace(/\.m?js$/, ".wasm");
22
- const runtime = await get(loadMicroPython({ stderr, stdout, url }));
23
- if (config.fetch) await fetchPaths(this, runtime, config.fetch);
24
- return runtime;
16
+ const interpreter = await get(loadMicroPython({ stderr, stdout, url }));
17
+ if (config.fetch) await fetchPaths(this, interpreter, config.fetch);
18
+ return interpreter;
25
19
  },
26
20
  setGlobal,
27
21
  deleteGlobal,
28
22
  run,
29
- runAsync,
23
+ // TODO: MicroPython doesn't have a Pyodide like top-level await,
24
+ // this method should still not throw errors once invoked
25
+ async runAsync(...args) {
26
+ return this.run(...args);
27
+ },
30
28
  writeFile,
31
29
  };
32
30
  /* c8 ignore stop */
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+ const { fetchPaths, stdio, writeFile } = require("./_utils.js");
3
+
4
+ const type = "webr";
5
+
6
+ const io = new WeakMap;
7
+
8
+ // REQUIRES INTEGRATION TEST
9
+ /* c8 ignore start */
10
+ module.exports = {
11
+ type,
12
+ module: (version = "0.1.1") =>
13
+ `https://webr.r-wasm.org/v${version}/webr.mjs`,
14
+ async engine({ WebR }, config) {
15
+ const { stderr, stdout, get } = stdio();
16
+ const webR = new WebR();
17
+ await webR.init();
18
+ const interpreter = await get(new webR.Shelter());
19
+ io.set(interpreter, { webR, stderr, stdout });
20
+ if (config.fetch) await fetchPaths(this, interpreter, config.fetch);
21
+ return interpreter;
22
+ },
23
+ setGlobal() {
24
+ // UNSUPPORTED
25
+ // const { webR } = io.get(interpreter);
26
+ // return webR.objs.globalEnv.bind(name, value);
27
+ },
28
+ deleteGlobal() {
29
+ // UNSUPPORTED
30
+ // const { webR } = io.get(interpreter);
31
+ // return webR.objs.globalEnv.bind(name, void 0);
32
+ },
33
+ run(interpreter, code) {
34
+ return this.runAsync(interpreter, code);
35
+ },
36
+ async runAsync(interpreter, code) {
37
+ const ioHandler = io.get(interpreter);
38
+ const { output, result } = await interpreter.captureR(code);
39
+ for (const { type, data } of output)
40
+ ioHandler[type](data);
41
+ return result;
42
+ },
43
+ writeFile: (interpreter, path, buffer) => {
44
+ const { webR } = io.get(interpreter);
45
+ return writeFile(webR.FS, path, buffer);
46
+ },
47
+ };
48
+ /* c8 ignore stop */
package/cjs/plugins.js ADDED
@@ -0,0 +1,89 @@
1
+ 'use strict';
2
+ const { $$ } = require("basic-devtools");
3
+
4
+ const { getDetails } = require("./script-handler.js");
5
+ const { registry, configs } = require("./runtimes.js");
6
+ const { getRuntimeID } = require("./loader.js");
7
+ const { io } = require("./runtime/_utils.js");
8
+
9
+ const PLUGINS_SELECTORS = [];
10
+ exports.PLUGINS_SELECTORS = PLUGINS_SELECTORS;
11
+
12
+ /**
13
+ * @typedef {Object} Runtime plugin configuration
14
+ * @prop {string} type the runtime type
15
+ * @prop {object} runtime the bootstrapped runtime
16
+ * @prop {(url:string, options?: object) => Worker} XWorker an XWorker constructor that defaults to same runtime on the Worker.
17
+ * @prop {object} config a cloned config used to bootstrap the runtime
18
+ * @prop {(code:string) => any} run an utility to run code within the runtime
19
+ * @prop {(code:string) => Promise<any>} runAsync an utility to run code asynchronously within the runtime
20
+ * @prop {(path:string, data:ArrayBuffer) => void} writeFile an utility to write a file in the virtual FS, if available
21
+ */
22
+
23
+ // REQUIRES INTEGRATION TEST
24
+ /* c8 ignore start */
25
+ /**
26
+ * @param {Element} node any DOM element registered via plugin.
27
+ */
28
+ const handlePlugin = (node) => {
29
+ for (const name of PLUGINS_SELECTORS) {
30
+ if (node.matches(name)) {
31
+ const { options, known } = plugins.get(name);
32
+ if (!known.has(node)) {
33
+ known.add(node);
34
+ const { type, version, config, env, onRuntimeReady } = options;
35
+ const name = getRuntimeID(type, version);
36
+ const id = env || `${name}${config ? `|${config}` : ""}`;
37
+ const { runtime: engine, XWorker } = getDetails(
38
+ type,
39
+ id,
40
+ name,
41
+ version,
42
+ config,
43
+ );
44
+ engine.then((runtime) => {
45
+ const module = registry.get(type);
46
+ onRuntimeReady(node, {
47
+ type,
48
+ runtime,
49
+ XWorker,
50
+ io: io.get(runtime),
51
+ config: structuredClone(configs.get(name)),
52
+ run: module.run.bind(module, runtime),
53
+ runAsync: module.runAsync.bind(module, runtime),
54
+ });
55
+ });
56
+ }
57
+ }
58
+ }
59
+ };
60
+ exports.handlePlugin = handlePlugin;
61
+
62
+ /**
63
+ * @type {Map<string, {options:object, known:WeakSet<Element>}>}
64
+ */
65
+ const plugins = new Map();
66
+
67
+ /**
68
+ * @typedef {Object} PluginOptions plugin configuration
69
+ * @prop {string} type the runtime/interpreter type to receive
70
+ * @prop {string} [version] the optional runtime version to use
71
+ * @prop {string} [config] the optional config to use within such runtime
72
+ * @prop {string} [env] the optional environment to use
73
+ * @prop {(node: Element, runtime: Runtime) => void} onRuntimeReady the callback that will be invoked once
74
+ */
75
+
76
+ /**
77
+ * Allows plugins and components on the page to receive runtimes to execute any code.
78
+ * @param {string} name the unique plugin name
79
+ * @param {PluginOptions} options the plugin configuration
80
+ */
81
+ const registerPlugin = (name, options) => {
82
+ if (PLUGINS_SELECTORS.includes(name))
83
+ throw new Error(`plugin ${name} already registered`);
84
+ PLUGINS_SELECTORS.push(name);
85
+ plugins.set(name, { options, known: new WeakSet() });
86
+ $$(name).forEach(handlePlugin);
87
+ };
88
+ exports.registerPlugin = registerPlugin;
89
+ /* c8 ignore stop */
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+ const { clean, writeFile: writeFileUtil } = require("./_utils.js");
3
+
4
+ // REQUIRES INTEGRATION TEST
5
+ /* c8 ignore start */
6
+ const run = (runtime, code) => runtime.runPython(clean(code));
7
+ exports.run = run;
8
+
9
+ const runAsync = (runtime, code) => runtime.runPythonAsync(clean(code));
10
+ exports.runAsync = runAsync;
11
+
12
+ function runEvent(runtime, code, key) {
13
+ code = `import js;event=js.__events.get(${key});${code}`;
14
+ return this.run(runtime, code);
15
+ }
16
+ exports.runEvent = runEvent
17
+
18
+ const worker = (method) =>
19
+ function (runtime, code, xworker) {
20
+ code = `from js import xworker;${code}`;
21
+ globalThis.xworker = xworker;
22
+ return this[method](runtime, code);
23
+ };
24
+
25
+ const runWorker = worker("run");
26
+ exports.runWorker = runWorker;
27
+
28
+ const runWorkerAsync = worker("runAsync");
29
+ exports.runWorkerAsync = runWorkerAsync;
30
+
31
+ const writeFile = ({ FS }, path, buffer) =>
32
+ writeFileUtil(FS, path, buffer);
33
+ exports.writeFile = writeFile;
34
+ /* c8 ignore stop */
@@ -0,0 +1,141 @@
1
+ 'use strict';
2
+ require("@ungap/with-resolvers");
3
+
4
+ const { getBuffer } = require("../fetch-utils.js");
5
+ const { absoluteURL } = require("../utils.js");
6
+
7
+ /**
8
+ * Trim code only if it's a single line that prettier or other tools might have modified.
9
+ * @param {string} code code that might be a single line
10
+ * @returns {string}
11
+ */
12
+ const clean = (code) =>
13
+ code.replace(/^[^\r\n]+$/, (line) => line.trim());
14
+ exports.clean = clean;
15
+
16
+ // REQUIRES INTEGRATION TEST
17
+ /* c8 ignore start */
18
+ const io = new WeakMap();
19
+ exports.io = io;
20
+ const stdio = (init) => {
21
+ const context = init || console;
22
+ const localIO = {
23
+ stderr: (context.stderr || console.error).bind(context),
24
+ stdout: (context.stdout || console.log).bind(context),
25
+ };
26
+ return {
27
+ stderr: (...args) => localIO.stderr(...args),
28
+ stdout: (...args) => localIO.stdout(...args),
29
+ async get(engine) {
30
+ const runtime = await engine;
31
+ io.set(runtime, localIO);
32
+ return runtime;
33
+ },
34
+ };
35
+ };
36
+ exports.stdio = stdio;
37
+ /* c8 ignore stop */
38
+
39
+ // This should be the only helper needed for all Emscripten based FS exports
40
+ const writeFile = (FS, path, buffer) => {
41
+ const { parentPath, name } = FS.analyzePath(path, true);
42
+ FS.mkdirTree(parentPath);
43
+ return FS.writeFile([parentPath, name].join("/"), new Uint8Array(buffer), {
44
+ canOwn: true,
45
+ });
46
+ };
47
+ exports.writeFile = writeFile;
48
+
49
+ // This is instead a fallback for Lua or others
50
+ const writeFileShim = (FS, path, buffer) => {
51
+ path = resolve(FS, path);
52
+ mkdirTree(FS, dirname(path));
53
+ return FS.writeFile(path, new Uint8Array(buffer), { canOwn: true });
54
+ };
55
+ exports.writeFileShim = writeFileShim;
56
+
57
+ const dirname = (path) => {
58
+ const tree = path.split("/");
59
+ tree.pop();
60
+ return tree.join("/");
61
+ };
62
+
63
+ const mkdirTree = (FS, path) => {
64
+ const current = [];
65
+ for (const branch of path.split("/")) {
66
+ current.push(branch);
67
+ if (branch) FS.mkdir(current.join("/"));
68
+ }
69
+ };
70
+
71
+ const resolve = (FS, path) => {
72
+ const tree = [];
73
+ for (const branch of path.split("/")) {
74
+ switch (branch) {
75
+ case "":
76
+ break;
77
+ case ".":
78
+ break;
79
+ case "..":
80
+ tree.pop();
81
+ break;
82
+ default:
83
+ tree.push(branch);
84
+ }
85
+ }
86
+ return [FS.cwd()].concat(tree).join("/").replace(/^\/+/, "/");
87
+ };
88
+
89
+ const { all, isArray } = require("../utils.js");
90
+
91
+ const calculateFetchPaths = (config_fetch) => {
92
+ // REQUIRES INTEGRATION TEST
93
+ /* c8 ignore start */
94
+ for (const { files, to_file, from = "" } of config_fetch) {
95
+ if (files !== undefined && to_file !== undefined)
96
+ throw new Error(
97
+ `Cannot use 'to_file' and 'files' parameters together!`,
98
+ );
99
+ if (files === undefined && to_file === undefined && from.endsWith("/"))
100
+ throw new Error(
101
+ `Couldn't determine the filename from the path ${from}, please supply 'to_file' parameter.`,
102
+ );
103
+ }
104
+ /* c8 ignore stop */
105
+ return config_fetch.flatMap(
106
+ ({ from = "", to_folder = ".", to_file, files }) => {
107
+ if (isArray(files))
108
+ return files.map((file) => ({
109
+ url: joinPaths([from, file]),
110
+ path: joinPaths([to_folder, file]),
111
+ }));
112
+ const filename = to_file || from.slice(1 + from.lastIndexOf("/"));
113
+ return [{ url: from, path: joinPaths([to_folder, filename]) }];
114
+ },
115
+ );
116
+ };
117
+
118
+ const joinPaths = (parts) => {
119
+ const res = parts
120
+ .map((part) => part.trim().replace(/(^[/]*|[/]*$)/g, ""))
121
+ .filter((p) => p !== "" && p !== ".")
122
+ .join("/");
123
+
124
+ return parts[0].startsWith("/") ? `/${res}` : res;
125
+ };
126
+
127
+ const fetchResolved = (config_fetch, url) =>
128
+ fetch(absoluteURL(url, base.get(config_fetch)));
129
+
130
+ const base = new WeakMap();
131
+ exports.base = base;
132
+
133
+ const fetchPaths = (module, runtime, config_fetch) =>
134
+ all(
135
+ calculateFetchPaths(config_fetch).map(({ url, path }) =>
136
+ fetchResolved(config_fetch, url)
137
+ .then(getBuffer)
138
+ .then((buffer) => module.writeFile(runtime, path, buffer)),
139
+ ),
140
+ );
141
+ exports.fetchPaths = fetchPaths;
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+ const { fetchPaths, stdio } = require("./_utils.js");
3
+ const {
4
+ run,
5
+ runAsync,
6
+ runEvent,
7
+ runWorker,
8
+ runWorkerAsync,
9
+ writeFile
10
+ } = require("./_python.js");
11
+
12
+ const type = "micropython";
13
+
14
+ let patchPromise = true;
15
+
16
+ // REQUIRES INTEGRATION TEST
17
+ /* c8 ignore start */
18
+ module.exports = {
19
+ type: [type, "mpy"],
20
+ module: () => `http://localhost:8080/micropython/micropython.mjs`,
21
+ async engine({ loadMicroPython }, config, url) {
22
+ // @bug https://github.com/micropython/micropython/issues/11749
23
+ if (patchPromise) {
24
+ patchPromise = false;
25
+ globalThis.Promise = class extends Promise {};
26
+ }
27
+ const { stderr, stdout, get } = stdio();
28
+ url = url.replace(/\.m?js$/, ".wasm");
29
+ const runtime = await get(loadMicroPython({ stderr, stdout, url }));
30
+ if (config.fetch) await fetchPaths(this, runtime, config.fetch);
31
+ return runtime;
32
+ },
33
+ run,
34
+ runAsync,
35
+ runEvent,
36
+ runWorker,
37
+ runWorkerAsync,
38
+ writeFile,
39
+ };
40
+ /* c8 ignore stop */
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+ const { fetchPaths, stdio } = require("./_utils.js");
3
+ const {
4
+ run,
5
+ runAsync,
6
+ runEvent,
7
+ runWorker,
8
+ runWorkerAsync,
9
+ writeFile
10
+ } = require("./_python.js");
11
+
12
+ const type = "pyodide";
13
+
14
+ // REQUIRES INTEGRATION TEST
15
+ /* c8 ignore start */
16
+ module.exports = {
17
+ type: [type, "py"],
18
+ module: (version = "0.23.2") =>
19
+ `https://cdn.jsdelivr.net/pyodide/v${version}/full/pyodide.mjs`,
20
+ async engine({ loadPyodide }, config, url) {
21
+ const { stderr, stdout, get } = stdio();
22
+ const indexURL = url.slice(0, url.lastIndexOf("/"));
23
+ const runtime = await get(loadPyodide({ stderr, stdout, indexURL }));
24
+ if (config.fetch) await fetchPaths(this, runtime, config.fetch);
25
+ if (config.packages) {
26
+ await runtime.loadPackage("micropip");
27
+ const micropip = await runtime.pyimport("micropip");
28
+ await micropip.install(config.packages);
29
+ micropip.destroy();
30
+ }
31
+ return runtime;
32
+ },
33
+ run,
34
+ runAsync,
35
+ runEvent,
36
+ runWorker,
37
+ runWorkerAsync,
38
+ writeFile,
39
+ };
40
+ /* c8 ignore stop */
@@ -0,0 +1,50 @@
1
+ 'use strict';
2
+ const { clean, fetchPaths } = require("./_utils.js");
3
+
4
+ const type = "ruby";
5
+
6
+ // MISSING:
7
+ // * there is no VFS apparently or I couldn't reach any
8
+ // * I've no idea how to override the stderr and stdout
9
+ // * I've no idea how to import packages
10
+
11
+ // REQUIRES INTEGRATION TEST
12
+ /* c8 ignore start */
13
+ const worker = (method) =>
14
+ function (runtime, code, xworker) {
15
+ globalThis.xworker = xworker;
16
+ return this[method](
17
+ runtime,
18
+ `require "js";xworker=JS::eval("return xworker");${code}`,
19
+ );
20
+ };
21
+
22
+ module.exports = {
23
+ experimental: true,
24
+ type: [type, "rb"],
25
+ module: (version = "2.0.0") =>
26
+ `https://cdn.jsdelivr.net/npm/ruby-3_2-wasm-wasi@${version}/dist/browser.esm.js`,
27
+ async engine({ DefaultRubyVM }, config, url) {
28
+ const response = await fetch(
29
+ `${url.slice(0, url.lastIndexOf("/"))}/ruby.wasm`,
30
+ );
31
+ const module = await WebAssembly.compile(await response.arrayBuffer());
32
+ const { vm: runtime } = await DefaultRubyVM(module);
33
+ if (config.fetch) await fetchPaths(this, runtime, config.fetch);
34
+ return runtime;
35
+ },
36
+ run: (runtime, code) => runtime.eval(clean(code)),
37
+ runAsync: (runtime, code) => runtime.evalAsync(clean(code)),
38
+ runEvent(runtime, code, key) {
39
+ return this.run(
40
+ runtime,
41
+ `require "js";event=JS::eval("return __events.get(${key})");${code}`,
42
+ );
43
+ },
44
+ runWorker: worker("run"),
45
+ runWorkerAsync: worker("runAsync"),
46
+ writeFile: () => {
47
+ throw new Error(`writeFile is not supported in ${type}`);
48
+ },
49
+ };
50
+ /* c8 ignore stop */
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+ const { clean, fetchPaths, stdio, writeFileShim } = require("./_utils.js");
3
+
4
+ const type = "wasmoon";
5
+
6
+ // REQUIRES INTEGRATION TEST
7
+ /* c8 ignore start */
8
+ const worker = (method) =>
9
+ function (runtime, code, xworker) {
10
+ runtime.global.set("xworker", xworker);
11
+ return this[method](runtime, code);
12
+ };
13
+
14
+ module.exports = {
15
+ type: [type, "lua"],
16
+ module: (version = "1.15.0") =>
17
+ `https://cdn.jsdelivr.net/npm/wasmoon@${version}/+esm`,
18
+ async engine({ LuaFactory, LuaLibraries }, config) {
19
+ const { stderr, stdout, get } = stdio();
20
+ const runtime = await get(new LuaFactory().createEngine());
21
+ runtime.global.getTable(LuaLibraries.Base, (index) => {
22
+ runtime.global.setField(index, "print", stdout);
23
+ runtime.global.setField(index, "printErr", stderr);
24
+ });
25
+ if (config.fetch) await fetchPaths(this, runtime, config.fetch);
26
+ return runtime;
27
+ },
28
+ run: (runtime, code) => runtime.doStringSync(clean(code)),
29
+ runAsync: (runtime, code) => runtime.doString(clean(code)),
30
+ runEvent(runtime, code, key) {
31
+ runtime.global.set("event", globalThis.__events.get(key));
32
+ return this.run(runtime, code);
33
+ },
34
+ runWorker: worker("run"),
35
+ runWorkerAsync: worker("runAsync"),
36
+ writeFile: (
37
+ {
38
+ cmodule: {
39
+ module: { FS },
40
+ },
41
+ },
42
+ path,
43
+ buffer,
44
+ ) => writeFileShim(FS, path, buffer),
45
+ };
46
+ /* c8 ignore stop */