@pyreon/runtime-dom 0.12.12 → 0.12.14
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/README.md +13 -0
- package/lib/analysis/index.js.html +1 -1
- package/lib/index.js +63 -12
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +12 -6
- package/src/hydrate.ts +35 -5
- package/src/mount.ts +8 -2
- package/src/props.ts +54 -10
- package/src/tests/callback-ref-unmount.browser.test.ts +62 -0
- package/src/tests/callback-ref-unmount.test.ts +52 -0
- package/src/tests/coverage.test.ts +4 -2
- package/src/tests/dev-gate-pattern.test.ts +40 -0
- package/src/tests/dev-gate-treeshake.test.ts +262 -0
- package/src/tests/mount.test.ts +6 -5
- package/src/tests/props.test.ts +5 -4
- package/src/tests/runtime-dom.browser.test.ts +295 -0
- package/src/tests/ssr-xss-round-trip.browser.test.ts +93 -0
- package/src/tests/style-key-removal.browser.test.ts +54 -0
- package/src/tests/style-key-removal.test.ts +88 -0
- package/src/tests/transition-timeout-leak.test.ts +81 -0
- package/src/tests/verified-correct-probes.test.ts +56 -0
- package/src/transition.ts +20 -2
package/README.md
CHANGED
|
@@ -115,6 +115,19 @@ const App = () => (
|
|
|
115
115
|
|
|
116
116
|
`TransitionProps`, `TransitionGroupProps`, `KeepAliveProps`, `SanitizeFn`, `DevtoolsComponentEntry`, `PyreonDevtools`
|
|
117
117
|
|
|
118
|
+
## Dev-mode warnings — bundler tree-shake
|
|
119
|
+
|
|
120
|
+
Dev warnings are gated on `import.meta.env?.DEV`. Tree-shake behavior depends on both the source pattern and the consumer bundler:
|
|
121
|
+
|
|
122
|
+
| Source pattern | Vite prod | Raw esbuild prod | Test |
|
|
123
|
+
| --- | --- | --- | --- |
|
|
124
|
+
| `if (!import.meta.env?.DEV) return` (inline early-return) | tree-shaken | tree-shaken | `flow/src/tests/integration.test.ts` (esbuild) |
|
|
125
|
+
| `const __DEV__ = ...; if (__DEV__) ...` | tree-shaken | mostly tree-shaken | `runtime-dom/src/tests/dev-gate-treeshake.test.ts` (Vite) |
|
|
126
|
+
| `const __DEV__ = ...; __DEV__ && cond && warn(...)` (chained &&) | tree-shaken | runtime-gated only | `runtime-dom/.../dev-gate-treeshake.test.ts` (Vite + non-Vite runtime smoke) |
|
|
127
|
+
| `typeof process !== 'undefined'` | dead in browser | dead in browser | `pyreon/no-process-dev-gate` lint rule |
|
|
128
|
+
|
|
129
|
+
Vite is Pyreon's primary supported bundler. Non-Vite consumers (webpack, bunchee, raw esbuild) using the chained `&&` form may retain warning strings as data, but the runtime gate evaluates to `false` when `import.meta.env.DEV` is undefined — warnings don't fire. Only a small bundle-size cost.
|
|
130
|
+
|
|
118
131
|
## License
|
|
119
132
|
|
|
120
133
|
MIT
|
|
@@ -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":"684913b0-1","name":"delegate.ts"},{"uid":"684913b0-3","name":"hydration-debug.ts"},{"uid":"684913b0-5","name":"devtools.ts"},{"uid":"684913b0-7","name":"nodes.ts"},{"uid":"684913b0-9","name":"props.ts"},{"uid":"684913b0-11","name":"mount.ts"},{"uid":"684913b0-13","name":"hydrate.ts"},{"uid":"684913b0-15","name":"keep-alive.ts"},{"uid":"684913b0-17","name":"template.ts"},{"uid":"684913b0-19","name":"transition.ts"},{"uid":"684913b0-21","name":"transition-group.ts"},{"uid":"684913b0-23","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"684913b0-1":{"renderedLength":1990,"gzipLength":976,"brotliLength":0,"metaUid":"684913b0-0"},"684913b0-3":{"renderedLength":662,"gzipLength":370,"brotliLength":0,"metaUid":"684913b0-2"},"684913b0-5":{"renderedLength":6803,"gzipLength":2095,"brotliLength":0,"metaUid":"684913b0-4"},"684913b0-7":{"renderedLength":16207,"gzipLength":4334,"brotliLength":0,"metaUid":"684913b0-6"},"684913b0-9":{"renderedLength":6783,"gzipLength":2530,"brotliLength":0,"metaUid":"684913b0-8"},"684913b0-11":{"renderedLength":11649,"gzipLength":3717,"brotliLength":0,"metaUid":"684913b0-10"},"684913b0-13":{"renderedLength":8239,"gzipLength":2455,"brotliLength":0,"metaUid":"684913b0-12"},"684913b0-15":{"renderedLength":1473,"gzipLength":701,"brotliLength":0,"metaUid":"684913b0-14"},"684913b0-17":{"renderedLength":5319,"gzipLength":2069,"brotliLength":0,"metaUid":"684913b0-16"},"684913b0-19":{"renderedLength":4384,"gzipLength":1356,"brotliLength":0,"metaUid":"684913b0-18"},"684913b0-21":{"renderedLength":6362,"gzipLength":1937,"brotliLength":0,"metaUid":"684913b0-20"},"684913b0-23":{"renderedLength":769,"gzipLength":474,"brotliLength":0,"metaUid":"684913b0-22"}},"nodeMetas":{"684913b0-0":{"id":"/src/delegate.ts","moduleParts":{"index.js":"684913b0-1"},"imported":[{"uid":"684913b0-24"}],"importedBy":[{"uid":"684913b0-22"},{"uid":"684913b0-12"},{"uid":"684913b0-8"}]},"684913b0-2":{"id":"/src/hydration-debug.ts","moduleParts":{"index.js":"684913b0-3"},"imported":[],"importedBy":[{"uid":"684913b0-22"},{"uid":"684913b0-12"}]},"684913b0-4":{"id":"/src/devtools.ts","moduleParts":{"index.js":"684913b0-5"},"imported":[],"importedBy":[{"uid":"684913b0-22"},{"uid":"684913b0-10"}]},"684913b0-6":{"id":"/src/nodes.ts","moduleParts":{"index.js":"684913b0-7"},"imported":[{"uid":"684913b0-25"},{"uid":"684913b0-24"}],"importedBy":[{"uid":"684913b0-12"},{"uid":"684913b0-10"}]},"684913b0-8":{"id":"/src/props.ts","moduleParts":{"index.js":"684913b0-9"},"imported":[{"uid":"684913b0-25"},{"uid":"684913b0-24"},{"uid":"684913b0-0"}],"importedBy":[{"uid":"684913b0-22"},{"uid":"684913b0-12"},{"uid":"684913b0-10"}]},"684913b0-10":{"id":"/src/mount.ts","moduleParts":{"index.js":"684913b0-11"},"imported":[{"uid":"684913b0-25"},{"uid":"684913b0-24"},{"uid":"684913b0-4"},{"uid":"684913b0-6"},{"uid":"684913b0-8"}],"importedBy":[{"uid":"684913b0-22"},{"uid":"684913b0-12"},{"uid":"684913b0-14"},{"uid":"684913b0-16"},{"uid":"684913b0-20"}]},"684913b0-12":{"id":"/src/hydrate.ts","moduleParts":{"index.js":"684913b0-13"},"imported":[{"uid":"684913b0-25"},{"uid":"684913b0-24"},{"uid":"684913b0-0"},{"uid":"684913b0-2"},{"uid":"684913b0-10"},{"uid":"684913b0-6"},{"uid":"684913b0-8"}],"importedBy":[{"uid":"684913b0-22"}]},"684913b0-14":{"id":"/src/keep-alive.ts","moduleParts":{"index.js":"684913b0-15"},"imported":[{"uid":"684913b0-25"},{"uid":"684913b0-24"},{"uid":"684913b0-10"}],"importedBy":[{"uid":"684913b0-22"}]},"684913b0-16":{"id":"/src/template.ts","moduleParts":{"index.js":"684913b0-17"},"imported":[{"uid":"684913b0-24"},{"uid":"684913b0-10"}],"importedBy":[{"uid":"684913b0-22"}]},"684913b0-18":{"id":"/src/transition.ts","moduleParts":{"index.js":"684913b0-19"},"imported":[{"uid":"684913b0-25"},{"uid":"684913b0-24"}],"importedBy":[{"uid":"684913b0-22"}]},"684913b0-20":{"id":"/src/transition-group.ts","moduleParts":{"index.js":"684913b0-21"},"imported":[{"uid":"684913b0-25"},{"uid":"684913b0-24"},{"uid":"684913b0-10"}],"importedBy":[{"uid":"684913b0-22"}]},"684913b0-22":{"id":"/src/index.ts","moduleParts":{"index.js":"684913b0-23"},"imported":[{"uid":"684913b0-0"},{"uid":"684913b0-12"},{"uid":"684913b0-2"},{"uid":"684913b0-14"},{"uid":"684913b0-10"},{"uid":"684913b0-8"},{"uid":"684913b0-16"},{"uid":"684913b0-18"},{"uid":"684913b0-20"},{"uid":"684913b0-4"}],"importedBy":[],"isEntry":true},"684913b0-24":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"684913b0-0"},{"uid":"684913b0-12"},{"uid":"684913b0-14"},{"uid":"684913b0-10"},{"uid":"684913b0-8"},{"uid":"684913b0-16"},{"uid":"684913b0-18"},{"uid":"684913b0-20"},{"uid":"684913b0-6"}]},"684913b0-25":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"684913b0-12"},{"uid":"684913b0-14"},{"uid":"684913b0-10"},{"uid":"684913b0-8"},{"uid":"684913b0-18"},{"uid":"684913b0-20"},{"uid":"684913b0-6"}]}},"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
|
@@ -978,10 +978,10 @@ function applyProps(el, props) {
|
|
|
978
978
|
*/
|
|
979
979
|
function applyEventProp(el, key, value) {
|
|
980
980
|
if (typeof value !== "function") {
|
|
981
|
-
if (__DEV__$3) console.warn(`[Pyreon] Event handler "${key}" received a non-function value (${typeof value}). Expected a function. Did you mean ${key}={() => ...}?`);
|
|
981
|
+
if (__DEV__$3 && value != null) console.warn(`[Pyreon] Event handler "${key}" received a non-function value (${typeof value}). Expected a function. Did you mean ${key}={() => ...}?`);
|
|
982
982
|
return null;
|
|
983
983
|
}
|
|
984
|
-
const eventName = key[2]?.toLowerCase() + key.slice(3);
|
|
984
|
+
const eventName = (key[2]?.toLowerCase() + key.slice(3)).toLowerCase();
|
|
985
985
|
const handler = value;
|
|
986
986
|
if (DELEGATED_EVENTS.has(eventName)) {
|
|
987
987
|
const prop = delegatedPropName(eventName);
|
|
@@ -1002,7 +1002,6 @@ function applyProp(el, key, value) {
|
|
|
1002
1002
|
return null;
|
|
1003
1003
|
}
|
|
1004
1004
|
if (key === "dangerouslySetInnerHTML") {
|
|
1005
|
-
if (__DEV__$3) console.warn("[Pyreon] dangerouslySetInnerHTML bypasses sanitization. Ensure the HTML is trusted.");
|
|
1006
1005
|
el.innerHTML = value.__html;
|
|
1007
1006
|
return null;
|
|
1008
1007
|
}
|
|
@@ -1020,15 +1019,36 @@ const URL_ATTRS = new Set([
|
|
|
1020
1019
|
"data"
|
|
1021
1020
|
]);
|
|
1022
1021
|
const UNSAFE_URL_RE = /^\s*(?:javascript|data):/i;
|
|
1022
|
+
const _prevStyleKeys = /* @__PURE__ */ new WeakMap();
|
|
1023
1023
|
/** Apply a style prop (string or object). */
|
|
1024
1024
|
function applyStyleProp(el, value) {
|
|
1025
|
-
if (typeof value === "string")
|
|
1026
|
-
|
|
1025
|
+
if (typeof value === "string") {
|
|
1026
|
+
el.style.cssText = value;
|
|
1027
|
+
_prevStyleKeys.delete(el);
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
const prev = _prevStyleKeys.get(el);
|
|
1031
|
+
if (value == null) {
|
|
1032
|
+
if (prev) {
|
|
1033
|
+
for (const propName of prev) el.style.removeProperty(propName);
|
|
1034
|
+
_prevStyleKeys.delete(el);
|
|
1035
|
+
}
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
if (typeof value === "object") {
|
|
1027
1039
|
const obj = value;
|
|
1040
|
+
const next = /* @__PURE__ */ new Set();
|
|
1028
1041
|
for (const k in obj) {
|
|
1042
|
+
const propName = k.startsWith("--") ? k : toKebabCase(k);
|
|
1043
|
+
next.add(propName);
|
|
1029
1044
|
const css = normalizeStyleValue(k, obj[k]);
|
|
1030
|
-
el.style.setProperty(
|
|
1045
|
+
el.style.setProperty(propName, css);
|
|
1046
|
+
}
|
|
1047
|
+
if (prev) {
|
|
1048
|
+
for (const propName of prev) if (!next.has(propName)) el.style.removeProperty(propName);
|
|
1031
1049
|
}
|
|
1050
|
+
if (next.size === 0) _prevStyleKeys.delete(el);
|
|
1051
|
+
else _prevStyleKeys.set(el, next);
|
|
1032
1052
|
}
|
|
1033
1053
|
}
|
|
1034
1054
|
function applyClassProp(el, value) {
|
|
@@ -1300,13 +1320,15 @@ function mountElement(vnode, parent, anchor) {
|
|
|
1300
1320
|
};
|
|
1301
1321
|
const refToClean = ref;
|
|
1302
1322
|
return () => {
|
|
1303
|
-
if (refToClean
|
|
1323
|
+
if (refToClean) if (typeof refToClean === "function") refToClean(null);
|
|
1324
|
+
else refToClean.current = null;
|
|
1304
1325
|
if (propCleanup) propCleanup();
|
|
1305
1326
|
childCleanup();
|
|
1306
1327
|
};
|
|
1307
1328
|
}
|
|
1308
1329
|
return () => {
|
|
1309
|
-
if (ref
|
|
1330
|
+
if (ref) if (typeof ref === "function") ref(null);
|
|
1331
|
+
else ref.current = null;
|
|
1310
1332
|
if (propCleanup) propCleanup();
|
|
1311
1333
|
childCleanup();
|
|
1312
1334
|
const p = el.parentNode;
|
|
@@ -1501,7 +1523,9 @@ function hydrateReactiveChild(child, domNode, parent, anchor, path) {
|
|
|
1501
1523
|
const initial = runUntracked(child);
|
|
1502
1524
|
if (initial == null || initial === false) return [mountReactive(child, parent, insertMarker(parent, domNode, "pyreon"), mountChild), domNode];
|
|
1503
1525
|
if (typeof initial === "string" || typeof initial === "number" || typeof initial === "boolean") return hydrateReactiveText(child, domNode, parent, anchor, path);
|
|
1504
|
-
|
|
1526
|
+
const next = domNode ? nextReal(domNode) : null;
|
|
1527
|
+
if (domNode && domNode.parentNode) domNode.parentNode.removeChild(domNode);
|
|
1528
|
+
return [mountReactive(child, parent, insertMarker(parent, next, "pyreon"), mountChild), next];
|
|
1505
1529
|
}
|
|
1506
1530
|
/** Hydrate a reactive text binding against an existing text node. */
|
|
1507
1531
|
function hydrateReactiveText(child, domNode, parent, anchor, path) {
|
|
@@ -1544,6 +1568,18 @@ function hydrateChild(child, domNode, parent, anchor, path = "root") {
|
|
|
1544
1568
|
warnHydrationMismatch("text", "TextNode", domNode?.nodeType ?? "null", `${path} > text`);
|
|
1545
1569
|
return [mountChild(child, parent, anchor), domNode];
|
|
1546
1570
|
}
|
|
1571
|
+
if (child?.__isNative === true) {
|
|
1572
|
+
const native = child;
|
|
1573
|
+
const next = domNode ? nextReal(domNode) : null;
|
|
1574
|
+
if (domNode && domNode.parentNode) domNode.parentNode.replaceChild(native.el, domNode);
|
|
1575
|
+
else parent.insertBefore(native.el, anchor);
|
|
1576
|
+
const cleanup = () => {
|
|
1577
|
+
native.cleanup?.();
|
|
1578
|
+
const p = native.el.parentNode;
|
|
1579
|
+
if (p && p.isConnected !== false) p.removeChild(native.el);
|
|
1580
|
+
};
|
|
1581
|
+
return [cleanup, next];
|
|
1582
|
+
}
|
|
1547
1583
|
return hydrateVNode(child, domNode, parent, anchor, path);
|
|
1548
1584
|
}
|
|
1549
1585
|
function hydrateElement(vnode, domNode, parent, anchor, path = "root") {
|
|
@@ -1560,7 +1596,8 @@ function hydrateElement(vnode, domNode, parent, anchor, path = "root") {
|
|
|
1560
1596
|
if (ref) if (typeof ref === "function") ref(el);
|
|
1561
1597
|
else ref.current = el;
|
|
1562
1598
|
const cleanup = () => {
|
|
1563
|
-
if (ref
|
|
1599
|
+
if (ref) if (typeof ref === "function") ref(null);
|
|
1600
|
+
else ref.current = null;
|
|
1564
1601
|
for (const c of cleanups) c();
|
|
1565
1602
|
el.remove();
|
|
1566
1603
|
};
|
|
@@ -1918,15 +1955,20 @@ function Transition(props) {
|
|
|
1918
1955
|
requestAnimationFrame(() => {
|
|
1919
1956
|
el.classList.remove(cls.ef);
|
|
1920
1957
|
el.classList.add(cls.et);
|
|
1958
|
+
let safetyTimer = null;
|
|
1921
1959
|
const done = () => {
|
|
1922
1960
|
el.removeEventListener("transitionend", done);
|
|
1923
1961
|
el.removeEventListener("animationend", done);
|
|
1962
|
+
if (safetyTimer !== null) {
|
|
1963
|
+
clearTimeout(safetyTimer);
|
|
1964
|
+
safetyTimer = null;
|
|
1965
|
+
}
|
|
1924
1966
|
el.classList.remove(cls.ea, cls.et);
|
|
1925
1967
|
props.onAfterEnter?.(el);
|
|
1926
1968
|
};
|
|
1927
1969
|
el.addEventListener("transitionend", done, { once: true });
|
|
1928
1970
|
el.addEventListener("animationend", done, { once: true });
|
|
1929
|
-
setTimeout(done, 5e3);
|
|
1971
|
+
safetyTimer = setTimeout(done, 5e3);
|
|
1930
1972
|
});
|
|
1931
1973
|
};
|
|
1932
1974
|
const applyLeave = (el) => {
|
|
@@ -1936,9 +1978,14 @@ function Transition(props) {
|
|
|
1936
1978
|
requestAnimationFrame(() => {
|
|
1937
1979
|
el.classList.remove(cls.lf);
|
|
1938
1980
|
el.classList.add(cls.lt);
|
|
1981
|
+
let safetyTimer = null;
|
|
1939
1982
|
const done = () => {
|
|
1940
1983
|
el.removeEventListener("transitionend", done);
|
|
1941
1984
|
el.removeEventListener("animationend", done);
|
|
1985
|
+
if (safetyTimer !== null) {
|
|
1986
|
+
clearTimeout(safetyTimer);
|
|
1987
|
+
safetyTimer = null;
|
|
1988
|
+
}
|
|
1942
1989
|
el.classList.remove(cls.la, cls.lt);
|
|
1943
1990
|
pendingLeaveCancel = null;
|
|
1944
1991
|
isMounted.set(false);
|
|
@@ -1947,11 +1994,15 @@ function Transition(props) {
|
|
|
1947
1994
|
pendingLeaveCancel = () => {
|
|
1948
1995
|
el.removeEventListener("transitionend", done);
|
|
1949
1996
|
el.removeEventListener("animationend", done);
|
|
1997
|
+
if (safetyTimer !== null) {
|
|
1998
|
+
clearTimeout(safetyTimer);
|
|
1999
|
+
safetyTimer = null;
|
|
2000
|
+
}
|
|
1950
2001
|
el.classList.remove(cls.lf, cls.la, cls.lt);
|
|
1951
2002
|
};
|
|
1952
2003
|
el.addEventListener("transitionend", done, { once: true });
|
|
1953
2004
|
el.addEventListener("animationend", done, { once: true });
|
|
1954
|
-
setTimeout(done, 5e3);
|
|
2005
|
+
safetyTimer = setTimeout(done, 5e3);
|
|
1955
2006
|
});
|
|
1956
2007
|
};
|
|
1957
2008
|
const handleVisibilityChange = (visible) => {
|