@barefootjs/jinja 0.18.5 → 0.19.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.
@@ -187308,7 +187308,13 @@ import {
187308
187308
  prepareLoweringMatchers,
187309
187309
  queryHrefArgs,
187310
187310
  isValidHelperId,
187311
- sortComparatorFromArrow as sortComparatorFromArrow2
187311
+ sortComparatorFromArrow as sortComparatorFromArrow2,
187312
+ isDangerousInnerHtmlAttr,
187313
+ resolveDangerousInnerHtml,
187314
+ dangerousInnerHtmlMetacharViolation,
187315
+ dangerousInnerHtmlDiagnostic,
187316
+ resolveStaticLoopSource,
187317
+ collectLoopBoundNames
187312
187318
  } from "@barefootjs/jsx";
187313
187319
 
187314
187320
  // src/adapter/boolean-result.ts
@@ -187699,6 +187705,39 @@ function renderFlatMethod(recv, depth, emit) {
187699
187705
  return `bf.flat(${recv}, ${d})`;
187700
187706
  }
187701
187707
 
187708
+ // src/adapter/lib/static-value.ts
187709
+ function staticValueToJinja(value) {
187710
+ if (value === null || value === undefined)
187711
+ return "none";
187712
+ if (typeof value === "boolean")
187713
+ return value ? "true" : "false";
187714
+ if (typeof value === "number")
187715
+ return String(value);
187716
+ if (typeof value === "string")
187717
+ return `'${escapeJinjaSingleQuoted(value)}'`;
187718
+ if (Array.isArray(value)) {
187719
+ const items = [];
187720
+ for (const el of value) {
187721
+ const serialized = staticValueToJinja(el);
187722
+ if (serialized === null)
187723
+ return null;
187724
+ items.push(serialized);
187725
+ }
187726
+ return `[${items.join(", ")}]`;
187727
+ }
187728
+ if (typeof value === "object") {
187729
+ const entries = [];
187730
+ for (const [key, val] of Object.entries(value)) {
187731
+ const serialized = staticValueToJinja(val);
187732
+ if (serialized === null)
187733
+ return null;
187734
+ entries.push(`${jinjaHashKey(key)}: ${serialized}`);
187735
+ }
187736
+ return `{${entries.join(", ")}}`;
187737
+ }
187738
+ return null;
187739
+ }
187740
+
187702
187741
  // src/adapter/expr/emitters.ts
187703
187742
  import {
187704
187743
  groupBinaryOperand,
@@ -188244,7 +188283,7 @@ function collectBooleanTypedProps(ir) {
188244
188283
  return new Set(ir.metadata.propsParams.filter((prop) => prop.type?.primitive === "boolean" || prop.type?.raw === "boolean").map((prop) => prop.name));
188245
188284
  }
188246
188285
  function collectNullableOptionalProps(ir) {
188247
- 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));
188248
188287
  }
