Skip to content

Commit fdee268

Browse files
Merge pull request #32 from pyscript/ready-event
Added type:ready event for scripts and workers
2 parents d757051 + a530d3b commit fdee268

File tree

11 files changed

+113
-14
lines changed

11 files changed

+113
-14
lines changed

core.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* [How Events Work](#how-events-work) - how `<button py-click="...">` works
1111
* [XWorker](#xworker) - how `XWorker` class and its `xworker` reference work
1212
* [Custom Scripts](#custom-scripts) - how *custom types* can be defined and used to enrich any core feature
13+
* [Ready Event](#ready-event) - how to listen to the `type:ready` event
1314
* [Examples](#examples) - some *polyscript* based live example
1415
* [Interpreter Features](#interpreter-features) - current state of supported interpreters
1516

@@ -490,6 +491,35 @@ wrap.io.stderr = (message) => {
490491
};
491492
```
492493

494+
## Ready Event
495+
496+
Whenever a *non-custom* script is going to run some code, or whenever *any worker* is going to run its own code, a `type:ready` event is dispatched through the element that is currently executing the code.
497+
498+
The [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) dispatched in either cases contains a `target` which refers to the element that is running code and a `detail.worker` boolean value that is `true` if such event came from a worker instead of the main thread.
499+
500+
The `worker` detail is essential to know if an `xworker` property is attached so that it's also easy to pollute its `sync` proxy utility.
501+
502+
### Custom Types on Main
503+
504+
The reason this event is not automatically dispatched on custom type elements or scripts is that these will have their own `onInterpreterReady` hook to eventually do more before desiring, or needing, to notify the "*readiness*" of such custom element and, in case of wanting the event to happen, this is the tiny boilerplate needed to simulate otherwise non-custom type events:
505+
506+
```js
507+
// note: type === 'py' or the defined type
508+
element.dispatchEvent(
509+
new CustomEvent(`${type}:ready`, {
510+
bubbles: true,
511+
detail: { worker: false },
512+
})
513+
);
514+
```
515+
516+
In the worker case, because the orchestration is inevitably coupled with this module, the custom type will be dispatched out of the blue to help extensions on op of this module to work best.
517+
518+
### Explicit Worker: No Event
519+
520+
Please note that if a worker is created explicitly, there won't be any element, script, or generic tag/node associated to it, so that no event will be triggered as there is no target for it. However, it's always possible to attach `sync` utilities to such explicit worker, so this should never be a real-world concern or blocker.
521+
522+
493523
## Examples
494524

495525
* [multi-pompom](./examples/multi-pompom/) - draw 4 pompom via turtle out of 4 different workers

docs/core.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/core.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

esm/script-handler.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import $xworker from './worker/class.js';
44
import workerURL from './worker/url.js';
55
import { getRuntime, getRuntimeID } from './loader.js';
66
import { registry } from './interpreters.js';
7-
import { all, resolve, defineProperty, nodeInfo } from './utils.js';
7+
import { all, dispatch, resolve, defineProperty, nodeInfo } from './utils.js';
88
import { getText } from './fetch-utils.js';
99

1010
const getRoot = (script) => {
@@ -44,10 +44,11 @@ const handled = new WeakMap();
4444
export const interpreters = new Map();
4545

4646
const execute = async (script, source, XWorker, isAsync) => {
47-
const module = registry.get(script.type);
47+
const { type } = script;
48+
const module = registry.get(type);
4849
/* c8 ignore start */
4950
if (module.experimental)
50-
console.warn(`The ${script.type} interpreter is experimental`);
51+
console.warn(`The ${type} interpreter is experimental`);
5152
const [interpreter, content] = await all([
5253
handled.get(script).interpreter,
5354
source,
@@ -60,6 +61,7 @@ const execute = async (script, source, XWorker, isAsync) => {
6061
get: () => script,
6162
});
6263
module.registerJSModule(interpreter, 'polyscript', { XWorker });
64+
dispatch(script, type, false, CustomEvent);
6365
return module[isAsync ? 'runAsync' : 'run'](interpreter, content);
6466
} finally {
6567
delete document.currentScript;

esm/utils.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,20 @@ const nodeInfo = (node, type) => ({
1616
id: node.id || (node.id = `${type}-w${id++}`),
1717
tag: node.tagName
1818
});
19+
20+
const dispatch = (target, type, worker, CustomEvent) => {
21+
if (target) target.dispatchEvent(
22+
new CustomEvent(`${type}:ready`, {
23+
bubbles: true,
24+
detail: { worker },
25+
})
26+
);
27+
};
1928
/* c8 ignore stop */
2029

2130
export {
2231
dedent,
32+
dispatch,
2333
isArray,
2434
assign,
2535
create,

esm/worker/_template.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import * as JSON from '@ungap/structured-clone/json';
88
import coincident from 'coincident/window';
99

10-
import { assign, create } from '../utils.js';
10+
import { assign, create, dispatch } from '../utils.js';
1111
import { registry } from '../interpreters.js';
1212
import { getRuntime, getRuntimeID } from '../loader.js';
1313

@@ -98,15 +98,16 @@ add('message', ({ data: { options, config: baseURL, code, hooks } }) => {
9898
}
9999
}
100100

101+
const { CustomEvent, document } = window;
102+
const element = document.getElementById(id);
103+
101104
let target = '';
102105

103106
// set the `xworker` global reference once
104107
details.registerJSModule(interpreter, 'polyscript', {
105108
xworker,
106109
get target() {
107110
if (!target) {
108-
const { document } = window;
109-
const element = document.getElementById(id);
110111
if (tag === 'SCRIPT') {
111112
element.after(assign(
112113
document.createElement(`script-${custom || type}`),
@@ -129,6 +130,8 @@ add('message', ({ data: { options, config: baseURL, code, hooks } }) => {
129130
// allows transforming arguments with sync
130131
transform = details.transform.bind(details, interpreter);
131132

133+
dispatch(element, custom || type, true, CustomEvent);
134+
132135
// run either sync or async code in the worker
133136
await details[name](interpreter, code);
134137
return interpreter;

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "polyscript",
3-
"version": "0.3.3",
3+
"version": "0.3.4",
44
"description": "PyScript single core to rule them all",
55
"main": "./cjs/index.js",
66
"types": "./types/polyscript/esm/index.d.ts",
@@ -70,6 +70,6 @@
7070
"coincident": "^0.11.5"
7171
},
7272
"worker": {
73-
"blob": "sha256-eIkd1K+zNPVHulEdSNGv85KED9dkOS9PcyhlMEJFJG8="
73+
"blob": "sha256-DIHUuInfI4tdn+DpgcTAEaslvvsvwq51ZU18P3a6bnk="
7474
}
7575
}

test/events.html

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<script>
7+
const listener = ({ target, detail }) => {
8+
console.log(detail.worker, target);
9+
if (detail.worker) {
10+
target.xworker.sync.test = () => {
11+
console.log(target.type, 1, 2, 3);
12+
};
13+
}
14+
};
15+
addEventListener("micropython:ready", listener);
16+
addEventListener("mpy:ready", listener);
17+
</script>
18+
<script type="module">
19+
import { define } from "/core.js";
20+
define("mpy", {
21+
interpreter: 'micropython',
22+
onInterpreterReady({ run, type }, element) {
23+
element.dispatchEvent(
24+
new CustomEvent(`${type}:ready`, {
25+
bubbles: true,
26+
detail: { worker: false },
27+
})
28+
);
29+
run(element.textContent);
30+
}
31+
});
32+
</script>
33+
</head>
34+
<body>
35+
<script type="micropython">
36+
print("main")
37+
</script>
38+
<script type="micropython" worker>
39+
from polyscript import xworker
40+
print("worker")
41+
xworker.sync.test()
42+
</script>
43+
<script type="mpy">
44+
print("mpy-main")
45+
</script>
46+
<script type="mpy" worker>
47+
from polyscript import xworker
48+
print("mpy-worker")
49+
xworker.sync.test()
50+
</script>
51+
</body>
52+
</html>

0 commit comments

Comments
 (0)