@barefootjs/jinja 0.18.7 → 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.
@@ -188283,7 +188283,7 @@ function collectBooleanTypedProps(ir) {
188283
188283
  return new Set(ir.metadata.propsParams.filter((prop) => prop.type?.primitive === "boolean" || prop.type?.raw === "boolean").map((prop) => prop.name));
188284
188284
  }
188285
188285
  function collectNullableOptionalProps(ir) {
188286
- return new Set(ir.metadata.propsParams.filter((p) => p.defaultValue === undefined && !p.isRest && p.type?.kind !== "primitive").map((p) => p.name));
188286
+ return new Set(ir.metadata.propsParams.filter((p) => p.defaultValue === undefined && !p.isRest && (p.type?.kind !== "primitive" || p.optional)).map((p) => p.name));
188287
188287
  }
188288
188288
  function collectStringValueNames(ir) {
188289
188289
  const names = new Set;
@@ -188930,10 +188930,11 @@ ${name}="{{ bf.string(${val}) }}"
188930
188930
  if (e.kind === "expr" && !isSupported(parseExpression2(e.expr)).supported)
188931
188931
  return null;
188932
188932
  }
188933
- 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(";");
188934
- }
188935
- escapeAttrText(s) {
188936
- return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
188933
+ const args = entries.flatMap((e) => [
188934
+ JSON.stringify(e.cssKey),
188935
+ e.kind === "literal" ? JSON.stringify(e.value) : this.convertExpressionToJinja(e.expr)
188936
+ ]);
188937
+ return `{{ bf.style_object(${args.join(", ")}) }}`;
188937
188938
  }
188938
188939
  renderAttributes(element) {
188939
188940
  const parts = [];
@@ -300,15 +300,16 @@ export declare class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<J
300
300
  */
301
301
  private readonly elementAttrEmitter;
302
302
  /**
303
- * Lower a `style={{ … }}` object literal to a CSS string with dynamic values
304
- * interpolated as Jinja expressions, e.g. `{ backgroundColor: color }` →
305
- * `background-color:{{ bf.string(color) }}`. Returns null when the shape is
306
- * unsupported or any value can't be lowered (caller falls through to
307
- * BF101). (#1322)
303
+ * Lower a `style={{ … }}` object literal to a `bf.style_object(...)` call,
304
+ * e.g. `{ backgroundColor: color }` → `{{ bf.style_object("background-color",
305
+ * color) }}`. `style_object` is the single oracle-matching sanitizer (ported
306
+ * from Hono's `hasUnsafeStyleValue`): it drops any key:value pair whose
307
+ * value could break out of a CSS declaration and HTML-escapes the rest,
308
+ * returning a `Markup` so Jinja's autoescape doesn't double-escape it (#2261).
309
+ * Returns null when the shape is unsupported or any value can't be lowered
310
+ * (caller falls through to BF101). (#1322)
308
311
  */
309
312
  private tryLowerStyleObject;
310
- /** HTML-attribute escape for static text inlined into a `"..."` attribute. */
311
- private escapeAttrText;
312
313
  private renderAttributes;
313
314
  renderScopeMarker(_instanceIdExpr: string): string;
314
315
  renderSlotMarker(slotId: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"jinja-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/jinja-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jinja-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/jinja-adapter.ts"],"names":[],"mappings":"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"}
@@ -14,8 +14,9 @@ import type { ComponentIR } from '@barefootjs/jsx';
14
14
  */
15
15
  export declare function collectBooleanTypedProps(ir: ComponentIR): Set<string>;
16
16
  /**
17
- * Bare references to optional, no-default, non-primitive props (e.g.
18
- * textarea's `rows`) are `None` when omitted → guarded with
17
+ * Bare references to presence-uncertain no-default props (non-primitive
18
+ * typed OR declared optional, #2259 — e.g. textarea's `rows`) are
19
+ * `None` when omitted → guarded with
19
20
  * `is defined and is not none` in `emitExpression`. See the
20
21
  * `nullableOptionalProps` field docstring in `jinja-adapter.ts`.
21
22
  */
