@pyscript/core 0.2.9 → 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=Flase` 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.9",
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,14 +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",
44
+ "sticky-module": "^0.1.0",
45
+ "to-json-callback": "^0.1.1",
37
46
  "type-checked-collections": "^0.1.7"
38
47
  },
39
48
  "devDependencies": {
40
49
  "@rollup/plugin-node-resolve": "^15.2.3",
41
50
  "@rollup/plugin-terser": "^0.4.4",
42
51
  "@webreflection/toml-j0.4": "^1.1.3",
43
- "eslint": "^8.51.0",
52
+ "chokidar": "^3.5.3",
53
+ "eslint": "^8.52.0",
44
54
  "rollup": "^4.1.4",
45
55
  "rollup-plugin-postcss": "^4.0.2",
46
56
  "rollup-plugin-string": "^3.0.0",
package/src/core.js CHANGED
@@ -1,31 +1,31 @@
1
1
  /*! (c) PyScript Development Team */
2
2
 
3
+ import stickyModule from "sticky-module";
3
4
  import "@ungap/with-resolvers";
4
5
 
5
- // These imports can hook more than usual and help debugging possible polyscript issues
6
6
  import {
7
7
  INVALID_CONTENT,
8
- define,
8
+ Hook,
9
9
  XWorker,
10
- } from "../node_modules/polyscript/esm/index.js";
11
- import { queryTarget } from "../node_modules/polyscript/esm/script-handler.js";
12
- import {
10
+ assign,
13
11
  dedent,
12
+ define,
13
+ defineProperty,
14
14
  dispatch,
15
+ queryTarget,
15
16
  unescape,
16
- } from "../node_modules/polyscript/esm/utils.js";
17
- import { Hook } from "../node_modules/polyscript/esm/worker/hooks.js";
17
+ whenDefined,
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";
27
-
28
- const { assign, defineProperty } = Object;
28
+ import { hooks, main, worker, codeFor, createFunction } from "./hooks.js";
29
29
 
30
30
  // allows lazy element features on code evaluation
31
31
  let currentElement;
@@ -33,25 +33,6 @@ let currentElement;
33
33
  // generic helper to disambiguate between custom element and script
34
34
  const isScript = ({ tagName }) => tagName === "SCRIPT";
35
35
 
36
- // helper for all script[type="py"] out there
37
- const before = (script) => {
38
- defineProperty(document, "currentScript", {
39
- configurable: true,
40
- get: () => script,
41
- });
42
- };
43
-
44
- const after = () => {
45
- delete document.currentScript;
46
- };
47
-
48
- // common life-cycle handlers for any node
49
- const bootstrapNodeAndPlugins = (wrap, element, callback, hook) => {
50
- // make it possible to reach the current target node via Python
51
- callback(element);
52
- for (const fn of hooks[hook]) fn(wrap, element);
53
- };
54
-
55
36
  let shouldRegister = true;
56
37
  const registerModule = ({ XWorker: $XWorker, interpreter, io }) => {
57
38
  // automatically use the pyscript stderr (when/if defined)
@@ -75,21 +56,35 @@ const registerModule = ({ XWorker: $XWorker, interpreter, io }) => {
75
56
  interpreter.runPython(stdlib, { globals: interpreter.runPython("{}") });
76
57
  };
77
58
 
78
- const workerHooks = {
79
- codeBeforeRunWorker: () =>
80
- [stdlib, ...hooks.codeBeforeRunWorker].map(dedent).join("\n"),
81
- codeBeforeRunWorkerAsync: () =>
82
- [stdlib, ...hooks.codeBeforeRunWorkerAsync].map(dedent).join("\n"),
83
- codeAfterRunWorker: () =>
84
- [...hooks.codeAfterRunWorker].map(dedent).join("\n"),
85
- codeAfterRunWorkerAsync: () =>
86
- [...hooks.codeAfterRunWorkerAsync].map(dedent).join("\n"),
59
+ // avoid multiple initialization of the same library
60
+ const [
61
+ {
62
+ PyWorker: exportedPyWorker,
63
+ hooks: exportedHooks,
64
+ config: exportedConfig,
65
+ whenDefined: exportedWhenDefined,
66
+ },
67
+ alreadyLive,
68
+ ] = stickyModule("@pyscript/core", {
69
+ PyWorker,
70
+ hooks,
71
+ config: {},
72
+ whenDefined,
73
+ });
74
+
75
+ export {
76
+ exportedPyWorker as PyWorker,
77
+ exportedHooks as hooks,
78
+ exportedConfig as config,
79
+ exportedWhenDefined as whenDefined,
87
80
  };
88
81
 
89
- const exportedConfig = {};
90
- export { exportedConfig as config, hooks };
82
+ const hooked = new Map();
91
83
 
92
84
  for (const [TYPE, interpreter] of TYPES) {
85
+ // avoid any dance if the module already landed
86
+ if (alreadyLive) break;
87
+
93
88
  const dispatchDone = (element, isAsync, result) => {
94
89
  if (isAsync) result.then(() => dispatch(element, TYPE, "done"));
95
90
  else dispatch(element, TYPE, "done");
@@ -135,106 +130,140 @@ for (const [TYPE, interpreter] of TYPES) {
135
130
  // possible early errors sent by polyscript
136
131
  const errors = new Map();
137
132
 
138
- define(TYPE, {
139
- config,
140
- interpreter,
141
- env: `${TYPE}-script`,
142
- version: config?.interpreter,
143
- onerror(error, element) {
144
- errors.set(element, error);
145
- },
146
- ...workerHooks,
147
- onWorkerReady(_, xworker) {
148
- assign(xworker.sync, sync);
149
- for (const callback of hooks.onWorkerReady)
150
- callback(_, xworker);
151
- },
152
- onBeforeRun(wrap, element) {
153
- currentElement = element;
154
- bootstrapNodeAndPlugins(
155
- wrap,
156
- element,
157
- before,
158
- "onBeforeRun",
159
- );
160
- },
161
- onBeforeRunAsync(wrap, element) {
162
- currentElement = element;
163
- bootstrapNodeAndPlugins(
164
- wrap,
165
- element,
166
- before,
167
- "onBeforeRunAsync",
168
- );
169
- },
170
- onAfterRun(wrap, element) {
171
- bootstrapNodeAndPlugins(wrap, element, after, "onAfterRun");
172
- },
173
- onAfterRunAsync(wrap, element) {
174
- bootstrapNodeAndPlugins(
175
- wrap,
176
- element,
177
- after,
178
- "onAfterRunAsync",
179
- );
180
- },
181
- async onInterpreterReady(wrap, element) {
182
- if (shouldRegister) {
183
- shouldRegister = false;
184
- registerModule(wrap);
185
- }
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
+ }
186
142
 
187
- // allows plugins to do whatever they want with the element
188
- // before regular stuff happens in here
189
- for (const callback of hooks.onInterpreterReady)
190
- callback(wrap, element);
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);
191
147
 
192
- // now that all possible plugins are configured,
193
- // bail out if polyscript encountered an error
194
- if (errors.has(element)) {
195
- let { message } = errors.get(element);
196
- errors.delete(element);
197
- const clone = message === INVALID_CONTENT;
198
- message = `(${ErrorCode.CONFLICTING_CODE}) ${message} for `;
199
- message += element.cloneNode(clone).outerHTML;
200
- wrap.io.stderr(message);
201
- return;
202
- }
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
+ }
203
159
 
204
- if (isScript(element)) {
205
- const {
206
- attributes: { async: isAsync, target },
207
- } = element;
208
- const hasTarget = !!target?.value;
209
- const show = hasTarget
210
- ? queryTarget(element, target.value)
211
- : document.createElement("script-py");
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");
212
168
 
213
- if (!hasTarget) {
214
- const { head, body } = document;
215
- if (head.contains(element)) body.append(show);
216
- else element.after(show);
217
- }
218
- if (!show.id) show.id = getID();
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();
219
175
 
220
- // allows the code to retrieve the target element via
221
- // document.currentScript.target if needed
222
- defineProperty(element, "target", { value: show });
176
+ // allows the code to retrieve the target element via
177
+ // document.currentScript.target if needed
178
+ defineProperty(element, "target", { value: show });
223
179
 
224
- // notify before the code runs
225
- dispatch(element, TYPE, "ready");
226
- dispatchDone(
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,
227
205
  element,
228
- isAsync,
229
- wrap[`run${isAsync ? "Async" : ""}`](
230
- await fetchSource(element, wrap.io, true),
231
- ),
206
+ "onBeforeRun",
232
207
  );
233
- } else {
234
- // resolve PyScriptElement to allow connectedCallback
235
- element._wrap.resolve(wrap);
236
- }
237
- console.debug("[pyscript/main] PyScript Ready");
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
+
259
+ define(TYPE, {
260
+ config,
261
+ interpreter,
262
+ hooks,
263
+ env: `${TYPE}-script`,
264
+ version: config?.interpreter,
265
+ onerror(error, element) {
266
+ errors.set(element, error);
238
267
  },
239
268
  });
240
269
 
@@ -290,13 +319,14 @@ for (const [TYPE, interpreter] of TYPES) {
290
319
  * @param {{config?: string | object, async?: boolean}} [options] optional configuration for the worker.
291
320
  * @returns {Worker & {sync: ProxyHandler<object>}}
292
321
  */
293
- export function PyWorker(file, options) {
322
+ function PyWorker(file, options) {
323
+ const hooks = hooked.get("py");
294
324
  // this propagates pyscript worker hooks without needing a pyscript
295
325
  // bootstrap + it passes arguments and enforces `pyodide`
296
326
  // as the interpreter to use in the worker, as all hooks assume that
297
327
  // and as `pyodide` is the only default interpreter that can deal with
298
328
  // all the features we need to deliver pyscript out there.
299
- const xworker = XWorker.call(new Hook(null, workerHooks), file, {
329
+ const xworker = XWorker.call(new Hook(null, hooks), file, {
300
330
  type: "pyodide",
301
331
  ...options,
302
332
  });
package/src/exceptions.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { assign } from "polyscript/exports";
2
+
1
3
  const CLOSEBUTTON =
2
4
  "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='currentColor' width='12px'><path d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/></svg>";
3
5
 
@@ -87,13 +89,13 @@ export function _createAlertBanner(
87
89
  }
88
90
 
89
91
  const content = messageType === "html" ? "innerHTML" : "textContent";
90
- const banner = Object.assign(document.createElement("div"), {
92
+ const banner = assign(document.createElement("div"), {
91
93
  className: `alert-banner py-${level}`,
92
94
  [content]: message,
93
95
  });
94
96
 
95
97
  if (level === "warning") {
96
- const closeButton = Object.assign(document.createElement("button"), {
98
+ const closeButton = assign(document.createElement("button"), {
97
99
  id: "alert-close-button",
98
100
  innerHTML: CLOSEBUTTON,
99
101
  });
package/src/fetch.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { FetchError, ErrorCode } from "./exceptions.js";
2
- import { getText } from "../node_modules/polyscript/esm/fetch-utils.js";
2
+ import { getText } from "polyscript/exports";
3
3
 
4
4
  export { getText };
5
5
 
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,16 +1,5 @@
1
- /**
2
- * A `Worker` facade able to bootstrap on the worker thread only a PyScript module.
3
- * @param {string} file the python file to run ina worker.
4
- * @param {{config?: string | object, async?: boolean}} [options] optional configuration for the worker.
5
- * @returns {Worker & {sync: ProxyHandler<object>}}
6
- */
7
- export function PyWorker(file: string, options?: {
8
- config?: string | object;
9
- async?: boolean;
10
- }): Worker & {
11
- sync: ProxyHandler<object>;
12
- };
13
- import sync from "./sync.js";
14
- declare const exportedConfig: {};
15
- import hooks from "./hooks.js";
16
- export { exportedConfig as config, hooks };
1
+ declare const exportedPyWorker: any;
2
+ declare const exportedHooks: any;
3
+ declare const exportedConfig: any;
4
+ declare const exportedWhenDefined: any;
5
+ export { exportedPyWorker as PyWorker, exportedHooks as hooks, exportedConfig as config, exportedWhenDefined as whenDefined };