@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.
@@ -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=False` is the *default* behavior, which is a breaking change compared to classic PyScript, but because there is no `Element` with its `write(content)` utility, which would have used that `append=False` behind the scene, we've decided that `false` as append default is more desired, specially after porting most examples in *PyScript Next*, where `append=True` is the exception, not the norm.
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.2.10",
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": "node rollup/toml.cjs && node rollup/stdlib.cjs && node rollup/plugins.cjs && rm -rf dist && rollup --config rollup/core.config.js && eslint src/ && npm run ts",
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.4.20",
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
- "eslint": "^8.51.0",
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", { PyWorker, hooks, config: {} });
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, workerHooks), file, {
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 default {
7
- /** @type {Set<function>} */
8
- onInterpreterReady: new SetFunction(),
9
- /** @type {Set<function>} */
10
- onBeforeRun: new SetFunction(),
11
- /** @type {Set<function>} */
12
- onBeforeRunAsync: new SetFunction(),
13
- /** @type {Set<function>} */
14
- onAfterRun: new SetFunction(),
15
- /** @type {Set<function>} */
16
- onAfterRunAsync: new SetFunction(),
17
-
18
- /** @type {Set<function>} */
19
- onWorkerReady: new SetFunction(),
20
- /** @type {Set<string>} */
21
- codeBeforeRunWorker: new SetString(),
22
- /** @type {Set<string>} */
23
- codeBeforeRunWorkerAsync: new SetString(),
24
- /** @type {Set<string>} */
25
- codeAfterRunWorker: new SetString(),
26
- /** @type {Set<string>} */
27
- codeAfterRunWorkerAsync: new SetString(),
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
  };
@@ -1,9 +1,9 @@
1
1
  // PyScript Error Plugin
2
2
  import { hooks } from "../core.js";
3
3
 
4
- hooks.onInterpreterReady.add(function override(pyScript) {
4
+ hooks.main.onReady.add(function override(pyScript) {
5
5
  // be sure this override happens only once
6
- hooks.onInterpreterReady.delete(override);
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
- export { exportedPyWorker as PyWorker, exportedHooks as hooks, exportedConfig as config };
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
- declare namespace _default {
2
- let onInterpreterReady: Set<Function>;
3
- let onBeforeRun: Set<Function>;
4
- let onBeforeRunAsync: Set<Function>;
5
- let onAfterRun: Set<Function>;
6
- let onAfterRunAsync: Set<Function>;
7
- let onWorkerReady: Set<Function>;
8
- let codeBeforeRunWorker: Set<string>;
9
- let codeBeforeRunWorkerAsync: Set<string>;
10
- let codeAfterRunWorker: Set<string>;
11
- let codeAfterRunWorkerAsync: Set<string>;
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;
@@ -0,0 +1,2 @@
1
+ declare function _default(main: any, wrap: any, element: any, hook: any): Promise<void>;
2
+ export default _default;
@@ -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"}