@@ -1 +1 @@
1
- {"version":3,"file":"prop-classes.d.ts","sourceRoot":"","sources":["../../../src/adapter/props/prop-classes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAGlD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAMrE;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAWzE;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAWpE"}
1
+ {"version":3,"file":"prop-classes.d.ts","sourceRoot":"","sources":["../../../src/adapter/props/prop-classes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAGlD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAMrE;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAWzE;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAWpE"}
package/dist/build.js CHANGED
@@ -188283,7 +188283,7 @@ function collectBooleanTypedProps(ir) {
188283
188283
  return new Set(ir.metadata.propsParams.filter((prop) => prop.type?.primitive === "boolean" || prop.type?.raw === "boolean").map((prop) => prop.name));
188284
188284
  }
188285
188285
  function collectNullableOptionalProps(ir) {
188286
- return new Set(ir.metadata.propsParams.filter((p) => p.defaultValue === undefined && !p.isRest && p.type?.kind !== "primitive").map((p) => p.name));
188286
+ return new Set(ir.metadata.propsParams.filter((p) => p.defaultValue === undefined && !p.isRest && (p.type?.kind !== "primitive" || p.optional)).map((p) => p.name));
188287
188287
  }
188288
188288
  function collectStringValueNames(ir) {
188289
188289
  const names = new Set;
@@ -188930,10 +188930,11 @@ ${name}="{{ bf.string(${val}) }}"
188930
188930
  if (e.kind === "expr" && !isSupported(parseExpression2(e.expr)).supported)
188931
188931
  return null;
188932
188932
  }
188933
- 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(";");
188934
- }
188935
- escapeAttrText(s) {
188936
- return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
188933
+ const args = entries.flatMap((e) => [
188934
+ JSON.stringify(e.cssKey),
188935
+ e.kind === "literal" ? JSON.stringify(e.value) : this.convertExpressionToJinja(e.expr)
188936
+ ]);
188937
+ return `{{ bf.style_object(${args.join(", ")}) }}`;
188937
188938
  }
188938
188939
  renderAttributes(element) {
188939
188940
  const parts = [];
package/dist/index.js CHANGED
@@ -188283,7 +188283,7 @@ function collectBooleanTypedProps(ir) {
188283
188283
  return new Set(ir.metadata.propsParams.filter((prop) => prop.type?.primitive === "boolean" || prop.type?.raw === "boolean").map((prop) => prop.name));
188284
188284
  }
188285
188285
  function collectNullableOptionalProps(ir) {
188286
- return new Set(ir.metadata.propsParams.filter((p) => p.defaultValue === undefined && !p.isRest && p.type?.kind !== "primitive").map((p) => p.name));
188286
+ return new Set(ir.metadata.propsParams.filter((p) => p.defaultValue === undefined && !p.isRest && (p.type?.kind !== "primitive" || p.optional)).map((p) => p.name));
188287
188287
  }
188288
188288
  function collectStringValueNames(ir) {
188289
188289
  const names = new Set;
@@ -188930,10 +188930,11 @@ ${name}="{{ bf.string(${val}) }}"
188930
188930
  if (e.kind === "expr" && !isSupported(parseExpression2(e.expr)).supported)
188931
188931
  return null;
188932
188932
  }
188933
- 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(";");
188934
- }
188935
- escapeAttrText(s) {
188936
- return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
188933
+ const args = entries.flatMap((e) => [
188934
+ JSON.stringify(e.cssKey),
188935
+ e.kind === "literal" ? JSON.stringify(e.value) : this.convertExpressionToJinja(e.expr)
188936
+ ]);
188937
+ return `{{ bf.style_object(${args.join(", ")}) }}`;
188937
188938
  }
188938
188939
  renderAttributes(element) {
188939
188940
  const parts = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/jinja",
3
- "version": "0.18.7",
3
+ "version": "0.19.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.18.7"
56
+ "@barefootjs/shared": "0.19.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.18.7",
63
+ "@barefootjs/jsx": "0.19.1",
64
64
  "typescript": "^5.0.0"
65
65
  }
66
66
  }
@@ -76,6 +76,8 @@ import re
76
76
  import uuid
77
77
  from typing import Any, Callable, Optional
78
78
 
79
+ from markupsafe import Markup
80
+
79
81
  from . import evaluator as _evaluator
80
82
  from .evaluator import looks_like_number, parse_number_literal
81
83
  from .search_params import SearchParams
@@ -360,6 +362,55 @@ def _html_escape(value: Any) -> str:
360
362
  return s
361
363
 
362
364
 
