pyodide 0.28.3 → 0.29.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.
- package/console-v2.html +393 -0
- package/package.json +14 -26
- package/pyodide-lock.json +1 -1
- package/pyodide.asm.js +1 -1
- package/pyodide.asm.wasm +0 -0
- package/pyodide.d.ts +47 -88
- package/pyodide.js +3 -3
- package/pyodide.js.map +3 -3
- package/pyodide.mjs +3 -3
- package/pyodide.mjs.map +3 -3
- package/python_stdlib.zip +0 -0
package/console-v2.html
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<!--
|
|
3
|
+
Pyodide Console v2 (Experimental)
|
|
4
|
+
|
|
5
|
+
This is an experimental version of the Pyodide console that provides
|
|
6
|
+
an enhanced terminal experience using xterm.js. This implementation replaces
|
|
7
|
+
the jQuery Terminal used in the original console with a more feature-rich
|
|
8
|
+
terminal emulator that offers better performance and modern terminal capabilities.
|
|
9
|
+
|
|
10
|
+
Note: This console is still under development and may not have all the features
|
|
11
|
+
of the stable console.
|
|
12
|
+
-->
|
|
13
|
+
<html>
|
|
14
|
+
<head>
|
|
15
|
+
<title>Pyodide Console</title>
|
|
16
|
+
<meta charset="UTF-8" />
|
|
17
|
+
<meta
|
|
18
|
+
http-equiv="origin-trial"
|
|
19
|
+
content="Aq6vv/4syIkcyMszFgCc9LlH0kX88jdE7SXfCFnh2RQN0nhhL8o6PCQ2oE3a7n3mC7+d9n89Repw5HYBtjarDw4AAAB3eyJvcmlnaW4iOiJodHRwczovL3B5b2RpZGUub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJBc3NlbWJseUpTUHJvbWlzZUludGVncmF0aW9uIiwiZXhwaXJ5IjoxNzMwMjQ2Mzk5LCJpc1N1YmRvbWFpbiI6dHJ1ZX0="
|
|
20
|
+
/>
|
|
21
|
+
<meta
|
|
22
|
+
http-equiv="origin-trial"
|
|
23
|
+
content="Ai8IXb0XqedlM/Q2guWXFfBkKiYY9uaPZpdjHqc8y0ZvpAfK9SKzp/dIuFH+txG/HEKxt59uIkk39hhWrhNgbw4AAABieyJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjgwMDAiLCJmZWF0dXJlIjoiV2ViQXNzZW1ibHlKU1Byb21pc2VJbnRlZ3JhdGlvbiIsImV4cGlyeSI6MTczMDI0NjM5OX0="
|
|
24
|
+
/>
|
|
25
|
+
<link
|
|
26
|
+
rel="stylesheet"
|
|
27
|
+
href="https://unpkg.com/@xterm/xterm@5.4.0/css/xterm.css"
|
|
28
|
+
/>
|
|
29
|
+
<link
|
|
30
|
+
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🐍</text></svg>"
|
|
31
|
+
rel="icon"
|
|
32
|
+
/>
|
|
33
|
+
<script src="https://unpkg.com/@xterm/xterm@5.4.0/lib/xterm.js"></script>
|
|
34
|
+
<script src="https://unpkg.com/@xterm/addon-fit@0.9.0/lib/addon-fit.js"></script>
|
|
35
|
+
<style>
|
|
36
|
+
* {
|
|
37
|
+
margin: 0;
|
|
38
|
+
padding: 0;
|
|
39
|
+
box-sizing: border-box;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
html,
|
|
43
|
+
body {
|
|
44
|
+
margin: 0;
|
|
45
|
+
background-color: #000000;
|
|
46
|
+
font-family: "Monaco", "Menlo", "Courier New", monospace;
|
|
47
|
+
overflow: hidden;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#terminal {
|
|
51
|
+
position: fixed;
|
|
52
|
+
inset: 10px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#loading {
|
|
56
|
+
display: inline-block;
|
|
57
|
+
width: 50px;
|
|
58
|
+
height: 50px;
|
|
59
|
+
position: fixed;
|
|
60
|
+
top: 50%;
|
|
61
|
+
left: 50%;
|
|
62
|
+
border: 3px solid rgba(172, 237, 255, 0.5);
|
|
63
|
+
border-radius: 50%;
|
|
64
|
+
border-top-color: #fff;
|
|
65
|
+
animation: spin 1s ease-in-out infinite;
|
|
66
|
+
-webkit-animation: spin 1s ease-in-out infinite;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@keyframes spin {
|
|
70
|
+
to {
|
|
71
|
+
-webkit-transform: rotate(360deg);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
@-webkit-keyframes spin {
|
|
75
|
+
to {
|
|
76
|
+
-webkit-transform: rotate(360deg);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
</style>
|
|
80
|
+
</head>
|
|
81
|
+
<body>
|
|
82
|
+
<div id="loading"></div>
|
|
83
|
+
<div id="terminal"></div>
|
|
84
|
+
<script type="module">
|
|
85
|
+
async function main() {
|
|
86
|
+
const fitAddon = new FitAddon.FitAddon();
|
|
87
|
+
const term = new Terminal({
|
|
88
|
+
cursorBlink: true,
|
|
89
|
+
cursorStyle: "block",
|
|
90
|
+
convertEol: true,
|
|
91
|
+
scrollback: 2_000,
|
|
92
|
+
fontSize: 18,
|
|
93
|
+
lineHeight: 1.4,
|
|
94
|
+
fontFamily: "monospace",
|
|
95
|
+
theme: {
|
|
96
|
+
background: "#000000",
|
|
97
|
+
foreground: "rgba(255, 255, 255, 0.8)",
|
|
98
|
+
cursor: "rgba(255, 255, 255, 0.8)",
|
|
99
|
+
selection: "#404040",
|
|
100
|
+
error: "#ff0000",
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
window.term = term;
|
|
104
|
+
|
|
105
|
+
term.open(document.getElementById("terminal"));
|
|
106
|
+
term.loadAddon(fitAddon);
|
|
107
|
+
|
|
108
|
+
fitAddon.fit();
|
|
109
|
+
term.focus();
|
|
110
|
+
|
|
111
|
+
window.addEventListener("resize", () => {
|
|
112
|
+
setTimeout(() => fitAddon.fit(), 50);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Re-fit after the page has fully loaded
|
|
116
|
+
window.addEventListener("load", () => {
|
|
117
|
+
setTimeout(() => fitAddon.fit(), 100);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Initialize Pyodide
|
|
121
|
+
let indexURL = "./";
|
|
122
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
123
|
+
const buildParam = urlParams.get("build");
|
|
124
|
+
if (buildParam && ["full", "debug", "pyc"].includes(buildParam)) {
|
|
125
|
+
indexURL = indexURL.replace("/full/", "/" + buildParam + "/");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const { loadPyodide } = await import(indexURL + "pyodide.mjs");
|
|
129
|
+
const pyodide = await loadPyodide();
|
|
130
|
+
globalThis.pyodide = pyodide;
|
|
131
|
+
|
|
132
|
+
// Hide loading spinner
|
|
133
|
+
document.getElementById("loading").style.display = "none";
|
|
134
|
+
|
|
135
|
+
const { repr_shorten, BANNER, PyodideConsole } =
|
|
136
|
+
pyodide.pyimport("pyodide.console");
|
|
137
|
+
|
|
138
|
+
term.writeln(
|
|
139
|
+
`Welcome to the Pyodide ${pyodide.version} terminal emulator 🐍\n${BANNER}`
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const pyconsole = PyodideConsole(pyodide.globals);
|
|
143
|
+
|
|
144
|
+
const namespace = pyodide.globals.get("dict")();
|
|
145
|
+
const await_fut = pyodide.runPython(
|
|
146
|
+
`
|
|
147
|
+
import builtins
|
|
148
|
+
from pyodide.ffi import to_js
|
|
149
|
+
async def await_fut(fut):
|
|
150
|
+
res = await fut
|
|
151
|
+
if res is not None:
|
|
152
|
+
builtins._ = res
|
|
153
|
+
return to_js([res], depth=1)
|
|
154
|
+
await_fut
|
|
155
|
+
`,
|
|
156
|
+
{ globals: namespace }
|
|
157
|
+
);
|
|
158
|
+
namespace.destroy();
|
|
159
|
+
|
|
160
|
+
pyconsole.stdout_callback = (s) => term.write(s);
|
|
161
|
+
pyconsole.stderr_callback = (s) => term.write(`\x1b[31m${s}\x1b[0m`);
|
|
162
|
+
|
|
163
|
+
// Handle fatal errors
|
|
164
|
+
pyodide._api.on_fatal = async (e) => {
|
|
165
|
+
if (e.name === "Exit") {
|
|
166
|
+
term.write(`\x1b[31m${e}\x1b[0m\r\n`);
|
|
167
|
+
term.write(
|
|
168
|
+
"\x1b[31mPyodide exited and can no longer be used.\x1b[0m\r\n"
|
|
169
|
+
);
|
|
170
|
+
} else {
|
|
171
|
+
term.write(
|
|
172
|
+
"\x1b[31mPyodide has suffered a fatal error. Please report this to the Pyodide maintainers.\x1b[0m\r\n"
|
|
173
|
+
);
|
|
174
|
+
term.write("\x1b[31mThe cause of the fatal error was:\x1b[0m\r\n");
|
|
175
|
+
term.write(`\x1b[31m${e.message || e}\x1b[0m\r\n`);
|
|
176
|
+
term.write(
|
|
177
|
+
"\x1b[31mLook in the browser console for more details.\x1b[0m\r\n"
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// REPL implementation
|
|
183
|
+
const ps1 = ">>> ";
|
|
184
|
+
const ps2 = "... ";
|
|
185
|
+
let buffer = "";
|
|
186
|
+
let cursorIndex = 0; // index within buffer for in-line editing
|
|
187
|
+
let prompt = ps1;
|
|
188
|
+
const history = [];
|
|
189
|
+
let historyIndex = null; // null means not navigating history
|
|
190
|
+
|
|
191
|
+
term.write(prompt);
|
|
192
|
+
|
|
193
|
+
function addToHistory(command) {
|
|
194
|
+
const trimmed = command.trimEnd();
|
|
195
|
+
if (!trimmed) return;
|
|
196
|
+
const last = history[history.length - 1];
|
|
197
|
+
if (last !== trimmed) history.push(trimmed);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function refreshLine() {
|
|
201
|
+
// Write left part, save cursor, write right part, clear, restore cursor.
|
|
202
|
+
const clearCommand = "\x1b[0K";
|
|
203
|
+
const leftPart = prompt + buffer.slice(0, cursorIndex);
|
|
204
|
+
const rightPart = buffer.slice(cursorIndex);
|
|
205
|
+
term.write(
|
|
206
|
+
`\x1b[0G${leftPart}\x1b[s${rightPart}${clearCommand}\x1b[u`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function setBuffer(newBuffer, newCursorIndex = null) {
|
|
211
|
+
buffer = newBuffer;
|
|
212
|
+
if (newCursorIndex === null) {
|
|
213
|
+
cursorIndex = buffer.length;
|
|
214
|
+
} else {
|
|
215
|
+
cursorIndex = Math.max(0, Math.min(newCursorIndex, buffer.length));
|
|
216
|
+
}
|
|
217
|
+
refreshLine();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function execLine(line) {
|
|
221
|
+
// Normalize non-breaking spaces to regular spaces
|
|
222
|
+
line = line.replace(/\u00a0/g, " ");
|
|
223
|
+
// clear the terminal
|
|
224
|
+
if (line === "clear") {
|
|
225
|
+
term.clear();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const fut = pyconsole.push(line);
|
|
230
|
+
|
|
231
|
+
switch (fut.syntax_check) {
|
|
232
|
+
case "syntax-error":
|
|
233
|
+
term.write(`\x1b[31m${fut.formatted_error.trimEnd()}\x1b[0m`);
|
|
234
|
+
term.write("\r\n");
|
|
235
|
+
prompt = ps1;
|
|
236
|
+
addToHistory(line);
|
|
237
|
+
historyIndex = null;
|
|
238
|
+
fut.destroy();
|
|
239
|
+
break;
|
|
240
|
+
case "incomplete":
|
|
241
|
+
prompt = ps2;
|
|
242
|
+
addToHistory(line);
|
|
243
|
+
historyIndex = null;
|
|
244
|
+
return;
|
|
245
|
+
case "complete":
|
|
246
|
+
prompt = ps1;
|
|
247
|
+
try {
|
|
248
|
+
const wrapped = await_fut(fut);
|
|
249
|
+
const [value] = await wrapped;
|
|
250
|
+
if (value !== undefined) {
|
|
251
|
+
const output = repr_shorten.callKwargs(value, {
|
|
252
|
+
separator: "\n<long output truncated>\n",
|
|
253
|
+
});
|
|
254
|
+
term.write(output);
|
|
255
|
+
term.write("\r\n");
|
|
256
|
+
}
|
|
257
|
+
if (value instanceof pyodide.ffi.PyProxy) value.destroy();
|
|
258
|
+
wrapped.destroy();
|
|
259
|
+
} catch (e) {
|
|
260
|
+
const msg = fut.formatted_error || e.message;
|
|
261
|
+
term.write(`\x1b[31m${String(msg).trimEnd()}\x1b[0m`);
|
|
262
|
+
term.write("\r\n");
|
|
263
|
+
} finally {
|
|
264
|
+
fut.destroy();
|
|
265
|
+
}
|
|
266
|
+
addToHistory(line);
|
|
267
|
+
historyIndex = null;
|
|
268
|
+
break;
|
|
269
|
+
default:
|
|
270
|
+
term.write(
|
|
271
|
+
`\r\nUnexpected syntax_check value: ${fut.syntax_check}`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
term.onData(async (data) => {
|
|
277
|
+
switch (data) {
|
|
278
|
+
case "\r": // Enter
|
|
279
|
+
term.write("\r\n");
|
|
280
|
+
await execLine(buffer);
|
|
281
|
+
buffer = "";
|
|
282
|
+
cursorIndex = 0;
|
|
283
|
+
term.write(prompt);
|
|
284
|
+
break;
|
|
285
|
+
case "\u0003": // Ctrl-C
|
|
286
|
+
pyconsole.buffer.clear();
|
|
287
|
+
buffer = "";
|
|
288
|
+
cursorIndex = 0;
|
|
289
|
+
term.write("^C\r\nKeyboardInterrupt\r\n" + ps1);
|
|
290
|
+
prompt = ps1;
|
|
291
|
+
historyIndex = null;
|
|
292
|
+
break;
|
|
293
|
+
case "\u0016": // Ctrl-V
|
|
294
|
+
// paste from clipboard
|
|
295
|
+
const clipboard = await navigator.clipboard.readText();
|
|
296
|
+
const newBuf =
|
|
297
|
+
buffer.slice(0, cursorIndex) +
|
|
298
|
+
clipboard +
|
|
299
|
+
buffer.slice(cursorIndex);
|
|
300
|
+
setBuffer(newBuf, newBuf.length);
|
|
301
|
+
break;
|
|
302
|
+
case "\u007F": // Backspace
|
|
303
|
+
if (cursorIndex > 0) {
|
|
304
|
+
const before = buffer.slice(0, cursorIndex - 1);
|
|
305
|
+
const after = buffer.slice(cursorIndex);
|
|
306
|
+
cursorIndex -= 1;
|
|
307
|
+
setBuffer(before + after, cursorIndex);
|
|
308
|
+
}
|
|
309
|
+
break;
|
|
310
|
+
case "\x1B[A": // Up arrow
|
|
311
|
+
if (prompt === ps1) {
|
|
312
|
+
if (historyIndex === null) historyIndex = history.length;
|
|
313
|
+
if (historyIndex > 0) {
|
|
314
|
+
historyIndex -= 1;
|
|
315
|
+
const newBuf = history[historyIndex] || "";
|
|
316
|
+
setBuffer(newBuf, newBuf.length);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
break;
|
|
320
|
+
case "\x1B[B": // Down arrow
|
|
321
|
+
if (prompt === ps1 && historyIndex !== null) {
|
|
322
|
+
if (historyIndex < history.length - 1) {
|
|
323
|
+
historyIndex += 1;
|
|
324
|
+
const newBuf = history[historyIndex] || "";
|
|
325
|
+
setBuffer(newBuf, newBuf.length);
|
|
326
|
+
} else {
|
|
327
|
+
historyIndex = null;
|
|
328
|
+
setBuffer("", 0);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
break;
|
|
332
|
+
case "\x1B[C": // Right arrow
|
|
333
|
+
if (cursorIndex < buffer.length) {
|
|
334
|
+
cursorIndex += 1;
|
|
335
|
+
refreshLine();
|
|
336
|
+
}
|
|
337
|
+
break;
|
|
338
|
+
case "\x1B[D": // Left arrow
|
|
339
|
+
if (cursorIndex > 0) {
|
|
340
|
+
cursorIndex -= 1;
|
|
341
|
+
refreshLine();
|
|
342
|
+
}
|
|
343
|
+
break;
|
|
344
|
+
default:
|
|
345
|
+
if (data) {
|
|
346
|
+
// Normalize non-breaking spaces to regular spaces
|
|
347
|
+
data = data.replace(/\u00a0/g, " ");
|
|
348
|
+
// Insert arbitrary string at cursor position
|
|
349
|
+
const before = buffer.slice(0, cursorIndex);
|
|
350
|
+
const after = buffer.slice(cursorIndex);
|
|
351
|
+
const newBuf = before + data + after;
|
|
352
|
+
const newCursor = cursorIndex + data.length;
|
|
353
|
+
setBuffer(newBuf, newCursor);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// 4. Extra features
|
|
359
|
+
let idbkvPromise;
|
|
360
|
+
async function getIDBKV() {
|
|
361
|
+
if (!idbkvPromise) {
|
|
362
|
+
idbkvPromise = await import(
|
|
363
|
+
"https://unpkg.com/idb-keyval@5.0.2/dist/esm/index.js"
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
return idbkvPromise;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async function mountDirectory(pyodideDirectory, directoryKey) {
|
|
370
|
+
if (pyodide.FS.analyzePath(pyodideDirectory).exists) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const { get, set } = await getIDBKV();
|
|
374
|
+
const opts = { id: "mountdirid", mode: "readwrite" };
|
|
375
|
+
let directoryHandle = await get(directoryKey);
|
|
376
|
+
if (!directoryHandle) {
|
|
377
|
+
directoryHandle = await showDirectoryPicker(opts);
|
|
378
|
+
await set(directoryKey, directoryHandle);
|
|
379
|
+
}
|
|
380
|
+
const permissionStatus = await directoryHandle.requestPermission(
|
|
381
|
+
opts
|
|
382
|
+
);
|
|
383
|
+
if (permissionStatus !== "granted") {
|
|
384
|
+
throw new Error("readwrite access to directory not granted");
|
|
385
|
+
}
|
|
386
|
+
await pyodide.mountNativeFS(pyodideDirectory, directoryHandle);
|
|
387
|
+
}
|
|
388
|
+
globalThis.mountDirectory = mountDirectory;
|
|
389
|
+
}
|
|
390
|
+
window.console_ready = main();
|
|
391
|
+
</script>
|
|
392
|
+
</body>
|
|
393
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pyodide",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "The Pyodide JavaScript package",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"python",
|
|
@@ -15,15 +15,17 @@
|
|
|
15
15
|
"url": "https://github.com/pyodide/pyodide/issues"
|
|
16
16
|
},
|
|
17
17
|
"license": "MPL-2.0",
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@types/emscripten": "^1.41.4",
|
|
20
|
+
"ws": "^8.5.0"
|
|
21
|
+
},
|
|
18
22
|
"devDependencies": {
|
|
23
|
+
"@biomejs/biome": "2.1.1",
|
|
24
|
+
"@playwright/test": "^1.55.1",
|
|
19
25
|
"@types/assert": "^1.5.6",
|
|
20
|
-
"@types/emscripten": "^1.40.1",
|
|
21
26
|
"@types/expect": "^24.3.0",
|
|
22
|
-
"@types/mocha": "^9.1.0",
|
|
23
27
|
"@types/node": "^20.8.4",
|
|
24
28
|
"@types/ws": "^8.5.3",
|
|
25
|
-
"chai": "^4.3.6",
|
|
26
|
-
"chai-as-promised": "^7.1.1",
|
|
27
29
|
"cross-env": "^7.0.3",
|
|
28
30
|
"dts-bundle-generator": "^8.1.1",
|
|
29
31
|
"esbuild": "^0.25.0",
|
|
@@ -31,10 +33,10 @@
|
|
|
31
33
|
"mocha": "^9.0.2",
|
|
32
34
|
"npm-run-all": "^4.1.5",
|
|
33
35
|
"nyc": "^15.1.0",
|
|
36
|
+
"playwright": "^1.55.1",
|
|
34
37
|
"prettier": "^2.2.1",
|
|
35
|
-
"sinon": "^18.0.0",
|
|
36
|
-
"ts-mocha": "^9.0.2",
|
|
37
38
|
"tsd": "^0.24.1",
|
|
39
|
+
"tsx": "^4.20.5",
|
|
38
40
|
"typedoc": "^0.27.6",
|
|
39
41
|
"typescript": "5.7",
|
|
40
42
|
"wabt": "^1.0.32"
|
|
@@ -67,7 +69,8 @@
|
|
|
67
69
|
"pyodide.d.ts",
|
|
68
70
|
"ffi.d.ts",
|
|
69
71
|
"pyodide-lock.json",
|
|
70
|
-
"console.html"
|
|
72
|
+
"console.html",
|
|
73
|
+
"console-v2.html"
|
|
71
74
|
],
|
|
72
75
|
"browser": {
|
|
73
76
|
"child_process": false,
|
|
@@ -83,25 +86,13 @@
|
|
|
83
86
|
"build-inner": "node esbuild.config.inner.mjs",
|
|
84
87
|
"build": "tsc --noEmit && node esbuild.config.outer.mjs",
|
|
85
88
|
"test": "npm-run-all test:*",
|
|
86
|
-
"test:unit": "cross-env TEST_NODE=1
|
|
87
|
-
"test:node": "cross-env TEST_NODE=1
|
|
88
|
-
"test:browser": "
|
|
89
|
+
"test:unit": "cross-env TEST_NODE=1 node --import tsx --experimental-wasm-stack-switching --test 'test/unit/**/*.test.*'",
|
|
90
|
+
"test:node": "cross-env TEST_NODE=1 npx playwright test",
|
|
91
|
+
"test:browser": "npx playwright test",
|
|
89
92
|
"tsc": "tsc --noEmit",
|
|
90
93
|
"coverage": "cross-env TEST_NODE=1 npm-run-all coverage:*",
|
|
91
94
|
"coverage:build": "nyc npm run test:node"
|
|
92
95
|
},
|
|
93
|
-
"mocha": {
|
|
94
|
-
"bail": false,
|
|
95
|
-
"timeout": 30000,
|
|
96
|
-
"full-trace": true,
|
|
97
|
-
"inline-diffs": true,
|
|
98
|
-
"check-leaks": false,
|
|
99
|
-
"global": [
|
|
100
|
-
"pyodide",
|
|
101
|
-
"page",
|
|
102
|
-
"chai"
|
|
103
|
-
]
|
|
104
|
-
},
|
|
105
96
|
"nyc": {
|
|
106
97
|
"reporter": [
|
|
107
98
|
"html",
|
|
@@ -128,9 +119,6 @@
|
|
|
128
119
|
]
|
|
129
120
|
}
|
|
130
121
|
},
|
|
131
|
-
"dependencies": {
|
|
132
|
-
"ws": "^8.5.0"
|
|
133
|
-
},
|
|
134
122
|
"types": "./pyodide.d.ts",
|
|
135
123
|
"engines": {
|
|
136
124
|
"node": ">=18.0.0"
|