@barefootjs/rust 0.19.0 → 0.19.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.
@@ -188924,10 +188924,11 @@ ${name}="{{ bf.string(${val}) }}"
188924
188924
  if (e.kind === "expr" && !isSupported(parseExpression2(e.expr)).supported)
188925
188925
  return null;
188926
188926
  }
188927
- return entries.map((e) => e.kind === "literal" ? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}` : `${this.escapeAttrText(e.cssKey)}:{{ bf.string(${this.convertExpressionToJinja(e.expr)}) }}`).join(";");
188928
- }
188929
- escapeAttrText(s) {
188930
- return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
188927
+ const args = entries.flatMap((e) => [
188928
+ JSON.stringify(e.cssKey),
188929
+ e.kind === "literal" ? JSON.stringify(e.value) : this.convertExpressionToJinja(e.expr)
188930
+ ]);
188931
+ return `{{ bf.style_object(${args.join(", ")}) }}`;
188931
188932
  }
188932
188933
  renderAttributes(element) {
188933
188934
  const parts = [];
@@ -327,15 +327,16 @@ export declare class MinijinjaAdapter extends BaseAdapter implements IRNodeEmitt
327
327
  */
328
328
  private readonly elementAttrEmitter;
329
329
  /**
330
- * Lower a `style={{ … }}` object literal to a CSS string with dynamic values
331
- * interpolated as Jinja expressions, e.g. `{ backgroundColor: color }` →
332
- * `background-color:{{ bf.string(color) }}`. Returns null when the shape is
333
- * unsupported or any value can't be lowered (caller falls through to
334
- * BF101). (#1322)
330
+ * Lower a `style={{ … }}` object literal to a `bf.style_object(...)` call,
331
+ * e.g. `{ backgroundColor: color }` → `{{ bf.style_object("background-color",
332
+ * color) }}`. `style_object` is the single oracle-matching sanitizer (ported
333
+ * from Hono's `hasUnsafeStyleValue`): it drops any key:value pair whose
334
+ * value could break out of a CSS declaration and HTML-escapes the rest,
335
+ * returning a safe string so the custom minijinja formatter doesn't
336
+ * re-escape it (#2261). Returns null when the shape is unsupported or any
337
+ * value can't be lowered (caller falls through to BF101). (#1322)
335
338
  */
336
339
  private tryLowerStyleObject;
337
- /** HTML-attribute escape for static text inlined into a `"..."` attribute. */
338
- private escapeAttrText;
339
340
  private renderAttributes;
340
341
  renderScopeMarker(_instanceIdExpr: string): string;
341
342
  renderSlotMarker(slotId: string): string;
@@ -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
@@ -188924,10 +188924,11 @@ ${name}="{{ bf.string(${val}) }}"
188924
188924
  if (e.kind === "expr" && !isSupported(parseExpression2(e.expr)).supported)
188925
188925
  return null;
188926
188926
  }
188927
- return entries.map((e) => e.kind === "literal" ? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}` : `${this.escapeAttrText(e.cssKey)}:{{ bf.string(${this.convertExpressionToJinja(e.expr)}) }}`).join(";");
188928
- }
188929
- escapeAttrText(s) {
188930
- return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
188927
+ const args = entries.flatMap((e) => [
188928
+ JSON.stringify(e.cssKey),
188929
+ e.kind === "literal" ? JSON.stringify(e.value) : this.convertExpressionToJinja(e.expr)
188930
+ ]);
188931
+ return `{{ bf.style_object(${args.join(", ")}) }}`;
188931
188932
  }
188932
188933
  renderAttributes(element) {
188933
188934
  const parts = [];
package/dist/index.js CHANGED
@@ -188924,10 +188924,11 @@ ${name}="{{ bf.string(${val}) }}"
188924
188924
  if (e.kind === "expr" && !isSupported(parseExpression2(e.expr)).supported)
188925
188925
  return null;
188926
188926
  }
