@pyscript/core 0.0.0

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 (86) hide show
  1. package/README.md +33 -0
  2. package/cjs/custom-types.js +212 -0
  3. package/cjs/fetch-utils.js +12 -0
  4. package/cjs/index.js +73 -0
  5. package/cjs/interpreter/_python.js +16 -0
  6. package/cjs/interpreter/_utils.js +141 -0
  7. package/cjs/interpreter/micropython.js +34 -0
  8. package/cjs/interpreter/pyodide.js +38 -0
  9. package/cjs/interpreter/ruby-wasm-wasi.js +43 -0
  10. package/cjs/interpreter/wasmoon.js +40 -0
  11. package/cjs/interpreters.js +64 -0
  12. package/cjs/listeners.js +71 -0
  13. package/cjs/loader.js +43 -0
  14. package/cjs/package.json +1 -0
  15. package/cjs/plugins/pyscript.js +105 -0
  16. package/cjs/script-handler.js +141 -0
  17. package/cjs/toml.js +10 -0
  18. package/cjs/utils.js +19 -0
  19. package/cjs/worker/class.js +46 -0
  20. package/cjs/worker/hooks.js +2 -0
  21. package/cjs/worker/xworker.js +3 -0
  22. package/esm/custom-types.js +207 -0
  23. package/esm/fetch-utils.js +8 -0
  24. package/esm/index.js +68 -0
  25. package/esm/interpreter/_python.js +12 -0
  26. package/esm/interpreter/_utils.js +133 -0
  27. package/esm/interpreter/micropython.js +33 -0
  28. package/esm/interpreter/pyodide.js +37 -0
  29. package/esm/interpreter/ruby-wasm-wasi.js +42 -0
  30. package/esm/interpreter/wasmoon.js +39 -0
  31. package/esm/interpreters.js +58 -0
  32. package/esm/listeners.js +68 -0
  33. package/esm/loader.js +40 -0
  34. package/esm/script-handler.js +136 -0
  35. package/esm/toml.js +8 -0
  36. package/esm/utils.js +20 -0
  37. package/esm/worker/class.js +45 -0
  38. package/esm/worker/hooks.js +1 -0
  39. package/esm/worker/xworker.js +2 -0
  40. package/min.js +2 -0
  41. package/package.json +55 -0
  42. package/types/coincident/structured.d.ts +5 -0
  43. package/types/fetch-utils.d.ts +3 -0
  44. package/types/index.d.ts +2 -0
  45. package/types/loader.d.ts +2 -0
  46. package/types/plugins.d.ts +61 -0
  47. package/types/pyscript/pyscript.core/esm/custom-types.d.ts +54 -0
  48. package/types/pyscript/pyscript.core/esm/fetch-utils.d.ts +3 -0
  49. package/types/pyscript/pyscript.core/esm/index.d.ts +2 -0
  50. package/types/pyscript/pyscript.core/esm/interpreter/_python.d.ts +5 -0
  51. package/types/pyscript/pyscript.core/esm/interpreter/_utils.d.ts +11 -0
  52. package/types/pyscript/pyscript.core/esm/interpreter/micropython.d.ts +17 -0
  53. package/types/pyscript/pyscript.core/esm/interpreter/pyodide.d.ts +17 -0
  54. package/types/pyscript/pyscript.core/esm/interpreter/ruby-wasm-wasi.d.ts +15 -0
  55. package/types/pyscript/pyscript.core/esm/interpreter/ruby.d.ts +16 -0
  56. package/types/pyscript/pyscript.core/esm/interpreter/wasmoon.d.ts +21 -0
  57. package/types/pyscript/pyscript.core/esm/interpreters.d.ts +9 -0
  58. package/types/pyscript/pyscript.core/esm/listeners.d.ts +2 -0
  59. package/types/pyscript/pyscript.core/esm/loader.d.ts +2 -0
  60. package/types/pyscript/pyscript.core/esm/plugins.d.ts +54 -0
  61. package/types/pyscript/pyscript.core/esm/runtime/_python.d.ts +8 -0
  62. package/types/pyscript/pyscript.core/esm/runtime/_utils.d.ts +11 -0
  63. package/types/pyscript/pyscript.core/esm/runtime/micropython.d.ts +20 -0
  64. package/types/pyscript/pyscript.core/esm/runtime/pyodide.d.ts +20 -0
  65. package/types/pyscript/pyscript.core/esm/runtime/ruby.d.ts +15 -0
  66. package/types/pyscript/pyscript.core/esm/runtime/wasmoon.d.ts +21 -0
  67. package/types/pyscript/pyscript.core/esm/runtimes.d.ts +9 -0
  68. package/types/pyscript/pyscript.core/esm/script-handler.d.ts +4 -0
  69. package/types/pyscript/pyscript.core/esm/toml.d.ts +1 -0
  70. package/types/pyscript/pyscript.core/esm/utils.d.ts +23 -0
  71. package/types/pyscript/pyscript.core/esm/worker/class.d.ts +19 -0
  72. package/types/pyscript/pyscript.core/esm/worker/hooks.d.ts +2 -0
  73. package/types/pyscript/pyscript.core/esm/worker/xworker.d.ts +2 -0
  74. package/types/runtime/_python.d.ts +8 -0
  75. package/types/runtime/_utils.d.ts +11 -0
  76. package/types/runtime/micropython.d.ts +20 -0
  77. package/types/runtime/pyodide.d.ts +20 -0
  78. package/types/runtime/ruby.d.ts +15 -0
  79. package/types/runtime/wasmoon.d.ts +21 -0
  80. package/types/runtimes.d.ts +9 -0
  81. package/types/script-handler.d.ts +3 -0
  82. package/types/toml.d.ts +1 -0
  83. package/types/utils.d.ts +23 -0
  84. package/types/worker/class.d.ts +19 -0
  85. package/types/worker/hooks.d.ts +2 -0
  86. package/types/worker/xworker.d.ts +2 -0
