@pyreon/runtime-dom 0.2.1 → 0.3.1
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 +140 -13
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +115 -13
- 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/env.d.ts +6 -0
- package/src/hydrate.ts +23 -4
- package/src/hydration-debug.ts +3 -1
- 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":"5f6bae27-1","name":"delegate.ts"},{"uid":"5f6bae27-3","name":"hydration-debug.ts"},{"uid":"5f6bae27-5","name":"devtools.ts"},{"uid":"5f6bae27-7","name":"nodes.ts"},{"uid":"5f6bae27-9","name":"props.ts"},{"uid":"5f6bae27-11","name":"mount.ts"},{"uid":"5f6bae27-13","name":"hydrate.ts"},{"uid":"5f6bae27-15","name":"keep-alive.ts"},{"uid":"5f6bae27-17","name":"template.ts"},{"uid":"5f6bae27-19","name":"transition.ts"},{"uid":"5f6bae27-21","name":"transition-group.ts"},{"uid":"5f6bae27-23","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"5f6bae27-1":{"renderedLength":1968,"gzipLength":967,"brotliLength":0,"metaUid":"5f6bae27-0"},"5f6bae27-3":{"renderedLength":704,"gzipLength":397,"brotliLength":0,"metaUid":"5f6bae27-2"},"5f6bae27-5":{"renderedLength":6803,"gzipLength":2095,"brotliLength":0,"metaUid":"5f6bae27-4"},"5f6bae27-7":{"renderedLength":16149,"gzipLength":4315,"brotliLength":0,"metaUid":"5f6bae27-6"},"5f6bae27-9":{"renderedLength":5779,"gzipLength":2234,"brotliLength":0,"metaUid":"5f6bae27-8"},"5f6bae27-11":{"renderedLength":7932,"gzipLength":2325,"brotliLength":0,"metaUid":"5f6bae27-10"},"5f6bae27-13":{"renderedLength":7632,"gzipLength":2311,"brotliLength":0,"metaUid":"5f6bae27-12"},"5f6bae27-15":{"renderedLength":1447,"gzipLength":692,"brotliLength":0,"metaUid":"5f6bae27-14"},"5f6bae27-17":{"renderedLength":4250,"gzipLength":1760,"brotliLength":0,"metaUid":"5f6bae27-16"},"5f6bae27-19":{"renderedLength":4007,"gzipLength":1316,"brotliLength":0,"metaUid":"5f6bae27-18"},"5f6bae27-21":{"renderedLength":6362,"gzipLength":1937,"brotliLength":0,"metaUid":"5f6bae27-20"},"5f6bae27-23":{"renderedLength":811,"gzipLength":491,"brotliLength":0,"metaUid":"5f6bae27-22"}},"nodeMetas":{"5f6bae27-0":{"id":"/src/delegate.ts","moduleParts":{"index.js":"5f6bae27-1"},"imported":[{"uid":"5f6bae27-24"}],"importedBy":[{"uid":"5f6bae27-22"},{"uid":"5f6bae27-12"},{"uid":"5f6bae27-8"}]},"5f6bae27-2":{"id":"/src/hydration-debug.ts","moduleParts":{"index.js":"5f6bae27-3"},"imported":[],"importedBy":[{"uid":"5f6bae27-22"},{"uid":"5f6bae27-12"}]},"5f6bae27-4":{"id":"/src/devtools.ts","moduleParts":{"index.js":"5f6bae27-5"},"imported":[],"importedBy":[{"uid":"5f6bae27-22"},{"uid":"5f6bae27-10"}]},"5f6bae27-6":{"id":"/src/nodes.ts","moduleParts":{"index.js":"5f6bae27-7"},"imported":[{"uid":"5f6bae27-24"}],"importedBy":[{"uid":"5f6bae27-12"},{"uid":"5f6bae27-10"}]},"5f6bae27-8":{"id":"/src/props.ts","moduleParts":{"index.js":"5f6bae27-9"},"imported":[{"uid":"5f6bae27-24"},{"uid":"5f6bae27-0"}],"importedBy":[{"uid":"5f6bae27-22"},{"uid":"5f6bae27-12"},{"uid":"5f6bae27-10"}]},"5f6bae27-10":{"id":"/src/mount.ts","moduleParts":{"index.js":"5f6bae27-11"},"imported":[{"uid":"5f6bae27-25"},{"uid":"5f6bae27-24"},{"uid":"5f6bae27-4"},{"uid":"5f6bae27-6"},{"uid":"5f6bae27-8"}],"importedBy":[{"uid":"5f6bae27-22"},{"uid":"5f6bae27-12"},{"uid":"5f6bae27-14"},{"uid":"5f6bae27-20"}]},"5f6bae27-12":{"id":"/src/hydrate.ts","moduleParts":{"index.js":"5f6bae27-13"},"imported":[{"uid":"5f6bae27-25"},{"uid":"5f6bae27-24"},{"uid":"5f6bae27-0"},{"uid":"5f6bae27-2"},{"uid":"5f6bae27-10"},{"uid":"5f6bae27-6"},{"uid":"5f6bae27-8"}],"importedBy":[{"uid":"5f6bae27-22"}]},"5f6bae27-14":{"id":"/src/keep-alive.ts","moduleParts":{"index.js":"5f6bae27-15"},"imported":[{"uid":"5f6bae27-25"},{"uid":"5f6bae27-24"},{"uid":"5f6bae27-10"}],"importedBy":[{"uid":"5f6bae27-22"}]},"5f6bae27-16":{"id":"/src/template.ts","moduleParts":{"index.js":"5f6bae27-17"},"imported":[],"importedBy":[{"uid":"5f6bae27-22"}]},"5f6bae27-18":{"id":"/src/transition.ts","moduleParts":{"index.js":"5f6bae27-19"},"imported":[{"uid":"5f6bae27-25"},{"uid":"5f6bae27-24"}],"importedBy":[{"uid":"5f6bae27-22"}]},"5f6bae27-20":{"id":"/src/transition-group.ts","moduleParts":{"index.js":"5f6bae27-21"},"imported":[{"uid":"5f6bae27-25"},{"uid":"5f6bae27-24"},{"uid":"5f6bae27-10"}],"importedBy":[{"uid":"5f6bae27-22"}]},"5f6bae27-22":{"id":"/src/index.ts","moduleParts":{"index.js":"5f6bae27-23"},"imported":[{"uid":"5f6bae27-0"},{"uid":"5f6bae27-12"},{"uid":"5f6bae27-2"},{"uid":"5f6bae27-14"},{"uid":"5f6bae27-10"},{"uid":"5f6bae27-8"},{"uid":"5f6bae27-16"},{"uid":"5f6bae27-18"},{"uid":"5f6bae27-20"},{"uid":"5f6bae27-4"}],"importedBy":[],"isEntry":true},"5f6bae27-24":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"5f6bae27-0"},{"uid":"5f6bae27-12"},{"uid":"5f6bae27-14"},{"uid":"5f6bae27-10"},{"uid":"5f6bae27-8"},{"uid":"5f6bae27-18"},{"uid":"5f6bae27-20"},{"uid":"5f6bae27-6"}]},"5f6bae27-25":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"5f6bae27-12"},{"uid":"5f6bae27-14"},{"uid":"5f6bae27-10"},{"uid":"5f6bae27-18"},{"uid":"5f6bae27-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,17 +1,82 @@
|
|
|
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/
|
|
4
|
+
//#region src/delegate.ts
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Event delegation — single listener per event type on the mount container.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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.
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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.
|
|
14
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
|
|
79
|
+
//#region src/hydration-debug.ts
|
|
15
80
|
let _enabled = typeof process !== "undefined" && process.env.NODE_ENV !== "production";
|
|
16
81
|
function enableHydrationWarnings() {
|
|
17
82
|
_enabled = true;
|
|
@@ -908,6 +973,13 @@ function applyProp(el, key, value) {
|
|
|
908
973
|
if (EVENT_RE.test(key)) {
|
|
909
974
|
const eventName = key[2]?.toLowerCase() + key.slice(3);
|
|
910
975
|
const handler = value;
|
|
976
|
+
if (DELEGATED_EVENTS.has(eventName)) {
|
|
977
|
+
const prop = delegatedPropName(eventName);
|
|
978
|
+
el[prop] = (e) => batch(() => handler(e));
|
|
979
|
+
return () => {
|
|
980
|
+
el[prop] = void 0;
|
|
981
|
+
};
|
|
982
|
+
}
|
|
911
983
|
const batched = (e) => batch(() => handler(e));
|
|
912
984
|
el.addEventListener(eventName, batched);
|
|
913
985
|
return () => el.removeEventListener(eventName, batched);
|
|
@@ -1239,7 +1311,7 @@ function firstReal(initialNode) {
|
|
|
1239
1311
|
node = node.nextSibling;
|
|
1240
1312
|
continue;
|
|
1241
1313
|
}
|
|
1242
|
-
if (node.nodeType === Node.TEXT_NODE && node.data
|
|
1314
|
+
if (node.nodeType === Node.TEXT_NODE && isWhitespaceOnly(node.data)) {
|
|
1243
1315
|
node = node.nextSibling;
|
|
1244
1316
|
continue;
|
|
1245
1317
|
}
|
|
@@ -1247,6 +1319,14 @@ function firstReal(initialNode) {
|
|
|
1247
1319
|
}
|
|
1248
1320
|
return null;
|
|
1249
1321
|
}
|
|
1322
|
+
/** Check if a string is whitespace-only without allocating a trimmed copy. */
|
|
1323
|
+
function isWhitespaceOnly(s) {
|
|
1324
|
+
for (let i = 0; i < s.length; i++) {
|
|
1325
|
+
const c = s.charCodeAt(i);
|
|
1326
|
+
if (c !== 32 && c !== 9 && c !== 10 && c !== 13 && c !== 12) return false;
|
|
1327
|
+
}
|
|
1328
|
+
return true;
|
|
1329
|
+
}
|
|
1250
1330
|
/** Advance past a node, skipping whitespace-only text and comments */
|
|
1251
1331
|
function nextReal(node) {
|
|
1252
1332
|
return firstReal(node.nextSibling);
|
|
@@ -1273,11 +1353,10 @@ function hydrateReactiveChild(child, domNode, parent, anchor, path) {
|
|
|
1273
1353
|
function hydrateReactiveText(child, domNode, parent, anchor, path) {
|
|
1274
1354
|
if (domNode?.nodeType === Node.TEXT_NODE) {
|
|
1275
1355
|
const textNode = domNode;
|
|
1276
|
-
|
|
1356
|
+
return [renderEffect(() => {
|
|
1277
1357
|
const v = child();
|
|
1278
1358
|
textNode.data = v == null ? "" : String(v);
|
|
1279
|
-
});
|
|
1280
|
-
return [() => e.dispose(), nextReal(domNode)];
|
|
1359
|
+
}), nextReal(domNode)];
|
|
1281
1360
|
}
|
|
1282
1361
|
warnHydrationMismatch("text", "TextNode", domNode?.nodeType ?? "null", `${path} > reactive`);
|
|
1283
1362
|
return [mountChild(child, parent, anchor), domNode];
|
|
@@ -1337,6 +1416,8 @@ function hydrateElement(vnode, domNode, parent, anchor, path = "root") {
|
|
|
1337
1416
|
return [mountChild(vnode, parent, anchor), domNode];
|
|
1338
1417
|
}
|
|
1339
1418
|
function hydrateChildren(children, domNode, parent, anchor, path = "root") {
|
|
1419
|
+
if (children.length === 0) return [noop, domNode];
|
|
1420
|
+
if (children.length === 1) return hydrateChild(children[0], domNode, parent, anchor, path);
|
|
1340
1421
|
const cleanups = [];
|
|
1341
1422
|
let cursor = domNode;
|
|
1342
1423
|
for (const child of children) {
|
|
@@ -1421,6 +1502,7 @@ function hydrateComponent(vnode, domNode, parent, anchor, path = "root") {
|
|
|
1421
1502
|
* const unmount = hydrateRoot(document.getElementById("app")!, h(App, null))
|
|
1422
1503
|
*/
|
|
1423
1504
|
function hydrateRoot(container, vnode) {
|
|
1505
|
+
setupDelegation(container);
|
|
1424
1506
|
const [cleanup] = hydrateChild(vnode, firstReal(container.firstChild), container, null);
|
|
1425
1507
|
return cleanup;
|
|
1426
1508
|
}
|
|
@@ -1516,6 +1598,50 @@ function createTemplate(html, bind) {
|
|
|
1516
1598
|
};
|
|
1517
1599
|
};
|
|
1518
1600
|
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Compiler-emitted direct text binding for single-signal text nodes.
|
|
1603
|
+
*
|
|
1604
|
+
* When the compiler detects `{signal()}` as the only reactive expression
|
|
1605
|
+
* in a text binding, it emits `_bindText(signal, textNode)` instead of
|
|
1606
|
+
* `_bind(() => { textNode.data = signal() })`.
|
|
1607
|
+
*
|
|
1608
|
+
* This bypasses the effect system entirely:
|
|
1609
|
+
* - No deps array allocation
|
|
1610
|
+
* - No withTracking / setDepsCollector overhead
|
|
1611
|
+
* - No `run` closure
|
|
1612
|
+
* - Signal.subscribe is used directly (O(1) subscribe + unsubscribe)
|
|
1613
|
+
*
|
|
1614
|
+
* @param source - A signal (anything with `._v` and `.direct`)
|
|
1615
|
+
* @param node - The Text node to update
|
|
1616
|
+
*/
|
|
1617
|
+
function _bindText(source, node) {
|
|
1618
|
+
const update = () => {
|
|
1619
|
+
const v = source._v;
|
|
1620
|
+
node.data = v == null || v === false ? "" : String(v);
|
|
1621
|
+
};
|
|
1622
|
+
update();
|
|
1623
|
+
return source.direct(update);
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Compiler-emitted direct binding for single-signal reactive expressions.
|
|
1627
|
+
*
|
|
1628
|
+
* Like _bindText but for arbitrary DOM updates (attributes, className, style).
|
|
1629
|
+
* When the compiler detects that a reactive expression depends on exactly one
|
|
1630
|
+
* signal call, it emits `_bindDirect(signal, updater)` instead of
|
|
1631
|
+
* `_bind(() => { updater() })`.
|
|
1632
|
+
*
|
|
1633
|
+
* Uses signal.direct() for zero-overhead registration:
|
|
1634
|
+
* - Flat array instead of Set (no hashing)
|
|
1635
|
+
* - Index-based disposal (no Set.delete)
|
|
1636
|
+
* - No deps array, no withTracking, no run closure
|
|
1637
|
+
*
|
|
1638
|
+
* @param source - A signal (anything with `._v` and `.direct`)
|
|
1639
|
+
* @param updater - Function that reads `source._v` and applies the DOM update
|
|
1640
|
+
*/
|
|
1641
|
+
function _bindDirect(source, updater) {
|
|
1642
|
+
updater(source._v);
|
|
1643
|
+
return source.direct(() => updater(source._v));
|
|
1644
|
+
}
|
|
1519
1645
|
const _tplCache = /* @__PURE__ */ new Map();
|
|
1520
1646
|
/**
|
|
1521
1647
|
* Compiler-emitted template instantiation.
|
|
@@ -1898,6 +2024,7 @@ const __DEV__ = typeof process !== "undefined" && process.env.NODE_ENV !== "prod
|
|
|
1898
2024
|
function mount(root, container) {
|
|
1899
2025
|
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
2026
|
installDevTools();
|
|
2027
|
+
setupDelegation(container);
|
|
1901
2028
|
container.innerHTML = "";
|
|
1902
2029
|
return mountChild(root, container, null);
|
|
1903
2030
|
}
|
|
@@ -1905,5 +2032,5 @@ function mount(root, container) {
|
|
|
1905
2032
|
const render = mount;
|
|
1906
2033
|
|
|
1907
2034
|
//#endregion
|
|
1908
|
-
export { KeepAlive, Transition, TransitionGroup, _tpl, applyProp, applyProps, createTemplate, disableHydrationWarnings, enableHydrationWarnings, hydrateRoot, mount, mountChild, render, sanitizeHtml, setSanitizer };
|
|
2035
|
+
export { DELEGATED_EVENTS, KeepAlive, Transition, TransitionGroup, _bindDirect, _bindText, _tpl, applyProp, applyProps, createTemplate, delegatedPropName, disableHydrationWarnings, enableHydrationWarnings, hydrateRoot, mount, mountChild, render, sanitizeHtml, setSanitizer, setupDelegation };
|
|
1909
2036
|
//# sourceMappingURL=index.js.map
|