188927
- return entries.map((e) => e.kind === "literal" ? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}` : `${this.escapeAttrText(e.cssKey)}:{{ bf.string(${this.convertExpressionToJinja(e.expr)}) }}`).join(";");
188928
- }
188929
- escapeAttrText(s) {
188930
- return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
188927
+ const args = entries.flatMap((e) => [
188928
+ JSON.stringify(e.cssKey),
188929
+ e.kind === "literal" ? JSON.stringify(e.value) : this.convertExpressionToJinja(e.expr)
188930
+ ]);
188931
+ return `{{ bf.style_object(${args.join(", ")}) }}`;
188931
188932
  }
188932
188933
  renderAttributes(element) {
188933
188934
  const parts = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/rust",
3
- "version": "0.19.0",
3
+ "version": "0.19.1",
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.0"
57
+ "@barefootjs/shared": "0.19.1"
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.0",
64
+ "@barefootjs/jsx": "0.19.1",
65
65
  "typescript": "^5.0.0"
66
66
  }
67
67
  }
@@ -158,6 +158,17 @@ fn char_len(s: &str) -> usize {
158
158
  s.chars().count()
159
159
  }
160
160
 
161
+ // JS `String.prototype.length` counts UTF-16 CODE UNITS, not Rust's
162
+ // codepoint-counting `chars().count()` (#2255, used by `char_len` above for
163
+ // the char-indexed slice/pad helpers). A codepoint outside the Basic
164
+ // Multilingual Plane (astral, U+10000-U+10FFFF — e.g. '👍') is a surrogate
165
+ // PAIR in UTF-16, so it counts as 2, not 1; '日本語' is 3 either way
166
+ // (BMP-only). Used only by `length` below — `char_len`'s callers need a
167
+ // codepoint OFFSET for slicing, not this count.
168
+ fn utf16_len(s: &str) -> usize {
169
+ s.chars().map(|c| if c as u32 > 0xFFFF { 2 } else { 1 }).sum()
170
+ }
171
+
161
172
  fn char_slice_from(s: &str, n: usize) -> String {
162
173
  s.chars().skip(n).collect()
163
174
  }
@@ -296,6 +307,80 @@ fn style_to_css(v: &JsValue) -> Option<String> {
296
307
  }
297
308
  }
298
309
 
310
+ /// Structural scan for characters that could break a value out of a CSS
311
+ /// declaration -- ported byte-for-byte from Hono's own `hasUnsafeStyleValue`
312
+ /// (`hono/jsx/utils.ts`), the ORACLE this adapter's dynamic `style={{...}}`
313
+ /// values must match (#2261). NOT real CSSOM property validation. Every
314
+ /// character this scan tests is ASCII, so scanning by byte agrees with
315
+ /// Hono's UTF-16-code-unit scan for every input -- a multibyte UTF-8
316
+ /// sequence has no byte in the ASCII range, so it can never spuriously
317
+ /// match one of these single-byte comparisons.
318
+ fn has_unsafe_style_value(value: &str) -> bool {
319
+ let bytes = value.as_bytes();
320
+ let mut quote: u8 = 0;
321
+ let mut block_stack: Vec<u8> = Vec::new();
322
+ let mut i = 0usize;
323
+ while i < bytes.len() {
324
+ let c = bytes[i];
325
+ if c == b'\\' {
326
+ if i == bytes.len() - 1 {
327
+ return true;
328
+ }
329
+ i += 1;
330
+ } else if quote != 0 {
331
+ if c == b'\n' || c == b'\x0c' || c == b'\r' {
332
+ return true;
333
+ }
334
+ if c == quote {
335
+ quote = 0;
336
+ }
337
+ } else if c == b'/' && i + 1 < bytes.len() && bytes[i + 1] == b'*' {
338
+ match value[i + 2..].find("*/") {
339
+ Some(end) => i = i + 2 + end + 1,
340
+ None => return true,
341
+ }
342
+ } else if c == b'"' || c == b'\'' {
343
+ quote = c;
344
+ } else if c == b'(' {
345
+ block_stack.push(b')');
346
+ } else if c == b'[' {
347
+ block_stack.push(b']');
348
+ } else if c == b'{' || c == b'}' {
349
+ return true;
350
+ } else if c == b')' || c == b']' {
351
+ if block_stack.last() != Some(&c) {
352
+ return true;
353
+ }
354
+ block_stack.pop();
355
+ } else if c == b';' && block_stack.is_empty() {
356
+ return true;
357
+ }
358
+ i += 1;
359
+ }
360
+ quote != 0 || !block_stack.is_empty()
361
+ }
362
+
363
+ /// Builds the CSS string for a `style={{...}}` JSX object-literal attribute
364
+ /// (#2261). `pairs` alternates CSS key (always compile-time-known), then
365
+ /// value. A value that fails `has_unsafe_style_value` (after JS-`String()`-
366
+ /// style stringification) is DROPPED -- the whole `key:value` pair is
367
+ /// omitted -- matching Hono's oracle exactly. The joined string is STILL
368
+ /// HTML-escaped (mirroring Hono's `escapeToBuffer`) since a structurally
369
+ /// "safe" value can still carry a literal `"`/`'`/`&`.
370
+ fn style_object(pairs: &[JsValue]) -> String {
371
+ let mut parts = Vec::new();
372
+ let mut i = 0;
373
+ while i + 1 < pairs.len() {
374
+ let key = js_string(&pairs[i]);
375
+ let value = js_string(&pairs[i + 1]);
376
+ if !has_unsafe_style_value(&value) {
377
+ parts.push(format!("{}:{}", escape_html_chars(&key), escape_html_chars(&value)));
378
+ }
379
+ i += 2;
380
+ }
381
+ parts.join(";")
382
+ }
383
+
299
384
  fn is_on_handler_skip(key: &str) -> bool {
300
385
  // Skip when key starts `on` and the third character is its own
301
386
  // uppercase form (matches `runtime.py`'s exact predicate, which is
@@ -1194,6 +1279,7 @@ impl Object for BfInstance {
1194
1279
  "uc" => Ok(MjValue::from(js_string(a(0)).to_uppercase())),
1195
1280
  "join" => Ok(MjValue::from(join(a(0), a(1)))),
1196
1281
  "length" => Ok(MjValue::from(length(a(0)))),
1282
+ "style_object" => Ok(safe(style_object(&js_args))),
1197
1283
  "index_of" => Ok(MjValue::from(array_index_of(a(0), a(1), false))),
1198
1284
  "last_index_of" => Ok(MjValue::from(array_index_of(a(0), a(1), true))),
1199
1285
  "at" => Ok(js_to_mj(&at(a(0), a(1)))),
@@ -1353,7 +1439,7 @@ pub fn length(recv: &JsValue) -> f64 {
1353
1439
  match recv {
1354
1440
  JsValue::Array(a) => a.len() as f64,
1355
1441
  JsValue::Object(_) => 0.0,
1356
- other => char_len(&js_string(other)) as f64,
1442
+ other => utf16_len(&js_string(other)) as f64,
1357
1443
  }
1358
1444
  }
1359
1445
 
@@ -56,19 +56,7 @@ runAdapterConformanceTests({
56
56
  // Same `/* @client */` keyed-map elision (data-table).
57
57
  'data-table',
58
58
  ]),
59
- skipDataPoints: new Set<string>([
60
- // #2255 — minijinja's length counts chars (codepoints); JS counts
61
- // UTF-16 code units, so a surrogate-pair character is 2 in JS, 1 here.
62
- 'string-length-text:astral',
63
- // #2260 — controlled boolean props: the SSR seed evaluates only the
64
- // static fallback of `props.X ?? internal()` chains.
65
- 'toggle:gen:pressed:true',
66
- 'switch:gen:checked:true',
67
- 'checkbox:gen:checked:true',
68
- // #2261 — invalid dynamic CSS value kept (escaped) where the oracle
69
- // drops the property.
70
- 'style-object-dynamic:gen:color:markup',
71
- ]),
59
+ skipDataPoints: new Set<string>(),
72
60
  onRenderError: (err, id) => {
73
61
  if (err instanceof RustNotAvailableError) {
74
62
  console.log(`Skipping [${id}]: ${err.message}`)
@@ -1453,11 +1453,14 @@ export class MinijinjaAdapter extends BaseAdapter implements IRNodeEmitter<Jinja
1453
1453
  }
1454
1454
 
1455
1455
  /**
1456
- * Lower a `style={{ … }}` object literal to a CSS string with dynamic values
1457
- * interpolated as Jinja expressions, e.g. `{ backgroundColor: color }` →
1458
- * `background-color:{{ bf.string(color) }}`. Returns null when the shape is
1459
- * unsupported or any value can't be lowered (caller falls through to
1460
- * BF101). (#1322)
1456
+ * Lower a `style={{ … }}` object literal to a `bf.style_object(...)` call,
1457
+ * e.g. `{ backgroundColor: color }` → `{{ bf.style_object("background-color",
1458
+ * color) }}`. `style_object` is the single oracle-matching sanitizer (ported
1459
+ * from Hono's `hasUnsafeStyleValue`): it drops any key:value pair whose
1460
+ * value could break out of a CSS declaration and HTML-escapes the rest,
1461
+ * returning a safe string so the custom minijinja formatter doesn't
1462
+ * re-escape it (#2261). Returns null when the shape is unsupported or any
1463
+ * value can't be lowered (caller falls through to BF101). (#1322)
1461
1464
  */
1462
1465
  private tryLowerStyleObject(expr: string): string | null {
1463
1466
  const entries = parseStyleObjectEntries(expr)
@@ -1465,27 +1468,11 @@ export class MinijinjaAdapter extends BaseAdapter implements IRNodeEmitter<Jinja
1465
1468
  for (const e of entries) {
1466
1469
  if (e.kind === 'expr' && !isSupported(parseExpression(e.expr)).supported) return null
1467
1470
  }
1468
- // The static CSS key + literal value are inlined into a double-quoted
1469
- // `style="..."` attribute as raw template text, so HTML-attr escape them
1470
- // (a value like `'"'` would otherwise break the attribute / inject
1471
- // markup). The dynamic arm's `{{ … }}` is HTML-escaped by Jinja.
1472
- return entries
1473
- .map(e =>
1474
- e.kind === 'literal'
1475
- ? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}`
1476
- : `${this.escapeAttrText(e.cssKey)}:{{ bf.string(${this.convertExpressionToJinja(e.expr)}) }}`,
1477
- )
1478
- .join(';')
1479
- }
1480
-
1481
- /** HTML-attribute escape for static text inlined into a `"..."` attribute. */
1482
- private escapeAttrText(s: string): string {
1483
- return s
1484
- .replace(/&/g, '&amp;')
1485
- .replace(/"/g, '&quot;')
1486
- .replace(/'/g, '&#39;')
1487
- .replace(/</g, '&lt;')
1488
- .replace(/>/g, '&gt;')
1471
+ const args = entries.flatMap(e => [
1472
+ JSON.stringify(e.cssKey),
1473
+ e.kind === 'literal' ? JSON.stringify(e.value) : this.convertExpressionToJinja(e.expr),
1474
+ ])
1475
+ return `{{ bf.style_object(${args.join(', ')}) }}`
1489
1476
  }
1490
1477
 
1491
1478
  private renderAttributes(element: IRElement): string {