Skip to content

Commit 5c9ff62

Browse files
committed
Breaking: refactory of all main/worker hooks
1 parent 69030d6 commit 5c9ff62

File tree

13 files changed

+179
-139
lines changed

13 files changed

+179
-139
lines changed

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/custom.js

Lines changed: 61 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import { registry as defaultRegistry, prefixes, configs } from './interpreters.j
77
import { getRuntimeID } from './loader.js';
88
import { io } from './interpreter/_utils.js';
99
import { addAllListeners } from './listeners.js';
10-
import { Hook } from './worker/hooks.js';
10+
import { main, worker } from './hooks.js';
1111
import workerURL from './worker/url.js';
12+
import Hook from './worker/hook.js';
1213
import XWorker from './xworker.js';
1314

1415
export const CUSTOM_SELECTORS = [];
@@ -44,8 +45,8 @@ export const handleCustomType = (node) => {
4445
config,
4546
version,
4647
env,
47-
onInterpreterReady,
4848
onerror,
49+
hooks,
4950
} = options;
5051

5152
let error;
@@ -83,52 +84,51 @@ export const handleCustomType = (node) => {
8384
engine.then((interpreter) => {
8485
const module = create(defaultRegistry.get(runtime));
8586

86-
const {
87-
onBeforeRun,
88-
onBeforeRunAsync,
89-
onAfterRun,
90-
onAfterRunAsync,
91-
} = options;
92-
93-
const hooks = new Hook(interpreter, options);
87+
const hook = new Hook(interpreter, options);
9488

9589
const XWorker = function XWorker(...args) {
96-
return Worker.apply(hooks, args);
90+
return Worker.apply(hook, args);
9791
};
9892

99-
// These two loops mimic a `new Map(arrayContent)` without needing
100-
// the new Map overhead so that [name, [before, after]] can be easily destructured
101-
// and new sync or async patches become easy to add (when the logic is the same).
102-
103-
// patch sync
104-
for (const [name, [before, after]] of [
105-
['run', [onBeforeRun, onAfterRun]],
106-
]) {
107-
const method = module[name];
108-
module[name] = function (interpreter, code, ...args) {
109-
if (before) before.call(this, resolved, node);
110-
const result = method.call(this, interpreter, code, ...args);
111-
if (after) after.call(this, resolved, node);
112-
return result;
113-
};
114-
}
115-
116-
// patch async
117-
for (const [name, [before, after]] of [
118-
['runAsync', [onBeforeRunAsync, onAfterRunAsync]],
119-
]) {
120-
const method = module[name];
121-
module[name] = async function (interpreter, code, ...args) {
122-
if (before) await before.call(this, resolved, node);
123-
const result = await method.call(
124-
this,
125-
interpreter,
126-
code,
127-
...args
128-
);
129-
if (after) await after.call(this, resolved, node);
130-
return result;
131-
};
93+
// patch methods accordingly to hooks (and only if needed)
94+
for (const suffix of ['Run', 'RunAsync']) {
95+
let before, after;
96+
// ignore onReady and all other worker-like hooks around code,
97+
// as that will be implemented later.
98+
// TODO: implement code snippets for custom types too!
99+
for (let i = worker.length; i < main.length; i++) {
100+
const key = main[i];
101+
const value = hooks?.main?.[key];
102+
if (value && key.endsWith(suffix)) {
103+
if (key.startsWith('onBefore'))
104+
before = value;
105+
else
106+
after = value;
107+
}
108+
}
109+
if (before || after) {
110+
const name = `r${suffix.slice(1)}`;
111+
const method = module[name];
112+
module[name] = suffix.endsWith('Async') ?
113+
async function (interpreter, code, ...args) {
114+
if (before) await before.call(this, resolved, node);
115+
const result = await method.call(
116+
this,
117+
interpreter,
118+
code,
119+
...args
120+
);
121+
if (after) await after.call(this, resolved, node);
122+
return result;
123+
} :
124+
function (interpreter, code, ...args) {
125+
if (before) before.call(this, resolved, node);
126+
const result = method.call(this, interpreter, code, ...args);
127+
if (after) after.call(this, resolved, node);
128+
return result;
129+
}
130+
;
131+
}
132132
}
133133

134134
module.registerJSModule(interpreter, 'polyscript', { XWorker });
@@ -148,7 +148,7 @@ export const handleCustomType = (node) => {
148148

149149
if (error) onerror?.(error, node);
150150

151-
onInterpreterReady?.(resolved, node);
151+
hooks?.main?.onReady?.(resolved, node);
152152
});
153153
}
154154
}
@@ -165,7 +165,6 @@ const registry = new Map();
165165
* @prop {'pyodide' | 'micropython' | 'wasmoon' | 'ruby-wasm-wasi'} interpreter the interpreter to use
166166
* @prop {string} [version] the optional interpreter version to use
167167
* @prop {string} [config] the optional config to use within such interpreter
168-
* @prop {(environment: object, node: Element) => void} [onInterpreterReady] the callback that will be invoked once
169168
*/
170169

171170
let dontBotherCount = 0;
@@ -198,17 +197,24 @@ export const define = (type, options) => {
198197

199198
if (dontBother) {
200199
// add a script then cleanup everything once that's ready
201-
const { onInterpreterReady } = options;
200+
const { hooks } = options;
201+
const onReady = hooks?.main?.onReady;
202202
options = {
203203
...options,
204-
onInterpreterReady(resolved, node) {
205-
CUSTOM_SELECTORS.splice(CUSTOM_SELECTORS.indexOf(type), 1);
206-
defaultRegistry.delete(type);
207-
registry.delete(type);
208-
waitList.delete(type);
209-
node.remove();
210-
onInterpreterReady?.(resolved);
211-
}
204+
hooks: {
205+
...hooks,
206+
main: {
207+
...hooks?.main,
208+
onReady(resolved, node) {
209+
CUSTOM_SELECTORS.splice(CUSTOM_SELECTORS.indexOf(type), 1);
210+
defaultRegistry.delete(type);
211+
registry.delete(type);
212+
waitList.delete(type);
213+
node.remove();
214+
onReady?.(resolved);
215+
}
216+
}
217+
},
212218
};
213219
document.head.append(
214220
assign(document.createElement('script'), { type })

esm/hooks.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const beforeRun = 'BeforeRun';
2+
const afterRun = 'AfterRun';
3+
4+
export const worker = [
5+
'onReady',
6+
`code${beforeRun}`,
7+
`code${beforeRun}Async`,
8+
`code${afterRun}`,
9+
`code${afterRun}Async`,
10+
];
11+
12+
export const main = [
13+
...worker,
14+
`on${beforeRun}`,
15+
`on${beforeRun}Async`,
16+
`on${afterRun}`,
17+
`on${afterRun}Async`,
18+
];

esm/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { listener, addAllListeners } from './listeners.js';
88

99
import { define as $define, whenDefined as $whenDefined } from './custom.js';
1010
import { env as $env } from './listeners.js';
11-
import { Hook as $Hook } from './worker/hooks.js';
11+
import $Hook from './worker/hook.js';
1212
import $XWorker from './xworker.js';
1313

1414
const polyscript = Symbol.for('polyscript');

esm/worker/_template.js

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import coincident from 'coincident/window';
1010
import { assign, create, dispatch } from '../utils.js';
1111
import { registry } from '../interpreters.js';
1212
import { getRuntime, getRuntimeID } from '../loader.js';
13+
import { worker } from '../hooks.js';
1314

1415
// bails out out of the box with a native/meaningful error
1516
// in case the SharedArrayBuffer is not available
@@ -76,26 +77,37 @@ add('message', ({ data: { options, config: baseURL, code, hooks } }) => {
7677
const name = `run${isAsync ? 'Async' : ''}`;
7778

7879
if (hooks) {
79-
// patch code if needed
80-
const { beforeRun, beforeRunAsync, afterRun, afterRunAsync } =
81-
hooks;
82-
83-
const after = isAsync ? afterRunAsync : afterRun;
84-
const before = isAsync ? beforeRunAsync : beforeRun;
85-
86-
// append code that should be executed *after* first
87-
if (after) {
80+
const overload = ($, pre) => {
8881
const method = details[name].bind(details);
8982
details[name] = (interpreter, code, ...args) =>
90-
method(interpreter, `${code}\n${after}`, ...args);
83+
method(interpreter, `${pre ? $ : code}\n${pre ? code : $}`, ...args);
84+
};
85+
86+
let before = '';
87+
let after = '';
88+
89+
// exclude `onReady` hook
90+
for (const key of worker.slice(1)) {
91+
const value = hooks[key];
92+
if (value) {
93+
const asyncCode = key.endsWith('Async');
94+
// either async hook and this worker is async
95+
// or sync hook and this worker is sync
96+
// other shared options possible cases are ignored
97+
if ((asyncCode && isAsync) || (!asyncCode && !isAsync)) {
98+
if (key.startsWith('codeBefore'))
99+
before = value;
100+
else
101+
after = value;
102+
}
103+
}
91104
}
92105

106+
// append code that should be executed *after* first
107+
if (after) overload(after, false);
108+
93109
// prepend code that should be executed *before* (so that after is post-patched)
94-
if (before) {
95-
const method = details[name].bind(details);
96-
details[name] = (interpreter, code, ...args) =>
97-
method(interpreter, `${before}\n${code}`, ...args);
98-
}
110+
if (before) overload(before, true);
99111
}
100112

101113
const { CustomEvent, document } = window;

esm/worker/class.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import xworker from './xworker.js';
44
import { getConfigURLAndType } from '../loader.js';
55
import { assign, defineProperties } from '../utils.js';
66
import { getText } from '../fetch-utils.js';
7-
import { Hook } from './hooks.js';
7+
import Hook from './hook.js';
88

99
/**
1010
* @typedef {Object} WorkerOptions custom configuration
@@ -39,7 +39,7 @@ export default (...args) =>
3939
const bootstrap = fetch(url)
4040
.then(getText)
4141
.then(code => {
42-
const hooks = isHook ? this.stringHooks : void 0;
42+
const hooks = isHook ? this.toJSON() : void 0;
4343
postMessage.call(worker, { options, config, code, hooks });
4444
});
4545

@@ -60,8 +60,6 @@ export default (...args) =>
6060
}
6161
});
6262

63-
if (isHook) this.onWorkerReady?.(this.interpreter, worker);
64-
6563
worker.addEventListener('message', event => {
6664
const { data } = event;
6765
if (data instanceof Error) {
@@ -73,5 +71,7 @@ export default (...args) =>
7371
}
7472
});
7573

74+
if (isHook) this.onReady?.(this.interpreter, worker);
75+
7676
return worker;
7777
};

esm/worker/hook.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { dedent } from '../utils.js';
2+
import { worker } from '../hooks.js';
3+
4+
// REQUIRES INTEGRATION TEST
5+
/* c8 ignore start */
6+
export default class Hook {
7+
constructor(interpreter, { hooks }) {
8+
this.interpreter = interpreter;
9+
for (const key of worker)
10+
this[key] = hooks?.worker?.[key];
11+
}
12+
toJSON() {
13+
const hooks = {};
14+
// exclude `onReady` callback
15+
for (const key of worker.slice(1)) {
16+
if (this[key]) hooks[key] = dedent(this[key]());
17+
}
18+
return hooks;
19+
}
20+
}
21+
/* c8 ignore stop */

esm/worker/hooks.js

Lines changed: 0 additions & 27 deletions
This file was deleted.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,6 @@
7676
"html-escaper": "^3.0.3"
7777
},
7878
"worker": {
79-
"blob": "sha256-704aE8aK4Uw8zQP/aNqDV7vYYSPe+xl2V6W8mI5d3W0="
79+
"blob": "sha256-ZouV2LoyZSnWAjhCxln/Ay0+2I5NO0lpp2UuWBTBFxw="
8080
}
8181
}

0 commit comments

Comments
 (0)