365
+ def _has_unsafe_style_value(value: str) -> bool:
366
+ """Mirrors Hono's own CSS-injection guard (`hono/jsx/utils.ts`'s
367
+ `hasUnsafeStyleValue` -- the ORACLE a dynamic `style={{...}}` value
368
+ must match, #2261): a hand-rolled structural scan for characters that
369
+ could break out of a CSS declaration, NOT real CSSOM property
370
+ validation. Ported character-for-character (codepoint comparisons,
371
+ consistently, throughout -- every tested character is ASCII, so a
372
+ multibyte codepoint can never spuriously match one of these
373
+ single-character comparisons regardless of scan unit). Skips the
374
+ reference implementation's regex fast-path (a pure optimization -- the
375
+ scan below already returns `False` promptly for a clean value)."""
376
+ quote = ""
377
+ block_stack: list[str] = []
378
+ i = 0
379
+ length = len(value)
380
+ while i < length:
381
+ c = value[i]
382
+ if c == "\\":
383
+ if i == length - 1:
384
+ return True
385
+ i += 1
386
+ elif quote:
387
+ if c in ("\n", "\f", "\r"):
388
+ return True
389
+ if c == quote:
390
+ quote = ""
391
+ elif c == "/" and i + 1 < length and value[i + 1] == "*":
392
+ end = value.find("*/", i + 2)
393
+ if end == -1:
394
+ return True
395
+ i = end + 1
396
+ elif c in ('"', "'"):
397
+ quote = c
398
+ elif c == "(":
399
+ block_stack.append(")")
400
+ elif c == "[":
401
+ block_stack.append("]")
402
+ elif c in ("{", "}"):
403
+ return True
404
+ elif c in (")", "]"):
405
+ if not block_stack or block_stack[-1] != c:
406
+ return True
407
+ block_stack.pop()
408
+ elif c == ";" and not block_stack:
409
+ return True
410
+ i += 1
411
+ return bool(quote) or bool(block_stack)
412
+
413
+
363
414
  def _style_to_css(value: Any) -> Optional[str]:
364
415
  if value is None:
365
416
  return None
@@ -912,6 +963,29 @@ class BarefootJS:
912
963
  return i
913
964
  return -1
914
965
 
966
+ def style_object(self, *pairs: Any) -> Markup:
967
+ """Builds the CSS string for a `style={{...}}` JSX object-literal
968
+ attribute (#2261) -- `pairs` alternates CSS key (always a
969
+ compile-time-known literal), then value. A value that fails
970
+ `_has_unsafe_style_value` (after JS-`String()`-style
971
+ stringification) is DROPPED -- the whole `key:value` pair is
972
+ omitted -- matching Hono's oracle behavior exactly. The final
973
+ joined string is STILL HTML-escaped (mirroring Hono's own
974
+ `escapeToBuffer` call on its accumulated style string) -- a "safe"
975
+ value can still carry a literal `"`/`'`/`&` (e.g. a BALANCED-quote
976
+ CSS string value like `"hello"` passes the structural scan) that
977
+ would otherwise break out of the double-quoted `style="..."`
978
+ attribute. Returns `Markup` so Jinja's autoescape treats the
979
+ (already-escaped) result as safe instead of double-escaping it."""
980
+ parts = []
981
+ for i in range(0, len(pairs) - 1, 2):
982
+ key, value = pairs[i], pairs[i + 1]
983
+ v = js_string(value)
984
+ if _has_unsafe_style_value(v):
985
+ continue
986
+ parts.append(f"{_html_escape(key)}:{_html_escape(v)}")
987
+ return Markup(";".join(parts))
988
+
915
989
  def lc(self, s: Any) -> str:
916
990
  return js_string(s).lower()
917
991
 
@@ -929,7 +1003,12 @@ class BarefootJS:
929
1003
  return len(recv)
930
1004
  if isinstance(recv, dict):
931
1005
  return 0
932
- return len(js_string(recv))
1006
+ # JS `String.prototype.length` counts UTF-16 CODE UNITS, not
1007
+ # Python's codepoint-counting `len(str)` (#2255). A codepoint
1008
+ # outside the Basic Multilingual Plane (astral, U+10000-U+10FFFF —
1009
+ # e.g. '👍') is a surrogate PAIR in UTF-16, so it counts as 2, not
1010
+ # 1; '日本語' is 3 either way (BMP-only).
1011
+ return sum(2 if ord(c) > 0xFFFF else 1 for c in js_string(recv))
933
1012
 
934
1013
  def index_of(self, recv: Any, elem: Any) -> int:
935
1014
  return _array_index_of(recv, elem, reverse=False)
@@ -20,7 +20,7 @@ classifiers = [
20
20
  "Programming Language :: Python :: 3.11",
21
21
  "Programming Language :: Python :: 3.12",
22
22
  ]
23
- dependencies = ["jinja2>=3.1"]
23
+ dependencies = ["jinja2>=3.1", "markupsafe>=2.0"]
24
24
 
25
25
  [project.urls]
26
26
  Homepage = "https://github.com/piconic-ai/barefootjs"
