@sigx/runtime-core 0.1.3 → 0.1.5
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/dist/component.d.ts +83 -6
- package/dist/component.d.ts.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +463 -113
- package/dist/index.js.map +1 -1
- package/dist/lazy.d.ts +99 -0
- package/dist/lazy.d.ts.map +1 -0
- package/dist/plugins.d.ts +25 -1
- package/dist/plugins.d.ts.map +1 -1
- package/dist/renderer.d.ts +39 -6
- package/dist/renderer.d.ts.map +1 -1
- package/dist/utils/component-helpers.d.ts +38 -0
- package/dist/utils/component-helpers.d.ts.map +1 -0
- package/dist/utils/normalize.d.ts +31 -0
- package/dist/utils/normalize.d.ts.map +1 -0
- package/dist/utils/props-accessor.d.ts +23 -0
- package/dist/utils/props-accessor.d.ts.map +1 -0
- package/dist/utils/slots.d.ts +46 -0
- package/dist/utils/slots.d.ts.map +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -28,6 +28,29 @@ function registerComponentPlugin(plugin) {
|
|
|
28
28
|
function getComponentPlugins() {
|
|
29
29
|
return plugins;
|
|
30
30
|
}
|
|
31
|
+
const contextExtensions = [];
|
|
32
|
+
/**
|
|
33
|
+
* Register a function that will be called to extend every component context.
|
|
34
|
+
* Extensions are called in order of registration.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* // In @sigx/server-renderer/client
|
|
39
|
+
* registerContextExtension((ctx) => {
|
|
40
|
+
* ctx.ssr = { load: () => {} };
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
function registerContextExtension(extension) {
|
|
45
|
+
contextExtensions.push(extension);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Apply all registered context extensions to a context object.
|
|
49
|
+
* Called internally by the renderer when creating component contexts.
|
|
50
|
+
*/
|
|
51
|
+
function applyContextExtensions(ctx) {
|
|
52
|
+
for (const extension of contextExtensions) extension(ctx);
|
|
53
|
+
}
|
|
31
54
|
|
|
32
55
|
//#endregion
|
|
33
56
|
//#region src/app.ts
|
|
@@ -405,6 +428,190 @@ function jsxs(type, props, key) {
|
|
|
405
428
|
}
|
|
406
429
|
const jsxDEV = jsx;
|
|
407
430
|
|
|
431
|
+
//#endregion
|
|
432
|
+
//#region src/lazy.tsx
|
|
433
|
+
/**
|
|
434
|
+
* Lazy loading utilities for sigx components.
|
|
435
|
+
*
|
|
436
|
+
* Provides runtime-only lazy loading with no build dependencies.
|
|
437
|
+
* Works with any bundler that supports dynamic import().
|
|
438
|
+
*/
|
|
439
|
+
let currentSuspenseBoundary = null;
|
|
440
|
+
/**
|
|
441
|
+
* Register a promise with the current Suspense boundary
|
|
442
|
+
* @internal
|
|
443
|
+
*/
|
|
444
|
+
function registerPendingPromise(promise) {
|
|
445
|
+
const boundary = currentSuspenseBoundary;
|
|
446
|
+
if (boundary) {
|
|
447
|
+
boundary.pending.add(promise);
|
|
448
|
+
promise.finally(() => {
|
|
449
|
+
boundary.pending.delete(promise);
|
|
450
|
+
if (boundary.pending.size === 0) boundary.onResolve();
|
|
451
|
+
});
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Create a lazy-loaded component wrapper.
|
|
458
|
+
*
|
|
459
|
+
* The component will be loaded on first render. Use with `<Suspense>` to show
|
|
460
|
+
* a fallback while loading.
|
|
461
|
+
*
|
|
462
|
+
* @param loader - Function that returns a Promise resolving to the component
|
|
463
|
+
* @returns A component factory that loads the real component on demand
|
|
464
|
+
*
|
|
465
|
+
* @example
|
|
466
|
+
* ```tsx
|
|
467
|
+
* import { lazy, Suspense } from 'sigx';
|
|
468
|
+
*
|
|
469
|
+
* // Component will be in a separate chunk
|
|
470
|
+
* const HeavyChart = lazy(() => import('./components/HeavyChart'));
|
|
471
|
+
*
|
|
472
|
+
* // Usage
|
|
473
|
+
* <Suspense fallback={<Spinner />}>
|
|
474
|
+
* <HeavyChart data={chartData} />
|
|
475
|
+
* </Suspense>
|
|
476
|
+
*
|
|
477
|
+
* // Preload on hover
|
|
478
|
+
* <button onMouseEnter={() => HeavyChart.preload()}>
|
|
479
|
+
* Show Chart
|
|
480
|
+
* </button>
|
|
481
|
+
* ```
|
|
482
|
+
*/
|
|
483
|
+
function lazy(loader) {
|
|
484
|
+
let Component = null;
|
|
485
|
+
let promise = null;
|
|
486
|
+
let error = null;
|
|
487
|
+
let state = "pending";
|
|
488
|
+
const LazyWrapper = defineComponent((ctx) => {
|
|
489
|
+
const loadState = ctx.signal({
|
|
490
|
+
state,
|
|
491
|
+
tick: 0
|
|
492
|
+
});
|
|
493
|
+
if (!promise) promise = loader().then((mod) => {
|
|
494
|
+
Component = "default" in mod ? mod.default : mod;
|
|
495
|
+
state = "resolved";
|
|
496
|
+
loadState.state = "resolved";
|
|
497
|
+
loadState.tick++;
|
|
498
|
+
return Component;
|
|
499
|
+
}).catch((err) => {
|
|
500
|
+
error = err instanceof Error ? err : new Error(String(err));
|
|
501
|
+
state = "rejected";
|
|
502
|
+
loadState.state = "rejected";
|
|
503
|
+
loadState.tick++;
|
|
504
|
+
throw error;
|
|
505
|
+
});
|
|
506
|
+
if (state === "resolved" && Component) return () => {
|
|
507
|
+
return jsx(Component, {});
|
|
508
|
+
};
|
|
509
|
+
if (state === "rejected" && error) throw error;
|
|
510
|
+
if (!registerPendingPromise(promise)) promise.catch(() => {});
|
|
511
|
+
return () => {
|
|
512
|
+
const currentState = loadState.state;
|
|
513
|
+
loadState.tick;
|
|
514
|
+
if (currentState === "resolved" && Component) return jsx(Component, {});
|
|
515
|
+
if (currentState === "rejected" && error) throw error;
|
|
516
|
+
return null;
|
|
517
|
+
};
|
|
518
|
+
}, { name: "LazyComponent" });
|
|
519
|
+
LazyWrapper.__lazy = true;
|
|
520
|
+
LazyWrapper.preload = () => {
|
|
521
|
+
if (!promise) promise = loader().then((mod) => {
|
|
522
|
+
Component = "default" in mod ? mod.default : mod;
|
|
523
|
+
state = "resolved";
|
|
524
|
+
return Component;
|
|
525
|
+
}).catch((err) => {
|
|
526
|
+
error = err instanceof Error ? err : new Error(String(err));
|
|
527
|
+
state = "rejected";
|
|
528
|
+
throw error;
|
|
529
|
+
});
|
|
530
|
+
return promise;
|
|
531
|
+
};
|
|
532
|
+
LazyWrapper.isLoaded = () => {
|
|
533
|
+
return state === "resolved";
|
|
534
|
+
};
|
|
535
|
+
return LazyWrapper;
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Suspense boundary component for handling async loading states.
|
|
539
|
+
*
|
|
540
|
+
* Wraps lazy-loaded components and shows a fallback while they load.
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* ```tsx
|
|
544
|
+
* import { lazy, Suspense } from 'sigx';
|
|
545
|
+
*
|
|
546
|
+
* const LazyDashboard = lazy(() => import('./Dashboard'));
|
|
547
|
+
*
|
|
548
|
+
* // Basic usage
|
|
549
|
+
* <Suspense fallback={<div>Loading...</div>}>
|
|
550
|
+
* <LazyDashboard />
|
|
551
|
+
* </Suspense>
|
|
552
|
+
*
|
|
553
|
+
* // With spinner component
|
|
554
|
+
* <Suspense fallback={<Spinner size="large" />}>
|
|
555
|
+
* <LazyDashboard />
|
|
556
|
+
* <LazyCharts />
|
|
557
|
+
* </Suspense>
|
|
558
|
+
* ```
|
|
559
|
+
*/
|
|
560
|
+
const Suspense = defineComponent((ctx) => {
|
|
561
|
+
const { props, slots } = ctx;
|
|
562
|
+
const state = ctx.signal({
|
|
563
|
+
isReady: false,
|
|
564
|
+
pendingCount: 0
|
|
565
|
+
});
|
|
566
|
+
const boundary = {
|
|
567
|
+
pending: /* @__PURE__ */ new Set(),
|
|
568
|
+
onResolve: () => {
|
|
569
|
+
state.pendingCount = boundary.pending.size;
|
|
570
|
+
if (boundary.pending.size === 0) state.isReady = true;
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
ctx.onMount(() => {
|
|
574
|
+
if (boundary.pending.size === 0) state.isReady = true;
|
|
575
|
+
});
|
|
576
|
+
return () => {
|
|
577
|
+
state.isReady;
|
|
578
|
+
state.pendingCount;
|
|
579
|
+
const prevBoundary = currentSuspenseBoundary;
|
|
580
|
+
currentSuspenseBoundary = boundary;
|
|
581
|
+
try {
|
|
582
|
+
const children = slots.default();
|
|
583
|
+
if (boundary.pending.size > 0) {
|
|
584
|
+
const fallback = props.fallback;
|
|
585
|
+
if (typeof fallback === "function") return fallback();
|
|
586
|
+
return fallback ?? null;
|
|
587
|
+
}
|
|
588
|
+
if (Array.isArray(children)) {
|
|
589
|
+
const filtered = children.filter((c) => c != null && c !== false && c !== true);
|
|
590
|
+
if (filtered.length === 0) return null;
|
|
591
|
+
if (filtered.length === 1) return filtered[0];
|
|
592
|
+
return filtered;
|
|
593
|
+
}
|
|
594
|
+
return children;
|
|
595
|
+
} catch (err) {
|
|
596
|
+
if (err instanceof Promise) {
|
|
597
|
+
registerPendingPromise(err);
|
|
598
|
+
const fallback = props.fallback;
|
|
599
|
+
if (typeof fallback === "function") return fallback();
|
|
600
|
+
return fallback ?? null;
|
|
601
|
+
}
|
|
602
|
+
throw err;
|
|
603
|
+
} finally {
|
|
604
|
+
currentSuspenseBoundary = prevBoundary;
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
}, { name: "Suspense" });
|
|
608
|
+
/**
|
|
609
|
+
* Check if a component is a lazy-loaded component
|
|
610
|
+
*/
|
|
611
|
+
function isLazyComponent(component) {
|
|
612
|
+
return component && component.__lazy === true;
|
|
613
|
+
}
|
|
614
|
+
|
|
408
615
|
//#endregion
|
|
409
616
|
//#region src/utils/index.ts
|
|
410
617
|
var Utils = class {
|
|
@@ -419,6 +626,195 @@ function guid$1() {
|
|
|
419
626
|
});
|
|
420
627
|
}
|
|
421
628
|
|
|
629
|
+
//#endregion
|
|
630
|
+
//#region src/utils/props-accessor.ts
|
|
631
|
+
/**
|
|
632
|
+
* Creates a props accessor that can be called with defaults or accessed directly.
|
|
633
|
+
* After calling with defaults, direct property access uses those defaults.
|
|
634
|
+
*
|
|
635
|
+
* @example
|
|
636
|
+
* ```ts
|
|
637
|
+
* // In component setup:
|
|
638
|
+
* const props = createPropsAccessor(reactiveProps);
|
|
639
|
+
*
|
|
640
|
+
* // Set defaults
|
|
641
|
+
* props({ count: 0, label: 'Default' });
|
|
642
|
+
*
|
|
643
|
+
* // Access props (falls back to defaults if not provided)
|
|
644
|
+
* const count = props.count;
|
|
645
|
+
* ```
|
|
646
|
+
*/
|
|
647
|
+
function createPropsAccessor(reactiveProps) {
|
|
648
|
+
let defaults = {};
|
|
649
|
+
const proxy = new Proxy(function propsAccessor() {}, {
|
|
650
|
+
get(_, key) {
|
|
651
|
+
if (typeof key === "symbol") return void 0;
|
|
652
|
+
const value = reactiveProps[key];
|
|
653
|
+
return value != null ? value : defaults[key];
|
|
654
|
+
},
|
|
655
|
+
apply(_, __, args) {
|
|
656
|
+
if (args[0] && typeof args[0] === "object") defaults = {
|
|
657
|
+
...defaults,
|
|
658
|
+
...args[0]
|
|
659
|
+
};
|
|
660
|
+
return proxy;
|
|
661
|
+
},
|
|
662
|
+
has(_, key) {
|
|
663
|
+
if (typeof key === "symbol") return false;
|
|
664
|
+
return key in reactiveProps || key in defaults;
|
|
665
|
+
},
|
|
666
|
+
ownKeys() {
|
|
667
|
+
return [...new Set([...Object.keys(reactiveProps), ...Object.keys(defaults)])];
|
|
668
|
+
},
|
|
669
|
+
getOwnPropertyDescriptor(_, key) {
|
|
670
|
+
if (typeof key === "symbol") return void 0;
|
|
671
|
+
if (key in reactiveProps || key in defaults) return {
|
|
672
|
+
enumerable: true,
|
|
673
|
+
configurable: true,
|
|
674
|
+
writable: false
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
return proxy;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
//#endregion
|
|
682
|
+
//#region src/utils/slots.ts
|
|
683
|
+
/**
|
|
684
|
+
* Slots system for component children.
|
|
685
|
+
* Supports default and named slots with reactivity.
|
|
686
|
+
*/
|
|
687
|
+
/**
|
|
688
|
+
* Create slots object from children and slots prop.
|
|
689
|
+
* Uses a version signal to trigger re-renders when children change.
|
|
690
|
+
*
|
|
691
|
+
* Supports named slots via:
|
|
692
|
+
* - `slots` prop object (e.g., `slots={{ header: () => <div>...</div> }}`)
|
|
693
|
+
* - `slot` prop on children (e.g., `<div slot="header">...</div>`)
|
|
694
|
+
*
|
|
695
|
+
* @example
|
|
696
|
+
* ```tsx
|
|
697
|
+
* // Parent component
|
|
698
|
+
* <Card slots={{ header: () => <h1>Title</h1> }}>
|
|
699
|
+
* <p>Default content</p>
|
|
700
|
+
* <span slot="footer">Footer text</span>
|
|
701
|
+
* </Card>
|
|
702
|
+
*
|
|
703
|
+
* // Card component setup
|
|
704
|
+
* const slots = createSlots(children, slotsFromProps);
|
|
705
|
+
* return () => (
|
|
706
|
+
* <div>
|
|
707
|
+
* {slots.header()}
|
|
708
|
+
* {slots.default()}
|
|
709
|
+
* {slots.footer()}
|
|
710
|
+
* </div>
|
|
711
|
+
* );
|
|
712
|
+
* ```
|
|
713
|
+
*/
|
|
714
|
+
function createSlots(children, slotsFromProps) {
|
|
715
|
+
const versionSignal = signal$1({ v: 0 });
|
|
716
|
+
function extractNamedSlotsFromChildren(c) {
|
|
717
|
+
const defaultChildren = [];
|
|
718
|
+
const namedSlots = {};
|
|
719
|
+
if (c == null) return {
|
|
720
|
+
defaultChildren,
|
|
721
|
+
namedSlots
|
|
722
|
+
};
|
|
723
|
+
const items = Array.isArray(c) ? c : [c];
|
|
724
|
+
for (const child of items) if (child && typeof child === "object" && child.props && child.props.slot) {
|
|
725
|
+
const slotName = child.props.slot;
|
|
726
|
+
if (!namedSlots[slotName]) namedSlots[slotName] = [];
|
|
727
|
+
namedSlots[slotName].push(child);
|
|
728
|
+
} else defaultChildren.push(child);
|
|
729
|
+
return {
|
|
730
|
+
defaultChildren,
|
|
731
|
+
namedSlots
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
const slotsObj = {
|
|
735
|
+
_children: children,
|
|
736
|
+
_slotsFromProps: slotsFromProps || {},
|
|
737
|
+
_version: versionSignal,
|
|
738
|
+
_isPatching: false,
|
|
739
|
+
default: function() {
|
|
740
|
+
this._version.v;
|
|
741
|
+
const c = this._children;
|
|
742
|
+
const { defaultChildren } = extractNamedSlotsFromChildren(c);
|
|
743
|
+
return defaultChildren.filter((child) => child != null && child !== false && child !== true);
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
return new Proxy(slotsObj, { get(target, prop) {
|
|
747
|
+
if (prop in target) return target[prop];
|
|
748
|
+
if (typeof prop === "string") return function(scopedProps) {
|
|
749
|
+
target._version.v;
|
|
750
|
+
if (target._slotsFromProps && typeof target._slotsFromProps[prop] === "function") {
|
|
751
|
+
const result = target._slotsFromProps[prop](scopedProps);
|
|
752
|
+
if (result == null) return [];
|
|
753
|
+
return Array.isArray(result) ? result : [result];
|
|
754
|
+
}
|
|
755
|
+
const { namedSlots } = extractNamedSlotsFromChildren(target._children);
|
|
756
|
+
return namedSlots[prop] || [];
|
|
757
|
+
};
|
|
758
|
+
} });
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
//#endregion
|
|
762
|
+
//#region src/utils/normalize.ts
|
|
763
|
+
/**
|
|
764
|
+
* VNode normalization utilities.
|
|
765
|
+
* Converts render results into proper VNode structures.
|
|
766
|
+
*/
|
|
767
|
+
/**
|
|
768
|
+
* Normalize render result to a VNode (wrapping arrays in Fragment).
|
|
769
|
+
* Handles null, undefined, false, true by returning an empty Text node.
|
|
770
|
+
*
|
|
771
|
+
* This is used to normalize the return value of component render functions
|
|
772
|
+
* into a consistent VNode structure for the renderer to process.
|
|
773
|
+
*
|
|
774
|
+
* @example
|
|
775
|
+
* ```ts
|
|
776
|
+
* // Conditional rendering returns null/false
|
|
777
|
+
* normalizeSubTree(null) // → empty Text node
|
|
778
|
+
* normalizeSubTree(false) // → empty Text node
|
|
779
|
+
*
|
|
780
|
+
* // Arrays become Fragments
|
|
781
|
+
* normalizeSubTree([<A/>, <B/>]) // → Fragment with children
|
|
782
|
+
*
|
|
783
|
+
* // Primitives become Text nodes
|
|
784
|
+
* normalizeSubTree("hello") // → Text node
|
|
785
|
+
* normalizeSubTree(42) // → Text node
|
|
786
|
+
*
|
|
787
|
+
* // VNodes pass through
|
|
788
|
+
* normalizeSubTree(<div/>) // → same VNode
|
|
789
|
+
* ```
|
|
790
|
+
*/
|
|
791
|
+
function normalizeSubTree(result) {
|
|
792
|
+
if (result == null || result === false || result === true) return {
|
|
793
|
+
type: Text,
|
|
794
|
+
props: {},
|
|
795
|
+
key: null,
|
|
796
|
+
children: [],
|
|
797
|
+
dom: null,
|
|
798
|
+
text: ""
|
|
799
|
+
};
|
|
800
|
+
if (Array.isArray(result)) return {
|
|
801
|
+
type: Fragment,
|
|
802
|
+
props: {},
|
|
803
|
+
key: null,
|
|
804
|
+
children: result,
|
|
805
|
+
dom: null
|
|
806
|
+
};
|
|
807
|
+
if (typeof result === "string" || typeof result === "number") return {
|
|
808
|
+
type: Text,
|
|
809
|
+
props: {},
|
|
810
|
+
key: null,
|
|
811
|
+
children: [],
|
|
812
|
+
dom: null,
|
|
813
|
+
text: result
|
|
814
|
+
};
|
|
815
|
+
return result;
|
|
816
|
+
}
|
|
817
|
+
|
|
422
818
|
//#endregion
|
|
423
819
|
//#region src/models/index.ts
|
|
424
820
|
const guid = guid$1;
|
|
@@ -715,7 +1111,6 @@ function isComponent(type) {
|
|
|
715
1111
|
}
|
|
716
1112
|
function createRenderer(options) {
|
|
717
1113
|
const { insert: hostInsert, remove: hostRemove, patchProp: hostPatchProp, createElement: hostCreateElement, createText: hostCreateText, createComment: hostCreateComment, setText: hostSetText, setElementText: hostSetElementText, parentNode: hostParentNode, nextSibling: hostNextSibling, cloneNode: hostCloneNode, insertStaticContent: hostInsertStaticContent } = options;
|
|
718
|
-
let isPatching = false;
|
|
719
1114
|
let currentAppContext = null;
|
|
720
1115
|
function render(element, container, appContext) {
|
|
721
1116
|
if (appContext) currentAppContext = appContext;
|
|
@@ -729,6 +1124,13 @@ function createRenderer(options) {
|
|
|
729
1124
|
dom: null,
|
|
730
1125
|
text: element
|
|
731
1126
|
};
|
|
1127
|
+
else if (isComponent(element)) vnode = {
|
|
1128
|
+
type: element,
|
|
1129
|
+
props: {},
|
|
1130
|
+
key: null,
|
|
1131
|
+
children: [],
|
|
1132
|
+
dom: null
|
|
1133
|
+
};
|
|
732
1134
|
else vnode = element;
|
|
733
1135
|
if (vnode) {
|
|
734
1136
|
if (oldVNode) patch(oldVNode, vnode, container);
|
|
@@ -740,6 +1142,7 @@ function createRenderer(options) {
|
|
|
740
1142
|
}
|
|
741
1143
|
}
|
|
742
1144
|
function mount(vnode, container, before = null) {
|
|
1145
|
+
if (vnode == null || vnode === false || vnode === true) return;
|
|
743
1146
|
if (vnode.type === Text) {
|
|
744
1147
|
const node = hostCreateText(String(vnode.text));
|
|
745
1148
|
vnode.dom = node;
|
|
@@ -751,7 +1154,7 @@ function createRenderer(options) {
|
|
|
751
1154
|
const anchor = hostCreateComment("");
|
|
752
1155
|
vnode.dom = anchor;
|
|
753
1156
|
hostInsert(anchor, container, before);
|
|
754
|
-
vnode.children.forEach((child) => mount(child, container, anchor));
|
|
1157
|
+
if (vnode.children) vnode.children.forEach((child) => mount(child, container, anchor));
|
|
755
1158
|
return;
|
|
756
1159
|
}
|
|
757
1160
|
if (isComponent(vnode.type)) {
|
|
@@ -768,17 +1171,18 @@ function createRenderer(options) {
|
|
|
768
1171
|
else if (typeof vnode.props.ref === "object") vnode.props.ref.current = element;
|
|
769
1172
|
}
|
|
770
1173
|
}
|
|
771
|
-
vnode.children.forEach((child) => {
|
|
1174
|
+
if (vnode.children) vnode.children.forEach((child) => {
|
|
772
1175
|
child.parent = vnode;
|
|
773
1176
|
mount(child, element);
|
|
774
1177
|
});
|
|
775
1178
|
hostInsert(element, container, before);
|
|
776
1179
|
}
|
|
777
1180
|
function unmount(vnode, container) {
|
|
778
|
-
|
|
1181
|
+
const internalVNode = vnode;
|
|
1182
|
+
if (internalVNode._effect) internalVNode._effect.stop();
|
|
779
1183
|
if (vnode.cleanup) vnode.cleanup();
|
|
780
1184
|
if (isComponent(vnode.type)) {
|
|
781
|
-
const subTree =
|
|
1185
|
+
const subTree = internalVNode._subTree;
|
|
782
1186
|
if (subTree) unmount(subTree, container);
|
|
783
1187
|
if (vnode.dom) hostRemove(vnode.dom);
|
|
784
1188
|
if (vnode.props?.ref) {
|
|
@@ -788,7 +1192,7 @@ function createRenderer(options) {
|
|
|
788
1192
|
return;
|
|
789
1193
|
}
|
|
790
1194
|
if (vnode.type === Fragment) {
|
|
791
|
-
vnode.children.forEach((child) => unmount(child, container));
|
|
1195
|
+
if (vnode.children) vnode.children.forEach((child) => unmount(child, container));
|
|
792
1196
|
if (vnode.dom) hostRemove(vnode.dom);
|
|
793
1197
|
return;
|
|
794
1198
|
}
|
|
@@ -808,13 +1212,15 @@ function createRenderer(options) {
|
|
|
808
1212
|
mount(newVNode, parent, nextSibling);
|
|
809
1213
|
return;
|
|
810
1214
|
}
|
|
811
|
-
|
|
1215
|
+
const oldInternal = oldVNode;
|
|
1216
|
+
const newInternal = newVNode;
|
|
1217
|
+
if (oldInternal._effect) {
|
|
812
1218
|
newVNode.dom = oldVNode.dom;
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
const props =
|
|
817
|
-
|
|
1219
|
+
newInternal._effect = oldInternal._effect;
|
|
1220
|
+
newInternal._subTree = oldInternal._subTree;
|
|
1221
|
+
newInternal._slots = oldInternal._slots;
|
|
1222
|
+
const props = oldInternal._componentProps;
|
|
1223
|
+
newInternal._componentProps = props;
|
|
818
1224
|
if (props) {
|
|
819
1225
|
const newProps$1 = newVNode.props || {};
|
|
820
1226
|
untrack(() => {
|
|
@@ -824,20 +1230,20 @@ function createRenderer(options) {
|
|
|
824
1230
|
for (const key in props) if (!(key in newProps$1) && key !== "children" && key !== "key" && key !== "ref") delete props[key];
|
|
825
1231
|
});
|
|
826
1232
|
}
|
|
827
|
-
const slotsRef =
|
|
1233
|
+
const slotsRef = oldInternal._slots;
|
|
828
1234
|
const newChildren = newVNode.props?.children;
|
|
829
1235
|
const newSlotsFromProps = newVNode.props?.slots;
|
|
830
1236
|
if (slotsRef) {
|
|
831
1237
|
if (newChildren !== void 0) slotsRef._children = newChildren;
|
|
832
1238
|
if (newSlotsFromProps !== void 0) slotsRef._slotsFromProps = newSlotsFromProps;
|
|
833
|
-
if (!
|
|
834
|
-
|
|
1239
|
+
if (!slotsRef._isPatching) {
|
|
1240
|
+
slotsRef._isPatching = true;
|
|
835
1241
|
try {
|
|
836
1242
|
untrack(() => {
|
|
837
1243
|
slotsRef._version.v++;
|
|
838
1244
|
});
|
|
839
1245
|
} finally {
|
|
840
|
-
|
|
1246
|
+
slotsRef._isPatching = false;
|
|
841
1247
|
}
|
|
842
1248
|
}
|
|
843
1249
|
}
|
|
@@ -951,9 +1357,10 @@ function createRenderer(options) {
|
|
|
951
1357
|
let exposeCalled = false;
|
|
952
1358
|
const { children, slots: slotsFromProps, ...propsData } = vnode.props || {};
|
|
953
1359
|
const reactiveProps = signal$1(propsData);
|
|
954
|
-
|
|
1360
|
+
const internalVNode = vnode;
|
|
1361
|
+
internalVNode._componentProps = reactiveProps;
|
|
955
1362
|
const slots = createSlots(children, slotsFromProps);
|
|
956
|
-
|
|
1363
|
+
internalVNode._slots = slots;
|
|
957
1364
|
const mountHooks = [];
|
|
958
1365
|
const cleanupHooks = [];
|
|
959
1366
|
const parentInstance = getCurrentInstance();
|
|
@@ -961,7 +1368,7 @@ function createRenderer(options) {
|
|
|
961
1368
|
const ctx = {
|
|
962
1369
|
el: container,
|
|
963
1370
|
signal: signal$1,
|
|
964
|
-
props: reactiveProps,
|
|
1371
|
+
props: createPropsAccessor(reactiveProps),
|
|
965
1372
|
slots,
|
|
966
1373
|
emit: (event, ...args) => {
|
|
967
1374
|
const handler = reactiveProps[`on${event[0].toUpperCase() + event.slice(1)}`];
|
|
@@ -977,8 +1384,11 @@ function createRenderer(options) {
|
|
|
977
1384
|
expose: (exposedValue) => {
|
|
978
1385
|
exposed = exposedValue;
|
|
979
1386
|
exposeCalled = true;
|
|
980
|
-
}
|
|
1387
|
+
},
|
|
1388
|
+
renderFn: null,
|
|
1389
|
+
update: () => {}
|
|
981
1390
|
};
|
|
1391
|
+
applyContextExtensions(ctx);
|
|
982
1392
|
ctx.__name = componentName;
|
|
983
1393
|
if (currentAppContext) ctx._appContext = currentAppContext;
|
|
984
1394
|
const componentInstance = {
|
|
@@ -989,7 +1399,9 @@ function createRenderer(options) {
|
|
|
989
1399
|
const prev = setCurrentInstance(ctx);
|
|
990
1400
|
let renderFn;
|
|
991
1401
|
try {
|
|
992
|
-
|
|
1402
|
+
const setupResult = setup(ctx);
|
|
1403
|
+
if (setupResult && typeof setupResult.then === "function") throw new Error(`Async setup in component "${componentName}" is only supported during SSR. On the client, use pre-loaded data from hydration or fetch in onMount.`);
|
|
1404
|
+
renderFn = setupResult;
|
|
993
1405
|
notifyComponentCreated(currentAppContext, componentInstance);
|
|
994
1406
|
} catch (err) {
|
|
995
1407
|
if (!handleComponentError(currentAppContext, err, componentInstance, "setup")) throw err;
|
|
@@ -1001,24 +1413,31 @@ function createRenderer(options) {
|
|
|
1001
1413
|
if (typeof vnode.props.ref === "function") vnode.props.ref(refValue);
|
|
1002
1414
|
else if (vnode.props.ref && typeof vnode.props.ref === "object") vnode.props.ref.current = refValue;
|
|
1003
1415
|
}
|
|
1004
|
-
if (renderFn)
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
const
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1416
|
+
if (renderFn) {
|
|
1417
|
+
ctx.renderFn = renderFn;
|
|
1418
|
+
const componentEffect = effect(() => {
|
|
1419
|
+
const prevInstance = setCurrentInstance(ctx);
|
|
1420
|
+
try {
|
|
1421
|
+
const subTreeResult = ctx.renderFn();
|
|
1422
|
+
if (subTreeResult == null) return;
|
|
1423
|
+
const subTree = normalizeSubTree(subTreeResult);
|
|
1424
|
+
const prevSubTree = internalVNode._subTree;
|
|
1425
|
+
if (prevSubTree) {
|
|
1426
|
+
patch(prevSubTree, subTree, container);
|
|
1427
|
+
notifyComponentUpdated(currentAppContext, componentInstance);
|
|
1428
|
+
} else mount(subTree, container, anchor);
|
|
1429
|
+
internalVNode._subTree = subTree;
|
|
1430
|
+
} catch (err) {
|
|
1431
|
+
if (!handleComponentError(currentAppContext, err, componentInstance, "render")) throw err;
|
|
1432
|
+
} finally {
|
|
1433
|
+
setCurrentInstance(prevInstance);
|
|
1434
|
+
}
|
|
1435
|
+
});
|
|
1436
|
+
internalVNode._effect = componentEffect;
|
|
1437
|
+
ctx.update = () => {
|
|
1438
|
+
componentEffect();
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1022
1441
|
const mountCtx = { el: container };
|
|
1023
1442
|
mountHooks.forEach((hook) => hook(mountCtx));
|
|
1024
1443
|
notifyComponentMounted(currentAppContext, componentInstance);
|
|
@@ -1027,81 +1446,12 @@ function createRenderer(options) {
|
|
|
1027
1446
|
cleanupHooks.forEach((hook) => hook(mountCtx));
|
|
1028
1447
|
};
|
|
1029
1448
|
}
|
|
1030
|
-
/**
|
|
1031
|
-
* Create slots object from children and slots prop.
|
|
1032
|
-
* Uses a version signal to trigger re-renders when children change.
|
|
1033
|
-
* Supports named slots via:
|
|
1034
|
-
* - `slots` prop object (e.g., slots={{ header: () => <div>...</div> }})
|
|
1035
|
-
* - `slot` prop on children (e.g., <div slot="header">...</div>)
|
|
1036
|
-
*/
|
|
1037
|
-
function createSlots(children, slotsFromProps) {
|
|
1038
|
-
const versionSignal = signal$1({ v: 0 });
|
|
1039
|
-
function extractNamedSlotsFromChildren(c) {
|
|
1040
|
-
const defaultChildren = [];
|
|
1041
|
-
const namedSlots = {};
|
|
1042
|
-
if (c == null) return {
|
|
1043
|
-
defaultChildren,
|
|
1044
|
-
namedSlots
|
|
1045
|
-
};
|
|
1046
|
-
const items = Array.isArray(c) ? c : [c];
|
|
1047
|
-
for (const child of items) if (child && typeof child === "object" && child.props && child.props.slot) {
|
|
1048
|
-
const slotName = child.props.slot;
|
|
1049
|
-
if (!namedSlots[slotName]) namedSlots[slotName] = [];
|
|
1050
|
-
namedSlots[slotName].push(child);
|
|
1051
|
-
} else defaultChildren.push(child);
|
|
1052
|
-
return {
|
|
1053
|
-
defaultChildren,
|
|
1054
|
-
namedSlots
|
|
1055
|
-
};
|
|
1056
|
-
}
|
|
1057
|
-
const slotsObj = {
|
|
1058
|
-
_children: children,
|
|
1059
|
-
_slotsFromProps: slotsFromProps || {},
|
|
1060
|
-
_version: versionSignal,
|
|
1061
|
-
default: function() {
|
|
1062
|
-
this._version.v;
|
|
1063
|
-
const c = this._children;
|
|
1064
|
-
const { defaultChildren } = extractNamedSlotsFromChildren(c);
|
|
1065
|
-
return defaultChildren;
|
|
1066
|
-
}
|
|
1067
|
-
};
|
|
1068
|
-
return new Proxy(slotsObj, { get(target, prop) {
|
|
1069
|
-
if (prop in target) return target[prop];
|
|
1070
|
-
if (typeof prop === "string") return function(scopedProps) {
|
|
1071
|
-
target._version.v;
|
|
1072
|
-
if (target._slotsFromProps && typeof target._slotsFromProps[prop] === "function") {
|
|
1073
|
-
const result = target._slotsFromProps[prop](scopedProps);
|
|
1074
|
-
if (result == null) return [];
|
|
1075
|
-
return Array.isArray(result) ? result : [result];
|
|
1076
|
-
}
|
|
1077
|
-
const { namedSlots } = extractNamedSlotsFromChildren(target._children);
|
|
1078
|
-
return namedSlots[prop] || [];
|
|
1079
|
-
};
|
|
1080
|
-
} });
|
|
1081
|
-
}
|
|
1082
|
-
/**
|
|
1083
|
-
* Normalize render result to a VNode (wrapping arrays in Fragment)
|
|
1084
|
-
*/
|
|
1085
|
-
function normalizeSubTree(result) {
|
|
1086
|
-
if (Array.isArray(result)) return {
|
|
1087
|
-
type: Fragment,
|
|
1088
|
-
props: {},
|
|
1089
|
-
key: null,
|
|
1090
|
-
children: result,
|
|
1091
|
-
dom: null
|
|
1092
|
-
};
|
|
1093
|
-
if (typeof result === "string" || typeof result === "number") return {
|
|
1094
|
-
type: Text,
|
|
1095
|
-
props: {},
|
|
1096
|
-
key: null,
|
|
1097
|
-
children: [],
|
|
1098
|
-
dom: null,
|
|
1099
|
-
text: result
|
|
1100
|
-
};
|
|
1101
|
-
return result;
|
|
1102
|
-
}
|
|
1103
1449
|
return {
|
|
1104
1450
|
render,
|
|
1451
|
+
patch,
|
|
1452
|
+
mount,
|
|
1453
|
+
unmount,
|
|
1454
|
+
mountComponent,
|
|
1105
1455
|
createApp: (rootComponent) => {
|
|
1106
1456
|
return { mount(selectorOrContainer) {
|
|
1107
1457
|
let container = null;
|
|
@@ -1119,5 +1469,5 @@ function createRenderer(options) {
|
|
|
1119
1469
|
}
|
|
1120
1470
|
|
|
1121
1471
|
//#endregion
|
|
1122
|
-
export { AppContextKey, Fragment, InstanceLifetimes, SubscriptionHandler, Text, Utils, createPropsProxy, createRenderer, createTopic, defineApp, defineComponent, defineFactory, defineInjectable, defineProvide, defineStore, getComponentMeta, getComponentPlugins, getCurrentInstance, getDefaultMount, getPlatformSyncProcessor, guid, handleComponentError, inject, injectApp, jsx, jsxDEV, jsxs, notifyComponentCreated, notifyComponentMounted, notifyComponentUnmounted, notifyComponentUpdated, onCleanup, onMount, provide, registerComponentPlugin, setCurrentInstance, setDefaultMount, setPlatformSyncProcessor, signal, toSubscriber, valueOf };
|
|
1472
|
+
export { AppContextKey, Fragment, InstanceLifetimes, SubscriptionHandler, Suspense, Text, Utils, applyContextExtensions, createPropsAccessor, createPropsProxy, createRenderer, createSlots, createTopic, defineApp, defineComponent, defineFactory, defineInjectable, defineProvide, defineStore, getComponentMeta, getComponentPlugins, getCurrentInstance, getDefaultMount, getPlatformSyncProcessor, guid, handleComponentError, inject, injectApp, isLazyComponent, jsx, jsxDEV, jsxs, lazy, normalizeSubTree, notifyComponentCreated, notifyComponentMounted, notifyComponentUnmounted, notifyComponentUpdated, onCleanup, onMount, provide, registerComponentPlugin, registerContextExtension, registerPendingPromise, setCurrentInstance, setDefaultMount, setPlatformSyncProcessor, signal, toSubscriber, valueOf };
|
|
1123
1473
|
//# sourceMappingURL=index.js.map
|