188249
188288
  function collectStringValueNames(ir) {
188250
188289
  const names = new Set;
@@ -188281,6 +188320,7 @@ class JinjaAdapter extends BaseAdapter {
188281
188320
  _searchParamsLocals = new Set;
188282
188321
  _loweringMatchers = [];
188283
188322
  localConstants = [];
188323
+ staticLoopSourceBoundNames = new Set;
188284
188324
  nullableOptionalProps = new Set;
188285
188325
  constructor(options = {}) {
188286
188326
  super();
@@ -188296,6 +188336,7 @@ class JinjaAdapter extends BaseAdapter {
188296
188336
  this.propsParams = ir.metadata.propsParams.map((p) => ({ name: p.name }));
188297
188337
  this.booleanTypedProps = collectBooleanTypedProps(ir);
188298
188338
  this.localConstants = ir.metadata.localConstants ?? [];
188339
+ this.staticLoopSourceBoundNames = collectLoopBoundNames(ir);
188299
188340
  this.nullableOptionalProps = collectNullableOptionalProps(ir);
188300
188341
  this.stringValueNames = collectStringValueNames(ir);
188301
188342
  this.moduleStringConsts = collectModuleStringConsts(ir.metadata.localConstants);
@@ -188417,7 +188458,8 @@ class JinjaAdapter extends BaseAdapter {
188417
188458
  renderElement(element) {
188418
188459
  const tag = element.tag;
188419
188460
  const attrs = this.renderAttributes(element);
188420
- const children = this.renderChildren(element.children);
188461
+ const dangerousHtml = this.renderDangerousInnerHtml(element);
188462
+ const children = dangerousHtml !== null ? dangerousHtml : this.renderChildren(element.children);
188421
188463
  let hydrationAttrs = "";
188422
188464
  if (element.needsScope) {
188423
188465
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
@@ -188452,6 +188494,22 @@ class JinjaAdapter extends BaseAdapter {
188452
188494
  }
188453
188495
  return `<${tag}${attrs}${hydrationAttrs}>${children}</${tag}>`;
188454
188496
  }
188497
+ renderDangerousInnerHtml(element) {
188498
+ const resolution = resolveDangerousInnerHtml(element);
188499
+ if (!resolution)
188500
+ return null;
188501
+ if (resolution.kind === "dynamic") {
188502
+ this.errors.push(dangerousInnerHtmlDiagnostic(resolution.expr, resolution.loc));
188503
+ return "";
188504
+ }
188505
+ const violation = dangerousInnerHtmlMetacharViolation(resolution.html, this.name);
188506
+ if (violation) {
188507
+ const attr = element.attrs.find(isDangerousInnerHtmlAttr);
188508
+ this.errors.push(dangerousInnerHtmlDiagnostic(`{ __html: ${JSON.stringify(resolution.html)} }`, attr.loc, violation));
188509
+ return "";
188510
+ }
188511
+ return resolution.html;
188512
+ }
188455
188513
  renderExpression(expr) {
188456
188514
  if (expr.clientOnly) {
188457
188515
  if (expr.slotId) {
@@ -188459,7 +188517,7 @@ class JinjaAdapter extends BaseAdapter {
188459
188517
  }
188460
188518
  return "";
188461
188519
  }
188462
- const jinjaExpr = `bf.string(${this.convertExpressionToJinja(expr.expr)})`;
188520
+ const jinjaExpr = `bf.string(${this.convertExpressionToJinja(expr.expr, expr.parsed)})`;
188463
188521
  if (expr.slotId) {
188464
188522
  return `{{ bf.text_start("${expr.slotId}") | safe }}{{ ${jinjaExpr} }}{{ bf.text_end() | safe }}`;
188465
188523
  }
@@ -188551,8 +188609,12 @@ ${whenTrue}
188551
188609
  }
188552
188610
  });
188553
188611
  }
188612
+ const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
188613
+ isNameShadowed: (name) => this.staticLoopSourceBoundNames.has(name)
188614
+ });
188615
+ const staticArray = staticItems !== null ? staticValueToJinja(staticItems) : null;
188554
188616
  const arrayName = loop.array.trim();
188555
- if (/^[A-Za-z_$][\w$]*$/.test(arrayName)) {
188617
+ if (staticArray === null && /^[A-Za-z_$][\w$]*$/.test(arrayName)) {
188556
188618
  const arrayConst = (this.localConstants ?? []).find((c) => c.name === arrayName);
188557
188619
  if (arrayConst && !arrayConst.isModule && this._resolveLiteralConst(arrayName) === null) {
188558
188620
  this.errors.push({
@@ -188566,7 +188628,7 @@ ${whenTrue}
188566
188628
  });
188567
188629
  }
188568
188630
  }
188569
- const rawArray = this.convertExpressionToJinja(loop.array);
188631
+ const rawArray = staticArray ?? this.convertExpressionToJinja(loop.array);
188570
188632
  let array = rawArray;
188571
188633
  if (loop.sortComparator) {
188572
188634
  const sort = loop.sortComparator;
@@ -188878,6 +188940,8 @@ ${name}="{{ bf.string(${val}) }}"
188878
188940
  for (const attr of element.attrs) {
188879
188941
  if (attr.clientOnly)
188880
188942
  continue;
188943
+ if (isDangerousInnerHtmlAttr(attr))
188944
+ continue;
188881
188945
  let attrName;
188882
188946
  if (attr.name === "className")
188883
188947
  attrName = "class";
@@ -189077,6 +189141,8 @@ Options:
189077
189141
  return isBooleanResultExpr(expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(expr);
189078
189142
  }
189079
189143
  _resolveLiteralConst(name) {
189144
+ if (this.staticLoopSourceBoundNames.has(name))
189145
+ return null;
189080
189146
  const c = (this.localConstants ?? []).find((lc) => lc.name === name);
189081
189147
  if (c?.value === undefined)
189082
189148
  return null;
@@ -189089,6 +189155,8 @@ Options:
189089
189155
  return null;
189090
189156
  }
189091
189157
  _resolveStaticRecordLiteral(objectName, key) {
189158
+ if (this.staticLoopSourceBoundNames.has(objectName))
189159
+ return null;
189092
189160
  const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants);
189093
189161
  if (!hit)
189094
189162
  return null;
@@ -168,6 +168,16 @@ export declare class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<J
168
168
  * const (`const sizeAttrs = size ? {…} : {}`) to its initializer text.
169
169
  */
170
170
  private localConstants;
171
+ /**
172
+ * Every name a `.map()`/`.filter()` loop callback binds as its item/index
173
+ * parameter anywhere in the component (#2208 fable review). A static
174
+ * loop-SOURCE name (e.g. a function-scope `const items = [...]`) must
175
+ * never resolve through `resolveStaticLoopSource` at a use site where a
176
+ * DIFFERENT, enclosing loop's own callback param shadows it — same
177
+ * shadowing hazard, and same coarse-but-safe mitigation, as #2212's
178
+ * `collectLoopBoundNames` use in `collectStringValueNames`.
179
+ */
180
+ private staticLoopSourceBoundNames;
171
181
  /**
172
182
  * Optional, no-default props that are `None` when the caller omits them.
173
183
  * Their bare-reference attribute emission is guarded with a Jinja
@@ -219,6 +229,13 @@ export declare class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<J
219
229
  private providerObjectLiteralJinja;
220
230
  emitAsync(node: IRAsync, _ctx: JinjaRenderCtx, _emit: EmitIRNode<JinjaRenderCtx>): string;
221
231
  renderElement(element: IRElement): string;
232
+ /**
233
+ * `dangerouslySetInnerHTML={{ __html: '...' }}` (#2207) — see the Blade
234
+ * adapter's identical helper for the full rationale. `null` means the
235
+ * attribute is absent (caller falls through to normal `renderChildren`);
236
+ * a non-`null` string (possibly `''`) replaces the children outright.
237
+ */
238
+ private renderDangerousInnerHtml;
222
239
  renderExpression(expr: IRExpression): string;
223
240
  renderConditional(cond: IRConditional): string;
224
241
  private renderNodeOrNull;
@@ -390,8 +407,32 @@ export declare class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<J
390
407
  * single-quoted string literal (`const totalPages = 5`, #1897
391
408
  * pagination) — function-scope consts never reach the per-render
392
409
  * context, so a bare reference would resolve to Undefined.
410
+ *
411
+ * The lookup is a flat name match with no notion of AST scope, so a
412
+ * name that any loop callback binds as its item/index param never
413
+ * inlines (#2221) — the occurrence may be the loop's own (shadowing)
414
+ * binding, and substituting the outer const's value there renders every
415
+ * iteration with the same hard-coded literal. Coarse (a genuinely
416
+ * non-shadowed same-named const elsewhere in the component also stops
417
+ * inlining, falling back to the bare identifier) but safe — the same
418
+ * trade-off as #2212's `collectLoopBoundNames` use in
419
+ * `collectStringValueNames`.
393
420
  */
394
421
  private _resolveLiteralConst;
422
+ /**
423
+ * Resolve `IDENT.key` where `IDENT` is a module-scope object-literal const
424
+ * (`variantClasses.ghost`, #1896/#1897) to the looked-up scalar.
425
+ *
426
+ * The lookup is a flat name match on `objectName` with no notion of AST
427
+ * scope, so an enclosing loop callback's own param of the same name
428
+ * (`.map((cfg) => <li>{cfg.x}</li>)` shadowing a module `const cfg = {…}`)
429
+ * still resolved to the OUTER const's member value at every iteration
430
+ * (#2237) — the sibling hazard to #2221's `_resolveLiteralConst`. Same
431
+ * coarse-but-safe `staticLoopSourceBoundNames` guard: any name a loop
432
+ * binds anywhere in the component never inlines, falling back to the bare
433
+ * `cfg['x']` member expression (which a Jinja for-loop binds correctly
434
+ * at the shadowed occurrences).
435
+ */
395
436
  private _resolveStaticRecordLiteral;
396
437
  private _resolveModuleStringConst;
397
438
  private _recordExprBF101;
@@ -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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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Serialize a compile-time-evaluated JS value (`@barefootjs/jsx`'s
3
+ * `evaluateStaticLiteral`/`resolveStaticLoopSource`, #2208) into a native
4
+ * Jinja2 literal. Used to inline a fully-static loop source (an inline
5
+ * array literal, or a function-scope local const with a static
6
+ * initializer) directly in a `{% for %}` header, rather than requiring a
7
+ * bound template variable.
8
+ *
9
+ * Returns `null` for a value this adapter can't represent as a literal
10
+ * (e.g. `undefined` reads for a missing object key are still representable
11
+ * as `none`, but anything else falls back to the caller's existing BF101
12
+ * refusal instead of guessing).
13
+ */
14
+ export declare function staticValueToJinja(value: unknown): string | null;
15
+ //# sourceMappingURL=static-value.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-value.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/static-value.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAwBhE"}
@@ -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
@@ -187308,7 +187308,13 @@ import {
187308
187308
  prepareLoweringMatchers,
187309
187309
  queryHrefArgs,
187310
187310
  isValidHelperId,
187311
- sortComparatorFromArrow as sortComparatorFromArrow2
187311
+ sortComparatorFromArrow as sortComparatorFromArrow2,
187312
+ isDangerousInnerHtmlAttr,
187313
+ resolveDangerousInnerHtml,
187314
+ dangerousInnerHtmlMetacharViolation,
187315
+ dangerousInnerHtmlDiagnostic,
187316
+ resolveStaticLoopSource,
187317
+ collectLoopBoundNames
187312
187318
  } from "@barefootjs/jsx";
187313
187319
 
187314
187320
  // src/adapter/boolean-result.ts
@@ -187699,6 +187705,39 @@ function renderFlatMethod(recv, depth, emit) {
187699
187705
  return `bf.flat(${recv}, ${d})`;
187700
187706
  }
187701
187707
 
187708
+ // src/adapter/lib/static-value.ts
187709
+ function staticValueToJinja(value) {
187710
+ if (value === null || value === undefined)
187711
+ return "none";
187712
+ if (typeof value === "boolean")
187713
+ return value ? "true" : "false";
187714
+ if (typeof value === "number")
187715
+ return String(value);
187716
+ if (typeof value === "string")
187717
+ return `'${escapeJinjaSingleQuoted(value)}'`;
187718
+ if (Array.isArray(value)) {
187719
+ const items = [];
187720
+ for (const el of value) {
187721
+ const serialized = staticValueToJinja(el);
187722
+ if (serialized === null)
187723
+ return null;
187724
+ items.push(serialized);
187725
+ }
187726
+ return `[${items.join(", ")}]`;
187727
+ }
187728
+ if (typeof value === "object") {
187729
+ const entries = [];
187730
+ for (const [key, val] of Object.entries(value)) {
187731
+ const serialized = staticValueToJinja(val);
187732
+ if (serialized === null)
187733
+ return null;
187734
+ entries.push(`${jinjaHashKey(key)}: ${serialized}`);
187735
+ }
187736
+ return `{${entries.join(", ")}}`;
187737
+ }
187738
+ return null;
187739
+ }
187740
+
187702
187741
  // src/adapter/expr/emitters.ts
187703
187742
  import {
187704
187743
  groupBinaryOperand,
@@ -188244,7 +188283,7 @@ function collectBooleanTypedProps(ir) {
188244
188283
  return new Set(ir.metadata.propsParams.filter((prop) => prop.type?.primitive === "boolean" || prop.type?.raw === "boolean").map((prop) => prop.name));
188245
188284
  }
188246
188285
  function collectNullableOptionalProps(ir) {
188247
- 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));
188248
188287
  }
188249
188288
  function collectStringValueNames(ir) {
188250
188289
  const names = new Set;
@@ -188281,6 +188320,7 @@ class JinjaAdapter extends BaseAdapter {
188281
188320
  _searchParamsLocals = new Set;
188282
188321
  _loweringMatchers = [];
188283
188322
  localConstants = [];
188323
+ staticLoopSourceBoundNames = new Set;
188284
188324
  nullableOptionalProps = new Set;
188285
188325
  constructor(options = {}) {
188286
188326
  super();
@@ -188296,6 +188336,7 @@ class JinjaAdapter extends BaseAdapter {
188296
188336
  this.propsParams = ir.metadata.propsParams.map((p) => ({ name: p.name }));
188297
188337
  this.booleanTypedProps = collectBooleanTypedProps(ir);
188298
188338
  this.localConstants = ir.metadata.localConstants ?? [];
188339
+ this.staticLoopSourceBoundNames = collectLoopBoundNames(ir);
188299
188340
  this.nullableOptionalProps = collectNullableOptionalProps(ir);
188300
188341
  this.stringValueNames = collectStringValueNames(ir);
188301
188342
  this.moduleStringConsts = collectModuleStringConsts(ir.metadata.localConstants);
@@ -188417,7 +188458,8 @@ class JinjaAdapter extends BaseAdapter {
188417
188458
  renderElement(element) {
188418
188459
  const tag = element.tag;
188419
188460
  const attrs = this.renderAttributes(element);
188420
- const children = this.renderChildren(element.children);
188461
+ const dangerousHtml = this.renderDangerousInnerHtml(element);
188462
+ const children = dangerousHtml !== null ? dangerousHtml : this.renderChildren(element.children);
188421
188463
  let hydrationAttrs = "";
188422
188464
  if (element.needsScope) {
188423
188465
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
@@ -188452,6 +188494,22 @@ class JinjaAdapter extends BaseAdapter {
188452
188494
  }
188453
188495
  return `<${tag}${attrs}${hydrationAttrs}>${children}</${tag}>`;
188454
188496
  }
188497
+ renderDangerousInnerHtml(element) {
188498
+ const resolution = resolveDangerousInnerHtml(element);
188499
+ if (!resolution)
188500
+ return null;
188501
+ if (resolution.kind === "dynamic") {
188502
+ this.errors.push(dangerousInnerHtmlDiagnostic(resolution.expr, resolution.loc));
188503
+ return "";
188504
+ }
188505
+ const violation = dangerousInnerHtmlMetacharViolation(resolution.html, this.name);
188506
+ if (violation) {
188507
+ const attr = element.attrs.find(isDangerousInnerHtmlAttr);
188508
+ this.errors.push(dangerousInnerHtmlDiagnostic(`{ __html: ${JSON.stringify(resolution.html)} }`, attr.loc, violation));
188509
+ return "";
188510
+ }
188511
+ return resolution.html;
188512
+ }
188455
188513
  renderExpression(expr) {
188456
188514
  if (expr.clientOnly) {
188457
188515
  if (expr.slotId) {
@@ -188459,7 +188517,7 @@ class JinjaAdapter extends BaseAdapter {
188459
188517
  }
188460
188518
  return "";
188461
188519
  }
188462
- const jinjaExpr = `bf.string(${this.convertExpressionToJinja(expr.expr)})`;
188520
+ const jinjaExpr = `bf.string(${this.convertExpressionToJinja(expr.expr, expr.parsed)})`;
188463
188521
  if (expr.slotId) {
188464
188522
  return `{{ bf.text_start("${expr.slotId}") | safe }}{{ ${jinjaExpr} }}{{ bf.text_end() | safe }}`;
188465
188523
  }
@@ -188551,8 +188609,12 @@ ${whenTrue}
188551
188609
  }
188552
188610
  });
188553
188611
  }
188612
+ const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
188613
+ isNameShadowed: (name) => this.staticLoopSourceBoundNames.has(name)
188614
+ });
188615
+ const staticArray = staticItems !== null ? staticValueToJinja(staticItems) : null;
188554
188616
  const arrayName = loop.array.trim();
188555
- if (/^[A-Za-z_$][\w$]*$/.test(arrayName)) {
188617
+ if (staticArray === null && /^[A-Za-z_$][\w$]*$/.test(arrayName)) {
188556
188618
  const arrayConst = (this.localConstants ?? []).find((c) => c.name === arrayName);
188557
188619
  if (arrayConst && !arrayConst.isModule && this._resolveLiteralConst(arrayName) === null) {
188558
188620
  this.errors.push({
@@ -188566,7 +188628,7 @@ ${whenTrue}
188566
188628
  });
188567
188629
  }
188568
188630
  }
188569
- const rawArray = this.convertExpressionToJinja(loop.array);
188631
+ const rawArray = staticArray ?? this.convertExpressionToJinja(loop.array);
188570
188632
  let array = rawArray;
188571
188633
  if (loop.sortComparator) {
188572
188634
  const sort = loop.sortComparator;
@@ -188878,6 +188940,8 @@ ${name}="{{ bf.string(${val}) }}"
188878
188940
  for (const attr of element.attrs) {
188879
188941
  if (attr.clientOnly)
188880
188942
  continue;
188943
+ if (isDangerousInnerHtmlAttr(attr))
188944
+ continue;
188881
188945
  let attrName;
188882
188946
  if (attr.name === "className")
188883
188947
  attrName = "class";
@@ -189077,6 +189141,8 @@ Options:
189077
189141
  return isBooleanResultExpr(expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(expr);
189078
189142
  }
189079
189143
  _resolveLiteralConst(name) {
189144
+ if (this.staticLoopSourceBoundNames.has(name))
189145
+ return null;
189080
189146
  const c = (this.localConstants ?? []).find((lc) => lc.name === name);
189081
189147
  if (c?.value === undefined)
189082
189148
  return null;
@@ -189089,6 +189155,8 @@ Options:
189089
189155
  return null;
189090
189156
  }
189091
189157
  _resolveStaticRecordLiteral(objectName, key) {
189158
+ if (this.staticLoopSourceBoundNames.has(objectName))
189159
+ return null;
189092
189160
  const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants);
189093
189161
  if (!hit)
189094
189162
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eA8F7B,CAAA"}
1
+ {"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eA6F7B,CAAA"}
package/dist/index.js CHANGED
@@ -187308,7 +187308,13 @@ import {
187308
187308
  prepareLoweringMatchers,
187309
187309
  queryHrefArgs,
187310
187310
  isValidHelperId,
187311
- sortComparatorFromArrow as sortComparatorFromArrow2
187311
+ sortComparatorFromArrow as sortComparatorFromArrow2,
187312
+ isDangerousInnerHtmlAttr,
187313
+ resolveDangerousInnerHtml,
187314
+ dangerousInnerHtmlMetacharViolation,
187315
+ dangerousInnerHtmlDiagnostic,
187316
+ resolveStaticLoopSource,
187317
+ collectLoopBoundNames
187312
187318
  } from "@barefootjs/jsx";
187313
187319
 
187314
187320
  // src/adapter/boolean-result.ts
@@ -187699,6 +187705,39 @@ function renderFlatMethod(recv, depth, emit) {
187699
187705
  return `bf.flat(${recv}, ${d})`;
187700
187706
  }
187701
187707
 
187708
+ // src/adapter/lib/static-value.ts
187709
+ function staticValueToJinja(value) {
187710
+ if (value === null || value === undefined)
187711
+ return "none";
187712
+ if (typeof value === "boolean")
187713
+ return value ? "true" : "false";
187714
+ if (typeof value === "number")
187715
+ return String(value);
187716
+ if (typeof value === "string")
187717
+ return `'${escapeJinjaSingleQuoted(value)}'`;
187718
+ if (Array.isArray(value)) {
187719
+ const items = [];
187720
+ for (const el of value) {
187721
+ const serialized = staticValueToJinja(el);
187722
+ if (serialized === null)
187723
+ return null;
187724
+ items.push(serialized);
187725
+ }
187726
+ return `[${items.join(", ")}]`;
187727
+ }
187728
+ if (typeof value === "object") {
187729
+ const entries = [];
187730
+ for (const [key, val] of Object.entries(value)) {
187731
+ const serialized = staticValueToJinja(val);
187732
+ if (serialized === null)
187733
+ return null;
187734
+ entries.push(`${jinjaHashKey(key)}: ${serialized}`);
187735
+ }
187736
+ return `{${entries.join(", ")}}`;
187737
+ }
187738
+ return null;
187739
+ }
187740
+
187702
187741
  // src/adapter/expr/emitters.ts
187703
187742
  import {
187704
187743
  groupBinaryOperand,
@@ -188244,7 +188283,7 @@ function collectBooleanTypedProps(ir) {
188244
188283
  return new Set(ir.metadata.propsParams.filter((prop) => prop.type?.primitive === "boolean" || prop.type?.raw === "boolean").map((prop) => prop.name));
188245
188284
  }
188246
188285
  function collectNullableOptionalProps(ir) {
188247
- 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));
188248
188287
  }
188249
188288
  function collectStringValueNames(ir) {
188250
188289
  const names = new Set;
@@ -188281,6 +188320,7 @@ class JinjaAdapter extends BaseAdapter {
188281
188320
  _searchParamsLocals = new Set;
188282
188321
  _loweringMatchers = [];
188283
188322
  localConstants = [];
188323
+ staticLoopSourceBoundNames = new Set;
188284
188324
  nullableOptionalProps = new Set;
188285
188325
  constructor(options = {}) {
188286
188326
  super();
@@ -188296,6 +188336,7 @@ class JinjaAdapter extends BaseAdapter {
188296
188336
  this.propsParams = ir.metadata.propsParams.map((p) => ({ name: p.name }));
188297
188337
  this.booleanTypedProps = collectBooleanTypedProps(ir);
188298
188338
  this.localConstants = ir.metadata.localConstants ?? [];
188339
+ this.staticLoopSourceBoundNames = collectLoopBoundNames(ir);
188299
188340
  this.nullableOptionalProps = collectNullableOptionalProps(ir);
188300
188341
  this.stringValueNames = collectStringValueNames(ir);
188301
188342
  this.moduleStringConsts = collectModuleStringConsts(ir.metadata.localConstants);
@@ -188417,7 +188458,8 @@ class JinjaAdapter extends BaseAdapter {
188417
188458
  renderElement(element) {
188418
188459
  const tag = element.tag;
188419
188460
  const attrs = this.renderAttributes(element);
188420
- const children = this.renderChildren(element.children);
188461
+ const dangerousHtml = this.renderDangerousInnerHtml(element);
188462
+ const children = dangerousHtml !== null ? dangerousHtml : this.renderChildren(element.children);
188421
188463
  let hydrationAttrs = "";
188422
188464
  if (element.needsScope) {
188423
188465
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
@@ -188452,6 +188494,22 @@ class JinjaAdapter extends BaseAdapter {
188452
188494
  }
188453
188495
  return `<${tag}${attrs}${hydrationAttrs}>${children}</${tag}>`;
188454
188496
  }
188497
+ renderDangerousInnerHtml(element) {
188498
+ const resolution = resolveDangerousInnerHtml(element);
188499
+ if (!resolution)
188500
+ return null;
188501
+ if (resolution.kind === "dynamic") {
188502
+ this.errors.push(dangerousInnerHtmlDiagnostic(resolution.expr, resolution.loc));
188503
+ return "";
188504
+ }
188505
+ const violation = dangerousInnerHtmlMetacharViolation(resolution.html, this.name);
188506
+ if (violation) {
188507
+ const attr = element.attrs.find(isDangerousInnerHtmlAttr);
188508
+ this.errors.push(dangerousInnerHtmlDiagnostic(`{ __html: ${JSON.stringify(resolution.html)} }`, attr.loc, violation));
188509
+ return "";
188510
+ }
188511
+ return resolution.html;
188512
+ }
188455
188513
  renderExpression(expr) {
188456
188514
  if (expr.clientOnly) {
188457
188515
  if (expr.slotId) {
@@ -188459,7 +188517,7 @@ class JinjaAdapter extends BaseAdapter {
188459
188517
  }
188460
188518
  return "";
188461
188519
  }
188462
- const jinjaExpr = `bf.string(${this.convertExpressionToJinja(expr.expr)})`;
188520
+ const jinjaExpr = `bf.string(${this.convertExpressionToJinja(expr.expr, expr.parsed)})`;
188463
188521
  if (expr.slotId) {
188464
188522
  return `{{ bf.text_start("${expr.slotId}") | safe }}{{ ${jinjaExpr} }}{{ bf.text_end() | safe }}`;
188465
188523
  }
@@ -188551,8 +188609,12 @@ ${whenTrue}
188551
188609
  }
188552
188610
  });
188553
188611
  }
188612
+ const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
188613
+ isNameShadowed: (name) => this.staticLoopSourceBoundNames.has(name)
188614
+ });
188615
+ const staticArray = staticItems !== null ? staticValueToJinja(staticItems) : null;
188554
188616
  const arrayName = loop.array.trim();
188555
- if (/^[A-Za-z_$][\w$]*$/.test(arrayName)) {
188617
+ if (staticArray === null && /^[A-Za-z_$][\w$]*$/.test(arrayName)) {
188556
188618
  const arrayConst = (this.localConstants ?? []).find((c) => c.name === arrayName);
188557
188619
  if (arrayConst && !arrayConst.isModule && this._resolveLiteralConst(arrayName) === null) {
188558
188620
  this.errors.push({
@@ -188566,7 +188628,7 @@ ${whenTrue}
188566
188628
  });
188567
188629
  }
188568
188630
  }
188569
- const rawArray = this.convertExpressionToJinja(loop.array);
188631
+ const rawArray = staticArray ?? this.convertExpressionToJinja(loop.array);
188570
188632
  let array = rawArray;
188571
188633
  if (loop.sortComparator) {
188572
188634
  const sort = loop.sortComparator;
@@ -188878,6 +188940,8 @@ ${name}="{{ bf.string(${val}) }}"
188878
188940
  for (const attr of element.attrs) {
188879
188941
  if (attr.clientOnly)
188880
188942
  continue;
188943
+ if (isDangerousInnerHtmlAttr(attr))
188944
+ continue;
188881
188945
  let attrName;
188882
188946
  if (attr.name === "className")
188883
188947
  attrName = "class";
@@ -189077,6 +189141,8 @@ Options:
189077
189141
  return isBooleanResultExpr(expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(expr);
189078
189142
  }
189079
189143
  _resolveLiteralConst(name) {
189144
+ if (this.staticLoopSourceBoundNames.has(name))
189145
+ return null;
189080
189146
  const c = (this.localConstants ?? []).find((lc) => lc.name === name);
189081
189147
  if (c?.value === undefined)
189082
189148
  return null;
@@ -189089,6 +189155,8 @@ Options:
189089
189155
  return null;
189090
189156
  }
189091
189157
  _resolveStaticRecordLiteral(objectName, key) {
189158
+ if (this.staticLoopSourceBoundNames.has(objectName))
189159
+ return null;
189092
189160
  const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants);
189093
189161
  if (!hit)
189094
189162
  return null;
@@ -189126,14 +189194,10 @@ Options:
189126
189194
  var jinjaAdapter = new JinjaAdapter;
189127
189195
  // src/conformance-pins.ts
189128
189196
  var conformancePins = {
189129
- "static-array-children": [{ code: "BF103", severity: "error" }],
189130
- "todo-app": [{ code: "BF103", severity: "error" }],
189131
- "todo-app-ssr": [{ code: "BF103", severity: "error" }],
189132
189197
  "static-array-from-props": [
189133
189198
  { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2087" }
189134
189199
  ],
189135
189200
  "static-array-from-props-with-component": [
189136
- { code: "BF103", severity: "error" },
189137
189201
  { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2087" }
189138
189202
  ],
189139
189203
  "filter-nested-callback-predicate": [
@@ -189142,8 +189206,7 @@ var conformancePins = {
189142
189206
  "filter-nested-find-predicate": [
189143
189207
  { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2038" }
189144
189208
  ],
189145
- "array-map-function-reference": [{ code: "BF101", severity: "error" }],
189146
- "dangerous-inner-html": [{ code: "BF101", severity: "error" }]
189209
+ "dangerous-inner-html-dynamic": [{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2215" }]
189147
189210
  };
189148
189211
  // src/render-divergences.ts
189149
189212
  var renderDivergences = {};
@@ -1 +1 @@
1
- {"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,iBAAsB,CAAA"}
1
+ {"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,iBAK/B,CAAA"}