@barefootjs/go-template 0.35.0 → 0.35.1
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/expr/url-builder.d.ts +37 -1
- package/dist/adapter/expr/url-builder.d.ts.map +1 -1
- package/dist/adapter/go-template-adapter.d.ts +18 -1
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +42 -22
- package/dist/conformance-pins.d.ts.map +1 -1
- package/dist/index.js +45 -25
- package/dist/vite.js +70 -50
- package/package.json +5 -5
- package/src/__tests__/go-template-adapter.test.ts +167 -7
- package/src/adapter/expr/url-builder.ts +63 -28
- package/src/adapter/go-template-adapter.ts +62 -37
- package/src/conformance-pins.ts +13 -7
|
@@ -130,7 +130,7 @@ import { analyzeBakeableStaticChildLoop, scalarToGoLiteral, type BakedStaticChil
|
|
|
130
130
|
import { analyzeBakeableStaticElementLoop } from "./analysis/static-element-loop-bake.ts"
|
|
131
131
|
import type { GoEmitContext } from "./emit-context.ts"
|
|
132
132
|
import { inlineLocalHelperCall } from "./expr/helper-inline.ts"
|
|
133
|
-
import { lowerRegisteredAttrCall, lowerRegisteredCall, lowerRegisteredCallNode, lowerTernary } from "./expr/url-builder.ts"
|
|
133
|
+
import { lowerRegisteredAttrCall, lowerRegisteredCall, lowerRegisteredCallNode, lowerTemplateLiteralValue, lowerTernary, lowerValueOperand } from "./expr/url-builder.ts"
|
|
134
134
|
import {
|
|
135
135
|
convertInitialValue,
|
|
136
136
|
jsLiteralToGo,
|
|
@@ -5180,12 +5180,35 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
5180
5180
|
// arithmetic index lowers correctly. A multi-token operand
|
|
5181
5181
|
// (`bf_add $i 1`) must be parenthesised or Go parses it as extra
|
|
5182
5182
|
// `bf_get` arguments.
|
|
5183
|
-
return `bf_get ${wrapIfMultiToken(
|
|
5183
|
+
return `bf_get ${wrapIfMultiToken(this.emitOperand(object, emit))} ${wrapIfMultiToken(this.emitOperand(index, emit))}`
|
|
5184
|
+
}
|
|
5185
|
+
|
|
5186
|
+
/**
|
|
5187
|
+
* A `binary`/`logical`/`unary` operand that feeds a prefix-call Go form
|
|
5188
|
+
* (`bf_mul a b`, `and a b`, `not a`, …), which cannot host a raw `{{…}}`
|
|
5189
|
+
* action the way the generic `emit` dispatcher's `templateLiteral()` would
|
|
5190
|
+
* produce for a template-literal operand (#2863). ONLY a template literal
|
|
5191
|
+
* is special-cased here, folded through `bf_concat_str` (the same door
|
|
5192
|
+
* `lowerValueOperand` uses for a ternary branch / helper-call arg /
|
|
5193
|
+
* query-guard value); every other kind still goes through the caller's own
|
|
5194
|
+
* `emit` closure unchanged. This matters: routing every operand through
|
|
5195
|
+
* `lowerValueOperand`'s `convertExpressionToGo` fallback (rather than only
|
|
5196
|
+
* the one broken kind) would ALSO pick up `convertExpressionToGo`'s own
|
|
5197
|
+
* string-keyed fast paths — e.g. inlining a bare identifier bound to a
|
|
5198
|
+
* local literal const — which `emit`'s AST-walk `identifier()` dispatch
|
|
5199
|
+
* does not do, a real behavioral divergence for that shape (caught by the
|
|
5200
|
+
* #2236 loop-param-shadowing regression pins).
|
|
5201
|
+
*/
|
|
5202
|
+
private emitOperand(n: ParsedExpr, emit: (e: ParsedExpr) => string): string {
|
|
5203
|
+
if (n.kind === 'template-literal') {
|
|
5204
|
+
return wrapIfMultiToken(lowerTemplateLiteralValue(this.emitCtx, n.parts))
|
|
5205
|
+
}
|
|
5206
|
+
return emit(n)
|
|
5184
5207
|
}
|
|
5185
5208
|
|
|
5186
5209
|
binary(op: string, left: ParsedExpr, right: ParsedExpr, emit: (e: ParsedExpr) => string): string {
|
|
5187
|
-
const l =
|
|
5188
|
-
const r =
|
|
5210
|
+
const l = this.emitOperand(left, emit)
|
|
5211
|
+
const r = this.emitOperand(right, emit)
|
|
5189
5212
|
// Every Go form below is a prefix function call (`bf_mul a b`, `gt a b`,
|
|
5190
5213
|
// `eq a b`), so a COMPOUND operand must be parenthesised or the template
|
|
5191
5214
|
// parser folds its tokens into the call's argument list — e.g.
|
|
@@ -5268,7 +5291,7 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
5268
5291
|
}
|
|
5269
5292
|
|
|
5270
5293
|
unary(op: string, argument: ParsedExpr, emit: (e: ParsedExpr) => string): string {
|
|
5271
|
-
const arg =
|
|
5294
|
+
const arg = this.emitOperand(argument, emit)
|
|
5272
5295
|
// `not` is a Go template prefix builtin like `and`/`or` (see `logical()`
|
|
5273
5296
|
// below) — a multi-token argument (e.g. `or a b`) must be parenthesised
|
|
5274
5297
|
// or it degrades into extra sibling args of `not` itself (#2758: `not
|
|
@@ -5291,8 +5314,8 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
5291
5314
|
// `and`/`or`. This makes `searchParams().get(k) ?? d` lower to
|
|
5292
5315
|
// `or (.SearchParams.Get "sort") "none"` instead of the broken
|
|
5293
5316
|
// `or .SearchParams.Get "sort" "none"`.
|
|
5294
|
-
const wrapLeft = wrapIfMultiToken(
|
|
5295
|
-
const wrapRight = wrapIfMultiToken(
|
|
5317
|
+
const wrapLeft = wrapIfMultiToken(this.emitOperand(left, emit))
|
|
5318
|
+
const wrapRight = wrapIfMultiToken(this.emitOperand(right, emit))
|
|
5296
5319
|
if (op === '&&') return `and ${wrapLeft} ${wrapRight}`
|
|
5297
5320
|
// `??` on a nillable prop needs true JS nullish semantics (#2248): Go's
|
|
5298
5321
|
// `or` is truthiness-based, so `{{or .Label "Default"}}` falls back on a
|
|
@@ -5600,8 +5623,16 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
5600
5623
|
method: ArrayMethod,
|
|
5601
5624
|
object: ParsedExpr,
|
|
5602
5625
|
args: ParsedExpr[],
|
|
5603
|
-
|
|
5626
|
+
rawEmit: (e: ParsedExpr) => string,
|
|
5604
5627
|
): string {
|
|
5628
|
+
// Every `emit(...)` call below lowers an ARGUMENT position (the
|
|
5629
|
+
// receiver or a `.method(...)` arg) that feeds a prefix-call Go form
|
|
5630
|
+
// (`bf_join (...) ...`, `bf_replace ... ... ...`, …) — shadow the
|
|
5631
|
+
// dispatcher's own `emit` with `emitOperand` (#2863) so a
|
|
5632
|
+
// template-literal receiver/argument (e.g. `items.join(\`${a}-${b}\`)`)
|
|
5633
|
+
// folds through `bf_concat_str` instead of leaking the TEXT-position
|
|
5634
|
+
// `templateLiteral()` form into another action's argument list.
|
|
5635
|
+
const emit = (e: ParsedExpr): string => this.emitOperand(e, rawEmit)
|
|
5605
5636
|
// `bf_join` etc. are registered in the runtime FuncMap. The exhaustive
|
|
5606
5637
|
// switch on `method` mirrors the IR-level discriminator — adding a new
|
|
5607
5638
|
// `ArrayMethod` variant becomes a TS compile error until every adapter
|
|
@@ -6682,34 +6713,23 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
6682
6713
|
}
|
|
6683
6714
|
continue
|
|
6684
6715
|
}
|
|
6685
|
-
// A ternary
|
|
6686
|
-
//
|
|
6687
|
-
//
|
|
6688
|
-
//
|
|
6689
|
-
//
|
|
6690
|
-
//
|
|
6691
|
-
//
|
|
6692
|
-
//
|
|
6693
|
-
//
|
|
6694
|
-
//
|
|
6695
|
-
|
|
6696
|
-
exprOut.parsed
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
go = go.slice(2, -2)
|
|
6703
|
-
}
|
|
6704
|
-
// Skip the fragment re-check for the just-unwrapped single-part case —
|
|
6705
|
-
// `kind` is still (accurately) `template-literal`, which would
|
|
6706
|
-
// otherwise re-trip `isTemplateFragment`'s `kind === 'template-literal'`
|
|
6707
|
-
// branch on the ALREADY-unwrapped bare value.
|
|
6708
|
-
if (!singlePartTemplateLiteral && this.isTemplateFragment(go, exprOut.parsed?.kind)) {
|
|
6709
|
-
// A genuine multi-part `{{if}}...{{end}}`-shaped fragment can't be a
|
|
6710
|
-
// bare pipeline argument — refuse loudly rather than silently
|
|
6711
|
-
// dropping the prop (the #2445 bug was exactly a silent drop one
|
|
6712
|
-
// level up).
|
|
6716
|
+
// A ternary or template literal (`row.on ? "yes" : "no"`, `` `#${row.id}
|
|
6717
|
+
// ${row.label}` ``) in this ARGUMENT position (a `bf_with_props`/
|
|
6718
|
+
// `bf_reprops` value, not a text position) must lower through the
|
|
6719
|
+
// shared value-operand door (#2863/#2335) — `convertExpressionToGo`'s
|
|
6720
|
+
// generic path returns `templateLiteral()`'s TEXT-position mixed
|
|
6721
|
+
// literal-text-plus-`{{…}}`-actions form for a template literal, which
|
|
6722
|
+
// is not a legal bare pipeline argument (multi-part) or needed an
|
|
6723
|
+
// ad-hoc single-part unwrap here (this replaces both). Recompute from
|
|
6724
|
+
// the already-validated `exprOut.parsed` tree rather than trying to
|
|
6725
|
+
// unwrap or refuse the text-position string after the fact.
|
|
6726
|
+
if (exprOut.parsed?.kind === 'template-literal' || exprOut.parsed?.kind === 'conditional') {
|
|
6727
|
+
go = lowerValueOperand(this.emitCtx, exprOut.parsed)
|
|
6728
|
+
} else if (this.isTemplateFragment(go, exprOut.parsed?.kind)) {
|
|
6729
|
+
// A genuine `{{if}}...{{end}}`-shaped (or other `{{`-leading action)
|
|
6730
|
+
// fragment can't be a bare pipeline argument — refuse loudly rather
|
|
6731
|
+
// than silently dropping the prop (the #2445 bug was exactly a
|
|
6732
|
+
// silent drop one level up).
|
|
6713
6733
|
this.state.errors.push({
|
|
6714
6734
|
code: 'BF101',
|
|
6715
6735
|
severity: 'error',
|
|
@@ -7164,7 +7184,12 @@ export class GoTemplateAdapter extends BaseAdapter implements ParsedExprEmitter,
|
|
|
7164
7184
|
}
|
|
7165
7185
|
|
|
7166
7186
|
case 'template-literal':
|
|
7167
|
-
|
|
7187
|
+
// #2863 follow-up: fold to one Go value (`bf_concat_str` chain) via
|
|
7188
|
+
// the same door as the `conditional` case just above, instead of
|
|
7189
|
+
// `renderParsedExpr`'s TEXT-position `templateLiteral()` form —
|
|
7190
|
+
// which would leak raw `{{…}}` into THIS condition-expression's own
|
|
7191
|
+
// pipeline position (e.g. `(a === \`${x}-${y}\`) ? … : …`).
|
|
7192
|
+
return plain(lowerTemplateLiteralValue(this.emitCtx, expr.parts))
|
|
7168
7193
|
|
|
7169
7194
|
case 'arrow':
|
|
7170
7195
|
// A standalone arrow has no Go condition form (callbacks reach the
|
package/src/conformance-pins.ts
CHANGED
|
@@ -52,13 +52,16 @@ export const conformancePins: ConformancePins = {
|
|
|
52
52
|
'filter-nested-find-predicate': [{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2320' }],
|
|
53
53
|
'date-method-uncatalogued': [{ code: 'BF021', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2356' }],
|
|
54
54
|
'rich-prop-client-read': [{ code: 'BF049', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2648' }],
|
|
55
|
-
//
|
|
55
|
+
// A ternary or array literal LITERALLY WRAPPING JSX at a non-children prop
|
|
56
56
|
// position (e.g. `header={cond ? <a/> : <b/>}`) is refused ahead of
|
|
57
57
|
// `adapter.generate()` in the shared jsx-to-ir.ts phase, so it is pinned
|
|
58
58
|
// identically on every adapter (including Hono) — same reasoning as
|
|
59
|
-
// `rich-prop-client-read` above.
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
// `rich-prop-client-read` above. This is the permanent, intended behavior
|
|
60
|
+
// (formerly tracked as #2667, closed): the issue's own acceptance criteria
|
|
61
|
+
// treated a loud refusal as fully resolving the silent-divergence bug, so
|
|
62
|
+
// no open issue tracks further work here.
|
|
63
|
+
'jsx-element-prop-ternary': [{ code: 'BF021', severity: 'error' }],
|
|
64
|
+
'jsx-element-prop-array': [{ code: 'BF021', severity: 'error' }],
|
|
62
65
|
// `jsx-element-prop-fragment-conditional` (#2703) is NOT pinned here — it
|
|
63
66
|
// renders correctly since `queueDynamicPropDefine` (go-template-adapter.ts)
|
|
64
67
|
// extended the reserved `children` slot's `bf_with_children`/`bf_tmpl`
|
|
@@ -88,11 +91,14 @@ export const conformancePins: ConformancePins = {
|
|
|
88
91
|
severity: 'error',
|
|
89
92
|
issue: 'https://github.com/piconic-ai/barefootjs/issues/2700',
|
|
90
93
|
}],
|
|
91
|
-
//
|
|
94
|
+
// A reactive primitive invoked through a namespace import
|
|
92
95
|
// (`import * as bf from '@barefootjs/client'`, `bf.createSignal(...)`)
|
|
93
96
|
// that the analyzer's checker-less fast path cannot recognize refuses
|
|
94
97
|
// loudly (BF013) instead of silently dropping the declaration — fired
|
|
95
98
|
// in the shared analyzer pass ahead of any adapter's `adapter.generate()`,
|
|
96
|
-
// so all nine adapters (including Hono) pin this identically.
|
|
97
|
-
|
|
99
|
+
// so all nine adapters (including Hono) pin this identically. A compile
|
|
100
|
+
// that supplies a shared `ts.Program` (e.g. via `@barefootjs/vite`)
|
|
101
|
+
// resolves the primitive normally and never reaches this refusal (formerly
|
|
102
|
+
// tracked as #2771, closed) — no open issue tracks further work.
|
|
103
|
+
'namespace-import-primitive': [{ code: 'BF013', severity: 'error' }],
|
|
98
104
|
}
|