@takazudo/zfb 4.3.3 → 4.5.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 +14 -0
- package/dist/css.d.ts +45 -0
- package/dist/css.js +137 -0
- package/dist/css.js.map +1 -0
- package/dist/zudo-react/dom-bindings.js +11 -5
- package/dist/zudo-react/dom-bindings.js.map +1 -1
- package/dist/zudo-react/error-boundary-client.d.ts +3 -0
- package/dist/zudo-react/error-boundary-client.js +232 -0
- package/dist/zudo-react/error-boundary-client.js.map +1 -0
- package/dist/zudo-react/error-boundary-type.d.ts +8 -0
- package/dist/zudo-react/error-boundary-type.js +25 -0
- package/dist/zudo-react/error-boundary-type.js.map +1 -0
- package/dist/zudo-react/error-boundary.d.ts +8 -0
- package/dist/zudo-react/error-boundary.js +13 -0
- package/dist/zudo-react/error-boundary.js.map +1 -0
- package/dist/zudo-react/form-recovery.d.ts +2 -0
- package/dist/zudo-react/form-recovery.js +116 -0
- package/dist/zudo-react/form-recovery.js.map +1 -0
- package/dist/zudo-react/forms.d.ts +38 -4
- package/dist/zudo-react/forms.js +143 -172
- package/dist/zudo-react/forms.js.map +1 -1
- package/dist/zudo-react/hydrate.d.ts +68 -0
- package/dist/zudo-react/hydrate.js +177 -89
- package/dist/zudo-react/hydrate.js.map +1 -1
- package/dist/zudo-react/index.d.ts +1 -0
- package/dist/zudo-react/index.js +1 -0
- package/dist/zudo-react/index.js.map +1 -1
- package/dist/zudo-react/render-html.js +55 -10
- package/dist/zudo-react/render-html.js.map +1 -1
- package/dist/zudo-react/scope.d.ts +21 -13
- package/dist/zudo-react/scope.js +141 -113
- package/dist/zudo-react/scope.js.map +1 -1
- package/dist/zudo-react/style.d.ts +0 -1
- package/dist/zudo-react/style.js +11 -503
- package/dist/zudo-react/style.js.map +1 -1
- package/dist/zudo-react/vocabulary.js +12 -49
- package/dist/zudo-react/vocabulary.js.map +1 -1
- package/package.json +11 -7
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { boundaryRenderer, integrity, recoverable } from "./error-boundary-type.js";
|
|
2
|
+
import { islandRootType } from "./island-root-type.js";
|
|
3
|
+
import { activateForms, prepareForms, reconcileForms, formHost, FormError, } from "./forms.js";
|
|
2
4
|
import { Fragment, isDescription } from "./description.js";
|
|
3
|
-
import {
|
|
5
|
+
import { RuntimeScope, withScope } from "./scope.js";
|
|
4
6
|
import { isReactive, read, styleText, setAttribute, bind, listener } from "./dom-bindings.js";
|
|
5
7
|
import { createRoot, diagnostic, report, rootPath, storedRoot, } from "./root.js";
|
|
6
|
-
import { subscribe } from "./reactive.js";
|
|
7
|
-
import { batch } from "./scheduler.js";
|
|
8
|
+
import { subscribe, untracked } from "./reactive.js";
|
|
9
|
+
import { batch, enqueue } from "./scheduler.js";
|
|
8
10
|
import { rawHtmlReserved } from "./raw-html.js";
|
|
9
11
|
import { Show, For, showProps, forProps, keyed, keyPayload, view } from "./structure.js";
|
|
10
12
|
import { attributeError, commonAttrs, dialectSuggestion, displayOnlyControl, emptyIframeChildren, formProps, htmlAttrs, htmlTags, isDialectProp, reactiveModelSuggestion, svgAttrs, svgTags, voidTags, } from "./vocabulary.js";
|
|
@@ -32,14 +34,21 @@ function owned(context, name, namespace, parentTag, path, factory, initial, kind
|
|
|
32
34
|
const fragment = context.document.createDocumentFragment();
|
|
33
35
|
const operationsStart = context.operations.length;
|
|
34
36
|
const formsStart = context.forms.length;
|
|
37
|
+
const rangesStart = context.dynamicRanges.length;
|
|
35
38
|
const previous = context.scope;
|
|
36
39
|
context.scope = scope;
|
|
37
40
|
const cleanups = [];
|
|
38
41
|
try {
|
|
39
42
|
withScope(scope, () => {
|
|
40
|
-
scope.
|
|
41
|
-
for (const cleanup of
|
|
42
|
-
|
|
43
|
+
scope.cleanups.push(() => {
|
|
44
|
+
for (const cleanup of cleanups.splice(0).reverse()) {
|
|
45
|
+
try {
|
|
46
|
+
cleanup();
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
scope.report("ZR_CLEANUP_ERROR", "cleanup", String(error));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
43
52
|
});
|
|
44
53
|
const output = factory();
|
|
45
54
|
if (kind)
|
|
@@ -49,16 +58,23 @@ function owned(context, name, namespace, parentTag, path, factory, initial, kind
|
|
|
49
58
|
});
|
|
50
59
|
const operations = context.operations.splice(operationsStart);
|
|
51
60
|
const forms = initial ? context.forms.slice(formsStart) : context.forms.splice(formsStart);
|
|
52
|
-
const instance = {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
const instance = {
|
|
62
|
+
scope,
|
|
63
|
+
fragment,
|
|
64
|
+
operations,
|
|
65
|
+
forms,
|
|
66
|
+
formOwners: context.formOwners,
|
|
67
|
+
cleanups,
|
|
68
|
+
};
|
|
69
|
+
for (const form of forms)
|
|
70
|
+
if (!context.formOwners.has(form))
|
|
71
|
+
context.formOwners.set(form, instance);
|
|
57
72
|
return instance;
|
|
58
73
|
}
|
|
59
74
|
catch (error) {
|
|
60
75
|
context.operations.length = operationsStart;
|
|
61
76
|
context.forms.length = formsStart;
|
|
77
|
+
context.dynamicRanges.length = rangesStart;
|
|
62
78
|
scope.dispose();
|
|
63
79
|
throw error;
|
|
64
80
|
}
|
|
@@ -69,12 +85,29 @@ function owned(context, name, namespace, parentTag, path, factory, initial, kind
|
|
|
69
85
|
function installOwned(instance, map, context, mode) {
|
|
70
86
|
if (mode === "mount" && instance.forms.length) {
|
|
71
87
|
const bindings = prepareForms(instance.forms, map, "mount", context.container);
|
|
72
|
-
reconcileForms(bindings, "mount");
|
|
73
|
-
|
|
88
|
+
reconcileForms(bindings, "mount", context.container);
|
|
89
|
+
activateOwnedForms(bindings, instance.forms, map, instance.formOwners, context, instance.scope, instance.cleanups);
|
|
74
90
|
}
|
|
75
91
|
for (const operation of instance.operations)
|
|
76
92
|
operation(map, instance.cleanups);
|
|
77
93
|
}
|
|
94
|
+
function activateOwnedForms(bindings, plans, map, owners, context, scope, cleanups) {
|
|
95
|
+
const byNode = new Map();
|
|
96
|
+
for (const plan of plans)
|
|
97
|
+
byNode.set(map.get(plan.element), owners.get(plan));
|
|
98
|
+
const groups = new Map();
|
|
99
|
+
for (const binding of bindings) {
|
|
100
|
+
const owner = byNode.get(binding.node);
|
|
101
|
+
const group = groups.get(owner) ?? [];
|
|
102
|
+
group.push(binding);
|
|
103
|
+
groups.set(owner, group);
|
|
104
|
+
}
|
|
105
|
+
// Root-owned bindings initialize first, as in the root adoption path.
|
|
106
|
+
activateForms(groups.get(undefined) ?? [], scope, cleanups, context.options, context.container);
|
|
107
|
+
groups.delete(undefined);
|
|
108
|
+
for (const [owner, group] of groups)
|
|
109
|
+
activateForms(group, owner.scope, owner.cleanups, context.options, context.container);
|
|
110
|
+
}
|
|
78
111
|
function selfMap(fragment) {
|
|
79
112
|
const map = new Map();
|
|
80
113
|
const visit = (node) => {
|
|
@@ -87,22 +120,23 @@ function selfMap(fragment) {
|
|
|
87
120
|
return map;
|
|
88
121
|
}
|
|
89
122
|
class Failure extends Error {
|
|
90
|
-
code;
|
|
91
|
-
phase;
|
|
92
|
-
expected;
|
|
93
|
-
actual;
|
|
94
|
-
path;
|
|
95
123
|
constructor(code, phase, expected, actual, path) {
|
|
96
124
|
super(`${code}: ${expected}; got ${actual}`);
|
|
97
|
-
this
|
|
98
|
-
this.phase = phase;
|
|
99
|
-
this.expected = expected;
|
|
100
|
-
this.actual = actual;
|
|
101
|
-
this.path = path;
|
|
125
|
+
Object.assign(this, { code, phase, expected, actual, path });
|
|
102
126
|
}
|
|
103
127
|
}
|
|
104
|
-
function fail(code, phase, expected, actual, path) {
|
|
105
|
-
|
|
128
|
+
function fail(code, phase, expected, actual, path, fatal = false) {
|
|
129
|
+
const error = new Failure(code, phase, expected, actual, path);
|
|
130
|
+
throw fatal ||
|
|
131
|
+
[
|
|
132
|
+
"ZR_IDENTITY",
|
|
133
|
+
"ZR_CANCELLED",
|
|
134
|
+
"ZR_DUPLICATE_ROOT",
|
|
135
|
+
"ZR_HYDRATION_MISMATCH",
|
|
136
|
+
"ZR_NESTED_ISLAND",
|
|
137
|
+
].includes(code)
|
|
138
|
+
? integrity(error)
|
|
139
|
+
: error;
|
|
106
140
|
}
|
|
107
141
|
function append(parent, node) {
|
|
108
142
|
if (node.nodeType === Node.TEXT_NODE && parent.lastChild?.nodeType === Node.TEXT_NODE) {
|
|
@@ -112,7 +146,7 @@ function append(parent, node) {
|
|
|
112
146
|
parent.appendChild(node);
|
|
113
147
|
}
|
|
114
148
|
function marker(context, parent, kind, content) {
|
|
115
|
-
const id = context.next
|
|
149
|
+
const id = `${context.prefix ?? ""}${context.next++}`;
|
|
116
150
|
const open = context.document.createComment(`zr:1:${id}:${kind}`);
|
|
117
151
|
parent.appendChild(open);
|
|
118
152
|
content();
|
|
@@ -122,7 +156,8 @@ function marker(context, parent, kind, content) {
|
|
|
122
156
|
context.opaque.add(open);
|
|
123
157
|
if (kind === "t")
|
|
124
158
|
context.textSlots.add(open);
|
|
125
|
-
if (context.collectRootRanges &&
|
|
159
|
+
if (context.collectRootRanges &&
|
|
160
|
+
(kind === "e" || kind === "t" || kind === "l" || kind.startsWith("s:")))
|
|
126
161
|
context.dynamicRanges.push([open, close]);
|
|
127
162
|
return [open, close];
|
|
128
163
|
}
|
|
@@ -158,7 +193,7 @@ function range(start, end) {
|
|
|
158
193
|
if (node === end)
|
|
159
194
|
return nodes;
|
|
160
195
|
}
|
|
161
|
-
throw new Error("ZR_REGION_RANGE");
|
|
196
|
+
throw integrity(new Error("ZR_REGION_RANGE"));
|
|
162
197
|
}
|
|
163
198
|
function removeRange(start, end) {
|
|
164
199
|
for (const node of range(start, end))
|
|
@@ -201,11 +236,12 @@ function scalar(value, path) {
|
|
|
201
236
|
fail("ZR_CHILD", "setup", "scalar", typeof value, path);
|
|
202
237
|
}
|
|
203
238
|
function validateRawHtml(value, tag, path) {
|
|
204
|
-
if (typeof value !== "string"
|
|
205
|
-
|
|
239
|
+
if (typeof value !== "string")
|
|
240
|
+
fail("ZR_RAW_HTML", "setup", "valid rawHtml", typeof value, path);
|
|
241
|
+
if (rawHtmlReserved(value, tag) ||
|
|
206
242
|
(tag === "script" && /<\/script/i.test(value)) ||
|
|
207
243
|
(tag === "style" && /<\/style/i.test(value)))
|
|
208
|
-
fail("ZR_RAW_HTML", "setup", "valid rawHtml", typeof value, path);
|
|
244
|
+
fail("ZR_RAW_HTML", "setup", "valid rawHtml", typeof value, path, true);
|
|
209
245
|
}
|
|
210
246
|
function children(value, parent, context, namespace, tag, path) {
|
|
211
247
|
if (value == null || typeof value === "boolean")
|
|
@@ -235,6 +271,8 @@ function element(desc, parent, context, namespace, parentTag, path) {
|
|
|
235
271
|
const childNamespace = tag === "foreignObject" ? HTML : ownNamespace;
|
|
236
272
|
const element = context.document.createElementNS(ownNamespace, tag);
|
|
237
273
|
const props = desc.props;
|
|
274
|
+
if ("data-zfb-island" in props || "data-zfb-island-skip-ssr" in props)
|
|
275
|
+
fail("ZR_NESTED_ISLAND", "preflight", "no nested root", tag, path);
|
|
238
276
|
if (tag === "iframe") {
|
|
239
277
|
if (Object.hasOwn(props, "rawHtml"))
|
|
240
278
|
fail("ZR_RAW_HTML", "preflight", "iframe without rawHtml", "rawHtml", path);
|
|
@@ -291,14 +329,15 @@ function element(desc, parent, context, namespace, parentTag, path) {
|
|
|
291
329
|
fail("ZR_LISTENER", "setup", "native listener", name, path);
|
|
292
330
|
context.operations.push((map, cleanups) => {
|
|
293
331
|
const node = map.get(element);
|
|
332
|
+
const eventError = (error) => report(context.options, diagnostic(context.options, "ZR_EVENT_ERROR", "update", `${rootPath(context.container)}${path}`, "handler success", String(error)));
|
|
294
333
|
const handler = (event) => {
|
|
295
334
|
try {
|
|
296
335
|
const result = original(event);
|
|
297
336
|
if (result && typeof result === "object" && "then" in result)
|
|
298
|
-
Promise.resolve(result).catch(
|
|
337
|
+
Promise.resolve(result).catch(eventError);
|
|
299
338
|
}
|
|
300
339
|
catch (error) {
|
|
301
|
-
|
|
340
|
+
eventError(error);
|
|
302
341
|
}
|
|
303
342
|
};
|
|
304
343
|
node.addEventListener(parsed.name, handler, parsed.capture);
|
|
@@ -343,8 +382,7 @@ function element(desc, parent, context, namespace, parentTag, path) {
|
|
|
343
382
|
(!custom &&
|
|
344
383
|
!commonAttrs.has(name) &&
|
|
345
384
|
!(ownNamespace === SVG ? svgAttrs.has(name) : htmlAttrs.has(name)) &&
|
|
346
|
-
!/^data-[\w.-]+$/.test(name) &&
|
|
347
|
-
!/^aria-[\w.-]+$/.test(name) &&
|
|
385
|
+
!/^(data|aria)-[\w.-]+$/.test(name) &&
|
|
348
386
|
!/^on[a-z]+$/.test(name)))
|
|
349
387
|
fail("ZR_ATTRIBUTE", "setup", "supported attribute", name, path);
|
|
350
388
|
const initial = read(original);
|
|
@@ -404,7 +442,7 @@ function element(desc, parent, context, namespace, parentTag, path) {
|
|
|
404
442
|
const template = context.document.createElement("template");
|
|
405
443
|
template.innerHTML = value;
|
|
406
444
|
if (start.parentNode !== node || end.parentNode !== node)
|
|
407
|
-
throw new Error("ZR_REGION_RANGE");
|
|
445
|
+
throw integrity(new Error("ZR_REGION_RANGE"));
|
|
408
446
|
for (const child of range(start, end).slice(1, -1))
|
|
409
447
|
node.removeChild(child);
|
|
410
448
|
node.insertBefore(template.content, end);
|
|
@@ -488,6 +526,31 @@ function render(value, parent, context, namespace, parentTag, path) {
|
|
|
488
526
|
}
|
|
489
527
|
if (!isDescription(value))
|
|
490
528
|
fail("ZR_CHILD", "setup", "description", typeof value, path);
|
|
529
|
+
if (value.type === islandRootType)
|
|
530
|
+
fail("ZR_NESTED_ISLAND", "preflight", "no nested root", "island", path);
|
|
531
|
+
const boundary = boundaryRenderer(value.type);
|
|
532
|
+
if (boundary) {
|
|
533
|
+
if (restricted.has(parentTag))
|
|
534
|
+
fail("ZR_UNSUPPORTED_POSITION", "preflight", "flow region", parentTag, path);
|
|
535
|
+
boundary(value, parent, context, namespace, parentTag, path, {
|
|
536
|
+
owned,
|
|
537
|
+
installOwned,
|
|
538
|
+
selfMap,
|
|
539
|
+
marker,
|
|
540
|
+
range,
|
|
541
|
+
fail,
|
|
542
|
+
openMarker,
|
|
543
|
+
matchingClose,
|
|
544
|
+
recoverable,
|
|
545
|
+
prepareForms,
|
|
546
|
+
read,
|
|
547
|
+
subscribe,
|
|
548
|
+
untracked,
|
|
549
|
+
enqueue,
|
|
550
|
+
formHost,
|
|
551
|
+
});
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
491
554
|
if (value.type === Fragment) {
|
|
492
555
|
marker(context, parent, "f", () => children(value.props.children, parent, context, namespace, parentTag, path));
|
|
493
556
|
return;
|
|
@@ -520,17 +583,21 @@ function render(value, parent, context, namespace, parentTag, path) {
|
|
|
520
583
|
if (current)
|
|
521
584
|
installOwned(current, map, context, "hydrate");
|
|
522
585
|
const subscription = subscribe(() => {
|
|
523
|
-
const next = props.when.value;
|
|
524
|
-
if (next === shown)
|
|
525
|
-
return;
|
|
526
|
-
const factory = next ? props.children : props.fallback;
|
|
527
586
|
let replacement = null;
|
|
587
|
+
let rendering = true;
|
|
528
588
|
try {
|
|
589
|
+
const next = props.when.value;
|
|
590
|
+
if (typeof next !== "boolean")
|
|
591
|
+
throw new TypeError(`ZR_CHILD: Show.when at ${path}`);
|
|
592
|
+
if (next === shown)
|
|
593
|
+
return;
|
|
594
|
+
const factory = next ? props.children : props.fallback;
|
|
529
595
|
if (factory)
|
|
530
596
|
replacement = owned(context, "Show", namespace, parentTag, path, factory, false, undefined, owner);
|
|
597
|
+
rendering = false;
|
|
531
598
|
const replacementMap = replacement ? selfMap(replacement.fragment) : null;
|
|
532
599
|
if (replacement && replacement.forms.length)
|
|
533
|
-
prepareForms(replacement.forms, replacementMap, "mount", context.container);
|
|
600
|
+
prepareForms(replacement.forms, replacementMap, "mount", context.container, current ? [current.scope] : [], start.parentNode);
|
|
534
601
|
if (current) {
|
|
535
602
|
current.scope.dispose();
|
|
536
603
|
let node = start.nextSibling;
|
|
@@ -550,6 +617,10 @@ function render(value, parent, context, namespace, parentTag, path) {
|
|
|
550
617
|
}
|
|
551
618
|
catch (error) {
|
|
552
619
|
replacement?.scope.dispose();
|
|
620
|
+
if (rendering) {
|
|
621
|
+
if (owner.recoverRender(error))
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
553
624
|
throw error;
|
|
554
625
|
}
|
|
555
626
|
}, {
|
|
@@ -603,14 +674,24 @@ function render(value, parent, context, namespace, parentTag, path) {
|
|
|
603
674
|
installOwned(entry.owned, map, context, "hydrate");
|
|
604
675
|
const end = map.get(ends[1]);
|
|
605
676
|
const subscription = subscribe(() => {
|
|
606
|
-
|
|
607
|
-
if (!Array.isArray(next))
|
|
608
|
-
throw new TypeError(`ZR_CHILD: For.each at ${path}`);
|
|
677
|
+
let next;
|
|
609
678
|
let nextKeys;
|
|
679
|
+
try {
|
|
680
|
+
next = props.each.value;
|
|
681
|
+
if (!Array.isArray(next))
|
|
682
|
+
throw new TypeError(`ZR_CHILD: For.each at ${path}`);
|
|
683
|
+
}
|
|
684
|
+
catch (error) {
|
|
685
|
+
if (owner.recoverRender(error))
|
|
686
|
+
return;
|
|
687
|
+
throw error;
|
|
688
|
+
}
|
|
610
689
|
try {
|
|
611
690
|
nextKeys = keyed(next, props.by, path);
|
|
612
691
|
}
|
|
613
692
|
catch (error) {
|
|
693
|
+
if (owner.recoverRender(error))
|
|
694
|
+
return;
|
|
614
695
|
const detail = String(error);
|
|
615
696
|
const code = detail.includes("ZR_DUPLICATE_KEY")
|
|
616
697
|
? "ZR_DUPLICATE_KEY"
|
|
@@ -622,6 +703,7 @@ function render(value, parent, context, namespace, parentTag, path) {
|
|
|
622
703
|
}
|
|
623
704
|
const old = new Map(current.map((entry) => [entry.key, entry]));
|
|
624
705
|
const created = [];
|
|
706
|
+
let rendering = true;
|
|
625
707
|
try {
|
|
626
708
|
const ordered = nextKeys.map((key, index) => {
|
|
627
709
|
const retained = old.get(key);
|
|
@@ -642,9 +724,12 @@ function render(value, parent, context, namespace, parentTag, path) {
|
|
|
642
724
|
created.push(entry);
|
|
643
725
|
return entry;
|
|
644
726
|
});
|
|
727
|
+
rendering = false;
|
|
645
728
|
for (const entry of created)
|
|
646
729
|
if (entry.owned.forms.length)
|
|
647
|
-
prepareForms(entry.owned.forms, selfMap(entry.owned.fragment), "mount", context.container
|
|
730
|
+
prepareForms(entry.owned.forms, selfMap(entry.owned.fragment), "mount", context.container, current
|
|
731
|
+
.filter((item) => !nextKeys.includes(item.key))
|
|
732
|
+
.map((item) => item.owned.scope), end.parentNode);
|
|
648
733
|
for (const entry of current)
|
|
649
734
|
if (!nextKeys.includes(entry.key)) {
|
|
650
735
|
entry.owned.scope.dispose();
|
|
@@ -682,6 +767,10 @@ function render(value, parent, context, namespace, parentTag, path) {
|
|
|
682
767
|
catch (error) {
|
|
683
768
|
for (const entry of created)
|
|
684
769
|
entry.owned.scope.dispose();
|
|
770
|
+
if (rendering) {
|
|
771
|
+
if (owner.recoverRender(error))
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
685
774
|
throw error;
|
|
686
775
|
}
|
|
687
776
|
}, {
|
|
@@ -765,22 +854,31 @@ function significant(nodes, pre) {
|
|
|
765
854
|
? nodes
|
|
766
855
|
: nodes.filter((node) => node.nodeType !== Node.TEXT_NODE || normalize(node.textContent ?? "", false) !== "");
|
|
767
856
|
}
|
|
857
|
+
const openingMarker = /^zr:1:((?:0|[1-9]\d*)(?:\.(?:0|[1-9]\d*))*):(c|f|t|h|l|s:[01]|i:[ns](?:[0-9a-f]{2})*|e)$/;
|
|
858
|
+
function openMarker(node) {
|
|
859
|
+
return node.nodeType === Node.COMMENT_NODE ? openingMarker.exec(node.textContent ?? "") : null;
|
|
860
|
+
}
|
|
768
861
|
function matchingClose(nodes, start) {
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
if (id === undefined)
|
|
862
|
+
const first = openMarker(nodes[start]);
|
|
863
|
+
if (!first)
|
|
772
864
|
return -1;
|
|
773
|
-
|
|
865
|
+
const stack = [first[1]];
|
|
774
866
|
for (let i = start + 1; i < nodes.length; i++) {
|
|
775
|
-
const
|
|
776
|
-
if (
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
867
|
+
const node = nodes[i];
|
|
868
|
+
if (node.nodeType !== Node.COMMENT_NODE)
|
|
869
|
+
continue;
|
|
870
|
+
const value = node.textContent ?? "";
|
|
871
|
+
const open = openMarker(node);
|
|
872
|
+
if (open)
|
|
873
|
+
stack.push(open[1]);
|
|
874
|
+
else if (value.startsWith("/zr:1:")) {
|
|
875
|
+
if (value !== `/zr:1:${stack.pop()}`)
|
|
876
|
+
return -1;
|
|
877
|
+
if (!stack.length)
|
|
878
|
+
return i;
|
|
783
879
|
}
|
|
880
|
+
else if (value.includes("zr:1:"))
|
|
881
|
+
return -1;
|
|
784
882
|
}
|
|
785
883
|
return -1;
|
|
786
884
|
}
|
|
@@ -793,6 +891,12 @@ function compareChildren(expected, actual, map, context, path, pre = false) {
|
|
|
793
891
|
const wanted = wants[i];
|
|
794
892
|
const found = has[j];
|
|
795
893
|
const childPath = `${path}/${wanted.nodeName.toLowerCase()}[${i}]`;
|
|
894
|
+
const boundary = context.errorRanges.get(wanted);
|
|
895
|
+
if (boundary) {
|
|
896
|
+
compare(wanted, found, map, context, childPath, pre);
|
|
897
|
+
[i, j] = boundary(wants, has, i, j, map, childPath);
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
796
900
|
if (context.opaque.has(wanted)) {
|
|
797
901
|
compare(wanted, found, map, context, childPath, pre);
|
|
798
902
|
const wantedEnd = matchingClose(wants, i);
|
|
@@ -861,7 +965,7 @@ function execute(node, container, options, mode) {
|
|
|
861
965
|
(node.type.displayName ?? node.type.name) !==
|
|
862
966
|
options.identity.component)
|
|
863
967
|
fail("ZR_IDENTITY", "preflight", options.identity.component, "different child component", rootPath(container));
|
|
864
|
-
scope =
|
|
968
|
+
scope = new RuntimeScope({
|
|
865
969
|
component: options.identity.component,
|
|
866
970
|
reporter: (value) => report(options, value),
|
|
867
971
|
protocol,
|
|
@@ -877,52 +981,36 @@ function execute(node, container, options, mode) {
|
|
|
877
981
|
next: 0,
|
|
878
982
|
operations: [],
|
|
879
983
|
forms: [],
|
|
880
|
-
formOwners: new
|
|
881
|
-
opaque: new
|
|
882
|
-
textSlots: new
|
|
883
|
-
displayOnlyTextareas: new
|
|
984
|
+
formOwners: new WeakMap(),
|
|
985
|
+
opaque: new WeakSet(),
|
|
986
|
+
textSlots: new WeakSet(),
|
|
987
|
+
displayOnlyTextareas: new WeakSet(),
|
|
988
|
+
errorRanges: new WeakMap(),
|
|
989
|
+
replacements: [],
|
|
884
990
|
dynamicRanges: [],
|
|
885
991
|
collectRootRanges: true,
|
|
886
992
|
};
|
|
887
993
|
const fragment = container.ownerDocument.createDocumentFragment();
|
|
888
994
|
render(node, fragment, context, HTML, "", rootPath(container));
|
|
889
995
|
context.collectRootRanges = false;
|
|
890
|
-
const
|
|
891
|
-
const map = new Map();
|
|
996
|
+
const map = mode === "hydrate" ? new Map() : selfMap(fragment);
|
|
892
997
|
if (mode === "hydrate")
|
|
893
998
|
compareChildren(fragment, container, map, context, rootPath(container));
|
|
894
|
-
else {
|
|
895
|
-
for (const expected of built) {
|
|
896
|
-
const visit = (item) => {
|
|
897
|
-
map.set(item, item);
|
|
898
|
-
for (const child of item.childNodes)
|
|
899
|
-
visit(child);
|
|
900
|
-
};
|
|
901
|
-
visit(expected);
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
999
|
const formBindings = prepareForms(context.forms, map, mode, container);
|
|
905
1000
|
if (options.signal?.aborted || !container.isConnected)
|
|
906
1001
|
fail("ZR_CANCELLED", "commit", "connected root", "aborted or disconnected", rootPath(container));
|
|
907
|
-
reconcileForms(formBindings, mode);
|
|
1002
|
+
reconcileForms(formBindings, mode, context.container);
|
|
908
1003
|
if (mode === "mount")
|
|
909
1004
|
container.replaceChildren(fragment);
|
|
1005
|
+
else
|
|
1006
|
+
for (const replace of context.replacements)
|
|
1007
|
+
replace();
|
|
1008
|
+
context.replacements.length = 0;
|
|
910
1009
|
committed = true;
|
|
911
1010
|
for (const operation of context.operations)
|
|
912
1011
|
operation(map, cleanups);
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
formOwnerByNode.set(map.get(plan.element), owner);
|
|
916
|
-
const rootForms = formBindings.filter((binding) => !formOwnerByNode.has(binding.node));
|
|
917
|
-
activateForms(rootForms, scope, cleanups, options, container);
|
|
918
|
-
const ownedForms = new Map();
|
|
919
|
-
for (const binding of formBindings) {
|
|
920
|
-
const owner = formOwnerByNode.get(binding.node);
|
|
921
|
-
if (owner)
|
|
922
|
-
ownedForms.set(owner, [...(ownedForms.get(owner) ?? []), binding]);
|
|
923
|
-
}
|
|
924
|
-
for (const [owner, bindings] of ownedForms)
|
|
925
|
-
activateForms(bindings, owner.scope, owner.cleanups, options, container);
|
|
1012
|
+
activateOwnedForms(formBindings, context.forms, map, context.formOwners, context, scope, cleanups);
|
|
1013
|
+
context.forms.length = 0;
|
|
926
1014
|
const owned = rootOwned(container, context, map);
|
|
927
1015
|
context.dynamicRanges.length = 0;
|
|
928
1016
|
const handle = createRoot(container, options, scope, cleanups, owned);
|