@pyreon/runtime-dom 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/analysis/index.js.html +1 -1
- package/lib/index.js +143 -6
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +118 -6
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index2.d.ts +71 -1
- package/lib/types/index2.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/delegate.ts +82 -0
- package/src/hydrate.ts +23 -4
- package/src/index.ts +4 -1
- package/src/props.ts +13 -1
- package/src/template.ts +56 -0
- package/src/tests/mount.test.ts +2 -2
|
@@ -5386,7 +5386,7 @@ var drawChart = (function (exports) {
|
|
|
5386
5386
|
</script>
|
|
5387
5387
|
<script>
|
|
5388
5388
|
/*<!--*/
|
|
5389
|
-
const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"
|
|
5389
|
+
const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"3dbea446-1","name":"delegate.ts"},{"uid":"3dbea446-3","name":"hydration-debug.ts"},{"uid":"3dbea446-5","name":"devtools.ts"},{"uid":"3dbea446-7","name":"nodes.ts"},{"uid":"3dbea446-9","name":"props.ts"},{"uid":"3dbea446-11","name":"mount.ts"},{"uid":"3dbea446-13","name":"hydrate.ts"},{"uid":"3dbea446-15","name":"keep-alive.ts"},{"uid":"3dbea446-17","name":"template.ts"},{"uid":"3dbea446-19","name":"transition.ts"},{"uid":"3dbea446-21","name":"transition-group.ts"},{"uid":"3dbea446-23","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"3dbea446-1":{"renderedLength":1968,"gzipLength":967,"brotliLength":0,"metaUid":"3dbea446-0"},"3dbea446-3":{"renderedLength":988,"gzipLength":509,"brotliLength":0,"metaUid":"3dbea446-2"},"3dbea446-5":{"renderedLength":6803,"gzipLength":2095,"brotliLength":0,"metaUid":"3dbea446-4"},"3dbea446-7":{"renderedLength":16149,"gzipLength":4315,"brotliLength":0,"metaUid":"3dbea446-6"},"3dbea446-9":{"renderedLength":5779,"gzipLength":2234,"brotliLength":0,"metaUid":"3dbea446-8"},"3dbea446-11":{"renderedLength":7932,"gzipLength":2325,"brotliLength":0,"metaUid":"3dbea446-10"},"3dbea446-13":{"renderedLength":7632,"gzipLength":2311,"brotliLength":0,"metaUid":"3dbea446-12"},"3dbea446-15":{"renderedLength":1447,"gzipLength":692,"brotliLength":0,"metaUid":"3dbea446-14"},"3dbea446-17":{"renderedLength":4250,"gzipLength":1760,"brotliLength":0,"metaUid":"3dbea446-16"},"3dbea446-19":{"renderedLength":4007,"gzipLength":1316,"brotliLength":0,"metaUid":"3dbea446-18"},"3dbea446-21":{"renderedLength":6362,"gzipLength":1937,"brotliLength":0,"metaUid":"3dbea446-20"},"3dbea446-23":{"renderedLength":811,"gzipLength":491,"brotliLength":0,"metaUid":"3dbea446-22"}},"nodeMetas":{"3dbea446-0":{"id":"/src/delegate.ts","moduleParts":{"index.js":"3dbea446-1"},"imported":[{"uid":"3dbea446-24"}],"importedBy":[{"uid":"3dbea446-22"},{"uid":"3dbea446-12"},{"uid":"3dbea446-8"}]},"3dbea446-2":{"id":"/src/hydration-debug.ts","moduleParts":{"index.js":"3dbea446-3"},"imported":[],"importedBy":[{"uid":"3dbea446-22"},{"uid":"3dbea446-12"}]},"3dbea446-4":{"id":"/src/devtools.ts","moduleParts":{"index.js":"3dbea446-5"},"imported":[],"importedBy":[{"uid":"3dbea446-22"},{"uid":"3dbea446-10"}]},"3dbea446-6":{"id":"/src/nodes.ts","moduleParts":{"index.js":"3dbea446-7"},"imported":[{"uid":"3dbea446-24"}],"importedBy":[{"uid":"3dbea446-12"},{"uid":"3dbea446-10"}]},"3dbea446-8":{"id":"/src/props.ts","moduleParts":{"index.js":"3dbea446-9"},"imported":[{"uid":"3dbea446-24"},{"uid":"3dbea446-0"}],"importedBy":[{"uid":"3dbea446-22"},{"uid":"3dbea446-12"},{"uid":"3dbea446-10"}]},"3dbea446-10":{"id":"/src/mount.ts","moduleParts":{"index.js":"3dbea446-11"},"imported":[{"uid":"3dbea446-25"},{"uid":"3dbea446-24"},{"uid":"3dbea446-4"},{"uid":"3dbea446-6"},{"uid":"3dbea446-8"}],"importedBy":[{"uid":"3dbea446-22"},{"uid":"3dbea446-12"},{"uid":"3dbea446-14"},{"uid":"3dbea446-20"}]},"3dbea446-12":{"id":"/src/hydrate.ts","moduleParts":{"index.js":"3dbea446-13"},"imported":[{"uid":"3dbea446-25"},{"uid":"3dbea446-24"},{"uid":"3dbea446-0"},{"uid":"3dbea446-2"},{"uid":"3dbea446-10"},{"uid":"3dbea446-6"},{"uid":"3dbea446-8"}],"importedBy":[{"uid":"3dbea446-22"}]},"3dbea446-14":{"id":"/src/keep-alive.ts","moduleParts":{"index.js":"3dbea446-15"},"imported":[{"uid":"3dbea446-25"},{"uid":"3dbea446-24"},{"uid":"3dbea446-10"}],"importedBy":[{"uid":"3dbea446-22"}]},"3dbea446-16":{"id":"/src/template.ts","moduleParts":{"index.js":"3dbea446-17"},"imported":[],"importedBy":[{"uid":"3dbea446-22"}]},"3dbea446-18":{"id":"/src/transition.ts","moduleParts":{"index.js":"3dbea446-19"},"imported":[{"uid":"3dbea446-25"},{"uid":"3dbea446-24"}],"importedBy":[{"uid":"3dbea446-22"}]},"3dbea446-20":{"id":"/src/transition-group.ts","moduleParts":{"index.js":"3dbea446-21"},"imported":[{"uid":"3dbea446-25"},{"uid":"3dbea446-24"},{"uid":"3dbea446-10"}],"importedBy":[{"uid":"3dbea446-22"}]},"3dbea446-22":{"id":"/src/index.ts","moduleParts":{"index.js":"3dbea446-23"},"imported":[{"uid":"3dbea446-0"},{"uid":"3dbea446-12"},{"uid":"3dbea446-2"},{"uid":"3dbea446-14"},{"uid":"3dbea446-10"},{"uid":"3dbea446-8"},{"uid":"3dbea446-16"},{"uid":"3dbea446-18"},{"uid":"3dbea446-20"},{"uid":"3dbea446-4"}],"importedBy":[],"isEntry":true},"3dbea446-24":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"3dbea446-0"},{"uid":"3dbea446-12"},{"uid":"3dbea446-14"},{"uid":"3dbea446-10"},{"uid":"3dbea446-8"},{"uid":"3dbea446-18"},{"uid":"3dbea446-20"},{"uid":"3dbea446-6"}]},"3dbea446-25":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"3dbea446-12"},{"uid":"3dbea446-14"},{"uid":"3dbea446-10"},{"uid":"3dbea446-18"},{"uid":"3dbea446-20"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
|
|
5390
5390
|
|
|
5391
5391
|
const run = () => {
|
|
5392
5392
|
const width = window.innerWidth;
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,81 @@
|
|
|
1
|
-
import { EMPTY_PROPS, ForSymbol, Fragment, PortalSymbol, createRef, dispatchToErrorBoundary, h, onMount, onUnmount, propagateError, reportError, runWithHooks } from "@pyreon/core";
|
|
2
1
|
import { batch, effect, effectScope, renderEffect, runUntracked, setCurrentScope, signal } from "@pyreon/reactivity";
|
|
2
|
+
import { EMPTY_PROPS, ForSymbol, Fragment, PortalSymbol, createRef, dispatchToErrorBoundary, h, onMount, onUnmount, propagateError, reportError, runWithHooks } from "@pyreon/core";
|
|
3
3
|
|
|
4
|
+
//#region src/delegate.ts
|
|
5
|
+
/**
|
|
6
|
+
* Event delegation — single listener per event type on the mount container.
|
|
7
|
+
*
|
|
8
|
+
* Instead of calling addEventListener on every element, the compiler emits
|
|
9
|
+
* `el.__click = handler` (expando property). A single delegated listener on the
|
|
10
|
+
* container walks event.target up the DOM tree, checking for expandos.
|
|
11
|
+
*
|
|
12
|
+
* Benefits:
|
|
13
|
+
* - Saves ~2000 addEventListener calls for 1000 rows with 2 handlers each
|
|
14
|
+
* - Reduces memory per row (no per-element listener closure)
|
|
15
|
+
* - Faster initial mount (~0.4-0.8ms savings on 1000-row benchmarks)
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Events that are delegated (common bubbling events).
|
|
19
|
+
* Non-bubbling events (focus, blur, mouseenter, mouseleave, load, error, scroll)
|
|
20
|
+
* are NOT delegated — they must use addEventListener.
|
|
21
|
+
*/
|
|
22
|
+
const DELEGATED_EVENTS = new Set([
|
|
23
|
+
"click",
|
|
24
|
+
"dblclick",
|
|
25
|
+
"contextmenu",
|
|
26
|
+
"focusin",
|
|
27
|
+
"focusout",
|
|
28
|
+
"input",
|
|
29
|
+
"change",
|
|
30
|
+
"keydown",
|
|
31
|
+
"keyup",
|
|
32
|
+
"mousedown",
|
|
33
|
+
"mouseup",
|
|
34
|
+
"mousemove",
|
|
35
|
+
"mouseover",
|
|
36
|
+
"mouseout",
|
|
37
|
+
"pointerdown",
|
|
38
|
+
"pointerup",
|
|
39
|
+
"pointermove",
|
|
40
|
+
"pointerover",
|
|
41
|
+
"pointerout",
|
|
42
|
+
"touchstart",
|
|
43
|
+
"touchend",
|
|
44
|
+
"touchmove",
|
|
45
|
+
"submit"
|
|
46
|
+
]);
|
|
47
|
+
/**
|
|
48
|
+
* Property name used on DOM elements to store delegated event handlers.
|
|
49
|
+
* Format: `__ev_{eventName}` e.g. `__ev_click`, `__ev_input`
|
|
50
|
+
*/
|
|
51
|
+
function delegatedPropName(eventName) {
|
|
52
|
+
return `__ev_${eventName}`;
|
|
53
|
+
}
|
|
54
|
+
const _delegated = /* @__PURE__ */ new WeakSet();
|
|
55
|
+
/**
|
|
56
|
+
* Install delegation listeners on a container element.
|
|
57
|
+
* Called once from mount(). Idempotent — safe to call multiple times.
|
|
58
|
+
*/
|
|
59
|
+
function setupDelegation(container) {
|
|
60
|
+
if (_delegated.has(container)) return;
|
|
61
|
+
_delegated.add(container);
|
|
62
|
+
for (const eventName of DELEGATED_EVENTS) {
|
|
63
|
+
const prop = delegatedPropName(eventName);
|
|
64
|
+
container.addEventListener(eventName, (e) => {
|
|
65
|
+
let el = e.target;
|
|
66
|
+
while (el && el !== container) {
|
|
67
|
+
const handler = el[prop];
|
|
68
|
+
if (handler) {
|
|
69
|
+
batch(() => handler(e));
|
|
70
|
+
if (e.cancelBubble) break;
|
|
71
|
+
}
|
|
72
|
+
el = el.parentElement;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//#endregion
|
|
4
79
|
//#region src/hydration-debug.ts
|
|
5
80
|
/**
|
|
6
81
|
* Hydration mismatch warnings.
|
|
@@ -908,6 +983,13 @@ function applyProp(el, key, value) {
|
|
|
908
983
|
if (EVENT_RE.test(key)) {
|
|
909
984
|
const eventName = key[2]?.toLowerCase() + key.slice(3);
|
|
910
985
|
const handler = value;
|
|
986
|
+
if (DELEGATED_EVENTS.has(eventName)) {
|
|
987
|
+
const prop = delegatedPropName(eventName);
|
|
988
|
+
el[prop] = (e) => batch(() => handler(e));
|
|
989
|
+
return () => {
|
|
990
|
+
el[prop] = void 0;
|
|
991
|
+
};
|
|
992
|
+
}
|
|
911
993
|
const batched = (e) => batch(() => handler(e));
|
|
912
994
|
el.addEventListener(eventName, batched);
|
|
913
995
|
return () => el.removeEventListener(eventName, batched);
|
|
@@ -1239,7 +1321,7 @@ function firstReal(initialNode) {
|
|
|
1239
1321
|
node = node.nextSibling;
|
|
1240
1322
|
continue;
|
|
1241
1323
|
}
|
|
1242
|
-
if (node.nodeType === Node.TEXT_NODE && node.data
|
|
1324
|
+
if (node.nodeType === Node.TEXT_NODE && isWhitespaceOnly(node.data)) {
|
|
1243
1325
|
node = node.nextSibling;
|
|
1244
1326
|
continue;
|
|
1245
1327
|
}
|
|
@@ -1247,6 +1329,14 @@ function firstReal(initialNode) {
|
|
|
1247
1329
|
}
|
|
1248
1330
|
return null;
|
|
1249
1331
|
}
|
|
1332
|
+
/** Check if a string is whitespace-only without allocating a trimmed copy. */
|
|
1333
|
+
function isWhitespaceOnly(s) {
|
|
1334
|
+
for (let i = 0; i < s.length; i++) {
|
|
1335
|
+
const c = s.charCodeAt(i);
|
|
1336
|
+
if (c !== 32 && c !== 9 && c !== 10 && c !== 13 && c !== 12) return false;
|
|
1337
|
+
}
|
|
1338
|
+
return true;
|
|
1339
|
+
}
|
|
1250
1340
|
/** Advance past a node, skipping whitespace-only text and comments */
|
|
1251
1341
|
function nextReal(node) {
|
|
1252
1342
|
return firstReal(node.nextSibling);
|
|
@@ -1273,11 +1363,10 @@ function hydrateReactiveChild(child, domNode, parent, anchor, path) {
|
|
|
1273
1363
|
function hydrateReactiveText(child, domNode, parent, anchor, path) {
|
|
1274
1364
|
if (domNode?.nodeType === Node.TEXT_NODE) {
|
|
1275
1365
|
const textNode = domNode;
|
|
1276
|
-
|
|
1366
|
+
return [renderEffect(() => {
|
|
1277
1367
|
const v = child();
|
|
1278
1368
|
textNode.data = v == null ? "" : String(v);
|
|
1279
|
-
});
|
|
1280
|
-
return [() => e.dispose(), nextReal(domNode)];
|
|
1369
|
+
}), nextReal(domNode)];
|
|
1281
1370
|
}
|
|
1282
1371
|
warnHydrationMismatch("text", "TextNode", domNode?.nodeType ?? "null", `${path} > reactive`);
|
|
1283
1372
|
return [mountChild(child, parent, anchor), domNode];
|
|
@@ -1337,6 +1426,8 @@ function hydrateElement(vnode, domNode, parent, anchor, path = "root") {
|
|
|
1337
1426
|
return [mountChild(vnode, parent, anchor), domNode];
|
|
1338
1427
|
}
|
|
1339
1428
|
function hydrateChildren(children, domNode, parent, anchor, path = "root") {
|
|
1429
|
+
if (children.length === 0) return [noop, domNode];
|
|
1430
|
+
if (children.length === 1) return hydrateChild(children[0], domNode, parent, anchor, path);
|
|
1340
1431
|
const cleanups = [];
|
|
1341
1432
|
let cursor = domNode;
|
|
1342
1433
|
for (const child of children) {
|
|
@@ -1421,6 +1512,7 @@ function hydrateComponent(vnode, domNode, parent, anchor, path = "root") {
|
|
|
1421
1512
|
* const unmount = hydrateRoot(document.getElementById("app")!, h(App, null))
|
|
1422
1513
|
*/
|
|
1423
1514
|
function hydrateRoot(container, vnode) {
|
|
1515
|
+
setupDelegation(container);
|
|
1424
1516
|
const [cleanup] = hydrateChild(vnode, firstReal(container.firstChild), container, null);
|
|
1425
1517
|
return cleanup;
|
|
1426
1518
|
}
|
|
@@ -1516,6 +1608,50 @@ function createTemplate(html, bind) {
|
|
|
1516
1608
|
};
|
|
1517
1609
|
};
|
|
1518
1610
|
}
|
|
1611
|
+
/**
|
|
1612
|
+
* Compiler-emitted direct text binding for single-signal text nodes.
|
|
1613
|
+
*
|
|
1614
|
+
* When the compiler detects `{signal()}` as the only reactive expression
|
|
1615
|
+
* in a text binding, it emits `_bindText(signal, textNode)` instead of
|
|
1616
|
+
* `_bind(() => { textNode.data = signal() })`.
|
|
1617
|
+
*
|
|
1618
|
+
* This bypasses the effect system entirely:
|
|
1619
|
+
* - No deps array allocation
|
|
1620
|
+
* - No withTracking / setDepsCollector overhead
|
|
1621
|
+
* - No `run` closure
|
|
1622
|
+
* - Signal.subscribe is used directly (O(1) subscribe + unsubscribe)
|
|
1623
|
+
*
|
|
1624
|
+
* @param source - A signal (anything with `._v` and `.direct`)
|
|
1625
|
+
* @param node - The Text node to update
|
|
1626
|
+
*/
|
|
1627
|
+
function _bindText(source, node) {
|
|
1628
|
+
const update = () => {
|
|
1629
|
+
const v = source._v;
|
|
1630
|
+
node.data = v == null || v === false ? "" : String(v);
|
|
1631
|
+
};
|
|
1632
|
+
update();
|
|
1633
|
+
return source.direct(update);
|
|
1634
|
+
}
|
|
1635
|
+
/**
|
|
1636
|
+
* Compiler-emitted direct binding for single-signal reactive expressions.
|
|
1637
|
+
*
|
|
1638
|
+
* Like _bindText but for arbitrary DOM updates (attributes, className, style).
|
|
1639
|
+
* When the compiler detects that a reactive expression depends on exactly one
|
|
1640
|
+
* signal call, it emits `_bindDirect(signal, updater)` instead of
|
|
1641
|
+
* `_bind(() => { updater() })`.
|
|
1642
|
+
*
|
|
1643
|
+
* Uses signal.direct() for zero-overhead registration:
|
|
1644
|
+
* - Flat array instead of Set (no hashing)
|
|
1645
|
+
* - Index-based disposal (no Set.delete)
|
|
1646
|
+
* - No deps array, no withTracking, no run closure
|
|
1647
|
+
*
|
|
1648
|
+
* @param source - A signal (anything with `._v` and `.direct`)
|
|
1649
|
+
* @param updater - Function that reads `source._v` and applies the DOM update
|
|
1650
|
+
*/
|
|
1651
|
+
function _bindDirect(source, updater) {
|
|
1652
|
+
updater(source._v);
|
|
1653
|
+
return source.direct(() => updater(source._v));
|
|
1654
|
+
}
|
|
1519
1655
|
const _tplCache = /* @__PURE__ */ new Map();
|
|
1520
1656
|
/**
|
|
1521
1657
|
* Compiler-emitted template instantiation.
|
|
@@ -1898,6 +2034,7 @@ const __DEV__ = typeof process !== "undefined" && process.env.NODE_ENV !== "prod
|
|
|
1898
2034
|
function mount(root, container) {
|
|
1899
2035
|
if (__DEV__ && container == null) throw new Error("[pyreon] mount() called with a null/undefined container. Make sure the element exists in the DOM, e.g. document.getElementById(\"app\")");
|
|
1900
2036
|
installDevTools();
|
|
2037
|
+
setupDelegation(container);
|
|
1901
2038
|
container.innerHTML = "";
|
|
1902
2039
|
return mountChild(root, container, null);
|
|
1903
2040
|
}
|
|
@@ -1905,5 +2042,5 @@ function mount(root, container) {
|
|
|
1905
2042
|
const render = mount;
|
|
1906
2043
|
|
|
1907
2044
|
//#endregion
|
|
1908
|
-
export { KeepAlive, Transition, TransitionGroup, _tpl, applyProp, applyProps, createTemplate, disableHydrationWarnings, enableHydrationWarnings, hydrateRoot, mount, mountChild, render, sanitizeHtml, setSanitizer };
|
|
2045
|
+
export { DELEGATED_EVENTS, KeepAlive, Transition, TransitionGroup, _bindDirect, _bindText, _tpl, applyProp, applyProps, createTemplate, delegatedPropName, disableHydrationWarnings, enableHydrationWarnings, hydrateRoot, mount, mountChild, render, sanitizeHtml, setSanitizer, setupDelegation };
|
|
1909
2046
|
//# sourceMappingURL=index.js.map
|