@barefootjs/go-template 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.
- package/dist/adapter/go-template-adapter.d.ts +15 -14
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +24 -54
- package/dist/adapter/lib/compile-state.d.ts +1 -4
- package/dist/adapter/lib/compile-state.d.ts.map +1 -1
- package/dist/index.js +27 -55
- package/dist/render-divergences.d.ts.map +1 -1
- package/dist/vite.js +107 -121
- package/package.json +5 -5
- package/src/__tests__/go-template-adapter.test.ts +8 -1
- package/src/adapter/go-template-adapter.ts +66 -51
- package/src/adapter/lib/compile-state.ts +0 -5
- package/src/render-divergences.ts +4 -0
- package/dist/adapter/lib/ir-scope.d.ts +0 -13
- package/dist/adapter/lib/ir-scope.d.ts.map +0 -1
- package/src/adapter/lib/ir-scope.ts +0 -31
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
BindingScope,
|
|
81
81
|
} from '@barefootjs/jsx'
|
|
82
82
|
import { findInterpolationEnd } from '@barefootjs/jsx/scanner'
|
|
83
|
-
import { BF_REGION, escapeHtml } from '@barefootjs/shared'
|
|
83
|
+
import { BF_REGION, escapeHtml, resolveJsxChildrenProp } from '@barefootjs/shared'
|
|
84
84
|
|
|
85
85
|
import {
|
|
86
86
|
GO_IDENTIFIER,
|
|
@@ -118,7 +118,6 @@ import type {
|
|
|
118
118
|
CtorLowerEnv,
|
|
119
119
|
GoTemplateAdapterOptions,
|
|
120
120
|
} from "./lib/types.ts"
|
|
121
|
-
import { collectRootScopeNodes } from "./lib/ir-scope.ts"
|
|
122
121
|
import { GO_TEMPLATE_PRIMITIVES } from "./lib/constants.ts"
|
|
123
122
|
import { CompileState, resolveSignalParsedThroughSeedPlan } from "./lib/compile-state.ts"
|
|
124
123
|
import { hasClientInteractivity, findNestedComponents } from "./analysis/component-tree.ts"
|
|
@@ -313,15 +312,6 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
313
312
|
* everywhere except the accessor lookup itself.
|
|
314
313
|
*/
|
|
315
314
|
private scope: BindingScope = BindingScope.EMPTY
|
|
316
|
-
/**
|
|
317
|
-
* Stack of `IRLoop.depth` values (innermost last), pushed/popped around
|
|
318
|
-
* `renderChildren(loop.children)` in `renderLoop`. `renderAttributes`
|
|
319
|
-
* reads the top of this stack to derive the `key` → `data-key`/
|
|
320
|
-
* `data-key-N` suffix — the depth is IR-computed (jsx-to-ir.ts), not
|
|
321
|
-
* re-derived here; this stack only threads that value down to the
|
|
322
|
-
* loop body's root element (#2168 nested-loop-outer-binding).
|
|
323
|
-
*/
|
|
324
|
-
private loopKeyDepthStack: number[] = []
|
|
325
315
|
/**
|
|
326
316
|
* Per-loop: true when the body renders the bare range value (scalar-item
|
|
327
317
|
* inline-literal loop), so the `bf_tmpl` companion is fed `.BfLoopItem` (the
|
|
@@ -521,7 +511,6 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
521
511
|
const isRootComponent = ir.root.type === 'component'
|
|
522
512
|
const isIfStatement = ir.root.type === 'if-statement'
|
|
523
513
|
|
|
524
|
-
this.state.rootScopeNodes = collectRootScopeNodes(ir.root)
|
|
525
514
|
// Map each array memo backing a loop (`<memo>().map(...)`) to that loop's
|
|
526
515
|
// handler-filled slice field, so `<memo>().length` lowers to the slice's
|
|
527
516
|
// length. Built before rendering — `.length` can precede the loop in source.
|
|
@@ -2323,7 +2312,7 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
2323
2312
|
}
|
|
2324
2313
|
case 'jsx-children':
|
|
2325
2314
|
// The RESERVED children slot (`comp.children.length > 0 ? comp.children
|
|
2326
|
-
// :
|
|
2315
|
+
// : resolveJsxChildrenProp(...)`) is handled below via
|
|
2327
2316
|
// `child.childrenText` / `child.childrenHtml` and must not be
|
|
2328
2317
|
// re-emitted here. A JSX-valued prop under any OTHER name
|
|
2329
2318
|
// (`header={<strong>Title</strong>}`, #2168 jsx-element-prop) is a
|
|
@@ -3173,17 +3162,6 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
3173
3162
|
return out
|
|
3174
3163
|
}
|
|
3175
3164
|
|
|
3176
|
-
/**
|
|
3177
|
-
* Pull the IR nodes out of a `children={<…/>}` attribute (a `jsx-children`
|
|
3178
|
-
* prop value). Empty when the component takes no such prop.
|
|
3179
|
-
*/
|
|
3180
|
-
private jsxChildrenPropNodes(props: IRProp[]): IRNode[] {
|
|
3181
|
-
for (const p of props) {
|
|
3182
|
-
if (p.value.kind === 'jsx-children') return p.value.children
|
|
3183
|
-
}
|
|
3184
|
-
return []
|
|
3185
|
-
}
|
|
3186
|
-
|
|
3187
3165
|
private extractHtmlChildren(children: IRNode[]): string | null {
|
|
3188
3166
|
if (children.length === 0) return null
|
|
3189
3167
|
if (children.every(c => c.type === 'text')) return null
|
|
@@ -3201,10 +3179,33 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
3201
3179
|
* plain static `childrenHtml` path already applies, or when any other template
|
|
3202
3180
|
* action survives (genuinely dynamic — those stay on the drop path).
|
|
3203
3181
|
*/
|
|
3182
|
+
/**
|
|
3183
|
+
* Set only while baking a hoisted children value into a static Go string.
|
|
3184
|
+
* `data-key` is a KEYED-LOOP-ROW contract, and a value baked into a
|
|
3185
|
+
* caller's props at compile time is by construction not a loop row — so
|
|
3186
|
+
* emitting `{{if .BfDataKey}}` there is dead weight. It is also actively
|
|
3187
|
+
* harmful: `extractScopedHtmlChildren` rejects any surviving `{{` action
|
|
3188
|
+
* as "genuinely dynamic" and returns null, and its caller drops the field
|
|
3189
|
+
* without a word, so the baked children silently vanish.
|
|
3190
|
+
*
|
|
3191
|
+
* `ctx.isRoot` leaks into a JSX-valued prop's subtree, so such a fragment
|
|
3192
|
+
* is already tagged `needsScopeComment` as if it were a component root —
|
|
3193
|
+
* which is why a relay `element.keyAttr` (#2753; `carriesDataKey` before
|
|
3194
|
+
* it) reaches this path at all.
|
|
3195
|
+
*/
|
|
3196
|
+
private bakingStaticChildren = false
|
|
3197
|
+
|
|
3204
3198
|
private extractScopedHtmlChildren(children: IRNode[]): string | null {
|
|
3205
3199
|
if (children.length === 0) return null
|
|
3206
3200
|
if (children.every(c => c.type === 'text')) return null
|
|
3207
|
-
const
|
|
3201
|
+
const prevBaking = this.bakingStaticChildren
|
|
3202
|
+
this.bakingStaticChildren = true
|
|
3203
|
+
let html: string
|
|
3204
|
+
try {
|
|
3205
|
+
html = this.renderChildren(children)
|
|
3206
|
+
} finally {
|
|
3207
|
+
this.bakingStaticChildren = prevBaking
|
|
3208
|
+
}
|
|
3208
3209
|
// The needsScope marker renders parent-scope hydration attrs; in a
|
|
3209
3210
|
// hoisted fragment every needsScope root resolves to the parent scopeID,
|
|
3210
3211
|
// so collapse the whole marker to a bare `bf-s` sentinel (the empty
|
|
@@ -3252,7 +3253,7 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
3252
3253
|
// paths below see them.
|
|
3253
3254
|
const effectiveChildren = comp.children.length > 0
|
|
3254
3255
|
? comp.children
|
|
3255
|
-
:
|
|
3256
|
+
: resolveJsxChildrenProp(comp.props)
|
|
3256
3257
|
result.push({
|
|
3257
3258
|
name: comp.name,
|
|
3258
3259
|
slotId: comp.slotId,
|
|
@@ -4118,12 +4119,34 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
4118
4119
|
if (element.needsScope) {
|
|
4119
4120
|
hydrationAttrs += ` ${this.renderScopeMarker('.ScopeID')}`
|
|
4120
4121
|
}
|
|
4121
|
-
//
|
|
4122
|
-
//
|
|
4123
|
-
//
|
|
4124
|
-
//
|
|
4125
|
-
|
|
4126
|
-
|
|
4122
|
+
// #2753: `element.keyAttr` is the ONE IR-resolved decision for this
|
|
4123
|
+
// element's row-key attribute — replacing both the `carriesDataKey`
|
|
4124
|
+
// boolean and this adapter's own `rootScopeNodes`/`needsScope` check
|
|
4125
|
+
// (mechanism 2: a render-root relay, including the #2732 fragment-root
|
|
4126
|
+
// case) and the separate `loopKeyDepthStack`-driven rewrite of a literal
|
|
4127
|
+
// `key` attribute this adapter used to do in `renderAttributes`
|
|
4128
|
+
// (mechanism 1: a `.map()` row root compiled inline here).
|
|
4129
|
+
if (element.keyAttr) {
|
|
4130
|
+
if (element.keyAttr.value !== undefined) {
|
|
4131
|
+
const lowered = emitAttrValue(
|
|
4132
|
+
{ kind: 'expression', expr: element.keyAttr.value },
|
|
4133
|
+
this.elementAttrEmitter,
|
|
4134
|
+
element.keyAttr.name,
|
|
4135
|
+
)
|
|
4136
|
+
if (lowered) hydrationAttrs += ` ${lowered}`
|
|
4137
|
+
} else if (!this.bakingStaticChildren) {
|
|
4138
|
+
// Relay: this element is one of THIS component's own render roots,
|
|
4139
|
+
// carrying whatever key its OWN caller supplies at runtime (the
|
|
4140
|
+
// parent's loop init stamped `.BfDataKey`), when this component is
|
|
4141
|
+
// itself invoked as a caller's keyed loop row. Applies to
|
|
4142
|
+
// early-return (if-statement) roots too, where every branch's top
|
|
4143
|
+
// element qualifies, and to #2732's fragment-root case.
|
|
4144
|
+
// `bakingStaticChildren` (see its docstring) excludes a hoisted
|
|
4145
|
+
// children value baked into a static Go string at compile time —
|
|
4146
|
+
// by construction not a loop row, and a surviving `{{if}}` action
|
|
4147
|
+
// there gets silently dropped by the caller rather than rendered.
|
|
4148
|
+
hydrationAttrs += `{{if .BfDataKey}} data-key="{{.BfDataKey}}"{{end}}`
|
|
4149
|
+
}
|
|
4127
4150
|
}
|
|
4128
4151
|
if (element.slotId) {
|
|
4129
4152
|
hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`
|
|
@@ -7139,7 +7162,6 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
7139
7162
|
this.scalarLiteralLoopGoType(loop.arrayParsed, loop.itemType) !== null,
|
|
7140
7163
|
)
|
|
7141
7164
|
this.loopWrapperStack.push(!!loop.childComponent)
|
|
7142
|
-
this.loopKeyDepthStack.push(loop.depth)
|
|
7143
7165
|
// Rendered inside the pushed loop scope so each initializer resolves
|
|
7144
7166
|
// against the range item (`.Done`), in source order so a later
|
|
7145
7167
|
// initializer sees an earlier `$` variable — same as the source block.
|
|
@@ -7147,7 +7169,6 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
7147
7169
|
.map(d => `{{$${d.name} := ${this.renderParsedExpr(d.valueParsed)}}}`)
|
|
7148
7170
|
.join('')
|
|
7149
7171
|
const children = this.renderChildren(loop.children)
|
|
7150
|
-
this.loopKeyDepthStack.pop()
|
|
7151
7172
|
this.loopWrapperStack.pop()
|
|
7152
7173
|
this.loopScalarItemStack.pop()
|
|
7153
7174
|
// Build the per-item anchor marker while the row scope is still active,
|
|
@@ -7239,7 +7260,6 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
7239
7260
|
const wasInLoopOuter = this.inLoop
|
|
7240
7261
|
this.inLoop = true
|
|
7241
7262
|
this.loopWrapperStack.push(false)
|
|
7242
|
-
this.loopKeyDepthStack.push(loop.depth)
|
|
7243
7263
|
this.loopScalarItemStack.push(this.scalarLiteralLoopGoType(loop.arrayParsed, loop.itemType) !== null)
|
|
7244
7264
|
// The bake gate (`analyzeBakeableStaticElementLoop`) already refused a
|
|
7245
7265
|
// destructured or `.keys()`-shape param, so `loop` binds via `enterLoopRow`
|
|
@@ -7272,7 +7292,6 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
7272
7292
|
|
|
7273
7293
|
this.scope = prevScope
|
|
7274
7294
|
this.loopScalarItemStack.pop()
|
|
7275
|
-
this.loopKeyDepthStack.pop()
|
|
7276
7295
|
this.loopWrapperStack.pop()
|
|
7277
7296
|
this.inLoop = wasInLoopOuter
|
|
7278
7297
|
|
|
@@ -7308,7 +7327,7 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
7308
7327
|
private queueDynamicChildrenDefine(comp: IRComponent): string | null {
|
|
7309
7328
|
const effectiveChildren = comp.children.length > 0
|
|
7310
7329
|
? comp.children
|
|
7311
|
-
:
|
|
7330
|
+
: resolveJsxChildrenProp(comp.props)
|
|
7312
7331
|
if (effectiveChildren.length === 0) return null
|
|
7313
7332
|
if (this.extractTextChildren(effectiveChildren) !== null) return null
|
|
7314
7333
|
if (this.extractHtmlChildren(effectiveChildren) !== null) return null
|
|
@@ -7335,7 +7354,7 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
7335
7354
|
private queueLoopBodyChildrenDefine(comp: IRComponent): string | null {
|
|
7336
7355
|
const effectiveChildren = comp.children.length > 0
|
|
7337
7356
|
? comp.children
|
|
7338
|
-
:
|
|
7357
|
+
: resolveJsxChildrenProp(comp.props)
|
|
7339
7358
|
if (effectiveChildren.length === 0) return null
|
|
7340
7359
|
if (this.extractTextChildren(effectiveChildren) !== null) return null
|
|
7341
7360
|
if (this.extractHtmlChildren(effectiveChildren) !== null) return null
|
|
@@ -7718,19 +7737,15 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
7718
7737
|
// literal never reaches the generic object-literal BF101 refusal
|
|
7719
7738
|
// (which would double-report alongside the purpose-built one).
|
|
7720
7739
|
if (isDangerousInnerHtmlAttr(attr)) continue
|
|
7721
|
-
//
|
|
7722
|
-
//
|
|
7723
|
-
//
|
|
7724
|
-
//
|
|
7725
|
-
//
|
|
7726
|
-
//
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
const depth = this.loopKeyDepthStack.at(-1) ?? 0
|
|
7731
|
-
attrName = depth > 0 ? `data-key-${depth}` : 'data-key'
|
|
7732
|
-
}
|
|
7733
|
-
else attrName = attr.name
|
|
7740
|
+
// `key` never renders as a literal HTML attribute — #2753 resolves it
|
|
7741
|
+
// onto `element.keyAttr` (name AND value) at IR-build time, emitted
|
|
7742
|
+
// separately in `renderElement`. A stray `key` that jsx-to-ir.ts did
|
|
7743
|
+
// NOT recognize as a loop row root (so `keyAttr` was never set) has no
|
|
7744
|
+
// defined rendering outside a `.map()` anyway; drop it rather than
|
|
7745
|
+
// leak an invalid `key="..."` HTML attribute.
|
|
7746
|
+
if (attr.name === 'key') continue
|
|
7747
|
+
// Rewrite JSX special-prop names to their HTML-attribute counterparts.
|
|
7748
|
+
const attrName = attr.name === 'className' ? 'class' : attr.name
|
|
7734
7749
|
const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName)
|
|
7735
7750
|
if (lowered) parts.push(lowered)
|
|
7736
7751
|
}
|
|
@@ -19,7 +19,6 @@ import type {
|
|
|
19
19
|
ContextConsumer,
|
|
20
20
|
EnvSignalReader,
|
|
21
21
|
IRMetadata,
|
|
22
|
-
IRNode,
|
|
23
22
|
LoweringMatcher,
|
|
24
23
|
MemoInfo,
|
|
25
24
|
ParsedExpr,
|
|
@@ -205,10 +204,6 @@ export class CompileState {
|
|
|
205
204
|
*/
|
|
206
205
|
stringValueNames: Set<string> = new Set()
|
|
207
206
|
|
|
208
|
-
/** Component root scope element(s) — each carries `data-key` for a keyed loop
|
|
209
|
-
* item. */
|
|
210
|
-
rootScopeNodes: Set<IRNode> = new Set()
|
|
211
|
-
|
|
212
207
|
/** Array-memo name → the handler-filled loop slice field its `.map()` feeds
|
|
213
208
|
* (e.g. `visible` → `PostListItems`). Lets `<memo>().length` lower to the
|
|
214
209
|
* slice's length instead of a nil/unset memo field. */
|
|
@@ -20,6 +20,10 @@ import type { RenderDivergences } from '@barefootjs/jsx'
|
|
|
20
20
|
export const renderDivergences: RenderDivergences = {
|
|
21
21
|
'jsx-element-prop-fragment-conditional':
|
|
22
22
|
'Go-specific half of the fragment-wrapped-conditional prop shape, tracked as https://github.com/piconic-ai/barefootjs/issues/2703 (distinct from #2702, the cross-adapter hydrate-time bug). #2702 is a hydrate-time-only bug (SSR is correct on every adapter, confirmed for Go by direct `adapter.generate()` inspection of the raw template TEXT, which does contain `{{if .Cond}}<a bf-c="^s0">x</a>{{else}}...{{end}}`-shaped markup). But the REAL Go render of this exact fixture (`go-template-adapter.test.ts`, real `go run`) produces an EMPTY `<header>` — `<header bf="s1"><!--bf:s0--><!--/--></header>` instead of the reference `<a>x</a>` — i.e. the `header` prop\'s jsx-children payload never reaches the child (`Card`) component\'s slot construction (`.CardSlot1`) at all for this NAMED-prop shape, even though the same conditional-in-fragment payload renders correctly when passed via `children` instead (`jsx-element-prop-children-escape`). Root cause not yet isolated to a specific emitter function — only the reproducible symptom is pinned here. Every other adapter (Hono, ERB, Jinja, Mojolicious, MiniJinja, Twig, Xslate, Blade) renders this fixture correctly; verified by running each adapter\'s own JSX Conformance Tests for this fixture id.',
|
|
23
|
+
'children-passthrough-renamed':
|
|
24
|
+
'A `children` prop destructured under a different name (`const { children: kids } = props`) does not reach the SSR template on Go, tracked as https://github.com/piconic-ai/barefootjs/issues/2788. The same fixture also fails on Mojolicious, where the mechanism IS isolated: the `.html.ep` interpolates the LOCAL alias (`$kids`) while the stash defines only the caller-facing `children`, so the Perl render dies inside `Mojo::Template::process`. Go\'s own failure mode has NOT been read — `go` is not reachable from the local test process (the conformance case prints "go command not found" and skips), so this entry is declared from the CI failure on #2787 alone, not from a local reproduction. Whoever graduates this should read Go\'s actual output first rather than assume it shares Mojo\'s mechanism. Same alias family as `aliased-destructured-prop` (`{ n: count }`), whose Go half graduated in #2525 — worth checking whether the reserved `children` slot bypasses that fix or never had it. `children-passthrough-renamed` asserts the CORRECT (Hono-generated) output, so deleting this entry is the graduation.',
|
|
23
25
|
'signal-object-spread-init':
|
|
24
26
|
'PRE-EXISTING, unrelated to the #2696 Step 2 spread work this fixture pins: a `derived`-classified signal/memo whose value is an OBJECT literal has no live-template-expression lowering on Go — unlike the other six template-stash backends (e.g. minijinja emits `{% set merged = dict(base, done=true) %}`), Go always bakes an object-typed signal/memo field into Go SOURCE at `NewXxxProps` constructor time (`convertInitialValue`/`parsedLiteralToGo`), and that baker is STATIC-only (identifier/call/member operands defer, `parsed-literal-to-go.ts`\'s own docstring) — it cannot reference a live prop at all. Reproduced identically with the spread REMOVED (`createSignal({ id: base.id, done: true })`), confirming the gap predates and is independent of spread: the signal seeds `nil` and every field read (`.Merged.ID`/`.Merged.Done`) reads the Go zero value regardless of `initialTodos`. Graduate by teaching the baker to emit prop-referencing Go expressions (https://github.com/piconic-ai/barefootjs/issues/2700).',
|
|
27
|
+
'textarea-row-breakout':
|
|
28
|
+
'A signal seeded from a bare identifier referencing a MODULE-LEVEL const (`const PAYLOAD = \'...\'; createSignal(PAYLOAD)`) bakes to `nil` in the generated `New<Component>Props` constructor instead of the const\'s literal value: `convertInitialValue` (`value-lowering.ts`) only resolves a direct prop reference or a literal expression for a bare identifier, and the analyzer types this signal `{ kind: \'unknown\' }`, so every typed branch falls through to the final `nil` fallback. `resolveModuleStringConst` exists on the adapter for exactly this resolution (used by `template-interp.ts` for live template expressions) but isn\'t wired into this signal-baking path. Unrelated to what this fixture exists to cover (#2765\'s loop-row textarea-escaping fix, verified correct here) — every other adapter renders the fixture\'s controlled `<textarea>` correctly. Tracked at https://github.com/piconic-ai/barefootjs/issues/2794; graduate by wiring `resolveModuleStringConst` into `convertInitialValue`\'s bare-identifier case.',
|
|
25
29
|
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* IR traversal helpers for the Go html/template adapter. Pure functions over
|
|
3
|
-
* the IR tree — no adapter instance state.
|
|
4
|
-
*/
|
|
5
|
-
import type { IRNode } from '@barefootjs/jsx';
|
|
6
|
-
/**
|
|
7
|
-
* Collect the component's root scope element node(s) — the elements that become
|
|
8
|
-
* the rendered root and so carry `data-key` for a keyed loop item. A plain
|
|
9
|
-
* element root is itself; an `if-statement` (early-return) root contributes the
|
|
10
|
-
* top element of each branch, since exactly one renders at runtime.
|
|
11
|
-
*/
|
|
12
|
-
export declare function collectRootScopeNodes(node: IRNode): Set<IRNode>;
|
|
13
|
-
//# sourceMappingURL=ir-scope.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ir-scope.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/ir-scope.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAA6B,MAAM,iBAAiB,CAAA;AAExE;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAiB/D"}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* IR traversal helpers for the Go html/template adapter. Pure functions over
|
|
3
|
-
* the IR tree — no adapter instance state.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { IRNode, IRIfStatement, IRFragment } from '@barefootjs/jsx'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Collect the component's root scope element node(s) — the elements that become
|
|
10
|
-
* the rendered root and so carry `data-key` for a keyed loop item. A plain
|
|
11
|
-
* element root is itself; an `if-statement` (early-return) root contributes the
|
|
12
|
-
* top element of each branch, since exactly one renders at runtime.
|
|
13
|
-
*/
|
|
14
|
-
export function collectRootScopeNodes(node: IRNode): Set<IRNode> {
|
|
15
|
-
const out = new Set<IRNode>()
|
|
16
|
-
const visit = (n: IRNode | null): void => {
|
|
17
|
-
if (!n) return
|
|
18
|
-
if (n.type === 'element') { out.add(n); return }
|
|
19
|
-
if (n.type === 'if-statement') {
|
|
20
|
-
const s = n as IRIfStatement
|
|
21
|
-
visit(s.consequent)
|
|
22
|
-
visit(s.alternate)
|
|
23
|
-
return
|
|
24
|
-
}
|
|
25
|
-
if (n.type === 'fragment') {
|
|
26
|
-
for (const c of (n as IRFragment).children) visit(c)
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
visit(node)
|
|
30
|
-
return out
|
|
31
|
-
}
|