@barefootjs/jinja 0.1.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.
Files changed (88) hide show
  1. package/README.md +66 -0
  2. package/dist/adapter/analysis/component-tree.d.ts +26 -0
  3. package/dist/adapter/analysis/component-tree.d.ts.map +1 -0
  4. package/dist/adapter/boolean-result.d.ts +84 -0
  5. package/dist/adapter/boolean-result.d.ts.map +1 -0
  6. package/dist/adapter/emit-context.d.ts +106 -0
  7. package/dist/adapter/emit-context.d.ts.map +1 -0
  8. package/dist/adapter/expr/array-method.d.ts +75 -0
  9. package/dist/adapter/expr/array-method.d.ts.map +1 -0
  10. package/dist/adapter/expr/emitters.d.ts +143 -0
  11. package/dist/adapter/expr/emitters.d.ts.map +1 -0
  12. package/dist/adapter/expr/operand.d.ts +25 -0
  13. package/dist/adapter/expr/operand.d.ts.map +1 -0
  14. package/dist/adapter/index.d.ts +6 -0
  15. package/dist/adapter/index.d.ts.map +1 -0
  16. package/dist/adapter/index.js +189097 -0
  17. package/dist/adapter/jinja-adapter.d.ts +394 -0
  18. package/dist/adapter/jinja-adapter.d.ts.map +1 -0
  19. package/dist/adapter/lib/constants.d.ts +21 -0
  20. package/dist/adapter/lib/constants.d.ts.map +1 -0
  21. package/dist/adapter/lib/ir-scope.d.ts +50 -0
  22. package/dist/adapter/lib/ir-scope.d.ts.map +1 -0
  23. package/dist/adapter/lib/jinja-naming.d.ts +77 -0
  24. package/dist/adapter/lib/jinja-naming.d.ts.map +1 -0
  25. package/dist/adapter/lib/types.d.ts +27 -0
  26. package/dist/adapter/lib/types.d.ts.map +1 -0
  27. package/dist/adapter/memo/seed.d.ts +81 -0
  28. package/dist/adapter/memo/seed.d.ts.map +1 -0
  29. package/dist/adapter/props/prop-classes.d.ts +33 -0
  30. package/dist/adapter/props/prop-classes.d.ts.map +1 -0
  31. package/dist/adapter/spread/spread-codegen.d.ts +61 -0
  32. package/dist/adapter/spread/spread-codegen.d.ts.map +1 -0
  33. package/dist/adapter/value/parsed-literal.d.ts +28 -0
  34. package/dist/adapter/value/parsed-literal.d.ts.map +1 -0
  35. package/dist/build.d.ts +28 -0
  36. package/dist/build.d.ts.map +1 -0
  37. package/dist/build.js +189117 -0
  38. package/dist/conformance-pins.d.ts +12 -0
  39. package/dist/conformance-pins.d.ts.map +1 -0
  40. package/dist/index.d.ts +9 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +189118 -0
  43. package/package.json +66 -0
  44. package/python/VERSION +1 -0
  45. package/python/barefootjs/__init__.py +57 -0
  46. package/python/barefootjs/backend_jinja.py +125 -0
  47. package/python/barefootjs/evaluator.py +787 -0
  48. package/python/barefootjs/runtime.py +1334 -0
  49. package/python/barefootjs/search_params.py +89 -0
  50. package/python/pyproject.toml +32 -0
  51. package/python/tests/__init__.py +0 -0
  52. package/python/tests/test_eval_vectors.py +88 -0
  53. package/python/tests/test_evaluator.py +406 -0
  54. package/python/tests/test_helper_vectors.py +288 -0
  55. package/python/tests/test_omit.py +62 -0
  56. package/python/tests/test_props_attr.py +54 -0
  57. package/python/tests/test_query.py +41 -0
  58. package/python/tests/test_render.py +102 -0
  59. package/python/tests/test_render_child.py +96 -0
  60. package/python/tests/test_search_params.py +50 -0
  61. package/python/tests/test_spread_attrs.py +86 -0
  62. package/python/tests/test_template_primitives.py +347 -0
  63. package/python/tests/vector-divergences.json +42 -0
  64. package/src/__tests__/jinja-adapter-unit.test.ts +390 -0
  65. package/src/__tests__/jinja-adapter.test.ts +53 -0
  66. package/src/__tests__/jinja-counter.test.ts +62 -0
  67. package/src/__tests__/jinja-query-href.test.ts +99 -0
  68. package/src/__tests__/jinja-spread-attrs.test.ts +225 -0
  69. package/src/adapter/analysis/component-tree.ts +119 -0
  70. package/src/adapter/boolean-result.ts +176 -0
  71. package/src/adapter/emit-context.ts +118 -0
  72. package/src/adapter/expr/array-method.ts +346 -0
  73. package/src/adapter/expr/emitters.ts +608 -0
  74. package/src/adapter/expr/operand.ts +35 -0
  75. package/src/adapter/index.ts +6 -0
  76. package/src/adapter/jinja-adapter.ts +1747 -0
  77. package/src/adapter/lib/constants.ts +33 -0
  78. package/src/adapter/lib/ir-scope.ts +95 -0
  79. package/src/adapter/lib/jinja-naming.ts +114 -0
  80. package/src/adapter/lib/types.ts +30 -0
  81. package/src/adapter/memo/seed.ts +132 -0
  82. package/src/adapter/props/prop-classes.ts +65 -0
  83. package/src/adapter/spread/spread-codegen.ts +166 -0
  84. package/src/adapter/value/parsed-literal.ts +76 -0
  85. package/src/build.ts +37 -0
  86. package/src/conformance-pins.ts +101 -0
  87. package/src/index.ts +9 -0
  88. package/src/test-render.ts +714 -0
