@barefootjs/rust 0.19.1 → 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.
@@ -188814,7 +188814,7 @@ ${alternate}
188814
188814
  renderFragment(fragment) {
188815
188815
  const children = this.renderChildren(fragment.children);
188816
188816
  if (fragment.needsScopeComment) {
188817
- return `{{ bf.scope_comment() | safe }}${children}`;
188817
+ return `{{ bf.scope_comment() | safe }}${children}{{ bf.scope_comment_end() | safe }}`;
188818
188818
  }
188819
188819
  return children;
188820
188820
  }
@@ -1 +1 @@
1
- {"version":3,"file":"minijinja-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/minijinja-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"minijinja-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/minijinja-adapter.ts"],"names":[],"mappings":"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"}
package/dist/build.js CHANGED
@@ -188814,7 +188814,7 @@ ${alternate}
188814
188814
  renderFragment(fragment) {
188815
188815
  const children = this.renderChildren(fragment.children);
188816
188816
  if (fragment.needsScopeComment) {
188817
- return `{{ bf.scope_comment() | safe }}${children}`;
188817
+ return `{{ bf.scope_comment() | safe }}${children}{{ bf.scope_comment_end() | safe }}`;
188818
188818
  }
188819
188819
  return children;
188820
188820
  }
@@ -1 +1 @@
1
- {"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eA4F7B,CAAA"}
1
+ {"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eAkG7B,CAAA"}
package/dist/index.js CHANGED
@@ -188814,7 +188814,7 @@ ${alternate}
188814
188814
  renderFragment(fragment) {
188815
188815
  const children = this.renderChildren(fragment.children);
188816
188816
  if (fragment.needsScopeComment) {
188817
- return `{{ bf.scope_comment() | safe }}${children}`;
188817
+ return `{{ bf.scope_comment() | safe }}${children}{{ bf.scope_comment_end() | safe }}`;
188818
188818
  }
188819
188819
  return children;
188820
188820
  }
@@ -189201,7 +189201,8 @@ var conformancePins = {
189201
189201
  "filter-nested-find-predicate": [
189202
189202
  { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2038" }
189203
189203
  ],
189204
- "dangerous-inner-html-dynamic": [{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2215" }]
189204
+ "dangerous-inner-html-dynamic": [{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2215" }],
189205
+ "date-method-uncatalogued": [{ code: "BF021", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2274" }]
189205
189206
  };
189206
189207
  // src/render-divergences.ts
189207
189208
  var renderDivergences = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/rust",
3
- "version": "0.19.1",
3
+ "version": "0.21.0",
4
4
  "description": "minijinja (Rust) adapter for BarefootJS — compiles IR to .j2 templates and ships a Rust rendering runtime (packages/adapter-rust/runtime/); runs under any Rust web framework (axum, etc.)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -54,14 +54,14 @@
54
54
  "directory": "packages/adapter-rust"
55
55
  },
56
56
  "dependencies": {
57
- "@barefootjs/shared": "0.19.1"
57
+ "@barefootjs/shared": "0.21.0"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "@barefootjs/jsx": ">=0.2.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@barefootjs/adapter-tests": "0.1.0",
64
- "@barefootjs/jsx": "0.19.1",
64
+ "@barefootjs/jsx": "0.21.0",
65
65
  "typescript": "^5.0.0"
66
66
  }
67
67
  }
@@ -0,0 +1,196 @@
1
+ //! `date(recv, op)` -- zero-arg `Date.prototype` method lowering
2
+ //! (spec/template-helpers.md "date", #2274). `recv` is either this
3
+ //! runtime's own [`JsValue::Date`] (an epoch-millisecond `i64`) or an
4
+ //! ISO-8601 `String` (a template prop may carry either depending on how
5
+ //! the host populated it) -- both normalize to a single epoch-ms integer
6
+ //! via [`epoch_ms_of`] before dispatch.
7
+ //!
8
+ //! No `chrono` (or any other date crate) is a dependency of this crate --
9
+ //! see the design doc's fixed dependency list, the same constraint
10
+ //! `num.rs`'s numeric-string grammar docstring cites for avoiding `regex`.
11
+ //! Calendar <-> day-count conversion is Howard Hinnant's `civil_from_days`
12
+ //! / `days_from_civil` (public-domain algorithm,
13
+ //! <http://howardhinnant.github.io/date_algorithms.html>), correct for the
14
+ //! full proleptic-Gregorian range this catalogue's vectors exercise
15
+ //! (epoch 0, a pre-1970 instant, a leap day, and a four-digit-year
16
+ //! boundary). Every division that must floor (not truncate) toward
17
+ //! -Infinity for a negative dividend uses `div_euclid`/`rem_euclid`
18
+ //! explicitly rather than plain `/`/`%` (which truncate toward zero in
19
+ //! Rust) -- the one correctness trap this port must not reproduce.
20
+
21
+ use crate::num::JsValue;
22
+
23
+ const MS_PER_DAY: i64 = 86_400_000;
24
+
25
+ /// Hinnant `days_from_civil`: (proleptic-Gregorian y/m/d) -> days since the
26
+ /// Unix epoch (1970-01-01). `m` is 1-based (January = 1).
27
+ fn days_from_civil(y: i64, m: i64, d: i64) -> i64 {
28
+ let y = if m <= 2 { y - 1 } else { y };
29
+ let era = y.div_euclid(400);
30
+ let yoe = y.rem_euclid(400); // [0, 399]
31
+ let mp = if m > 2 { m - 3 } else { m + 9 }; // [0, 11], Mar=0 .. Feb=11
32
+ let doy = (153 * mp + 2).div_euclid(5) + d - 1; // [0, 365]
33
+ let doe = yoe * 365 + yoe.div_euclid(4) - yoe.div_euclid(100) + doy; // [0, 146096]
34
+ era * 146_097 + doe - 719_468
35
+ }
36
+
37
+ /// Hinnant `civil_from_days`: days since the Unix epoch -> (y, m, d), `m`
38
+ /// 1-based. Inverse of [`days_from_civil`].
39
+ fn civil_from_days(z: i64) -> (i64, i64, i64) {
40
+ let z = z + 719_468;
41
+ let era = z.div_euclid(146_097);
42
+ let doe = z.rem_euclid(146_097); // [0, 146096], always non-negative
43
+ // `doe` (and everything derived from it below) stays within [0,
44
+ // 146096] for the rest of this function, so plain truncating `/`
45
+ // already equals floor division here -- only the two extractions above
46
+ // (on a value that CAN be negative) need `div_euclid`.
47
+ let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146_096) / 365; // [0, 399]
48
+ let y = yoe + era * 400;
49
+ let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365]
50
+ let mp = (5 * doy + 2) / 153; // [0, 11]
51
+ let d = doy - (153 * mp + 2) / 5 + 1; // [1, 31]
52
+ let m = if mp < 10 { mp + 3 } else { mp - 9 }; // [1, 12]
53
+ let y = if m <= 2 { y + 1 } else { y };
54
+ (y, m, d)
55
+ }
56
+
57
+ /// Parse the exact ISO-8601 shape this catalogue's contract uses --
58
+ /// `YYYY-MM-DDTHH:MM:SS(.mmm)?Z`, always UTC (a bare `Z`, no numeric
59
+ /// offset) -- into epoch milliseconds. This is the one shape every string
60
+ /// the golden vectors pass in uses, and the one shape [`format_iso8601`]
61
+ /// below ever produces, so a fixed-width positional parse (no `regex`
62
+ /// crate in this crate's dependency set) covers the full contract without
63
+ /// a general-purpose date-string grammar.
64
+ pub fn parse_iso8601(s: &str) -> Option<i64> {
65
+ let b = s.as_bytes();
66
+ let has_ms = match b.len() {
67
+ 20 => false,
68
+ 24 => true,
69
+ _ => return None,
70
+ };
71
+ if b[4] != b'-' || b[7] != b'-' || b[10] != b'T' || b[13] != b':' || b[16] != b':' {
72
+ return None;
73
+ }
74
+ if has_ms && b[19] != b'.' {
75
+ return None;
76
+ }
77
+ if b[b.len() - 1] != b'Z' {
78
+ return None;
79
+ }
80
+ let year: i64 = s.get(0..4)?.parse().ok()?;
81
+ let month: i64 = s.get(5..7)?.parse().ok()?;
82
+ let day: i64 = s.get(8..10)?.parse().ok()?;
83
+ let hour: i64 = s.get(11..13)?.parse().ok()?;
84
+ let minute: i64 = s.get(14..16)?.parse().ok()?;
85
+ let second: i64 = s.get(17..19)?.parse().ok()?;
86
+ let ms: i64 = if has_ms { s.get(20..23)?.parse().ok()? } else { 0 };
87
+ if !(1..=12).contains(&month) || !(1..=31).contains(&day) {
88
+ return None;
89
+ }
90
+ if hour > 23 || minute > 59 || second > 60 {
91
+ return None;
92
+ }
93
+ let days = days_from_civil(year, month, day);
94
+ Some(days * MS_PER_DAY + hour * 3_600_000 + minute * 60_000 + second * 1000 + ms)
95
+ }
96
+
97
+ /// The inverse of [`parse_iso8601`]: epoch milliseconds -> the exact JS
98
+ /// `Date.prototype.toISOString` shape (always millisecond precision,
99
+ /// always UTC).
100
+ pub fn format_iso8601(ms: i64) -> String {
101
+ let days = ms.div_euclid(MS_PER_DAY);
102
+ let ms_of_day = ms.rem_euclid(MS_PER_DAY); // always [0, 86_399_999]
103
+ let (y, m, d) = civil_from_days(days);
104
+ let hour = ms_of_day / 3_600_000;
105
+ let minute = (ms_of_day / 60_000) % 60;
106
+ let second = (ms_of_day / 1000) % 60;
107
+ let msec = ms_of_day % 1000;
108
+ format!("{y:04}-{m:02}-{d:02}T{hour:02}:{minute:02}:{second:02}.{msec:03}Z")
109
+ }
110
+
111
+ /// Normalize a `date()` receiver to a single epoch-ms integer: this
112
+ /// runtime's own [`JsValue::Date`] unwraps directly; an ISO-8601
113
+ /// [`JsValue::String`] parses via [`parse_iso8601`]. Anything else (the
114
+ /// receiver contract's only two accepted shapes are not met) is `None`,
115
+ /// letting [`date`] apply its documented zero-value fallback.
116
+ pub fn epoch_ms_of(recv: &JsValue) -> Option<i64> {
117
+ match recv {
118
+ JsValue::Date(ms) => Some(*ms),
119
+ JsValue::String(s) => parse_iso8601(s),
120
+ _ => None,
121
+ }
122
+ }
123
+
124
+ /// Dispatch one of the zero-arg `Date.prototype` ops the compiler's
125
+ /// lowering plugin recognizes against an already-normalized epoch-ms
126
+ /// instant. Every accessor and `getTime` render as an INTEGER (a whole
127
+ /// `f64`, never fractional) -- `civil_from_days`'s inputs/outputs are all
128
+ /// exact integers, so no rounding is possible here regardless of how far
129
+ /// `ms` sits from the epoch. `getUTCMonth` is 0-based, matching JS (NOT
130
+ /// `civil_from_days`'s own 1-based `m`, hence the `- 1`).
131
+ pub fn date_op(ms: i64, op: &str) -> JsValue {
132
+ match op {
133
+ "getTime" => return JsValue::Number(ms as f64),
134
+ "toISOString" => return JsValue::String(format_iso8601(ms)),
135
+ _ => {}
136
+ }
137
+ let days = ms.div_euclid(MS_PER_DAY);
138
+ let ms_of_day = ms.rem_euclid(MS_PER_DAY);
139
+ let (y, m, d) = civil_from_days(days);
140
+ match op {
141
+ "getUTCFullYear" => JsValue::Number(y as f64),
142
+ "getUTCMonth" => JsValue::Number((m - 1) as f64),
143
+ "getUTCDate" => JsValue::Number(d as f64),
144
+ "getUTCHours" => JsValue::Number((ms_of_day / 3_600_000) as f64),
145
+ "getUTCMinutes" => JsValue::Number(((ms_of_day / 60_000) % 60) as f64),
146
+ "getUTCSeconds" => JsValue::Number(((ms_of_day / 1000) % 60) as f64),
147
+ _ => JsValue::Number(0.0),
148
+ }
149
+ }
150
+
151
+ /// `date(recv, op)` -- the full helper: normalize `recv` (native
152
+ /// [`JsValue::Date`] or ISO-8601 string) then dispatch `op`. An
153
+ /// unrecognized receiver shape falls back to `toTime`'s documented
154
+ /// zero-value contract (mirrors the Go runtime's `Date`/`toTime` fallback
155
+ /// pair): `''` for `toISOString`, `0` for every accessor/`getTime`.
156
+ pub fn date(recv: &JsValue, op: &str) -> JsValue {
157
+ match epoch_ms_of(recv) {
158
+ Some(ms) => date_op(ms, op),
159
+ None if op == "toISOString" => JsValue::String(String::new()),
160
+ None => JsValue::Number(0.0),
161
+ }
162
+ }
163
+
164
+ #[cfg(test)]
165
+ mod tests {
166
+ use super::*;
167
+
168
+ #[test]
169
+ fn round_trips_epoch_0() {
170
+ let ms = parse_iso8601("1970-01-01T00:00:00.000Z").unwrap();
171
+ assert_eq!(ms, 0);
172
+ assert_eq!(format_iso8601(ms), "1970-01-01T00:00:00.000Z");
173
+ }
174
+
175
+ #[test]
176
+ fn round_trips_pre_1970() {
177
+ let ms = parse_iso8601("1969-07-20T20:17:40.123Z").unwrap();
178
+ assert_eq!(ms, -14_182_939_877);
179
+ assert_eq!(format_iso8601(ms), "1969-07-20T20:17:40.123Z");
180
+ }
181
+
182
+ #[test]
183
+ fn round_trips_leap_day() {
184
+ let ms = parse_iso8601("2024-02-29T23:59:59.999Z").unwrap();
185
+ assert_eq!(format_iso8601(ms), "2024-02-29T23:59:59.999Z");
186
+ assert_eq!(date_op(ms, "getUTCMonth"), JsValue::Number(1.0));
187
+ assert_eq!(date_op(ms, "getUTCDate"), JsValue::Number(29.0));
188
+ }
189
+
190
+ #[test]
191
+ fn round_trips_far_future() {
192
+ let ms = parse_iso8601("9999-12-31T23:59:59.999Z").unwrap();
193
+ assert_eq!(format_iso8601(ms), "9999-12-31T23:59:59.999Z");
194
+ assert_eq!(date_op(ms, "getUTCFullYear"), JsValue::Number(9999.0));
195
+ }
196
+ }
@@ -306,6 +306,14 @@ pub fn to_number(v: &JsValue) -> f64 {
306
306
  }
307
307
  }
308
308
  JsValue::Array(_) | JsValue::Object(_) => f64::NAN,
309
+ // Unreachable in practice: this evaluator only ever sees `JsValue`s
310
+ // decoded from a `serde_json::Value` ParsedExpr tree or produced by
311
+ // its own arithmetic, and JSON can't spell a `Date` (see the `num`
312
+ // module docstring) -- kept exhaustive rather than `unreachable!()`
313
+ // since `JsValue` is a shared, cross-module enum. `valueOf`-style
314
+ // (matches `runtime::js_number`'s `JsValue::Date` arm) for
315
+ // consistency if that ever changes.
316
+ JsValue::Date(ms) => *ms as f64,
309
317
  }
310
318
  }
311
319
 
@@ -335,6 +343,10 @@ pub fn to_string(v: &JsValue) -> String {
335
343
  .collect::<Vec<_>>()
336
344
  .join(","),
337
345
  JsValue::Object(_) => "[object Object]".to_string(),
346
+ // Unreachable in practice -- see `to_number`'s `JsValue::Date` arm
347
+ // above. `toISOString` is the one Date->string shape this
348
+ // catalogue defines (matches `runtime::js_string`).
349
+ JsValue::Date(ms) => crate::date::format_iso8601(*ms),
338
350
  }
339
351
  }
