@barefootjs/client 0.20.0 → 0.21.2
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/runtime/date.d.ts +25 -0
- package/dist/runtime/date.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +111 -8
- package/dist/runtime/query.d.ts +24 -0
- package/dist/runtime/query.d.ts.map +1 -1
- package/dist/runtime/render.d.ts.map +1 -1
- package/dist/runtime/scope.d.ts +6 -2
- package/dist/runtime/scope.d.ts.map +1 -1
- package/dist/runtime/slot-resolver.d.ts.map +1 -1
- package/dist/runtime/standalone.js +111 -8
- package/package.json +2 -2
- package/src/runtime/date.ts +30 -0
- package/src/runtime/index.ts +1 -0
- package/src/runtime/query.ts +144 -6
- package/src/runtime/render.ts +4 -1
- package/src/runtime/scope.ts +30 -6
- package/src/runtime/slot-resolver.ts +11 -2
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side runtime for the catalogued `Date` lowering (#2274, #2292).
|
|
3
|
+
*
|
|
4
|
+
* The compiler lowers a Date-typed prop's zero-arg accessor call
|
|
5
|
+
* (`createdAt.toISOString()`, matched by `datePlugin` /
|
|
6
|
+
* `DATE_METHODS` in `packages/jsx/src/date-lowering.ts`) to
|
|
7
|
+
* `date(recv, "op")` on every backend. Each backend's `date` runtime
|
|
8
|
+
* helper must agree byte-for-byte on the dispatched value — the shared
|
|
9
|
+
* oracle is the `date` reference function in
|
|
10
|
+
* `packages/adapter-tests/vectors/cases.ts`.
|
|
11
|
+
*
|
|
12
|
+
* The CLIENT receiver shape differs from the SSR/oracle one: props are
|
|
13
|
+
* JSON round-tripped with no type-aware revival
|
|
14
|
+
* (`packages/client/src/runtime/hydrate.ts` `parseProps`), so a
|
|
15
|
+
* `Date`-typed prop arrives at hydration as its `toJSON()` ISO string,
|
|
16
|
+
* not a real `Date` instance. `recv` is therefore either:
|
|
17
|
+
* - a real `Date` (e.g. a value constructed client-side), or
|
|
18
|
+
* - an ISO-8601 string (the hydrated-prop case).
|
|
19
|
+
* (No `{$date: ...}` envelope — that shape is test/oracle-only, never a
|
|
20
|
+
* runtime value.) Both are coerced via `new Date(recv)`; a nil or
|
|
21
|
+
* unparseable receiver degrades to the same zero value every backend
|
|
22
|
+
* documents ('' for toISOString, 0 otherwise) instead of throwing.
|
|
23
|
+
*/
|
|
24
|
+
export declare function date(recv: unknown, op: string): string | number;
|
|
25
|
+
//# sourceMappingURL=date.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/runtime/date.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAM/D"}
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export { applyRestAttrs } from './apply-rest-attrs.ts';
|
|
|
17
17
|
export { spreadAttrs } from './spread-attrs.ts';
|
|
18
18
|
export { styleToCss } from './style.ts';
|
|
19
19
|
export { findScope, find, $, $c, $t, qsa, qsaChildScope, qsaChildScopes, cssEscape, tAfter } from './query.ts';
|
|
20
|
+
export { date } from './date.ts';
|
|
20
21
|
export { hydrate, rehydrateAll, rehydrateScope, disposeScope, flushHydration, getRegisteredDef } from './hydrate.ts';
|
|
21
22
|
export { registerComponent, getComponentInit, initChild, upsertChild } from './registry.ts';
|
|
22
23
|
export { insert, type BranchConfig, type BranchTemplateResult } from './insert.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/index.js
CHANGED
|
@@ -174,6 +174,7 @@ var BF_PORTAL_ID = "bf-pi";
|
|
|
174
174
|
var BF_PORTAL_PLACEHOLDER = "bf-pp";
|
|
175
175
|
var BF_PARENT_OWNED_PREFIX = "^";
|
|
176
176
|
var BF_SCOPE_COMMENT_PREFIX = "bf-scope:";
|
|
177
|
+
var BF_SCOPE_COMMENT_END_PREFIX = "bf-/scope:";
|
|
177
178
|
var BF_LOOP_START = "bf-loop";
|
|
178
179
|
var BF_LOOP_END = "bf-/loop";
|
|
179
180
|
var BF_LOOP_ITEM = "bf-loop-i";
|
|
@@ -494,7 +495,15 @@ function getPortalScopeId(element) {
|
|
|
494
495
|
return info?.scopeId ?? null;
|
|
495
496
|
}
|
|
496
497
|
function getCommentScopeBoundary(commentNode) {
|
|
497
|
-
const
|
|
498
|
+
const anchorValue = commentNode.nodeValue ?? "";
|
|
499
|
+
const isLoopItem = anchorValue.startsWith(`${BF_LOOP_ITEM}:`);
|
|
500
|
+
let endValue = null;
|
|
501
|
+
if (!isLoopItem && anchorValue.startsWith(BF_SCOPE_COMMENT_PREFIX)) {
|
|
502
|
+
const rest = anchorValue.slice(BF_SCOPE_COMMENT_PREFIX.length);
|
|
503
|
+
const pipeIdx = rest.indexOf("|");
|
|
504
|
+
endValue = BF_SCOPE_COMMENT_END_PREFIX + (pipeIdx >= 0 ? rest.slice(0, pipeIdx) : rest);
|
|
505
|
+
}
|
|
506
|
+
let legacyBoundary = null;
|
|
498
507
|
let node = commentNode.nextSibling;
|
|
499
508
|
while (node) {
|
|
500
509
|
if (node.nodeType === Node.COMMENT_NODE) {
|
|
@@ -503,13 +512,18 @@ function getCommentScopeBoundary(commentNode) {
|
|
|
503
512
|
if (value.startsWith(`${BF_LOOP_ITEM}:`) || value.startsWith(`${BF_LOOP_END}:`)) {
|
|
504
513
|
return node;
|
|
505
514
|
}
|
|
506
|
-
} else if (value
|
|
515
|
+
} else if (endValue && value === endValue) {
|
|
507
516
|
return node;
|
|
517
|
+
} else if (value.startsWith(BF_SCOPE_COMMENT_PREFIX)) {
|
|
518
|
+
if (!endValue)
|
|
519
|
+
return node;
|
|
520
|
+
if (!legacyBoundary)
|
|
521
|
+
legacyBoundary = node;
|
|
508
522
|
}
|
|
509
523
|
}
|
|
510
524
|
node = node.nextSibling;
|
|
511
525
|
}
|
|
512
|
-
return
|
|
526
|
+
return legacyBoundary;
|
|
513
527
|
}
|
|
514
528
|
|
|
515
529
|
// src/runtime/query.ts
|
|
@@ -626,7 +640,41 @@ function belongsToScope(element, scope) {
|
|
|
626
640
|
if (element.getAttribute(BF_SCOPE))
|
|
627
641
|
return false;
|
|
628
642
|
const nearestScope = element.closest(`[${BF_SCOPE}]`);
|
|
629
|
-
|
|
643
|
+
if (nearestScope !== scope)
|
|
644
|
+
return false;
|
|
645
|
+
return !isInsideNestedCommentScope(element, scope);
|
|
646
|
+
}
|
|
647
|
+
function isInsideNestedCommentScope(element, scope) {
|
|
648
|
+
let current = element;
|
|
649
|
+
while (current !== scope) {
|
|
650
|
+
const parent = current.parentNode;
|
|
651
|
+
if (!parent)
|
|
652
|
+
return false;
|
|
653
|
+
if (isTopLevelCommentScopeNode(current))
|
|
654
|
+
return true;
|
|
655
|
+
current = parent;
|
|
656
|
+
}
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
function isTopLevelCommentScopeNode(node) {
|
|
660
|
+
const parent = node.parentNode;
|
|
661
|
+
if (!parent)
|
|
662
|
+
return false;
|
|
663
|
+
let depth = 0;
|
|
664
|
+
let sib = parent.firstChild;
|
|
665
|
+
while (sib) {
|
|
666
|
+
if (sib === node)
|
|
667
|
+
return depth > 0;
|
|
668
|
+
if (sib.nodeType === Node.COMMENT_NODE) {
|
|
669
|
+
const value = sib.nodeValue ?? "";
|
|
670
|
+
if (value.startsWith(BF_SCOPE_COMMENT_PREFIX))
|
|
671
|
+
depth++;
|
|
672
|
+
else if (value.startsWith(BF_SCOPE_COMMENT_END_PREFIX) && depth > 0)
|
|
673
|
+
depth--;
|
|
674
|
+
}
|
|
675
|
+
sib = sib.nextSibling;
|
|
676
|
+
}
|
|
677
|
+
return false;
|
|
630
678
|
}
|
|
631
679
|
function isInCommentScopeRange(element, commentNode) {
|
|
632
680
|
const boundary = getCommentScopeBoundary(commentNode);
|
|
@@ -651,7 +699,8 @@ function find(scope, selector, ignoreScope) {
|
|
|
651
699
|
if (commentInfo) {
|
|
652
700
|
if (candidate.parentElement === commentInfo.commentNode.parentElement)
|
|
653
701
|
return candidate;
|
|
654
|
-
|
|
702
|
+
const nearestScope = candidate.closest(`[${BF_SCOPE}]`);
|
|
703
|
+
if (!nearestScope || !isInCommentScopeRange(nearestScope, commentInfo.commentNode))
|
|
655
704
|
return candidate;
|
|
656
705
|
} else {
|
|
657
706
|
if (belongsToScope(candidate, scope))
|
|
@@ -766,9 +815,48 @@ function $cSingle(scope, id) {
|
|
|
766
815
|
}
|
|
767
816
|
if (scope.matches?.(`[${BF_SCOPE}$="_${cleanId}"]`))
|
|
768
817
|
return scope;
|
|
769
|
-
return
|
|
818
|
+
return findCommentChildScope(scope, parentScopeIds, cleanId);
|
|
819
|
+
}
|
|
820
|
+
return findChildScope(scope, `[${BF_SCOPE}$="_${cleanId}"]`) ?? findCommentChildScope(scope, [], cleanId);
|
|
821
|
+
}
|
|
822
|
+
function findCommentChildScope(scope, parentIds, slotId) {
|
|
823
|
+
for (const comment of commentsInScope(scope)) {
|
|
824
|
+
const id = parseCommentScopeId(comment.nodeValue ?? "", BF_SCOPE_COMMENT_PREFIX);
|
|
825
|
+
if (!id)
|
|
826
|
+
continue;
|
|
827
|
+
const matches = parentIds.length > 0 ? parentIds.some((parentId) => id === `${parentId}_${slotId}`) : id.endsWith(`_${slotId}`) && !NESTED_SLOT_SUFFIX.test(id);
|
|
828
|
+
if (!matches)
|
|
829
|
+
continue;
|
|
830
|
+
const proxyEl = nextElementSibling(comment) ?? comment.parentElement;
|
|
831
|
+
if (!proxyEl)
|
|
832
|
+
return null;
|
|
833
|
+
commentScopeRegistry.set(proxyEl, { commentNode: comment, scopeId: id });
|
|
834
|
+
return proxyEl;
|
|
835
|
+
}
|
|
836
|
+
return null;
|
|
837
|
+
}
|
|
838
|
+
function* commentsInScope(scope) {
|
|
839
|
+
const commentInfo = commentScopeRegistry.get(scope);
|
|
840
|
+
if (commentInfo) {
|
|
841
|
+
const boundary = getCommentScopeBoundary(commentInfo.commentNode);
|
|
842
|
+
let node = commentInfo.commentNode.nextSibling;
|
|
843
|
+
while (node && node !== boundary) {
|
|
844
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
845
|
+
yield node;
|
|
846
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
847
|
+
yield* commentsUnder(node);
|
|
848
|
+
}
|
|
849
|
+
node = node.nextSibling;
|
|
850
|
+
}
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
yield* commentsUnder(scope);
|
|
854
|
+
}
|
|
855
|
+
function* commentsUnder(root) {
|
|
856
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);
|
|
857
|
+
while (walker.nextNode()) {
|
|
858
|
+
yield walker.currentNode;
|
|
770
859
|
}
|
|
771
|
-
return findChildScope(scope, `[${BF_SCOPE}$="_${cleanId}"]`);
|
|
772
860
|
}
|
|
773
861
|
function findChildScope(scope, selector) {
|
|
774
862
|
if (scope.matches?.(selector))
|
|
@@ -868,7 +956,10 @@ function findSsrScopeBySlotIn(parent, slotId, anchorScope, selfMatch) {
|
|
|
868
956
|
const suffixSelector = `[${BF_SCOPE}$="_${slotId}"]`;
|
|
869
957
|
if (selfMatch && parent.matches(suffixSelector))
|
|
870
958
|
return parent;
|
|
871
|
-
|
|
959
|
+
const bySuffix = parent.querySelector(suffixSelector);
|
|
960
|
+
if (bySuffix)
|
|
961
|
+
return bySuffix;
|
|
962
|
+
return findCommentChildScope(parent, parentBfs ? [parentBfs] : [], slotId);
|
|
872
963
|
}
|
|
873
964
|
|
|
874
965
|
// src/runtime/registry.ts
|
|
@@ -2280,6 +2371,16 @@ function spreadAttrs(obj) {
|
|
|
2280
2371
|
}
|
|
2281
2372
|
return parts.join(" ");
|
|
2282
2373
|
}
|
|
2374
|
+
// src/runtime/date.ts
|
|
2375
|
+
function date(recv, op) {
|
|
2376
|
+
const zero = op === "toISOString" ? "" : 0;
|
|
2377
|
+
if (recv === null || recv === undefined)
|
|
2378
|
+
return zero;
|
|
2379
|
+
const d = recv instanceof Date ? recv : new Date(recv);
|
|
2380
|
+
if (Number.isNaN(d.getTime()))
|
|
2381
|
+
return zero;
|
|
2382
|
+
return d[op]();
|
|
2383
|
+
}
|
|
2283
2384
|
// src/runtime/insert.ts
|
|
2284
2385
|
import { createEffect as createEffect3, untrack as untrack2 } from "@barefootjs/client/reactive";
|
|
2285
2386
|
function makeRegion(scope) {
|
|
@@ -2676,6 +2777,7 @@ function render(container, nameOrDef, props = {}) {
|
|
|
2676
2777
|
for (const node of Array.from(tpl.content.childNodes)) {
|
|
2677
2778
|
container.appendChild(node);
|
|
2678
2779
|
}
|
|
2780
|
+
container.appendChild(document.createComment(`${BF_SCOPE_COMMENT_END_PREFIX}${scopeId}`));
|
|
2679
2781
|
const proxyEl = rootElements[0];
|
|
2680
2782
|
commentScopeRegistry.set(proxyEl, { commentNode, scopeId });
|
|
2681
2783
|
init(proxyEl, props);
|
|
@@ -2756,6 +2858,7 @@ export {
|
|
|
2756
2858
|
escapeAttr,
|
|
2757
2859
|
endTurn,
|
|
2758
2860
|
disposeScope,
|
|
2861
|
+
date,
|
|
2759
2862
|
cssEscape,
|
|
2760
2863
|
createSignal2 as createSignal,
|
|
2761
2864
|
createSelector,
|
package/dist/runtime/query.d.ts
CHANGED
|
@@ -74,6 +74,30 @@ export declare function $(scope: Element | null, ...ids: string[]): (Element | n
|
|
|
74
74
|
* Always returns an array — callers use destructuring.
|
|
75
75
|
*/
|
|
76
76
|
export declare function $c(scope: Element | null, ...ids: string[]): (Element | null)[];
|
|
77
|
+
/**
|
|
78
|
+
* Resolve a comment-anchored child scope (fragment-root child, #2289).
|
|
79
|
+
*
|
|
80
|
+
* A child component whose root is a JSX Fragment has no element carrying
|
|
81
|
+
* `bf-s`/`bf-h`/`bf-m` — its scope is declared by a
|
|
82
|
+
* `<!--bf-scope:<parentId>_<slotId>|h=...|m=...-->` comment. The element
|
|
83
|
+
* selectors in `$cSingle` / `findSsrScopeBySlotIn` can never match it, so
|
|
84
|
+
* without this fallback the parent's `initChild(...)` receives `null` and
|
|
85
|
+
* the child is never initialized: every callback prop dies silently and
|
|
86
|
+
* no reactive update ever reaches the child.
|
|
87
|
+
*
|
|
88
|
+
* Matches on the scope id embedded in the comment (`<parentId>_<slotId>`,
|
|
89
|
+
* the same convention the element path's `[bf-s$="<parentId>_<slotId>"]`
|
|
90
|
+
* selector relies on) rather than on the `|h=`/`|m=` segments — the scope
|
|
91
|
+
* id is the first `|`-free token, so matching it cannot be confused by
|
|
92
|
+
* props JSON that happens to contain `|h=`.
|
|
93
|
+
*
|
|
94
|
+
* On match, the proxy element (first element sibling after the comment,
|
|
95
|
+
* falling back to the comment's parent) is registered in
|
|
96
|
+
* `commentScopeRegistry` so the child's own `$`/`$t` queries walk the
|
|
97
|
+
* comment range — the same wiring `hydrateCommentScope` performs for
|
|
98
|
+
* walker-owned root fragments.
|
|
99
|
+
*/
|
|
100
|
+
export declare function findCommentChildScope(scope: Element, parentIds: readonly string[], slotId: string): Element | null;
|
|
77
101
|
/**
|
|
78
102
|
* Find Text nodes for reactive text expressions marked by comment nodes.
|
|
79
103
|
* Expects marker format: <!--bf:sX-->text<!--/-->
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/runtime/query.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,sFAAsF;AACtF,eAAO,MAAM,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAGJ,CAAA;AAqCnC;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,EACjC,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,GAAG,IAAI,CAoDhB;
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/runtime/query.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,sFAAsF;AACtF,eAAO,MAAM,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAGJ,CAAA;AAqCnC;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,EACjC,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,GAAG,IAAI,CAoDhB;AAiLD;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAClB,KAAK,EAAE,OAAO,GAAG,IAAI,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,OAAO,GACpB,OAAO,GAAG,IAAI,CAiChB;AAuBD;;;;;;;GAOG;AACH,wBAAgB,GAAG,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CA0BxE;AA+CD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAU9E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,CAO1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,CAAC,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAO7E;AAED;;;;;GAKG;AACH,wBAAgB,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAE9E;AAkDD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,MAAM,EAAE,MAAM,GACb,OAAO,GAAG,IAAI,CAehB;AAmFD;;;;;;;GAOG;AACH,wBAAgB,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAkC3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAS3D;AAED,OAAO,EAAE,oBAAoB,IAAI,MAAM,EAAE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/runtime/render.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAU,MAAM,YAAY,CAAA;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,MAAM,CACpB,SAAS,EAAE,WAAW,EACtB,SAAS,EAAE,MAAM,GAAG,YAAY,EAChC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAClC,IAAI,
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/runtime/render.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAU,MAAM,YAAY,CAAA;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,MAAM,CACpB,SAAS,EAAE,WAAW,EACtB,SAAS,EAAE,MAAM,GAAG,YAAY,EAChC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAClC,IAAI,CAoFN"}
|
package/dist/runtime/scope.d.ts
CHANGED
|
@@ -24,8 +24,12 @@ export declare function getPortalScopeId(element: Element): string | null;
|
|
|
24
24
|
* Find the end boundary for a comment-based scope.
|
|
25
25
|
*
|
|
26
26
|
* The boundary depends on the anchor's kind:
|
|
27
|
-
* - `bf-scope:` anchor (fragment-root component): boundary is
|
|
28
|
-
* `bf
|
|
27
|
+
* - `bf-scope:` anchor (fragment-root component): boundary is this scope's
|
|
28
|
+
* own `bf-/scope:<scopeId>` end marker when present (#2289). Without one
|
|
29
|
+
* (SSR HTML from an adapter runtime predating the end marker), fall back
|
|
30
|
+
* to the next `bf-scope:` comment or the end of the parent's children —
|
|
31
|
+
* the historical heuristic, which over-extends the range past the
|
|
32
|
+
* fragment's real last root.
|
|
29
33
|
* - `bf-loop-i:<key>` anchor (loop item, #1665): boundary is the next
|
|
30
34
|
* loop-item anchor (`bf-loop-i:*`) or the loop end marker (`bf-/loop:*`),
|
|
31
35
|
* so one item's range never bleeds into the next item or past the loop.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/runtime/scope.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,OAAO,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB,oCAA2C,CAAA;AAE5E;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/runtime/scope.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,OAAO,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB,oCAA2C,CAAA;AAE5E;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAqCzE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slot-resolver.d.ts","sourceRoot":"","sources":["../../src/runtime/slot-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;gEAEgE;AAChE,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,MAAM,CAInF;AAED;;;;6BAI6B;AAC7B,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,GAC3B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAEnC;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,EACvC,SAAS,EAAE,OAAO,GACjB,WAAW,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"slot-resolver.d.ts","sourceRoot":"","sources":["../../src/runtime/slot-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;gEAEgE;AAChE,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,MAAM,CAInF;AAED;;;;6BAI6B;AAC7B,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,GAC3B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAEnC;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,EACvC,SAAS,EAAE,OAAO,GACjB,WAAW,GAAG,IAAI,CAsBpB"}
|
|
@@ -10,6 +10,7 @@ var BF_PORTAL_ID = "bf-pi";
|
|
|
10
10
|
var BF_PORTAL_PLACEHOLDER = "bf-pp";
|
|
11
11
|
var BF_PARENT_OWNED_PREFIX = "^";
|
|
12
12
|
var BF_SCOPE_COMMENT_PREFIX = "bf-scope:";
|
|
13
|
+
var BF_SCOPE_COMMENT_END_PREFIX = "bf-/scope:";
|
|
13
14
|
var BF_LOOP_START = "bf-loop";
|
|
14
15
|
var BF_LOOP_END = "bf-/loop";
|
|
15
16
|
var BF_LOOP_ITEM = "bf-loop-i";
|
|
@@ -902,7 +903,15 @@ function getPortalScopeId(element) {
|
|
|
902
903
|
return info?.scopeId ?? null;
|
|
903
904
|
}
|
|
904
905
|
function getCommentScopeBoundary(commentNode) {
|
|
905
|
-
const
|
|
906
|
+
const anchorValue = commentNode.nodeValue ?? "";
|
|
907
|
+
const isLoopItem = anchorValue.startsWith(`${BF_LOOP_ITEM}:`);
|
|
908
|
+
let endValue = null;
|
|
909
|
+
if (!isLoopItem && anchorValue.startsWith(BF_SCOPE_COMMENT_PREFIX)) {
|
|
910
|
+
const rest = anchorValue.slice(BF_SCOPE_COMMENT_PREFIX.length);
|
|
911
|
+
const pipeIdx = rest.indexOf("|");
|
|
912
|
+
endValue = BF_SCOPE_COMMENT_END_PREFIX + (pipeIdx >= 0 ? rest.slice(0, pipeIdx) : rest);
|
|
913
|
+
}
|
|
914
|
+
let legacyBoundary = null;
|
|
906
915
|
let node = commentNode.nextSibling;
|
|
907
916
|
while (node) {
|
|
908
917
|
if (node.nodeType === Node.COMMENT_NODE) {
|
|
@@ -911,13 +920,18 @@ function getCommentScopeBoundary(commentNode) {
|
|
|
911
920
|
if (value.startsWith(`${BF_LOOP_ITEM}:`) || value.startsWith(`${BF_LOOP_END}:`)) {
|
|
912
921
|
return node;
|
|
913
922
|
}
|
|
914
|
-
} else if (value
|
|
923
|
+
} else if (endValue && value === endValue) {
|
|
915
924
|
return node;
|
|
925
|
+
} else if (value.startsWith(BF_SCOPE_COMMENT_PREFIX)) {
|
|
926
|
+
if (!endValue)
|
|
927
|
+
return node;
|
|
928
|
+
if (!legacyBoundary)
|
|
929
|
+
legacyBoundary = node;
|
|
916
930
|
}
|
|
917
931
|
}
|
|
918
932
|
node = node.nextSibling;
|
|
919
933
|
}
|
|
920
|
-
return
|
|
934
|
+
return legacyBoundary;
|
|
921
935
|
}
|
|
922
936
|
|
|
923
937
|
// src/runtime/query.ts
|
|
@@ -1034,7 +1048,41 @@ function belongsToScope(element, scope) {
|
|
|
1034
1048
|
if (element.getAttribute(BF_SCOPE))
|
|
1035
1049
|
return false;
|
|
1036
1050
|
const nearestScope = element.closest(`[${BF_SCOPE}]`);
|
|
1037
|
-
|
|
1051
|
+
if (nearestScope !== scope)
|
|
1052
|
+
return false;
|
|
1053
|
+
return !isInsideNestedCommentScope(element, scope);
|
|
1054
|
+
}
|
|
1055
|
+
function isInsideNestedCommentScope(element, scope) {
|
|
1056
|
+
let current = element;
|
|
1057
|
+
while (current !== scope) {
|
|
1058
|
+
const parent = current.parentNode;
|
|
1059
|
+
if (!parent)
|
|
1060
|
+
return false;
|
|
1061
|
+
if (isTopLevelCommentScopeNode(current))
|
|
1062
|
+
return true;
|
|
1063
|
+
current = parent;
|
|
1064
|
+
}
|
|
1065
|
+
return false;
|
|
1066
|
+
}
|
|
1067
|
+
function isTopLevelCommentScopeNode(node) {
|
|
1068
|
+
const parent = node.parentNode;
|
|
1069
|
+
if (!parent)
|
|
1070
|
+
return false;
|
|
1071
|
+
let depth = 0;
|
|
1072
|
+
let sib = parent.firstChild;
|
|
1073
|
+
while (sib) {
|
|
1074
|
+
if (sib === node)
|
|
1075
|
+
return depth > 0;
|
|
1076
|
+
if (sib.nodeType === Node.COMMENT_NODE) {
|
|
1077
|
+
const value = sib.nodeValue ?? "";
|
|
1078
|
+
if (value.startsWith(BF_SCOPE_COMMENT_PREFIX))
|
|
1079
|
+
depth++;
|
|
1080
|
+
else if (value.startsWith(BF_SCOPE_COMMENT_END_PREFIX) && depth > 0)
|
|
1081
|
+
depth--;
|
|
1082
|
+
}
|
|
1083
|
+
sib = sib.nextSibling;
|
|
1084
|
+
}
|
|
1085
|
+
return false;
|
|
1038
1086
|
}
|
|
1039
1087
|
function isInCommentScopeRange(element, commentNode) {
|
|
1040
1088
|
const boundary = getCommentScopeBoundary(commentNode);
|
|
@@ -1059,7 +1107,8 @@ function find(scope, selector, ignoreScope) {
|
|
|
1059
1107
|
if (commentInfo) {
|
|
1060
1108
|
if (candidate.parentElement === commentInfo.commentNode.parentElement)
|
|
1061
1109
|
return candidate;
|
|
1062
|
-
|
|
1110
|
+
const nearestScope = candidate.closest(`[${BF_SCOPE}]`);
|
|
1111
|
+
if (!nearestScope || !isInCommentScopeRange(nearestScope, commentInfo.commentNode))
|
|
1063
1112
|
return candidate;
|
|
1064
1113
|
} else {
|
|
1065
1114
|
if (belongsToScope(candidate, scope))
|
|
@@ -1174,9 +1223,48 @@ function $cSingle(scope, id) {
|
|
|
1174
1223
|
}
|
|
1175
1224
|
if (scope.matches?.(`[${BF_SCOPE}$="_${cleanId}"]`))
|
|
1176
1225
|
return scope;
|
|
1177
|
-
return
|
|
1226
|
+
return findCommentChildScope(scope, parentScopeIds, cleanId);
|
|
1227
|
+
}
|
|
1228
|
+
return findChildScope(scope, `[${BF_SCOPE}$="_${cleanId}"]`) ?? findCommentChildScope(scope, [], cleanId);
|
|
1229
|
+
}
|
|
1230
|
+
function findCommentChildScope(scope, parentIds, slotId) {
|
|
1231
|
+
for (const comment of commentsInScope(scope)) {
|
|
1232
|
+
const id = parseCommentScopeId(comment.nodeValue ?? "", BF_SCOPE_COMMENT_PREFIX);
|
|
1233
|
+
if (!id)
|
|
1234
|
+
continue;
|
|
1235
|
+
const matches = parentIds.length > 0 ? parentIds.some((parentId) => id === `${parentId}_${slotId}`) : id.endsWith(`_${slotId}`) && !NESTED_SLOT_SUFFIX.test(id);
|
|
1236
|
+
if (!matches)
|
|
1237
|
+
continue;
|
|
1238
|
+
const proxyEl = nextElementSibling(comment) ?? comment.parentElement;
|
|
1239
|
+
if (!proxyEl)
|
|
1240
|
+
return null;
|
|
1241
|
+
commentScopeRegistry.set(proxyEl, { commentNode: comment, scopeId: id });
|
|
1242
|
+
return proxyEl;
|
|
1243
|
+
}
|
|
1244
|
+
return null;
|
|
1245
|
+
}
|
|
1246
|
+
function* commentsInScope(scope) {
|
|
1247
|
+
const commentInfo = commentScopeRegistry.get(scope);
|
|
1248
|
+
if (commentInfo) {
|
|
1249
|
+
const boundary = getCommentScopeBoundary(commentInfo.commentNode);
|
|
1250
|
+
let node = commentInfo.commentNode.nextSibling;
|
|
1251
|
+
while (node && node !== boundary) {
|
|
1252
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
1253
|
+
yield node;
|
|
1254
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
1255
|
+
yield* commentsUnder(node);
|
|
1256
|
+
}
|
|
1257
|
+
node = node.nextSibling;
|
|
1258
|
+
}
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
yield* commentsUnder(scope);
|
|
1262
|
+
}
|
|
1263
|
+
function* commentsUnder(root) {
|
|
1264
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);
|
|
1265
|
+
while (walker.nextNode()) {
|
|
1266
|
+
yield walker.currentNode;
|
|
1178
1267
|
}
|
|
1179
|
-
return findChildScope(scope, `[${BF_SCOPE}$="_${cleanId}"]`);
|
|
1180
1268
|
}
|
|
1181
1269
|
function findChildScope(scope, selector) {
|
|
1182
1270
|
if (scope.matches?.(selector))
|
|
@@ -1276,7 +1364,10 @@ function findSsrScopeBySlotIn(parent, slotId, anchorScope, selfMatch) {
|
|
|
1276
1364
|
const suffixSelector = `[${BF_SCOPE}$="_${slotId}"]`;
|
|
1277
1365
|
if (selfMatch && parent.matches(suffixSelector))
|
|
1278
1366
|
return parent;
|
|
1279
|
-
|
|
1367
|
+
const bySuffix = parent.querySelector(suffixSelector);
|
|
1368
|
+
if (bySuffix)
|
|
1369
|
+
return bySuffix;
|
|
1370
|
+
return findCommentChildScope(parent, parentBfs ? [parentBfs] : [], slotId);
|
|
1280
1371
|
}
|
|
1281
1372
|
|
|
1282
1373
|
// src/runtime/registry.ts
|
|
@@ -2682,6 +2773,16 @@ function spreadAttrs(obj) {
|
|
|
2682
2773
|
}
|
|
2683
2774
|
return parts.join(" ");
|
|
2684
2775
|
}
|
|
2776
|
+
// src/runtime/date.ts
|
|
2777
|
+
function date(recv, op) {
|
|
2778
|
+
const zero = op === "toISOString" ? "" : 0;
|
|
2779
|
+
if (recv === null || recv === undefined)
|
|
2780
|
+
return zero;
|
|
2781
|
+
const d = recv instanceof Date ? recv : new Date(recv);
|
|
2782
|
+
if (Number.isNaN(d.getTime()))
|
|
2783
|
+
return zero;
|
|
2784
|
+
return d[op]();
|
|
2785
|
+
}
|
|
2685
2786
|
// src/runtime/insert.ts
|
|
2686
2787
|
function makeRegion(scope) {
|
|
2687
2788
|
if (scope.nodeType === Node.COMMENT_NODE) {
|
|
@@ -3076,6 +3177,7 @@ function render(container, nameOrDef, props = {}) {
|
|
|
3076
3177
|
for (const node of Array.from(tpl.content.childNodes)) {
|
|
3077
3178
|
container.appendChild(node);
|
|
3078
3179
|
}
|
|
3180
|
+
container.appendChild(document.createComment(`${BF_SCOPE_COMMENT_END_PREFIX}${scopeId}`));
|
|
3079
3181
|
const proxyEl = rootElements[0];
|
|
3080
3182
|
commentScopeRegistry.set(proxyEl, { commentNode, scopeId });
|
|
3081
3183
|
init(proxyEl, props);
|
|
@@ -3156,6 +3258,7 @@ export {
|
|
|
3156
3258
|
escapeAttr,
|
|
3157
3259
|
endTurn,
|
|
3158
3260
|
disposeScope,
|
|
3261
|
+
date,
|
|
3159
3262
|
cssEscape,
|
|
3160
3263
|
createSignal,
|
|
3161
3264
|
createSelector,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.2",
|
|
4
4
|
"description": "BarefootJS client package: reactive primitives (SSR-safe) plus browser runtime under the `/runtime` subpath (compiler target)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"directory": "packages/client"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@barefootjs/shared": "0.
|
|
58
|
+
"@barefootjs/shared": "0.21.2"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"@barefootjs/jsx": ">=0.2.0"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side runtime for the catalogued `Date` lowering (#2274, #2292).
|
|
3
|
+
*
|
|
4
|
+
* The compiler lowers a Date-typed prop's zero-arg accessor call
|
|
5
|
+
* (`createdAt.toISOString()`, matched by `datePlugin` /
|
|
6
|
+
* `DATE_METHODS` in `packages/jsx/src/date-lowering.ts`) to
|
|
7
|
+
* `date(recv, "op")` on every backend. Each backend's `date` runtime
|
|
8
|
+
* helper must agree byte-for-byte on the dispatched value — the shared
|
|
9
|
+
* oracle is the `date` reference function in
|
|
10
|
+
* `packages/adapter-tests/vectors/cases.ts`.
|
|
11
|
+
*
|
|
12
|
+
* The CLIENT receiver shape differs from the SSR/oracle one: props are
|
|
13
|
+
* JSON round-tripped with no type-aware revival
|
|
14
|
+
* (`packages/client/src/runtime/hydrate.ts` `parseProps`), so a
|
|
15
|
+
* `Date`-typed prop arrives at hydration as its `toJSON()` ISO string,
|
|
16
|
+
* not a real `Date` instance. `recv` is therefore either:
|
|
17
|
+
* - a real `Date` (e.g. a value constructed client-side), or
|
|
18
|
+
* - an ISO-8601 string (the hydrated-prop case).
|
|
19
|
+
* (No `{$date: ...}` envelope — that shape is test/oracle-only, never a
|
|
20
|
+
* runtime value.) Both are coerced via `new Date(recv)`; a nil or
|
|
21
|
+
* unparseable receiver degrades to the same zero value every backend
|
|
22
|
+
* documents ('' for toISOString, 0 otherwise) instead of throwing.
|
|
23
|
+
*/
|
|
24
|
+
export function date(recv: unknown, op: string): string | number {
|
|
25
|
+
const zero = op === 'toISOString' ? '' : 0
|
|
26
|
+
if (recv === null || recv === undefined) return zero
|
|
27
|
+
const d = recv instanceof Date ? recv : new Date(recv as string | number)
|
|
28
|
+
if (Number.isNaN(d.getTime())) return zero
|
|
29
|
+
return (d as unknown as Record<string, () => string | number>)[op]()
|
|
30
|
+
}
|
package/src/runtime/index.ts
CHANGED
|
@@ -103,6 +103,7 @@ export { styleToCss } from './style.ts'
|
|
|
103
103
|
|
|
104
104
|
// Runtime helpers
|
|
105
105
|
export { findScope, find, $, $c, $t, qsa, qsaChildScope, qsaChildScopes, cssEscape, tAfter } from './query.ts'
|
|
106
|
+
export { date } from './date.ts'
|
|
106
107
|
export { hydrate, rehydrateAll, rehydrateScope, disposeScope, flushHydration, getRegisteredDef } from './hydrate.ts'
|
|
107
108
|
export { registerComponent, getComponentInit, initChild, upsertChild } from './registry.ts'
|
|
108
109
|
export { insert, type BranchConfig, type BranchTemplateResult } from './insert.ts'
|
package/src/runtime/query.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { commentScopeRegistry, getCommentScopeBoundary } from './scope.ts'
|
|
10
10
|
import { hydratedScopes } from './hydration-state.ts'
|
|
11
|
-
import { BF_SCOPE, BF_SLOT, BF_PORTAL_OWNER, BF_PARENT_OWNED_PREFIX, BF_SCOPE_COMMENT_PREFIX } from '@barefootjs/shared'
|
|
11
|
+
import { BF_SCOPE, BF_SLOT, BF_PORTAL_OWNER, BF_PARENT_OWNED_PREFIX, BF_SCOPE_COMMENT_PREFIX, BF_SCOPE_COMMENT_END_PREFIX } from '@barefootjs/shared'
|
|
12
12
|
|
|
13
13
|
/** CSS attribute-value escape with a fallback for environments lacking CSS.escape. */
|
|
14
14
|
export const cssEscape: (s: string) => string =
|
|
@@ -222,7 +222,59 @@ function belongsToScope(element: Element, scope: Element): boolean {
|
|
|
222
222
|
|
|
223
223
|
// Check if nearest scope matches
|
|
224
224
|
const nearestScope = element.closest(`[${BF_SCOPE}]`)
|
|
225
|
-
|
|
225
|
+
if (nearestScope !== scope) return false
|
|
226
|
+
|
|
227
|
+
// A fragment-rooted child mounted between `element` and `scope` has no
|
|
228
|
+
// `bf-s` element of its own (#2302) — `closest()` walks straight past it
|
|
229
|
+
// to `scope`, so a slot search on `scope` can wrongly claim a descendant
|
|
230
|
+
// that actually belongs to that nested child's comment scope. Reject it.
|
|
231
|
+
return !isInsideNestedCommentScope(element, scope)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* True if some ancestor of `element` (strictly below `scope`) is a
|
|
236
|
+
* top-level node of a fragment-rooted child's `<!--bf-scope:...-->` range —
|
|
237
|
+
* i.e. `element` structurally belongs to a nested child component even
|
|
238
|
+
* though that child has no `bf-s`-attributed element for `.closest()` to
|
|
239
|
+
* stop at, and regardless of whether `commentScopeRegistry` has registered
|
|
240
|
+
* it yet (a parent's own slot lookups can run before its child-scope
|
|
241
|
+
* lookups register the child, #2302).
|
|
242
|
+
*/
|
|
243
|
+
function isInsideNestedCommentScope(element: Element, scope: Element): boolean {
|
|
244
|
+
let current: Node = element
|
|
245
|
+
while (current !== scope) {
|
|
246
|
+
const parent: Node | null = current.parentNode
|
|
247
|
+
if (!parent) return false
|
|
248
|
+
if (isTopLevelCommentScopeNode(current)) return true
|
|
249
|
+
current = parent
|
|
250
|
+
}
|
|
251
|
+
return false
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* True if `node` falls inside a `<!--bf-scope:...-->` … `<!--bf-/scope:...-->`
|
|
256
|
+
* range among its own siblings — i.e. `node` is (or is inside) a top-level
|
|
257
|
+
* element of a fragment-rooted child mounted as `node`'s sibling. Single
|
|
258
|
+
* left-to-right pass over the sibling list with a nesting depth counter, so
|
|
259
|
+
* a nested child's own begin/end pair doesn't prematurely close an outer
|
|
260
|
+
* one. Missing end markers (older SSR output) leave depth un-decremented,
|
|
261
|
+
* matching `getCommentScopeBoundary`'s legacy end-of-parent fallback.
|
|
262
|
+
*/
|
|
263
|
+
function isTopLevelCommentScopeNode(node: Node): boolean {
|
|
264
|
+
const parent = node.parentNode
|
|
265
|
+
if (!parent) return false
|
|
266
|
+
let depth = 0
|
|
267
|
+
let sib: Node | null = parent.firstChild
|
|
268
|
+
while (sib) {
|
|
269
|
+
if (sib === node) return depth > 0
|
|
270
|
+
if (sib.nodeType === Node.COMMENT_NODE) {
|
|
271
|
+
const value = (sib as Comment).nodeValue ?? ''
|
|
272
|
+
if (value.startsWith(BF_SCOPE_COMMENT_PREFIX)) depth++
|
|
273
|
+
else if (value.startsWith(BF_SCOPE_COMMENT_END_PREFIX) && depth > 0) depth--
|
|
274
|
+
}
|
|
275
|
+
sib = sib.nextSibling
|
|
276
|
+
}
|
|
277
|
+
return false
|
|
226
278
|
}
|
|
227
279
|
|
|
228
280
|
/**
|
|
@@ -271,10 +323,16 @@ export function find(
|
|
|
271
323
|
if (ignoreScope) return candidate
|
|
272
324
|
if (commentInfo) {
|
|
273
325
|
// Comment scope: top-level siblings in the comment range are always
|
|
274
|
-
// accepted (even if they have bf-s, like proxy elements).
|
|
275
|
-
//
|
|
326
|
+
// accepted (even if they have bf-s, like proxy elements). A descendant
|
|
327
|
+
// nested inside one of those siblings is accepted unless a *nested*
|
|
328
|
+
// child scope sits between it and this one — i.e. its nearest bf-s
|
|
329
|
+
// ancestor falls within this comment's own sibling range. An ancestor
|
|
330
|
+
// bf-s element outside that range doesn't disqualify the candidate:
|
|
331
|
+
// a fragment-root child mounted inside a normal parent island always
|
|
332
|
+
// has one, and `.closest()` alone can't tell the two apart (#2302).
|
|
276
333
|
if (candidate.parentElement === commentInfo.commentNode.parentElement) return candidate
|
|
277
|
-
|
|
334
|
+
const nearestScope = candidate.closest(`[${BF_SCOPE}]`)
|
|
335
|
+
if (!nearestScope || !isInCommentScopeRange(nearestScope, commentInfo.commentNode)) return candidate
|
|
278
336
|
} else {
|
|
279
337
|
if (belongsToScope(candidate, scope)) return candidate
|
|
280
338
|
}
|
|
@@ -492,11 +550,91 @@ function $cSingle(scope: Element | null, id: string): Element | null {
|
|
|
492
550
|
// Precise match found nothing. Check if scope itself matches the short suffix
|
|
493
551
|
// (fragment root / inlined component where scope IS the child).
|
|
494
552
|
if (scope.matches?.(`[${BF_SCOPE}$="_${cleanId}"]`)) return scope
|
|
495
|
-
|
|
553
|
+
// Fragment-root child: no element carries the child's scope id — it is
|
|
554
|
+
// declared by a bf-scope: comment instead (#2289).
|
|
555
|
+
return findCommentChildScope(scope, parentScopeIds, cleanId)
|
|
496
556
|
}
|
|
497
557
|
|
|
498
558
|
// Fallback: no parent scope ID available — use short suffix match (best-effort)
|
|
499
559
|
return findChildScope(scope, `[${BF_SCOPE}$="_${cleanId}"]`)
|
|
560
|
+
?? findCommentChildScope(scope, [], cleanId)
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Resolve a comment-anchored child scope (fragment-root child, #2289).
|
|
565
|
+
*
|
|
566
|
+
* A child component whose root is a JSX Fragment has no element carrying
|
|
567
|
+
* `bf-s`/`bf-h`/`bf-m` — its scope is declared by a
|
|
568
|
+
* `<!--bf-scope:<parentId>_<slotId>|h=...|m=...-->` comment. The element
|
|
569
|
+
* selectors in `$cSingle` / `findSsrScopeBySlotIn` can never match it, so
|
|
570
|
+
* without this fallback the parent's `initChild(...)` receives `null` and
|
|
571
|
+
* the child is never initialized: every callback prop dies silently and
|
|
572
|
+
* no reactive update ever reaches the child.
|
|
573
|
+
*
|
|
574
|
+
* Matches on the scope id embedded in the comment (`<parentId>_<slotId>`,
|
|
575
|
+
* the same convention the element path's `[bf-s$="<parentId>_<slotId>"]`
|
|
576
|
+
* selector relies on) rather than on the `|h=`/`|m=` segments — the scope
|
|
577
|
+
* id is the first `|`-free token, so matching it cannot be confused by
|
|
578
|
+
* props JSON that happens to contain `|h=`.
|
|
579
|
+
*
|
|
580
|
+
* On match, the proxy element (first element sibling after the comment,
|
|
581
|
+
* falling back to the comment's parent) is registered in
|
|
582
|
+
* `commentScopeRegistry` so the child's own `$`/`$t` queries walk the
|
|
583
|
+
* comment range — the same wiring `hydrateCommentScope` performs for
|
|
584
|
+
* walker-owned root fragments.
|
|
585
|
+
*/
|
|
586
|
+
export function findCommentChildScope(
|
|
587
|
+
scope: Element,
|
|
588
|
+
parentIds: readonly string[],
|
|
589
|
+
slotId: string,
|
|
590
|
+
): Element | null {
|
|
591
|
+
for (const comment of commentsInScope(scope)) {
|
|
592
|
+
const id = parseCommentScopeId(comment.nodeValue ?? '', BF_SCOPE_COMMENT_PREFIX)
|
|
593
|
+
if (!id) continue
|
|
594
|
+
const matches = parentIds.length > 0
|
|
595
|
+
? parentIds.some(parentId => id === `${parentId}_${slotId}`)
|
|
596
|
+
: id.endsWith(`_${slotId}`) && !NESTED_SLOT_SUFFIX.test(id)
|
|
597
|
+
if (!matches) continue
|
|
598
|
+
|
|
599
|
+
const proxyEl = nextElementSibling(comment) ?? comment.parentElement
|
|
600
|
+
if (!proxyEl) return null
|
|
601
|
+
commentScopeRegistry.set(proxyEl, { commentNode: comment, scopeId: id })
|
|
602
|
+
return proxyEl
|
|
603
|
+
}
|
|
604
|
+
return null
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Enumerate comment nodes in a scope's DOM range: the comment-scope
|
|
609
|
+
* sibling range for comment-anchored scopes, the element subtree
|
|
610
|
+
* otherwise. Mirrors candidatesInScope's notion of "where the scope's
|
|
611
|
+
* content lives", but yields comments instead of elements.
|
|
612
|
+
*/
|
|
613
|
+
function* commentsInScope(scope: Element): Generator<Comment> {
|
|
614
|
+
const commentInfo = commentScopeRegistry.get(scope)
|
|
615
|
+
|
|
616
|
+
if (commentInfo) {
|
|
617
|
+
const boundary = getCommentScopeBoundary(commentInfo.commentNode)
|
|
618
|
+
let node: Node | null = commentInfo.commentNode.nextSibling
|
|
619
|
+
while (node && node !== boundary) {
|
|
620
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
621
|
+
yield node as Comment
|
|
622
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
623
|
+
yield* commentsUnder(node as Element)
|
|
624
|
+
}
|
|
625
|
+
node = node.nextSibling
|
|
626
|
+
}
|
|
627
|
+
return
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
yield* commentsUnder(scope)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function* commentsUnder(root: Element): Generator<Comment> {
|
|
634
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT)
|
|
635
|
+
while (walker.nextNode()) {
|
|
636
|
+
yield walker.currentNode as Comment
|
|
637
|
+
}
|
|
500
638
|
}
|
|
501
639
|
|
|
502
640
|
/**
|
package/src/runtime/render.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* never import this module.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { BF_SCOPE, BF_SCOPE_COMMENT_PREFIX } from '@barefootjs/shared'
|
|
9
|
+
import { BF_SCOPE, BF_SCOPE_COMMENT_PREFIX, BF_SCOPE_COMMENT_END_PREFIX } from '@barefootjs/shared'
|
|
10
10
|
import { setParentScopeId } from './component.ts'
|
|
11
11
|
import { hydratedScopes } from './hydration-state.ts'
|
|
12
12
|
import { getComponentInit } from './registry.ts'
|
|
@@ -119,6 +119,9 @@ export function render(
|
|
|
119
119
|
for (const node of Array.from(tpl.content.childNodes)) {
|
|
120
120
|
container.appendChild(node)
|
|
121
121
|
}
|
|
122
|
+
// Close the range so scope queries never leak past the fragment's last
|
|
123
|
+
// root (mirrors the SSR end marker, #2289).
|
|
124
|
+
container.appendChild(document.createComment(`${BF_SCOPE_COMMENT_END_PREFIX}${scopeId}`))
|
|
122
125
|
|
|
123
126
|
const proxyEl = rootElements[0]
|
|
124
127
|
commentScopeRegistry.set(proxyEl, { commentNode, scopeId })
|
package/src/runtime/scope.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Maps an element to its comment node and the sibling range boundary.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { BF_SCOPE_COMMENT_PREFIX, BF_LOOP_ITEM, BF_LOOP_END } from '@barefootjs/shared'
|
|
8
|
+
import { BF_SCOPE_COMMENT_PREFIX, BF_SCOPE_COMMENT_END_PREFIX, BF_LOOP_ITEM, BF_LOOP_END } from '@barefootjs/shared'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Information about a comment-based scope.
|
|
@@ -33,14 +33,31 @@ export function getPortalScopeId(element: Element): string | null {
|
|
|
33
33
|
* Find the end boundary for a comment-based scope.
|
|
34
34
|
*
|
|
35
35
|
* The boundary depends on the anchor's kind:
|
|
36
|
-
* - `bf-scope:` anchor (fragment-root component): boundary is
|
|
37
|
-
* `bf
|
|
36
|
+
* - `bf-scope:` anchor (fragment-root component): boundary is this scope's
|
|
37
|
+
* own `bf-/scope:<scopeId>` end marker when present (#2289). Without one
|
|
38
|
+
* (SSR HTML from an adapter runtime predating the end marker), fall back
|
|
39
|
+
* to the next `bf-scope:` comment or the end of the parent's children —
|
|
40
|
+
* the historical heuristic, which over-extends the range past the
|
|
41
|
+
* fragment's real last root.
|
|
38
42
|
* - `bf-loop-i:<key>` anchor (loop item, #1665): boundary is the next
|
|
39
43
|
* loop-item anchor (`bf-loop-i:*`) or the loop end marker (`bf-/loop:*`),
|
|
40
44
|
* so one item's range never bleeds into the next item or past the loop.
|
|
41
45
|
*/
|
|
42
46
|
export function getCommentScopeBoundary(commentNode: Comment): Node | null {
|
|
43
|
-
const
|
|
47
|
+
const anchorValue = commentNode.nodeValue ?? ''
|
|
48
|
+
const isLoopItem = anchorValue.startsWith(`${BF_LOOP_ITEM}:`)
|
|
49
|
+
|
|
50
|
+
// Exact end-marker value for a scope anchor: `bf-/scope:<scopeId>`. The
|
|
51
|
+
// scope id is the `|`-free head of the anchor value, so a nested child
|
|
52
|
+
// scope's markers (different id) never terminate this range.
|
|
53
|
+
let endValue: string | null = null
|
|
54
|
+
if (!isLoopItem && anchorValue.startsWith(BF_SCOPE_COMMENT_PREFIX)) {
|
|
55
|
+
const rest = anchorValue.slice(BF_SCOPE_COMMENT_PREFIX.length)
|
|
56
|
+
const pipeIdx = rest.indexOf('|')
|
|
57
|
+
endValue = BF_SCOPE_COMMENT_END_PREFIX + (pipeIdx >= 0 ? rest.slice(0, pipeIdx) : rest)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let legacyBoundary: Node | null = null
|
|
44
61
|
let node: Node | null = commentNode.nextSibling
|
|
45
62
|
while (node) {
|
|
46
63
|
if (node.nodeType === Node.COMMENT_NODE) {
|
|
@@ -49,11 +66,18 @@ export function getCommentScopeBoundary(commentNode: Comment): Node | null {
|
|
|
49
66
|
if (value.startsWith(`${BF_LOOP_ITEM}:`) || value.startsWith(`${BF_LOOP_END}:`)) {
|
|
50
67
|
return node
|
|
51
68
|
}
|
|
52
|
-
} else if (value
|
|
69
|
+
} else if (endValue && value === endValue) {
|
|
53
70
|
return node
|
|
71
|
+
} else if (value.startsWith(BF_SCOPE_COMMENT_PREFIX)) {
|
|
72
|
+
// Historically the range ended at the next scope comment. When this
|
|
73
|
+
// anchor can have an end marker, keep scanning: the comment seen
|
|
74
|
+
// here may belong to a nested child scope inside the range, not to
|
|
75
|
+
// a sibling that terminates it.
|
|
76
|
+
if (!endValue) return node
|
|
77
|
+
if (!legacyBoundary) legacyBoundary = node
|
|
54
78
|
}
|
|
55
79
|
}
|
|
56
80
|
node = node.nextSibling
|
|
57
81
|
}
|
|
58
|
-
return
|
|
82
|
+
return legacyBoundary // null → end of parent's children
|
|
59
83
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { BF_SCOPE, BF_HOST, BF_AT } from '@barefootjs/shared'
|
|
14
|
-
import { cssEscape } from './query.ts'
|
|
14
|
+
import { cssEscape, findCommentChildScope } from './query.ts'
|
|
15
15
|
|
|
16
16
|
/** Resolve the host scope id for a slot lookup. Prefers the explicit
|
|
17
17
|
* `anchorScope` because the immediate `parent` element may be a freshly-
|
|
@@ -62,5 +62,14 @@ export function findSsrScopeBySlotIn(
|
|
|
62
62
|
|
|
63
63
|
const suffixSelector = `[${BF_SCOPE}$="_${slotId}"]`
|
|
64
64
|
if (selfMatch && parent.matches(suffixSelector)) return parent as HTMLElement
|
|
65
|
-
|
|
65
|
+
const bySuffix = parent.querySelector(suffixSelector) as HTMLElement | null
|
|
66
|
+
if (bySuffix) return bySuffix
|
|
67
|
+
|
|
68
|
+
// Fragment-root child: its scope is a bf-scope: comment, not an element
|
|
69
|
+
// carrying (bf-h, bf-m) — resolve the comment's proxy element (#2289).
|
|
70
|
+
return findCommentChildScope(
|
|
71
|
+
parent,
|
|
72
|
+
parentBfs ? [parentBfs] : [],
|
|
73
|
+
slotId,
|
|
74
|
+
) as HTMLElement | null
|
|
66
75
|
}
|