@barefootjs/jinja 0.33.1 → 0.33.3

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.
@@ -187377,7 +187377,7 @@ function isAriaBooleanAttr(name) {
187377
187377
  }
187378
187378
 
187379
187379
  // src/adapter/jinja-adapter.ts
187380
- import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
187380
+ import { BF_SLOT, BF_COND, BF_REGION, escapeHtml, resolveJsxChildrenProp } from "@barefootjs/shared";
187381
187381
 
187382
187382
  // src/adapter/lib/constants.ts
187383
187383
  var JINJA_TEMPLATE_PRIMITIVES = {
@@ -187452,62 +187452,6 @@ function jinjaAccessorFromSegments(base, segments) {
187452
187452
  return acc;
187453
187453
  }
187454
187454
 
187455
- // src/adapter/lib/ir-scope.ts
187456
- var NON_VAR_TOKENS = new Set([
187457
- "bf",
187458
- "if",
187459
- "else",
187460
- "is",
187461
- "not",
187462
- "and",
187463
- "or",
187464
- "in",
187465
- "none",
187466
- "true",
187467
- "false",
187468
- "defined"
187469
- ]);
187470
- function extractTopLevelIdentifiers(jinjaExpr) {
187471
- const stripped = jinjaExpr.replace(/'(?:\\.|[^'\\])*'/g, " ");
187472
- const out = [];
187473
- for (const m of stripped.matchAll(/(?<!\.)\b([A-Za-z_]\w*)\b/g)) {
187474
- if (!NON_VAR_TOKENS.has(m[1]))
187475
- out.push(m[1]);
187476
- }
187477
- return out;
187478
- }
187479
- function resolveJsxChildrenProp(props) {
187480
- const prop = props.find((p) => p.name === "children");
187481
- if (!prop)
187482
- return [];
187483
- if (prop.value.kind !== "jsx-children")
187484
- return [];
187485
- return prop.value.children;
187486
- }
187487
- function collectRootScopeNodes(node) {
187488
- const out = new Set;
187489
- const visit = (n) => {
187490
- if (!n)
187491
- return;
187492
- if (n.type === "element") {
187493
- out.add(n);
187494
- return;
187495
- }
187496
- if (n.type === "if-statement") {
187497
- const s = n;
187498
- visit(s.consequent);
187499
- visit(s.alternate);
187500
- return;
187501
- }
187502
- if (n.type === "fragment") {
187503
- for (const c of n.children)
187504
- visit(c);
187505
- }
187506
- };
187507
- visit(node);
187508
- return out;
187509
- }
187510
-
187511
187455
  // src/adapter/expr/array-method.ts
