Skip to content

Commit 3a94107

Browse files
committed
Breaking: refactory of all main/worker hooks
1 parent 52ba005 commit 3a94107

File tree

13 files changed

+178
-138
lines changed

13 files changed

+178
-138
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: 60 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ 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 { main, worker } from './hooks.js';
1011
import { Hook, XWorker } from './xworker.js';
1112
import workerURL from './worker/url.js';
1213

@@ -43,8 +44,8 @@ export const handleCustomType = (node) => {
4344
config,
4445
version,
4546
env,
46-
onInterpreterReady,
4747
onerror,
48+
hooks,
4849
} = options;
4950

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

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

9488
const XWorker = function XWorker(...args) {
95-
return Worker.apply(hooks, args);
89+
return Worker.apply(hook, args);
9690
};
9791

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

133133
module.registerJSModule(interpreter, 'polyscript', { XWorker });
@@ -147,7 +147,7 @@ export const handleCustomType = (node) => {
147147

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

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

170169
let dontBotherCount = 0;
@@ -197,17 +196,24 @@ export const define = (type, options) => {
197196

198197
if (dontBother) {
199198
// add a script then cleanup everything once that's ready
200-
const { onInterpreterReady } = options;
199+
const { hooks } = options;
200+
const onReady = hooks?.main?.onReady;
201201
options = {
202202
...options,
203-
onInterpreterReady(resolved, node) {
204-
CUSTOM_SELECTORS.splice(CUSTOM_SELECTORS.indexOf(type), 1);
205-
defaultRegistry.delete(type);
206-
registry.delete(type);
207-
waitList.delete(type);
208-
node.remove();
209-
onInterpreterReady?.(resolved);
210-
}
203+
hooks: {
204+
...hooks,
205+
main: {
206+
...hooks?.main,
207+
onReady(resolved, node) {
208+
CUSTOM_SELECTORS.splice(CUSTOM_SELECTORS.indexOf(type), 1);
209+
defaultRegistry.delete(type);
210+
registry.delete(type);
211+
waitList.delete(type);
212+
node.remove();
213+
onReady?.(resolved);
214+
}
215+
}
216+
},
211217
};
212218
document.head.append(
213219
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/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, create, 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.

esm/xworker.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import xworker from './worker/class.js';
2-
import { Hook } from './worker/hooks.js';
2+
import Hook from './worker/hook.js';
33

44
const XWorker = xworker();
55

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,6 @@
8282
"sticky-module": "^0.1.0"
8383
},
8484
"worker": {
85-
"blob": "sha256-704aE8aK4Uw8zQP/aNqDV7vYYSPe+xl2V6W8mI5d3W0="
85+
"blob": "sha256-ZouV2LoyZSnWAjhCxln/Ay0+2I5NO0lpp2UuWBTBFxw="
8686
}
8787
}

0 commit comments

Comments
 (0)