@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.
- package/README.md +66 -0
- package/dist/adapter/analysis/component-tree.d.ts +26 -0
- package/dist/adapter/analysis/component-tree.d.ts.map +1 -0
- package/dist/adapter/boolean-result.d.ts +84 -0
- package/dist/adapter/boolean-result.d.ts.map +1 -0
- package/dist/adapter/emit-context.d.ts +106 -0
- package/dist/adapter/emit-context.d.ts.map +1 -0
- package/dist/adapter/expr/array-method.d.ts +75 -0
- package/dist/adapter/expr/array-method.d.ts.map +1 -0
- package/dist/adapter/expr/emitters.d.ts +143 -0
- package/dist/adapter/expr/emitters.d.ts.map +1 -0
- package/dist/adapter/expr/operand.d.ts +25 -0
- package/dist/adapter/expr/operand.d.ts.map +1 -0
- package/dist/adapter/index.d.ts +6 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +189097 -0
- package/dist/adapter/jinja-adapter.d.ts +394 -0
- package/dist/adapter/jinja-adapter.d.ts.map +1 -0
- package/dist/adapter/lib/constants.d.ts +21 -0
- package/dist/adapter/lib/constants.d.ts.map +1 -0
- package/dist/adapter/lib/ir-scope.d.ts +50 -0
- package/dist/adapter/lib/ir-scope.d.ts.map +1 -0
- package/dist/adapter/lib/jinja-naming.d.ts +77 -0
- package/dist/adapter/lib/jinja-naming.d.ts.map +1 -0
- package/dist/adapter/lib/types.d.ts +27 -0
- package/dist/adapter/lib/types.d.ts.map +1 -0
- package/dist/adapter/memo/seed.d.ts +81 -0
- package/dist/adapter/memo/seed.d.ts.map +1 -0
- package/dist/adapter/props/prop-classes.d.ts +33 -0
- package/dist/adapter/props/prop-classes.d.ts.map +1 -0
- package/dist/adapter/spread/spread-codegen.d.ts +61 -0
- package/dist/adapter/spread/spread-codegen.d.ts.map +1 -0
- package/dist/adapter/value/parsed-literal.d.ts +28 -0
- package/dist/adapter/value/parsed-literal.d.ts.map +1 -0
- package/dist/build.d.ts +28 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +189117 -0
- package/dist/conformance-pins.d.ts +12 -0
- package/dist/conformance-pins.d.ts.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +189118 -0
- package/package.json +66 -0
- package/python/VERSION +1 -0
- package/python/barefootjs/__init__.py +57 -0
- package/python/barefootjs/backend_jinja.py +125 -0
- package/python/barefootjs/evaluator.py +787 -0
- package/python/barefootjs/runtime.py +1334 -0
- package/python/barefootjs/search_params.py +89 -0
- package/python/pyproject.toml +32 -0
- package/python/tests/__init__.py +0 -0
- package/python/tests/test_eval_vectors.py +88 -0
- package/python/tests/test_evaluator.py +406 -0
- package/python/tests/test_helper_vectors.py +288 -0
- package/python/tests/test_omit.py +62 -0
- package/python/tests/test_props_attr.py +54 -0
- package/python/tests/test_query.py +41 -0
- package/python/tests/test_render.py +102 -0
- package/python/tests/test_render_child.py +96 -0
- package/python/tests/test_search_params.py +50 -0
- package/python/tests/test_spread_attrs.py +86 -0
- package/python/tests/test_template_primitives.py +347 -0
- package/python/tests/vector-divergences.json +42 -0
- package/src/__tests__/jinja-adapter-unit.test.ts +390 -0
- package/src/__tests__/jinja-adapter.test.ts +53 -0
- package/src/__tests__/jinja-counter.test.ts +62 -0
- package/src/__tests__/jinja-query-href.test.ts +99 -0
- package/src/__tests__/jinja-spread-attrs.test.ts +225 -0
- package/src/adapter/analysis/component-tree.ts +119 -0
- package/src/adapter/boolean-result.ts +176 -0
- package/src/adapter/emit-context.ts +118 -0
- package/src/adapter/expr/array-method.ts +346 -0
- package/src/adapter/expr/emitters.ts +608 -0
- package/src/adapter/expr/operand.ts +35 -0
- package/src/adapter/index.ts +6 -0
- package/src/adapter/jinja-adapter.ts +1747 -0
- package/src/adapter/lib/constants.ts +33 -0
- package/src/adapter/lib/ir-scope.ts +95 -0
- package/src/adapter/lib/jinja-naming.ts +114 -0
- package/src/adapter/lib/types.ts +30 -0
- package/src/adapter/memo/seed.ts +132 -0
- package/src/adapter/props/prop-classes.ts +65 -0
- package/src/adapter/spread/spread-codegen.ts +166 -0
- package/src/adapter/value/parsed-literal.ts +76 -0
- package/src/build.ts +37 -0
- package/src/conformance-pins.ts +101 -0
- package/src/index.ts +9 -0
- 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"}
|