187512
187456
  import {
187513
187457
  serializeParsedExpr,
@@ -188237,6 +188181,33 @@ import {
188237
188181
  computeSsrSeedPlan,
188238
188182
  materializeGetterCalls
188239
188183
  } from "@barefootjs/jsx";
188184
+
188185
+ // src/adapter/lib/ir-scope.ts
188186
+ var NON_VAR_TOKENS = new Set([
188187
+ "bf",
188188
+ "if",
188189
+ "else",
188190
+ "is",
188191
+ "not",
188192
+ "and",
188193
+ "or",
188194
+ "in",
188195
+ "none",
188196
+ "true",
188197
+ "false",
188198
+ "defined"
188199
+ ]);
188200
+ function extractTopLevelIdentifiers(jinjaExpr) {
188201
+ const stripped = jinjaExpr.replace(/'(?:\\.|[^'\\])*'/g, " ");
188202
+ const out = [];
188203
+ for (const m of stripped.matchAll(/(?<!\.)\b([A-Za-z_]\w*)\b/g)) {
188204
+ if (!NON_VAR_TOKENS.has(m[1]))
188205
+ out.push(m[1]);
188206
+ }
188207
+ return out;
188208
+ }
188209
+
188210
+ // src/adapter/memo/seed.ts
188240
188211
  function contextDefaultJinja(c) {
188241
188212
  const d = c.defaultValue;
188242
188213
  if (d === null || d === undefined)
@@ -188318,11 +188289,9 @@ class JinjaAdapter extends BaseAdapter {
188318
188289
  templatesPerComponent = true;
188319
188290
  templatePrimitives = JINJA_PRIMITIVE_EMIT_MAP;
188320
188291
  componentName = "";
188321
- rootScopeNodes = new Set;
188322
188292
  options;
188323
188293
  errors = [];
188324
188294
  inLoop = false;
188325
- currentLoopKeyDepth = 0;
188326
188295
  propsObjectName = null;
188327
188296
  propsParams = [];
188328
188297
  booleanTypedProps = new Set;
@@ -188358,7 +188327,6 @@ class JinjaAdapter extends BaseAdapter {
188358
188327
  if (!options?.siblingTemplatesRegistered) {
188359
188328
  this.errors.push(...collectImportedLoopChildComponentErrors(ir, this.componentName));
188360
188329
  }
188361
- this.rootScopeNodes = collectRootScopeNodes(ir.root);
188362
188330
  const templateBody = ir.root.type === "if-statement" ? this.renderIfStatement(ir.root) : this.renderNode(ir.root);
188363
188331
  const scriptReg = options?.skipScriptRegistration ? "" : this.generateScriptRegistrations(ir, options?.scriptBaseName, options?.scriptAssets, options?.preloadAssets);
188364
188332
  const ctxSeed = generateContextConsumerSeed(ir);
@@ -188491,8 +188459,14 @@ class JinjaAdapter extends BaseAdapter {
188491
188459
  if (element.needsScope) {
188492
188460
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
188493
188461
  }
188494
- if (this.rootScopeNodes.has(element) && element.needsScope) {
188495
- hydrationAttrs += ` {{ bf.data_key_attr() | safe }}`;
188462
+ if (element.keyAttr) {
188463
+ if (element.keyAttr.value !== undefined) {
188464
+ const lowered = emitAttrValue({ kind: "expression", expr: element.keyAttr.value }, this.elementAttrEmitter, element.keyAttr.name);
188465
+ if (lowered)
188466
+ hydrationAttrs += ` ${lowered}`;
188467
+ } else {
188468
+ hydrationAttrs += ` {{ bf.data_key_attr() | safe }}`;
188469
+ }
188496
188470
  }
188497
188471
  if (element.slotId) {
188498
188472
  hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`;
@@ -188695,13 +188669,10 @@ ${whenTrue}
188695
188669
  }
188696
188670
  const prevInLoop = this.inLoop;
188697
188671
  this.inLoop = true;
188698
- const prevLoopKeyDepth = this.currentLoopKeyDepth;
188699
- this.currentLoopKeyDepth = loop.depth;
188700
188672
  const prevScope = this.scope;
188701
188673
  this.scope = prevScope.enterLoopRow(loop);
188702
188674
  const preambleLines = (loop.preamble?.declarations ?? []).map((d) => `{% set ${jinjaIdent(d.name)} = ${this.convertExpressionToJinja(d.raw, d.valueParsed)} %}`);
188703
188675
  const childrenUnderLoop = this.renderChildren(loop.children);
188704
- this.currentLoopKeyDepth = prevLoopKeyDepth;
188705
188676
  this.inLoop = prevInLoop;
188706
188677
  const bodyChildren = loop.bodyIsItemConditional && loop.key ? `{{ bf.comment("loop-i:" ~ bf.string(${this.convertExpressionToJinja(loop.key)})) | safe }}
188707
188678
  ${childrenUnderLoop}` : childrenUnderLoop;
@@ -188976,14 +188947,9 @@ ${name}="{{ bf.string(${val}) }}"
188976
188947
  continue;
188977
188948
  if (isDangerousInnerHtmlAttr(attr))
188978
188949
  continue;
188979
- let attrName;
188980
- if (attr.name === "className")
188981
- attrName = "class";
188982
- else if (attr.name === "key") {
188983
- const depth = this.currentLoopKeyDepth;
188984
- attrName = depth > 0 ? `data-key-${depth}` : "data-key";
188985
- } else
188986
- attrName = attr.name;
188950
+ if (attr.name === "key")
188951
+ continue;
188952
+ const attrName = attr.name === "className" ? "class" : attr.name;
188987
188953
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
188988
188954
  if (lowered)
188989
188955
  parts.push(lowered);
@@ -104,22 +104,9 @@ export declare class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<J
104
104
  */
105
105
  templatePrimitives: TemplatePrimitiveRegistry;
106
106
  private componentName;
107
- /** Component root scope element(s) — each carries `data-key` for a keyed loop
108
- * item (set by the child renderer from the JSX `key` prop). A plain element
109
- * root is one node; an `if-statement` (early-return) root contributes the
110
- * top element of every branch. */
111
- private rootScopeNodes;
112
107
  private options;
113
108
  private errors;
114
109
  private inLoop;
115
- /**
116
- * `IRLoop.depth` of the loop currently being rendered (save/restore
117
- * around `renderChildren(loop.children)`, mirroring `inLoop` above).
118
- * `renderAttributes` reads this to derive the `key` → `data-key`/
119
- * `data-key-N` suffix — the depth is IR-computed (jsx-to-ir.ts), not
120
- * re-derived here (#2168 nested-loop-outer-binding).
121
- */
122
- private currentLoopKeyDepth;
123
110
  /**
124
111
  * SolidJS-style props identifier (`function(props: P)`) and the
125
112
  * analyzer-extracted prop names. Stashed at `generate()` entry so the
@@ -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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"}
@@ -2,8 +2,16 @@
2
2
  * IR traversal helpers for the Jinja2 template adapter.
3
3
  *
4
4
  * Ported from `packages/adapter-xslate/src/adapter/lib/ir-scope.ts`.
5
- * `resolveJsxChildrenProp` and `collectRootScopeNodes` are byte-identical
6
- * (adapter-agnostic IR walks).
5
+ * `resolveJsxChildrenProp` used to live here too (byte-identical across
6
+ * every adapter carrying a copy of this file); it moved to
7
+ * `@barefootjs/shared` (#2773) so every adapter — DSL and Go template
8
+ * alike — calls the one implementation instead of each keeping a copy
9
+ * that can silently re-diverge. `collectRootScopeNodes` — the "which
10
+ * elements are this component's own render root(s)" walk this file used
11
+ * to duplicate too — moved into `jsx-to-ir.ts`'s `resolveRootKeyAttr`
12
+ * (#2753): the decision it fed (a `data-key` relay attribute) is resolved
13
+ * once, onto `IRElement.keyAttr`, instead of re-derived at emit time by
14
+ * every adapter.
7
15
  *
8
16
  * `extractTopLevelIdentifiers` scans the RENDERED template text (rather than
9
17
  * re-deriving free vars from the original JS AST) so it stays exactly in
@@ -25,7 +33,6 @@
25
33
  * AVAILABILITY itself is now the shared `computeSsrSeedPlan`'s job
26
34
  * (packages/jsx/src/ssr-seed-plan.ts), not this module's (#2075).
27
35
  */
28
- import type { IRNode, IRProp } from '@barefootjs/jsx';
29
36
  /**
30
37
  * Extract the set of "top-level identifier" tokens from a rendered Jinja
31
38
  * expression: bare words, excluding quoted-string content, dotted
@@ -33,18 +40,4 @@ import type { IRNode, IRProp } from '@barefootjs/jsx';
33
40
  * See the file header for why this replaces a direct `\w+` scan.
34
41
  */
35
42
  export declare function extractTopLevelIdentifiers(jinjaExpr: string): string[];
36
- /**
37
- * Find the `children` prop's `jsx-children` payload. Narrowed via the
38
- * AttrValue `kind` discriminator so adapter code stays type-safe if the IR
39
- * shape evolves.
40
- */
41
- export declare function resolveJsxChildrenProp(props: readonly IRProp[]): IRNode[];
42
- /**
43
- * Collect the component's root scope element node(s) — the elements that
44
- * become the rendered root and so carry `data-key` for a keyed loop item. A
45
- * plain element root is itself; an `if-statement` (early-return) root
46
- * contributes the top element of each branch, since exactly one renders at
47
- * runtime. (#1297)
48
- */
49
- export declare function collectRootScopeNodes(node: IRNode): Set<IRNode>;
50
43
  //# sourceMappingURL=ir-scope.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ir-scope.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/ir-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAA6B,MAAM,iBAAiB,CAAA;AAYhF;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAUtE;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAKzE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAiB/D"}
1
+ {"version":3,"file":"ir-scope.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/ir-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAYH;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAUtE"}
package/dist/index.js CHANGED
@@ -187377,7 +187377,7 @@ function isAriaBooleanAttr(name) {
187377
187377
  }
187378
187378
 
187379
187379
  // src/adapter/jinja-adapter.ts
187380
- import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
187380
+ import { BF_SLOT, BF_COND, BF_REGION, escapeHtml, resolveJsxChildrenProp } from "@barefootjs/shared";
187381
187381
 
187382
187382
  // src/adapter/lib/constants.ts
187383
187383
  var JINJA_TEMPLATE_PRIMITIVES = {
@@ -187452,62 +187452,6 @@ function jinjaAccessorFromSegments(base, segments) {
187452
187452
  return acc;
187453
187453
  }
187454
187454
 
187455
- // src/adapter/lib/ir-scope.ts
187456
- var NON_VAR_TOKENS = new Set([
187457
- "bf",
187458
- "if",
187459
- "else",
187460
- "is",
187461
- "not",
187462
- "and",
187463
- "or",
187464
- "in",
187465
- "none",
187466
- "true",
187467
- "false",
187468
- "defined"
187469
- ]);
187470
- function extractTopLevelIdentifiers(jinjaExpr) {
187471
- const stripped = jinjaExpr.replace(/'(?:\\.|[^'\\])*'/g, " ");
187472
- const out = [];
187473
- for (const m of stripped.matchAll(/(?<!\.)\b([A-Za-z_]\w*)\b/g)) {
187474
- if (!NON_VAR_TOKENS.has(m[1]))
187475
- out.push(m[1]);
187476
- }
187477
- return out;
187478
- }
187479
- function resolveJsxChildrenProp(props) {
187480
- const prop = props.find((p) => p.name === "children");
187481
- if (!prop)
187482
- return [];
187483
- if (prop.value.kind !== "jsx-children")
187484
- return [];
187485
- return prop.value.children;
187486
- }
187487
- function collectRootScopeNodes(node) {
187488
- const out = new Set;
187489
- const visit = (n) => {
187490
- if (!n)
187491
- return;
187492
- if (n.type === "element") {
187493
- out.add(n);
187494
- return;
187495
- }
187496
- if (n.type === "if-statement") {
187497
- const s = n;
187498
- visit(s.consequent);
187499
- visit(s.alternate);
187500
- return;
187501
- }
187502
- if (n.type === "fragment") {
187503
- for (const c of n.children)
187504
- visit(c);
187505
- }
187506
- };
187507
- visit(node);
187508
- return out;
187509
- }
187510
-
187511
187455
  // src/adapter/expr/array-method.ts
187512
187456
  import {
187513
187457
  serializeParsedExpr,
@@ -188237,6 +188181,33 @@ import {
188237
188181
  computeSsrSeedPlan,
188238
188182
  materializeGetterCalls
188239
188183
  } from "@barefootjs/jsx";
188184
+
188185
+ // src/adapter/lib/ir-scope.ts
188186
+ var NON_VAR_TOKENS = new Set([
188187
+ "bf",
188188
+ "if",
188189
+ "else",
188190
+ "is",
188191
+ "not",
188192
+ "and",
188193
+ "or",
188194
+ "in",
188195
+ "none",
188196
+ "true",
188197
+ "false",
188198
+ "defined"
188199
+ ]);
188200
+ function extractTopLevelIdentifiers(jinjaExpr) {
188201
+ const stripped = jinjaExpr.replace(/'(?:\\.|[^'\\])*'/g, " ");
188202
+ const out = [];
188203
+ for (const m of stripped.matchAll(/(?<!\.)\b([A-Za-z_]\w*)\b/g)) {
188204
+ if (!NON_VAR_TOKENS.has(m[1]))
188205
+ out.push(m[1]);
188206
+ }
188207
+ return out;
188208
+ }
188209
+
188210
+ // src/adapter/memo/seed.ts
188240
188211
  function contextDefaultJinja(c) {
188241
188212
  const d = c.defaultValue;
188242
188213
  if (d === null || d === undefined)
@@ -188318,11 +188289,9 @@ class JinjaAdapter extends BaseAdapter {
188318
188289
  templatesPerComponent = true;
188319
188290
  templatePrimitives = JINJA_PRIMITIVE_EMIT_MAP;
188320
188291
  componentName = "";
188321
- rootScopeNodes = new Set;
188322
188292
  options;
188323
188293
  errors = [];
188324
188294
  inLoop = false;
188325
- currentLoopKeyDepth = 0;
188326
188295
  propsObjectName = null;
188327
188296
  propsParams = [];
188328
188297
  booleanTypedProps = new Set;
@@ -188358,7 +188327,6 @@ class JinjaAdapter extends BaseAdapter {
188358
188327
  if (!options?.siblingTemplatesRegistered) {
188359
188328
  this.errors.push(...collectImportedLoopChildComponentErrors(ir, this.componentName));
188360
188329
  }
188361
- this.rootScopeNodes = collectRootScopeNodes(ir.root);
188362
188330
  const templateBody = ir.root.type === "if-statement" ? this.renderIfStatement(ir.root) : this.renderNode(ir.root);
188363
188331
  const scriptReg = options?.skipScriptRegistration ? "" : this.generateScriptRegistrations(ir, options?.scriptBaseName, options?.scriptAssets, options?.preloadAssets);
188364
188332
  const ctxSeed = generateContextConsumerSeed(ir);
@@ -188491,8 +188459,14 @@ class JinjaAdapter extends BaseAdapter {
188491
188459
  if (element.needsScope) {
188492
188460
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
188493
188461
  }
188494
- if (this.rootScopeNodes.has(element) && element.needsScope) {
188495
- hydrationAttrs += ` {{ bf.data_key_attr() | safe }}`;
188462
+ if (element.keyAttr) {
188463
+ if (element.keyAttr.value !== undefined) {
188464
+ const lowered = emitAttrValue({ kind: "expression", expr: element.keyAttr.value }, this.elementAttrEmitter, element.keyAttr.name);
188465
+ if (lowered)
188466
+ hydrationAttrs += ` ${lowered}`;
188467
+ } else {
188468
+ hydrationAttrs += ` {{ bf.data_key_attr() | safe }}`;
188469
+ }
188496
188470
  }
188497
188471
  if (element.slotId) {
188498
188472
  hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`;
@@ -188695,13 +188669,10 @@ ${whenTrue}
188695
188669
  }
188696
188670
  const prevInLoop = this.inLoop;
188697
188671
  this.inLoop = true;
188698
- const prevLoopKeyDepth = this.currentLoopKeyDepth;
188699
- this.currentLoopKeyDepth = loop.depth;
188700
188672
  const prevScope = this.scope;
188701
188673
  this.scope = prevScope.enterLoopRow(loop);
188702
188674
  const preambleLines = (loop.preamble?.declarations ?? []).map((d) => `{% set ${jinjaIdent(d.name)} = ${this.convertExpressionToJinja(d.raw, d.valueParsed)} %}`);
188703
188675
  const childrenUnderLoop = this.renderChildren(loop.children);
188704
- this.currentLoopKeyDepth = prevLoopKeyDepth;
188705
188676
  this.inLoop = prevInLoop;
188706
188677
  const bodyChildren = loop.bodyIsItemConditional && loop.key ? `{{ bf.comment("loop-i:" ~ bf.string(${this.convertExpressionToJinja(loop.key)})) | safe }}
188707
188678
  ${childrenUnderLoop}` : childrenUnderLoop;
@@ -188976,14 +188947,9 @@ ${name}="{{ bf.string(${val}) }}"
188976
188947
  continue;
188977
188948
  if (isDangerousInnerHtmlAttr(attr))
188978
188949
  continue;
188979
- let attrName;
188980
- if (attr.name === "className")
188981
- attrName = "class";
188982
- else if (attr.name === "key") {
188983
- const depth = this.currentLoopKeyDepth;
188984
- attrName = depth > 0 ? `data-key-${depth}` : "data-key";
188985
- } else
188986
- attrName = attr.name;
188950
+ if (attr.name === "key")
188951
+ continue;
188952
+ const attrName = attr.name === "className" ? "class" : attr.name;
188987
188953
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
188988
188954
  if (lowered)
188989
188955
  parts.push(lowered);
package/dist/vite.js CHANGED
@@ -5,7 +5,7 @@ import { barefoot as coreBarefoot } from "@barefootjs/vite";
5
5
  import { devModuleUrl, loadManifest, resolveDevOrigin, resolveScriptAssets, toPosixRelative } from "@barefootjs/vite";
6
6
 
7
7
  // ../jsx/src/compiler.ts
8
- import ts24 from "typescript";
8
+ import ts25 from "typescript";
9
9
 
10
10
  // ../jsx/src/analyzer.ts
11
11
  import ts9 from "typescript";
@@ -2134,6 +2134,15 @@ function isBooleanAttr(name) {
2134
2134
  function escapeHtml(text) {
2135
2135
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2136
2136
  }
2137
+ // ../shared/src/jsx-children-prop.ts
2138
+ function resolveJsxChildrenProp(props) {
2139
+ const prop = props.find((p) => p.name === "children");
2140
+ if (!prop)
2141
+ return [];
2142
+ if (prop.value.kind !== "jsx-children")
2143
+ return [];
2144
+ return prop.value.children ?? [];
2145
+ }
2137
2146
  // ../jsx/src/ir-to-client-js/csr-substitute.ts
2138
2147
  import ts4 from "typescript";
2139
2148
  function extractFreeIdentifiersFromText(text) {
@@ -3060,6 +3069,10 @@ var EMPTY_BOUND = new Set;
3060
3069
  var constInitializerCache = new WeakMap;
3061
3070
  var functionInfoExprCache = new WeakMap;
3062
3071
 
3072
+ // ../jsx/src/ir-to-client-js/prop-handling.ts
3073
+ var _localConstantValuesCache = new WeakMap;
3074
+ var _restSpreadNamesCache = new WeakMap;
3075
+
3063
3076
  // ../jsx/src/ir-to-client-js/control-flow/stringify/template-parse.ts
3064
3077
  var SVG_ROOT_TAGS = new Set([
3065
3078
  "svg",
@@ -3181,11 +3194,14 @@ var KEYWORDS_AND_GLOBALS = new Set([
3181
3194
  // ../jsx/src/ir-to-client-js/walk-prop-accesses.ts
3182
3195
  import ts14 from "typescript";
3183
3196
 
3197
+ // ../jsx/src/ir-to-client-js/imports.ts
3198
+ import ts16 from "typescript";
3199
+
3184
3200
  // ../jsx/src/value-references.ts
3185
3201
  import ts15 from "typescript";
3186
3202
 
3187
3203
  // ../jsx/src/relocate.ts
3188
- import ts16 from "typescript";
3204
+ import ts17 from "typescript";
3189
3205
 
3190
3206
  // ../jsx/src/lowering-registry.ts
3191
3207
  var plugins = [];
@@ -3402,10 +3418,10 @@ function matchSearchParamsMethodCall(callee, args, localNames) {
3402
3418
  }
3403
3419
 
3404
3420
  // ../jsx/src/ir-to-client-js/prune-unused-prop-extractions.ts
3405
- import ts17 from "typescript";
3421
+ import ts18 from "typescript";
3406
3422
 
3407
3423
  // ../jsx/src/ir-to-client-js/control-flow/plan/lazy-preamble.ts
3408
- import ts18 from "typescript";
3424
+ import ts19 from "typescript";
3409
3425
  var NO_PREAMBLE = {
3410
3426
  lazySafe: true,
3411
3427
  facts: { declaredNames: new Set, freeNames: new Set }
@@ -3455,7 +3471,7 @@ var INERT_BINDING_GLOBALS = new Set([
3455
3471
  ]);
3456
3472
 
3457
3473
  // ../jsx/src/ir-to-client-js/emit-reactive.ts
3458
- import ts19 from "typescript";
3474
+ import ts20 from "typescript";
3459
3475
 
3460
3476
  // ../jsx/src/ir-to-client-js/control-flow/stringify/event-delegation.ts
3461
3477
  var NON_BUBBLING_EVENTS = new Set([
@@ -3470,7 +3486,7 @@ var NON_BUBBLING_EVENTS = new Set([
3470
3486
  ]);
3471
3487
 
3472
3488
  // ../jsx/src/ir-to-client-js/rewrite-props-object.ts
3473
- import ts20 from "typescript";
3489
+ import ts21 from "typescript";
3474
3490
 
3475
3491
  // ../jsx/src/ir-to-client-js/source-map.ts
3476
3492
  var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -3561,15 +3577,15 @@ class SourceMapGenerator {
3561
3577
  }
3562
3578
 
3563
3579
  // ../jsx/src/preprocess-inline-jsx-callbacks.ts
3564
- import ts21 from "typescript";
3580
+ import ts22 from "typescript";
3565
3581
 
3566
3582
  // ../jsx/src/ssr-defaults.ts
3567
- import ts22 from "typescript";
3583
+ import ts23 from "typescript";
3568
3584
  var UNRESOLVED = Symbol("unresolved");
3569
3585
  var NO_RETURN = Symbol("no-return");
3570
3586
 
3571
3587
  // ../jsx/src/augment-inherited-props.ts
3572
- import ts23 from "typescript";
3588
+ import ts24 from "typescript";
3573
3589
  function collectContextConsumers(metadata) {
3574
3590
  const constants = metadata.localConstants ?? [];
3575
3591
  const contextDefaults = new Map;
@@ -3601,47 +3617,47 @@ function collectContextConsumers(metadata) {
3601
3617
  }
3602
3618
  function parseUseContextArg(source) {
3603
3619
  const expr = parseSingleExpression(source);
3604
- if (!expr || !ts23.isCallExpression(expr))
3620
+ if (!expr || !ts24.isCallExpression(expr))
3605
3621
  return null;
3606
- if (!ts23.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
3622
+ if (!ts24.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
3607
3623
  return null;
3608
3624
  if (expr.arguments.length !== 1)
3609
3625
  return null;
3610
3626
  const arg = expr.arguments[0];
3611
- return ts23.isIdentifier(arg) ? arg.text : null;
3627
+ return ts24.isIdentifier(arg) ? arg.text : null;
3612
3628
  }
3613
3629
  function parseCreateContextDefault(source) {
3614
3630
  const expr = parseSingleExpression(source);
3615
- if (!expr || !ts23.isCallExpression(expr))
3631
+ if (!expr || !ts24.isCallExpression(expr))
3616
3632
  return null;
3617
3633
  if (expr.arguments.length === 0)
3618
3634
  return null;
3619
3635
  const arg = expr.arguments[0];
3620
- if (ts23.isStringLiteral(arg) || ts23.isNoSubstitutionTemplateLiteral(arg))
3636
+ if (ts24.isStringLiteral(arg) || ts24.isNoSubstitutionTemplateLiteral(arg))
3621
3637
  return arg.text;
3622
- if (ts23.isNumericLiteral(arg))
3638
+ if (ts24.isNumericLiteral(arg))
3623
3639
  return Number(arg.text);
3624
- if (arg.kind === ts23.SyntaxKind.TrueKeyword)
3640
+ if (arg.kind === ts24.SyntaxKind.TrueKeyword)
3625
3641
  return true;
3626
- if (arg.kind === ts23.SyntaxKind.FalseKeyword)
3642
+ if (arg.kind === ts24.SyntaxKind.FalseKeyword)
3627
3643
  return false;
3628
3644
  return null;
3629
3645
  }
3630
3646
  function isObjectLiteralCreateContextDefault(source) {
3631
3647
  const expr = parseSingleExpression(source);
3632
- if (!expr || !ts23.isCallExpression(expr))
3648
+ if (!expr || !ts24.isCallExpression(expr))
3633
3649
  return false;
3634
3650
  if (expr.arguments.length === 0)
3635
3651
  return false;
3636
- return ts23.isObjectLiteralExpression(expr.arguments[0]);
3652
+ return ts24.isObjectLiteralExpression(expr.arguments[0]);
3637
3653
  }
3638
3654
  function parseSingleExpression(source) {
3639
- const sf = ts23.createSourceFile("__ctx.ts", `(${source})`, ts23.ScriptTarget.Latest, false);
3655
+ const sf = ts24.createSourceFile("__ctx.ts", `(${source})`, ts24.ScriptTarget.Latest, false);
3640
3656
  const stmt = sf.statements[0];
3641
- if (!stmt || !ts23.isExpressionStatement(stmt))
3657
+ if (!stmt || !ts24.isExpressionStatement(stmt))
3642
3658
  return null;
3643
3659
  let e = stmt.expression;
3644
- while (ts23.isParenthesizedExpression(e))
3660
+ while (ts24.isParenthesizedExpression(e))
3645
3661
  e = e.expression;
3646
3662
  return e;
3647
3663
  }
@@ -3666,25 +3682,25 @@ function augmentInheritedPropAccesses(ir) {
3666
3682
  const pinCoalesceLiterals = (s) => {
3667
3683
  if (!s || !s.includes(propsObj))
3668
3684
  return;
3669
- const sf = ts23.createSourceFile("__aug.ts", `(${s})`, ts23.ScriptTarget.Latest, false);
3685
+ const sf = ts24.createSourceFile("__aug.ts", `(${s})`, ts24.ScriptTarget.Latest, false);
3670
3686
  const visit = (n) => {
3671
- if (ts23.isBinaryExpression(n) && (n.operatorToken.kind === ts23.SyntaxKind.QuestionQuestionToken || n.operatorToken.kind === ts23.SyntaxKind.BarBarToken)) {
3687
+ if (ts24.isBinaryExpression(n) && (n.operatorToken.kind === ts24.SyntaxKind.QuestionQuestionToken || n.operatorToken.kind === ts24.SyntaxKind.BarBarToken)) {
3672
3688
  let left = n.left;
3673
- while (ts23.isParenthesizedExpression(left))
3689
+ while (ts24.isParenthesizedExpression(left))
3674
3690
  left = left.expression;
3675
- if (ts23.isPropertyAccessExpression(left) && ts23.isIdentifier(left.expression) && left.expression.text === propsObj) {
3691
+ if (ts24.isPropertyAccessExpression(left) && ts24.isIdentifier(left.expression) && left.expression.text === propsObj) {
3676
3692
  const name = left.name.text;
3677
3693
  let right = n.right;
3678
- while (ts23.isParenthesizedExpression(right))
3694
+ while (ts24.isParenthesizedExpression(right))
3679
3695
  right = right.expression;
3680
- if (ts23.isPrefixUnaryExpression(right))
3696
+ if (ts24.isPrefixUnaryExpression(right))
3681
3697
  right = right.operand;
3682
- const kind = ts23.isNumericLiteral(right) ? "number" : right.kind === ts23.SyntaxKind.TrueKeyword || right.kind === ts23.SyntaxKind.FalseKeyword ? "boolean" : ts23.isStringLiteralLike(right) ? "string" : null;
3698
+ const kind = ts24.isNumericLiteral(right) ? "number" : right.kind === ts24.SyntaxKind.TrueKeyword || right.kind === ts24.SyntaxKind.FalseKeyword ? "boolean" : ts24.isStringLiteralLike(right) ? "string" : null;
3683
3699
  if (kind && !coalesceLiteralTypes.has(name))
3684
3700
  coalesceLiteralTypes.set(name, kind);
3685
3701
  }
3686
3702
  }
3687
- ts23.forEachChild(n, visit);
3703
+ ts24.forEachChild(n, visit);
3688
3704
  };
3689
3705
  visit(sf);
3690
3706
  };
@@ -3795,33 +3811,33 @@ function augmentInheritedPropAccesses(ir) {
3795
3811
  }
3796
3812
  }
3797
3813
  function parseStaticStringConst(source) {
3798
- const sf = ts23.createSourceFile("__const.ts", `const __x = (${source});`, ts23.ScriptTarget.Latest, false);
3814
+ const sf = ts24.createSourceFile("__const.ts", `const __x = (${source});`, ts24.ScriptTarget.Latest, false);
3799
3815
  const stmt = sf.statements[0];
3800
- if (!stmt || !ts23.isVariableStatement(stmt))
3816
+ if (!stmt || !ts24.isVariableStatement(stmt))
3801
3817
  return null;
3802
3818
  let init = stmt.declarationList.declarations[0]?.initializer;
3803
- while (init && ts23.isParenthesizedExpression(init))
3819
+ while (init && ts24.isParenthesizedExpression(init))
3804
3820
  init = init.expression;
3805
3821
  if (!init)
3806
3822
  return null;
3807
- if (ts23.isStringLiteral(init) || ts23.isNoSubstitutionTemplateLiteral(init)) {
3823
+ if (ts24.isStringLiteral(init) || ts24.isNoSubstitutionTemplateLiteral(init)) {
3808
3824
  return init.text;
3809
3825
  }
3810
3826
  return evalStringArrayJoin(source);
3811
3827
  }
3812
3828
  function evalTemplateOfStringConsts(source, resolved) {
3813
- const sf = ts23.createSourceFile("__const.ts", `const __x = (${source});`, ts23.ScriptTarget.Latest, false);
3829
+ const sf = ts24.createSourceFile("__const.ts", `const __x = (${source});`, ts24.ScriptTarget.Latest, false);
3814
3830
  const stmt = sf.statements[0];
3815
- if (!stmt || !ts23.isVariableStatement(stmt))
3831
+ if (!stmt || !ts24.isVariableStatement(stmt))
3816
3832
  return null;
3817
3833
  let init = stmt.declarationList.declarations[0]?.initializer;
3818
- while (init && ts23.isParenthesizedExpression(init))
3834
+ while (init && ts24.isParenthesizedExpression(init))
3819
3835
  init = init.expression;
3820
- if (!init || !ts23.isTemplateExpression(init))
3836
+ if (!init || !ts24.isTemplateExpression(init))
3821
3837
  return null;
3822
3838
  let out = init.head.text;
3823
3839
  for (const span of init.templateSpans) {
3824
- if (!ts23.isIdentifier(span.expression))
3840
+ if (!ts24.isIdentifier(span.expression))
3825
3841
  return null;
3826
3842
  const value = resolved.get(span.expression.text);
3827
3843
  if (value === undefined)
@@ -3854,30 +3870,30 @@ function lookupStaticRecordLiteral(objectName, key, constants, isShadowed) {
3854
3870
  const constInfo = (constants ?? []).find((c) => c.name === objectName && c.isModule);
3855
3871
  if (constInfo?.value === undefined)
3856
3872
  return null;
3857
- const sf = ts23.createSourceFile("__rec.ts", `(${constInfo.value})`, ts23.ScriptTarget.Latest, true);
3873
+ const sf = ts24.createSourceFile("__rec.ts", `(${constInfo.value})`, ts24.ScriptTarget.Latest, true);
3858
3874
  if (sf.statements.length !== 1)
3859
3875
  return null;
3860
3876
  const stmt = sf.statements[0];
3861
- if (!ts23.isExpressionStatement(stmt))
3877
+ if (!ts24.isExpressionStatement(stmt))
3862
3878
  return null;
3863
3879
  let parsed = stmt.expression;
3864
- while (ts23.isParenthesizedExpression(parsed))
3880
+ while (ts24.isParenthesizedExpression(parsed))
3865
3881
  parsed = parsed.expression;
3866
- if (!ts23.isObjectLiteralExpression(parsed))
3882
+ if (!ts24.isObjectLiteralExpression(parsed))
3867
3883
  return null;
3868
3884
  for (const prop of parsed.properties) {
3869
- if (!ts23.isPropertyAssignment(prop))
3885
+ if (!ts24.isPropertyAssignment(prop))
3870
3886
  continue;
3871
3887
  const name = prop.name;
3872
- const propKey = ts23.isIdentifier(name) || ts23.isStringLiteral(name) || ts23.isNoSubstitutionTemplateLiteral(name) ? name.text : null;
3888
+ const propKey = ts24.isIdentifier(name) || ts24.isStringLiteral(name) || ts24.isNoSubstitutionTemplateLiteral(name) ? name.text : null;
3873
3889
  if (propKey !== key)
3874
3890
  continue;
3875
3891
  let v = prop.initializer;
3876
- while (ts23.isParenthesizedExpression(v))
3892
+ while (ts24.isParenthesizedExpression(v))
3877
3893
  v = v.expression;
3878
- if (ts23.isNumericLiteral(v))
3894
+ if (ts24.isNumericLiteral(v))
3879
3895
  return { kind: "number", text: v.text };
3880
- if (ts23.isStringLiteral(v) || ts23.isNoSubstitutionTemplateLiteral(v)) {
3896
+ if (ts24.isStringLiteral(v) || ts24.isNoSubstitutionTemplateLiteral(v)) {
3881
3897
  return { kind: "string", text: v.text };
3882
3898
  }
3883
3899
  return null;
@@ -3885,28 +3901,28 @@ function lookupStaticRecordLiteral(objectName, key, constants, isShadowed) {
3885
3901
  return null;
3886
3902
  }
3887
3903
  function evalStringArrayJoin(source) {
3888
- const sf = ts23.createSourceFile("__join.ts", `const __x = (${source});`, ts23.ScriptTarget.Latest, false);
3904
+ const sf = ts24.createSourceFile("__join.ts", `const __x = (${source});`, ts24.ScriptTarget.Latest, false);
3889
3905
  const stmt = sf.statements[0];
3890
- if (!stmt || !ts23.isVariableStatement(stmt))
3906
+ if (!stmt || !ts24.isVariableStatement(stmt))
3891
3907
  return null;
3892
3908
  let node = stmt.declarationList.declarations[0]?.initializer;
3893
- while (node && ts23.isParenthesizedExpression(node))
3909
+ while (node && ts24.isParenthesizedExpression(node))
3894
3910
  node = node.expression;
3895
- if (!node || !ts23.isCallExpression(node))
3911
+ if (!node || !ts24.isCallExpression(node))
3896
3912
  return null;
3897
3913
  const callee = node.expression;
3898
- if (!ts23.isPropertyAccessExpression(callee))
3914
+ if (!ts24.isPropertyAccessExpression(callee))
3899
3915
  return null;
3900
3916
  if (callee.name.text !== "join")
3901
3917
  return null;
3902
3918
  let recv = callee.expression;
3903
- while (ts23.isParenthesizedExpression(recv))
3919
+ while (ts24.isParenthesizedExpression(recv))
3904
3920
  recv = recv.expression;
3905
- if (!ts23.isArrayLiteralExpression(recv))
3921
+ if (!ts24.isArrayLiteralExpression(recv))
3906
3922
  return null;
3907
3923
  const parts = [];
3908
3924
  for (const el of recv.elements) {
3909
- if (ts23.isStringLiteral(el) || ts23.isNoSubstitutionTemplateLiteral(el)) {
3925
+ if (ts24.isStringLiteral(el) || ts24.isNoSubstitutionTemplateLiteral(el)) {
3910
3926
  parts.push(el.text);
3911
3927
  } else {
3912
3928
  return null;
@@ -3915,7 +3931,7 @@ function evalStringArrayJoin(source) {
3915
3931
  let sep = ",";
3916
3932
  if (node.arguments.length >= 1) {
3917
3933
  const arg = node.arguments[0];
3918
- if (ts23.isStringLiteral(arg) || ts23.isNoSubstitutionTemplateLiteral(arg))
3934
+ if (ts24.isStringLiteral(arg) || ts24.isNoSubstitutionTemplateLiteral(arg))
3919
3935
  sep = arg.text;
3920
3936
  else
3921
3937
  return null;
@@ -3923,11 +3939,11 @@ function evalStringArrayJoin(source) {
3923
3939
  return parts.join(sep);
3924
3940
  }
3925
3941
  function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
3926
- if (!ts23.isElementAccessExpression(val))
3942
+ if (!ts24.isElementAccessExpression(val))
3927
3943
  return null;
3928
3944
  const obj = val.expression;
3929
3945
  const arg = val.argumentExpression;
3930
- if (!ts23.isIdentifier(obj) || !ts23.isIdentifier(arg))
3946
+ if (!ts24.isIdentifier(obj) || !ts24.isIdentifier(arg))
3931
3947
  return null;
3932
3948
  let indexPropName;
3933
3949
  let defaultKey;
@@ -3943,35 +3959,35 @@ function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
3943
3959
  const constInfo = localConstants.find((c) => c.name === obj.text && c.isModule);
3944
3960
  if (constInfo?.value === undefined)
3945
3961
  return null;
3946
- const sf = ts23.createSourceFile("__rec.ts", `(${constInfo.value})`, ts23.ScriptTarget.Latest, true);
3962
+ const sf = ts24.createSourceFile("__rec.ts", `(${constInfo.value})`, ts24.ScriptTarget.Latest, true);
3947
3963
  if (sf.statements.length !== 1)
3948
3964
  return null;
3949
3965
  const stmt = sf.statements[0];
3950
- if (!ts23.isExpressionStatement(stmt))
3966
+ if (!ts24.isExpressionStatement(stmt))
3951
3967
  return null;
3952
3968
  let parsed = stmt.expression;
3953
- while (ts23.isParenthesizedExpression(parsed))
3969
+ while (ts24.isParenthesizedExpression(parsed))
3954
3970
  parsed = parsed.expression;
3955
- if (!ts23.isObjectLiteralExpression(parsed))
3971
+ if (!ts24.isObjectLiteralExpression(parsed))
3956
3972
  return null;
3957
3973
  const entries = [];
3958
3974
  for (const prop of parsed.properties) {
3959
- if (!ts23.isPropertyAssignment(prop))
3975
+ if (!ts24.isPropertyAssignment(prop))
3960
3976
  return null;
3961
3977
  let key;
3962
- if (ts23.isIdentifier(prop.name)) {
3978
+ if (ts24.isIdentifier(prop.name)) {
3963
3979
  key = prop.name.text;
3964
- } else if (ts23.isStringLiteral(prop.name) || ts23.isNoSubstitutionTemplateLiteral(prop.name)) {
3980
+ } else if (ts24.isStringLiteral(prop.name) || ts24.isNoSubstitutionTemplateLiteral(prop.name)) {
3965
3981
  key = prop.name.text;
3966
3982
  } else {
3967
3983
  return null;
3968
3984
  }
3969
3985
  let v = prop.initializer;
3970
- while (ts23.isParenthesizedExpression(v))
3986
+ while (ts24.isParenthesizedExpression(v))
3971
3987
  v = v.expression;
3972
- if (ts23.isNumericLiteral(v)) {
3988
+ if (ts24.isNumericLiteral(v)) {
3973
3989
  entries.push({ key, value: { kind: "number", text: v.text } });
3974
- } else if (ts23.isStringLiteral(v) || ts23.isNoSubstitutionTemplateLiteral(v)) {
3990
+ } else if (ts24.isStringLiteral(v) || ts24.isNoSubstitutionTemplateLiteral(v)) {
3975
3991
  entries.push({ key, value: { kind: "string", text: v.text } });
3976
3992
  } else {
3977
3993
  return null;
@@ -4060,7 +4076,7 @@ function computeSsrSeedPlan(metadata) {
4060
4076
  // ../jsx/src/rich-type-refusal.ts
4061
4077
  var EMPTY_BINDINGS2 = new Map;
4062
4078
  // ../jsx/src/shared-program.ts
4063
- import ts25 from "typescript";
4079
+ import ts26 from "typescript";
4064
4080
  // ../jsx/src/adapters/interface.ts
4065
4081
  class BaseAdapter {
4066
4082
  renderChildren(children) {
@@ -4342,7 +4358,7 @@ class JsxAdapter extends BaseAdapter {
4342
4358
  }
4343
4359
 
4344
4360
  // ../jsx/src/adapters/template-imports.ts
4345
- import ts26 from "typescript";
4361
+ import ts27 from "typescript";
4346
4362
  var CLIENT_PACKAGE_SOURCES = new Set([
4347
4363
  "@barefootjs/client",
4348
4364
  "@barefootjs/client/runtime"
@@ -5055,7 +5071,7 @@ function dangerousInnerHtmlDiagnostic(expr, loc, reason) {
5055
5071
  };
5056
5072
  }
5057
5073
  // ../jsx/src/combine-client-js.ts
5058
- import ts27 from "typescript";
5074
+ import ts28 from "typescript";
5059
5075
  // ../jsx/src/loop-destructure.ts
5060
5076
  function isLowerableLoopDestructure(loop) {
5061
5077
  const bindings = loop.paramBindings;
@@ -5195,9 +5211,9 @@ function escapeRe(s) {
5195
5211
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5196
5212
  }
5197
5213
  // ../jsx/src/debug.ts
5198
- import ts28 from "typescript";
5199
- // ../jsx/src/profiler.ts
5200
5214
  import ts29 from "typescript";
5215
+ // ../jsx/src/profiler.ts
5216
+ import ts30 from "typescript";
5201
5217
 
5202
5218
  // ../jsx/src/index.ts
5203
5219
  registerBuiltinLoweringPlugins();
@@ -5331,62 +5347,6 @@ function jinjaAccessorFromSegments(base, segments) {
5331
5347
  return acc;
5332
5348
  }
5333
5349
 
5334
- // src/adapter/lib/ir-scope.ts
5335
- var NON_VAR_TOKENS = new Set([
5336
- "bf",
5337
- "if",
5338
- "else",
5339
- "is",
5340
- "not",
5341
- "and",
5342
- "or",
5343
- "in",
5344
- "none",
5345
- "true",
5346
- "false",
5347
- "defined"
5348
- ]);
5349
- function extractTopLevelIdentifiers(jinjaExpr) {
5350
- const stripped = jinjaExpr.replace(/'(?:\\.|[^'\\])*'/g, " ");
5351
- const out = [];
5352
- for (const m of stripped.matchAll(/(?<!\.)\b([A-Za-z_]\w*)\b/g)) {
5353
- if (!NON_VAR_TOKENS.has(m[1]))
5354
- out.push(m[1]);
5355
- }
5356
- return out;
5357
- }
5358
- function resolveJsxChildrenProp(props) {
5359
- const prop = props.find((p) => p.name === "children");
5360
- if (!prop)
5361
- return [];
5362
- if (prop.value.kind !== "jsx-children")
5363
- return [];
5364
- return prop.value.children;
5365
- }
5366
- function collectRootScopeNodes(node) {
5367
- const out = new Set;
5368
- const visit = (n) => {
5369
- if (!n)
5370
- return;
5371
- if (n.type === "element") {
5372
- out.add(n);
5373
- return;
5374
- }
5375
- if (n.type === "if-statement") {
5376
- const s = n;
5377
- visit(s.consequent);
5378
- visit(s.alternate);
5379
- return;
5380
- }
5381
- if (n.type === "fragment") {
5382
- for (const c of n.children)
5383
- visit(c);
5384
- }
5385
- };
5386
- visit(node);
5387
- return out;
5388
- }
5389
-
5390
5350
  // src/adapter/expr/array-method.ts
5391
5351
  function renderArrayMethod(method, object, args, emit) {
5392
5352
  switch (method) {
@@ -6030,7 +5990,7 @@ function collectImportedLoopChildComponentErrors(ir, componentName) {
6030
5990
  }
6031
5991
 
6032
5992
  // src/adapter/spread/spread-codegen.ts
6033
- import ts30 from "typescript";
5993
+ import ts31 from "typescript";
6034
5994
  function conditionalSpreadToJinja(ctx, expr) {
6035
5995
  if (!expr || expr.kind !== "conditional")
6036
5996
  return null;
@@ -6087,7 +6047,7 @@ function recordIndexAccessToJinja(ctx, val) {
6087
6047
  if (val.kind !== "index-access" || val.object.kind !== "identifier" || val.index.kind !== "identifier") {
6088
6048
  return null;
6089
6049
  }
6090
- const tsVal = ts30.factory.createElementAccessExpression(ts30.factory.createIdentifier(val.object.name), ts30.factory.createIdentifier(val.index.name));
6050
+ const tsVal = ts31.factory.createElementAccessExpression(ts31.factory.createIdentifier(val.object.name), ts31.factory.createIdentifier(val.index.name));
6091
6051
  const parsed = parseRecordIndexAccess(tsVal, ctx.localConstants ?? [], ctx.propsParams);
6092
6052
  if (!parsed)
6093
6053
  return null;
@@ -6098,6 +6058,31 @@ function recordIndexAccessToJinja(ctx, val) {
6098
6058
  return `{${entries.join(", ")}}[${jinjaIdent(parsed.indexPropName)}]`;
6099
6059
  }
6100
6060
 
6061
+ // src/adapter/lib/ir-scope.ts
6062
+ var NON_VAR_TOKENS = new Set([
6063
+ "bf",
6064
+ "if",
6065
+ "else",
6066
+ "is",
6067
+ "not",
6068
+ "and",
6069
+ "or",
6070
+ "in",
6071
+ "none",
6072
+ "true",
6073
+ "false",
6074
+ "defined"
6075
+ ]);
6076
+ function extractTopLevelIdentifiers(jinjaExpr) {
6077
+ const stripped = jinjaExpr.replace(/'(?:\\.|[^'\\])*'/g, " ");
6078
+ const out = [];
6079
+ for (const m of stripped.matchAll(/(?<!\.)\b([A-Za-z_]\w*)\b/g)) {
6080
+ if (!NON_VAR_TOKENS.has(m[1]))
6081
+ out.push(m[1]);
6082
+ }
6083
+ return out;
6084
+ }
6085
+
6101
6086
  // src/adapter/memo/seed.ts
6102
6087
  function contextDefaultJinja(c) {
6103
6088
  const d = c.defaultValue;
@@ -6179,11 +6164,9 @@ class JinjaAdapter extends BaseAdapter {
6179
6164
  templatesPerComponent = true;
6180
6165
  templatePrimitives = JINJA_PRIMITIVE_EMIT_MAP;
6181
6166
  componentName = "";
6182
- rootScopeNodes = new Set;
6183
6167
  options;
6184
6168
  errors = [];
6185
6169
  inLoop = false;
6186
- currentLoopKeyDepth = 0;
6187
6170
  propsObjectName = null;
6188
6171
  propsParams = [];
6189
6172
  booleanTypedProps = new Set;
@@ -6219,7 +6202,6 @@ class JinjaAdapter extends BaseAdapter {
6219
6202
  if (!options?.siblingTemplatesRegistered) {
6220
6203
  this.errors.push(...collectImportedLoopChildComponentErrors(ir, this.componentName));
6221
6204
  }
6222
- this.rootScopeNodes = collectRootScopeNodes(ir.root);
6223
6205
  const templateBody = ir.root.type === "if-statement" ? this.renderIfStatement(ir.root) : this.renderNode(ir.root);
6224
6206
  const scriptReg = options?.skipScriptRegistration ? "" : this.generateScriptRegistrations(ir, options?.scriptBaseName, options?.scriptAssets, options?.preloadAssets);
6225
6207
  const ctxSeed = generateContextConsumerSeed(ir);
@@ -6352,8 +6334,14 @@ class JinjaAdapter extends BaseAdapter {
6352
6334
  if (element.needsScope) {
6353
6335
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
6354
6336
  }
6355
- if (this.rootScopeNodes.has(element) && element.needsScope) {
6356
- hydrationAttrs += ` {{ bf.data_key_attr() | safe }}`;
6337
+ if (element.keyAttr) {
6338
+ if (element.keyAttr.value !== undefined) {
6339
+ const lowered = emitAttrValue({ kind: "expression", expr: element.keyAttr.value }, this.elementAttrEmitter, element.keyAttr.name);
6340
+ if (lowered)
6341
+ hydrationAttrs += ` ${lowered}`;
6342
+ } else {
6343
+ hydrationAttrs += ` {{ bf.data_key_attr() | safe }}`;
6344
+ }
6357
6345
  }
6358
6346
  if (element.slotId) {
6359
6347
  hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`;
@@ -6556,13 +6544,10 @@ ${whenTrue}
6556
6544
  }
6557
6545
  const prevInLoop = this.inLoop;
6558
6546
  this.inLoop = true;
6559
- const prevLoopKeyDepth = this.currentLoopKeyDepth;
6560
- this.currentLoopKeyDepth = loop.depth;
6561
6547
  const prevScope = this.scope;
6562
6548
  this.scope = prevScope.enterLoopRow(loop);
6563
6549
  const preambleLines = (loop.preamble?.declarations ?? []).map((d) => `{% set ${jinjaIdent(d.name)} = ${this.convertExpressionToJinja(d.raw, d.valueParsed)} %}`);
6564
6550
  const childrenUnderLoop = this.renderChildren(loop.children);
6565
- this.currentLoopKeyDepth = prevLoopKeyDepth;
6566
6551
  this.inLoop = prevInLoop;
6567
6552
  const bodyChildren = loop.bodyIsItemConditional && loop.key ? `{{ bf.comment("loop-i:" ~ bf.string(${this.convertExpressionToJinja(loop.key)})) | safe }}
6568
6553
  ${childrenUnderLoop}` : childrenUnderLoop;
@@ -6837,14 +6822,9 @@ ${name}="{{ bf.string(${val}) }}"
6837
6822
  continue;
6838
6823
  if (isDangerousInnerHtmlAttr(attr))
6839
6824
  continue;
6840
- let attrName;
6841
- if (attr.name === "className")
6842
- attrName = "class";
6843
- else if (attr.name === "key") {
6844
- const depth = this.currentLoopKeyDepth;
6845
- attrName = depth > 0 ? `data-key-${depth}` : "data-key";
6846
- } else
6847
- attrName = attr.name;
6825
+ if (attr.name === "key")
6826
+ continue;
6827
+ const attrName = attr.name === "className" ? "class" : attr.name;
6848
6828
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
6849
6829
  if (lowered)
6850
6830
  parts.push(lowered);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/jinja",
3
- "version": "0.33.1",
3
+ "version": "0.33.3",
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,7 +53,7 @@
53
53
  "directory": "packages/adapter-jinja"
54
54
  },
55
55
  "dependencies": {
56
- "@barefootjs/shared": "0.33.1"
56
+ "@barefootjs/shared": "0.33.3"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "@barefootjs/jsx": ">=0.2.0",
@@ -70,9 +70,9 @@
70
70
  },
71
71
  "devDependencies": {
72
72
  "@barefootjs/adapter-tests": "0.1.0",
73
- "@barefootjs/jsx": "0.33.1",
74
- "@barefootjs/vite": "0.33.1",
75
- "@barefootjs/client": "0.33.1",
73
+ "@barefootjs/jsx": "0.33.3",
74
+ "@barefootjs/vite": "0.33.3",
75
+ "@barefootjs/client": "0.33.3",
76
76
  "typescript": "^5.0.0",
77
77
  "vite": "^6.0.0"
78
78
  }
@@ -588,12 +588,20 @@ export function ChildFragment() {
588
588
  // last top-level node -- both read the same `bf._scope_id()` at render
589
589
  // time, so the emitted scope ids are identical by construction.
590
590
  const beginIdx = template.indexOf('bf.scope_comment() | safe')
591
- const buttonIdx = template.indexOf('<button>')
591
+ const buttonIdx = template.indexOf('<button')
592
592
  const pIdx = template.indexOf('<p>')
593
593
  const endIdx = template.indexOf('bf.scope_comment_end() | safe')
594
594
  expect(beginIdx).toBeLessThan(buttonIdx)
595
595
  expect(buttonIdx).toBeLessThan(pIdx)
596
596
  expect(pIdx).toBeLessThan(endIdx)
597
+ // #2732: the fragment's FIRST element carries `data-key` — the wrapping
598
+ // comment holds scope identity only, so the tag is no longer bare (which
599
+ // is why the locator above matches `<button` and not `<button>`). Pinned
600
+ // both ways so "first element" can never silently widen to "every
601
+ // element": a second carrier would put the loop key on two nodes and
602
+ // `mapArray`'s `primaryEl.dataset.key` read would pick an arbitrary one.
603
+ expect(template.slice(buttonIdx, pIdx)).toContain('data_key_attr')
604
+ expect(template.slice(pIdx)).not.toContain('data_key_attr')
597
605
  })
598
606
 
599
607
  test('a non-fragment (single element) root does NOT get scope_comment_end (element attrs bound the scope instead)', () => {
@@ -151,7 +151,7 @@ import {
151
151
  } from '@barefootjs/jsx'
152
152
  import { isAriaBooleanAttr, isBooleanResultExpr, isExplicitStringCall } from './boolean-result.ts'
153
153
  import type { ParsedExpr, LoweringMatcher } from '@barefootjs/jsx'
154
- import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from '@barefootjs/shared'
154
+ import { BF_SLOT, BF_COND, BF_REGION, escapeHtml, resolveJsxChildrenProp } from '@barefootjs/shared'
155
155
 
156
156
  import type { JinjaRenderCtx } from './lib/types.ts'
157
157
  import { JINJA_PRIMITIVE_EMIT_MAP } from './lib/constants.ts'
@@ -161,10 +161,6 @@ import {
161
161
  escapeJinjaSingleQuoted,
162
162
  jinjaAccessorFromSegments,
163
163
  } from './lib/jinja-naming.ts'
164
- import {
165
- resolveJsxChildrenProp,
166
- collectRootScopeNodes,
167
- } from './lib/ir-scope.ts'
168
164
  import { renderSortMethod, renderSortEval } from './expr/array-method.ts'
169
165
  import { staticValueToJinja } from './lib/static-value.ts'
170
166
  import { JinjaFilterEmitter, JinjaTopLevelEmitter, truthyTest } from './expr/emitters.ts'
@@ -204,22 +200,9 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
204
200
  templatePrimitives: TemplatePrimitiveRegistry = JINJA_PRIMITIVE_EMIT_MAP
205
201
 
206
202
  private componentName: string = ''
207
- /** Component root scope element(s) — each carries `data-key` for a keyed loop
208
- * item (set by the child renderer from the JSX `key` prop). A plain element
209
- * root is one node; an `if-statement` (early-return) root contributes the
210
- * top element of every branch. */
211
- private rootScopeNodes: Set<IRNode> = new Set()
212
203
  private options: Required<JinjaAdapterOptions>
213
204
  private errors: CompilerError[] = []
214
205
  private inLoop: boolean = false
215
- /**
216
- * `IRLoop.depth` of the loop currently being rendered (save/restore
217
- * around `renderChildren(loop.children)`, mirroring `inLoop` above).
218
- * `renderAttributes` reads this to derive the `key` → `data-key`/
219
- * `data-key-N` suffix — the depth is IR-computed (jsx-to-ir.ts), not
220
- * re-derived here (#2168 nested-loop-outer-binding).
221
- */
222
- private currentLoopKeyDepth = 0
223
206
  /**
224
207
  * SolidJS-style props identifier (`function(props: P)`) and the
225
208
  * analyzer-extracted prop names. Stashed at `generate()` entry so the
@@ -343,7 +326,6 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
343
326
  this.errors.push(...collectImportedLoopChildComponentErrors(ir, this.componentName))
344
327
  }
345
328
 
346
- this.rootScopeNodes = collectRootScopeNodes(ir.root)
347
329
  const templateBody = ir.root.type === 'if-statement'
348
330
  ? this.renderIfStatement(ir.root as IRIfStatement)
349
331
  : this.renderNode(ir.root)
@@ -579,12 +561,30 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
579
561
  if (element.needsScope) {
580
562
  hydrationAttrs += ` ${this.renderScopeMarker('')}`
581
563
  }
582
- // A root scope element carries `data-key` for a keyed loop item (set on the
583
- // bf instance by the child renderer from the JSX `key` prop); non-keyed
584
- // renders add nothing. Mirrors Hono stamping data-key on each loop item's
585
- // root, including early-return (if-statement) roots. (#1297)
586
- if (this.rootScopeNodes.has(element) && element.needsScope) {
587
- hydrationAttrs += ` {{ bf.data_key_attr() | safe }}`
564
+ // #2753: `element.keyAttr` is the ONE IR-resolved decision for this
565
+ // element's row-key attribute replacing both the `carriesDataKey`
566
+ // boolean and this adapter's own `rootScopeNodes`/`needsScope` check
567
+ // (mechanism 2: a render-root relay, including the #2732 fragment-root
568
+ // case) and the separate `currentLoopKeyDepth`-driven rewrite of a
569
+ // literal `key` attribute this adapter used to do in `renderAttributes`
570
+ // (mechanism 1: a `.map()` row root compiled inline here).
571
+ if (element.keyAttr) {
572
+ if (element.keyAttr.value !== undefined) {
573
+ const lowered = emitAttrValue(
574
+ { kind: 'expression', expr: element.keyAttr.value },
575
+ this.elementAttrEmitter,
576
+ element.keyAttr.name,
577
+ )
578
+ if (lowered) hydrationAttrs += ` ${lowered}`
579
+ } else {
580
+ // Relay: this element is one of THIS component's own render roots,
581
+ // carrying whatever key its OWN caller supplies at runtime (the `bf`
582
+ // instance's `data_key` field) when this component is itself
583
+ // invoked as a caller's keyed loop row. Mirrors Hono stamping
584
+ // data-key on each loop item's root, including early-return
585
+ // (if-statement) roots (#1297), and #2732's fragment-root case.
586
+ hydrationAttrs += ` {{ bf.data_key_attr() | safe }}`
587
+ }
588
588
  }
589
589
  if (element.slotId) {
590
590
  hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`
@@ -932,8 +932,6 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
932
932
 
933
933
  const prevInLoop = this.inLoop
934
934
  this.inLoop = true
935
- const prevLoopKeyDepth = this.currentLoopKeyDepth
936
- this.currentLoopKeyDepth = loop.depth
937
935
  // Re-render children now that inLoop is set (so nested components use the
938
936
  // loop-child naming convention). renderedChildren above was computed with
939
937
  // the previous flag; recompute under the loop flag.
@@ -975,7 +973,6 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
975
973
  d => `{% set ${jinjaIdent(d.name)} = ${this.convertExpressionToJinja(d.raw, d.valueParsed)} %}`,
976
974
  )
977
975
  const childrenUnderLoop = this.renderChildren(loop.children)
978
- this.currentLoopKeyDepth = prevLoopKeyDepth
979
976
  this.inLoop = prevInLoop
980
977
  void renderedChildren
981
978
 
@@ -1543,14 +1540,15 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
1543
1540
  // literal never reaches the generic object-literal BF101 refusal
1544
1541
  // (which would double-report alongside the purpose-built one).
1545
1542
  if (isDangerousInnerHtmlAttr(attr)) continue
1543
+ // `key` never renders as a literal HTML attribute — #2753 resolves it
1544
+ // onto `element.keyAttr` (name AND value) at IR-build time, emitted
1545
+ // separately in `renderElement`. A stray `key` that jsx-to-ir.ts did
1546
+ // NOT recognize as a loop row root (so `keyAttr` was never set) has no
1547
+ // defined rendering outside a `.map()` anyway; drop it rather than
1548
+ // leak an invalid `key="..."` HTML attribute.
1549
+ if (attr.name === 'key') continue
1546
1550
  // Rewrite JSX special-prop names to their HTML-attribute counterparts.
1547
- let attrName: string
1548
- if (attr.name === 'className') attrName = 'class'
1549
- else if (attr.name === 'key') {
1550
- const depth = this.currentLoopKeyDepth
1551
- attrName = depth > 0 ? `data-key-${depth}` : 'data-key'
1552
- }
1553
- else attrName = attr.name
1551
+ const attrName = attr.name === 'className' ? 'class' : attr.name
1554
1552
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName)
1555
1553
  if (lowered) parts.push(lowered)
1556
1554
  }
@@ -2,8 +2,16 @@
2
2
  * IR traversal helpers for the Jinja2 template adapter.
3
3
  *
4
4
  * Ported from `packages/adapter-xslate/src/adapter/lib/ir-scope.ts`.
5
- * `resolveJsxChildrenProp` and `collectRootScopeNodes` are byte-identical
6
- * (adapter-agnostic IR walks).
5
+ * `resolveJsxChildrenProp` used to live here too (byte-identical across
6
+ * every adapter carrying a copy of this file); it moved to
7
+ * `@barefootjs/shared` (#2773) so every adapter — DSL and Go template
8
+ * alike — calls the one implementation instead of each keeping a copy
9
+ * that can silently re-diverge. `collectRootScopeNodes` — the "which
10
+ * elements are this component's own render root(s)" walk this file used
11
+ * to duplicate too — moved into `jsx-to-ir.ts`'s `resolveRootKeyAttr`
12
+ * (#2753): the decision it fed (a `data-key` relay attribute) is resolved
13
+ * once, onto `IRElement.keyAttr`, instead of re-derived at emit time by
14
+ * every adapter.
7
15
  *
8
16
  * `extractTopLevelIdentifiers` scans the RENDERED template text (rather than
9
17
  * re-deriving free vars from the original JS AST) so it stays exactly in
@@ -26,8 +34,6 @@
26
34
  * (packages/jsx/src/ssr-seed-plan.ts), not this module's (#2075).
27
35
  */
28
36
 
29
- import type { IRNode, IRProp, IRIfStatement, IRFragment } from '@barefootjs/jsx'
30
-
31
37
  /** Tokens this adapter's own codegen can emit that are never context vars. */
32
38
  const NON_VAR_TOKENS = new Set([
33
39
  'bf', 'if', 'else', 'is', 'not', 'and', 'or', 'in', 'none', 'true', 'false',
@@ -55,41 +61,3 @@ export function extractTopLevelIdentifiers(jinjaExpr: string): string[] {
55
61
  }
56
62
  return out
57
63
  }
58
-
59
- /**
60
- * Find the `children` prop's `jsx-children` payload. Narrowed via the
61
- * AttrValue `kind` discriminator so adapter code stays type-safe if the IR
62
- * shape evolves.
63
- */
64
- export function resolveJsxChildrenProp(props: readonly IRProp[]): IRNode[] {
65
- const prop = props.find(p => p.name === 'children')
66
- if (!prop) return []
67
- if (prop.value.kind !== 'jsx-children') return []
68
- return prop.value.children
69
- }
70
-
71
- /**
72
- * Collect the component's root scope element node(s) — the elements that
73
- * become the rendered root and so carry `data-key` for a keyed loop item. A
74
- * plain element root is itself; an `if-statement` (early-return) root
75
- * contributes the top element of each branch, since exactly one renders at
76
- * runtime. (#1297)
77
- */
78
- export function collectRootScopeNodes(node: IRNode): Set<IRNode> {
79
- const out = new Set<IRNode>()
80
- const visit = (n: IRNode | null): void => {
81
- if (!n) return
82
- if (n.type === 'element') { out.add(n); return }
83
- if (n.type === 'if-statement') {
84
- const s = n as IRIfStatement
85
- visit(s.consequent)
86
- visit(s.alternate)
87
- return
88
- }
89
- if (n.type === 'fragment') {
90
- for (const c of (n as IRFragment).children) visit(c)
91
- }
92
- }
93
- visit(node)
94
- return out
95
- }