@barefootjs/jinja 0.21.4 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -189196,18 +189196,18 @@ var jinjaAdapter = new JinjaAdapter;
|
|
|
189196
189196
|
// src/conformance-pins.ts
|
|
189197
189197
|
var conformancePins = {
|
|
189198
189198
|
"static-array-from-props": [
|
|
189199
|
-
{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/
|
|
189199
|
+
{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2321" }
|
|
189200
189200
|
],
|
|
189201
189201
|
"static-array-from-props-with-component": [
|
|
189202
|
-
{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/
|
|
189202
|
+
{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2321" }
|
|
189203
189203
|
],
|
|
189204
189204
|
"filter-nested-callback-predicate": [
|
|
189205
|
-
{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/
|
|
189205
|
+
{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2320" }
|
|
189206
189206
|
],
|
|
189207
189207
|
"filter-nested-find-predicate": [
|
|
189208
|
-
{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/
|
|
189208
|
+
{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2320" }
|
|
189209
189209
|
],
|
|
189210
|
-
"dangerous-inner-html-dynamic": [{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/
|
|
189210
|
+
"dangerous-inner-html-dynamic": [{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2319" }],
|
|
189211
189211
|
"date-method-uncatalogued": [{ code: "BF021", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2274" }]
|
|
189212
189212
|
};
|
|
189213
189213
|
// src/render-divergences.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/jinja",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.1",
|
|
4
4
|
"description": "Jinja2 adapter for BarefootJS — compiles IR to .jinja templates and ships the Python BarefootJS rendering runtime; runs under any Python web framework (Flask, etc.)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -53,14 +53,14 @@
|
|
|
53
53
|
"directory": "packages/adapter-jinja"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@barefootjs/shared": "0.
|
|
56
|
+
"@barefootjs/shared": "0.24.1"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@barefootjs/jsx": ">=0.2.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@barefootjs/adapter-tests": "0.1.0",
|
|
63
|
-
"@barefootjs/jsx": "0.
|
|
63
|
+
"@barefootjs/jsx": "0.24.1",
|
|
64
64
|
"typescript": "^5.0.0"
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -958,6 +958,133 @@ class BarefootJS:
|
|
|
958
958
|
)
|
|
959
959
|
return 0
|
|
960
960
|
|
|
961
|
+
_FORMAT_DATE_TZ_RE = re.compile(r"^([+-])(\d{2}):(\d{2})$")
|
|
962
|
+
_FORMAT_DATE_TOKEN_RE = re.compile(r"YYYY|MMMM|MMM|MM|DD|dddd|ddd|M|D")
|
|
963
|
+
|
|
964
|
+
# `names`-table section offsets (spec/template-helpers.md "format_date",
|
|
965
|
+
# #2334) -- mirrors packages/client/src/format-date.ts's
|
|
966
|
+
# MONTHS_WIDE/MONTHS_ABBR/WEEKDAYS_WIDE/WEEKDAYS_ABBR constants.
|
|
967
|
+
_FORMAT_DATE_MONTHS_WIDE = 0
|
|
968
|
+
_FORMAT_DATE_MONTHS_ABBR = 12
|
|
969
|
+
_FORMAT_DATE_WEEKDAYS_WIDE = 24
|
|
970
|
+
_FORMAT_DATE_WEEKDAYS_ABBR = 31
|
|
971
|
+
|
|
972
|
+
def format_date(
|
|
973
|
+
self, recv: Any, pattern: str, tz: str, names: Optional[list] = None
|
|
974
|
+
) -> str:
|
|
975
|
+
"""`format_date(recv, pattern, tz, names)` -- the lowering target for
|
|
976
|
+
a `formatDate(date, pattern, timeZone, names)` call (#2324 numeric
|
|
977
|
+
tokens, #2334 name tokens; spec entry "format_date"). `recv` follows
|
|
978
|
+
the same receiver contract as `date` above -- this runtime's own
|
|
979
|
+
`datetime` or an ISO-8601 string; a nil or unparseable receiver
|
|
980
|
+
renders `''` (never a crash, never "now"). Mirrors
|
|
981
|
+
packages/client/src/format-date.ts byte-for-byte. Canonical arity is
|
|
982
|
+
4 -- the lowering always supplies `tz` and `names` (defaulting
|
|
983
|
+
`'UTC'` and `[]`).
|
|
984
|
+
|
|
985
|
+
`tz` is `'UTC'` or a fixed offset matching `±HH:MM`. The helper is
|
|
986
|
+
total: any other value -- an IANA zone name, a malformed offset --
|
|
987
|
+
normalizes to a zero-minute shift (UTC), so every backend degrades
|
|
988
|
+
identically instead of dragging in host tzdata. This is pure
|
|
989
|
+
arithmetic (`_FORMAT_DATE_TZ_RE` + an epoch-ms shift), never
|
|
990
|
+
`zoneinfo`. The shifted instant's UTC calendar fields are what the
|
|
991
|
+
pattern tokens read -- the shifted UTC clock face IS the local
|
|
992
|
+
clock face at that offset. The shift and field derivation run on
|
|
993
|
+
integers (Hinnant civil-from-days), NOT on a shifted `datetime`:
|
|
994
|
+
a positive offset on 9999-12-31 lands in year 10000, past
|
|
995
|
+
`datetime.max`, and must render `10000-…` like the JS reference
|
|
996
|
+
instead of overflowing (golden vector "positive offset pushes the
|
|
997
|
+
far-future instant past year 9999"). The weekday used by `dddd`/
|
|
998
|
+
`ddd` comes from the SAME integer path -- `(shifted_ms // 86_400_000
|
|
999
|
+
+ 4) % 7` (Python floor division; `+4` because epoch day 0,
|
|
1000
|
+
1970-01-01, is a Thursday, and the table is Sunday-first so
|
|
1001
|
+
`weekday == 0` must mean Sunday) -- rather than re-deriving it from
|
|
1002
|
+
`yoe`/`doy`, so it stays correct across the same year-10000
|
|
1003
|
+
overflow boundary the civil-from-days math above already handles.
|
|
1004
|
+
|
|
1005
|
+
`pattern` is scanned longest-match over
|
|
1006
|
+
`YYYY|MMMM|MMM|MM|DD|dddd|ddd|M|D` (`_FORMAT_DATE_TOKEN_RE`,
|
|
1007
|
+
alternation ordered so longer tokens win before their shorter
|
|
1008
|
+
prefixes); every other character -- including multi-byte ones like
|
|
1009
|
+
年/月/日 -- passes through literally. `YYYY` is `abs(year)`
|
|
1010
|
+
zero-padded to 4 digits, `-`-prefixed for a negative year; `MM`/`DD`
|
|
1011
|
+
zero-pad to 2; `M`/`D` are bare. `MMMM`/`MMM` read `names[month-1]`
|
|
1012
|
+
/ `names[12+month-1]` (wide/abbreviated month name); `dddd`/`ddd`
|
|
1013
|
+
read `names[24+weekday]` / `names[31+weekday]` (wide/abbreviated
|
|
1014
|
+
weekday name, Sunday-first) -- `names` is the flat table documented
|
|
1015
|
+
on the module's `formatDate` JS sibling: `[0..11]` wide months,
|
|
1016
|
+
`[12..23]` abbreviated months, `[24..30]` wide weekdays, `[31..37]`
|
|
1017
|
+
abbreviated weekdays. An index past the end of (or an altogether
|
|
1018
|
+
missing) `names` table renders `''` for that token, never an
|
|
1019
|
+
`IndexError`."""
|
|
1020
|
+
names = names or []
|
|
1021
|
+
|
|
1022
|
+
def _name_at(index: int) -> str:
|
|
1023
|
+
return names[index] if 0 <= index < len(names) else ""
|
|
1024
|
+
|
|
1025
|
+
if isinstance(recv, datetime.datetime):
|
|
1026
|
+
dt = recv
|
|
1027
|
+
else:
|
|
1028
|
+
s = str(recv)
|
|
1029
|
+
if s.endswith("Z"):
|
|
1030
|
+
s = s[:-1] + "+00:00"
|
|
1031
|
+
try:
|
|
1032
|
+
dt = datetime.datetime.fromisoformat(s)
|
|
1033
|
+
except ValueError:
|
|
1034
|
+
return ""
|
|
1035
|
+
dt = dt.replace(tzinfo=datetime.timezone.utc) if dt.tzinfo is None else dt.astimezone(datetime.timezone.utc)
|
|
1036
|
+
m = self._FORMAT_DATE_TZ_RE.match(tz)
|
|
1037
|
+
offset_minutes = 0
|
|
1038
|
+
if m:
|
|
1039
|
+
offset_minutes = (-1 if m.group(1) == "-" else 1) * (int(m.group(2)) * 60 + int(m.group(3)))
|
|
1040
|
+
delta = dt - _DATE_EPOCH
|
|
1041
|
+
epoch_ms = (delta.days * 86_400 + delta.seconds) * 1_000 + delta.microseconds // 1_000
|
|
1042
|
+
shifted_ms = epoch_ms + offset_minutes * 60_000
|
|
1043
|
+
# Hinnant civil-from-days over Python's floor division (exact for
|
|
1044
|
+
# negative epochs and for days past datetime.max).
|
|
1045
|
+
z = shifted_ms // 86_400_000 + 719_468
|
|
1046
|
+
era = z // 146_097
|
|
1047
|
+
doe = z - era * 146_097
|
|
1048
|
+
yoe = (doe - doe // 1_460 + doe // 36_524 - doe // 146_096) // 365
|
|
1049
|
+
doy = doe - (365 * yoe + yoe // 4 - yoe // 100)
|
|
1050
|
+
mp = (5 * doy + 2) // 153
|
|
1051
|
+
day = doy - (153 * mp + 2) // 5 + 1
|
|
1052
|
+
month = mp + 3 if mp < 10 else mp - 9
|
|
1053
|
+
year = yoe + era * 400 + (1 if month <= 2 else 0)
|
|
1054
|
+
yyyy = ("-" if year < 0 else "") + f"{abs(year):04d}"
|
|
1055
|
+
# Weekday from the SAME integer path as the civil-from-days fields
|
|
1056
|
+
# above (NOT re-derived from yoe/doy) -- 0=Sunday, matching the
|
|
1057
|
+
# table's Sunday-first layout. `(shifted_ms // 86_400_000)` is the
|
|
1058
|
+
# signed epoch-day count (Python floor division, exact for negative
|
|
1059
|
+
# epochs); `+4` anchors it since epoch day 0 (1970-01-01) is a
|
|
1060
|
+
# Thursday.
|
|
1061
|
+
days = shifted_ms // 86_400_000
|
|
1062
|
+
weekday = (days + 4) % 7
|
|
1063
|
+
|
|
1064
|
+
def _sub(match: "re.Match[str]") -> str:
|
|
1065
|
+
token = match.group(0)
|
|
1066
|
+
if token == "YYYY":
|
|
1067
|
+
return yyyy
|
|
1068
|
+
if token == "MMMM":
|
|
1069
|
+
return _name_at(self._FORMAT_DATE_MONTHS_WIDE + month - 1)
|
|
1070
|
+
if token == "MMM":
|
|
1071
|
+
return _name_at(self._FORMAT_DATE_MONTHS_ABBR + month - 1)
|
|
1072
|
+
if token == "MM":
|
|
1073
|
+
return f"{month:02d}"
|
|
1074
|
+
if token == "M":
|
|
1075
|
+
return str(month)
|
|
1076
|
+
if token == "DD":
|
|
1077
|
+
return f"{day:02d}"
|
|
1078
|
+
if token == "D":
|
|
1079
|
+
return str(day)
|
|
1080
|
+
if token == "dddd":
|
|
1081
|
+
return _name_at(self._FORMAT_DATE_WEEKDAYS_WIDE + weekday)
|
|
1082
|
+
if token == "ddd":
|
|
1083
|
+
return _name_at(self._FORMAT_DATE_WEEKDAYS_ABBR + weekday)
|
|
1084
|
+
return token
|
|
1085
|
+
|
|
1086
|
+
return self._FORMAT_DATE_TOKEN_RE.sub(_sub, pattern)
|
|
1087
|
+
|
|
961
1088
|
# -----------------------------------------------------------------
|
|
962
1089
|
# Array / String method helpers (#1448 Tier A)
|
|
963
1090
|
# -----------------------------------------------------------------
|
|
@@ -139,6 +139,7 @@ BINDINGS = {
|
|
|
139
139
|
"abs": bf.abs,
|
|
140
140
|
"to_fixed": lambda *a: bf.to_fixed(*a),
|
|
141
141
|
"date": lambda recv, op: bf.date(recv, op),
|
|
142
|
+
"format_date": lambda recv, pattern, tz, names=None: bf.format_date(recv, pattern, tz, names),
|
|
142
143
|
"lower": bf.lc,
|
|
143
144
|
"upper": bf.uc,
|
|
144
145
|
"trim": bf.trim,
|
package/src/conformance-pins.ts
CHANGED
|
@@ -42,15 +42,15 @@ export const conformancePins: ConformancePins = {
|
|
|
42
42
|
// see the "Loop array `<name>` is a local computed value" diagnostic.
|
|
43
43
|
// Fixing the underlying gap (computed-array-from-props as a loop
|
|
44
44
|
// source) is out of scope for #2087; tracked as a follow-up at
|
|
45
|
-
// https://github.com/piconic-ai/barefootjs/issues/
|
|
45
|
+
// https://github.com/piconic-ai/barefootjs/issues/2321.
|
|
46
46
|
'static-array-from-props': [
|
|
47
|
-
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/
|
|
47
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2321' },
|
|
48
48
|
],
|
|
49
49
|
// BF101 (unresolvable computed loop array, see above) fires; BF103
|
|
50
50
|
// (imported child in the loop body) no longer does now that the
|
|
51
51
|
// conformance harness passes `siblingTemplatesRegistered: true` (#2205).
|
|
52
52
|
'static-array-from-props-with-component': [
|
|
53
|
-
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/
|
|
53
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2321' },
|
|
54
54
|
],
|
|
55
55
|
// Rest-destructure / structured-path `.map()` callbacks (#2087 Phase B):
|
|
56
56
|
// `isLowerableLoopDestructure` now admits fixed bindings at any
|
|
@@ -80,12 +80,12 @@ export const conformancePins: ConformancePins = {
|
|
|
80
80
|
// lossy, same as xslate. The `/* @client */` twin
|
|
81
81
|
// (`filter-nested-callback-predicate-client`) has no pin here: it must
|
|
82
82
|
// render clean on every adapter, which asserts the suppression contract.
|
|
83
|
-
// https://github.com/piconic-ai/barefootjs/issues/2038
|
|
83
|
+
// Faithful lowering tracked: https://github.com/piconic-ai/barefootjs/issues/2320 (successor to #2038)
|
|
84
84
|
'filter-nested-callback-predicate': [
|
|
85
|
-
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/
|
|
85
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2320' },
|
|
86
86
|
],
|
|
87
87
|
'filter-nested-find-predicate': [
|
|
88
|
-
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/
|
|
88
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2320' },
|
|
89
89
|
],
|
|
90
90
|
// NB: TOP-LEVEL `.find` / `.findIndex` / `.findLast` / `.findLastIndex`
|
|
91
91
|
// (text position) are NOT pinned here — like xslate (unlike mojo, which
|
|
@@ -101,8 +101,8 @@ export const conformancePins: ConformancePins = {
|
|
|
101
101
|
// `dangerouslySetInnerHTML={{ __html: '...' }}` is spliced directly into
|
|
102
102
|
// the template as trusted raw text (`resolveDangerousInnerHtml`, #2207).
|
|
103
103
|
// A dynamic/signal-derived value still refuses with BF101 — see the
|
|
104
|
-
// `dangerous-inner-html-dynamic` fixture/pin below (tracked: #2215).
|
|
105
|
-
'dangerous-inner-html-dynamic': [{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/
|
|
104
|
+
// `dangerous-inner-html-dynamic` fixture/pin below (tracked: #2319, successor to #2215).
|
|
105
|
+
'dangerous-inner-html-dynamic': [{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2319' }],
|
|
106
106
|
// #2273: a method call on a prop typed as a built-in host rich type
|
|
107
107
|
// (Date, Map, …) has no catalogued lowering in any adapter — this is a
|
|
108
108
|
// compiler-level refusal (`checkRichTypeMethodCalls`, wired ahead of
|