340
352
 
@@ -345,6 +357,10 @@ pub fn truthy(v: &JsValue) -> bool {
345
357
  JsValue::Number(n) => !n.is_nan() && *n != 0.0, // nonzero and not NaN
346
358
  JsValue::String(s) => !s.is_empty(), // incl. the truthy "0"
347
359
  JsValue::Array(_) | JsValue::Object(_) => true,
360
+ // Unreachable in practice -- see `to_number`'s `JsValue::Date` arm
361
+ // above. A JS Date object is always truthy, like every other
362
+ // object.
363
+ JsValue::Date(_) => true,
348
364
  }
349
365
  }
350
366
 
@@ -7,6 +7,7 @@
7
7
  //! documented divergences.
8
8
 
9
9
  pub mod backend_minijinja;
10
+ pub mod date;
10
11
  pub mod evaluator;
11
12
  pub mod manifest;
12
13
  pub mod num;
@@ -94,6 +94,16 @@ pub enum JsValue {
94
94
  /// contract for free, in one place, rather than re-sorting at the
95
95
  /// JSON-encoding boundary.
96
96
  Object(BTreeMap<String, JsValue>),
97
+ /// This runtime's own native `Date` receiver shape for `date()`'s
98
+ /// zero-arg method lowering (spec/template-helpers.md "date", #2274) --
99
+ /// a signed epoch-MILLISECOND integer (never a float), so a pre-1970
100
+ /// instant round-trips exactly with no rounding risk. The other half of
101
+ /// `date()`'s receiver contract, an ISO-8601 `String`, needs no
102
+ /// dedicated variant since [`JsValue::String`] already carries it; see
103
+ /// `date::epoch_ms_of`. JSON can't spell this type (`from_json` never
104
+ /// produces one); `to_json` renders it via its `toISOString` form,
105
+ /// matching JS `JSON.stringify(date)`.
106
+ Date(i64),
97
107
  }