@@ -51,6 +51,7 @@ runAdapterConformanceTests({
51
51
  // Same `/* @client */` keyed-map elision (data-table).
52
52
  'data-table',
53
53
  ]),
54
+ skipDataPoints: new Set<string>(),
54
55
  onRenderError: (err, id) => {
55
56
  if (err instanceof PythonNotAvailableError) {
56
57
  console.log(`Skipping [${id}]: ${err.message}`)
@@ -1404,11 +1404,14 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
1404
1404
  }
1405
1405
 
1406
1406
  /**
1407
- * Lower a `style={{ … }}` object literal to a CSS string with dynamic values
1408
- * interpolated as Jinja expressions, e.g. `{ backgroundColor: color }` →
1409
- * `background-color:{{ bf.string(color) }}`. Returns null when the shape is
1410
- * unsupported or any value can't be lowered (caller falls through to
1411
- * BF101). (#1322)
1407
+ * Lower a `style={{ … }}` object literal to a `bf.style_object(...)` call,
1408
+ * e.g. `{ backgroundColor: color }` → `{{ bf.style_object("background-color",
1409
+ * color) }}`. `style_object` is the single oracle-matching sanitizer (ported
1410
+ * from Hono's `hasUnsafeStyleValue`): it drops any key:value pair whose
1411
+ * value could break out of a CSS declaration and HTML-escapes the rest,
1412
+ * returning a `Markup` so Jinja's autoescape doesn't double-escape it (#2261).
1413
+ * Returns null when the shape is unsupported or any value can't be lowered
1414
+ * (caller falls through to BF101). (#1322)
1412
1415
  */
1413
1416
  private tryLowerStyleObject(expr: string): string | null {
1414
1417
  const entries = parseStyleObjectEntries(expr)
@@ -1416,27 +1419,11 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
1416
1419
  for (const e of entries) {
1417
1420
  if (e.kind === 'expr' && !isSupported(parseExpression(e.expr)).supported) return null
1418
1421
  }
1419
- // The static CSS key + literal value are inlined into a double-quoted
1420
- // `style="..."` attribute as raw template text, so HTML-attr escape them
1421
- // (a value like `'"'` would otherwise break the attribute / inject
1422
- // markup). The dynamic arm's `{{ … }}` is HTML-escaped by Jinja.
1423
- return entries
1424
- .map(e =>
1425
- e.kind === 'literal'
1426
- ? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}`
1427
- : `${this.escapeAttrText(e.cssKey)}:{{ bf.string(${this.convertExpressionToJinja(e.expr)}) }}`,
1428
- )
1429
- .join(';')
1430
- }
1431
-
1432
- /** HTML-attribute escape for static text inlined into a `"..."` attribute. */
1433
- private escapeAttrText(s: string): string {
1434
- return s
1435
- .replace(/&/g, '&amp;')
1436
- .replace(/"/g, '&quot;')
1437
- .replace(/'/g, '&#39;')
1438
- .replace(/</g, '&lt;')
1439
- .replace(/>/g, '&gt;')
1422
+ const args = entries.flatMap(e => [
1423
+ JSON.stringify(e.cssKey),
1424
+ e.kind === 'literal' ? JSON.stringify(e.value) : this.convertExpressionToJinja(e.expr),
1425
+ ])
1426
+ return `{{ bf.style_object(${args.join(', ')}) }}`
1440
1427
  }
1441
1428
 
1442
1429
  private renderAttributes(element: IRElement): string {
@@ -24,8 +24,9 @@ export function collectBooleanTypedProps(ir: ComponentIR): Set<string> {
24
24
  }
25
25
 
26
26
  /**
27
- * Bare references to optional, no-default, non-primitive props (e.g.
28
- * textarea's `rows`) are `None` when omitted → guarded with
27
+ * Bare references to presence-uncertain no-default props (non-primitive
28
+ * typed OR declared optional, #2259 — e.g. textarea's `rows`) are
29
+ * `None` when omitted → guarded with
29
30
  * `is defined and is not none` in `emitExpression`. See the
30
31
  * `nullableOptionalProps` field docstring in `jinja-adapter.ts`.
31
32
  */
@@ -36,7 +37,7 @@ export function collectNullableOptionalProps(ir: ComponentIR): Set<string> {
36
37
  p =>
37
38
  p.defaultValue === undefined &&
38
39
  !p.isRest &&
39
- p.type?.kind !== 'primitive',
40
+ (p.type?.kind !== 'primitive' || p.optional),
40
41
  )
41
42
  .map(p => p.name),
42
43
  )