@barefootjs/client 0.20.0 → 0.21.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/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 +74 -6
- 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 +74 -6
- package/package.json +2 -2
- package/src/runtime/date.ts +30 -0
- package/src/runtime/index.ts +1 -0
- package/src/runtime/query.ts +81 -1
- 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
|
|
@@ -766,9 +780,48 @@ function $cSingle(scope, id) {
|
|
|
766
780
|
}
|
|
767
781
|
if (scope.matches?.(`[${BF_SCOPE}$="_${cleanId}"]`))
|
|
768
782
|
return scope;
|
|
769
|
-
return
|
|
783
|
+
return findCommentChildScope(scope, parentScopeIds, cleanId);
|
|
784
|
+
}
|
|
785
|
+
return findChildScope(scope, `[${BF_SCOPE}$="_${cleanId}"]`) ?? findCommentChildScope(scope, [], cleanId);
|
|
786
|
+
}
|
|
787
|
+
function findCommentChildScope(scope, parentIds, slotId) {
|
|
788
|
+
for (const comment of commentsInScope(scope)) {
|
|
789
|
+
const id = parseCommentScopeId(comment.nodeValue ?? "", BF_SCOPE_COMMENT_PREFIX);
|
|
790
|
+
if (!id)
|
|
791
|
+
continue;
|
|
792
|
+
const matches = parentIds.length > 0 ? parentIds.some((parentId) => id === `${parentId}_${slotId}`) : id.endsWith(`_${slotId}`) && !NESTED_SLOT_SUFFIX.test(id);
|
|
793
|
+
if (!matches)
|
|
794
|
+
continue;
|
|
795
|
+
const proxyEl = nextElementSibling(comment) ?? comment.parentElement;
|
|
796
|
+
if (!proxyEl)
|
|
797
|
+
return null;
|
|
798
|
+
commentScopeRegistry.set(proxyEl, { commentNode: comment, scopeId: id });
|
|
799
|
+
return proxyEl;
|
|
800
|
+
}
|
|
801
|
+
return null;
|
|
802
|
+
}
|
|
803
|
+
function* commentsInScope(scope) {
|
|
804
|
+
const commentInfo = commentScopeRegistry.get(scope);
|
|
805
|
+
if (commentInfo) {
|
|
806
|
+
const boundary = getCommentScopeBoundary(commentInfo.commentNode);
|
|
807
|
+
let node = commentInfo.commentNode.nextSibling;
|
|
808
|
+
while (node && node !== boundary) {
|
|
809
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
810
|
+
yield node;
|
|
811
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
812
|
+
yield* commentsUnder(node);
|
|
813
|
+
}
|
|
814
|
+
node = node.nextSibling;
|
|
815
|
+
}
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
yield* commentsUnder(scope);
|
|
819
|
+
}
|
|
820
|
+
function* commentsUnder(root) {
|
|
821
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);
|
|
822
|
+
while (walker.nextNode()) {
|
|
823
|
+
yield walker.currentNode;
|
|
770
824
|
}
|
|
771
|
-
return findChildScope(scope, `[${BF_SCOPE}$="_${cleanId}"]`);
|
|
772
825
|
}
|
|
773
826
|
function findChildScope(scope, selector) {
|
|
774
827
|
if (scope.matches?.(selector))
|
|
@@ -868,7 +921,10 @@ function findSsrScopeBySlotIn(parent, slotId, anchorScope, selfMatch) {
|
|
|
868
921
|
const suffixSelector = `[${BF_SCOPE}$="_${slotId}"]`;
|
|
869
922
|
if (selfMatch && parent.matches(suffixSelector))
|
|
870
923
|
return parent;
|
|
871
|
-
|
|
924
|
+
const bySuffix = parent.querySelector(suffixSelector);
|
|
925
|
+
if (bySuffix)
|
|
926
|
+
return bySuffix;
|
|
927
|
+
return findCommentChildScope(parent, parentBfs ? [parentBfs] : [], slotId);
|
|
872
928
|
}
|
|
873
929
|
|
|
874
930
|
// src/runtime/registry.ts
|
|
@@ -2280,6 +2336,16 @@ function spreadAttrs(obj) {
|
|
|
2280
2336
|
}
|
|
2281
2337
|
return parts.join(" ");
|
|
2282
2338
|
}
|
|
2339
|
+
// src/runtime/date.ts
|
|
2340
|
+
function date(recv, op) {
|
|
2341
|
+
const zero = op === "toISOString" ? "" : 0;
|
|
2342
|
+
if (recv === null || recv === undefined)
|
|
2343
|
+
return zero;
|
|
2344
|
+
const d = recv instanceof Date ? recv : new Date(recv);
|
|
2345
|
+
if (Number.isNaN(d.getTime()))
|
|
2346
|
+
return zero;
|
|
2347
|
+
return d[op]();
|
|
2348
|
+
}
|
|
2283
2349
|
// src/runtime/insert.ts
|
|
2284
2350
|
import { createEffect as createEffect3, untrack as untrack2 } from "@barefootjs/client/reactive";
|
|
2285
2351
|
function makeRegion(scope) {
|
|
@@ -2676,6 +2742,7 @@ function render(container, nameOrDef, props = {}) {
|
|
|
2676
2742
|
for (const node of Array.from(tpl.content.childNodes)) {
|
|
2677
2743
|
container.appendChild(node);
|
|
2678
2744
|
}
|
|
2745
|
+
container.appendChild(document.createComment(`${BF_SCOPE_COMMENT_END_PREFIX}${scopeId}`));
|
|
2679
2746
|
const proxyEl = rootElements[0];
|
|
2680
2747
|
commentScopeRegistry.set(proxyEl, { commentNode, scopeId });
|
|
2681
2748
|
init(proxyEl, props);
|
|
@@ -2756,6 +2823,7 @@ export {
|
|
|
2756
2823
|
escapeAttr,
|
|
2757
2824
|
endTurn,
|
|
2758
2825
|
disposeScope,
|
|
2826
|
+
date,
|
|
2759
2827
|
cssEscape,
|
|
2760
2828
|
createSignal2 as createSignal,
|
|
2761
2829
|
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;AA6HD;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAClB,KAAK,EAAE,OAAO,GAAG,IAAI,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,OAAO,GACpB,OAAO,GAAG,IAAI,CA2BhB;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;
|
|
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;AA6HD;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAClB,KAAK,EAAE,OAAO,GAAG,IAAI,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,OAAO,GACpB,OAAO,GAAG,IAAI,CA2BhB;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
|
|
@@ -1174,9 +1188,48 @@ function $cSingle(scope, id) {
|
|
|
1174
1188
|
}
|
|
1175
1189
|
if (scope.matches?.(`[${BF_SCOPE}$="_${cleanId}"]`))
|
|
1176
1190
|
return scope;
|
|
1177
|
-
return
|
|
1191
|
+
return findCommentChildScope(scope, parentScopeIds, cleanId);
|
|
1192
|
+
}
|
|
1193
|
+
return findChildScope(scope, `[${BF_SCOPE}$="_${cleanId}"]`) ?? findCommentChildScope(scope, [], cleanId);
|
|
1194
|
+
}
|
|
1195
|
+
function findCommentChildScope(scope, parentIds, slotId) {
|
|
1196
|
+
for (const comment of commentsInScope(scope)) {
|
|
1197
|
+
const id = parseCommentScopeId(comment.nodeValue ?? "", BF_SCOPE_COMMENT_PREFIX);
|
|
1198
|
+
if (!id)
|
|
1199
|
+
continue;
|
|
1200
|
+
const matches = parentIds.length > 0 ? parentIds.some((parentId) => id === `${parentId}_${slotId}`) : id.endsWith(`_${slotId}`) && !NESTED_SLOT_SUFFIX.test(id);
|
|
1201
|
+
if (!matches)
|
|
1202
|
+
continue;
|
|
1203
|
+
const proxyEl = nextElementSibling(comment) ?? comment.parentElement;
|
|
1204
|
+
if (!proxyEl)
|
|
1205
|
+
return null;
|
|
1206
|
+
commentScopeRegistry.set(proxyEl, { commentNode: comment, scopeId: id });
|
|
1207
|
+
return proxyEl;
|
|
1208
|
+
}
|
|
1209
|
+
return null;
|
|
1210
|
+
}
|
|
1211
|
+
function* commentsInScope(scope) {
|
|
1212
|
+
const commentInfo = commentScopeRegistry.get(scope);
|
|
1213
|
+
if (commentInfo) {
|
|
1214
|
+
const boundary = getCommentScopeBoundary(commentInfo.commentNode);
|
|
1215
|
+
let node = commentInfo.commentNode.nextSibling;
|
|
1216
|
+
while (node && node !== boundary) {
|
|
1217
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
1218
|
+
yield node;
|
|
1219
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
1220
|
+
yield* commentsUnder(node);
|
|
1221
|
+
}
|
|
1222
|
+
node = node.nextSibling;
|
|
1223
|
+
}
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
yield* commentsUnder(scope);
|
|
1227
|
+
}
|
|
1228
|
+
function* commentsUnder(root) {
|
|
1229
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);
|
|
1230
|
+
while (walker.nextNode()) {
|
|
1231
|
+
yield walker.currentNode;
|
|
1178
1232
|
}
|
|
1179
|
-
return findChildScope(scope, `[${BF_SCOPE}$="_${cleanId}"]`);
|
|
1180
1233
|
}
|
|
1181
1234
|
function findChildScope(scope, selector) {
|
|
1182
1235
|
if (scope.matches?.(selector))
|
|
@@ -1276,7 +1329,10 @@ function findSsrScopeBySlotIn(parent, slotId, anchorScope, selfMatch) {
|
|
|
1276
1329
|
const suffixSelector = `[${BF_SCOPE}$="_${slotId}"]`;
|
|
1277
1330
|
if (selfMatch && parent.matches(suffixSelector))
|
|
1278
1331
|
return parent;
|
|
1279
|
-
|
|
1332
|
+
const bySuffix = parent.querySelector(suffixSelector);
|
|
1333
|
+
if (bySuffix)
|
|
1334
|
+
return bySuffix;
|
|
1335
|
+
return findCommentChildScope(parent, parentBfs ? [parentBfs] : [], slotId);
|
|
1280
1336
|
}
|
|
1281
1337
|
|
|
1282
1338
|
// src/runtime/registry.ts
|
|
@@ -2682,6 +2738,16 @@ function spreadAttrs(obj) {
|
|
|
2682
2738
|
}
|
|
2683
2739
|
return parts.join(" ");
|
|
2684
2740
|
}
|
|
2741
|
+
// src/runtime/date.ts
|
|
2742
|
+
function date(recv, op) {
|
|
2743
|
+
const zero = op === "toISOString" ? "" : 0;
|
|
2744
|
+
if (recv === null || recv === undefined)
|
|
2745
|
+
return zero;
|
|
2746
|
+
const d = recv instanceof Date ? recv : new Date(recv);
|
|
2747
|
+
if (Number.isNaN(d.getTime()))
|
|
2748
|
+
return zero;
|
|
2749
|
+
return d[op]();
|
|
2750
|
+
}
|
|
2685
2751
|
// src/runtime/insert.ts
|
|
2686
2752
|
function makeRegion(scope) {
|
|
2687
2753
|
if (scope.nodeType === Node.COMMENT_NODE) {
|
|
@@ -3076,6 +3142,7 @@ function render(container, nameOrDef, props = {}) {
|
|
|
3076
3142
|
for (const node of Array.from(tpl.content.childNodes)) {
|
|
3077
3143
|
container.appendChild(node);
|
|
3078
3144
|
}
|
|
3145
|
+
container.appendChild(document.createComment(`${BF_SCOPE_COMMENT_END_PREFIX}${scopeId}`));
|
|
3079
3146
|
const proxyEl = rootElements[0];
|
|
3080
3147
|
commentScopeRegistry.set(proxyEl, { commentNode, scopeId });
|
|
3081
3148
|
init(proxyEl, props);
|
|
@@ -3156,6 +3223,7 @@ export {
|
|
|
3156
3223
|
escapeAttr,
|
|
3157
3224
|
endTurn,
|
|
3158
3225
|
disposeScope,
|
|
3226
|
+
date,
|
|
3159
3227
|
cssEscape,
|
|
3160
3228
|
createSignal,
|
|
3161
3229
|
createSelector,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
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.0"
|
|
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
|
@@ -492,11 +492,91 @@ function $cSingle(scope: Element | null, id: string): Element | null {
|
|
|
492
492
|
// Precise match found nothing. Check if scope itself matches the short suffix
|
|
493
493
|
// (fragment root / inlined component where scope IS the child).
|
|
494
494
|
if (scope.matches?.(`[${BF_SCOPE}$="_${cleanId}"]`)) return scope
|
|
495
|
-
|
|
495
|
+
// Fragment-root child: no element carries the child's scope id — it is
|
|
496
|
+
// declared by a bf-scope: comment instead (#2289).
|
|
497
|
+
return findCommentChildScope(scope, parentScopeIds, cleanId)
|
|
496
498
|
}
|
|
497
499
|
|
|
498
500
|
// Fallback: no parent scope ID available — use short suffix match (best-effort)
|
|
499
501
|
return findChildScope(scope, `[${BF_SCOPE}$="_${cleanId}"]`)
|
|
502
|
+
?? findCommentChildScope(scope, [], cleanId)
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Resolve a comment-anchored child scope (fragment-root child, #2289).
|
|
507
|
+
*
|
|
508
|
+
* A child component whose root is a JSX Fragment has no element carrying
|
|
509
|
+
* `bf-s`/`bf-h`/`bf-m` — its scope is declared by a
|
|
510
|
+
* `<!--bf-scope:<parentId>_<slotId>|h=...|m=...-->` comment. The element
|
|
511
|
+
* selectors in `$cSingle` / `findSsrScopeBySlotIn` can never match it, so
|
|
512
|
+
* without this fallback the parent's `initChild(...)` receives `null` and
|
|
513
|
+
* the child is never initialized: every callback prop dies silently and
|
|
514
|
+
* no reactive update ever reaches the child.
|
|
515
|
+
*
|
|
516
|
+
* Matches on the scope id embedded in the comment (`<parentId>_<slotId>`,
|
|
517
|
+
* the same convention the element path's `[bf-s$="<parentId>_<slotId>"]`
|
|
518
|
+
* selector relies on) rather than on the `|h=`/`|m=` segments — the scope
|
|
519
|
+
* id is the first `|`-free token, so matching it cannot be confused by
|
|
520
|
+
* props JSON that happens to contain `|h=`.
|
|
521
|
+
*
|
|
522
|
+
* On match, the proxy element (first element sibling after the comment,
|
|
523
|
+
* falling back to the comment's parent) is registered in
|
|
524
|
+
* `commentScopeRegistry` so the child's own `$`/`$t` queries walk the
|
|
525
|
+
* comment range — the same wiring `hydrateCommentScope` performs for
|
|
526
|
+
* walker-owned root fragments.
|
|
527
|
+
*/
|
|
528
|
+
export function findCommentChildScope(
|
|
529
|
+
scope: Element,
|
|
530
|
+
parentIds: readonly string[],
|
|
531
|
+
slotId: string,
|
|
532
|
+
): Element | null {
|
|
533
|
+
for (const comment of commentsInScope(scope)) {
|
|
534
|
+
const id = parseCommentScopeId(comment.nodeValue ?? '', BF_SCOPE_COMMENT_PREFIX)
|
|
535
|
+
if (!id) continue
|
|
536
|
+
const matches = parentIds.length > 0
|
|
537
|
+
? parentIds.some(parentId => id === `${parentId}_${slotId}`)
|
|
538
|
+
: id.endsWith(`_${slotId}`) && !NESTED_SLOT_SUFFIX.test(id)
|
|
539
|
+
if (!matches) continue
|
|
540
|
+
|
|
541
|
+
const proxyEl = nextElementSibling(comment) ?? comment.parentElement
|
|
542
|
+
if (!proxyEl) return null
|
|
543
|
+
commentScopeRegistry.set(proxyEl, { commentNode: comment, scopeId: id })
|
|
544
|
+
return proxyEl
|
|
545
|
+
}
|
|
546
|
+
return null
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Enumerate comment nodes in a scope's DOM range: the comment-scope
|
|
551
|
+
* sibling range for comment-anchored scopes, the element subtree
|
|
552
|
+
* otherwise. Mirrors candidatesInScope's notion of "where the scope's
|
|
553
|
+
* content lives", but yields comments instead of elements.
|
|
554
|
+
*/
|
|
555
|
+
function* commentsInScope(scope: Element): Generator<Comment> {
|
|
556
|
+
const commentInfo = commentScopeRegistry.get(scope)
|
|
557
|
+
|
|
558
|
+
if (commentInfo) {
|
|
559
|
+
const boundary = getCommentScopeBoundary(commentInfo.commentNode)
|
|
560
|
+
let node: Node | null = commentInfo.commentNode.nextSibling
|
|
561
|
+
while (node && node !== boundary) {
|
|
562
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
563
|
+
yield node as Comment
|
|
564
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
565
|
+
yield* commentsUnder(node as Element)
|
|
566
|
+
}
|
|
567
|
+
node = node.nextSibling
|
|
568
|
+
}
|
|
569
|
+
return
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
yield* commentsUnder(scope)
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function* commentsUnder(root: Element): Generator<Comment> {
|
|
576
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT)
|
|
577
|
+
while (walker.nextNode()) {
|
|
578
|
+
yield walker.currentNode as Comment
|
|
579
|
+
}
|
|
500
580
|
}
|
|
501
581
|
|
|
502
582
|
/**
|
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
|
}
|