98
108
 
99
109
  impl JsValue {
@@ -159,6 +169,9 @@ impl JsValue {
159
169
  JsValue::Object(o) => {
160
170
  JsonValue::Object(o.iter().map(|(k, v)| (k.clone(), v.to_json())).collect())
161
171
  }
172
+ // Matches JS `JSON.stringify(date)`, which calls the date's own
173
+ // `toJSON` -> `toISOString`.
174
+ JsValue::Date(ms) => JsonValue::String(crate::date::format_iso8601(*ms)),
162
175
  }
163
176
  }
164
177
  }
@@ -43,9 +43,10 @@
43
43
  //! operator it emits.
44
44
 
45
45
  use crate::backend_minijinja;
46
+ use crate::date;
46
47
  use crate::evaluator;
47
48
  use crate::num::{self, JsValue};
48
- use minijinja::value::{from_args, Enumerator, Kwargs, Object, Value as MjValue, ValueKind};
49
+ use minijinja::value::{from_args, Enumerator, Kwargs, Object, ObjectRepr, Value as MjValue, ValueKind};
49
50
  use minijinja::{Error, ErrorKind, State};
50
51
  use std::collections::{BTreeMap, HashMap, HashSet};
51
52
  use std::sync::{Arc, Mutex};
@@ -111,6 +112,11 @@ pub fn js_string(v: &JsValue) -> String {
111
112
  .collect::<Vec<_>>()
112
113
  .join(","),
113
114
  JsValue::Object(_) => "[object Object]".to_string(),
115
+ // No JS `Date.prototype.toString()` port (that format -- e.g. "Fri
116
+ // Jul 20 1969 20:17:40 GMT+0000" -- is out of this catalogue's
117
+ // scope, #2274); `toISOString` is the one Date->string shape the
118
+ // spec defines, so a bare `bf.string(date)` uses it too.
119
+ JsValue::Date(ms) => date::format_iso8601(*ms),
114
120
  }
