@barefootjs/client 0.21.3 → 0.23.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/format-date.d.ts +30 -0
- package/dist/format-date.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +61 -1
- package/dist/runtime/query.d.ts +6 -0
- package/dist/runtime/query.d.ts.map +1 -1
- package/dist/runtime/standalone.js +61 -1
- package/package.json +2 -2
- package/src/format-date.ts +71 -0
- package/src/index.ts +1 -0
- package/src/runtime/index.ts +1 -0
- package/src/runtime/query.ts +61 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `formatDate` — format a `Date` (or ISO-8601 string) with an explicit pattern
|
|
3
|
+
* and an explicit UTC offset. The pure-function date formatter of #2324: every
|
|
4
|
+
* input is explicit, so the output is a deterministic function of its
|
|
5
|
+
* arguments — no host locale, no host timezone, no ICU/CLDR data. That is what
|
|
6
|
+
* lets the SSR adapters lower a `formatDate(...)` call to their native
|
|
7
|
+
* `format_date` runtime helper (spec/template-helpers.md) with byte-identical
|
|
8
|
+
* output on every backend.
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <time>{formatDate(createdAt, 'YYYY/M/D', '+09:00')}</time>
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Pattern tokens (longest-match; any other character passes through
|
|
15
|
+
* literally): `YYYY` (4-digit zero-padded year, `-`-prefixed for negative
|
|
16
|
+
* years), `MM` / `M` (zero-padded / bare month `01`–`12` / `1`–`12`), `DD` /
|
|
17
|
+
* `D` (zero-padded / bare day of month). The v1 token set is date-only and
|
|
18
|
+
* numeric — locale-dependent fields (month names, weekday names) are a
|
|
19
|
+
* deliberate later stage of #2324, and per-locale *pattern selection* belongs
|
|
20
|
+
* to the app's i18n layer, not to this function.
|
|
21
|
+
*
|
|
22
|
+
* `timeZone` is `'UTC'` or a fixed offset `±HH:MM` (`'+09:00'`, `'-05:30'`).
|
|
23
|
+
* The function is total: any other value — including IANA zone names, which
|
|
24
|
+
* would drag host tzdata versions into the output — normalizes to `'UTC'`, so
|
|
25
|
+
* every backend degrades identically instead of diverging. An unset or
|
|
26
|
+
* unparseable `date` renders `''` (the same normative zero-value discipline as
|
|
27
|
+
* the `date` helper, spec/template-helpers.md).
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatDate(date: Date | string, pattern: string, timeZone?: string): string;
|
|
30
|
+
//# sourceMappingURL=format-date.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-date.d.ts","sourceRoot":"","sources":["../src/format-date.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AASH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,SAAQ,GAAG,MAAM,CAkCzF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { __slot, type SlotMarker } from './slot.ts';
|
|
|
5
5
|
export { forwardProps } from './forward-props.ts';
|
|
6
6
|
export { unwrap } from './unwrap.ts';
|
|
7
7
|
export { queryHref, type QueryParams, type QueryParamValue } from './query-href.ts';
|
|
8
|
+
export { formatDate } from './format-date.ts';
|
|
8
9
|
export { createContext, type Context } from './context.ts';
|
|
9
10
|
export { useContext, provideContext, createPortal, isSSRPortal, findSiblingSlot, cleanupPortalPlaceholder, type Portal, type PortalChildren, type PortalOptions, type Renderable, } from './shims.ts';
|
|
10
11
|
export { Async, Region, type AsyncProps, type RegionProps, } from './builtins.ts';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,UAAU,EACV,cAAc,EACd,UAAU,EACV,SAAS,EACT,OAAO,EACP,OAAO,EACP,KAAK,EACL,eAAe,EACf,SAAS,EACT,OAAO,EACP,gBAAgB,EAMhB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,IAAI,EACT,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,6BAA6B,CAAA;AAEpC,OAAO,EACL,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAA;AAEnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,OAAO,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,UAAU,EACV,cAAc,EACd,UAAU,EACV,SAAS,EACT,OAAO,EACP,OAAO,EACP,KAAK,EACL,eAAe,EACf,SAAS,EACT,OAAO,EACP,gBAAgB,EAMhB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,IAAI,EACT,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,6BAA6B,CAAA;AAEpC,OAAO,EACL,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAA;AAEnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,OAAO,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,aAAa,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAE1D,OAAO,EACL,UAAU,EACV,cAAc,EACd,YAAY,EACZ,WAAW,EACX,eAAe,EACf,wBAAwB,EACxB,KAAK,MAAM,EACX,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,YAAY,CAAA;AAKnB,OAAO,EACL,KAAK,EACL,MAAM,EACN,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,eAAe,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -162,6 +162,43 @@ function queryHref(base, params) {
|
|
|
162
162
|
const qs = u.toString();
|
|
163
163
|
return qs ? `${base}?${qs}` : base;
|
|
164
164
|
}
|
|
165
|
+
// src/format-date.ts
|
|
166
|
+
var OFFSET_RE = /^([+-])(\d{2}):(\d{2})$/;
|
|
167
|
+
var TOKEN_RE = /YYYY|MM|DD|M|D/g;
|
|
168
|
+
function pad2(n) {
|
|
169
|
+
return String(n).padStart(2, "0");
|
|
170
|
+
}
|
|
171
|
+
function formatDate(date, pattern, timeZone = "UTC") {
|
|
172
|
+
if (date === null || date === undefined)
|
|
173
|
+
return "";
|
|
174
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
175
|
+
const t = d.getTime();
|
|
176
|
+
if (Number.isNaN(t))
|
|
177
|
+
return "";
|
|
178
|
+
const m = OFFSET_RE.exec(timeZone);
|
|
179
|
+
const offsetMinutes = m ? (m[1] === "-" ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0;
|
|
180
|
+
const s = new Date(t + offsetMinutes * 60000);
|
|
181
|
+
const year = s.getUTCFullYear();
|
|
182
|
+
const yyyy = (year < 0 ? "-" : "") + String(Math.abs(year)).padStart(4, "0");
|
|
183
|
+
const month = s.getUTCMonth() + 1;
|
|
184
|
+
const day = s.getUTCDate();
|
|
185
|
+
return pattern.replace(TOKEN_RE, (token) => {
|
|
186
|
+
switch (token) {
|
|
187
|
+
case "YYYY":
|
|
188
|
+
return yyyy;
|
|
189
|
+
case "MM":
|
|
190
|
+
return pad2(month);
|
|
191
|
+
case "M":
|
|
192
|
+
return String(month);
|
|
193
|
+
case "DD":
|
|
194
|
+
return pad2(day);
|
|
195
|
+
case "D":
|
|
196
|
+
return String(day);
|
|
197
|
+
default:
|
|
198
|
+
return token;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
165
202
|
// src/context.ts
|
|
166
203
|
function createContext(defaultValue) {
|
|
167
204
|
return {
|
|
@@ -216,6 +253,7 @@ export {
|
|
|
216
253
|
onCleanup,
|
|
217
254
|
isSSRPortal,
|
|
218
255
|
forwardProps,
|
|
256
|
+
formatDate,
|
|
219
257
|
findSiblingSlot,
|
|
220
258
|
endTurn,
|
|
221
259
|
createSignal,
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { __slot, type SlotMarker } from '../slot.ts';
|
|
|
5
5
|
export { forwardProps } from '../forward-props.ts';
|
|
6
6
|
export { unwrap } from '../unwrap.ts';
|
|
7
7
|
export { queryHref, type QueryParams, type QueryParamValue } from '../query-href.ts';
|
|
8
|
+
export { formatDate } from '../format-date.ts';
|
|
8
9
|
export { createContext, useContext, provideContext, setCurrentScope, type Context, } from './context.ts';
|
|
9
10
|
export { createPortal, isSSRPortal, findSiblingSlot, cleanupPortalPlaceholder, type Portal, type PortalOptions, type Renderable, type PortalChildren, } from './portal.ts';
|
|
10
11
|
export { reconcileList, type RenderItemFn } from './list.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,UAAU,EACV,cAAc,EACd,UAAU,EACV,SAAS,EACT,OAAO,EACP,OAAO,EACP,KAAK,EACL,eAAe,EACf,SAAS,EACT,OAAO,EACP,gBAAgB,EAOhB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,IAAI,EACT,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,6BAA6B,CAAA;AAEpC,OAAO,EACL,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;
|
|
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
|
@@ -162,6 +162,43 @@ function queryHref(base, params) {
|
|
|
162
162
|
const qs = u.toString();
|
|
163
163
|
return qs ? `${base}?${qs}` : base;
|
|
164
164
|
}
|
|
165
|
+
// src/format-date.ts
|
|
166
|
+
var OFFSET_RE = /^([+-])(\d{2}):(\d{2})$/;
|
|
167
|
+
var TOKEN_RE = /YYYY|MM|DD|M|D/g;
|
|
168
|
+
function pad2(n) {
|
|
169
|
+
return String(n).padStart(2, "0");
|
|
170
|
+
}
|
|
171
|
+
function formatDate(date, pattern, timeZone = "UTC") {
|
|
172
|
+
if (date === null || date === undefined)
|
|
173
|
+
return "";
|
|
174
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
175
|
+
const t = d.getTime();
|
|
176
|
+
if (Number.isNaN(t))
|
|
177
|
+
return "";
|
|
178
|
+
const m = OFFSET_RE.exec(timeZone);
|
|
179
|
+
const offsetMinutes = m ? (m[1] === "-" ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0;
|
|
180
|
+
const s = new Date(t + offsetMinutes * 60000);
|
|
181
|
+
const year = s.getUTCFullYear();
|
|
182
|
+
const yyyy = (year < 0 ? "-" : "") + String(Math.abs(year)).padStart(4, "0");
|
|
183
|
+
const month = s.getUTCMonth() + 1;
|
|
184
|
+
const day = s.getUTCDate();
|
|
185
|
+
return pattern.replace(TOKEN_RE, (token) => {
|
|
186
|
+
switch (token) {
|
|
187
|
+
case "YYYY":
|
|
188
|
+
return yyyy;
|
|
189
|
+
case "MM":
|
|
190
|
+
return pad2(month);
|
|
191
|
+
case "M":
|
|
192
|
+
return String(month);
|
|
193
|
+
case "DD":
|
|
194
|
+
return pad2(day);
|
|
195
|
+
case "D":
|
|
196
|
+
return String(day);
|
|
197
|
+
default:
|
|
198
|
+
return token;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
165
202
|
// ../shared/src/markers.ts
|
|
166
203
|
var BF_SCOPE = "bf-s";
|
|
167
204
|
var BF_SLOT = "bf";
|
|
@@ -656,6 +693,21 @@ function isInsideNestedCommentScope(element, scope) {
|
|
|
656
693
|
}
|
|
657
694
|
return false;
|
|
658
695
|
}
|
|
696
|
+
function isInsideNestedChildScope(element, root) {
|
|
697
|
+
let current = element;
|
|
698
|
+
while (current !== root) {
|
|
699
|
+
const parent = current.parentNode;
|
|
700
|
+
if (!parent)
|
|
701
|
+
return false;
|
|
702
|
+
if (parent !== root && parent.nodeType === Node.ELEMENT_NODE && parent.hasAttribute(BF_SCOPE)) {
|
|
703
|
+
return true;
|
|
704
|
+
}
|
|
705
|
+
if (isTopLevelCommentScopeNode(current))
|
|
706
|
+
return true;
|
|
707
|
+
current = parent;
|
|
708
|
+
}
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
659
711
|
function isTopLevelCommentScopeNode(node) {
|
|
660
712
|
const parent = node.parentNode;
|
|
661
713
|
if (!parent)
|
|
@@ -760,7 +812,14 @@ function qsa(el, selector) {
|
|
|
760
812
|
}
|
|
761
813
|
if (el.matches(selector))
|
|
762
814
|
return el;
|
|
763
|
-
|
|
815
|
+
const first = el.querySelector(selector);
|
|
816
|
+
if (!first || !isInsideNestedChildScope(first, el))
|
|
817
|
+
return first;
|
|
818
|
+
for (const candidate of el.querySelectorAll(selector)) {
|
|
819
|
+
if (candidate !== first && !isInsideNestedChildScope(candidate, el))
|
|
820
|
+
return candidate;
|
|
821
|
+
}
|
|
822
|
+
return null;
|
|
764
823
|
}
|
|
765
824
|
function splitTopLevelCommas(selector) {
|
|
766
825
|
const out = [];
|
|
@@ -2863,6 +2922,7 @@ export {
|
|
|
2863
2922
|
getComponentProps,
|
|
2864
2923
|
getComponentInit,
|
|
2865
2924
|
forwardProps,
|
|
2925
|
+
formatDate,
|
|
2866
2926
|
flushHydration,
|
|
2867
2927
|
findSiblingSlot,
|
|
2868
2928
|
findScope,
|
package/dist/runtime/query.d.ts
CHANGED
|
@@ -61,6 +61,12 @@ export declare function findCondTarget(scope: Element, selector: string): Elemen
|
|
|
61
61
|
* then its descendants. Unlike querySelector() which only searches descendants,
|
|
62
62
|
* this also matches the root element.
|
|
63
63
|
*
|
|
64
|
+
* Descendant candidates that fall inside a nested child component's own
|
|
65
|
+
* scope are skipped (`isInsideNestedChildScope`) — compiler slot IDs
|
|
66
|
+
* (`bf="sN"`) are assigned independently per component file, so a slot
|
|
67
|
+
* number can coincidentally collide between `el`'s own template and a
|
|
68
|
+
* child component mounted somewhere inside it (#2316).
|
|
69
|
+
*
|
|
64
70
|
* Used by compiler-generated code for event binding and attribute updates
|
|
65
71
|
* on loop items where the target may be the loop item's root element itself.
|
|
66
72
|
*/
|
|
@@ -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;AA2ND;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAClB,KAAK,EAAE,OAAO,GAAG,IAAI,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,OAAO,GACpB,OAAO,GAAG,IAAI,CAiChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAW/E;AAuBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,GAAG,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAsCxE;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;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAiB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,CAkBnE;AAyDD;;;;;;;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"}
|
|
@@ -815,6 +815,43 @@ function queryHref(base, params) {
|
|
|
815
815
|
const qs = u.toString();
|
|
816
816
|
return qs ? `${base}?${qs}` : base;
|
|
817
817
|
}
|
|
818
|
+
// src/format-date.ts
|
|
819
|
+
var OFFSET_RE = /^([+-])(\d{2}):(\d{2})$/;
|
|
820
|
+
var TOKEN_RE = /YYYY|MM|DD|M|D/g;
|
|
821
|
+
function pad2(n) {
|
|
822
|
+
return String(n).padStart(2, "0");
|
|
823
|
+
}
|
|
824
|
+
function formatDate(date, pattern, timeZone = "UTC") {
|
|
825
|
+
if (date === null || date === undefined)
|
|
826
|
+
return "";
|
|
827
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
828
|
+
const t = d.getTime();
|
|
829
|
+
if (Number.isNaN(t))
|
|
830
|
+
return "";
|
|
831
|
+
const m = OFFSET_RE.exec(timeZone);
|
|
832
|
+
const offsetMinutes = m ? (m[1] === "-" ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0;
|
|
833
|
+
const s = new Date(t + offsetMinutes * 60000);
|
|
834
|
+
const year = s.getUTCFullYear();
|
|
835
|
+
const yyyy = (year < 0 ? "-" : "") + String(Math.abs(year)).padStart(4, "0");
|
|
836
|
+
const month = s.getUTCMonth() + 1;
|
|
837
|
+
const day = s.getUTCDate();
|
|
838
|
+
return pattern.replace(TOKEN_RE, (token) => {
|
|
839
|
+
switch (token) {
|
|
840
|
+
case "YYYY":
|
|
841
|
+
return yyyy;
|
|
842
|
+
case "MM":
|
|
843
|
+
return pad2(month);
|
|
844
|
+
case "M":
|
|
845
|
+
return String(month);
|
|
846
|
+
case "DD":
|
|
847
|
+
return pad2(day);
|
|
848
|
+
case "D":
|
|
849
|
+
return String(day);
|
|
850
|
+
default:
|
|
851
|
+
return token;
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
}
|
|
818
855
|
// src/context.ts
|
|
819
856
|
function createContext(defaultValue) {
|
|
820
857
|
return {
|
|
@@ -1064,6 +1101,21 @@ function isInsideNestedCommentScope(element, scope) {
|
|
|
1064
1101
|
}
|
|
1065
1102
|
return false;
|
|
1066
1103
|
}
|
|
1104
|
+
function isInsideNestedChildScope(element, root) {
|
|
1105
|
+
let current = element;
|
|
1106
|
+
while (current !== root) {
|
|
1107
|
+
const parent = current.parentNode;
|
|
1108
|
+
if (!parent)
|
|
1109
|
+
return false;
|
|
1110
|
+
if (parent !== root && parent.nodeType === Node.ELEMENT_NODE && parent.hasAttribute(BF_SCOPE)) {
|
|
1111
|
+
return true;
|
|
1112
|
+
}
|
|
1113
|
+
if (isTopLevelCommentScopeNode(current))
|
|
1114
|
+
return true;
|
|
1115
|
+
current = parent;
|
|
1116
|
+
}
|
|
1117
|
+
return false;
|
|
1118
|
+
}
|
|
1067
1119
|
function isTopLevelCommentScopeNode(node) {
|
|
1068
1120
|
const parent = node.parentNode;
|
|
1069
1121
|
if (!parent)
|
|
@@ -1168,7 +1220,14 @@ function qsa(el, selector) {
|
|
|
1168
1220
|
}
|
|
1169
1221
|
if (el.matches(selector))
|
|
1170
1222
|
return el;
|
|
1171
|
-
|
|
1223
|
+
const first = el.querySelector(selector);
|
|
1224
|
+
if (!first || !isInsideNestedChildScope(first, el))
|
|
1225
|
+
return first;
|
|
1226
|
+
for (const candidate of el.querySelectorAll(selector)) {
|
|
1227
|
+
if (candidate !== first && !isInsideNestedChildScope(candidate, el))
|
|
1228
|
+
return candidate;
|
|
1229
|
+
}
|
|
1230
|
+
return null;
|
|
1172
1231
|
}
|
|
1173
1232
|
function splitTopLevelCommas(selector) {
|
|
1174
1233
|
const out = [];
|
|
@@ -3263,6 +3322,7 @@ export {
|
|
|
3263
3322
|
getComponentProps,
|
|
3264
3323
|
getComponentInit,
|
|
3265
3324
|
forwardProps,
|
|
3325
|
+
formatDate,
|
|
3266
3326
|
flushHydration,
|
|
3267
3327
|
findSiblingSlot,
|
|
3268
3328
|
findScope,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.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.23.0"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"@barefootjs/jsx": ">=0.2.0"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `formatDate` — format a `Date` (or ISO-8601 string) with an explicit pattern
|
|
3
|
+
* and an explicit UTC offset. The pure-function date formatter of #2324: every
|
|
4
|
+
* input is explicit, so the output is a deterministic function of its
|
|
5
|
+
* arguments — no host locale, no host timezone, no ICU/CLDR data. That is what
|
|
6
|
+
* lets the SSR adapters lower a `formatDate(...)` call to their native
|
|
7
|
+
* `format_date` runtime helper (spec/template-helpers.md) with byte-identical
|
|
8
|
+
* output on every backend.
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <time>{formatDate(createdAt, 'YYYY/M/D', '+09:00')}</time>
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Pattern tokens (longest-match; any other character passes through
|
|
15
|
+
* literally): `YYYY` (4-digit zero-padded year, `-`-prefixed for negative
|
|
16
|
+
* years), `MM` / `M` (zero-padded / bare month `01`–`12` / `1`–`12`), `DD` /
|
|
17
|
+
* `D` (zero-padded / bare day of month). The v1 token set is date-only and
|
|
18
|
+
* numeric — locale-dependent fields (month names, weekday names) are a
|
|
19
|
+
* deliberate later stage of #2324, and per-locale *pattern selection* belongs
|
|
20
|
+
* to the app's i18n layer, not to this function.
|
|
21
|
+
*
|
|
22
|
+
* `timeZone` is `'UTC'` or a fixed offset `±HH:MM` (`'+09:00'`, `'-05:30'`).
|
|
23
|
+
* The function is total: any other value — including IANA zone names, which
|
|
24
|
+
* would drag host tzdata versions into the output — normalizes to `'UTC'`, so
|
|
25
|
+
* every backend degrades identically instead of diverging. An unset or
|
|
26
|
+
* unparseable `date` renders `''` (the same normative zero-value discipline as
|
|
27
|
+
* the `date` helper, spec/template-helpers.md).
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const OFFSET_RE = /^([+-])(\d{2}):(\d{2})$/
|
|
31
|
+
const TOKEN_RE = /YYYY|MM|DD|M|D/g
|
|
32
|
+
|
|
33
|
+
function pad2(n: number): string {
|
|
34
|
+
return String(n).padStart(2, '0')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function formatDate(date: Date | string, pattern: string, timeZone = 'UTC'): string {
|
|
38
|
+
// Explicit nullish guard: `new Date(null)` is epoch 0, not Invalid Date,
|
|
39
|
+
// so without this a nil receiver would format 1970-01-01 instead of the
|
|
40
|
+
// contract's '' (spec/template-helpers.md "format_date", golden vector
|
|
41
|
+
// "nil receiver renders the empty string").
|
|
42
|
+
if (date === null || date === undefined) return ''
|
|
43
|
+
const d = date instanceof Date ? date : new Date(date)
|
|
44
|
+
const t = d.getTime()
|
|
45
|
+
if (Number.isNaN(t)) return ''
|
|
46
|
+
const m = OFFSET_RE.exec(timeZone)
|
|
47
|
+
const offsetMinutes = m ? (m[1] === '-' ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0
|
|
48
|
+
// Shift the instant by the offset, then read UTC fields: the shifted UTC
|
|
49
|
+
// clock face IS the local clock face at that offset.
|
|
50
|
+
const s = new Date(t + offsetMinutes * 60_000)
|
|
51
|
+
const year = s.getUTCFullYear()
|
|
52
|
+
const yyyy = (year < 0 ? '-' : '') + String(Math.abs(year)).padStart(4, '0')
|
|
53
|
+
const month = s.getUTCMonth() + 1
|
|
54
|
+
const day = s.getUTCDate()
|
|
55
|
+
return pattern.replace(TOKEN_RE, (token) => {
|
|
56
|
+
switch (token) {
|
|
57
|
+
case 'YYYY':
|
|
58
|
+
return yyyy
|
|
59
|
+
case 'MM':
|
|
60
|
+
return pad2(month)
|
|
61
|
+
case 'M':
|
|
62
|
+
return String(month)
|
|
63
|
+
case 'DD':
|
|
64
|
+
return pad2(day)
|
|
65
|
+
case 'D':
|
|
66
|
+
return String(day)
|
|
67
|
+
default:
|
|
68
|
+
return token
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -49,6 +49,7 @@ export { forwardProps } from './forward-props.ts'
|
|
|
49
49
|
export { unwrap } from './unwrap.ts'
|
|
50
50
|
|
|
51
51
|
export { queryHref, type QueryParams, type QueryParamValue } from './query-href.ts'
|
|
52
|
+
export { formatDate } from './format-date.ts'
|
|
52
53
|
|
|
53
54
|
export { createContext, type Context } from './context.ts'
|
|
54
55
|
|
package/src/runtime/index.ts
CHANGED
|
@@ -53,6 +53,7 @@ export { __slot, type SlotMarker } from '../slot.ts'
|
|
|
53
53
|
export { forwardProps } from '../forward-props.ts'
|
|
54
54
|
export { unwrap } from '../unwrap.ts'
|
|
55
55
|
export { queryHref, type QueryParams, type QueryParamValue } from '../query-href.ts'
|
|
56
|
+
export { formatDate } from '../format-date.ts'
|
|
56
57
|
|
|
57
58
|
// Context API (real DOM-bound implementations; `createContext` is the
|
|
58
59
|
// same pure function re-exported from `../context`).
|
package/src/runtime/query.ts
CHANGED
|
@@ -251,6 +251,48 @@ function isInsideNestedCommentScope(element: Element, scope: Element): boolean {
|
|
|
251
251
|
return false
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
/**
|
|
255
|
+
* True if `element` sits inside a nested child component's own scope
|
|
256
|
+
* boundary somewhere on the path up to `root` — a plain `[bf-s]` element
|
|
257
|
+
* (a regular child component's root) or a fragment-rooted child's
|
|
258
|
+
* comment-scope range mounted as a sibling along that path.
|
|
259
|
+
*
|
|
260
|
+
* Generalizes `isInsideNestedCommentScope` (which only catches the
|
|
261
|
+
* comment-scope case) to also catch plain `bf-s` boundaries, for `qsa()`
|
|
262
|
+
* — the loose, unfiltered sibling of `find()`'s `belongsToScope`-gated
|
|
263
|
+
* search. Unlike `belongsToScope`, this walks up to `root` by object
|
|
264
|
+
* identity rather than requiring `root` itself to carry `bf-s`: `qsa()`'s
|
|
265
|
+
* roots include `insert()`'s whole-component branch scope (which does
|
|
266
|
+
* carry `bf-s`) but also loop-item template clones (which normally don't),
|
|
267
|
+
* so a `.closest('[bf-s]') === root` check can't be reused here.
|
|
268
|
+
*
|
|
269
|
+
* Without this, a slot number that happens to collide between a component
|
|
270
|
+
* and a nested child mounted earlier in its DOM (compiler slot IDs are
|
|
271
|
+
* assigned independently per component file, so collisions are expected)
|
|
272
|
+
* makes `qsa()` return the child's element instead of the component's own.
|
|
273
|
+
*
|
|
274
|
+
* Deliberately does NOT reject `element` itself for carrying `bf-s`
|
|
275
|
+
* (unlike `belongsToScope`'s own-scope check) — `qsa()` doubles as the
|
|
276
|
+
* lookup for a nested child component's *own* scope root (e.g. compiled
|
|
277
|
+
* `qsa(parentScope, '[bf-h="X"][bf-m="sN"], [bf-s$="_sN"]')` calls feeding
|
|
278
|
+
* `initChild()`), and that target legitimately carries `bf-s`. Only an
|
|
279
|
+
* *intermediate* `bf-s` between `element` and `root` — i.e. `element`
|
|
280
|
+
* nested inside some other, unrelated child scope — disqualifies it.
|
|
281
|
+
*/
|
|
282
|
+
function isInsideNestedChildScope(element: Element, root: Element): boolean {
|
|
283
|
+
let current: Node = element
|
|
284
|
+
while (current !== root) {
|
|
285
|
+
const parent: Node | null = current.parentNode
|
|
286
|
+
if (!parent) return false
|
|
287
|
+
if (parent !== root && parent.nodeType === Node.ELEMENT_NODE && (parent as Element).hasAttribute(BF_SCOPE)) {
|
|
288
|
+
return true
|
|
289
|
+
}
|
|
290
|
+
if (isTopLevelCommentScopeNode(current)) return true
|
|
291
|
+
current = parent
|
|
292
|
+
}
|
|
293
|
+
return false
|
|
294
|
+
}
|
|
295
|
+
|
|
254
296
|
/**
|
|
255
297
|
* True if `node` falls inside a `<!--bf-scope:...-->` … `<!--bf-/scope:...-->`
|
|
256
298
|
* range among its own siblings — i.e. `node` is (or is inside) a top-level
|
|
@@ -409,6 +451,12 @@ function findInPortals(scopeId: string, selector: string): Element | null {
|
|
|
409
451
|
* then its descendants. Unlike querySelector() which only searches descendants,
|
|
410
452
|
* this also matches the root element.
|
|
411
453
|
*
|
|
454
|
+
* Descendant candidates that fall inside a nested child component's own
|
|
455
|
+
* scope are skipped (`isInsideNestedChildScope`) — compiler slot IDs
|
|
456
|
+
* (`bf="sN"`) are assigned independently per component file, so a slot
|
|
457
|
+
* number can coincidentally collide between `el`'s own template and a
|
|
458
|
+
* child component mounted somewhere inside it (#2316).
|
|
459
|
+
*
|
|
412
460
|
* Used by compiler-generated code for event binding and attribute updates
|
|
413
461
|
* on loop items where the target may be the loop item's root element itself.
|
|
414
462
|
*/
|
|
@@ -437,7 +485,19 @@ export function qsa(el: Element | null, selector: string): Element | null {
|
|
|
437
485
|
return qsaChildScope(el, selector)
|
|
438
486
|
}
|
|
439
487
|
if (el.matches(selector)) return el
|
|
440
|
-
|
|
488
|
+
|
|
489
|
+
// Fast path: the overwhelmingly common case has no nested-child-scope
|
|
490
|
+
// collision, so a single querySelector() (matching the old behavior)
|
|
491
|
+
// resolves it without the cost of enumerating every match. Only fall
|
|
492
|
+
// back to the full querySelectorAll() scan — needed to skip past a
|
|
493
|
+
// rejected candidate to the next DOM-order match — when the first hit
|
|
494
|
+
// actually turns out to belong to a nested child scope.
|
|
495
|
+
const first = el.querySelector(selector)
|
|
496
|
+
if (!first || !isInsideNestedChildScope(first, el)) return first
|
|
497
|
+
for (const candidate of el.querySelectorAll(selector)) {
|
|
498
|
+
if (candidate !== first && !isInsideNestedChildScope(candidate, el)) return candidate
|
|
499
|
+
}
|
|
500
|
+
return null
|
|
441
501
|
}
|
|
442
502
|
|
|
443
503
|
/** Split a CSS selector list on top-level commas, ignoring commas inside
|