Files
pyscript/pyscript.core/esm/index.js
2023-06-08 13:49:49 +02:00

120 lines
3.9 KiB
JavaScript

import { $x, $$ } from "basic-devtools";
import xworker from "./worker/class.js";
import { handle, runtimes } from "./script-handler.js";
import { all, assign, create, defineProperty } from "./utils.js";
import { registry, selectors, prefixes } from "./runtimes.js";
import { PLUGINS_SELECTORS, handlePlugin } from "./plugins.js";
export { registerPlugin } from "./plugins.js";
export const XWorker = xworker();
const RUNTIME_SELECTOR = selectors.join(",");
// ensure both runtime and its queue are awaited then returns the runtime
const awaitRuntime = async (key) => {
if (runtimes.has(key)) {
const { runtime, queue } = runtimes.get(key);
return (await all([runtime, queue]))[0];
}
const available = runtimes.size
? `Available runtimes are: ${[...runtimes.keys()]
.map((r) => `"${r}"`)
.join(", ")}.`
: `There are no runtimes in this page.`;
throw new Error(`The runtime "${key}" was not found. ${available}`);
};
defineProperty(globalThis, "pyscript", {
value: {
env: new Proxy(create(null), { get: (_, name) => awaitRuntime(name) }),
},
});
let index = 0;
globalThis.__events = new Map();
// attributes are tested via integration / e2e
/* c8 ignore next 17 */
const listener = async (event) => {
const { type, currentTarget } = event;
for (let { name, value, ownerElement: el } of $x(
`./@*[${prefixes.map((p) => `name()="${p}${type}"`).join(" or ")}]`,
currentTarget,
)) {
name = name.slice(0, -(type.length + 1));
const runtime = await awaitRuntime(
el.getAttribute(`${name}-env`) || name,
);
const i = index++;
try {
globalThis.__events.set(i, event);
registry.get(name).runEvent(runtime, value, i);
} finally {
globalThis.__events.delete(i);
}
}
};
// attributes are tested via integration / e2e
/* c8 ignore next 8 */
for (let { name, ownerElement: el } of $x(
`.//@*[${prefixes.map((p) => `starts-with(name(),"${p}")`).join(" or ")}]`,
)) {
name = name.slice(name.indexOf("-") + 1);
if (name !== "env") el.addEventListener(name, listener);
}
const mo = new MutationObserver((records) => {
for (const { type, target, attributeName, addedNodes } of records) {
// attributes are tested via integration / e2e
/* c8 ignore next 17 */
if (type === "attributes") {
const i = attributeName.indexOf("-") + 1;
if (i) {
const prefix = attributeName.slice(0, i);
for (const p of prefixes) {
if (prefix === p) {
const type = attributeName.slice(i);
if (type !== "env") {
const method = target.hasAttribute(attributeName)
? "add"
: "remove";
target[`${method}EventListener`](type, listener);
}
break;
}
}
}
continue;
}
for (const node of addedNodes) {
if (node.nodeType === 1) {
if (node.matches(RUNTIME_SELECTOR)) handle(node);
else {
$$(RUNTIME_SELECTOR, node).forEach(handle);
if (!PLUGINS_SELECTORS.length) continue;
handlePlugin(node);
$$(PLUGINS_SELECTORS.join(","), node).forEach(handlePlugin);
}
}
}
}
});
const observe = (root) => {
mo.observe(root, { childList: true, subtree: true, attributes: true });
return root;
};
const { attachShadow } = Element.prototype;
assign(Element.prototype, {
attachShadow(init) {
return observe(attachShadow.call(this, init));
},
});
$$(RUNTIME_SELECTOR, observe(document)).forEach(handle);