@@ -0,0 +1,394 @@
1
+ /**
2
+ * BarefootJS Jinja2 Template Adapter
3
+ *
4
+ * Generates Jinja2 template files (.jinja) from BarefootJS IR.
5
+ *
6
+ * Near-mechanical port of the Text::Xslate (Kolon) adapter
7
+ * (packages/adapter-xslate/src/adapter/xslate-adapter.ts), itself a
8
+ * near-mechanical port of the Mojolicious EP adapter, from Kolon syntax to
9
+ * Jinja2 syntax. The expression-lowering PIPELINE (JS → scalar values /
10
+ * `bf.helper(...)` calls) is shared in spirit with the Xslate adapter; the
11
+ * surrounding template syntax differs:
12
+ *
13
+ * Kolon `<: EXPR :>` → Jinja `{{ EXPR }}` (HTML-escaped)
14
+ * Kolon `<: EXPR | mark_raw :>` → Jinja `{{ EXPR | safe }}` (raw)
15
+ * Kolon `$bf.method(args)` → Jinja `bf.method(args)`
16
+ * Kolon `$name` → Jinja `name`
17
+ * Kolon `: if (C) { A : } else { B : }` → Jinja `{% if C %}A{% else %}B{% endif %}` (`elsif` → `{% elif %}`)
18
+ * Kolon `: for $arr -> $item { … : }` → Jinja `{% for item in arr %}…{% endfor %}`
19
+ * Kolon `: my $x = e;` → Jinja `{% set x = e %}`
20
+ * Kolon `{ k => v }` hashref → Jinja `{'k': v}` dict literal (ALWAYS quoted key — see `lib/jinja-naming.ts`)
21
+ * Kolon `~` concat → Jinja `~` concat
22
+ * Kolon `//` defined-or → `(l if (l is defined and l is not none) else r)` inline (Jinja has no `//`; the `is defined` guard also treats a context var that was never seeded — Jinja's `ChainableUndefined` — as nullish, matching JS `??`'s null-OR-undefined check — see `expr/emitters.ts`'s `logical` for the full rationale)
23
+ * Kolon macro children capture → Jinja `{% set NAME %}…{% endset %}` set-block (see "Children capture" below)
24
+ *
25
+ * The render `jinja2.Environment` this adapter's output assumes:
26
+ * `autoescape=True`, `undefined=ChainableUndefined`, `FileSystemLoader`,
27
+ * PLUS **`trim_blocks=True, lstrip_blocks=True`** — required because this
28
+ * adapter places `{% … %}` control tags on their own source line (mirroring
29
+ * Kolon's line-statement `:` mode, which consumes its own line for free);
30
+ * without `trim_blocks`/`lstrip_blocks` every such line would leak a stray
31
+ * newline/indentation into the rendered HTML. Templates are named
32
+ * `<snake_case_component>.jinja` (same convention as Xslate's `.tx` files).
33
+ *
34
+ * Divergences beyond the syntax table above (all uniform, not per-fixture —
35
+ * see the individual definition sites for the full rationale):
36
+ *
37
+ * 1. **JS truthiness** (`boolean-result.ts`, `expr/emitters.ts`'s
38
+ * `truthyTest`, this file's `convertConditionToJinja`). Python's `[]` /
39
+ * `{}` are falsy; JS's are truthy. Perl doesn't have this problem (a
40
+ * Perl reference is always true), so Kolon needed no truthy-routing
41
+ * layer. Every condition-TEST position (an `{% if %}` / `{% elif %}`
42
+ * test, a ternary test, the left operand of `&&`/`||`) routes through
43
+ * `bf.truthy(...)` unless it is structurally already boolean-shaped.
44
+ * 2. **Stringification** (`bf.string`, applied at every text/attribute
45
+ * interpolation position). Perl's default scalar stringification is
46
+ * close enough to JS's `String(x)` that Kolon only special-cases
47
+ * explicit `String()` calls and boolean-typed values (routed to
48
+ * `bf.bool_str`). Python's default `str()` diverges further —
49
+ * `str(True)` == `"True"`, `str(1.0)` == `"1.0"`, `str(None)` ==
50
+ * `"None"`, and Jinja's `~` concatenation operator calls `str()` on
51
+ * each operand internally — so this port explicitly routes EVERY
52
+ * text/attribute-position value (not already boolean-routed) through
53
+ * `bf.string(...)` before it reaches Jinja's own escaping/concat
54
+ * machinery. Verified empirically (`'a' ~ true ~ none` → `"aTrueNone"`
55
+ * under plain Jinja) — the reason this wrapping is mandatory, not
56
+ * cosmetic.
57
+ * 3. **No Jinja lambda** (`expr/emitters.ts` header, divergence 2). Kolon's
58
+ * `-> $x { … }` lambda — the Xslate top-level emitter's fallback when a
59
+ * predicate callback can't be serialized to the runtime evaluator's
60
+ * JSON form — has no Jinja equivalent. This adapter uses ONE mechanism
61
+ * for every higher-order callback (the evaluator-JSON `*_eval`
62
+ * payload); an unserializable predicate surfaces `BF101` instead of a
63
+ * lambda fallback. `.sort`'s non-lambda STRUCTURED fallback
64
+ * (`bf.sort` with a `{keys: […]}` descriptor) is unaffected and ports
65
+ * unchanged.
66
+ * 4. **Children/fallback capture via `{% set %}...{% endset %}`, never a
67
+ * macro.** Every Kolon macro-capture site in the ported adapter
68
+ * (`renderComponent`'s children forward, `renderAsync`'s fallback) is
69
+ * invoked immediately, in place, with zero arguments — never reused
70
+ * elsewhere or invoked lazily with different arguments. Jinja's
71
+ * set-block (`{% set NAME %}…{% endset %}`) captures exactly that
72
+ * shape as a `Markup` string (under `autoescape=True`) with no macro
73
+ * indirection needed; the captured name is then referenced bare
74
+ * (`NAME`, not `NAME()`) everywhere the Kolon port called `NAME()`.
75
+ * 5. **Reserved-word identifier mangling** (`lib/jinja-naming.ts`). Every
76
+ * bare Jinja variable reference / `{% set %}` target is passed through
77
+ * `jinjaIdent()`; the Python runtime must apply the IDENTICAL mangling
78
+ * when it builds the per-render context dict (so a prop literally
79
+ * named e.g. `class` is threaded through as context key `'class_'` on
80
+ * both sides). Dict-LITERAL keys are a separate, unconditional
81
+ * concern — see `jinjaHashKey`'s docstring for why they are always
82
+ * quoted (unlike Kolon's bareword-key sugar).
83
+ * 6. **In-template signal/memo self-reference seeding is NOT skipped**
84
+ * (`memo/seed.ts`'s file header) — Jinja's `{% set x = x + 1 %}` safely
85
+ * resolves the right-hand `x` from the enclosing scope (verified
86
+ * empirically), unlike Kolon's `my`-shadowing hazard, so a same-name
87
+ * prop-derived signal/memo IS seeded in-template here (Xslate skips
88
+ * it). Strictly more correct, not merely a port artifact.
89
+ */
90
+ import type { ComponentIR, IRNode, IRElement, IRText, IRExpression, IRConditional, IRLoop, IRComponent, IRFragment, IRSlot, IRIfStatement, IRProvider, IRAsync, TemplatePrimitiveRegistry } from '@barefootjs/jsx';
91
+ import { BaseAdapter, type AdapterOutput, type AdapterGenerateOptions, type IRNodeEmitter, type EmitIRNode } from '@barefootjs/jsx';
92
+ import type { JinjaRenderCtx } from './lib/types.ts';
93
+ export type { JinjaAdapterOptions } from './lib/types.ts';
94
+ import type { JinjaAdapterOptions } from './lib/types.ts';
95
+ export declare class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRenderCtx> {
96
+ name: string;
97
+ extension: string;
98
+ templatesPerComponent: boolean;
99
+ importMapInjection: 'html-snippet';
100
+ /**
101
+ * Identifier-path callees the Jinja runtime can render in template scope.
102
+ * The relocate pass consults this map to mark matching calls as
103
+ * template-safe; the SSR template emitter substitutes the JS call with the
104
+ * registered `bf.NAME(...)` helper invocation.
105
+ */
106
+ templatePrimitives: TemplatePrimitiveRegistry;
107
+ private componentName;
108
+ /** Component root scope element(s) — each carries `data-key` for a keyed loop
109
+ * item (set by the child renderer from the JSX `key` prop). A plain element
110
+ * root is one node; an `if-statement` (early-return) root contributes the
111
+ * top element of every branch. */
112
+ private rootScopeNodes;
113
+ private options;
114
+ private errors;
115
+ private inLoop;
116
+ /**
117
+ * SolidJS-style props identifier (`function(props: P)`) and the
118
+ * analyzer-extracted prop names. Stashed at `generate()` entry so the
119
+ * per-attribute `emitSpread` callback can build a propsObject spread bag as
120
+ * an inline Jinja dict literal without re-walking the IR.
121
+ */
122
+ private propsObjectName;
123
+ private propsParams;
124
+ private booleanTypedProps;
125
+ /**
126
+ * Names (signal getters + props) whose value is a string. Carried for
127
+ * parity with the Perl-family adapters (Mojo needs it for `eq`/`ne`
128
+ * selection); the Jinja emitters don't consume it — Jinja's `==`/`!=`
129
+ * compare strings and numbers correctly.
130
+ */
131
+ private stringValueNames;
132
+ /**
133
+ * Module-scope pure-string consts (`const x = 'literal'`), keyed by name →
134
+ * unescaped value. A className template literal that references such a const
135
+ * (`className={`${x} ${className}`}`) must inline the literal: the const is
136
+ * module-scope, so it never reaches the per-render context, and a bare
137
+ * reference to `x` would resolve to Undefined.
138
+ */
139
+ private moduleStringConsts;
140
+ /**
141
+ * (#1922) Local binding names the request-scoped `searchParams()` env signal
142
+ * is imported under (handles `import { searchParams as sp }`). When non-empty
143
+ * the emitter lowers a `<binding>().get(k)` call to a real method call on the
144
+ * per-request `searchParams` reader (`searchParams.get('sort')`) instead of
145
+ * the generic dot deref. Set at `generate()` entry from `ir.metadata.imports`;
146
+ * read by the top-level ParsedExpr emitter.
147
+ */
148
+ private _searchParamsLocals;
149
+ /**
150
+ * Call-lowering matchers active for this component (#2057). Bound at
151
+ * `generate()` entry via `prepareLoweringMatchers` and read by the top-level
152
+ * emitter. Covers both userland plugins and the compiler's built-in plugins
153
+ * (e.g. `queryHref` → `bf.query`, #2042) — one uniform path, no per-API branch.
154
+ */
155
+ private _loweringMatchers;
156
+ /**
157
+ * Local + module constants from the IR, used by the conditional-spread and
158
+ * `Record<staticKeys, scalar>[propKey]` lowering paths (#textarea / #checkbox).
159
+ * Stashed at `generate()` entry so `emitSpread` can resolve a bare local
160
+ * const (`const sizeAttrs = size ? {…} : {}`) to its initializer text.
161
+ */
162
+ private localConstants;
163
+ /**
164
+ * Optional, no-default props that are `None` when the caller omits them.
165
+ * Their bare-reference attribute emission is guarded with a Jinja
166
+ * `is defined and is not none` test so the attribute DROPS rather than
167
+ * rendering `attr=""` (Hono-style nullish omission, e.g. textarea's
168
+ * `rows`). The filter excludes destructure-defaulted, rest, and
169
+ * concrete-primitive props.
170
+ */
171
+ private nullableOptionalProps;
172
+ constructor(options?: JinjaAdapterOptions);
173
+ generate(ir: ComponentIR, options?: AdapterGenerateOptions): AdapterOutput;
174
+ private generateScriptRegistrations;
175
+ /**
176
+ * Public entry point for node rendering. Delegates to the shared
177
+ * `IRNodeEmitter` dispatcher; per-kind logic lives in the `IRNodeEmitter`
178
+ * methods below.
179
+ */
180
+ renderNode(node: IRNode): string;
181
+ emitElement(node: IRElement, _ctx: JinjaRenderCtx, _emit: EmitIRNode<JinjaRenderCtx>): string;
182
+ emitText(node: IRText): string;
183
+ emitExpression(node: IRExpression): string;
184
+ emitConditional(node: IRConditional, _ctx: JinjaRenderCtx, _emit: EmitIRNode<JinjaRenderCtx>): string;
185
+ emitLoop(node: IRLoop, _ctx: JinjaRenderCtx, _emit: EmitIRNode<JinjaRenderCtx>): string;
186
+ emitComponent(node: IRComponent, _ctx: JinjaRenderCtx, _emit: EmitIRNode<JinjaRenderCtx>): string;
187
+ emitFragment(node: IRFragment, _ctx: JinjaRenderCtx, _emit: EmitIRNode<JinjaRenderCtx>): string;
188
+ emitSlot(node: IRSlot): string;
189
+ emitIfStatement(node: IRIfStatement, _ctx: JinjaRenderCtx, _emit: EmitIRNode<JinjaRenderCtx>): string;
190
+ emitProvider(node: IRProvider, _ctx: JinjaRenderCtx, _emit: EmitIRNode<JinjaRenderCtx>): string;
191
+ /** Lower a `<Ctx.Provider value>` value prop to a Jinja expression. */
192
+ private providerValueJinja;
193
+ /**
194
+ * Lower an object-literal provider value (`value={{ open: () => props.open
195
+ * ?? false, onOpenChange: … }}`) to a Jinja dict literal (#1897). The
196
+ * SSR lowering is a per-member snapshot of what a consumer would READ
197
+ * during the same render:
198
+ *
199
+ * - zero-param expression-body arrows are getters — lower the body (the
200
+ * value is fixed for the render, so the call-time indirection drops out)
201
+ * - `on[A-Z]`-named members and function-shaped values are client-only
202
+ * behavior SSR never invokes — lower to `none`
203
+ * - anything else lowers through the normal expression pipeline (so an
204
+ * unsupported getter body still refuses loudly with BF101)
205
+ *
206
+ * Keys keep their JS names verbatim so a consumer-side `ctx.open` access
207
+ * maps onto the same dict key. Returns `null` when the expression is not a
208
+ * plain object literal (spread / computed key) — the caller falls back to
209
+ * the whole-expression path, which refuses those shapes with BF101.
210
+ */
211
+ private providerObjectLiteralJinja;
212
+ emitAsync(node: IRAsync, _ctx: JinjaRenderCtx, _emit: EmitIRNode<JinjaRenderCtx>): string;
213
+ renderElement(element: IRElement): string;
214
+ renderExpression(expr: IRExpression): string;
215
+ renderConditional(cond: IRConditional): string;
216
+ private renderNodeOrNull;
217
+ /**
218
+ * Add bf-c attribute to the first HTML element in a branch.
219
+ * If no element found, wrap with comment markers.
220
+ */
221
+ private addCondMarkerToFirstElement;
222
+ renderLoop(loop: IRLoop): string;
223
+ /**
224
+ * AttrValue lowering for component invocation props (Jinja dict-entry
225
+ * form). Jinja CANNOT splat a dict into positional args, so every prop is
226
+ * emitted as a `'key': value` entry that the caller collects into ONE dict
227
+ * literal passed to `bf.render_child(name, { ... })`.
228
+ *
229
+ * `jsx-children` returns empty — children are captured via a Jinja
230
+ * set-block below, not threaded through the dict entry list.
231
+ */
232
+ private readonly componentPropEmitter;
233
+ /**
234
+ * A `renderComponent` props dict, built as an ORDERED sequence of
235
+ * segments so `{...before, ...spread, after: 1}` JSX spread semantics
236
+ * (later entries win) survive the trip through Jinja, which has no
237
+ * dict-splat syntax for anything past a SINGLE `**` per `dict(...)`
238
+ * call. Each `'entries'` segment is a literal Jinja dict `{'k': v, ...}`;
239
+ * each `'spread'` segment is an arbitrary expression lowered from a
240
+ * `{...expr}` prop. `combineComponentPropSegments` folds the sequence
241
+ * into ONE expression via nested `dict(base, **top)` calls (later
242
+ * segment wins on key conflict, matching `Object.assign`/JSX order).
243
+ */
244
+ private componentPropSegmentEntries;
245
+ /**
246
+ * Fold ordered prop segments into a single Jinja expression via nested
247
+ * `dict(base, **top)` calls — CPython's `dict()` builtin raises
248
+ * `TemplateSyntaxError` on MORE THAN ONE `**` per call, so a flat
249
+ * `dict(**a, **b, **c)` is not an option; each successive segment wraps
250
+ * the accumulator as the positional `base` with the new segment
251
+ * `**`-unpacked on top, later argument wins on key conflict — exactly
252
+ * like `{...a, ...b}`. A spread segment's expression is wrapped
253
+ * `(EXPR or {})` before unpacking: Jinja's `ChainableUndefined` lets a
254
+ * missing bag (e.g. `children.props` when `children` was never passed)
255
+ * chain through member access without raising, but `**`-unpacking an
256
+ * `Undefined`/`None` value still fails, so the `or {}` guard normalises
257
+ * it to an empty dict first (verified against real jinja2 3.1.6).
258
+ * Empty `'entries'` segments are dropped so a leading/trailing spread
259
+ * doesn't drag in a needless `dict({}, **...)`. Returns `'{}'` when
260
+ * every segment is empty (no props at all).
261
+ */
262
+ private combineComponentPropSegments;
263
+ renderComponent(comp: IRComponent): string;
264
+ private childrenCaptureCounter;
265
+ /** Uniquifies the `presenceOrUndefined` temp binding (`bf_puN`) so two
266
+ * presence-folded attrs in one template don't collide. */
267
+ private presenceVarCounter;
268
+ private toTemplateName;
269
+ private renderIfStatement;
270
+ private renderFragment;
271
+ private renderSlot;
272
+ renderAsync(node: IRAsync): string;
273
+ /**
274
+ * AttrValue lowering for intrinsic-element attributes (Jinja).
275
+ */
276
+ private readonly elementAttrEmitter;
277
+ /**
278
+ * Lower a `style={{ … }}` object literal to a CSS string with dynamic values
279
+ * interpolated as Jinja expressions, e.g. `{ backgroundColor: color }` →
280
+ * `background-color:{{ bf.string(color) }}`. Returns null when the shape is
281
+ * unsupported or any value can't be lowered (caller falls through to
282
+ * BF101). (#1322)
283
+ */
284
+ private tryLowerStyleObject;
285
+ /** HTML-attribute escape for static text inlined into a `"..."` attribute. */
286
+ private escapeAttrText;
287
+ private renderAttributes;
288
+ renderScopeMarker(_instanceIdExpr: string): string;
289
+ renderSlotMarker(slotId: string): string;
290
+ renderCondMarker(condId: string): string;
291
+ /**
292
+ * Convert a ParsedExpr AST to a Jinja expression string for filter
293
+ * predicates. Wraps the shared ParsedExpr dispatcher with a
294
+ * `JinjaFilterEmitter` carrying the predicate's loop param and any
295
+ * block-body local var aliases.
296
+ */
297
+ private renderJinjaFilterExpr;
298
+ private convertTemplateLiteralPartsToJinja;
299
+ /**
300
+ * Translate `${EXPR}` interpolations in a static template-part string into
301
+ * Jinja variable references and concatenate them with the surrounding
302
+ * literal text. Each interpolated (non-literal) segment routes through
303
+ * `bf.string(...)` — see the file header, divergence 2.
304
+ */
305
+ private substituteJsInterpolationsToJinja;
306
+ /**
307
+ * Refuse JS expression shapes that have no idiomatic Jinja representation:
308
+ * object literals (`style={{...}}`) and tagged-template-literal call
309
+ * expressions (`cn\`base \${tone()}\``). Records `BF101`. Returns `true`
310
+ * when the shape was rejected (caller should drop the attribute).
311
+ */
312
+ private refuseUnsupportedAttrExpression;
313
+ /**
314
+ * Build the EmitContext seam the top-level `ParsedExpr` emitter depends on.
315
+ * Built as a private object (the adapter does NOT `implements JinjaEmitContext`)
316
+ * so the wrapped bookkeeping — `_searchParamsLocals`, the const/record
317
+ * resolvers, BF101 recording, the filter-predicate entry — stays private and
318
+ * off the exported adapter's public type, matching the Go adapter's
319
+ * `emitCtx` and the `spreadCtx` / `memoCtx` seams below.
320
+ */
321
+ private get emitCtx();
322
+ /**
323
+ * Build the narrow context the extracted spread lowering depends on. Passing
324
+ * a purpose-built object (rather than `this`) keeps the adapter's bookkeeping
325
+ * members private — they stay internal implementation detail, not part of the
326
+ * exported class's public surface.
327
+ */
328
+ private get spreadCtx();
329
+ /** Build the narrow context the extracted memo seeding depends on. */
330
+ private get memoCtx();
331
+ private convertExpressionToJinja;
332
+ /**
333
+ * Convert a JS condition (an `if` / ternary / loop-filter test) to a Jinja
334
+ * boolean expression, routing through `bf.truthy(...)` unless the
335
+ * expression is structurally already boolean-shaped. See the file header,
336
+ * divergence 1.
337
+ */
338
+ private convertConditionToJinja;
339
+ /**
340
+ * Shared helper: given the ORIGINAL JS expression (or its already-parsed
341
+ * tree) and its ALREADY-RENDERED Jinja text, wrap the rendered text with
342
+ * `bf.truthy(...)` unless the expression is structurally boolean-shaped.
343
+ * Split from `convertConditionToJinja` so a caller that already lowered the
344
+ * expression for another purpose (e.g. the `presenceOrUndefined` temp bind)
345
+ * doesn't lower it twice.
346
+ */
347
+ private wrapConditionExpr;
348
+ /**
349
+ * Render a full ParsedExpr tree to Jinja for top-level (non-filter)
350
+ * expressions where identifiers are signals / template vars.
351
+ */
352
+ private renderParsedExprToJinja;
353
+ /** Whether `name` (a signal getter or prop) holds a string value. Carried
354
+ * for parity with the Perl-family adapters; the Jinja emitters don't
355
+ * consume it (Jinja's `==`/`!=` compare strings and numbers correctly). */
356
+ private _isStringValueName;
357
+ /**
358
+ * Parse `cond ? value : undefined` (or `: null`), returning the
359
+ * condition/consequent source spans, else `null`. Used for the
360
+ * attribute-omission rule (#1897).
361
+ */
362
+ parseUndefinedAlternateTernary(expr: string): {
363
+ condition: string;
364
+ consequent: string;
365
+ } | null;
366
+ isBooleanTypedPropRef(expr: string): boolean;
367
+ /**
368
+ * Whether an attribute-value expression should route through
369
+ * `bf.bool_str` (vs. plain `bf.string`) at its interpolation site.
370
+ * `isExplicitStringCall` is checked FIRST and short-circuits the other
371
+ * three: an explicit `String(x)` call already lowers to `bf.string(x)`,
372
+ * which — unlike Kolon's Perl port — correctly stringifies a real
373
+ * boolean on its own (see `runtime.js_string`'s bool branch), so
374
+ * layering `bf.bool_str` on top would run Python truthiness over the
375
+ * ALREADY-STRINGIFIED text instead of the original boolean. See
376
+ * `isExplicitStringCall`'s docstring in `boolean-result.ts` for the full
377
+ * double-wrap failure mode this guards against.
378
+ */
379
+ private shouldBoolStr;
380
+ /**
381
+ * Inline a const (any scope) whose initializer is a pure numeric or
382
+ * single-quoted string literal (`const totalPages = 5`, #1897
383
+ * pagination) — function-scope consts never reach the per-render
384
+ * context, so a bare reference would resolve to Undefined.
385
+ */
386
+ private _resolveLiteralConst;
387
+ private _resolveStaticRecordLiteral;
388
+ private _resolveModuleStringConst;
389
+ private _recordExprBF101;
390
+ /** Internal hook for higher-order: predicate body re-uses the filter emitter. */
391
+ private _renderJinjaFilterExprPublic;
392
+ }
393
+ export declare const jinjaAdapter: JinjaAdapter;
394
+ //# sourceMappingURL=jinja-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jinja-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/jinja-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Compile-time constant tables for the Jinja2 template adapter.
3
+ *
4
+ * Extracted the way `packages/adapter-xslate/src/adapter/lib/constants.ts` is.
5
+ */
6
+ import type { PrimitiveSpec } from './types.ts';
7
+ /**
8
+ * Single source of truth for the Jinja adapter's template-primitive
9
+ * surface. Each entry pairs the expected arity with the emit function.
10
+ *
11
+ * The emit fn returns a Jinja expression (no surrounding `{{ }}`) suitable
12
+ * for embedding inside an interpolation — `bf.json(val)`, `bf.floor(val)`,
13
+ * etc. The same primitive names as the Xslate adapter, but invoked as
14
+ * `bf.NAME(args)` (bare, no `$` sigil) instead of `$bf.NAME(args)`.
15
+ */
16
+ export declare const JINJA_TEMPLATE_PRIMITIVES: Record<string, PrimitiveSpec>;
17
+ /**
18
+ * Module-scope `templatePrimitives` map derived once from the spec record.
19
+ */
20
+ export declare const JINJA_PRIMITIVE_EMIT_MAP: Record<string, (args: string[]) => string>;
21
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAOnE,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAG7E,CAAA"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * IR traversal helpers for the Jinja2 template adapter.
3
+ *
4
+ * Ported from `packages/adapter-xslate/src/adapter/lib/ir-scope.ts`.
5
+ * `resolveJsxChildrenProp` and `collectRootScopeNodes` are byte-identical
6
+ * (adapter-agnostic IR walks).
7
+ *
8
+ * `extractTopLevelIdentifiers` scans the RENDERED template text (rather than
9
+ * re-deriving free vars from the original JS AST) so it stays exactly in
10
+ * sync with whatever the emitter actually produced — but Jinja identifiers
11
+ * have no sigil (unlike Kolon's `$`, which made a trivially safe
12
+ * `\$([A-Za-z_]\w*)` scan possible): a bare word in the rendered text could
13
+ * be a genuine context-var reference, a `bf.` runtime-helper method name, a
14
+ * Jinja grammar keyword emitted by this adapter's own condition/ternary
15
+ * lowering (`if`/`else`/`is`/`not`/`and`/`or`/`none`/`true`/`false`), or
16
+ * content inside a single-quoted string literal. This helper makes the scan
17
+ * sound: it strips quoted string spans first, then matches identifier tokens
18
+ * NOT immediately preceded by a `.` (excluding dotted property/method names
19
+ * — the same exclusion the `$` sigil gave Kolon for free, since Kolon's
20
+ * regex only ever matched right after `$`), then drops the closed set of
21
+ * tokens this adapter's own codegen can emit that aren't context vars (`bf`
22
+ * and the Jinja keywords above). `memo/seed.ts` uses it to detect a
23
+ * constant lowering (no top-level identifier at all) that should keep the
24
+ * static ssr-defaults seed instead of an in-template `{% set %}`; scope
25
+ * AVAILABILITY itself is now the shared `computeSsrSeedPlan`'s job
26
+ * (packages/jsx/src/ssr-seed-plan.ts), not this module's (#2075).
27
+ */
28
+ import type { IRNode, IRProp } from '@barefootjs/jsx';
29
+ /**
30
+ * Extract the set of "top-level identifier" tokens from a rendered Jinja
31
+ * expression: bare words, excluding quoted-string content, dotted
32
+ * property/method names, and this adapter's own non-var keyword vocabulary.
33
+ * See the file header for why this replaces a direct `\w+` scan.
34
+ */
35
+ 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
+ //# sourceMappingURL=ir-scope.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Jinja2/Python identifier and dict-key conventions for the Jinja adapter.
3
+ *
4
+ * Pure helpers, ported from the Kolon equivalent
5
+ * (`packages/adapter-xslate/src/adapter/lib/kolon-naming.ts`) but adjusted for
6
+ * two real syntax divergences between Kolon and Jinja2:
7
+ *
8
+ * 1. **Dict-literal keys are always quoted.** Kolon's hashref fat-comma
9
+ * (`key => value`) auto-quotes a bareword key, so `kolonHashKey` only
10
+ * quotes a non-identifier-safe name (`'data-slot'`). A Jinja/Python dict
11
+ * LITERAL has no such bareword-key sugar — `{key: value}` means "look up
12
+ * the *variable* `key` and use its value as the key", not the string
13
+ * `"key"`. `jinjaHashKey` therefore ALWAYS emits a quoted string literal,
14
+ * identifier-safe or not — the one place this port is NOT a bare 1:1
15
+ * syntax substitution, because treating it as one would silently change
16
+ * dict keys into undefined-variable lookups.
17
+ * 2. **Reserved-word identifier mangling.** Verified empirically against
18
+ * Jinja 3.1 that most Python keywords (`class`, `import`, `for`, …) parse
19
+ * fine as bare Jinja variable / loop-target names — Jinja compiles every
20
+ * user template name to an internally-prefixed Python local (`l_0_…`), so
21
+ * the template layer itself mostly sidesteps Python's own reserved-word
22
+ * rules. The genuinely Jinja-reserved bare words are `not` / `and` / `or`
23
+ * / `in` / `is` / `if` / `else` (grammar keywords) plus the three
24
+ * constants `true` / `false` / `none` (rejected specifically as
25
+ * assignment targets). Per the adapter plan, `jinjaIdent` mangles the
26
+ * FULL Python reserved-word list (a conservative superset of what Jinja's
27
+ * grammar strictly requires) with a trailing-underscore suffix, trivial
28
+ * and easy to keep in lock-step with the Python runtime's identical rule
29
+ * (`barefootjs.runtime.jinja_ident`) — the runtime applies the SAME
30
+ * mangling when it builds the per-render context dict, so a prop/signal
31
+ * named e.g. `class` is threaded through as context key `'class_'` on
32
+ * both sides.
33
+ */
34
+ import type { LoopBindingPathSegment } from '@barefootjs/jsx';
35
+ /**
36
+ * Escape a string for a Jinja/Python single-quoted literal: backslash first
37
+ * (so it doesn't double-escape the quote we add next), then the quote.
38
+ * Python string-literal escaping treats `\\` / `\'` identically to Perl's,
39
+ * so this is byte-identical to `escapeKolonSingleQuoted`.
40
+ */
41
+ export declare function escapeJinjaSingleQuoted(s: string): string;
42
+ /**
43
+ * Quote a dict-literal KEY for Jinja. UNLIKE Kolon's `kolonHashKey`, this
44
+ * always quotes — see the file header for why a bareword key would silently
45
+ * become a variable lookup instead of a string key.
46
+ */
47
+ export declare function jinjaHashKey(name: string): string;
48
+ /**
49
+ * Mangle a JS identifier (prop name, signal getter, loop param, …) into a
50
+ * Jinja-safe variable name: reserved words get a trailing `_` suffix,
51
+ * everything else passes through unchanged. Applied at every point the
52
+ * adapter emits a bare Jinja variable reference or `{% set %}` target —
53
+ * mirrors Kolon's `$name` sigil giving those references collision immunity
54
+ * for free; Jinja has no sigil, so the adapter mangles instead.
55
+ */
56
+ export declare function jinjaIdent(name: string): string;
57
+ /**
58
+ * Build a native Jinja accessor expression from a `.map()` destructure
59
+ * binding's structured {@link LoopBindingPathSegment} path (#2087 Phase B) —
60
+ * the per-adapter counterpart to the JS accessor suffix carried in
61
+ * `LoopParamBinding.path`. Walks `segments` (never string-parses `path` —
62
+ * repo rule) appending, per step:
63
+ *
64
+ * - `{ kind: 'index', index }` → `[N]` (a Python list index — same postfix
65
+ * Jinja uses for dict-key lookup, so this is unambiguous either way).
66
+ * - `{ kind: 'field', key, isIdent: true }` → `.key` (bare attribute
67
+ * access — Jinja resolves it against a dict transparently).
68
+ * - `{ kind: 'field', key, isIdent: false }` → `[<quoted key>]` (e.g. a
69
+ * `'data-priority'` sibling key can't be a bare Jinja attribute, same
70
+ * reasoning as `jinjaHashKey` on dict-literal keys).
71
+ *
72
+ * `base` is the already-`jinjaIdent`-mangled loop variable (or a parent
73
+ * accessor expression when called recursively); an empty `segments` array
74
+ * (a rest binding sitting at the loop root) returns `base` unchanged.
75
+ */
76
+ export declare function jinjaAccessorFromSegments(base: string, segments: readonly LoopBindingPathSegment[]): string;
77
+ //# sourceMappingURL=jinja-naming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jinja-naming.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/jinja-naming.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAE7D;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAcD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,SAAS,sBAAsB,EAAE,GAC1C,MAAM,CAYR"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared type aliases for the Jinja2 template adapter.
3
+ *
4
+ * Extracted the way `packages/adapter-xslate/src/adapter/lib/types.ts` is:
5
+ * pure type declarations — no runtime behaviour — so the extracted emit
6
+ * modules and the main adapter share one definition rather than
7
+ * re-declaring the render context / options shape.
8
+ */
9
+ /** A template-primitive spec: expected call arity + the emit fn. */
10
+ export interface PrimitiveSpec {
11
+ arity: number;
12
+ emit: (args: string[]) => string;
13
+ }
14
+ /**
15
+ * Jinja adapter's IRNode render context. Like the Xslate adapter, Jinja's
16
+ * lowering doesn't consume any render-position flags, so the Ctx is empty.
17
+ * Kept as a named alias so future flags can extend it without changing the
18
+ * `IRNodeEmitter` interface.
19
+ */
20
+ export type JinjaRenderCtx = Record<string, never>;
21
+ export interface JinjaAdapterOptions {
22
+ /** Base path for client JS files (default: '/static/components/') */
23
+ clientJsBasePath?: string;
24
+ /** Path to barefoot.js runtime (default: '/static/components/barefoot.js') */
25
+ barefootJsPath?: string;
26
+ }
27
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAA;CACjC;AAED;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;AAElD,MAAM,WAAW,mBAAmB;IAClC,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAEzB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB"}