115
121
  }
116
122
 
@@ -125,6 +131,9 @@ pub fn js_number(v: &JsValue) -> f64 {
125
131
  JsValue::Number(n) => *n,
126
132
  JsValue::String(s) => if num::looks_like_number(s) { num::parse_number_literal(s) } else { f64::NAN },
127
133
  JsValue::Array(_) | JsValue::Object(_) => f64::NAN,
134
+ // `Number(date)` mirrors JS `Date`'s default `valueOf` coercion:
135
+ // the epoch-ms count itself.
136
+ JsValue::Date(ms) => *ms as f64,
128
137
  }
129
138
  }
130
139
 
@@ -137,6 +146,7 @@ pub fn js_truthy(v: &JsValue) -> bool {
137
146
  JsValue::Number(n) => !n.is_nan() && *n != 0.0, // not NaN, not zero
138
147
  JsValue::String(s) => !s.is_empty(), // incl. the JS-truthy "0"
139
148
  JsValue::Array(_) | JsValue::Object(_) => true,
149
+ JsValue::Date(_) => true, // a JS Date object is always truthy
140
150
  }
141
151
  }
142
152
 
@@ -440,13 +450,13 @@ fn is_numeric_like(v: &JsValue) -> bool {
440
450
  JsValue::Null | JsValue::Bool(_) => false,
441
451
  JsValue::Number(_) => true,
442
452
  JsValue::String(s) => num::looks_like_number(s),
443
- JsValue::Array(_) | JsValue::Object(_) => false,
453
+ JsValue::Array(_) | JsValue::Object(_) | JsValue::Date(_) => false,
444
454
  }
