Skip to content

Commit 617f0fb

Browse files
committed
Added a way to hook into polyscript MutationObserver
1 parent 53c6d4b commit 617f0fb

File tree

4 files changed

+128
-118
lines changed

4 files changed

+128
-118
lines changed

docs/index.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/index.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: 121 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import { getDetails } from './script-handler.js';
66
import { registry as defaultRegistry, prefixes, configs } from './interpreters.js';
77
import { getRuntimeID } from './loader.js';
88
import { addAllListeners } from './listeners.js';
9-
import { Hook, XWorker } from './xworker.js';
9+
import { Hook, XWorker as XW } from './xworker.js';
1010
import { polluteJS, js as jsHooks, code as codeHooks } from './hooks.js';
1111
import workerURL from './worker/url.js';
1212

1313
export const CUSTOM_SELECTORS = [];
1414

15+
export const customObserver = new Map();
16+
1517
/**
1618
* @typedef {Object} Runtime custom configuration
1719
* @prop {object} interpreter the bootstrapped interpreter
@@ -30,131 +32,137 @@ const waitList = new Map();
3032
/**
3133
* @param {Element} node any DOM element registered via define.
3234
*/
33-
export const handleCustomType = (node) => {
35+
export const handleCustomType = async (node) => {
3436
for (const selector of CUSTOM_SELECTORS) {
3537
if (node.matches(selector)) {
3638
const type = types.get(selector);
3739
const details = registry.get(type);
3840
const { resolve } = waitList.get(type);
3941
const { options, known } = details;
40-
if (!known.has(node)) {
41-
known.add(node);
42-
const {
43-
interpreter: runtime,
44-
configURL,
45-
config,
46-
version,
47-
env,
48-
onerror,
49-
hooks,
50-
} = options;
51-
52-
let error;
53-
try {
54-
const worker = workerURL(node);
55-
if (worker) {
56-
const xworker = XWorker.call(new Hook(null, hooks), worker, {
57-
...nodeInfo(node, type),
58-
version,
59-
configURL,
60-
type: runtime,
61-
custom: type,
62-
config: node.getAttribute('config') || config || {},
63-
async: node.hasAttribute('async')
64-
});
65-
defineProperty(node, 'xworker', { value: xworker });
66-
resolve({ type, xworker });
67-
return;
68-
}
69-
}
70-
// let the custom type handle errors via its `io`
71-
catch (workerError) {
72-
error = workerError;
42+
43+
if (known.has(node)) return;
44+
known.add(node);
45+
46+
for (const [selector, callback] of customObserver) {
47+
if (node.matches(selector)) await callback(node);
48+
}
49+
50+
const {
51+
interpreter: runtime,
52+
configURL,
53+
config,
54+
version,
55+
env,
56+
onerror,
57+
hooks,
58+
} = options;
59+
60+
let error;
61+
try {
62+
const worker = workerURL(node);
63+
if (worker) {
64+
const xworker = XW.call(new Hook(null, hooks), worker, {
65+
...nodeInfo(node, type),
66+
version,
67+
configURL,
68+
type: runtime,
69+
custom: type,
70+
config: node.getAttribute('config') || config || {},
71+
async: node.hasAttribute('async')
72+
});
73+
defineProperty(node, 'xworker', { value: xworker });
74+
resolve({ type, xworker });
75+
return;
7376
}
77+
}
78+
// let the custom type handle errors via its `io`
79+
catch (workerError) {
80+
error = workerError;
81+
}
82+
83+
const name = getRuntimeID(runtime, version);
84+
const id = env || `${name}${config ? `|${config}` : ''}`;
85+
const { interpreter: engine, XWorker: Worker } = getDetails(
86+
type,
87+
id,
88+
name,
89+
version,
90+
config,
91+
configURL,
92+
runtime
93+
);
94+
95+
const interpreter = await engine;
7496

75-
const name = getRuntimeID(runtime, version);
76-
const id = env || `${name}${config ? `|${config}` : ''}`;
77-
const { interpreter: engine, XWorker: Worker } = getDetails(
97+
const module = create(defaultRegistry.get(runtime));
98+
99+
const hook = new Hook(interpreter, hooks);
100+
101+
const XWorker = function XWorker(...args) {
102+
return Worker.apply(hook, args);
103+
};
104+
105+
const resolved = {
106+
...createResolved(
107+
module,
78108
type,
79-
id,
80-
name,
81-
version,
82-
config,
83-
configURL,
84-
runtime
85-
);
86-
engine.then((interpreter) => {
87-
const module = create(defaultRegistry.get(runtime));
88-
89-
const hook = new Hook(interpreter, hooks);
90-
91-
const XWorker = function XWorker(...args) {
92-
return Worker.apply(hook, args);
93-
};
94-
95-
const resolved = {
96-
...createResolved(
97-
module,
98-
type,
99-
structuredClone(configs.get(name)),
100-
interpreter,
101-
),
102-
XWorker,
103-
};
104-
105-
registerJSModules(runtime, module, interpreter, JSModules);
106-
module.registerJSModule(interpreter, 'polyscript', {
107-
XWorker,
108-
currentScript: type.startsWith('_') ? null : node,
109-
js_modules: JSModules,
110-
});
109+
structuredClone(configs.get(name)),
110+
interpreter,
111+
),
112+
XWorker,
113+
};
111114

112-
// patch methods accordingly to hooks (and only if needed)
113-
for (const suffix of ['Run', 'RunAsync']) {
114-
let before = '';
115-
let after = '';
116-
117-
for (const key of codeHooks) {
118-
const value = hooks?.main?.[key];
119-
if (value && key.endsWith(suffix)) {
120-
if (key.startsWith('codeBefore'))
121-
before = dedent(value());
122-
else
123-
after = dedent(value());
124-
}
125-
}
126-
127-
if (before || after) {
128-
createOverload(
129-
module,
130-
`r${suffix.slice(1)}`,
131-
before,
132-
after,
133-
);
134-
}
135-
136-
let beforeCB, afterCB;
137-
// ignore onReady and onWorker
138-
for (let i = 2; i < jsHooks.length; i++) {
139-
const key = jsHooks[i];
140-
const value = hooks?.main?.[key];
141-
if (value && key.endsWith(suffix)) {
142-
if (key.startsWith('onBefore'))
143-
beforeCB = value;
144-
else
145-
afterCB = value;
146-
}
147-
}
148-
polluteJS(module, resolved, node, suffix.endsWith('Async'), beforeCB, afterCB);
115+
registerJSModules(runtime, module, interpreter, JSModules);
116+
module.registerJSModule(interpreter, 'polyscript', {
117+
XWorker,
118+
currentScript: type.startsWith('_') ? null : node,
119+
js_modules: JSModules,
120+
});
121+
122+
// patch methods accordingly to hooks (and only if needed)
123+
for (const suffix of ['Run', 'RunAsync']) {
124+
let before = '';
125+
let after = '';
126+
127+
for (const key of codeHooks) {
128+
const value = hooks?.main?.[key];
129+
if (value && key.endsWith(suffix)) {
130+
if (key.startsWith('codeBefore'))
131+
before = dedent(value());
132+
else
133+
after = dedent(value());
149134
}
135+
}
150136

151-
details.queue = details.queue.then(() => {
152-
resolve(resolved);
153-
if (error) onerror?.(error, node);
154-
return hooks?.main?.onReady?.(resolved, node);
155-
});
156-
});
137+
if (before || after) {
138+
createOverload(
139+
module,
140+
`r${suffix.slice(1)}`,
141+
before,
142+
after,
143+
);
144+
}
145+
146+
let beforeCB, afterCB;
147+
// ignore onReady and onWorker
148+
for (let i = 2; i < jsHooks.length; i++) {
149+
const key = jsHooks[i];
150+
const value = hooks?.main?.[key];
151+
if (value && key.endsWith(suffix)) {
152+
if (key.startsWith('onBefore'))
153+
beforeCB = value;
154+
else
155+
afterCB = value;
156+
}
157+
}
158+
polluteJS(module, resolved, node, suffix.endsWith('Async'), beforeCB, afterCB);
157159
}
160+
161+
details.queue = details.queue.then(() => {
162+
resolve(resolved);
163+
if (error) onerror?.(error, node);
164+
return hooks?.main?.onReady?.(resolved, node);
165+
});
158166
}
159167
}
160168
};

esm/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import { selectors, prefixes } from './interpreters.js';
77
import { CUSTOM_SELECTORS, handleCustomType } from './custom.js';
88
import { listener, addAllListeners } from './listeners.js';
99

10-
import { define as $define, whenDefined as $whenDefined } from './custom.js';
10+
import { customObserver as $customObserver, define as $define, whenDefined as $whenDefined } from './custom.js';
1111
import { env as $env } from './listeners.js';
1212
import { Hook as $Hook, XWorker as $XWorker } from './xworker.js';
1313

1414
// avoid multiple initialization of the same library
1515
const [
1616
{
17+
customObserver,
1718
define,
1819
whenDefined,
1920
env,
@@ -24,6 +25,7 @@ const [
2425
] = stickyModule(
2526
'polyscript',
2627
{
28+
customObserver: $customObserver,
2729
define: $define,
2830
whenDefined: $whenDefined,
2931
env: $env,
@@ -32,7 +34,7 @@ const [
3234
}
3335
);
3436

35-
export { define, whenDefined, env, Hook, XWorker };
37+
export { customObserver, define, whenDefined, env, Hook, XWorker };
3638
export * from './errors.js';
3739

3840

0 commit comments

Comments
 (0)