@pyreon/core 0.15.0 → 0.18.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/README.md +1 -0
- package/lib/analysis/index.js.html +1 -1
- package/lib/analysis/jsx-dev-runtime.js.html +1 -1
- package/lib/analysis/jsx-runtime.js.html +1 -1
- package/lib/index.js +198 -16
- package/lib/jsx-dev-runtime.js +45 -11
- package/lib/jsx-runtime.js +45 -11
- package/lib/types/index.d.ts +176 -6
- package/lib/types/jsx-dev-runtime.d.ts +16 -2
- package/lib/types/jsx-runtime.d.ts +16 -2
- package/package.json +2 -2
- package/src/defer.ts +241 -0
- package/src/for.ts +13 -1
- package/src/h.ts +16 -2
- package/src/index.ts +11 -1
- package/src/jsx-runtime.ts +46 -8
- package/src/manifest.ts +12 -4
- package/src/props.ts +59 -0
- package/src/tests/core.test.ts +1 -1
- package/src/tests/defer.test.ts +359 -0
- package/src/tests/extract-props-overloads.types.test.ts +135 -0
- package/src/tests/for.test.ts +23 -0
- package/src/tests/h.test.ts +21 -0
- package/src/tests/reactive-props.test.ts +71 -1
- package/src/types.ts +43 -2
package/README.md
CHANGED
|
@@ -50,6 +50,7 @@ Lifecycle hook arrays are lazy-allocated -- `LifecycleHooks.mount`/`.unmount`/`.
|
|
|
50
50
|
|
|
51
51
|
- **`makeReactiveProps(raw)`** -- Converts compiler-emitted `_rp()` wrappers into getter properties. Uses a scan-first strategy: checks for any branded reactive prop before allocating the result object. Static-only components return `raw` immediately with no allocation.
|
|
52
52
|
- **`_rp(fn)`** -- Brands a function as a reactive prop wrapper (compiler-emitted, not user-facing).
|
|
53
|
+
- **`_wrapSpread(source)`** -- Compiler-emitted helper that makes JSX spread on a component reactivity-safe. For `<Comp {...source}>`, the compiler emits `<Comp {..._wrapSpread(source)}>`. `_wrapSpread` walks `source`'s own keys without firing getters and re-brands each getter-shaped value as an `_rp` thunk pointing back at the live source. JS spread then carries the brands as plain data properties; `makeReactiveProps` converts them back to getters on the consumer side -- so reactive props survive the spread end-to-end. Fast path: when `source` has no getter descriptors, returns the source unchanged (zero cost). Not user-facing; emitted automatically by `@pyreon/compiler` for any component JSX with a spread. See `docs/patterns/reactive-spread.md` for the full contract.
|
|
53
54
|
|
|
54
55
|
### Context
|
|
55
56
|
|
|
@@ -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":"5310d1da-1","name":"lifecycle.ts"},{"uid":"5310d1da-3","name":"component.ts"},{"uid":"5310d1da-5","name":"compat-marker.ts"},{"uid":"5310d1da-7","name":"context.ts"},{"uid":"5310d1da-9","name":"h.ts"},{"uid":"5310d1da-11","name":"dynamic.ts"},{"uid":"5310d1da-13","name":"telemetry.ts"},{"uid":"5310d1da-15","name":"error-boundary.ts"},{"uid":"5310d1da-17","name":"for.ts"},{"uid":"5310d1da-19","name":"ref.ts"},{"uid":"5310d1da-21","name":"defer.ts"},{"uid":"5310d1da-23","name":"lazy.ts"},{"uid":"5310d1da-25","name":"map-array.ts"},{"uid":"5310d1da-27","name":"portal.ts"},{"uid":"5310d1da-29","name":"props.ts"},{"uid":"5310d1da-31","name":"show.ts"},{"uid":"5310d1da-33","name":"style.ts"},{"uid":"5310d1da-35","name":"suspense.ts"},{"uid":"5310d1da-37","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"5310d1da-1":{"renderedLength":3078,"gzipLength":1313,"brotliLength":0,"metaUid":"5310d1da-0"},"5310d1da-3":{"renderedLength":1471,"gzipLength":693,"brotliLength":0,"metaUid":"5310d1da-2"},"5310d1da-5":{"renderedLength":3173,"gzipLength":1409,"brotliLength":0,"metaUid":"5310d1da-4"},"5310d1da-7":{"renderedLength":3600,"gzipLength":1542,"brotliLength":0,"metaUid":"5310d1da-6"},"5310d1da-9":{"renderedLength":1813,"gzipLength":957,"brotliLength":0,"metaUid":"5310d1da-8"},"5310d1da-11":{"renderedLength":490,"gzipLength":291,"brotliLength":0,"metaUid":"5310d1da-10"},"5310d1da-13":{"renderedLength":1208,"gzipLength":633,"brotliLength":0,"metaUid":"5310d1da-12"},"5310d1da-15":{"renderedLength":1659,"gzipLength":843,"brotliLength":0,"metaUid":"5310d1da-14"},"5310d1da-17":{"renderedLength":700,"gzipLength":478,"brotliLength":0,"metaUid":"5310d1da-16"},"5310d1da-19":{"renderedLength":86,"gzipLength":98,"brotliLength":0,"metaUid":"5310d1da-18"},"5310d1da-21":{"renderedLength":3979,"gzipLength":1715,"brotliLength":0,"metaUid":"5310d1da-20"},"5310d1da-23":{"renderedLength":461,"gzipLength":273,"brotliLength":0,"metaUid":"5310d1da-22"},"5310d1da-25":{"renderedLength":1018,"gzipLength":571,"brotliLength":0,"metaUid":"5310d1da-24"},"5310d1da-27":{"renderedLength":818,"gzipLength":491,"brotliLength":0,"metaUid":"5310d1da-26"},"5310d1da-29":{"renderedLength":6310,"gzipLength":2344,"brotliLength":0,"metaUid":"5310d1da-28"},"5310d1da-31":{"renderedLength":2022,"gzipLength":854,"brotliLength":0,"metaUid":"5310d1da-30"},"5310d1da-33":{"renderedLength":1858,"gzipLength":825,"brotliLength":0,"metaUid":"5310d1da-32"},"5310d1da-35":{"renderedLength":1104,"gzipLength":614,"brotliLength":0,"metaUid":"5310d1da-34"},"5310d1da-37":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"5310d1da-36"}},"nodeMetas":{"5310d1da-0":{"id":"/src/lifecycle.ts","moduleParts":{"index.js":"5310d1da-1"},"imported":[],"importedBy":[{"uid":"5310d1da-36"},{"uid":"5310d1da-2"},{"uid":"5310d1da-6"},{"uid":"5310d1da-14"},{"uid":"5310d1da-20"}]},"5310d1da-2":{"id":"/src/component.ts","moduleParts":{"index.js":"5310d1da-3"},"imported":[{"uid":"5310d1da-0"}],"importedBy":[{"uid":"5310d1da-36"},{"uid":"5310d1da-14"}]},"5310d1da-4":{"id":"/src/compat-marker.ts","moduleParts":{"index.js":"5310d1da-5"},"imported":[],"importedBy":[{"uid":"5310d1da-36"},{"uid":"5310d1da-14"}]},"5310d1da-6":{"id":"/src/context.ts","moduleParts":{"index.js":"5310d1da-7"},"imported":[{"uid":"5310d1da-38"},{"uid":"5310d1da-0"}],"importedBy":[{"uid":"5310d1da-36"}]},"5310d1da-8":{"id":"/src/h.ts","moduleParts":{"index.js":"5310d1da-9"},"imported":[],"importedBy":[{"uid":"5310d1da-36"},{"uid":"5310d1da-10"},{"uid":"5310d1da-20"},{"uid":"5310d1da-22"},{"uid":"5310d1da-34"}]},"5310d1da-10":{"id":"/src/dynamic.ts","moduleParts":{"index.js":"5310d1da-11"},"imported":[{"uid":"5310d1da-8"}],"importedBy":[{"uid":"5310d1da-36"}]},"5310d1da-12":{"id":"/src/telemetry.ts","moduleParts":{"index.js":"5310d1da-13"},"imported":[],"importedBy":[{"uid":"5310d1da-36"},{"uid":"5310d1da-14"}]},"5310d1da-14":{"id":"/src/error-boundary.ts","moduleParts":{"index.js":"5310d1da-15"},"imported":[{"uid":"5310d1da-38"},{"uid":"5310d1da-4"},{"uid":"5310d1da-2"},{"uid":"5310d1da-0"},{"uid":"5310d1da-12"}],"importedBy":[{"uid":"5310d1da-36"}]},"5310d1da-16":{"id":"/src/for.ts","moduleParts":{"index.js":"5310d1da-17"},"imported":[],"importedBy":[{"uid":"5310d1da-36"}]},"5310d1da-18":{"id":"/src/ref.ts","moduleParts":{"index.js":"5310d1da-19"},"imported":[],"importedBy":[{"uid":"5310d1da-36"},{"uid":"5310d1da-20"}]},"5310d1da-20":{"id":"/src/defer.ts","moduleParts":{"index.js":"5310d1da-21"},"imported":[{"uid":"5310d1da-38"},{"uid":"5310d1da-8"},{"uid":"5310d1da-0"},{"uid":"5310d1da-18"}],"importedBy":[{"uid":"5310d1da-36"}]},"5310d1da-22":{"id":"/src/lazy.ts","moduleParts":{"index.js":"5310d1da-23"},"imported":[{"uid":"5310d1da-38"},{"uid":"5310d1da-8"}],"importedBy":[{"uid":"5310d1da-36"}]},"5310d1da-24":{"id":"/src/map-array.ts","moduleParts":{"index.js":"5310d1da-25"},"imported":[],"importedBy":[{"uid":"5310d1da-36"}]},"5310d1da-26":{"id":"/src/portal.ts","moduleParts":{"index.js":"5310d1da-27"},"imported":[],"importedBy":[{"uid":"5310d1da-36"}]},"5310d1da-28":{"id":"/src/props.ts","moduleParts":{"index.js":"5310d1da-29"},"imported":[],"importedBy":[{"uid":"5310d1da-36"}]},"5310d1da-30":{"id":"/src/show.ts","moduleParts":{"index.js":"5310d1da-31"},"imported":[],"importedBy":[{"uid":"5310d1da-36"}]},"5310d1da-32":{"id":"/src/style.ts","moduleParts":{"index.js":"5310d1da-33"},"imported":[],"importedBy":[{"uid":"5310d1da-36"}]},"5310d1da-34":{"id":"/src/suspense.ts","moduleParts":{"index.js":"5310d1da-35"},"imported":[{"uid":"5310d1da-8"}],"importedBy":[{"uid":"5310d1da-36"}]},"5310d1da-36":{"id":"/src/index.ts","moduleParts":{"index.js":"5310d1da-37"},"imported":[{"uid":"5310d1da-2"},{"uid":"5310d1da-4"},{"uid":"5310d1da-6"},{"uid":"5310d1da-10"},{"uid":"5310d1da-14"},{"uid":"5310d1da-16"},{"uid":"5310d1da-8"},{"uid":"5310d1da-20"},{"uid":"5310d1da-22"},{"uid":"5310d1da-0"},{"uid":"5310d1da-24"},{"uid":"5310d1da-26"},{"uid":"5310d1da-28"},{"uid":"5310d1da-18"},{"uid":"5310d1da-30"},{"uid":"5310d1da-32"},{"uid":"5310d1da-34"},{"uid":"5310d1da-12"}],"importedBy":[],"isEntry":true},"5310d1da-38":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"5310d1da-6"},{"uid":"5310d1da-14"},{"uid":"5310d1da-20"},{"uid":"5310d1da-22"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
|
|
5390
5390
|
|
|
5391
5391
|
const run = () => {
|
|
5392
5392
|
const width = window.innerWidth;
|
|
@@ -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":"jsx-dev-runtime.js","children":[{"name":"src","children":[{"uid":"
|
|
5389
|
+
const data = {"version":2,"tree":{"name":"root","children":[{"name":"jsx-dev-runtime.js","children":[{"name":"src","children":[{"uid":"430b4b1b-1","name":"h.ts"},{"uid":"430b4b1b-3","name":"jsx-runtime.ts"},{"uid":"430b4b1b-5","name":"jsx-dev-runtime.ts"}]}]}],"isRoot":true},"nodeParts":{"430b4b1b-1":{"renderedLength":1813,"gzipLength":957,"brotliLength":0,"metaUid":"430b4b1b-0"},"430b4b1b-3":{"renderedLength":1789,"gzipLength":834,"brotliLength":0,"metaUid":"430b4b1b-2"},"430b4b1b-5":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"430b4b1b-4"}},"nodeMetas":{"430b4b1b-0":{"id":"/src/h.ts","moduleParts":{"jsx-dev-runtime.js":"430b4b1b-1"},"imported":[],"importedBy":[{"uid":"430b4b1b-2"}]},"430b4b1b-2":{"id":"/src/jsx-runtime.ts","moduleParts":{"jsx-dev-runtime.js":"430b4b1b-3"},"imported":[{"uid":"430b4b1b-0"}],"importedBy":[{"uid":"430b4b1b-4"}]},"430b4b1b-4":{"id":"/src/jsx-dev-runtime.ts","moduleParts":{"jsx-dev-runtime.js":"430b4b1b-5"},"imported":[{"uid":"430b4b1b-2"}],"importedBy":[],"isEntry":true}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
|
|
5390
5390
|
|
|
5391
5391
|
const run = () => {
|
|
5392
5392
|
const width = window.innerWidth;
|
|
@@ -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":"jsx-runtime.js","children":[{"name":"src","children":[{"uid":"
|
|
5389
|
+
const data = {"version":2,"tree":{"name":"root","children":[{"name":"jsx-runtime.js","children":[{"name":"src","children":[{"uid":"36dca003-1","name":"h.ts"},{"uid":"36dca003-3","name":"jsx-runtime.ts"}]}]}],"isRoot":true},"nodeParts":{"36dca003-1":{"renderedLength":1813,"gzipLength":957,"brotliLength":0,"metaUid":"36dca003-0"},"36dca003-3":{"renderedLength":1789,"gzipLength":834,"brotliLength":0,"metaUid":"36dca003-2"}},"nodeMetas":{"36dca003-0":{"id":"/src/h.ts","moduleParts":{"jsx-runtime.js":"36dca003-1"},"imported":[],"importedBy":[{"uid":"36dca003-2"}]},"36dca003-2":{"id":"/src/jsx-runtime.ts","moduleParts":{"jsx-runtime.js":"36dca003-3"},"imported":[{"uid":"36dca003-0"}],"importedBy":[],"isEntry":true}},"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,7 +1,7 @@
|
|
|
1
|
-
import { setSnapshotCapture, signal } from "@pyreon/reactivity";
|
|
1
|
+
import { effect, setSnapshotCapture, signal } from "@pyreon/reactivity";
|
|
2
2
|
|
|
3
3
|
//#region src/lifecycle.ts
|
|
4
|
-
const __DEV__$
|
|
4
|
+
const __DEV__$4 = process.env.NODE_ENV !== "production";
|
|
5
5
|
let _current = null;
|
|
6
6
|
function setCurrentHooks(hooks) {
|
|
7
7
|
_current = hooks;
|
|
@@ -40,7 +40,7 @@ function captureCallSite() {
|
|
|
40
40
|
return "";
|
|
41
41
|
}
|
|
42
42
|
function warnOutsideSetup(hookName) {
|
|
43
|
-
if (__DEV__$
|
|
43
|
+
if (__DEV__$4 && !_current) {
|
|
44
44
|
const callSite = captureCallSite();
|
|
45
45
|
const location = callSite ? `\n Called from: ${callSite}` : "";
|
|
46
46
|
console.warn(`[Pyreon] ${hookName}() called outside component setup. Lifecycle hooks must be called synchronously during a component's setup function.` + location + (hookName === "onUnmount" ? "\n Hint: `provide()` internally calls onUnmount(). If you use provide(), ensure it runs during synchronous component setup — not inside effects, callbacks, or after awaits." : ""));
|
|
@@ -350,8 +350,22 @@ setSnapshotCapture({
|
|
|
350
350
|
|
|
351
351
|
//#endregion
|
|
352
352
|
//#region src/h.ts
|
|
353
|
-
/**
|
|
354
|
-
|
|
353
|
+
/**
|
|
354
|
+
* Marker for fragment nodes — renders children without a wrapper element.
|
|
355
|
+
*
|
|
356
|
+
* MUST use `Symbol.for(...)` (global registry, keyed by string), NOT
|
|
357
|
+
* `Symbol(...)` (fresh per evaluation). `h.ts` is inlined into BOTH the
|
|
358
|
+
* main `lib/index.js` and the `lib/jsx-runtime.js` published bundles —
|
|
359
|
+
* each bundle's evaluation of a bare `Symbol(...)` would produce a
|
|
360
|
+
* DISTINCT Symbol identity. JSX `<>` compiles to `jsx(Fragment, ...)` and
|
|
361
|
+
* resolves to jsx-runtime's identity; `runtime-server` checks
|
|
362
|
+
* `vnode.type === Fragment` against the main-entry identity. Mismatch
|
|
363
|
+
* fell through to `renderElement` and crashed SSG with
|
|
364
|
+
* `TypeError: Cannot convert a Symbol value to a string`.
|
|
365
|
+
* `Symbol.for()` keys by string in a global registry shared across all
|
|
366
|
+
* bundle evaluations — same identity everywhere.
|
|
367
|
+
*/
|
|
368
|
+
const Fragment = Symbol.for("Pyreon.Fragment");
|
|
355
369
|
/**
|
|
356
370
|
* Hyperscript function — the compiled output of JSX.
|
|
357
371
|
* `<div class="x">hello</div>` → `h("div", { class: "x" }, "hello")`
|
|
@@ -382,10 +396,10 @@ function flattenChildren(children) {
|
|
|
382
396
|
|
|
383
397
|
//#endregion
|
|
384
398
|
//#region src/dynamic.ts
|
|
385
|
-
const __DEV__$
|
|
399
|
+
const __DEV__$3 = process.env.NODE_ENV !== "production";
|
|
386
400
|
function Dynamic(props) {
|
|
387
401
|
const { component, children, ...rest } = props;
|
|
388
|
-
if (__DEV__$
|
|
402
|
+
if (__DEV__$3 && !component) console.warn("[Pyreon] <Dynamic> received a falsy `component` prop. Nothing will be rendered.");
|
|
389
403
|
if (!component) return null;
|
|
390
404
|
if (children === void 0) return h(component, rest);
|
|
391
405
|
if (Array.isArray(children)) return h(component, rest, ...children);
|
|
@@ -436,7 +450,7 @@ function _installReactivityBridge() {
|
|
|
436
450
|
|
|
437
451
|
//#endregion
|
|
438
452
|
//#region src/error-boundary.ts
|
|
439
|
-
const __DEV__$
|
|
453
|
+
const __DEV__$2 = process.env.NODE_ENV !== "production";
|
|
440
454
|
/**
|
|
441
455
|
* ErrorBoundary — catches errors thrown by child components and renders a
|
|
442
456
|
* fallback UI instead of crashing the whole tree.
|
|
@@ -461,7 +475,7 @@ const __DEV__$1 = process.env.NODE_ENV !== "production";
|
|
|
461
475
|
* </ErrorBoundary>
|
|
462
476
|
*/
|
|
463
477
|
function ErrorBoundary(props) {
|
|
464
|
-
if (__DEV__$
|
|
478
|
+
if (__DEV__$2 && typeof props.fallback !== "function") console.warn(`[Pyreon] <ErrorBoundary> expects \`fallback\` to be a function: (err, reset) => VNode. Received ${typeof props.fallback}.`);
|
|
465
479
|
const error = signal(null);
|
|
466
480
|
const reset = () => error.set(null);
|
|
467
481
|
const handler = (err) => {
|
|
@@ -513,6 +527,132 @@ function For(props) {
|
|
|
513
527
|
};
|
|
514
528
|
}
|
|
515
529
|
|
|
530
|
+
//#endregion
|
|
531
|
+
//#region src/ref.ts
|
|
532
|
+
function createRef() {
|
|
533
|
+
return { current: null };
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
//#endregion
|
|
537
|
+
//#region src/defer.ts
|
|
538
|
+
const __DEV__$1 = process.env.NODE_ENV !== "production";
|
|
539
|
+
/**
|
|
540
|
+
* Set up the `on="idle"` trigger. Returns a teardown function the
|
|
541
|
+
* caller must invoke on unmount. Browser-API access is gated by
|
|
542
|
+
* `typeof` checks so SSR / jsdom environments fall back to a
|
|
543
|
+
* `setTimeout(1)` shim. Extracted as a standalone helper so it's
|
|
544
|
+
* directly testable without going through `onMount` (core tests
|
|
545
|
+
* don't run in happy-dom; runtime-dom is where the lifecycle hooks
|
|
546
|
+
* live).
|
|
547
|
+
*
|
|
548
|
+
* @internal Exported for tests; not part of the stable public API.
|
|
549
|
+
*/
|
|
550
|
+
function _setupIdleTrigger(startLoad) {
|
|
551
|
+
const ric = globalThis.requestIdleCallback;
|
|
552
|
+
const cic = globalThis.cancelIdleCallback;
|
|
553
|
+
if (typeof ric === "function") {
|
|
554
|
+
const id = ric(startLoad);
|
|
555
|
+
return () => cic?.(id);
|
|
556
|
+
}
|
|
557
|
+
const t = setTimeout(startLoad, 1);
|
|
558
|
+
return () => clearTimeout(t);
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Set up the `on="visible"` trigger. Observes `el` via an
|
|
562
|
+
* `IntersectionObserver` and fires `startLoad` once on the first
|
|
563
|
+
* intersection. If `IntersectionObserver` is unavailable (jsdom)
|
|
564
|
+
* or `el` is null (SSR), falls back to loading immediately.
|
|
565
|
+
*
|
|
566
|
+
* Returns a teardown function — call to disconnect the observer.
|
|
567
|
+
*
|
|
568
|
+
* @internal Exported for tests; not part of the stable public API.
|
|
569
|
+
*/
|
|
570
|
+
function _setupVisibleTrigger(el, startLoad, rootMargin) {
|
|
571
|
+
if (!el || typeof IntersectionObserver === "undefined") {
|
|
572
|
+
startLoad();
|
|
573
|
+
return () => {};
|
|
574
|
+
}
|
|
575
|
+
const obs = new IntersectionObserver((entries) => {
|
|
576
|
+
if (entries.some((e) => e.isIntersecting)) {
|
|
577
|
+
startLoad();
|
|
578
|
+
obs.disconnect();
|
|
579
|
+
}
|
|
580
|
+
}, { rootMargin });
|
|
581
|
+
obs.observe(el);
|
|
582
|
+
return () => obs.disconnect();
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Lazy-load a chunk when a trigger condition is met.
|
|
586
|
+
*
|
|
587
|
+
* Three trigger modes:
|
|
588
|
+
* - `when={() => signal()}` — load when condition flips truthy (modal pattern)
|
|
589
|
+
* - `on="visible"` — load when the wrapper scrolls into view
|
|
590
|
+
* - `on="idle"` — load during browser idle time
|
|
591
|
+
*
|
|
592
|
+
* The chunk fetch is fired exactly once per `Defer` instance — repeated
|
|
593
|
+
* trigger firings after the chunk loads are no-ops.
|
|
594
|
+
*
|
|
595
|
+
* @example
|
|
596
|
+
* // Signal-driven (modal):
|
|
597
|
+
* <Defer chunk={() => import('./ConfirmDeleteModal')} when={open}>
|
|
598
|
+
* {Modal => <Modal onClose={() => setOpen(false)} />}
|
|
599
|
+
* </Defer>
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* // Viewport-driven (below-fold):
|
|
603
|
+
* <Defer chunk={() => import('./Comments')} on="visible">
|
|
604
|
+
* {Comments => <Comments postId={id} />}
|
|
605
|
+
* </Defer>
|
|
606
|
+
*
|
|
607
|
+
* @example
|
|
608
|
+
* // Idle-driven (non-critical):
|
|
609
|
+
* <Defer chunk={() => import('./Analytics')} on="idle">
|
|
610
|
+
* {Dashboard => <Dashboard />}
|
|
611
|
+
* </Defer>
|
|
612
|
+
*/
|
|
613
|
+
function Defer(props) {
|
|
614
|
+
const Loaded = signal(null);
|
|
615
|
+
const Failed = signal(null);
|
|
616
|
+
let loadStarted = false;
|
|
617
|
+
const startLoad = () => {
|
|
618
|
+
if (loadStarted) return;
|
|
619
|
+
loadStarted = true;
|
|
620
|
+
props.chunk().then((mod) => {
|
|
621
|
+
const Comp = typeof mod === "function" ? mod : mod.default;
|
|
622
|
+
if (__DEV__$1 && typeof Comp !== "function") {
|
|
623
|
+
console.warn("[Pyreon] <Defer> chunk() resolved without a default-exported component. Make sure your module exports default.");
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
Loaded.set(Comp);
|
|
627
|
+
}).catch((err) => {
|
|
628
|
+
const wrapped = err instanceof Error ? err : new Error(String(err));
|
|
629
|
+
if (__DEV__$1) console.error("[Pyreon] <Defer> chunk() rejected:", wrapped);
|
|
630
|
+
Failed.set(wrapped);
|
|
631
|
+
});
|
|
632
|
+
};
|
|
633
|
+
if ("when" in props) effect(() => {
|
|
634
|
+
if (props.when() && !loadStarted) startLoad();
|
|
635
|
+
});
|
|
636
|
+
else if (props.on === "idle") onMount(() => _setupIdleTrigger(startLoad));
|
|
637
|
+
const renderContent = () => {
|
|
638
|
+
const err = Failed();
|
|
639
|
+
if (err) throw err;
|
|
640
|
+
const Comp = Loaded();
|
|
641
|
+
if (!Comp) return props.fallback ?? null;
|
|
642
|
+
return props.children ? props.children(Comp) : h(Comp, {});
|
|
643
|
+
};
|
|
644
|
+
if ("on" in props && props.on === "visible") {
|
|
645
|
+
const containerRef = createRef();
|
|
646
|
+
onMount(() => _setupVisibleTrigger(containerRef.current, startLoad, props.rootMargin ?? "200px"));
|
|
647
|
+
return h("div", {
|
|
648
|
+
"data-pyreon-defer": "visible",
|
|
649
|
+
ref: containerRef,
|
|
650
|
+
style: "display: contents"
|
|
651
|
+
}, renderContent);
|
|
652
|
+
}
|
|
653
|
+
return h(Fragment, null, renderContent);
|
|
654
|
+
}
|
|
655
|
+
|
|
516
656
|
//#endregion
|
|
517
657
|
//#region src/lazy.ts
|
|
518
658
|
function lazy(load) {
|
|
@@ -691,6 +831,54 @@ function _rp(fn) {
|
|
|
691
831
|
return fn;
|
|
692
832
|
}
|
|
693
833
|
/**
|
|
834
|
+
* Wrap a JSX spread source so its getter-shaped reactive props survive
|
|
835
|
+
* the JS-level object spread that esbuild's automatic JSX runtime emits
|
|
836
|
+
* for `<Comp {...source}>`.
|
|
837
|
+
*
|
|
838
|
+
* Without this wrapper, esbuild compiles `<Comp {...source}>` to
|
|
839
|
+
* `jsx(Comp, { ...source })` — and JS spread fires every getter on
|
|
840
|
+
* `source`, storing the resolved values as plain data properties. Any
|
|
841
|
+
* compiler-emitted reactive prop (`_rp(() => signal())` converted to a
|
|
842
|
+
* getter by `makeReactiveProps`) on `source` is collapsed to its
|
|
843
|
+
* initial value before the receiving component ever sees it.
|
|
844
|
+
*
|
|
845
|
+
* `_wrapSpread(source)` walks `source`'s own keys via `Reflect.ownKeys`
|
|
846
|
+
* (no getter firing) and returns a new object whose values are
|
|
847
|
+
* `_rp`-branded thunks `() => source[key]`. When `{ ..._wrapSpread(s) }`
|
|
848
|
+
* is spread by esbuild, the thunks are stored as plain data property
|
|
849
|
+
* values (no getters to fire), then `makeReactiveProps` in `mount.ts`
|
|
850
|
+
* converts the brands back into getters that lazily read from the
|
|
851
|
+
* original `source` — preserving the reactive subscription end-to-end.
|
|
852
|
+
*
|
|
853
|
+
* Fast path: when `source` has no getter descriptors, return the
|
|
854
|
+
* source object unchanged. JS spread will work correctly in that case
|
|
855
|
+
* because there's nothing reactive to preserve. Saves N thunk
|
|
856
|
+
* allocations per component render in the 99% case.
|
|
857
|
+
*
|
|
858
|
+
* Emitted by the compiler — not generally meant for hand-written code.
|
|
859
|
+
*/
|
|
860
|
+
function _wrapSpread(source) {
|
|
861
|
+
if (!source || typeof source !== "object") return source;
|
|
862
|
+
const descriptors = Object.getOwnPropertyDescriptors(source);
|
|
863
|
+
let hasGetter = false;
|
|
864
|
+
for (const k in descriptors) if (descriptors[k].get) {
|
|
865
|
+
hasGetter = true;
|
|
866
|
+
break;
|
|
867
|
+
}
|
|
868
|
+
if (!hasGetter) return source;
|
|
869
|
+
const result = {};
|
|
870
|
+
for (const key of Reflect.ownKeys(source)) {
|
|
871
|
+
const desc = descriptors[key];
|
|
872
|
+
if (!desc) continue;
|
|
873
|
+
if (desc.get) {
|
|
874
|
+
const fn = () => source[key];
|
|
875
|
+
fn[REACTIVE_PROP] = true;
|
|
876
|
+
result[key] = fn;
|
|
877
|
+
} else result[key] = desc.value;
|
|
878
|
+
}
|
|
879
|
+
return result;
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
694
882
|
* Convert compiler-emitted `_rp(() => expr)` prop values into getter properties.
|
|
695
883
|
*
|
|
696
884
|
* Only converts functions branded with REACTIVE_PROP — user-written accessor
|
|
@@ -738,12 +926,6 @@ function createUniqueId() {
|
|
|
738
926
|
return `pyreon-${++_idCounter}`;
|
|
739
927
|
}
|
|
740
928
|
|
|
741
|
-
//#endregion
|
|
742
|
-
//#region src/ref.ts
|
|
743
|
-
function createRef() {
|
|
744
|
-
return { current: null };
|
|
745
|
-
}
|
|
746
|
-
|
|
747
929
|
//#endregion
|
|
748
930
|
//#region src/show.ts
|
|
749
931
|
function callWhen(when) {
|
|
@@ -922,5 +1104,5 @@ function Suspense(props) {
|
|
|
922
1104
|
}
|
|
923
1105
|
|
|
924
1106
|
//#endregion
|
|
925
|
-
export { CSS_UNITLESS, Dynamic, EMPTY_PROPS, ErrorBoundary, For, ForSymbol, Fragment, Match, MatchSymbol, NATIVE_COMPAT_MARKER, Portal, PortalSymbol, REACTIVE_PROP, Show, Suspense, Switch, _rp, captureContextStack, createContext, createReactiveContext, createRef, createUniqueId, cx, defineComponent, dispatchToErrorBoundary, h, isNativeCompat, lazy, makeReactiveProps, mapArray, mergeProps, nativeCompat, normalizeStyleValue, onErrorCaptured, onMount, onUnmount, onUpdate, popContext, propagateError, provide, pushContext, registerErrorHandler, reportError, restoreContextStack, runWithHooks, setContextStackProvider, splitProps, toKebabCase, useContext, withContext };
|
|
1107
|
+
export { CSS_UNITLESS, Defer, Dynamic, EMPTY_PROPS, ErrorBoundary, For, ForSymbol, Fragment, Match, MatchSymbol, NATIVE_COMPAT_MARKER, Portal, PortalSymbol, REACTIVE_PROP, Show, Suspense, Switch, _rp, _wrapSpread, captureContextStack, createContext, createReactiveContext, createRef, createUniqueId, cx, defineComponent, dispatchToErrorBoundary, h, isNativeCompat, lazy, makeReactiveProps, mapArray, mergeProps, nativeCompat, normalizeStyleValue, onErrorCaptured, onMount, onUnmount, onUpdate, popContext, propagateError, provide, pushContext, registerErrorHandler, reportError, restoreContextStack, runWithHooks, setContextStackProvider, splitProps, toKebabCase, useContext, withContext };
|
|
926
1108
|
//# sourceMappingURL=index.js.map
|
package/lib/jsx-dev-runtime.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
//#region src/h.ts
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Marker for fragment nodes — renders children without a wrapper element.
|
|
4
|
+
*
|
|
5
|
+
* MUST use `Symbol.for(...)` (global registry, keyed by string), NOT
|
|
6
|
+
* `Symbol(...)` (fresh per evaluation). `h.ts` is inlined into BOTH the
|
|
7
|
+
* main `lib/index.js` and the `lib/jsx-runtime.js` published bundles —
|
|
8
|
+
* each bundle's evaluation of a bare `Symbol(...)` would produce a
|
|
9
|
+
* DISTINCT Symbol identity. JSX `<>` compiles to `jsx(Fragment, ...)` and
|
|
10
|
+
* resolves to jsx-runtime's identity; `runtime-server` checks
|
|
11
|
+
* `vnode.type === Fragment` against the main-entry identity. Mismatch
|
|
12
|
+
* fell through to `renderElement` and crashed SSG with
|
|
13
|
+
* `TypeError: Cannot convert a Symbol value to a string`.
|
|
14
|
+
* `Symbol.for()` keys by string in a global registry shared across all
|
|
15
|
+
* bundle evaluations — same identity everywhere.
|
|
16
|
+
*/
|
|
17
|
+
const Fragment = Symbol.for("Pyreon.Fragment");
|
|
4
18
|
/**
|
|
5
19
|
* Hyperscript function — the compiled output of JSX.
|
|
6
20
|
* `<div class="x">hello</div>` → `h("div", { class: "x" }, "hello")`
|
|
@@ -46,15 +60,35 @@ function flattenChildren(children) {
|
|
|
46
60
|
* jsx-runtime.ts into the consumer's compilation unit.
|
|
47
61
|
*/
|
|
48
62
|
function jsx(type, props, key) {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
const descriptors = Object.getOwnPropertyDescriptors(props);
|
|
64
|
+
let hasGetter = false;
|
|
65
|
+
for (const k in descriptors) if (descriptors[k].get) {
|
|
66
|
+
hasGetter = true;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
const children = props.children;
|
|
70
|
+
if (!hasGetter) {
|
|
71
|
+
const { children: _ignored, ...rest } = props;
|
|
72
|
+
const propsWithKey = key != null ? {
|
|
73
|
+
...rest,
|
|
74
|
+
key
|
|
75
|
+
} : rest;
|
|
76
|
+
if (typeof type === "function") return h(type, children !== void 0 ? {
|
|
77
|
+
...propsWithKey,
|
|
78
|
+
children
|
|
79
|
+
} : propsWithKey);
|
|
80
|
+
return h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);
|
|
81
|
+
}
|
|
82
|
+
const propsWithKey = {};
|
|
83
|
+
for (const k in descriptors) {
|
|
84
|
+
if (k === "children") continue;
|
|
85
|
+
Object.defineProperty(propsWithKey, k, descriptors[k]);
|
|
86
|
+
}
|
|
87
|
+
if (key != null) propsWithKey.key = key;
|
|
88
|
+
if (typeof type === "function") {
|
|
89
|
+
if (children !== void 0) propsWithKey.children = children;
|
|
90
|
+
return h(type, propsWithKey);
|
|
91
|
+
}
|
|
58
92
|
return h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);
|
|
59
93
|
}
|
|
60
94
|
const jsxs = jsx;
|
package/lib/jsx-runtime.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
//#region src/h.ts
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Marker for fragment nodes — renders children without a wrapper element.
|
|
4
|
+
*
|
|
5
|
+
* MUST use `Symbol.for(...)` (global registry, keyed by string), NOT
|
|
6
|
+
* `Symbol(...)` (fresh per evaluation). `h.ts` is inlined into BOTH the
|
|
7
|
+
* main `lib/index.js` and the `lib/jsx-runtime.js` published bundles —
|
|
8
|
+
* each bundle's evaluation of a bare `Symbol(...)` would produce a
|
|
9
|
+
* DISTINCT Symbol identity. JSX `<>` compiles to `jsx(Fragment, ...)` and
|
|
10
|
+
* resolves to jsx-runtime's identity; `runtime-server` checks
|
|
11
|
+
* `vnode.type === Fragment` against the main-entry identity. Mismatch
|
|
12
|
+
* fell through to `renderElement` and crashed SSG with
|
|
13
|
+
* `TypeError: Cannot convert a Symbol value to a string`.
|
|
14
|
+
* `Symbol.for()` keys by string in a global registry shared across all
|
|
15
|
+
* bundle evaluations — same identity everywhere.
|
|
16
|
+
*/
|
|
17
|
+
const Fragment = Symbol.for("Pyreon.Fragment");
|
|
4
18
|
/**
|
|
5
19
|
* Hyperscript function — the compiled output of JSX.
|
|
6
20
|
* `<div class="x">hello</div>` → `h("div", { class: "x" }, "hello")`
|
|
@@ -46,15 +60,35 @@ function flattenChildren(children) {
|
|
|
46
60
|
* jsx-runtime.ts into the consumer's compilation unit.
|
|
47
61
|
*/
|
|
48
62
|
function jsx(type, props, key) {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
const descriptors = Object.getOwnPropertyDescriptors(props);
|
|
64
|
+
let hasGetter = false;
|
|
65
|
+
for (const k in descriptors) if (descriptors[k].get) {
|
|
66
|
+
hasGetter = true;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
const children = props.children;
|
|
70
|
+
if (!hasGetter) {
|
|
71
|
+
const { children: _ignored, ...rest } = props;
|
|
72
|
+
const propsWithKey = key != null ? {
|
|
73
|
+
...rest,
|
|
74
|
+
key
|
|
75
|
+
} : rest;
|
|
76
|
+
if (typeof type === "function") return h(type, children !== void 0 ? {
|
|
77
|
+
...propsWithKey,
|
|
78
|
+
children
|
|
79
|
+
} : propsWithKey);
|
|
80
|
+
return h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);
|
|
81
|
+
}
|
|
82
|
+
const propsWithKey = {};
|
|
83
|
+
for (const k in descriptors) {
|
|
84
|
+
if (k === "children") continue;
|
|
85
|
+
Object.defineProperty(propsWithKey, k, descriptors[k]);
|
|
86
|
+
}
|
|
87
|
+
if (key != null) propsWithKey.key = key;
|
|
88
|
+
if (typeof type === "function") {
|
|
89
|
+
if (children !== void 0) propsWithKey.children = children;
|
|
90
|
+
return h(type, propsWithKey);
|
|
91
|
+
}
|
|
58
92
|
return h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);
|
|
59
93
|
}
|
|
60
94
|
const jsxs = jsx;
|