package/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # @pyscript/core
2
+
3
+ [![build](https://github.com/WebReflection/python/actions/workflows/node.js.yml/badge.svg)](https://github.com/WebReflection/python/actions/workflows/node.js.yml) [![Coverage Status](https://coveralls.io/repos/github/WebReflection/python/badge.svg?branch=api&t=1RBdLX)](https://coveralls.io/github/WebReflection/python?branch=api)
4
+
5
+ ---
6
+
7
+ ## Development
8
+
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).
10
+
11
+ ```sh
12
+ # install all dependencies needed by core
13
+ npm i
14
+ ```
15
+
16
+ ### Build / Artifacts
17
+
18
+ This project requires some automatic artifact creation to:
19
+
20
+ * create a _Worker_ as a _Blob_ based on the same code used by this repo
21
+ * create automatically the list of runtimes available via the module
22
+ * create the `min.js` file used by most integration tests
23
+ * create a sha256 version of the Blob content for CSP cases
24
+
25
+ Accordingly, to build latest project:
26
+
27
+ ```sh
28
+ # create all artifacts needed to test core
29
+ npm run build
30
+
31
+ # optionally spin a server with CORS, COOP, and COEP enabled
32
+ npm run server
33
+ ```
@@ -0,0 +1,212 @@
1
+ 'use strict';
2
+ require("@ungap/with-resolvers");
3
+ const { $$ } = require("basic-devtools");
4
+
5
+ const { assign, create } = require("./utils.js");
6
+ const { getDetails } = require("./script-handler.js");
7
+ const {
8
+ registry: defaultRegistry,
9
+ prefixes,
10
+ configs
11
+ } = require("./interpreters.js");
12
+ const { getRuntimeID } = require("./loader.js");
13
+ const { io } = require("./interpreter/_utils.js");
14
+ const { addAllListeners } = require("./listeners.js");
15
+
16
+ const workerHooks = (m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)(require("./worker/hooks.js"));
17
+
18
+ const CUSTOM_SELECTORS = [];
19
+ exports.CUSTOM_SELECTORS = CUSTOM_SELECTORS;
20
+
21
+ /**
22
+ * @typedef {Object} Runtime custom configuration
23
+ * @prop {object} interpreter the bootstrapped interpreter
24
+ * @prop {(url:string, options?: object) => Worker} XWorker an XWorker constructor that defaults to same interpreter on the Worker.
25
+ * @prop {object} config a cloned config used to bootstrap the interpreter
26
+ * @prop {(code:string) => any} run an utility to run code within the interpreter
27
+ * @prop {(code:string) => Promise<any>} runAsync an utility to run code asynchronously within the interpreter
28
+ * @prop {(path:string, data:ArrayBuffer) => void} writeFile an utility to write a file in the virtual FS, if available
29
+ */
30
+
31
+ const patched = new Map();
32
+ const types = new Map();
33
+ const waitList = new Map();
34
+
35
+ // REQUIRES INTEGRATION TEST
36
+ /* c8 ignore start */
37
+ /**
38
+ * @param {Element} node any DOM element registered via define.
39
+ */
40
+ const handleCustomType = (node) => {
41
+ for (const selector of CUSTOM_SELECTORS) {
42
+ if (node.matches(selector)) {
43
+ const type = types.get(selector);
44
+ const { resolve } = waitList.get(type);
45
+ const { options, known } = registry.get(type);
46
+ if (!known.has(node)) {
47
+ known.add(node);
48
+ const {
49
+ interpreter: runtime,
50
+ version,
51
+ config,
52
+ env,
53
+ onRuntimeReady,
54
+ } = options;
55
+ const name = getRuntimeID(runtime, version);
56
+ const id = env || `${name}${config ? `|${config}` : ""}`;
57
+ const { interpreter: engine, XWorker } = getDetails(
58
+ runtime,
59
+ id,
60
+ name,
61
+ version,
62
+ config,
63
+ );
64
+ engine.then((interpreter) => {
65
+ if (!patched.has(id)) {
66
+ const module = create(defaultRegistry.get(runtime));
67
+ const {
68
+ onBeforeRun,
69
+ onBeforeRunAsync,
70
+ onAfterRun,
71
+ onAfterRunAsync,
72
+ codeBeforeRunWorker,
73
+ codeBeforeRunWorkerAsync,
74
+ codeAfterRunWorker,
75
+ codeAfterRunWorkerAsync,
76
+ } = options;
77
+
78
+ // These two loops mimic a `new Map(arrayContent)` without needing
79
+ // the new Map overhead so that [name, [before, after]] can be easily destructured
80
+ // and new sync or async patches become easy to add (when the logic is the same).
81
+
82
+ // patch sync
83
+ for (const [name, [before, after]] of [
84
+ ["run", [onBeforeRun, onAfterRun]],
85
+ ]) {
86
+ const method = module[name];
87
+ module[name] = function (interpreter, code) {
88
+ if (before) before.call(this, resolved, node);
89
+ const result = method.call(
90
+ this,
91
+ interpreter,
92
+ code,
93
+ );
94
+ if (after) after.call(this, resolved, node);
95
+ return result;
96
+ };
97
+ }
98
+
99
+ // patch async
100
+ for (const [name, [before, after]] of [
101
+ ["runAsync", [onBeforeRunAsync, onAfterRunAsync]],
102
+ ]) {
103
+ const method = module[name];
104
+ module[name] = async function (interpreter, code) {
105
+ if (before)
106
+ await before.call(this, resolved, node);
107
+ const result = await method.call(
108
+ this,
109
+ interpreter,
110
+ code,
111
+ );
112
+ if (after)
113
+ await after.call(this, resolved, node);
114
+ return result;
115
+ };
116
+ }
117
+
118
+ // setup XWorker hooks, allowing strings to be forwarded to the worker
119
+ // whenever it's created, as functions can't possibly be serialized
120
+ // unless these are pure with no outer scope access (or globals vars)
121
+ // so that making it strings disambiguate about their running context.
122
+ workerHooks.set(XWorker, {
123
+ beforeRun: codeBeforeRunWorker,
124
+ beforeRunAsync: codeBeforeRunWorkerAsync,
125
+ afterRun: codeAfterRunWorker,
126
+ afterRunAsync: codeAfterRunWorkerAsync,
127
+ });
128
+
129
+ module.setGlobal(interpreter, "XWorker", XWorker);
130
+
131
+ const resolved = {
132
+ type,
133
+ interpreter,
134
+ XWorker,
135
+ io: io.get(interpreter),
136
+ config: structuredClone(configs.get(name)),
137
+ run: module.run.bind(module, interpreter),
138
+ runAsync: module.runAsync.bind(module, interpreter),
139
+ };
140
+
141
+ patched.set(id, resolved);
142
+ resolve(resolved);
143
+ }
144
+
145
+ onRuntimeReady?.(patched.get(id), node);
146
+ });
147
+ }
148
+ }
149
+ }
150
+ };
151
+ exports.handleCustomType = handleCustomType;
152
+
153
+ /**
154
+ * @type {Map<string, {options:object, known:WeakSet<Element>}>}
155
+ */
156
+ const registry = new Map();
157
+
158
+ /**
159
+ * @typedef {Object} PluginOptions custom configuration
160
+ * @prop {'pyodide' | 'micropython' | 'wasmoon' | 'ruby-wasm-wasi'} interpreter the interpreter to use
161
+ * @prop {string} [version] the optional interpreter version to use
162
+ * @prop {string} [config] the optional config to use within such interpreter
163
+ * @prop {(environment: object, node: Element) => void} [onRuntimeReady] the callback that will be invoked once
164
+ */
165
+
166
+ /**
167
+ * Allows custom types and components on the page to receive interpreters to execute any code
168
+ * @param {string} type the unique `<script type="...">` identifier
169
+ * @param {PluginOptions} options the custom type configuration
170
+ */
171
+ const define = (type, options) => {
172
+ if (defaultRegistry.has(type) || registry.has(type))
173
+ throw new Error(`<script type="${type}"> already registered`);
174
+
175
+ if (!defaultRegistry.has(options?.interpreter))
176
+ throw new Error(`Unspecified interpreter`);
177
+
178
+ // allows reaching out the interpreter helpers on events
179
+ defaultRegistry.set(type, defaultRegistry.get(options?.interpreter));
180
+
181
+ // ensure a Promise can resolve once a custom type has been bootstrapped
182
+ whenDefined(type);
183
+
184
+ // allows selector -> registry by type
185
+ const selectors = [`script[type="${type}"]`, `${type}-script`];
186
+ for (const selector of selectors) types.set(selector, type);
187
+
188
+ CUSTOM_SELECTORS.push(...selectors);
189
+ prefixes.push(`${type}-`);
190
+
191
+ // ensure always same env for this custom type
192
+ registry.set(type, {
193
+ options: assign({ env: type }, options),
194
+ known: new WeakSet(),
195
+ });
196
+
197
+ addAllListeners(document);
198
+ $$(selectors.join(",")).forEach(handleCustomType);
199
+ };
200
+ exports.define = define;
201
+
202
+ /**
203
+ * Resolves whenever a defined custom type is bootstrapped on the page
204
+ * @param {string} type the unique `<script type="...">` identifier
205
+ * @returns {Promise<object>}
206
+ */
207
+ const whenDefined = (type) => {
208
+ if (!waitList.has(type)) waitList.set(type, Promise.withResolvers());
209
+ return waitList.get(type).promise;
210
+ };
211
+ exports.whenDefined = whenDefined;
212
+ /* c8 ignore stop */
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+ /** @param {Response} response */
3
+ const getBuffer = (response) => response.arrayBuffer();
4
+ exports.getBuffer = getBuffer;
5
+
6
+ /** @param {Response} response */
7
+ const getJSON = (response) => response.json();
8
+ exports.getJSON = getJSON;
9
+
10
+ /** @param {Response} response */
11
+ const getText = (response) => response.text();
12
+ exports.getText = getText;
package/cjs/index.js ADDED
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+ const { $$ } = require("basic-devtools");
3
+
4
+ const xworker = (m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)(require("./worker/class.js"));
5
+ const { handle } = require("./script-handler.js");
6
+ const { assign } = require("./utils.js");
7
+ const { selectors, prefixes } = require("./interpreters.js");
8
+ const { CUSTOM_SELECTORS, handleCustomType } = require("./custom-types.js");
9
+ const { listener, addAllListeners } = require("./listeners.js");
10
+
11
+ (m => {
12
+ exports.define = m.define;
13
+ exports.whenDefined = m.whenDefined;
14
+ })(require("./custom-types.js"));
15
+ const XWorker = xworker();
16
+ exports.XWorker = XWorker;
17
+
18
+ const INTERPRETER_SELECTORS = selectors.join(",");
19
+
20
+ const mo = new MutationObserver((records) => {
21
+ for (const { type, target, attributeName, addedNodes } of records) {
22
+ // attributes are tested via integration / e2e
23
+ /* c8 ignore next 17 */
24
+ if (type === "attributes") {
25
+ const i = attributeName.lastIndexOf("-") + 1;
26
+ if (i) {
27
+ const prefix = attributeName.slice(0, i);
28
+ for (const p of prefixes) {
29
+ if (prefix === p) {
30
+ const type = attributeName.slice(i);
31
+ if (type !== "env") {
32
+ const method = target.hasAttribute(attributeName)
33
+ ? "add"
34
+ : "remove";
35
+ target[`${method}EventListener`](type, listener);
36
+ }
37
+ break;
38
+ }
39
+ }
40
+ }
41
+ continue;
42
+ }
43
+ for (const node of addedNodes) {
44
+ if (node.nodeType === 1) {
45
+ addAllListeners(node);
46
+ if (node.matches(INTERPRETER_SELECTORS)) handle(node);
47
+ else {
48
+ $$(INTERPRETER_SELECTORS, node).forEach(handle);
49
+ if (!CUSTOM_SELECTORS.length) continue;
50
+ handleCustomType(node);
51
+ $$(CUSTOM_SELECTORS.join(","), node).forEach(
52
+ handleCustomType,
53
+ );
54
+ }
55
+ }
56
+ }
57
+ }
58
+ });
59
+
60
+ const observe = (root) => {
61
+ mo.observe(root, { childList: true, subtree: true, attributes: true });
62
+ return root;
63
+ };
64
+
65
+ const { attachShadow } = Element.prototype;
66
+ assign(Element.prototype, {
67
+ attachShadow(init) {
68
+ return observe(attachShadow.call(this, init));
69
+ },
70
+ });
71
+
72
+ addAllListeners(observe(document));
73
+ $$(INTERPRETER_SELECTORS, document).forEach(handle);
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+ const { clean, writeFile: writeFileUtil } = require("./_utils.js");
3
+
4
+ // REQUIRES INTEGRATION TEST
5
+ /* c8 ignore start */
6
+ const run = (interpreter, code) => interpreter.runPython(clean(code));
7
+ exports.run = run;
8
+
9
+ const runAsync = (interpreter, code) =>
10
+ interpreter.runPythonAsync(clean(code));
11
+ exports.runAsync = runAsync;
12
+
13
+ const writeFile = ({ FS }, path, buffer) =>
14
+ writeFileUtil(FS, path, buffer);
15
+ exports.writeFile = writeFile;
16
+ /* 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 interpreter = await engine;
31
+ io.set(interpreter, localIO);
32
+ return interpreter;
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, interpreter, config_fetch) =>
134
+ all(
135
+ calculateFetchPaths(config_fetch).map(({ url, path }) =>
136
+ fetchResolved(config_fetch, url)
137
+ .then(getBuffer)
138
+ .then((buffer) => module.writeFile(interpreter, path, buffer)),
139
+ ),
140
+ );
141
+ exports.fetchPaths = fetchPaths;
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+ const { fetchPaths, stdio } = require("./_utils.js");
3
+ const { run, runAsync, writeFile } = require("./_python.js");
4
+
5
+ const type = "micropython";
6
+
7
+ // REQUIRES INTEGRATION TEST
8
+ /* c8 ignore start */
9
+ module.exports = {
10
+ type,
11
+ module: (version = "1.20.0-239") =>
12
+ `https://cdn.jsdelivr.net/npm/@micropython/micropython-webassembly-pyscript@${version}/micropython.mjs`,
13
+ async engine({ loadMicroPython }, config, url) {
14
+ const { stderr, stdout, get } = stdio();
15
+ url = url.replace(/\.m?js$/, ".wasm");
16
+ const runtime = await get(loadMicroPython({ stderr, stdout, url }));
17
+ if (config.fetch) await fetchPaths(this, runtime, config.fetch);
18
+ return runtime;
19
+ },
20
+ setGlobal(interpreter, name, value) {
21
+ const id = `__pyscript_${this.type}_${name}`;
22
+ globalThis[id] = value;
23
+ this.run(interpreter, `from js import ${id};${name}=${id};`);
24
+ },
25
+ deleteGlobal(interpreter, name) {
26
+ const id = `__pyscript_${this.type}_${name}`;
27
+ this.run(interpreter, `del ${id};del ${name}`);
28
+ delete globalThis[id];
29
+ },
30
+ run,
31
+ runAsync,
32
+ writeFile,
33
+ };
34
+ /* c8 ignore stop */
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+ const { fetchPaths, stdio } = require("./_utils.js");
3
+ const { run, runAsync, writeFile } = require("./_python.js");
4
+
5
+ const type = "pyodide";
6
+
7
+ // REQUIRES INTEGRATION TEST
8
+ /* c8 ignore start */
9
+ module.exports = {
10
+ type,
11
+ module: (version = "0.23.2") =>
12
+ `https://cdn.jsdelivr.net/pyodide/v${version}/full/pyodide.mjs`,
13
+ async engine({ loadPyodide }, config, url) {
14
+ const { stderr, stdout, get } = stdio();
15
+ const indexURL = url.slice(0, url.lastIndexOf("/"));
16
+ const interpreter = await get(
17
+ loadPyodide({ stderr, stdout, indexURL }),
18
+ );
19
+ if (config.fetch) await fetchPaths(this, interpreter, config.fetch);
20
+ if (config.packages) {
21
+ await interpreter.loadPackage("micropip");
22
+ const micropip = await interpreter.pyimport("micropip");
23
+ await micropip.install(config.packages);
24
+ micropip.destroy();
25
+ }
26
+ return interpreter;
27
+ },
28
+ setGlobal(interpreter, name, value) {
29
+ interpreter.globals.set(name, value);
30
+ },
31
+ deleteGlobal(interpreter, name) {
32
+ interpreter.globals.delete(name);
33
+ },
34
+ run,
35
+ runAsync,
36
+ writeFile,
37
+ };
38
+ /* c8 ignore stop */
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+ const { clean, fetchPaths } = require("./_utils.js");
3
+
4
+ const type = "ruby-wasm-wasi";
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
+ module.exports = {
14
+ type,
15
+ experimental: true,
16
+ module: (version = "2.0.0") =>
17
+ `https://cdn.jsdelivr.net/npm/ruby-3_2-wasm-wasi@${version}/dist/browser.esm.js`,
18
+ async engine({ DefaultRubyVM }, config, url) {
19
+ const response = await fetch(
20
+ `${url.slice(0, url.lastIndexOf("/"))}/ruby.wasm`,
21
+ );
22
+ const module = await WebAssembly.compile(await response.arrayBuffer());
23
+ const { vm: interpreter } = await DefaultRubyVM(module);
24
+ if (config.fetch) await fetchPaths(this, interpreter, config.fetch);
25
+ return interpreter;
26
+ },
27
+ setGlobal(interpreter, name, value) {
28
+ const id = `__pyscript_ruby_wasm_wasi_${name}`;
29
+ globalThis[id] = value;
30
+ this.run(interpreter, `require "js";$${name}=JS::eval("return ${id}")`);
31
+ },
32
+ deleteGlobal(interpreter, name) {
33
+ const id = `__pyscript_ruby_wasm_wasi_${name}`;
34
+ this.run(interpreter, `$${name}=nil`);
35
+ delete globalThis[id];
36
+ },
37
+ run: (interpreter, code) => interpreter.eval(clean(code)),
38
+ runAsync: (interpreter, code) => interpreter.evalAsync(clean(code)),
39
+ writeFile: () => {
40
+ throw new Error(`writeFile is not supported in ${type}`);
41
+ },
42
+ };
43
+ /* c8 ignore stop */
@@ -0,0 +1,40 @@
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
+ module.exports = {
9
+ type,
10
+ module: (version = "1.15.0") =>
11
+ `https://cdn.jsdelivr.net/npm/wasmoon@${version}/+esm`,
12
+ async engine({ LuaFactory, LuaLibraries }, config) {
13
+ const { stderr, stdout, get } = stdio();
14
+ const interpreter = await get(new LuaFactory().createEngine());
15
+ interpreter.global.getTable(LuaLibraries.Base, (index) => {
16
+ interpreter.global.setField(index, "print", stdout);
17
+ interpreter.global.setField(index, "printErr", stderr);
18
+ });
19
+ if (config.fetch) await fetchPaths(this, interpreter, config.fetch);
20
+ return interpreter;
21
+ },
22
+ setGlobal(interpreter, name, value) {
23
+ interpreter.global.set(name, value);
24
+ },
25
+ deleteGlobal(interpreter, name) {
26
+ interpreter.global.set(name, void 0);
27
+ },
28
+ run: (interpreter, code) => interpreter.doStringSync(clean(code)),
29
+ runAsync: (interpreter, code) => interpreter.doString(clean(code)),
30
+ writeFile: (
31
+ {
32
+ cmodule: {
33
+ module: { FS },
34
+ },
35
+ },
36
+ path,
37
+ buffer,
38
+ ) => writeFileShim(FS, path, buffer),
39
+ };
40
+ /* c8 ignore stop */