445
455
  }
446
456
 
447
457
  fn numeric_value(v: &JsValue) -> f64 {
448
458
  match v {
449
- JsValue::Null | JsValue::Array(_) | JsValue::Object(_) => 0.0,
459
+ JsValue::Null | JsValue::Array(_) | JsValue::Object(_) | JsValue::Date(_) => 0.0,
450
460
  JsValue::Bool(b) => if *b { 1.0 } else { 0.0 },
451
461
  JsValue::Number(n) => *n,
452
462
  JsValue::String(s) => if num::looks_like_number(s) { num::parse_number_literal(s) } else { 0.0 },
@@ -936,6 +946,13 @@ impl BfInstance {
936
946
  format!("<!--bf-scope:{}{host_segment}{props_json}-->", self.scope_id)
937
947
  }
938
948
 
949
+ /// Paired end marker for `scope_comment`, emitted after the fragment's
950
+ /// last top-level node. No host/props segments -- the client only needs
951
+ /// the scope id to close the boundary (#2289).
952
+ fn scope_comment_end(&self) -> String {
953
+ format!("<!--bf-/scope:{}-->", self.scope_id)
954
+ }
955
+
939
956
  fn provide_context(&self, name: &str, value: JsValue) {
940
957
  self.session.context_stacks.lock().unwrap().entry(name.to_string()).or_default().push(value);
941
958
  }
@@ -1075,6 +1092,14 @@ impl BfInstance {
1075
1092
  // ---------------------------------------------------------------------------
1076
1093
 
1077
1094
  pub fn mj_to_js(v: &MjValue) -> JsValue {
1095
+ // A `JsDate`-wrapped native Date prop (see that struct's docstring)
1096
+ // isn't a Seq/Map/Iterable, so it would otherwise fall into the
1097
+ // catch-all `_ => JsValue::Null` below, silently losing the receiver
1098
+ // `date()` needs -- checked first, ahead of the generic `v.kind()`
1099
+ // dispatch.
1100
+ if let Some(d) = v.downcast_object_ref::<JsDate>() {
1101
+ return JsValue::Date(d.0);
1102
+ }
1078
1103
  match v.kind() {
1079
1104
  ValueKind::Undefined | ValueKind::None => JsValue::Null,
1080
1105
  ValueKind::Bool => JsValue::Bool(v.is_true()),
@@ -1148,6 +1173,39 @@ pub fn js_to_mj(v: &JsValue) -> MjValue {
1148
1173
  let map: BTreeMap<String, MjValue> = o.iter().map(|(k, v)| (k.clone(), js_to_mj(v))).collect();
1149
1174
  MjValue::from(map)
1150
1175
  }
1176
+ JsValue::Date(ms) => MjValue::from_object(JsDate(*ms)),
1177
+ }
1178
+ }
1179
+
1180
+ /// This runtime's own native "Date" value, wrapped as a minijinja
1181
+ /// `Object` so a host can seed a template var with a real Date-typed
1182
+ /// receiver instead of always going through `date()`'s ISO-8601-string
1183
+ /// contract half (spec/template-helpers.md "date", #2274; mirrors the Go
1184
+ /// runtime's `time.Time` / the Ruby runtime's `Time` as the "native"
1185
+ /// receiver shape). Carries nothing but the epoch-ms integer
1186
+ /// `JsValue::Date` already carries -- `mj_to_js`/`js_to_mj` above
1187
+ /// round-trip it losslessly via `downcast_object_ref`/`from_object`.
1188
+ #[derive(Debug)]
1189
+ struct JsDate(i64);
1190
+
1191
+ impl Object for JsDate {
1192
+ fn repr(self: &Arc<Self>) -> ObjectRepr {
1193
+ ObjectRepr::Plain
1194
+ }
1195
+
1196
+ // A JS `Date` object is always truthy, like every other object.
1197
+ fn is_true(self: &Arc<Self>) -> bool {
1198
+ true
1199
+ }
1200
+
1201
+ fn render(self: &Arc<Self>, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
1202
+ where
1203
+ Self: Sized + 'static,
1204
+ {
1205
+ // `toISOString` is the one Date->string shape this catalogue
1206
+ // defines (see `js_string`'s `JsValue::Date` arm); a bare
1207
+ // interpolation of a native Date value uses the same shape.
1208
+ write!(f, "{}", date::format_iso8601(self.0))
1151
1209
  }
1152
1210
  }
1153
1211
 
@@ -1240,6 +1298,7 @@ impl Object for BfInstance {
1240
1298
  "text_start" => Ok(safe(format!("<!--bf:{}-->", js_string(a(0))))),
1241
1299
  "text_end" => Ok(safe("<!--/-->".to_string())),
1242
1300
  "scope_comment" => Ok(safe(self.scope_comment())),
1301
+ "scope_comment_end" => Ok(safe(self.scope_comment_end())),
1243
1302
 
1244
1303
  // -- Script registration ---------------------------------------
1245
1304
  "register_script" => {
@@ -1272,6 +1331,13 @@ impl Object for BfInstance {
1272
1331
  "max" => Ok(MjValue::from(num::js_max(js_number(a(0)), js_number(a(1))))),
1273
1332
  "abs" => Ok(MjValue::from(num::js_abs(js_number(a(0))))),
1274
1333
  "to_fixed" => Ok(MjValue::from(num::to_fixed(js_number(a(0)), num::to_f64(a(1)) as i32))),
1334
+ // `date(recv, op)` -- zero-arg `Date.prototype` method
1335
+ // lowering (spec/template-helpers.md "date", #2274). `recv` is
1336
+ // either a `JsDate`-wrapped native receiver (already converted
1337
+ // to `JsValue::Date` by `mj_to_js` above) or an ISO-8601
1338
+ // string; `date::date` normalizes and dispatches both. `op`
1339
+ // stays a plain string arg, never routed through `js_number`.
1340
+ "date" => Ok(js_to_mj(&date::date(a(0), a(1).as_str().unwrap_or("")))),
1275
1341
 
1276
1342
  // -- Array / string method helpers (#1448 Tier A) ------------------
1277
1343
  "includes" => Ok(MjValue::from(includes(a(0), a(1)))),
@@ -24,6 +24,7 @@
24
24
  //! `tests/vector-divergences.json`.
25
25
 
26
26
  use barefootjs::backend_minijinja;
27
+ use barefootjs::date;
27
28
  use barefootjs::num::{self, JsValue};
28
29
  use barefootjs::runtime;
29
30
  use barefootjs::SearchParams;
@@ -44,6 +45,31 @@ fn obj(v: &JsValue, key: &str) -> JsValue {
44
45
  v.as_object().and_then(|m| m.get(key)).cloned().unwrap_or(JsValue::Null)
45
46
  }
46
47
 
48
+ /// Materializes the `{"$date": "<ISO>"}` native-date arg sentinel (#2288)
49
+ /// into this runtime's own `JsValue::Date`, so `date()`'s native-receiver
50
+ /// branch (not just its ISO-string branch) is exercised. Recurses through
51
+ /// arrays/objects the same shape the Perl port's `normalize_arg` walks,
52
+ /// since a vector's `args` may nest the sentinel inside a higher-order
53
+ /// projection payload. Panics on an unparseable `$date` string -- the
54
+ /// corpus is generated from a fixed set of known-good ISO instants, so a
55
+ /// parse failure here means the harness itself is broken, not the case
56
+ /// under test.
57
+ fn materialize_arg(v: &JsonValue) -> JsValue {
58
+ if let Some(o) = v.as_object() {
59
+ if let Some(iso) = o.get("$date").and_then(|d| d.as_str()) {
60
+ if o.len() == 1 {
61
+ let ms = date::parse_iso8601(iso).unwrap_or_else(|| panic!("materialize_arg: invalid $date {iso:?}"));
62
+ return JsValue::Date(ms);
63
+ }
64
+ }
65
+ return JsValue::Object(o.iter().map(|(k, x)| (k.clone(), materialize_arg(x))).collect());
66
+ }
67
+ if let Some(a) = v.as_array() {
68
+ return JsValue::Array(a.iter().map(materialize_arg).collect());
69
+ }
70
+ JsValue::from_json(v)
71
+ }
72
+
47
73
  /// Mirrors `test_helper_vectors.py`'s `_truthy_pred`.
48
74
  fn truthy_pred(item: &JsValue, field: &str) -> bool {
49
75
  runtime::js_truthy(&obj(item, field))
@@ -123,6 +149,7 @@ fn call_binding(fn_name: &str, args: &[JsValue]) -> Option<JsValue> {
123
149
  let digits = if args.len() > 1 { num::to_f64(&a(1)) as i32 } else { 0 };
124
150
  JsValue::String(num::to_fixed(runtime::js_number(&a(0)), digits))
125
151
  }
152
+ "date" => date::date(&a(0), a(1).as_str().unwrap_or("")),
126
153
  "lower" => JsValue::String(runtime::js_string(&a(0)).to_lowercase()),
127
154
  "upper" => JsValue::String(runtime::js_string(&a(0)).to_uppercase()),
128
155
  "trim" => JsValue::String(runtime::trim(&a(0))),
@@ -237,6 +264,11 @@ fn py_truthy(v: &JsValue) -> bool {
237
264
  JsValue::String(s) => !s.is_empty(),
238
265
  JsValue::Array(a) => !a.is_empty(),
239
266
  JsValue::Object(o) => !o.is_empty(),
267
+ // Unreachable: no binding in this file ever returns a
268
+ // `JsValue::Date` (`date()` itself always returns `Number` or
269
+ // `String`, per its own return type) -- kept exhaustive since
270
+ // `JsValue` is a shared enum.
271
+ JsValue::Date(_) => true,
240
272
  }
241
273
  }
242
274
 
@@ -298,7 +330,7 @@ fn helper_vectors_match_js_reference_or_declared_divergence() {
298
330
  let note = case["note"].as_str().unwrap();
299
331
  let key = format!("{fn_name}/{note}");
300
332
  let raw_args = case["args"].as_array().cloned().unwrap_or_default();
301
- let args: Vec<JsValue> = raw_args.iter().map(JsValue::from_json).collect();
333
+ let args: Vec<JsValue> = raw_args.iter().map(materialize_arg).collect();
302
334
  let expect = &case["expect"];
303
335
 
304
336
  if let Some(reason) = div_file.unsupported.get(fn_name) {
@@ -530,3 +530,28 @@ function Widget({ rows }: { rows: { x: string }[] }) {
530
530
  expect(template).toContain('bf.string(cfg.x)')
531
531
  })
532
532
  })
533
+
534
+ describe('MinijinjaAdapter - fragment-root scope comment end marker (#2289)', () => {
535
+ // A fragment-rooted component (multiple top-level nodes) uses a
536
+ // comment-based scope marker instead of a real DOM element, so it needs a
537
+ // paired end marker after the last top-level node -- otherwise
538
+ // client-side queries from this scope leak onto later siblings owned by
539
+ // the parent. See `scope_comment_end` in `runtime/src/runtime.rs`.
540
+ test('emits a bf-/scope end marker with the same scope id as the begin marker', () => {
541
+ const { template } = compileAndGenerate(`
542
+ export function Wrapper({ children }: { children?: any }) {
543
+ return (
544
+ <>
545
+ <div>Header</div>
546
+ {children}
547
+ </>
548
+ )
549
+ }
550
+ `)
551
+ expect(template).toContain('{{ bf.scope_comment() | safe }}')
552
+ expect(template).toContain('{{ bf.scope_comment_end() | safe }}')
553
+ expect(template.indexOf('bf.scope_comment()')).toBeLessThan(
554
+ template.indexOf('bf.scope_comment_end()'),
555
+ )
556
+ })
557
+ })
@@ -1262,7 +1262,10 @@ export class MinijinjaAdapter extends BaseAdapter implements IRNodeEmitter<Jinja
1262
1262
  private renderFragment(fragment: IRFragment): string {
1263
1263
  const children = this.renderChildren(fragment.children)
1264
1264
  if (fragment.needsScopeComment) {
1265
- return `{{ bf.scope_comment() | safe }}${children}`
1265
+ // End marker bounds the scope's sibling range -- without it, queries
1266
+ // from the fragment scope leak onto later siblings owned by the
1267
+ // parent (#2289).
1268
+ return `{{ bf.scope_comment() | safe }}${children}{{ bf.scope_comment_end() | safe }}`
1266
1269
  }
1267
1270
  return children
1268
1271
  }
@@ -103,4 +103,10 @@ export const conformancePins: ConformancePins = {
103
103
  // A dynamic/signal-derived value still refuses with BF101 — see the
104
104
  // `dangerous-inner-html-dynamic` fixture/pin below (tracked: #2215).
105
105
  'dangerous-inner-html-dynamic': [{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2215' }],
106
+ // #2273: a method call on a prop typed as a built-in host rich type
107
+ // (Date, Map, …) has no catalogued lowering in any adapter — this is a
108
+ // compiler-level refusal (`checkRichTypeMethodCalls`, wired ahead of
109
+ // `adapter.generate()`), not an adapter-specific gap, so it is pinned
110
+ // identically across every adapter package including Hono.
111
+ 'date-method-uncatalogued': [{ code: 'BF021', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2274' }],
106
112
  }
@@ -529,6 +529,15 @@ function buildVars(
529
529
  * uniformly rather than threading the transform through each builder.
530
530
  */
531
531
  function encodeSpecials(value: unknown): unknown {
532
+ if (value instanceof Date) {
533
+ // A Date prop crosses the language boundary as its ISO-8601 string —
534
+ // the shared `date()` runtime helper (#2274/#2288) accepts an ISO
535
+ // string as well as a native `chrono` date type. MUST come before the
536
+ // generic object branch below, which REBUILDS the object via
537
+ // `Object.entries()` — a Date's fields are non-enumerable, so that
538
+ // rebuild would silently erase it into `{}`.
539
+ return value.toISOString()
540
+ }
532
541
  if (typeof value === 'number') {
533
542
  if (Number.isNaN(value)) return { __bf_special: 'nan' }
534
543
  if (value === Infinity) return { __bf_special: 'inf' }