@barefootjs/go-template 0.33.4 → 0.34.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/dist/adapter/emit-context.d.ts +12 -0
- package/dist/adapter/emit-context.d.ts.map +1 -1
- package/dist/adapter/expr/url-builder.d.ts +53 -1
- package/dist/adapter/expr/url-builder.d.ts.map +1 -1
- package/dist/adapter/go-template-adapter.d.ts +153 -7
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +253 -99
- package/dist/adapter/lib/compile-state.d.ts +17 -0
- package/dist/adapter/lib/compile-state.d.ts.map +1 -1
- package/dist/adapter/value/parsed-literal-to-go.d.ts.map +1 -1
- package/dist/adapter/value/value-lowering.d.ts.map +1 -1
- package/dist/conformance-pins.d.ts.map +1 -1
- package/dist/index.js +261 -103
- package/dist/render-divergences.d.ts +10 -0
- package/dist/render-divergences.d.ts.map +1 -1
- package/dist/vite.js +420 -214
- package/package.json +5 -5
- package/src/__tests__/go-template-adapter.test.ts +333 -9
- package/src/__tests__/lowering-plugin.test.ts +38 -0
- package/src/__tests__/query-href.test.ts +126 -0
- package/src/adapter/emit-context.ts +14 -0
- package/src/adapter/expr/url-builder.ts +114 -8
- package/src/adapter/go-template-adapter.ts +514 -107
- package/src/adapter/lib/compile-state.ts +18 -0
- package/src/adapter/value/parsed-literal-to-go.ts +11 -7
- package/src/adapter/value/value-lowering.ts +17 -0
- package/src/conformance-pins.ts +19 -0
- package/src/render-divergences.ts +12 -6
- package/src/test-render.ts +32 -15
|
@@ -128,6 +128,44 @@ function lowerTernaryTest(ctx: GoEmitContext, test: ParsedExpr): string {
|
|
|
128
128
|
return isBoolShape ? go : `(bf_truthy ${go})`
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
/**
|
|
132
|
+
* First registered matcher that recognises `callee(args)`, or null (#2842).
|
|
133
|
+
* The single registry consultation every Go lowering path shares: the
|
|
134
|
+
* top-level `lowerRegisteredCall` early return, the ParsedExpr `call()`
|
|
135
|
+
* dispatcher (`go-template-adapter.ts`, so a registered call is recognised no
|
|
136
|
+
* matter how deep it sits in an expression tree — a ternary branch, a
|
|
137
|
+
* template-literal interpolation, a binary operand — not only when the call
|
|
138
|
+
* IS the whole expression), and the attribute-position `bf_attr` route all
|
|
139
|
+
* ask this one question. First-match-wins: a malformed helper id from one
|
|
140
|
+
* plugin must not be silently masked by falling through to another.
|
|
141
|
+
*/
|
|
142
|
+
export function matchRegisteredCall(
|
|
143
|
+
ctx: GoEmitContext,
|
|
144
|
+
callee: ParsedExpr,
|
|
145
|
+
args: readonly ParsedExpr[],
|
|
146
|
+
): LoweringNode | null {
|
|
147
|
+
for (const matcher of ctx.state.loweringMatchers) {
|
|
148
|
+
const node = matcher(callee, args)
|
|
149
|
+
if (node) return node
|
|
150
|
+
}
|
|
151
|
+
return null
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Render a recognised call's neutral node to its Go pipeline (unparenthesised
|
|
156
|
+
* — same contract as `lowerRegisteredCall`), or null when no plugin matches
|
|
157
|
+
* or the matched helper has no Go mapping.
|
|
158
|
+
*/
|
|
159
|
+
export function lowerRegisteredCallNode(
|
|
160
|
+
ctx: GoEmitContext,
|
|
161
|
+
callee: ParsedExpr,
|
|
162
|
+
args: readonly ParsedExpr[],
|
|
163
|
+
): string | null {
|
|
164
|
+
if (ctx.state.loweringMatchers.length === 0) return null
|
|
165
|
+
const node = matchRegisteredCall(ctx, callee, args)
|
|
166
|
+
return node ? renderLoweringNode(ctx, node) : null
|
|
167
|
+
}
|
|
168
|
+
|
|
131
169
|
/**
|
|
132
170
|
* Lower a helper call to a Go template expression, or null when no registered
|
|
133
171
|
* plugin recognises it (→ the generic lowering). Matchers — including the
|
|
@@ -144,8 +182,7 @@ export function lowerRegisteredCall(
|
|
|
144
182
|
jsExpr: string,
|
|
145
183
|
preParsed?: ParsedExpr,
|
|
146
184
|
): string | null {
|
|
147
|
-
|
|
148
|
-
if (matchers.length === 0) return null
|
|
185
|
+
if (ctx.state.loweringMatchers.length === 0) return null
|
|
149
186
|
|
|
150
187
|
let call: ParsedExpr | undefined = preParsed?.kind === 'call' ? preParsed : undefined
|
|
151
188
|
if (!call) {
|
|
@@ -155,13 +192,82 @@ export function lowerRegisteredCall(
|
|
|
155
192
|
call = parsed
|
|
156
193
|
}
|
|
157
194
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
195
|
+
return lowerRegisteredCallNode(ctx, call.callee, call.args)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Attribute-position twin of `lowerRegisteredCall` (#2743). When an intrinsic
|
|
200
|
+
* element attribute's value is a `query` guard-list (the neutral node the
|
|
201
|
+
* `queryHref` plugin — or any plugin reusing the `query` helper — produces),
|
|
202
|
+
* emit the WHOLE attribute as one `bf_attr` action returning
|
|
203
|
+
* `template.HTMLAttr`, instead of `name="{{bf_query …}}"`. html/template
|
|
204
|
+
* infers a URL context from the attribute NAME (`href`, `src`, `action`,
|
|
205
|
+
* `data-*`, anything containing `src`/`uri`/`url`, …) and percent-encodes the
|
|
206
|
+
* entire value there; the JS reference (Hono) only HTML-escapes. Keyed on the
|
|
207
|
+
* neutral `helper` id, not the attribute name (Go's URL-context table is a
|
|
208
|
+
* heuristic we must not re-implement) and not the JS API name. Returns null
|
|
209
|
+
* for a non-call, an unmatched call, or any other node kind/helper — the
|
|
210
|
+
* caller then takes its ordinary path.
|
|
211
|
+
*
|
|
212
|
+
* Also accepts a `conditional` node directly (#2743 follow-up, pullfrog
|
|
213
|
+
* review on #2841): a ternary with a real, non-`undefined` alternate
|
|
214
|
+
* (`cond ? queryHref(...) : '/fallback'`) is syntactically valid and already
|
|
215
|
+
* lowers its branches correctly via `lowerTernary` — but the CALLER used to
|
|
216
|
+
* still wrap the resulting `(bf_ternary …)` pipeline in the ordinary
|
|
217
|
+
* `name="{{...}}"` form, which leaves it exposed to the same URL-context
|
|
218
|
+
* percent-encoding this function exists to route around. Whenever EITHER
|
|
219
|
+
* branch (recursing through a right-folded nested ternary chain, the same
|
|
220
|
+
* shape `lowerTernary` itself right-folds) is a `query` guard-list call, the
|
|
221
|
+
* whole ternary is wrapped in `bf_attr` too — matching the reference, which
|
|
222
|
+
* HTML-escapes the picked branch's value the same way regardless of which
|
|
223
|
+
* branch won.
|
|
224
|
+
*
|
|
225
|
+
* The caller passes the CONSEQUENT ALONE (not the full ternary) for the
|
|
226
|
+
* `undefined`-alternate omission shape (`cond ? queryHref(...) : undefined`,
|
|
227
|
+
* #2842) — omission needs the `{{if}}` wrapper that shape's caller already
|
|
228
|
+
* builds; a `bf_ternary … ""` inside `bf_attr` can't express it (`Attr`
|
|
229
|
+
* renders `name=""` for an empty value, not attribute absence).
|
|
230
|
+
*/
|
|
231
|
+
export function lowerRegisteredAttrCall(
|
|
232
|
+
ctx: GoEmitContext,
|
|
233
|
+
attrName: string,
|
|
234
|
+
parsed: ParsedExpr,
|
|
235
|
+
): string | null {
|
|
236
|
+
if (parsed.kind === 'conditional') {
|
|
237
|
+
if (!ternaryHasQueryBranch(ctx, parsed.consequent, parsed.alternate)) return null
|
|
238
|
+
// `lowerTernary` already returns a complete parenthesised `(bf_ternary …)`
|
|
239
|
+
// pipeline — pass it straight through as `bf_attr`'s value argument
|
|
240
|
+
// rather than re-wrapping it in a redundant second set of parens.
|
|
241
|
+
const rendered = lowerTernary(ctx, parsed.test, parsed.consequent, parsed.alternate)
|
|
242
|
+
return `{{bf_attr ${JSON.stringify(attrName)} ${rendered}}}`
|
|
163
243
|
}
|
|
164
|
-
return null
|
|
244
|
+
if (parsed.kind !== 'call') return null
|
|
245
|
+
const node = matchRegisteredCall(ctx, parsed.callee, parsed.args)
|
|
246
|
+
if (!node || node.kind !== 'guard-list' || node.helper !== 'query') return null
|
|
247
|
+
const rendered = renderLoweringNode(ctx, node)
|
|
248
|
+
return rendered === null ? null : `{{bf_attr ${JSON.stringify(attrName)} (${rendered})}}`
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Whether a `call` node is a recognised `query` guard-list lowering. */
|
|
252
|
+
function isQueryGuardListCall(ctx: GoEmitContext, node: ParsedExpr): boolean {
|
|
253
|
+
if (node.kind !== 'call') return false
|
|
254
|
+
const lowered = matchRegisteredCall(ctx, node.callee, node.args)
|
|
255
|
+
return lowered?.kind === 'guard-list' && lowered.helper === 'query'
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Whether either branch of a ternary is (or, for a right-folded chain,
|
|
260
|
+
* eventually resolves to) a `query` guard-list call — the trigger for
|
|
261
|
+
* routing the whole ternary through `bf_attr` in `lowerRegisteredAttrCall`.
|
|
262
|
+
*/
|
|
263
|
+
function ternaryHasQueryBranch(
|
|
264
|
+
ctx: GoEmitContext,
|
|
265
|
+
consequent: ParsedExpr,
|
|
266
|
+
alternate: ParsedExpr,
|
|
267
|
+
): boolean {
|
|
268
|
+
const branchHasQuery = (n: ParsedExpr): boolean =>
|
|
269
|
+
n.kind === 'conditional' ? ternaryHasQueryBranch(ctx, n.consequent, n.alternate) : isQueryGuardListCall(ctx, n)
|
|
270
|
+
return branchHasQuery(consequent) || branchHasQuery(alternate)
|
|
165
271
|
}
|
|
166
272
|
|
|
167
273
|
/**
|