@pyscript/core 0.2.10 → 0.3.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/dev.cjs +31 -0
- package/dist/core.js +4 -3
- package/dist/core.js.map +1 -1
- package/dist/error-96hMSEw8.js +2 -0
- package/dist/error-96hMSEw8.js.map +1 -0
- package/docs/README.md +1 -1
- package/package.json +14 -5
- package/src/core.js +142 -126
- package/src/hooks.js +85 -22
- package/src/plugins/error.js +2 -2
- package/src/plugins-helper.js +26 -0
- package/types/core.d.ts +2 -1
- package/types/hooks.d.ts +37 -12
- package/types/plugins-helper.d.ts +2 -0
- package/dist/error-0_IesYFM.js +0 -2
- package/dist/error-0_IesYFM.js.map +0 -1
@@ -0,0 +1,2 @@
|
|
1
|
+
import{hooks as e}from"./core.js";function o(e){const o=document.createElement("div");o.className="py-error",o.textContent=e,o.style.cssText="\n border: 1px solid red;\n background: #ffdddd;\n color: black;\n font-family: courier, monospace;\n white-space: pre;\n overflow-x: auto;\n padding: 8px;\n margin-top: 8px;\n ",document.body.append(o)}e.main.onReady.add((function n(r){e.main.onReady.delete(n);const{stderr:t}=r.io;r.io.stderr=(e,...n)=>(o(e.message||e),t(e,...n)),addEventListener("error",(({message:e})=>{e.startsWith("Uncaught PythonError")&&o(e)}))}));export{o as notify};
|
2
|
+
//# sourceMappingURL=error-96hMSEw8.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"error-96hMSEw8.js","sources":["../src/plugins/error.js"],"sourcesContent":["// PyScript Error Plugin\nimport { hooks } from \"../core.js\";\n\nhooks.main.onReady.add(function override(pyScript) {\n // be sure this override happens only once\n hooks.main.onReady.delete(override);\n\n // trap generic `stderr` to propagate to it regardless\n const { stderr } = pyScript.io;\n\n // override it with our own logic\n pyScript.io.stderr = (error, ...rest) => {\n notify(error.message || error);\n // let other plugins or stderr hook, if any, do the rest\n return stderr(error, ...rest);\n };\n\n // be sure uncaught Python errors are also visible\n addEventListener(\"error\", ({ message }) => {\n if (message.startsWith(\"Uncaught PythonError\")) notify(message);\n });\n});\n\n// Error hook utilities\n\n// Custom function to show notifications\n\n/**\n * Add a banner to the top of the page, notifying the user of an error\n * @param {string} message\n */\nexport function notify(message) {\n const div = document.createElement(\"div\");\n div.className = \"py-error\";\n div.textContent = message;\n div.style.cssText = `\n border: 1px solid red;\n background: #ffdddd;\n color: black;\n font-family: courier, monospace;\n white-space: pre;\n overflow-x: auto;\n padding: 8px;\n margin-top: 8px;\n `;\n document.body.append(div);\n}\n"],"names":["notify","message","div","document","createElement","className","textContent","style","cssText","body","append","hooks","main","onReady","add","override","pyScript","delete","stderr","io","error","rest","addEventListener","startsWith"],"mappings":"kCA+BO,SAASA,EAAOC,GACnB,MAAMC,EAAMC,SAASC,cAAc,OACnCF,EAAIG,UAAY,WAChBH,EAAII,YAAcL,EAClBC,EAAIK,MAAMC,QAAU,6MAUpBL,SAASM,KAAKC,OAAOR,EACzB,CA3CAS,EAAMC,KAAKC,QAAQC,KAAI,SAASC,EAASC,GAErCL,EAAMC,KAAKC,QAAQI,OAAOF,GAG1B,MAAMG,OAAEA,GAAWF,EAASG,GAG5BH,EAASG,GAAGD,OAAS,CAACE,KAAUC,KAC5BrB,EAAOoB,EAAMnB,SAAWmB,GAEjBF,EAAOE,KAAUC,IAI5BC,iBAAiB,SAAS,EAAGrB,cACrBA,EAAQsB,WAAW,yBAAyBvB,EAAOC,EAAQ,GAEvE"}
|
package/docs/README.md
CHANGED
@@ -159,7 +159,7 @@ The commonly shared utilities are:
|
|
159
159
|
* **display** in both main and worker, refers to the good old `display` utility except:
|
160
160
|
* in the *main* it automatically uses the current script `target` to display content
|
161
161
|
* in the *worker* it still needs to know *where* to display content using the `target="dom-id"` named argument, as workers don't get a default target attached
|
162
|
-
* in both main and worker, the `append=
|
162
|
+
* in both main and worker, the `append=True` is the *default* behavior, which is inherited from the classic PyScript.
|
163
163
|
|
164
164
|
#### Extra main-only features
|
165
165
|
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@pyscript/core",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.3.0",
|
4
4
|
"type": "module",
|
5
5
|
"description": "PyScript",
|
6
6
|
"module": "./index.js",
|
@@ -20,9 +20,16 @@
|
|
20
20
|
},
|
21
21
|
"scripts": {
|
22
22
|
"server": "npx static-handler --coi .",
|
23
|
-
"build": "
|
23
|
+
"build": "npm run build:toml && npm run build:stdlib && npm run build:plugins && npm run build:core && eslint src/ && npm run ts",
|
24
|
+
"build:core": "rm -rf dist && rollup --config rollup/core.config.js",
|
25
|
+
"build:plugins": "node rollup/plugins.cjs",
|
26
|
+
"build:stdlib": "node rollup/stdlib.cjs",
|
27
|
+
"build:toml": "node rollup/toml.cjs",
|
28
|
+
"dev": "node dev.cjs",
|
29
|
+
"release": "npm run build && npm run zip",
|
24
30
|
"size": "echo -e \"\\033[1mdist/*.js file size\\033[0m\"; for js in $(ls dist/*.js); do echo -e \"\\033[2m$js:\\033[0m $(cat $js | brotli | wc -c) bytes\"; done",
|
25
|
-
"ts": "tsc -p ."
|
31
|
+
"ts": "tsc -p .",
|
32
|
+
"zip": "zip -r dist.zip ./dist"
|
26
33
|
},
|
27
34
|
"keywords": [
|
28
35
|
"pyscript",
|
@@ -33,15 +40,17 @@
|
|
33
40
|
"dependencies": {
|
34
41
|
"@ungap/with-resolvers": "^0.1.0",
|
35
42
|
"basic-devtools": "^0.1.6",
|
36
|
-
"polyscript": "^0.
|
43
|
+
"polyscript": "^0.5.1",
|
37
44
|
"sticky-module": "^0.1.0",
|
45
|
+
"to-json-callback": "^0.1.1",
|
38
46
|
"type-checked-collections": "^0.1.7"
|
39
47
|
},
|
40
48
|
"devDependencies": {
|
41
49
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
42
50
|
"@rollup/plugin-terser": "^0.4.4",
|
43
51
|
"@webreflection/toml-j0.4": "^1.1.3",
|
44
|
-
"
|
52
|
+
"chokidar": "^3.5.3",
|
53
|
+
"eslint": "^8.52.0",
|
45
54
|
"rollup": "^4.1.4",
|
46
55
|
"rollup-plugin-postcss": "^4.0.2",
|
47
56
|
"rollup-plugin-string": "^3.0.0",
|
package/src/core.js
CHANGED
@@ -14,16 +14,18 @@ import {
|
|
14
14
|
dispatch,
|
15
15
|
queryTarget,
|
16
16
|
unescape,
|
17
|
+
whenDefined,
|
17
18
|
} from "polyscript/exports";
|
18
19
|
|
19
20
|
import "./all-done.js";
|
20
21
|
import TYPES from "./types.js";
|
21
22
|
import configs from "./config.js";
|
22
|
-
import hooks from "./hooks.js";
|
23
23
|
import sync from "./sync.js";
|
24
24
|
import stdlib from "./stdlib.js";
|
25
|
+
import bootstrapNodeAndPlugins from "./plugins-helper.js";
|
25
26
|
import { ErrorCode } from "./exceptions.js";
|
26
27
|
import { robustFetch as fetch, getText } from "./fetch.js";
|
28
|
+
import { hooks, main, worker, codeFor, createFunction } from "./hooks.js";
|
27
29
|
|
28
30
|
// allows lazy element features on code evaluation
|
29
31
|
let currentElement;
|
@@ -31,25 +33,6 @@ let currentElement;
|
|
31
33
|
// generic helper to disambiguate between custom element and script
|
32
34
|
const isScript = ({ tagName }) => tagName === "SCRIPT";
|
33
35
|
|
34
|
-
// helper for all script[type="py"] out there
|
35
|
-
const before = (script) => {
|
36
|
-
defineProperty(document, "currentScript", {
|
37
|
-
configurable: true,
|
38
|
-
get: () => script,
|
39
|
-
});
|
40
|
-
};
|
41
|
-
|
42
|
-
const after = () => {
|
43
|
-
delete document.currentScript;
|
44
|
-
};
|
45
|
-
|
46
|
-
// common life-cycle handlers for any node
|
47
|
-
const bootstrapNodeAndPlugins = (wrap, element, callback, hook) => {
|
48
|
-
// make it possible to reach the current target node via Python
|
49
|
-
callback(element);
|
50
|
-
for (const fn of hooks[hook]) fn(wrap, element);
|
51
|
-
};
|
52
|
-
|
53
36
|
let shouldRegister = true;
|
54
37
|
const registerModule = ({ XWorker: $XWorker, interpreter, io }) => {
|
55
38
|
// automatically use the pyscript stderr (when/if defined)
|
@@ -73,33 +56,31 @@ const registerModule = ({ XWorker: $XWorker, interpreter, io }) => {
|
|
73
56
|
interpreter.runPython(stdlib, { globals: interpreter.runPython("{}") });
|
74
57
|
};
|
75
58
|
|
76
|
-
const workerHooks = {
|
77
|
-
codeBeforeRunWorker: () =>
|
78
|
-
[stdlib, ...hooks.codeBeforeRunWorker].map(dedent).join("\n"),
|
79
|
-
codeBeforeRunWorkerAsync: () =>
|
80
|
-
[stdlib, ...hooks.codeBeforeRunWorkerAsync].map(dedent).join("\n"),
|
81
|
-
codeAfterRunWorker: () =>
|
82
|
-
[...hooks.codeAfterRunWorker].map(dedent).join("\n"),
|
83
|
-
codeAfterRunWorkerAsync: () =>
|
84
|
-
[...hooks.codeAfterRunWorkerAsync].map(dedent).join("\n"),
|
85
|
-
};
|
86
|
-
|
87
59
|
// avoid multiple initialization of the same library
|
88
60
|
const [
|
89
61
|
{
|
90
62
|
PyWorker: exportedPyWorker,
|
91
63
|
hooks: exportedHooks,
|
92
64
|
config: exportedConfig,
|
65
|
+
whenDefined: exportedWhenDefined,
|
93
66
|
},
|
94
67
|
alreadyLive,
|
95
|
-
] = stickyModule("@pyscript/core", {
|
68
|
+
] = stickyModule("@pyscript/core", {
|
69
|
+
PyWorker,
|
70
|
+
hooks,
|
71
|
+
config: {},
|
72
|
+
whenDefined,
|
73
|
+
});
|
96
74
|
|
97
75
|
export {
|
98
76
|
exportedPyWorker as PyWorker,
|
99
77
|
exportedHooks as hooks,
|
100
78
|
exportedConfig as config,
|
79
|
+
exportedWhenDefined as whenDefined,
|
101
80
|
};
|
102
81
|
|
82
|
+
const hooked = new Map();
|
83
|
+
|
103
84
|
for (const [TYPE, interpreter] of TYPES) {
|
104
85
|
// avoid any dance if the module already landed
|
105
86
|
if (alreadyLive) break;
|
@@ -149,107 +130,141 @@ for (const [TYPE, interpreter] of TYPES) {
|
|
149
130
|
// possible early errors sent by polyscript
|
150
131
|
const errors = new Map();
|
151
132
|
|
133
|
+
// specific main and worker hooks
|
134
|
+
const hooks = {
|
135
|
+
main: {
|
136
|
+
...codeFor(main),
|
137
|
+
async onReady(wrap, element) {
|
138
|
+
if (shouldRegister) {
|
139
|
+
shouldRegister = false;
|
140
|
+
registerModule(wrap);
|
141
|
+
}
|
142
|
+
|
143
|
+
// allows plugins to do whatever they want with the element
|
144
|
+
// before regular stuff happens in here
|
145
|
+
for (const callback of main("onReady"))
|
146
|
+
await callback(wrap, element);
|
147
|
+
|
148
|
+
// now that all possible plugins are configured,
|
149
|
+
// bail out if polyscript encountered an error
|
150
|
+
if (errors.has(element)) {
|
151
|
+
let { message } = errors.get(element);
|
152
|
+
errors.delete(element);
|
153
|
+
const clone = message === INVALID_CONTENT;
|
154
|
+
message = `(${ErrorCode.CONFLICTING_CODE}) ${message} for `;
|
155
|
+
message += element.cloneNode(clone).outerHTML;
|
156
|
+
wrap.io.stderr(message);
|
157
|
+
return;
|
158
|
+
}
|
159
|
+
|
160
|
+
if (isScript(element)) {
|
161
|
+
const {
|
162
|
+
attributes: { async: isAsync, target },
|
163
|
+
} = element;
|
164
|
+
const hasTarget = !!target?.value;
|
165
|
+
const show = hasTarget
|
166
|
+
? queryTarget(element, target.value)
|
167
|
+
: document.createElement("script-py");
|
168
|
+
|
169
|
+
if (!hasTarget) {
|
170
|
+
const { head, body } = document;
|
171
|
+
if (head.contains(element)) body.append(show);
|
172
|
+
else element.after(show);
|
173
|
+
}
|
174
|
+
if (!show.id) show.id = getID();
|
175
|
+
|
176
|
+
// allows the code to retrieve the target element via
|
177
|
+
// document.currentScript.target if needed
|
178
|
+
defineProperty(element, "target", { value: show });
|
179
|
+
|
180
|
+
// notify before the code runs
|
181
|
+
dispatch(element, TYPE, "ready");
|
182
|
+
dispatchDone(
|
183
|
+
element,
|
184
|
+
isAsync,
|
185
|
+
wrap[`run${isAsync ? "Async" : ""}`](
|
186
|
+
await fetchSource(element, wrap.io, true),
|
187
|
+
),
|
188
|
+
);
|
189
|
+
} else {
|
190
|
+
// resolve PyScriptElement to allow connectedCallback
|
191
|
+
element._wrap.resolve(wrap);
|
192
|
+
}
|
193
|
+
console.debug("[pyscript/main] PyScript Ready");
|
194
|
+
},
|
195
|
+
onWorker(_, xworker) {
|
196
|
+
assign(xworker.sync, sync);
|
197
|
+
for (const callback of main("onWorker"))
|
198
|
+
callback(_, xworker);
|
199
|
+
},
|
200
|
+
onBeforeRun(wrap, element) {
|
201
|
+
currentElement = element;
|
202
|
+
bootstrapNodeAndPlugins(
|
203
|
+
main,
|
204
|
+
wrap,
|
205
|
+
element,
|
206
|
+
"onBeforeRun",
|
207
|
+
);
|
208
|
+
},
|
209
|
+
onBeforeRunAsync(wrap, element) {
|
210
|
+
currentElement = element;
|
211
|
+
return bootstrapNodeAndPlugins(
|
212
|
+
main,
|
213
|
+
wrap,
|
214
|
+
element,
|
215
|
+
"onBeforeRunAsync",
|
216
|
+
);
|
217
|
+
},
|
218
|
+
onAfterRun(wrap, element) {
|
219
|
+
bootstrapNodeAndPlugins(
|
220
|
+
main,
|
221
|
+
wrap,
|
222
|
+
element,
|
223
|
+
"onAfterRun",
|
224
|
+
);
|
225
|
+
},
|
226
|
+
onAfterRunAsync(wrap, element) {
|
227
|
+
return bootstrapNodeAndPlugins(
|
228
|
+
main,
|
229
|
+
wrap,
|
230
|
+
element,
|
231
|
+
"onAfterRunAsync",
|
232
|
+
);
|
233
|
+
},
|
234
|
+
},
|
235
|
+
worker: {
|
236
|
+
...codeFor(worker),
|
237
|
+
// these are lazy getters that returns a composition
|
238
|
+
// of the current hooks or undefined, if no hook is present
|
239
|
+
get onReady() {
|
240
|
+
return createFunction(this, "onReady", true);
|
241
|
+
},
|
242
|
+
get onBeforeRun() {
|
243
|
+
return createFunction(this, "onBeforeRun", false);
|
244
|
+
},
|
245
|
+
get onBeforeRunAsync() {
|
246
|
+
return createFunction(this, "onBeforeRunAsync", true);
|
247
|
+
},
|
248
|
+
get onAfterRun() {
|
249
|
+
return createFunction(this, "onAfterRun", false);
|
250
|
+
},
|
251
|
+
get onAfterRunAsync() {
|
252
|
+
return createFunction(this, "onAfterRunAsync", true);
|
253
|
+
},
|
254
|
+
},
|
255
|
+
};
|
256
|
+
|
257
|
+
hooked.set(TYPE, hooks);
|
258
|
+
|
152
259
|
define(TYPE, {
|
153
260
|
config,
|
154
261
|
interpreter,
|
262
|
+
hooks,
|
155
263
|
env: `${TYPE}-script`,
|
156
264
|
version: config?.interpreter,
|
157
265
|
onerror(error, element) {
|
158
266
|
errors.set(element, error);
|
159
267
|
},
|
160
|
-
...workerHooks,
|
161
|
-
onWorkerReady(_, xworker) {
|
162
|
-
assign(xworker.sync, sync);
|
163
|
-
for (const callback of hooks.onWorkerReady)
|
164
|
-
callback(_, xworker);
|
165
|
-
},
|
166
|
-
onBeforeRun(wrap, element) {
|
167
|
-
currentElement = element;
|
168
|
-
bootstrapNodeAndPlugins(
|
169
|
-
wrap,
|
170
|
-
element,
|
171
|
-
before,
|
172
|
-
"onBeforeRun",
|
173
|
-
);
|
174
|
-
},
|
175
|
-
onBeforeRunAsync(wrap, element) {
|
176
|
-
currentElement = element;
|
177
|
-
bootstrapNodeAndPlugins(
|
178
|
-
wrap,
|
179
|
-
element,
|
180
|
-
before,
|
181
|
-
"onBeforeRunAsync",
|
182
|
-
);
|
183
|
-
},
|
184
|
-
onAfterRun(wrap, element) {
|
185
|
-
bootstrapNodeAndPlugins(wrap, element, after, "onAfterRun");
|
186
|
-
},
|
187
|
-
onAfterRunAsync(wrap, element) {
|
188
|
-
bootstrapNodeAndPlugins(
|
189
|
-
wrap,
|
190
|
-
element,
|
191
|
-
after,
|
192
|
-
"onAfterRunAsync",
|
193
|
-
);
|
194
|
-
},
|
195
|
-
async onInterpreterReady(wrap, element) {
|
196
|
-
if (shouldRegister) {
|
197
|
-
shouldRegister = false;
|
198
|
-
registerModule(wrap);
|
199
|
-
}
|
200
|
-
|
201
|
-
// allows plugins to do whatever they want with the element
|
202
|
-
// before regular stuff happens in here
|
203
|
-
for (const callback of hooks.onInterpreterReady)
|
204
|
-
callback(wrap, element);
|
205
|
-
|
206
|
-
// now that all possible plugins are configured,
|
207
|
-
// bail out if polyscript encountered an error
|
208
|
-
if (errors.has(element)) {
|
209
|
-
let { message } = errors.get(element);
|
210
|
-
errors.delete(element);
|
211
|
-
const clone = message === INVALID_CONTENT;
|
212
|
-
message = `(${ErrorCode.CONFLICTING_CODE}) ${message} for `;
|
213
|
-
message += element.cloneNode(clone).outerHTML;
|
214
|
-
wrap.io.stderr(message);
|
215
|
-
return;
|
216
|
-
}
|
217
|
-
|
218
|
-
if (isScript(element)) {
|
219
|
-
const {
|
220
|
-
attributes: { async: isAsync, target },
|
221
|
-
} = element;
|
222
|
-
const hasTarget = !!target?.value;
|
223
|
-
const show = hasTarget
|
224
|
-
? queryTarget(element, target.value)
|
225
|
-
: document.createElement("script-py");
|
226
|
-
|
227
|
-
if (!hasTarget) {
|
228
|
-
const { head, body } = document;
|
229
|
-
if (head.contains(element)) body.append(show);
|
230
|
-
else element.after(show);
|
231
|
-
}
|
232
|
-
if (!show.id) show.id = getID();
|
233
|
-
|
234
|
-
// allows the code to retrieve the target element via
|
235
|
-
// document.currentScript.target if needed
|
236
|
-
defineProperty(element, "target", { value: show });
|
237
|
-
|
238
|
-
// notify before the code runs
|
239
|
-
dispatch(element, TYPE, "ready");
|
240
|
-
dispatchDone(
|
241
|
-
element,
|
242
|
-
isAsync,
|
243
|
-
wrap[`run${isAsync ? "Async" : ""}`](
|
244
|
-
await fetchSource(element, wrap.io, true),
|
245
|
-
),
|
246
|
-
);
|
247
|
-
} else {
|
248
|
-
// resolve PyScriptElement to allow connectedCallback
|
249
|
-
element._wrap.resolve(wrap);
|
250
|
-
}
|
251
|
-
console.debug("[pyscript/main] PyScript Ready");
|
252
|
-
},
|
253
268
|
});
|
254
269
|
|
255
270
|
customElements.define(
|
@@ -305,12 +320,13 @@ for (const [TYPE, interpreter] of TYPES) {
|
|
305
320
|
* @returns {Worker & {sync: ProxyHandler<object>}}
|
306
321
|
*/
|
307
322
|
function PyWorker(file, options) {
|
323
|
+
const hooks = hooked.get("py");
|
308
324
|
// this propagates pyscript worker hooks without needing a pyscript
|
309
325
|
// bootstrap + it passes arguments and enforces `pyodide`
|
310
326
|
// as the interpreter to use in the worker, as all hooks assume that
|
311
327
|
// and as `pyodide` is the only default interpreter that can deal with
|
312
328
|
// all the features we need to deliver pyscript out there.
|
313
|
-
const xworker = XWorker.call(new Hook(null,
|
329
|
+
const xworker = XWorker.call(new Hook(null, hooks), file, {
|
314
330
|
type: "pyodide",
|
315
331
|
...options,
|
316
332
|
});
|
package/src/hooks.js
CHANGED
@@ -1,28 +1,91 @@
|
|
1
1
|
import { typedSet } from "type-checked-collections";
|
2
|
+
import { dedent } from "polyscript/exports";
|
3
|
+
import toJSONCallback from "to-json-callback";
|
4
|
+
|
5
|
+
import stdlib from "./stdlib.js";
|
6
|
+
|
7
|
+
export const main = (name) => hooks.main[name];
|
8
|
+
export const worker = (name) => hooks.worker[name];
|
9
|
+
|
10
|
+
const code = (hooks, branch, key, lib) => {
|
11
|
+
hooks[key] = () => {
|
12
|
+
const arr = lib ? [lib] : [];
|
13
|
+
arr.push(...branch(key));
|
14
|
+
return arr.map(dedent).join("\n");
|
15
|
+
};
|
16
|
+
};
|
17
|
+
|
18
|
+
export const codeFor = (branch) => {
|
19
|
+
const hooks = {};
|
20
|
+
code(hooks, branch, `codeBeforeRun`, stdlib);
|
21
|
+
code(hooks, branch, `codeBeforeRunAsync`, stdlib);
|
22
|
+
code(hooks, branch, `codeAfterRun`);
|
23
|
+
code(hooks, branch, `codeAfterRunAsync`);
|
24
|
+
return hooks;
|
25
|
+
};
|
26
|
+
|
27
|
+
export const createFunction = (self, name) => {
|
28
|
+
const cbs = [...worker(name)];
|
29
|
+
if (cbs.length) {
|
30
|
+
const cb = toJSONCallback(
|
31
|
+
self[`_${name}`] ||
|
32
|
+
(name.endsWith("Async")
|
33
|
+
? async (wrap, xworker, ...cbs) => {
|
34
|
+
for (const cb of cbs) await cb(wrap, xworker);
|
35
|
+
}
|
36
|
+
: (wrap, xworker, ...cbs) => {
|
37
|
+
for (const cb of cbs) cb(wrap, xworker);
|
38
|
+
}),
|
39
|
+
);
|
40
|
+
const a = cbs.map(toJSONCallback).join(", ");
|
41
|
+
return Function(`return(w,x)=>(${cb})(w,x,...[${a}])`)();
|
42
|
+
}
|
43
|
+
};
|
2
44
|
|
3
45
|
const SetFunction = typedSet({ typeof: "function" });
|
4
46
|
const SetString = typedSet({ typeof: "string" });
|
5
47
|
|
6
|
-
export
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
48
|
+
export const hooks = {
|
49
|
+
main: {
|
50
|
+
/** @type {Set<function>} */
|
51
|
+
onWorker: new SetFunction(),
|
52
|
+
/** @type {Set<function>} */
|
53
|
+
onReady: new SetFunction(),
|
54
|
+
/** @type {Set<function>} */
|
55
|
+
onBeforeRun: new SetFunction(),
|
56
|
+
/** @type {Set<function>} */
|
57
|
+
onBeforeRunAsync: new SetFunction(),
|
58
|
+
/** @type {Set<function>} */
|
59
|
+
onAfterRun: new SetFunction(),
|
60
|
+
/** @type {Set<function>} */
|
61
|
+
onAfterRunAsync: new SetFunction(),
|
62
|
+
/** @type {Set<string>} */
|
63
|
+
codeBeforeRun: new SetString(),
|
64
|
+
/** @type {Set<string>} */
|
65
|
+
codeBeforeRunAsync: new SetString(),
|
66
|
+
/** @type {Set<string>} */
|
67
|
+
codeAfterRun: new SetString(),
|
68
|
+
/** @type {Set<string>} */
|
69
|
+
codeAfterRunAsync: new SetString(),
|
70
|
+
},
|
71
|
+
worker: {
|
72
|
+
/** @type {Set<function>} */
|
73
|
+
onReady: new SetFunction(),
|
74
|
+
/** @type {Set<function>} */
|
75
|
+
onBeforeRun: new SetFunction(),
|
76
|
+
/** @type {Set<function>} */
|
77
|
+
onBeforeRunAsync: new SetFunction(),
|
78
|
+
/** @type {Set<function>} */
|
79
|
+
onAfterRun: new SetFunction(),
|
80
|
+
/** @type {Set<function>} */
|
81
|
+
onAfterRunAsync: new SetFunction(),
|
82
|
+
/** @type {Set<string>} */
|
83
|
+
codeBeforeRun: new SetString(),
|
84
|
+
/** @type {Set<string>} */
|
85
|
+
codeBeforeRunAsync: new SetString(),
|
86
|
+
/** @type {Set<string>} */
|
87
|
+
codeAfterRun: new SetString(),
|
88
|
+
/** @type {Set<string>} */
|
89
|
+
codeAfterRunAsync: new SetString(),
|
90
|
+
},
|
28
91
|
};
|
package/src/plugins/error.js
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
// PyScript Error Plugin
|
2
2
|
import { hooks } from "../core.js";
|
3
3
|
|
4
|
-
hooks.
|
4
|
+
hooks.main.onReady.add(function override(pyScript) {
|
5
5
|
// be sure this override happens only once
|
6
|
-
hooks.
|
6
|
+
hooks.main.onReady.delete(override);
|
7
7
|
|
8
8
|
// trap generic `stderr` to propagate to it regardless
|
9
9
|
const { stderr } = pyScript.io;
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { defineProperty } from "polyscript/exports";
|
2
|
+
|
3
|
+
// helper for all script[type="py"] out there
|
4
|
+
const before = (script) => {
|
5
|
+
defineProperty(document, "currentScript", {
|
6
|
+
configurable: true,
|
7
|
+
get: () => script,
|
8
|
+
});
|
9
|
+
};
|
10
|
+
|
11
|
+
const after = () => {
|
12
|
+
delete document.currentScript;
|
13
|
+
};
|
14
|
+
|
15
|
+
// common life-cycle handlers for any node
|
16
|
+
export default async (main, wrap, element, hook) => {
|
17
|
+
const isAsync = hook.endsWith("Async");
|
18
|
+
const isBefore = hook.startsWith("onBefore");
|
19
|
+
// make it possible to reach the current target node via Python
|
20
|
+
// or clean up for other scripts executing around this one
|
21
|
+
(isBefore ? before : after)(element);
|
22
|
+
for (const fn of main(hook)) {
|
23
|
+
if (isAsync) await fn(wrap, element);
|
24
|
+
else fn(wrap, element);
|
25
|
+
}
|
26
|
+
};
|
package/types/core.d.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
declare const exportedPyWorker: any;
|
2
2
|
declare const exportedHooks: any;
|
3
3
|
declare const exportedConfig: any;
|
4
|
-
|
4
|
+
declare const exportedWhenDefined: any;
|
5
|
+
export { exportedPyWorker as PyWorker, exportedHooks as hooks, exportedConfig as config, exportedWhenDefined as whenDefined };
|
package/types/hooks.d.ts
CHANGED
@@ -1,13 +1,38 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
export function main(name: any): any;
|
2
|
+
export function worker(name: any): any;
|
3
|
+
export function codeFor(branch: any): {};
|
4
|
+
export function createFunction(self: any, name: any): any;
|
5
|
+
export namespace hooks {
|
6
|
+
namespace main {
|
7
|
+
let onWorker: Set<Function>;
|
8
|
+
let onReady: Set<Function>;
|
9
|
+
let onBeforeRun: Set<Function>;
|
10
|
+
let onBeforeRunAsync: Set<Function>;
|
11
|
+
let onAfterRun: Set<Function>;
|
12
|
+
let onAfterRunAsync: Set<Function>;
|
13
|
+
let codeBeforeRun: Set<string>;
|
14
|
+
let codeBeforeRunAsync: Set<string>;
|
15
|
+
let codeAfterRun: Set<string>;
|
16
|
+
let codeAfterRunAsync: Set<string>;
|
17
|
+
}
|
18
|
+
namespace worker {
|
19
|
+
let onReady_1: Set<Function>;
|
20
|
+
export { onReady_1 as onReady };
|
21
|
+
let onBeforeRun_1: Set<Function>;
|
22
|
+
export { onBeforeRun_1 as onBeforeRun };
|
23
|
+
let onBeforeRunAsync_1: Set<Function>;
|
24
|
+
export { onBeforeRunAsync_1 as onBeforeRunAsync };
|
25
|
+
let onAfterRun_1: Set<Function>;
|
26
|
+
export { onAfterRun_1 as onAfterRun };
|
27
|
+
let onAfterRunAsync_1: Set<Function>;
|
28
|
+
export { onAfterRunAsync_1 as onAfterRunAsync };
|
29
|
+
let codeBeforeRun_1: Set<string>;
|
30
|
+
export { codeBeforeRun_1 as codeBeforeRun };
|
31
|
+
let codeBeforeRunAsync_1: Set<string>;
|
32
|
+
export { codeBeforeRunAsync_1 as codeBeforeRunAsync };
|
33
|
+
let codeAfterRun_1: Set<string>;
|
34
|
+
export { codeAfterRun_1 as codeAfterRun };
|
35
|
+
let codeAfterRunAsync_1: Set<string>;
|
36
|
+
export { codeAfterRunAsync_1 as codeAfterRunAsync };
|
37
|
+
}
|
12
38
|
}
|
13
|
-
export default _default;
|
package/dist/error-0_IesYFM.js
DELETED
@@ -1,2 +0,0 @@
|
|
1
|
-
import{hooks as e}from"./core.js";function r(e){const r=document.createElement("div");r.className="py-error",r.textContent=e,r.style.cssText="\n border: 1px solid red;\n background: #ffdddd;\n color: black;\n font-family: courier, monospace;\n white-space: pre;\n overflow-x: auto;\n padding: 8px;\n margin-top: 8px;\n ",document.body.append(r)}e.onInterpreterReady.add((function o(n){e.onInterpreterReady.delete(o);const{stderr:t}=n.io;n.io.stderr=(e,...o)=>(r(e.message||e),t(e,...o)),addEventListener("error",(({message:e})=>{e.startsWith("Uncaught PythonError")&&r(e)}))}));export{r as notify};
|
2
|
-
//# sourceMappingURL=error-0_IesYFM.js.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"error-0_IesYFM.js","sources":["../src/plugins/error.js"],"sourcesContent":["// PyScript Error Plugin\nimport { hooks } from \"../core.js\";\n\nhooks.onInterpreterReady.add(function override(pyScript) {\n // be sure this override happens only once\n hooks.onInterpreterReady.delete(override);\n\n // trap generic `stderr` to propagate to it regardless\n const { stderr } = pyScript.io;\n\n // override it with our own logic\n pyScript.io.stderr = (error, ...rest) => {\n notify(error.message || error);\n // let other plugins or stderr hook, if any, do the rest\n return stderr(error, ...rest);\n };\n\n // be sure uncaught Python errors are also visible\n addEventListener(\"error\", ({ message }) => {\n if (message.startsWith(\"Uncaught PythonError\")) notify(message);\n });\n});\n\n// Error hook utilities\n\n// Custom function to show notifications\n\n/**\n * Add a banner to the top of the page, notifying the user of an error\n * @param {string} message\n */\nexport function notify(message) {\n const div = document.createElement(\"div\");\n div.className = \"py-error\";\n div.textContent = message;\n div.style.cssText = `\n border: 1px solid red;\n background: #ffdddd;\n color: black;\n font-family: courier, monospace;\n white-space: pre;\n overflow-x: auto;\n padding: 8px;\n margin-top: 8px;\n `;\n document.body.append(div);\n}\n"],"names":["notify","message","div","document","createElement","className","textContent","style","cssText","body","append","hooks","onInterpreterReady","add","override","pyScript","delete","stderr","io","error","rest","addEventListener","startsWith"],"mappings":"kCA+BO,SAASA,EAAOC,GACnB,MAAMC,EAAMC,SAASC,cAAc,OACnCF,EAAIG,UAAY,WAChBH,EAAII,YAAcL,EAClBC,EAAIK,MAAMC,QAAU,6MAUpBL,SAASM,KAAKC,OAAOR,EACzB,CA3CAS,EAAMC,mBAAmBC,KAAI,SAASC,EAASC,GAE3CJ,EAAMC,mBAAmBI,OAAOF,GAGhC,MAAMG,OAAEA,GAAWF,EAASG,GAG5BH,EAASG,GAAGD,OAAS,CAACE,KAAUC,KAC5BpB,EAAOmB,EAAMlB,SAAWkB,GAEjBF,EAAOE,KAAUC,IAI5BC,iBAAiB,SAAS,EAAGpB,cACrBA,EAAQqB,WAAW,yBAAyBtB,EAAOC,EAAQ,GAEvE"}
|