/** * ParsedExpr → Jinja2 emitters for the Jinja template adapter. * * Ported from `packages/adapter-xslate/src/adapter/expr/emitters.ts` * (`XslateFilterEmitter` / `XslateTopLevelEmitter`). Two `ParsedExprEmitter` * implementations: * * - `JinjaFilterEmitter` — filter/predicate context (loop param + local * aliases + bare identifier signal fallback); self-contained, reads no * adapter state. * - `JinjaTopLevelEmitter` — top-level / per-render-var context; depends on * the adapter only through the narrow `JinjaEmitContext` seam. * * Two deliberate divergences from the Kolon port, both documented at their * definition site below: * * 1. **JS-truthy condition wrapping** (`truthyTest`) — Python truthiness * diverges from JS on empty containers; Perl doesn't have this problem * (a Perl ref is always true), so Kolon never needed this. Every * condition-TEST position (`!x`, the left operand of `&&`/`||`, a * ternary's test) routes through the shared `bf.truthy(...)` runtime * helper unless the operand is structurally already boolean-shaped * (`isBooleanResultParsed`). `&&`/`||` still return the ORIGINAL * operand VALUE (not a coerced bool) on the taken branch — matching JS * `a || b` returning `a` itself, not `true` — only the BRANCH TEST uses * `bf.truthy`. Because Jinja's `if/else` ternary has no way to reuse an * already-computed test value for both the test and (conditionally) the * result, the left operand's rendered text is emitted TWICE (once as * the test, once as the value) — safe because every operand reaching * this pipeline is a pure, side-effect-free read (a signal/prop lookup * or a call into a pure runtime helper), never a stateful expression. * 2. **No Jinja lambda for the predicate-callback fallback.** Kolon's * `-> $x { … }` lambda is the Xslate top-level emitter's fallback when a * `.filter`/`.every`/`.some`/`.find*` predicate can't be serialized to * the runtime evaluator's JSON form (e.g. a nested method-call * predicate). Jinja has no lambda-expression syntax at all, so there is * no equivalent fallback to port. `JinjaTopLevelEmitter` therefore uses * ONE mechanism for every higher-order callback: the evaluator-JSON * `*_eval` payload (`bf.filter_eval`, `bf.sort_eval`, …). When * `serializeParsedExpr` refuses the body, the call surfaces `BF101` * instead of silently degrading — `.sort`/`.toSorted` is the one * exception, whose non-lambda STRUCTURED fallback (`bf.sort` with a * `{keys: […]}` descriptor, no callback body at all) survives the port * unchanged since it was never lambda-shaped to begin with. * `JinjaFilterEmitter` (the loop `.filter().map()` INLINE predicate, * rendered as a plain boolean expression, never a lambda) is otherwise * unaffected and still used for that path plus the filter-predicate * entry point `_renderJinjaFilterExprPublic`. */ import { type ParsedExprEmitter, type LoweringEmitter, type ArrayMethod, type LiteralType, type ParsedExpr, type ObjectLiteralProperty, type FlatDepth, type TemplatePart, type CallbackLoweringOptions } from '@barefootjs/jsx'; import type { JinjaEmitContext } from '../emit-context.ts'; export declare const CALLBACK_LOWERING: CallbackLoweringOptions; /** * Route a condition-TEST position through `bf.truthy(...)` unless the node * is structurally already boolean-shaped. See the file header (divergence * 1). Shared by both emitters below and reused by the adapter's top-level * `convertConditionToJinja` for IR-level `if` / loop-filter conditions. */ export declare function truthyTest(node: ParsedExpr, rendered: string): string; export declare class JinjaFilterEmitter implements ParsedExprEmitter { private readonly param; private readonly localVarMap; private readonly isStringName; private readonly onUnsupported?; constructor(param: string, localVarMap: Map, isStringName?: (n: string) => boolean, onUnsupported?: (message: string, reason?: string) => void); identifier(name: string): string; literal(value: string | number | boolean | null, literalType: LiteralType): string; member(object: ParsedExpr, property: string, computed: boolean, optional: boolean, emit: (e: ParsedExpr) => string): string; indexAccess(object: ParsedExpr, index: ParsedExpr, emit: (e: ParsedExpr) => string): string; call(callee: ParsedExpr, args: ParsedExpr[], emit: (e: ParsedExpr) => string): string; unary(op: string, argument: ParsedExpr, emit: (e: ParsedExpr) => string): string; binary(op: string, left: ParsedExpr, right: ParsedExpr, emit: (e: ParsedExpr) => string): string; logical(op: '&&' | '||' | '??', left: ParsedExpr, right: ParsedExpr, emit: (e: ParsedExpr) => string): string; callbackMethod(method: string, object: ParsedExpr, _arrow: Extract, _restArgs: ParsedExpr[], emit: (e: ParsedExpr) => string): string; arrayLiteral(elements: ParsedExpr[], emit: (e: ParsedExpr) => string): string; arrayMethod(method: ArrayMethod, object: ParsedExpr, args: ParsedExpr[], emit: (e: ParsedExpr) => string): string; flatMethod(object: ParsedExpr, depth: FlatDepth | { expr: ParsedExpr; }, emit: (e: ParsedExpr) => string): string; conditional(_test: ParsedExpr, _consequent: ParsedExpr, _alternate: ParsedExpr): string; templateLiteral(_parts: TemplatePart[]): string; arrow(_params: string[], _body: ParsedExpr): string; regex(_raw: string): string; unsupported(_raw: string, reason: string): string; objectLiteral(_properties: ObjectLiteralProperty[], _raw: string, _emit: (e: ParsedExpr) => string): string; /** * A predicate shape this emitter cannot spell is BF101, never a truthy * constant standing in for it (#3534): that rendered every row. */ private refuseShape; } /** * Lowering for top-level expressions whose identifiers resolve against the * Jinja template's per-render context vars (signals, props, locals * introduced by `{% set x = … %}`). Differs from the filter emitter mainly * in * - `conditional` is supported (filter predicates can't return ternaries), * - higher-order methods route through `bf.*` array/evaluator helpers, * - no lambda fallback exists (see the file header, divergence 2). */ export declare class JinjaTopLevelEmitter implements ParsedExprEmitter { private readonly ctx; constructor(ctx: JinjaEmitContext); /** * Registered-lowering seam (#2843): `emitParsedExpr`'s shared `call` case * tries every matcher here BEFORE `call()` itself, so a registered call * (the built-in `queryHref`, or any userland plugin) is recognised no * matter where it sits in the tree. `render` is what used to live inline * in `JinjaAdapter.convertExpressionToJinja` before the object-literal * support-gate refusal (`checkSupport`'s `call` arm, now itself * registry-aware) made the pre-gate special case unnecessary. */ get lowering(): LoweringEmitter; identifier(name: string): string; literal(value: string | number | boolean | null, literalType: LiteralType): string; member(object: ParsedExpr, property: string, computed: boolean, optional: boolean, emit: (e: ParsedExpr) => string): string; indexAccess(object: ParsedExpr, index: ParsedExpr, emit: (e: ParsedExpr) => string): string; call(callee: ParsedExpr, args: ParsedExpr[], emit: (e: ParsedExpr) => string): string; unary(op: string, argument: ParsedExpr, emit: (e: ParsedExpr) => string): string; /** Spell `arithmeticOperandCoercion` (#3474): a `null` operand reads as * `0`, as JS `ToNumber(null)` does (#3350); an `undefined` one as `NaN`, as * `ToNumber(undefined)` does (#3390). */ private coerceNullishOperand; binary(op: string, left: ParsedExpr, right: ParsedExpr, emit: (e: ParsedExpr) => string): string; logical(op: '&&' | '||' | '??', left: ParsedExpr, right: ParsedExpr, emit: (e: ParsedExpr) => string): string; callbackMethod(method: string, object: ParsedExpr, arrow: Extract, restArgs: ParsedExpr[], emit: (e: ParsedExpr) => string): string; arrayLiteral(elements: ParsedExpr[], emit: (e: ParsedExpr) => string): string; arrayMethod(method: ArrayMethod, object: ParsedExpr, args: ParsedExpr[], emit: (e: ParsedExpr) => string): string; flatMethod(object: ParsedExpr, depth: FlatDepth | { expr: ParsedExpr; }, emit: (e: ParsedExpr) => string): string; conditional(test: ParsedExpr, consequent: ParsedExpr, alternate: ParsedExpr, emit: (e: ParsedExpr) => string): string; templateLiteral(parts: TemplatePart[], emit: (e: ParsedExpr) => string): string; arrow(_params: string[], _body: ParsedExpr): string; regex(_raw: string): string; unsupported(_raw: string, _reason: string): string; objectLiteral(properties: ObjectLiteralProperty[], _raw: string, emit: (e: ParsedExpr) => string): string; } //# sourceMappingURL=emitters.d.ts.map