ilha 0.9.5 → 0.10.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/README.md +102 -41
- package/dist/{index-Dx_q_SGo.d.ts → index-Bi_I3qEL.d.ts} +106 -46
- package/dist/index.d.ts +2 -2
- package/dist/index.js +499 -69
- package/dist/jsx-dev-runtime.d.ts +1 -1
- package/dist/jsx-runtime-DJ2B4fvA.d.ts +633 -0
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +96 -17
- package/package.json +1 -1
- package/dist/jsx-runtime-DysRyz9R.d.ts +0 -37
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { computed, effect, endBatch, setActiveSub, signal, startBatch } from "alien-signals";
|
|
2
2
|
|
|
3
3
|
//#region src/index.ts
|
|
4
|
-
const MAX_SNAPSHOT_CHARS =
|
|
4
|
+
const MAX_SNAPSHOT_CHARS = 262144;
|
|
5
5
|
const MAX_SNAPSHOT_DEPTH = 32;
|
|
6
6
|
function exceedsMaxDepth(value, depth) {
|
|
7
7
|
if (depth > MAX_SNAPSHOT_DEPTH) return true;
|
|
@@ -11,7 +11,7 @@ function exceedsMaxDepth(value, depth) {
|
|
|
11
11
|
return false;
|
|
12
12
|
}
|
|
13
13
|
for (const key in value) {
|
|
14
|
-
if (!Object.
|
|
14
|
+
if (!Object.hasOwn(value, key)) continue;
|
|
15
15
|
if (exceedsMaxDepth(value[key], depth + 1)) return true;
|
|
16
16
|
}
|
|
17
17
|
return false;
|
|
@@ -60,7 +60,7 @@ function shallowEqualInput(a, b) {
|
|
|
60
60
|
const bk = Object.keys(b);
|
|
61
61
|
if (ak.length !== bk.length) return false;
|
|
62
62
|
for (const k of ak) {
|
|
63
|
-
if (!Object.
|
|
63
|
+
if (!Object.hasOwn(b, k)) return false;
|
|
64
64
|
if (!Object.is(a[k], b[k])) return false;
|
|
65
65
|
}
|
|
66
66
|
return true;
|
|
@@ -103,10 +103,10 @@ function getIslandSlotTag(island) {
|
|
|
103
103
|
if (typeof tag === "string" && tag.length > 0) return tag;
|
|
104
104
|
return "div";
|
|
105
105
|
}
|
|
106
|
-
function wrapIslandSlotHtml(tag, id, propsAttr, inner) {
|
|
106
|
+
function wrapIslandSlotHtml({ tag, id, propsAttr, inner }) {
|
|
107
107
|
return `<${tag} ${SLOT_ATTR}="${escapeHtml(id)}"${propsAttr}>${inner}</${tag}>`;
|
|
108
108
|
}
|
|
109
|
-
function isStableInlineSlotMount(initialHtml, renderedHtml, slotIds) {
|
|
109
|
+
function isStableInlineSlotMount({ initialHtml, renderedHtml, slotIds }) {
|
|
110
110
|
if (typeof document === "undefined") return false;
|
|
111
111
|
const initialRoot = parseHtmlFragment(initialHtml);
|
|
112
112
|
const renderedRoot = parseHtmlFragment(renderedHtml);
|
|
@@ -485,7 +485,8 @@ const PROPS_ATTR = "data-ilha-props";
|
|
|
485
485
|
const STATE_ATTR = "data-ilha-state";
|
|
486
486
|
const CSS_ATTR = "data-ilha-css";
|
|
487
487
|
function buildScopedStyle(css) {
|
|
488
|
-
|
|
488
|
+
const safeCss = css.replace(/<\/style/gi, "<\\/style");
|
|
489
|
+
return `<style ${CSS_ATTR}>@scope (:scope) to ([data-ilha]){${safeCss}}</style>`;
|
|
489
490
|
}
|
|
490
491
|
const RENDER_CTX_STACK = Symbol.for("ilha.renderCtxStack");
|
|
491
492
|
function renderCtxStack() {
|
|
@@ -498,7 +499,8 @@ function pushRenderCtx(liveHost, asyncChildren) {
|
|
|
498
499
|
positional: 0,
|
|
499
500
|
liveHost,
|
|
500
501
|
pending: asyncChildren ? /* @__PURE__ */ new Map() : void 0,
|
|
501
|
-
binds: []
|
|
502
|
+
binds: [],
|
|
503
|
+
events: []
|
|
502
504
|
};
|
|
503
505
|
renderCtxStack().push(ctx);
|
|
504
506
|
return ctx;
|
|
@@ -518,7 +520,7 @@ function isIslandCall(v) {
|
|
|
518
520
|
if (ISLAND_CALL in v) return true;
|
|
519
521
|
return typeof v === "object" && "island" in v && isIsland(v.island);
|
|
520
522
|
}
|
|
521
|
-
function emitIslandSlot(island, props, key) {
|
|
523
|
+
function emitIslandSlot({ island, props, key }) {
|
|
522
524
|
const ctx = currentRenderCtx();
|
|
523
525
|
let id;
|
|
524
526
|
if (key !== void 0) id = `k:${key}`;
|
|
@@ -530,22 +532,42 @@ function emitIslandSlot(island, props, key) {
|
|
|
530
532
|
const slotTag = getIslandSlotTag(island);
|
|
531
533
|
const attrProps = slotPropsForAttr(props);
|
|
532
534
|
const propsAttr = attrProps ? ` ${PROPS_ATTR}='${escapeHtml(JSON.stringify(attrProps))}'` : "";
|
|
533
|
-
if (ctx?.liveHost) return wrapIslandSlotHtml(
|
|
535
|
+
if (ctx?.liveHost) return wrapIslandSlotHtml({
|
|
536
|
+
tag: slotTag,
|
|
537
|
+
id,
|
|
538
|
+
propsAttr,
|
|
539
|
+
inner: ""
|
|
540
|
+
});
|
|
534
541
|
if (ctx?.pending) {
|
|
535
542
|
popRenderCtx();
|
|
536
543
|
try {
|
|
537
544
|
const result = island(props);
|
|
538
545
|
if (result instanceof Promise) {
|
|
539
546
|
ctx.pending.set(id, result.then(String));
|
|
540
|
-
return wrapIslandSlotHtml(
|
|
547
|
+
return wrapIslandSlotHtml({
|
|
548
|
+
tag: slotTag,
|
|
549
|
+
id,
|
|
550
|
+
propsAttr,
|
|
551
|
+
inner: asyncSlotMarker(id)
|
|
552
|
+
});
|
|
541
553
|
}
|
|
542
|
-
return wrapIslandSlotHtml(
|
|
554
|
+
return wrapIslandSlotHtml({
|
|
555
|
+
tag: slotTag,
|
|
556
|
+
id,
|
|
557
|
+
propsAttr,
|
|
558
|
+
inner: String(result)
|
|
559
|
+
});
|
|
543
560
|
} finally {
|
|
544
561
|
renderCtxStack().push(ctx);
|
|
545
562
|
}
|
|
546
563
|
}
|
|
547
564
|
const inner = island.toString(props);
|
|
548
|
-
return wrapIslandSlotHtml(
|
|
565
|
+
return wrapIslandSlotHtml({
|
|
566
|
+
tag: slotTag,
|
|
567
|
+
id,
|
|
568
|
+
propsAttr,
|
|
569
|
+
inner
|
|
570
|
+
});
|
|
549
571
|
}
|
|
550
572
|
function asyncSlotMarker(id) {
|
|
551
573
|
return `<!--ilha-async:${escapeHtml(id)}-->`;
|
|
@@ -557,6 +579,15 @@ async function resolveAsyncChildren(html, pending) {
|
|
|
557
579
|
}
|
|
558
580
|
return html;
|
|
559
581
|
}
|
|
582
|
+
function resolveSignalSetter(read, value) {
|
|
583
|
+
if (typeof value !== "function") return value;
|
|
584
|
+
const prevSub = setActiveSub(void 0);
|
|
585
|
+
try {
|
|
586
|
+
return value(read());
|
|
587
|
+
} finally {
|
|
588
|
+
setActiveSub(prevSub);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
560
591
|
function markSignalAccessor(fn) {
|
|
561
592
|
fn[SIGNAL_ACCESSOR] = true;
|
|
562
593
|
const accessor = fn;
|
|
@@ -574,14 +605,19 @@ function getAtPath(obj, path) {
|
|
|
574
605
|
}
|
|
575
606
|
return cur;
|
|
576
607
|
}
|
|
577
|
-
function setAtPath(
|
|
608
|
+
function setAtPath({ object, path, value }) {
|
|
609
|
+
const obj = object;
|
|
578
610
|
if (path.length === 0) return value;
|
|
579
611
|
const [head, ...rest] = path;
|
|
580
612
|
if (Array.isArray(obj)) {
|
|
581
613
|
const idx = head;
|
|
582
614
|
if (idx < 0 || idx >= obj.length) return obj;
|
|
583
615
|
const next = obj[idx];
|
|
584
|
-
const updated = rest.length === 0 ? value : setAtPath(
|
|
616
|
+
const updated = rest.length === 0 ? value : setAtPath({
|
|
617
|
+
object: next,
|
|
618
|
+
path: rest,
|
|
619
|
+
value
|
|
620
|
+
});
|
|
585
621
|
if (Object.is(next, updated)) return obj;
|
|
586
622
|
const copy = obj.slice();
|
|
587
623
|
copy[idx] = updated;
|
|
@@ -591,7 +627,11 @@ function setAtPath(obj, path, value) {
|
|
|
591
627
|
const record = obj;
|
|
592
628
|
const key = String(head);
|
|
593
629
|
const next = record[key];
|
|
594
|
-
const updated = rest.length === 0 ? value : setAtPath(
|
|
630
|
+
const updated = rest.length === 0 ? value : setAtPath({
|
|
631
|
+
object: next,
|
|
632
|
+
path: rest,
|
|
633
|
+
value
|
|
634
|
+
});
|
|
595
635
|
if (rest.length === 0) {
|
|
596
636
|
if (Object.is(next, value)) return obj;
|
|
597
637
|
} else if (Object.is(next, updated)) return obj;
|
|
@@ -600,7 +640,11 @@ function setAtPath(obj, path, value) {
|
|
|
600
640
|
[key]: updated
|
|
601
641
|
};
|
|
602
642
|
}
|
|
603
|
-
return rest.length === 0 ? value : setAtPath(
|
|
643
|
+
return rest.length === 0 ? value : setAtPath({
|
|
644
|
+
object: void 0,
|
|
645
|
+
path: rest,
|
|
646
|
+
value
|
|
647
|
+
});
|
|
604
648
|
}
|
|
605
649
|
function toPathSegment(prop) {
|
|
606
650
|
if (typeof prop === "symbol") return null;
|
|
@@ -629,8 +673,15 @@ function createSelectAccessor(root, selector) {
|
|
|
629
673
|
if (!Object.is(selected, resolved)) throw new Error("select(): selector must only traverse nested properties or array indexes — derived or transformed values are not supported.");
|
|
630
674
|
return markSignalAccessor((...args) => {
|
|
631
675
|
if (args.length === 0) return path.length === 0 ? selector(root()) : getAtPath(root(), path);
|
|
632
|
-
const
|
|
633
|
-
|
|
676
|
+
const previousRoot = root();
|
|
677
|
+
const previousSelected = path.length === 0 ? selector(previousRoot) : getAtPath(previousRoot, path);
|
|
678
|
+
const value = resolveSignalSetter(() => previousSelected, args[0]);
|
|
679
|
+
const next = path.length === 0 ? value : setAtPath({
|
|
680
|
+
object: previousRoot,
|
|
681
|
+
path,
|
|
682
|
+
value
|
|
683
|
+
});
|
|
684
|
+
if (!Object.is(previousRoot, next)) root((() => next));
|
|
634
685
|
});
|
|
635
686
|
}
|
|
636
687
|
function ilhaRaw(value) {
|
|
@@ -654,9 +705,17 @@ function interpolateValue(v) {
|
|
|
654
705
|
if (isRawHtml(v)) return v.value;
|
|
655
706
|
if (isIslandCall(v)) {
|
|
656
707
|
const call = typeof v === "function" ? v() : v;
|
|
657
|
-
return emitIslandSlot(
|
|
708
|
+
return emitIslandSlot({
|
|
709
|
+
island: call.island,
|
|
710
|
+
props: call.props,
|
|
711
|
+
key: call.key
|
|
712
|
+
});
|
|
658
713
|
}
|
|
659
|
-
if (isIsland(v)) return emitIslandSlot(
|
|
714
|
+
if (isIsland(v)) return emitIslandSlot({
|
|
715
|
+
island: v,
|
|
716
|
+
props: void 0,
|
|
717
|
+
key: void 0
|
|
718
|
+
});
|
|
660
719
|
if (isSignalAccessor(v)) return escapeHtml(v());
|
|
661
720
|
if (typeof v === "function") return escapeHtml(v());
|
|
662
721
|
return escapeHtml(v);
|
|
@@ -672,6 +731,30 @@ const BIND_VALID_KINDS = /* @__PURE__ */ new Set([
|
|
|
672
731
|
"this"
|
|
673
732
|
]);
|
|
674
733
|
const BIND_PREFIX_RE = /\bbind:([a-zA-Z]+)\s*=\s*("|')?$/;
|
|
734
|
+
const EVENT_PREFIX_RE = /(?:^|\s)on([a-z][a-z0-9-]*)(?::([a-z][a-z0-9-]*))?\s*=\s*("|')?$/;
|
|
735
|
+
const NATIVE_EVENT_MODIFIERS = /* @__PURE__ */ new Set([
|
|
736
|
+
"abortable",
|
|
737
|
+
"once",
|
|
738
|
+
"capture",
|
|
739
|
+
"passive"
|
|
740
|
+
]);
|
|
741
|
+
function isEventAttributePosition(source, index) {
|
|
742
|
+
let open = -1;
|
|
743
|
+
let quote = null;
|
|
744
|
+
for (let i = 0; i < index; i++) {
|
|
745
|
+
const char = source[i];
|
|
746
|
+
if (open === -1) {
|
|
747
|
+
if (char === "<") open = i;
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
if (quote) {
|
|
751
|
+
if (char === quote) quote = null;
|
|
752
|
+
} else if (char === "\"" || char === "'") quote = char;
|
|
753
|
+
else if (char === ">") open = -1;
|
|
754
|
+
}
|
|
755
|
+
if (open === -1 || quote !== null) return false;
|
|
756
|
+
return !/^\s*[!/?]/.test(source.slice(open + 1, index));
|
|
757
|
+
}
|
|
675
758
|
function findTagCloseIndex(chunk, initialQuote) {
|
|
676
759
|
let quote = initialQuote;
|
|
677
760
|
for (let i = 0; i < chunk.length; i++) {
|
|
@@ -706,7 +789,7 @@ function extractElementStaticValue(prefix) {
|
|
|
706
789
|
if (!m) return null;
|
|
707
790
|
return m[2] ?? m[3] ?? m[4] ?? null;
|
|
708
791
|
}
|
|
709
|
-
function emitBindSSR(kind, index, accessor, prefixForStaticPeek) {
|
|
792
|
+
function emitBindSSR({ kind, index, accessor, prefixForStaticPeek }) {
|
|
710
793
|
const spec = `${kind}:${index}`;
|
|
711
794
|
let v;
|
|
712
795
|
try {
|
|
@@ -733,6 +816,7 @@ function ilhaHtml(strings, ...values) {
|
|
|
733
816
|
let result = "";
|
|
734
817
|
let stripLeadingQuote = null;
|
|
735
818
|
let pendingBindSpecs = "";
|
|
819
|
+
let pendingEventSpecs = "";
|
|
736
820
|
let pendingQuote = null;
|
|
737
821
|
const ctx = currentRenderCtx();
|
|
738
822
|
for (let i = 0; i < strings.length; i++) {
|
|
@@ -741,11 +825,15 @@ function ilhaHtml(strings, ...values) {
|
|
|
741
825
|
if (chunk.startsWith(stripLeadingQuote)) chunk = chunk.slice(1);
|
|
742
826
|
stripLeadingQuote = null;
|
|
743
827
|
}
|
|
744
|
-
if (pendingBindSpecs !== "") {
|
|
828
|
+
if (pendingBindSpecs !== "" || pendingEventSpecs !== "") {
|
|
745
829
|
const { index: gtIdx, quote } = findTagCloseIndex(chunk, pendingQuote);
|
|
746
830
|
if (gtIdx !== -1) {
|
|
747
|
-
|
|
831
|
+
const head = chunk.slice(0, gtIdx).replace(/\s*\/\s*$/, "");
|
|
832
|
+
const bindAttr = pendingBindSpecs ? ` data-ilha-bind="${pendingBindSpecs}"` : "";
|
|
833
|
+
const eventAttr = pendingEventSpecs ? ` data-ilha-on="${pendingEventSpecs}"` : "";
|
|
834
|
+
chunk = head + bindAttr + eventAttr + ">" + chunk.slice(gtIdx + 1);
|
|
748
835
|
pendingBindSpecs = "";
|
|
836
|
+
pendingEventSpecs = "";
|
|
749
837
|
pendingQuote = null;
|
|
750
838
|
} else pendingQuote = quote;
|
|
751
839
|
}
|
|
@@ -754,6 +842,29 @@ function ilhaHtml(strings, ...values) {
|
|
|
754
842
|
continue;
|
|
755
843
|
}
|
|
756
844
|
const value = values[i];
|
|
845
|
+
const eventMatch = chunk.match(EVENT_PREFIX_RE);
|
|
846
|
+
if (eventMatch && isEventAttributePosition(result + chunk, result.length + chunk.length - eventMatch[0].length)) {
|
|
847
|
+
const eventName = eventMatch[1];
|
|
848
|
+
const rawModifier = eventMatch[2];
|
|
849
|
+
const modifier = NATIVE_EVENT_MODIFIERS.has(rawModifier) ? rawModifier : void 0;
|
|
850
|
+
const openQuote = eventMatch[3] ?? null;
|
|
851
|
+
const matchStart = chunk.length - eventMatch[0].length;
|
|
852
|
+
const cleanPrefix = chunk.slice(0, matchStart).replace(/\s+$/, "");
|
|
853
|
+
result += cleanPrefix;
|
|
854
|
+
if (ctx && typeof value === "function") {
|
|
855
|
+
const index = ctx.events.length;
|
|
856
|
+
if (rawModifier && !modifier) {} else {
|
|
857
|
+
ctx.events.push({
|
|
858
|
+
type: eventName,
|
|
859
|
+
handler: value,
|
|
860
|
+
modifier
|
|
861
|
+
});
|
|
862
|
+
pendingEventSpecs += (pendingEventSpecs ? "," : "") + `${eventName}:${index}`;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if (openQuote) stripLeadingQuote = openQuote;
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
757
868
|
const m = chunk.match(BIND_PREFIX_RE);
|
|
758
869
|
if (m && isSignalAccessor(value)) {
|
|
759
870
|
const name = m[1];
|
|
@@ -776,7 +887,12 @@ function ilhaHtml(strings, ...values) {
|
|
|
776
887
|
kind: name,
|
|
777
888
|
accessor: value
|
|
778
889
|
});
|
|
779
|
-
const [valueAttrs, spec] = emitBindSSR(
|
|
890
|
+
const [valueAttrs, spec] = emitBindSSR({
|
|
891
|
+
kind: name,
|
|
892
|
+
index,
|
|
893
|
+
accessor: value,
|
|
894
|
+
prefixForStaticPeek: cleanPrefix
|
|
895
|
+
});
|
|
780
896
|
result += cleanPrefix + valueAttrs;
|
|
781
897
|
pendingBindSpecs += (pendingBindSpecs ? "," : "") + spec;
|
|
782
898
|
if (openQuote) stripLeadingQuote = openQuote;
|
|
@@ -785,6 +901,7 @@ function ilhaHtml(strings, ...values) {
|
|
|
785
901
|
result += chunk + interpolateValue(value);
|
|
786
902
|
}
|
|
787
903
|
if (pendingBindSpecs !== "") result += ` data-ilha-bind="${pendingBindSpecs}"`;
|
|
904
|
+
if (pendingEventSpecs !== "") result += ` data-ilha-on="${pendingEventSpecs}"`;
|
|
788
905
|
return {
|
|
789
906
|
[RAW]: true,
|
|
790
907
|
value: dedentString(result)
|
|
@@ -803,7 +920,7 @@ function ilhaContextFn(key, initial) {
|
|
|
803
920
|
const s = signal(initial);
|
|
804
921
|
const accessor = (...args) => {
|
|
805
922
|
if (args.length === 0) return s();
|
|
806
|
-
s(args[0]);
|
|
923
|
+
s(resolveSignalSetter(() => s(), args[0]));
|
|
807
924
|
};
|
|
808
925
|
contextRegistry.set(key, accessor);
|
|
809
926
|
return accessor;
|
|
@@ -833,7 +950,7 @@ function ilhaSignal(initial) {
|
|
|
833
950
|
const s = signal(initial);
|
|
834
951
|
return markSignalAccessor((...args) => {
|
|
835
952
|
if (args.length === 0) return s();
|
|
836
|
-
s(args[0]);
|
|
953
|
+
s(resolveSignalSetter(() => s(), args[0]));
|
|
837
954
|
});
|
|
838
955
|
}
|
|
839
956
|
/**
|
|
@@ -960,7 +1077,7 @@ function buildDerivedAccessors(envelopes) {
|
|
|
960
1077
|
return accessors.get(key) ?? defaultDerivedAccessor();
|
|
961
1078
|
} });
|
|
962
1079
|
}
|
|
963
|
-
function createDerivedProxy(entries, state, input, derivedSnapshot) {
|
|
1080
|
+
function createDerivedProxy({ entries, state, input, derivedSnapshot }) {
|
|
964
1081
|
const envelopes = /* @__PURE__ */ new Map();
|
|
965
1082
|
for (const entry of entries) {
|
|
966
1083
|
let initialEnvelope;
|
|
@@ -1067,9 +1184,10 @@ function createDerivedProxy(entries, state, input, derivedSnapshot) {
|
|
|
1067
1184
|
return;
|
|
1068
1185
|
}
|
|
1069
1186
|
const prevSub = setActiveSub(void 0);
|
|
1187
|
+
const prevVal = env();
|
|
1070
1188
|
env({
|
|
1071
1189
|
loading: true,
|
|
1072
|
-
value:
|
|
1190
|
+
value: prevVal.value,
|
|
1073
1191
|
error: void 0
|
|
1074
1192
|
});
|
|
1075
1193
|
setActiveSub(prevSub);
|
|
@@ -1102,6 +1220,7 @@ function createDerivedProxy(entries, state, input, derivedSnapshot) {
|
|
|
1102
1220
|
};
|
|
1103
1221
|
}
|
|
1104
1222
|
const BIND_SENTINEL_ATTR = "data-ilha-bind";
|
|
1223
|
+
const EVENT_SENTINEL_ATTR = "data-ilha-on";
|
|
1105
1224
|
function parseBindSentinel(value) {
|
|
1106
1225
|
const out = [];
|
|
1107
1226
|
for (const part of value.split(",")) {
|
|
@@ -1181,12 +1300,86 @@ function resolveBindOps(el, kind) {
|
|
|
1181
1300
|
}
|
|
1182
1301
|
}
|
|
1183
1302
|
}
|
|
1303
|
+
function templateElementBelongsToHost(host, candidate) {
|
|
1304
|
+
if (candidate === host) return true;
|
|
1305
|
+
let current = candidate;
|
|
1306
|
+
while (current && current !== host) {
|
|
1307
|
+
if (current.hasAttribute(SLOT_ATTR) || current.hasAttribute("data-ilha")) return false;
|
|
1308
|
+
current = current.parentElement;
|
|
1309
|
+
}
|
|
1310
|
+
return current === host;
|
|
1311
|
+
}
|
|
1312
|
+
function applyJsxEvents({ host, records, reportError, unmountSignal, onceFired }) {
|
|
1313
|
+
if (records.length === 0) return () => {};
|
|
1314
|
+
const cleanups = [];
|
|
1315
|
+
const elements = [];
|
|
1316
|
+
if (host.hasAttribute(EVENT_SENTINEL_ATTR)) elements.push(host);
|
|
1317
|
+
elements.push(...Array.from(host.querySelectorAll(`[${EVENT_SENTINEL_ATTR}]`)).filter((element) => templateElementBelongsToHost(host, element)));
|
|
1318
|
+
for (const el of elements) {
|
|
1319
|
+
const sentinel = el.getAttribute(EVENT_SENTINEL_ATTR) ?? "";
|
|
1320
|
+
for (const part of sentinel.split(",")) {
|
|
1321
|
+
const separator = part.lastIndexOf(":");
|
|
1322
|
+
if (separator < 1) continue;
|
|
1323
|
+
const type = part.slice(0, separator);
|
|
1324
|
+
const index = Number(part.slice(separator + 1));
|
|
1325
|
+
if (!Number.isInteger(index) || index < 0) continue;
|
|
1326
|
+
const record = records[index];
|
|
1327
|
+
if (!record || record.type !== type) continue;
|
|
1328
|
+
if (record.modifier === "once" && onceFired.get(el)?.has(type)) continue;
|
|
1329
|
+
const listenerController = new AbortController();
|
|
1330
|
+
let invocationController;
|
|
1331
|
+
const options = {
|
|
1332
|
+
once: record.modifier === "once",
|
|
1333
|
+
capture: record.modifier === "capture",
|
|
1334
|
+
passive: record.modifier === "passive"
|
|
1335
|
+
};
|
|
1336
|
+
const reportHandlerError = (error) => {
|
|
1337
|
+
if (error?.name !== "AbortError") reportError(error);
|
|
1338
|
+
};
|
|
1339
|
+
const listener = (event) => {
|
|
1340
|
+
if (record.modifier === "once") {
|
|
1341
|
+
let fired = onceFired.get(el);
|
|
1342
|
+
if (!fired) onceFired.set(el, fired = /* @__PURE__ */ new Set());
|
|
1343
|
+
fired.add(type);
|
|
1344
|
+
}
|
|
1345
|
+
if (record.modifier === "abortable") {
|
|
1346
|
+
invocationController?.abort();
|
|
1347
|
+
invocationController = new AbortController();
|
|
1348
|
+
}
|
|
1349
|
+
const signal = invocationController ? anySignal([listenerController.signal, invocationController.signal]) : listenerController.signal;
|
|
1350
|
+
let result;
|
|
1351
|
+
startBatch();
|
|
1352
|
+
try {
|
|
1353
|
+
result = record.handler(event, { signal });
|
|
1354
|
+
} catch (error) {
|
|
1355
|
+
reportHandlerError(error);
|
|
1356
|
+
return;
|
|
1357
|
+
} finally {
|
|
1358
|
+
endBatch();
|
|
1359
|
+
}
|
|
1360
|
+
if (result != null && typeof result.then === "function") Promise.resolve(result).catch(reportHandlerError);
|
|
1361
|
+
};
|
|
1362
|
+
const stop = () => {
|
|
1363
|
+
listenerController.abort();
|
|
1364
|
+
invocationController?.abort();
|
|
1365
|
+
el.removeEventListener(type, listener, options);
|
|
1366
|
+
};
|
|
1367
|
+
el.addEventListener(type, listener, options);
|
|
1368
|
+
unmountSignal.addEventListener("abort", stop, { once: true });
|
|
1369
|
+
cleanups.push(() => {
|
|
1370
|
+
unmountSignal.removeEventListener("abort", stop);
|
|
1371
|
+
stop();
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
return () => cleanups.forEach((cleanup) => cleanup());
|
|
1376
|
+
}
|
|
1184
1377
|
function applyTemplateBindings(host, binds) {
|
|
1185
1378
|
if (binds.length === 0) return () => {};
|
|
1186
1379
|
const cleanups = [];
|
|
1187
1380
|
const elements = [];
|
|
1188
1381
|
if (host.hasAttribute(BIND_SENTINEL_ATTR)) elements.push(host);
|
|
1189
|
-
for (const el of host.querySelectorAll(`[${BIND_SENTINEL_ATTR}]`)) elements.push(el);
|
|
1382
|
+
for (const el of host.querySelectorAll(`[${BIND_SENTINEL_ATTR}]`)) if (templateElementBelongsToHost(host, el)) elements.push(el);
|
|
1190
1383
|
for (const el of elements) {
|
|
1191
1384
|
const specs = parseBindSentinel(el.getAttribute(BIND_SENTINEL_ATTR));
|
|
1192
1385
|
for (const spec of specs) {
|
|
@@ -1328,6 +1521,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1328
1521
|
defaultInput,
|
|
1329
1522
|
states: [],
|
|
1330
1523
|
deriveds: [],
|
|
1524
|
+
actions: [],
|
|
1331
1525
|
ons: [],
|
|
1332
1526
|
effects: [],
|
|
1333
1527
|
onMounts: [],
|
|
@@ -1363,6 +1557,15 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1363
1557
|
}]
|
|
1364
1558
|
});
|
|
1365
1559
|
}
|
|
1560
|
+
action(key, fn) {
|
|
1561
|
+
return new IlhaBuilder({
|
|
1562
|
+
...this._cfg,
|
|
1563
|
+
actions: [...this._cfg.actions, {
|
|
1564
|
+
key,
|
|
1565
|
+
fn
|
|
1566
|
+
}]
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1366
1569
|
on(selectorOrCombined, handler) {
|
|
1367
1570
|
const parsed = parseOnArgs(selectorOrCombined);
|
|
1368
1571
|
return new IlhaBuilder({
|
|
@@ -1417,7 +1620,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1417
1620
|
});
|
|
1418
1621
|
}
|
|
1419
1622
|
render(fn) {
|
|
1420
|
-
const { schema, defaultInput, states, deriveds, ons, effects, onMounts, onErrors, transition, css: cssSource, as: slotAs } = this._cfg;
|
|
1623
|
+
const { schema, defaultInput, states, deriveds, actions, ons, effects, onMounts, onErrors, transition, css: cssSource, as: slotAs } = this._cfg;
|
|
1421
1624
|
const stylePrefix = cssSource != null ? buildScopedStyle(cssSource) : "";
|
|
1422
1625
|
const configuredSlotTag = slotAs ?? "div";
|
|
1423
1626
|
function resolveInput(props) {
|
|
@@ -1428,26 +1631,29 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1428
1631
|
if (!schema) return merged;
|
|
1429
1632
|
return validateSchema(schema, merged);
|
|
1430
1633
|
}
|
|
1431
|
-
function renderWithCtx(
|
|
1634
|
+
function renderWithCtx({ render, liveHost, asyncChildren }) {
|
|
1432
1635
|
const ctx = pushRenderCtx(liveHost, asyncChildren);
|
|
1433
1636
|
try {
|
|
1434
|
-
const html = unwrapHtml(
|
|
1637
|
+
const html = unwrapHtml(render());
|
|
1435
1638
|
const slots = ctx.slots;
|
|
1436
1639
|
const binds = ctx.binds;
|
|
1640
|
+
const events = ctx.events;
|
|
1437
1641
|
if (ctx.pending && ctx.pending.size > 0) {
|
|
1438
1642
|
const pending = ctx.pending;
|
|
1439
1643
|
return (async () => {
|
|
1440
1644
|
return {
|
|
1441
1645
|
html: await resolveAsyncChildren(html, pending),
|
|
1442
1646
|
slots,
|
|
1443
|
-
binds
|
|
1647
|
+
binds,
|
|
1648
|
+
events
|
|
1444
1649
|
};
|
|
1445
1650
|
})();
|
|
1446
1651
|
}
|
|
1447
1652
|
return {
|
|
1448
1653
|
html,
|
|
1449
1654
|
slots,
|
|
1450
|
-
binds
|
|
1655
|
+
binds,
|
|
1656
|
+
events
|
|
1451
1657
|
};
|
|
1452
1658
|
} finally {
|
|
1453
1659
|
popRenderCtx();
|
|
@@ -1467,15 +1673,38 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1467
1673
|
function buildSignalState(input, snapshot) {
|
|
1468
1674
|
const state = {};
|
|
1469
1675
|
for (const entry of states) {
|
|
1470
|
-
const
|
|
1676
|
+
const initial = snapshot && entry.key in snapshot ? snapshot[entry.key] : typeof entry.init === "function" ? entry.init(input) : entry.init;
|
|
1677
|
+
const s = signal(initial);
|
|
1471
1678
|
const accessor = markSignalAccessor((...args) => {
|
|
1472
1679
|
if (args.length === 0) return s();
|
|
1473
|
-
s(args[0]);
|
|
1680
|
+
s(resolveSignalSetter(() => s(), args[0]));
|
|
1474
1681
|
});
|
|
1475
1682
|
state[entry.key] = accessor;
|
|
1476
1683
|
}
|
|
1477
1684
|
return state;
|
|
1478
1685
|
}
|
|
1686
|
+
function buildSsrActions() {
|
|
1687
|
+
const out = {};
|
|
1688
|
+
for (const entry of actions) {
|
|
1689
|
+
const invoke = () => {};
|
|
1690
|
+
Object.defineProperties(invoke, {
|
|
1691
|
+
pending: {
|
|
1692
|
+
get: () => false,
|
|
1693
|
+
enumerable: true
|
|
1694
|
+
},
|
|
1695
|
+
data: {
|
|
1696
|
+
get: () => void 0,
|
|
1697
|
+
enumerable: true
|
|
1698
|
+
},
|
|
1699
|
+
error: {
|
|
1700
|
+
get: () => void 0,
|
|
1701
|
+
enumerable: true
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1704
|
+
out[entry.key] = invoke;
|
|
1705
|
+
}
|
|
1706
|
+
return out;
|
|
1707
|
+
}
|
|
1479
1708
|
/** Detached host for SSR setup hooks; must not be `{}` (Areia/components call host.matches). */
|
|
1480
1709
|
function createSsrOnMountHost() {
|
|
1481
1710
|
if (typeof document !== "undefined") return document.createElement("div");
|
|
@@ -1491,18 +1720,19 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1491
1720
|
return stub;
|
|
1492
1721
|
}
|
|
1493
1722
|
/** Run .onMount() before top-level SSR only (seed module/external state from `input`). */
|
|
1494
|
-
function runOnMountForRender(input, state, derived) {
|
|
1723
|
+
function runOnMountForRender({ input, state, derived, action }) {
|
|
1495
1724
|
if (onMounts.length === 0) return;
|
|
1496
1725
|
if (currentRenderCtx()) return;
|
|
1497
1726
|
const host = createSsrOnMountHost();
|
|
1498
1727
|
const ssrCleanups = [];
|
|
1499
1728
|
for (const entry of onMounts) {
|
|
1500
1729
|
const prevSub = setActiveSub(void 0);
|
|
1501
|
-
let userCleanup
|
|
1730
|
+
let userCleanup;
|
|
1502
1731
|
try {
|
|
1503
1732
|
userCleanup = entry.fn({
|
|
1504
1733
|
state,
|
|
1505
1734
|
derived,
|
|
1735
|
+
action,
|
|
1506
1736
|
input,
|
|
1507
1737
|
host,
|
|
1508
1738
|
hydrated: false
|
|
@@ -1517,6 +1747,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1517
1747
|
source: "mount",
|
|
1518
1748
|
state,
|
|
1519
1749
|
derived,
|
|
1750
|
+
action,
|
|
1520
1751
|
input,
|
|
1521
1752
|
host
|
|
1522
1753
|
});
|
|
@@ -1540,6 +1771,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1540
1771
|
function renderToString(props, sync = false) {
|
|
1541
1772
|
const input = resolveInput(props);
|
|
1542
1773
|
const state = buildSignalState(input);
|
|
1774
|
+
const action = buildSsrActions();
|
|
1543
1775
|
const results = deriveds.map((entry) => {
|
|
1544
1776
|
const prevSub = setActiveSub(void 0);
|
|
1545
1777
|
try {
|
|
@@ -1573,14 +1805,20 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1573
1805
|
error: void 0
|
|
1574
1806
|
};
|
|
1575
1807
|
const derived = buildDerivedAccessors(envelopes);
|
|
1576
|
-
if (!currentRenderCtx()) runOnMountForRender(
|
|
1808
|
+
if (!currentRenderCtx()) runOnMountForRender({
|
|
1809
|
+
input,
|
|
1810
|
+
state,
|
|
1811
|
+
derived,
|
|
1812
|
+
action
|
|
1813
|
+
});
|
|
1577
1814
|
const prevSub = setActiveSub(void 0);
|
|
1578
1815
|
try {
|
|
1579
|
-
const { html } = renderWithCtx(() => fn({
|
|
1816
|
+
const { html } = renderWithCtx({ render: () => fn({
|
|
1580
1817
|
state,
|
|
1581
1818
|
derived,
|
|
1819
|
+
action,
|
|
1582
1820
|
input
|
|
1583
|
-
}));
|
|
1821
|
+
}) });
|
|
1584
1822
|
return stylePrefix + html;
|
|
1585
1823
|
} finally {
|
|
1586
1824
|
setActiveSub(prevSub);
|
|
@@ -1610,14 +1848,23 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1610
1848
|
const envelopes = {};
|
|
1611
1849
|
for (const r of resolved) envelopes[r.key] = r.envelope;
|
|
1612
1850
|
const derived = buildDerivedAccessors(envelopes);
|
|
1613
|
-
if (!currentRenderCtx()) runOnMountForRender(
|
|
1851
|
+
if (!currentRenderCtx()) runOnMountForRender({
|
|
1852
|
+
input,
|
|
1853
|
+
state,
|
|
1854
|
+
derived,
|
|
1855
|
+
action
|
|
1856
|
+
});
|
|
1614
1857
|
const prevSub = setActiveSub(void 0);
|
|
1615
1858
|
try {
|
|
1616
|
-
const { html } = await renderWithCtx(
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1859
|
+
const { html } = await renderWithCtx({
|
|
1860
|
+
render: () => fn({
|
|
1861
|
+
state,
|
|
1862
|
+
derived,
|
|
1863
|
+
action,
|
|
1864
|
+
input
|
|
1865
|
+
}),
|
|
1866
|
+
asyncChildren: true
|
|
1867
|
+
});
|
|
1621
1868
|
return stylePrefix + html;
|
|
1622
1869
|
} finally {
|
|
1623
1870
|
setActiveSub(prevSub);
|
|
@@ -1684,8 +1931,14 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1684
1931
|
const cleanups = [];
|
|
1685
1932
|
const unmountController = new AbortController();
|
|
1686
1933
|
cleanups.push(() => unmountController.abort());
|
|
1687
|
-
const { proxy: derived, setup: setupDerived } = createDerivedProxy(
|
|
1934
|
+
const { proxy: derived, setup: setupDerived } = createDerivedProxy({
|
|
1935
|
+
entries: deriveds,
|
|
1936
|
+
state,
|
|
1937
|
+
input,
|
|
1938
|
+
derivedSnapshot
|
|
1939
|
+
});
|
|
1688
1940
|
let stopDerived = () => {};
|
|
1941
|
+
let action = {};
|
|
1689
1942
|
function reportError(err, source) {
|
|
1690
1943
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
1691
1944
|
if (onErrors.length === 0) {
|
|
@@ -1698,6 +1951,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1698
1951
|
source,
|
|
1699
1952
|
state,
|
|
1700
1953
|
derived,
|
|
1954
|
+
action,
|
|
1701
1955
|
input,
|
|
1702
1956
|
host
|
|
1703
1957
|
});
|
|
@@ -1705,6 +1959,107 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1705
1959
|
console.error(handlerErr);
|
|
1706
1960
|
}
|
|
1707
1961
|
}
|
|
1962
|
+
const actionEntries = actions;
|
|
1963
|
+
const actionObject = {};
|
|
1964
|
+
let disposed = false;
|
|
1965
|
+
cleanups.push(() => {
|
|
1966
|
+
disposed = true;
|
|
1967
|
+
});
|
|
1968
|
+
for (const entry of actionEntries) {
|
|
1969
|
+
const envelope = signal({
|
|
1970
|
+
pending: 0,
|
|
1971
|
+
data: void 0,
|
|
1972
|
+
error: void 0
|
|
1973
|
+
});
|
|
1974
|
+
const readEnvelope = () => untrack(() => envelope());
|
|
1975
|
+
let latestRun = 0;
|
|
1976
|
+
const invoke = (props) => {
|
|
1977
|
+
if (currentRenderCtx()) return;
|
|
1978
|
+
if (disposed) return;
|
|
1979
|
+
const run = ++latestRun;
|
|
1980
|
+
const runSignal = unmountController.signal;
|
|
1981
|
+
const ctx = {
|
|
1982
|
+
state,
|
|
1983
|
+
derived,
|
|
1984
|
+
input,
|
|
1985
|
+
host,
|
|
1986
|
+
signal: runSignal
|
|
1987
|
+
};
|
|
1988
|
+
let result;
|
|
1989
|
+
let isAsync = false;
|
|
1990
|
+
startBatch();
|
|
1991
|
+
try {
|
|
1992
|
+
result = entry.fn(props, ctx);
|
|
1993
|
+
isAsync = result != null && typeof result.then === "function";
|
|
1994
|
+
const current = readEnvelope();
|
|
1995
|
+
envelope(isAsync ? {
|
|
1996
|
+
pending: current.pending + 1,
|
|
1997
|
+
data: current.data,
|
|
1998
|
+
error: void 0
|
|
1999
|
+
} : {
|
|
2000
|
+
pending: current.pending,
|
|
2001
|
+
data: result,
|
|
2002
|
+
error: void 0
|
|
2003
|
+
});
|
|
2004
|
+
} catch (error) {
|
|
2005
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
2006
|
+
envelope({
|
|
2007
|
+
pending: readEnvelope().pending,
|
|
2008
|
+
data: void 0,
|
|
2009
|
+
error: normalized
|
|
2010
|
+
});
|
|
2011
|
+
reportError(normalized, "action");
|
|
2012
|
+
return;
|
|
2013
|
+
} finally {
|
|
2014
|
+
endBatch();
|
|
2015
|
+
}
|
|
2016
|
+
if (!isAsync) return;
|
|
2017
|
+
Promise.resolve(result).then((data) => {
|
|
2018
|
+
if (disposed || runSignal.aborted || run !== latestRun) return;
|
|
2019
|
+
const value = readEnvelope();
|
|
2020
|
+
envelope({
|
|
2021
|
+
pending: value.pending,
|
|
2022
|
+
data,
|
|
2023
|
+
error: void 0
|
|
2024
|
+
});
|
|
2025
|
+
}).catch((error) => {
|
|
2026
|
+
if (disposed || runSignal.aborted || error?.name === "AbortError") return;
|
|
2027
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
2028
|
+
if (run === latestRun) {
|
|
2029
|
+
const value = readEnvelope();
|
|
2030
|
+
envelope({
|
|
2031
|
+
pending: value.pending,
|
|
2032
|
+
data: void 0,
|
|
2033
|
+
error: normalized
|
|
2034
|
+
});
|
|
2035
|
+
}
|
|
2036
|
+
reportError(normalized, "action");
|
|
2037
|
+
}).finally(() => {
|
|
2038
|
+
if (disposed) return;
|
|
2039
|
+
const value = readEnvelope();
|
|
2040
|
+
envelope({
|
|
2041
|
+
...value,
|
|
2042
|
+
pending: Math.max(0, value.pending - 1)
|
|
2043
|
+
});
|
|
2044
|
+
});
|
|
2045
|
+
};
|
|
2046
|
+
Object.defineProperties(invoke, {
|
|
2047
|
+
pending: {
|
|
2048
|
+
get: () => envelope().pending > 0,
|
|
2049
|
+
enumerable: true
|
|
2050
|
+
},
|
|
2051
|
+
data: {
|
|
2052
|
+
get: () => envelope().data,
|
|
2053
|
+
enumerable: true
|
|
2054
|
+
},
|
|
2055
|
+
error: {
|
|
2056
|
+
get: () => envelope().error,
|
|
2057
|
+
enumerable: true
|
|
2058
|
+
}
|
|
2059
|
+
});
|
|
2060
|
+
actionObject[entry.key] = invoke;
|
|
2061
|
+
}
|
|
2062
|
+
action = actionObject;
|
|
1708
2063
|
if (transition?.enter) try {
|
|
1709
2064
|
const result = transition.enter(host);
|
|
1710
2065
|
if (result instanceof Promise) result.catch((err) => reportError(err, "transition"));
|
|
@@ -1736,9 +2091,9 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1736
2091
|
entry.el.remove();
|
|
1737
2092
|
}
|
|
1738
2093
|
function slotBelongsToHost(candidate) {
|
|
1739
|
-
let el = candidate;
|
|
2094
|
+
let el = candidate.parentElement;
|
|
1740
2095
|
while (el && el !== host) {
|
|
1741
|
-
if (el.hasAttribute("data-ilha")) return false;
|
|
2096
|
+
if (el.hasAttribute(SLOT_ATTR) || el.hasAttribute("data-ilha")) return false;
|
|
1742
2097
|
el = el.parentElement;
|
|
1743
2098
|
}
|
|
1744
2099
|
return el === host;
|
|
@@ -1809,7 +2164,13 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1809
2164
|
function attachListeners() {
|
|
1810
2165
|
for (const entry of ons) {
|
|
1811
2166
|
if (entry.options.once && firedOnce.has(entry)) continue;
|
|
1812
|
-
|
|
2167
|
+
let targets;
|
|
2168
|
+
try {
|
|
2169
|
+
targets = entry.selector === "" ? [host] : Array.from(host.querySelectorAll(entry.selector)).filter((element) => templateElementBelongsToHost(host, element));
|
|
2170
|
+
} catch {
|
|
2171
|
+
continue;
|
|
2172
|
+
}
|
|
2173
|
+
targets.forEach((listenerTarget) => {
|
|
1813
2174
|
const listener = (event) => {
|
|
1814
2175
|
if (entry.options.once) {
|
|
1815
2176
|
firedOnce.add(entry);
|
|
@@ -1837,6 +2198,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1837
2198
|
result = entry.handler({
|
|
1838
2199
|
state,
|
|
1839
2200
|
derived,
|
|
2201
|
+
action,
|
|
1840
2202
|
input,
|
|
1841
2203
|
host,
|
|
1842
2204
|
target: handlerTarget,
|
|
@@ -1876,11 +2238,12 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1876
2238
|
function invokeOnMounts() {
|
|
1877
2239
|
for (const entry of onMounts) {
|
|
1878
2240
|
const prevSub = setActiveSub(void 0);
|
|
1879
|
-
let userCleanup
|
|
2241
|
+
let userCleanup;
|
|
1880
2242
|
try {
|
|
1881
2243
|
userCleanup = entry.fn({
|
|
1882
2244
|
state,
|
|
1883
2245
|
derived,
|
|
2246
|
+
action,
|
|
1884
2247
|
input,
|
|
1885
2248
|
host,
|
|
1886
2249
|
hydrated
|
|
@@ -1904,14 +2267,27 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1904
2267
|
}
|
|
1905
2268
|
const preserveSSRDom = hydrated && host.childNodes.length > 0;
|
|
1906
2269
|
if (hydrated && onMounts.length > 0 && !shouldSkipOnMount) invokeOnMounts();
|
|
1907
|
-
const
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
2270
|
+
const nativeOnceFired = /* @__PURE__ */ new WeakMap();
|
|
2271
|
+
const initial = renderWithCtx({
|
|
2272
|
+
render: () => fn({
|
|
2273
|
+
state,
|
|
2274
|
+
derived,
|
|
2275
|
+
action,
|
|
2276
|
+
input
|
|
2277
|
+
}),
|
|
2278
|
+
liveHost: preserveSSRDom ? host : void 0
|
|
2279
|
+
});
|
|
1912
2280
|
if (!preserveSSRDom) host.innerHTML = stylePrefix + initial.html;
|
|
1913
2281
|
let stopBindings = applyTemplateBindings(host, initial.binds);
|
|
1914
2282
|
cleanups.push(() => stopBindings());
|
|
2283
|
+
let stopJsxEvents = applyJsxEvents({
|
|
2284
|
+
host,
|
|
2285
|
+
records: initial.events,
|
|
2286
|
+
reportError: (error) => reportError(error, "on"),
|
|
2287
|
+
unmountSignal: unmountController.signal,
|
|
2288
|
+
onceFired: nativeOnceFired
|
|
2289
|
+
});
|
|
2290
|
+
cleanups.push(() => stopJsxEvents());
|
|
1915
2291
|
mountSlots(initial.slots);
|
|
1916
2292
|
cleanups.push(() => mountedSlots.forEach((entry) => entry.unmount()));
|
|
1917
2293
|
attachListeners();
|
|
@@ -1936,6 +2312,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1936
2312
|
userCleanup = entry.fn({
|
|
1937
2313
|
state,
|
|
1938
2314
|
derived,
|
|
2315
|
+
action,
|
|
1939
2316
|
input,
|
|
1940
2317
|
host,
|
|
1941
2318
|
signal: runSignal
|
|
@@ -1964,11 +2341,23 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1964
2341
|
let renderEpoch = 0;
|
|
1965
2342
|
const stopRender = effect(() => {
|
|
1966
2343
|
const epoch = ++renderEpoch;
|
|
1967
|
-
const { html: rendered, slots: newSlotMap, binds: newBinds } = renderWithCtx(
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
2344
|
+
const { html: rendered, slots: newSlotMap, binds: newBinds, events: newEvents } = renderWithCtx({
|
|
2345
|
+
render: () => fn({
|
|
2346
|
+
state,
|
|
2347
|
+
derived,
|
|
2348
|
+
action,
|
|
2349
|
+
input
|
|
2350
|
+
}),
|
|
2351
|
+
liveHost: host
|
|
2352
|
+
});
|
|
2353
|
+
stopJsxEvents();
|
|
2354
|
+
stopJsxEvents = applyJsxEvents({
|
|
2355
|
+
host,
|
|
2356
|
+
records: newEvents,
|
|
2357
|
+
reportError: (error) => reportError(error, "on"),
|
|
2358
|
+
unmountSignal: unmountController.signal,
|
|
2359
|
+
onceFired: nativeOnceFired
|
|
2360
|
+
});
|
|
1972
2361
|
const html = stylePrefix + rendered;
|
|
1973
2362
|
if (!initialized) {
|
|
1974
2363
|
initialized = true;
|
|
@@ -1983,7 +2372,11 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1983
2372
|
lastRendered = rendered;
|
|
1984
2373
|
return;
|
|
1985
2374
|
}
|
|
1986
|
-
if (mountedSlots.size > 0 && mountedSlots.size === newSlotMap.size && [...newSlotMap.keys()].every((id) => mountedSlots.has(id)) && isStableInlineSlotMount(
|
|
2375
|
+
if (mountedSlots.size > 0 && mountedSlots.size === newSlotMap.size && [...newSlotMap.keys()].every((id) => mountedSlots.has(id)) && isStableInlineSlotMount({
|
|
2376
|
+
initialHtml: initialRenderedHtml,
|
|
2377
|
+
renderedHtml: rendered,
|
|
2378
|
+
slotIds: newSlotMap.keys()
|
|
2379
|
+
})) {
|
|
1987
2380
|
pushUpdatedProps(newSlotMap);
|
|
1988
2381
|
return;
|
|
1989
2382
|
}
|
|
@@ -1994,6 +2387,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
1994
2387
|
}
|
|
1995
2388
|
detachListeners();
|
|
1996
2389
|
stopBindings();
|
|
2390
|
+
stopJsxEvents();
|
|
1997
2391
|
const prevMountedCount = mountedSlots.size;
|
|
1998
2392
|
const leavingPromises = [];
|
|
1999
2393
|
for (const [id, entry] of mountedSlots) if (!newSlotMap.has(id)) {
|
|
@@ -2039,6 +2433,13 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2039
2433
|
}
|
|
2040
2434
|
attachListeners();
|
|
2041
2435
|
stopBindings = applyTemplateBindings(host, newBinds);
|
|
2436
|
+
stopJsxEvents = applyJsxEvents({
|
|
2437
|
+
host,
|
|
2438
|
+
records: newEvents,
|
|
2439
|
+
reportError: (error) => reportError(error, "on"),
|
|
2440
|
+
unmountSignal: unmountController.signal,
|
|
2441
|
+
onceFired: nativeOnceFired
|
|
2442
|
+
});
|
|
2042
2443
|
mountSlots(newSlotMap);
|
|
2043
2444
|
};
|
|
2044
2445
|
if (leavingPromises.length > 0) Promise.allSettled(leavingPromises).then((results) => {
|
|
@@ -2051,6 +2452,8 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2051
2452
|
const unmount = () => {
|
|
2052
2453
|
if (tornDown) return;
|
|
2053
2454
|
tornDown = true;
|
|
2455
|
+
disposed = true;
|
|
2456
|
+
unmountController.abort();
|
|
2054
2457
|
ISLAND_MOUNT_HANDLES.delete(host);
|
|
2055
2458
|
stopRender();
|
|
2056
2459
|
detachListeners();
|
|
@@ -2098,7 +2501,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2098
2501
|
* The declared return type omits {@link IslandCall} because TypeScript
|
|
2099
2502
|
* cannot narrow based on runtime stack state.
|
|
2100
2503
|
*/
|
|
2101
|
-
const island =
|
|
2504
|
+
const island = ((props) => {
|
|
2102
2505
|
if (currentRenderCtx()) return {
|
|
2103
2506
|
[ISLAND_CALL]: true,
|
|
2104
2507
|
island,
|
|
@@ -2106,7 +2509,7 @@ var IlhaBuilder = class IlhaBuilder {
|
|
|
2106
2509
|
key: void 0
|
|
2107
2510
|
};
|
|
2108
2511
|
return renderToString(props);
|
|
2109
|
-
};
|
|
2512
|
+
});
|
|
2110
2513
|
island.toString = (props) => renderToString(props, true);
|
|
2111
2514
|
island.mount = (host, props) => mountIsland(host, props);
|
|
2112
2515
|
island[ISLAND_MOUNT_INTERNAL] = (host, props) => mountIslandInternal(host, props);
|
|
@@ -2329,6 +2732,7 @@ const rootBuilder = new IlhaBuilder({
|
|
|
2329
2732
|
defaultInput: null,
|
|
2330
2733
|
states: [],
|
|
2331
2734
|
deriveds: [],
|
|
2735
|
+
actions: [],
|
|
2332
2736
|
ons: [],
|
|
2333
2737
|
effects: [],
|
|
2334
2738
|
onMounts: [],
|
|
@@ -2337,7 +2741,17 @@ const rootBuilder = new IlhaBuilder({
|
|
|
2337
2741
|
css: null,
|
|
2338
2742
|
as: null
|
|
2339
2743
|
});
|
|
2340
|
-
const
|
|
2744
|
+
const callableRoot = ((fn) => rootBuilder.input().render(fn));
|
|
2745
|
+
Object.defineProperty(callableRoot, "_cfg", {
|
|
2746
|
+
value: rootBuilder._cfg,
|
|
2747
|
+
enumerable: false
|
|
2748
|
+
});
|
|
2749
|
+
for (const key of Object.getOwnPropertyNames(IlhaBuilder.prototype)) {
|
|
2750
|
+
if (key === "constructor") continue;
|
|
2751
|
+
const descriptor = Object.getOwnPropertyDescriptor(IlhaBuilder.prototype, key);
|
|
2752
|
+
if (descriptor) Object.defineProperty(callableRoot, key, descriptor);
|
|
2753
|
+
}
|
|
2754
|
+
const ilha = Object.assign(callableRoot, {
|
|
2341
2755
|
html: ilhaHtml,
|
|
2342
2756
|
raw: ilhaRaw,
|
|
2343
2757
|
mount: mountAll,
|
|
@@ -2349,9 +2763,25 @@ const ilha = Object.assign(rootBuilder, {
|
|
|
2349
2763
|
untrack,
|
|
2350
2764
|
onUncaughtError
|
|
2351
2765
|
});
|
|
2766
|
+
/** @internal Used by the separate JSX runtime to register a native event handler. */
|
|
2767
|
+
function __ilhaJsxEvent({ type, handler, modifier }) {
|
|
2768
|
+
const ctx = currentRenderCtx();
|
|
2769
|
+
if (!ctx) return void 0;
|
|
2770
|
+
const index = ctx.events.length;
|
|
2771
|
+
ctx.events.push({
|
|
2772
|
+
type,
|
|
2773
|
+
handler,
|
|
2774
|
+
modifier
|
|
2775
|
+
});
|
|
2776
|
+
return index;
|
|
2777
|
+
}
|
|
2352
2778
|
/** @internal Used by the separate JSX runtime entry to preserve island slot composition. */
|
|
2353
|
-
function __ilhaJsxSlot(island, props, key) {
|
|
2354
|
-
return ilhaRaw(emitIslandSlot(
|
|
2779
|
+
function __ilhaJsxSlot({ island, props, key }) {
|
|
2780
|
+
return ilhaRaw(emitIslandSlot({
|
|
2781
|
+
island,
|
|
2782
|
+
props,
|
|
2783
|
+
key
|
|
2784
|
+
}));
|
|
2355
2785
|
}
|
|
2356
2786
|
const html = ilhaHtml;
|
|
2357
2787
|
const raw = ilhaRaw;
|
|
@@ -2361,4 +2791,4 @@ const from = ilhaFrom;
|
|
|
2361
2791
|
const context = ilhaContext;
|
|
2362
2792
|
|
|
2363
2793
|
//#endregion
|
|
2364
|
-
export { ISLAND_MOUNT_HANDLES, ISLAND_MOUNT_INTERNAL, __ilhaJsxSlot, batch, ilhaComputed as computed, context, css, ilha as default, ilhaEffect as effect, from, html, ilhaSignal, ilhaSignal as signal, mount, onUncaughtError, raw, untrack };
|
|
2794
|
+
export { ISLAND_MOUNT_HANDLES, ISLAND_MOUNT_INTERNAL, __ilhaJsxEvent, __ilhaJsxSlot, batch, ilhaComputed as computed, context, css, ilha as default, ilhaEffect as effect, from, html, ilhaSignal, ilhaSignal as signal, mount, onUncaughtError, raw, untrack };
|