@barefootjs/jinja 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/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/2087" }
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/2087" }
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/2038" }
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/2038" }
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/2215" }],
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.21.3",
3
+ "version": "0.23.0",
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.21.3"
56
+ "@barefootjs/shared": "0.23.0"
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.21.3",
63
+ "@barefootjs/jsx": "0.23.0",
64
64
  "typescript": "^5.0.0"
65
65
  }
66
66
  }
@@ -958,6 +958,85 @@ 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|MM|DD|M|D")
963
+
964
+ def format_date(self, recv: Any, pattern: str, tz: str) -> str:
965
+ """`format_date(recv, pattern, tz)` -- the lowering target for a
966
+ `formatDate(date, pattern, timeZone)` call (#2324, spec entry
967
+ "format_date"). `recv` follows the same receiver contract as `date`
968
+ above -- this runtime's own `datetime` or an ISO-8601 string; a nil
969
+ or unparseable receiver renders `''` (never a crash, never "now").
970
+ Mirrors packages/client/src/format-date.ts byte-for-byte.
971
+
972
+ `tz` is `'UTC'` or a fixed offset matching `±HH:MM`. The helper is
973
+ total: any other value -- an IANA zone name, a malformed offset --
974
+ normalizes to a zero-minute shift (UTC), so every backend degrades
975
+ identically instead of dragging in host tzdata. This is pure
976
+ arithmetic (`_FORMAT_DATE_TZ_RE` + an epoch-ms shift), never
977
+ `zoneinfo`. The shifted instant's UTC calendar fields are what the
978
+ pattern tokens read -- the shifted UTC clock face IS the local
979
+ clock face at that offset. The shift and field derivation run on
980
+ integers (Hinnant civil-from-days), NOT on a shifted `datetime`:
981
+ a positive offset on 9999-12-31 lands in year 10000, past
982
+ `datetime.max`, and must render `10000-…` like the JS reference
983
+ instead of overflowing (golden vector "positive offset pushes the
984
+ far-future instant past year 9999").
985
+
986
+ `pattern` is scanned longest-match over `YYYY|MM|DD|M|D`
987
+ (`_FORMAT_DATE_TOKEN_RE`, alternation ordered so the 2/4-char
988
+ tokens win before their single-char prefixes); every other
989
+ character -- including multi-byte ones like 年/月/日 -- passes
990
+ through literally. `YYYY` is `abs(year)` zero-padded to 4 digits,
991
+ `-`-prefixed for a negative year; `MM`/`DD` zero-pad to 2; `M`/`D`
992
+ are bare."""
993
+ if isinstance(recv, datetime.datetime):
994
+ dt = recv
995
+ else:
996
+ s = str(recv)
997
+ if s.endswith("Z"):
998
+ s = s[:-1] + "+00:00"
999
+ try:
1000
+ dt = datetime.datetime.fromisoformat(s)
1001
+ except ValueError:
1002
+ return ""
1003
+ dt = dt.replace(tzinfo=datetime.timezone.utc) if dt.tzinfo is None else dt.astimezone(datetime.timezone.utc)
1004
+ m = self._FORMAT_DATE_TZ_RE.match(tz)
1005
+ offset_minutes = 0
1006
+ if m:
1007
+ offset_minutes = (-1 if m.group(1) == "-" else 1) * (int(m.group(2)) * 60 + int(m.group(3)))
1008
+ delta = dt - _DATE_EPOCH
1009
+ epoch_ms = (delta.days * 86_400 + delta.seconds) * 1_000 + delta.microseconds // 1_000
1010
+ shifted_ms = epoch_ms + offset_minutes * 60_000
1011
+ # Hinnant civil-from-days over Python's floor division (exact for
1012
+ # negative epochs and for days past datetime.max).
1013
+ z = shifted_ms // 86_400_000 + 719_468
1014
+ era = z // 146_097
1015
+ doe = z - era * 146_097
1016
+ yoe = (doe - doe // 1_460 + doe // 36_524 - doe // 146_096) // 365
1017
+ doy = doe - (365 * yoe + yoe // 4 - yoe // 100)
1018
+ mp = (5 * doy + 2) // 153
1019
+ day = doy - (153 * mp + 2) // 5 + 1
1020
+ month = mp + 3 if mp < 10 else mp - 9
1021
+ year = yoe + era * 400 + (1 if month <= 2 else 0)
1022
+ yyyy = ("-" if year < 0 else "") + f"{abs(year):04d}"
1023
+
1024
+ def _sub(match: "re.Match[str]") -> str:
1025
+ token = match.group(0)
1026
+ if token == "YYYY":
1027
+ return yyyy
1028
+ if token == "MM":
1029
+ return f"{month:02d}"
1030
+ if token == "M":
1031
+ return str(month)
1032
+ if token == "DD":
1033
+ return f"{day:02d}"
1034
+ if token == "D":
1035
+ return str(day)
1036
+ return token
1037
+
1038
+ return self._FORMAT_DATE_TOKEN_RE.sub(_sub, pattern)
1039
+
961
1040
  # -----------------------------------------------------------------
962
1041
  # Array / String method helpers (#1448 Tier A)
963
1042
  # -----------------------------------------------------------------
@@ -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: bf.format_date(recv, pattern, tz),
142
143
  "lower": bf.lc,
143
144
  "upper": bf.uc,
144
145
  "trim": bf.trim,
@@ -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/2087.
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/2087' },
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/2087' },
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/2038' },
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/2038' },
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/2215' }],
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