You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
|version|`{verstion: '0.23.2'}`| Allow the usage of a specific version of an interpreter, same way `version` attribute works with `<script>` elements. |
452
-
| config |`{config: 'type.toml'}``{config: {}}`| Ensure such config is already parsed and available, if not already passed as object, for every custom `type` that execute code. |
453
-
|onerror|`(error, element) => { throw error; }`| Allows custom types to intercept early errors possibly happened while bootstrapping elements. |
464
+
|interpreter|`{interpreter: 'pyodide'}`| Specifies the interpreter to use, such as *pyodide*, *micropython*, *wasmoon* or others. |
465
+
| config |`{config: 'type.toml'}``{config: {}}`| Ensure such config is already parsed and available, if not already passed as object, for every custom `type` that execute code. |
466
+
|version|`{version: '0.23.2'}`| Allow the usage of a specific version of an interpreter, same way `version` attribute works with `<script>` elements. |
454
467
| env |`{env: 'my-project'}`| Guarantee same environment for every custom `type`, avoiding conflicts with any other possible default or custom environment. |
455
-
| onInterpreterReady |`{onInterpreterReady(wrap, element) {}}`| This is the main entry point to define anything extra to the context of the always same interpreter. This callback is *awaited* and executed, after the desired *interpreter* is fully available and bootstrapped *once* though other optional fields, per each element that matches the defined `type`. The `wrap` reference contains many fields and utilities helpful to run most common operations, and it is passed along most other options too, when defined. |
456
-
| onBeforeRun |`{onBeforeRun(wrap, element) {}}`| This is a **hook** into the logic that runs right before any *interpreter*`run(...)` is performed. It receives the same `wrap` already sent when *onInterpreterReady* executes, and it passes along the current `element` that is going to execute such code. |
457
-
| onAfterRun |`{onAfterRun(wrap, element) {}}`| This is a **hook** into the logic that runs right after any *interpreter*`run(...)` is performed. It receives the same `wrap` already sent when *onInterpreterReady* executes, and it passes along the current `element` that already executed the code. |
458
-
| onBeforeRunAsync |`{onBeforeRunAsync(wrap, element) {}}`| This is a **hook** into the logic that runs right before any *interpreter*`runAsync(...)` is performed. It receives the same `wrap` already sent when *onInterpreterReady* executes, and it passes along the current `element` that is going to execute such code asynchronously. |
459
-
| onAfterRunAsync |`{onAfterRunAsync(wrap, element) {}}`| This is a **hook** into the logic that runs right after any *interpreter*`runAsync(...)` is performed. It receives the same `wrap` already sent when *onInterpreterReady* executes, and it passes along the current `element` that already executed the code asynchronously. |
460
-
| onWorkerReady |`{onWorkerReady(interpreter, xworker) {}}`| This is a **hook** into the logic that runs right before a new `XWorker` instance has been created in the **main** thread. It makes it possible to pre-define exposed `sync` methods to the `xworker` counter-part, enabling cross thread features out of the custom type without needing any extra effort. |
461
-
| codeBeforeRunWorker |`{codeBeforeRunWorker(){}}`| This is a **hook** into the logic that runs right before any *interpreter*`run(...)` is performed *within a worker*. Because all worker code is executed as `code`, this callback is expected to **return a string** that can be prepended for any worker synchronous operation. |
462
-
| codeAfterRunWorker |`{codeAfterRunWorker(){}}`| This is a **hook** into the logic that runs right after any *interpreter*`run(...)` is performed *within a worker*. Because all worker code is executed as `code`, this callback is expected to **return a string** that can be appended for any worker synchronous operation. |
463
-
| codeBeforeRunWorkerAsync |`{codeBeforeRunWorkerAsync(){}}`| This is a **hook** into the logic that runs right before any *interpreter*`runAsync(...)` is performed *within a worker*. Because all worker code is executed as `code`, this callback is expected to **return a string** that can be prepended for any worker asynchronous operation. |
464
-
| codeAfterRunWorkerAsync |`{codeAfterRunWorkerAsync(){}}`| This is a **hook** into the logic that runs right after any *interpreter*`runAsync(...)` is performed *within a worker*. Because all worker code is executed as `code`, this callback is expected to **return a string** that can be appended for any worker asynchronous operation. |
468
+
| onerror |`(error, element) => { throw error; }`| Allows custom types to intercept early errors possibly happened while bootstrapping elements. |
469
+
| hooks |`{hooks: {main: {}, worker: {}}}`| Allows custom types to hook logic around every main thread or worker tag via defined hooks. |
470
+
471
+
## Hooks
472
+
473
+
Every special script or tag inevitably passes through some main or worker thread related tasks.
474
+
475
+
In both worlds, the exact sequence of steps around code execution is the following:
476
+
477
+
***ready** - the DOM recognized the special script or tag and the associated interpreter is ready to work. A *JS* callback might be useful to instrument the interpreter before anything else happens.
478
+
***before run** - there could be some *JS* code setup specific for the script on the main thread, or the worker. This is similar to a generic *setup* callback in tests.
479
+
***code before run** - there could be some *PL* code to prepend to the one being executed. In this case the code is a string because it will be part of the evaluation.
480
+
***actual code** - the code in the script or tag or the `src` file specified in the script. This is not a hook, just the exact time the code gets executed in general.
481
+
***code after run** - there could be some *PL* code to append to the one being executed. Same as *before*, the code is a string targeting the foreign *PL*.
482
+
***after run** - there could be some *JS* to execute right after the whole code has been evaluated. This is similar to a generic *teardown* callback in tests.
483
+
484
+
As most interpreters can run their code either *synchronously* or *asynchronously*, the very same sequence is guaranteed to run in order in both cases, and the difference is only around the naming convention.
485
+
486
+
### Main Hooks
487
+
488
+
When it comes to *main* hooks all callbacks will receive a *wrapper* of the interpreter with its utilities, see the further section to know more, plus the element on the page that is going to execute its related code, being this a custom script/type or a custom tag.
489
+
490
+
This is the list of all possible, yet **optional** hooks, a custom type can define for **main**:
| onReady | (Wrap, Element) => void |`onReady(wrap, element) {}`| If defined, it is invoked before any other hook to signal that the element is going to execute the code. |
495
+
| onBeforeRun | (Wrap, Element) => void |`onBeforeRun(wrap, element) {}`| If defined, it is invoked before any other hook to signal that the element is going to execute the code. |
496
+
| onBeforeRunAsync | (Wrap, Element) => void |`onBeforeRunAsync(wrap, element) {}`| Same as `onBeforeRun` except it's the one used whenever the script is `async`. |
497
+
| codeBeforeRun | () => string |`codeBeforeRun: () => 'print("before")'`| If defined, prepend some code to evaluate right before the rest of the code gets executed. |
498
+
| codeBeforeRunAsync | () => string |`codeBeforeRunAsync: () => 'print("before")'`| Same as `codeBeforeRun` except it's the one used whenever the script is `async`. |
499
+
| codeAfterRun | () => string |`codeAfterRun: () => 'print("after")'`| If defined, append some code to evaluate right after the rest of the code already executed. |
500
+
| codeAfterRunAsync | () => string |`codeAfterRunAsync: () => 'print("after")'`| Same as `codeAfterRun` except it's the one used whenever the script is `async`. |
501
+
| onAfterRun | (Wrap, Element) => void |`onAfterRun(wrap, element) {}`| If defined, it is invoked after the foreign code has been executed already. |
502
+
| onAfterRunAsync | (Wrap, Element) => void |`onAfterRunAsync(wrap, element) {}`| Same as `onAfterRun` except it's the one used whenever the script is `async`. |
503
+
| onWorker | (Wrap?, XWorker) => void |`onWorker(wrap = null, xworker) {}`| If defined, whenever a script or tag with a `worker` attribute is processed it gets triggered on the main thread, to allow to expose possible `xworker` features before the code gets executed within the worker thread. The `wrap` reference is most of the time `null` unless an explicit `XWorker` call has been initialized manually and/or there is an interpreter on the main thread (*very advanced use case*). Please **note** this is the only hook that doesn't exist in the *worker* counter list of hooks. |
504
+
505
+
### Worker Hooks
506
+
507
+
When it comes to *worker* hooks, **all non code related callbacks must be serializable**, meaning that callbacks cannot use any outer scope reference, as these are forwarded as strings, hence evaluated after in the worker, to survive the main <-> worker `postMessage` dance.
508
+
509
+
Here an example of what works and what doesn't:
510
+
511
+
```js
512
+
// this works 👍
513
+
define('pl', {
514
+
interpreter:'programming-lang',
515
+
hooks: {
516
+
worker: {
517
+
onReady() {
518
+
// NOT suggested, just as example!
519
+
if (!('i'inglobalThis))
520
+
globalThis.i=0;
521
+
console.log(++i);
522
+
}
523
+
}
524
+
}
525
+
});
526
+
527
+
// this DOES NOT WORK ⚠️
528
+
let i =0;
529
+
define('pl', {
530
+
interpreter:'programming-lang',
531
+
hooks: {
532
+
worker: {
533
+
onReady() {
534
+
// that outer-scope `i` is nowhere understood
535
+
// whenever this code executes in the worker
536
+
// as this function gets stringified and re-evaluated
537
+
console.log(++i);
538
+
}
539
+
}
540
+
}
541
+
});
542
+
```
543
+
544
+
At the same time, as the worker doesn't have any `element` strictly related, as workers can be created also procedurally, the second argument won't be an element but the related *xworker* that is driving the logic.
545
+
546
+
As summary, this is the list of all possible, yet **optional** hooks, a custom type can define for **worker**:
| onReady | (Wrap, XWorker) => void |`onReady(wrap, xworker) {}`| If defined, it is invoked before any other hook to signal that the xworker is going to execute the code. |
551
+
| onBeforeRun | (Wrap, XWorker) => void |`onBeforeRun(wrap, xworker) {}`| If defined, it is invoked before any other hook to signal that the xworker is going to execute the code. |
552
+
| onBeforeRunAsync | (Wrap, XWorker) => void |`onBeforeRunAsync(wrap, xworker) {}`| Same as `onBeforeRun` except it's the one used whenever the worker script is `async`. |
553
+
| codeBeforeRun | () => string |`codeBeforeRun: () => 'print("before")'`| If defined, prepend some code to evaluate right before the rest of the code gets executed. |
554
+
| codeBeforeRunAsync | () => string |`codeBeforeRunAsync: () => 'print("before")'`| Same as `codeBeforeRun` except it's the one used whenever the worker script is `async`. |
555
+
| codeAfterRun | () => string |`codeAfterRun: () => 'print("after")'`| If defined, append some code to evaluate right after the rest of the code already executed. |
556
+
| codeAfterRunAsync | () => string |`codeAfterRunAsync: () => 'print("after")'`| Same as `codeAfterRun` except it's the one used whenever the worker script is `async`. |
557
+
| onAfterRun | (Wrap, XWorker) => void |`onAfterRun(wrap, xworker) {}`| If defined, it is invoked after the foreign code has been executed already. |
558
+
| onAfterRunAsync | (Wrap, XWorker) => void |`onAfterRunAsync(wrap, xworker) {}`| Same as `onAfterRun` except it's the one used whenever the worker script is `async`. |
0 commit comments