@barefootjs/go-template 0.17.0 → 0.18.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/expr/url-builder.d.ts.map +1 -1
- package/dist/adapter/go-template-adapter.d.ts +144 -14
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +449 -118
- package/dist/adapter/lib/compile-state.d.ts +27 -1
- package/dist/adapter/lib/compile-state.d.ts.map +1 -1
- package/dist/adapter/lib/go-emit.d.ts +9 -0
- package/dist/adapter/lib/go-emit.d.ts.map +1 -1
- package/dist/adapter/lib/go-naming.d.ts +23 -0
- package/dist/adapter/lib/go-naming.d.ts.map +1 -1
- package/dist/adapter/memo/memo-compute.d.ts +54 -5
- package/dist/adapter/memo/memo-compute.d.ts.map +1 -1
- package/dist/adapter/memo/memo-type.d.ts +10 -0
- package/dist/adapter/memo/memo-type.d.ts.map +1 -1
- package/dist/adapter/type/type-codegen.d.ts +5 -1
- package/dist/adapter/type/type-codegen.d.ts.map +1 -1
- package/dist/adapter/value/parsed-literal-to-go.d.ts +13 -4
- package/dist/adapter/value/parsed-literal-to-go.d.ts.map +1 -1
- package/dist/build.js +449 -118
- package/dist/conformance-pins.d.ts +12 -0
- package/dist/conformance-pins.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +468 -118
- package/package.json +3 -3
- package/src/__tests__/go-template-adapter.test.ts +652 -129
- package/src/__tests__/lowering-plugin.test.ts +56 -0
- package/src/adapter/expr/url-builder.ts +14 -3
- package/src/adapter/go-template-adapter.ts +637 -140
- package/src/adapter/lib/compile-state.ts +31 -0
- package/src/adapter/lib/go-emit.ts +20 -0
- package/src/adapter/lib/go-naming.ts +29 -0
- package/src/adapter/memo/memo-compute.ts +228 -18
- package/src/adapter/memo/memo-type.ts +19 -0
- package/src/adapter/type/type-codegen.ts +22 -3
- package/src/adapter/value/parsed-literal-to-go.ts +82 -9
- package/src/conformance-pins.ts +135 -0
- package/src/index.ts +1 -0
- package/src/test-render.ts +46 -8
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-fixture build-time contracts for shapes the Go template adapter
|
|
3
|
+
* intentionally refuses to lower. Lives here (not on the shared fixtures)
|
|
4
|
+
* so adding a new adapter doesn't require touching any cross-adapter
|
|
5
|
+
* file — every adapter declares its own refusal set against the
|
|
6
|
+
* canonical fixture corpus. Consumed by this package's own conformance
|
|
7
|
+
* test (as `expectedDiagnostics`) and by `bf compat` (issue-URL
|
|
8
|
+
* attribution).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ConformancePins } from '@barefootjs/jsx'
|
|
12
|
+
|
|
13
|
+
export const conformancePins: ConformancePins = {
|
|
14
|
+
// `style-object-dynamic` / `style-3-signals` no longer pinned — a
|
|
15
|
+
// `style={{ … }}` object literal now lowers to a CSS string with dynamic
|
|
16
|
+
// values interpolated (`background-color:{{.Color}};padding:8px`) via
|
|
17
|
+
// `tryLowerStyleObject` (#1322).
|
|
18
|
+
// Sibling-imported child component inside a loop body: the adapter
|
|
19
|
+
// emits `{{template "X" .}}` which only resolves if the user has
|
|
20
|
+
// compiled the sibling file and registered the template on the
|
|
21
|
+
// same instance. BF103 makes that requirement loud. (The barefoot
|
|
22
|
+
// CLI passes `siblingTemplatesRegistered: true` so CLI builds
|
|
23
|
+
// suppress the diagnostic — see compileJSX `siblingTemplatesRegistered`.)
|
|
24
|
+
'static-array-children': [{ code: 'BF103', severity: 'error' }],
|
|
25
|
+
// TodoApp / TodoAppSSR import `TodoItem` from a sibling file and
|
|
26
|
+
// call it inside a keyed `.map`. Same BF103 surface as
|
|
27
|
+
// `static-array-children` above — pinned at adapter level so the
|
|
28
|
+
// shared-component corpus stays adapter-neutral.
|
|
29
|
+
'todo-app': [{ code: 'BF103', severity: 'error' }],
|
|
30
|
+
'todo-app-ssr': [{ code: 'BF103', severity: 'error' }],
|
|
31
|
+
// `([emoji, users]) => ...` is an array-index tuple destructure — #2087
|
|
32
|
+
// Phase B's widened gate now admits this shape (`destructure-array-index-in-map`
|
|
33
|
+
// exercises the same `segments`-based lowering). The remaining refusal here
|
|
34
|
+
// is orthogonal: `entries` is a function-scope local const with a computed
|
|
35
|
+
// initializer (`Object.entries(props.reactions ?? {}).filter(...)`) that
|
|
36
|
+
// the Go adapter has no binding for (only a STRING-derived local resolves
|
|
37
|
+
// to a generated struct field, via `computeDerivedConstFields`/`isStringExpr`)
|
|
38
|
+
// — left unchecked this would silently execute-time-fail instead of
|
|
39
|
+
// building loud, so `renderLoop` raises BF101 for a bare-identifier loop
|
|
40
|
+
// array bound to such a const. See the `renderLoop` comment at the check
|
|
41
|
+
// site; Jinja / ERB apply the same narrow check for the same reason.
|
|
42
|
+
'static-array-from-props': [{ code: 'BF101', severity: 'error' }],
|
|
43
|
+
// Same computed-const array as above, plus the pre-existing BF103 (a
|
|
44
|
+
// sibling-imported child component used inside the loop body) — the
|
|
45
|
+
// destructure param itself no longer contributes a diagnostic.
|
|
46
|
+
'static-array-from-props-with-component': [
|
|
47
|
+
{ code: 'BF103', severity: 'error' },
|
|
48
|
+
{ code: 'BF101', severity: 'error' },
|
|
49
|
+
],
|
|
50
|
+
// (`style-3-signals` graduated alongside `style-object-dynamic` — see note
|
|
51
|
+
// above; the `style={{ … }}` object now lowers to a CSS string.)
|
|
52
|
+
// (`tagged-template-classname` graduated by #2092 — the tag resolves
|
|
53
|
+
// through the interleave-tag catalogue and desugars to an untagged
|
|
54
|
+
// template literal, so it lowers like any other className template.)
|
|
55
|
+
// #2038: a filter predicate whose body contains a NESTED callback call
|
|
56
|
+
// (`t => !picked().some(p => …)` / `t => picked().find(p => …)`). The
|
|
57
|
+
// evaluator refuses nested arrows and `renderFilterExpr` has no faithful
|
|
58
|
+
// Go form for the inner call (its `call` arm used to silently drop the
|
|
59
|
+
// arrow argument and render only the callee) — the compiler is loud
|
|
60
|
+
// instead of lossy. The `/* @client */` twin
|
|
61
|
+
// (`filter-nested-callback-predicate-client`) has no pin here: it must
|
|
62
|
+
// render clean on every adapter, which asserts the suppression contract.
|
|
63
|
+
// https://github.com/piconic-ai/barefootjs/issues/2038
|
|
64
|
+
'filter-nested-callback-predicate': [
|
|
65
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2038' },
|
|
66
|
+
],
|
|
67
|
+
'filter-nested-find-predicate': [
|
|
68
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2038' },
|
|
69
|
+
],
|
|
70
|
+
// #1310 / #2087: rest destructure in .map() callback. `isLowerableLoopDestructure`
|
|
71
|
+
// now admits every shape this fixture family exercises — each fixed/rest
|
|
72
|
+
// binding resolves via `buildSegmentAccessor`/`buildDestructureBindingMap`
|
|
73
|
+
// against a synthetic `$__bf_item0` range var (the reserved `__bf_item`
|
|
74
|
+
// name, depth-suffixed): a plain field → `$__bf_item0.Id`, an array-index
|
|
75
|
+
// step → `(index $__bf_item0 0)`, an array-rest → `(bf_slice $__bf_item0
|
|
76
|
+
// 1)` (composes under `.length` via `member()`'s generic `len <obj>` arm),
|
|
77
|
+
// and an object-rest member read (`rest.flag`) → `$__bf_item0.Flag`. A
|
|
78
|
+
// `{...rest}` SPREAD (`rest-destructure-object-spread-in-map`) routes
|
|
79
|
+
// through the new `bf_omit` runtime helper instead, so the residual omits
|
|
80
|
+
// exactly the sibling keys the pattern destructured out. No fixture in
|
|
81
|
+
// this family is pinned anymore — all six render to real Go / byte-exact
|
|
82
|
+
// HTML (`rest-destructure-object-in-map`, `rest-destructure-object-spread-in-map`,
|
|
83
|
+
// `rest-destructure-array-in-map`, `rest-destructure-nested-in-map`,
|
|
84
|
+
// `destructure-array-index-in-map`, `destructure-nested-object-in-map`).
|
|
85
|
+
// #1443: `[a, b].filter(Boolean).join(' ')` (registry Slot) now
|
|
86
|
+
// lowers to `bf_join (bf_filter_truthy (bf_arr ...)) " "`. No
|
|
87
|
+
// BF101 expected — pinned positively by the
|
|
88
|
+
// `branch-local-filter-join-go` template-output test below.
|
|
89
|
+
//
|
|
90
|
+
// #1448 Tier A — JS Array / String methods that the Go template
|
|
91
|
+
// adapter hasn't lowered yet. Each row drops once the
|
|
92
|
+
// corresponding method PR lands. Hono / CSR pass these out of
|
|
93
|
+
// the box (they evaluate JS at runtime) so the pin only applies
|
|
94
|
+
// here.
|
|
95
|
+
//
|
|
96
|
+
// `array-includes` / `string-includes` no longer pinned — both
|
|
97
|
+
// shapes lower via the shared `array-method` IR + the polymorphic
|
|
98
|
+
// `bf_includes` runtime helper that dispatches on
|
|
99
|
+
// `reflect.Kind()` (slice/array → element search, string →
|
|
100
|
+
// substring search). The condition-position lowering picks up
|
|
101
|
+
// the same emit through the `array-method` arm of
|
|
102
|
+
// `renderConditionExpr` (#1448 Tier A first PR).
|
|
103
|
+
//
|
|
104
|
+
// Remaining fixtures land at expression position and surface BF101
|
|
105
|
+
// via `convertExpressionToGo`. Distinct codes for the two paths is
|
|
106
|
+
// pre-existing adapter behaviour, not something this catalog
|
|
107
|
+
// should paper over — pinned literally here.
|
|
108
|
+
// `array-indexOf` / `array-lastIndexOf` no longer pinned —
|
|
109
|
+
// value-equality `bf_index_of` / `bf_last_index_of` Go runtime
|
|
110
|
+
// helpers handle the shape (#1448 Tier A second PR).
|
|
111
|
+
// `array-at` no longer pinned — the pre-existing `bf_at` runtime
|
|
112
|
+
// helper now lowers `.at(i)` (#1448 Tier A third PR).
|
|
113
|
+
// `array-concat` no longer pinned — the new `bf_concat` runtime
|
|
114
|
+
// helper merges two arrays into a single `[]any` (#1448 Tier A
|
|
115
|
+
// fourth PR).
|
|
116
|
+
// `array-slice` no longer pinned — the new `bf_slice` runtime
|
|
117
|
+
// helper carves out a sub-range with JS-compat clamping
|
|
118
|
+
// (#1448 Tier A fifth PR).
|
|
119
|
+
// `array-reverse` / `array-toReversed` no longer pinned —
|
|
120
|
+
// both share the `bf_reverse` helper since SSR templates
|
|
121
|
+
// render a snapshot and the JS mutate-vs-new distinction has
|
|
122
|
+
// no template-level meaning (#1448 Tier A sixth PR).
|
|
123
|
+
// `string-toLowerCase` / `string-toUpperCase` no longer pinned —
|
|
124
|
+
// pre-existing `bf_lower` / `bf_upper` runtime helpers wire to
|
|
125
|
+
// the JS method names at the adapter layer (#1448 Tier A
|
|
126
|
+
// seventh + eighth PRs).
|
|
127
|
+
// `string-trim` no longer pinned — pre-existing `bf_trim`
|
|
128
|
+
// (wraps `strings.TrimSpace`) handles the strip (#1448 Tier A
|
|
129
|
+
// ninth PR, closing out Tier A).
|
|
130
|
+
// #2073 follow-up: a function-reference `.map(format)` callback has no
|
|
131
|
+
// arrow body to serialize — not a CALLBACK_METHODS shape — so the
|
|
132
|
+
// UNSUPPORTED_METHODS gate refuses it with BF101 rather than emitting
|
|
133
|
+
// a broken template.
|
|
134
|
+
'array-map-function-reference': [{ code: 'BF101', severity: 'error' }],
|
|
135
|
+
}
|
package/src/index.ts
CHANGED
package/src/test-render.ts
CHANGED
|
@@ -615,13 +615,23 @@ function buildGoPropsInit(
|
|
|
615
615
|
// a bare `[]any{…}` would fail to compile against the typed field.
|
|
616
616
|
// Fall back to `[]any` when the field type is `[]any` / unknown.
|
|
617
617
|
const elemType = goSliceElemType(goTypes, componentName, goField)
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
}
|
|
624
|
-
|
|
618
|
+
let sliceLiteral: string
|
|
619
|
+
if (elemType && elemType.startsWith('map[')) {
|
|
620
|
+
// An untyped object-array Input field (an inline prop object type
|
|
621
|
+
// that didn't synthesize a named struct, e.g. `items: { title:
|
|
622
|
+
// string; tags: string[] }[]` — `typeInfoToGo`'s 'object' case,
|
|
623
|
+
// type-codegen.ts) resolves to `[]map[string]interface{}`, not a
|
|
624
|
+
// named struct. `goTypedSliceLiteralFromArray`'s `goStructLiteral`
|
|
625
|
+
// emits bare `Field: value` entries, which is struct-literal syntax
|
|
626
|
+
// and doesn't compile as a map literal's keys — route these through
|
|
627
|
+
// the map-literal builder instead (#2075, search-params-derived-filter).
|
|
628
|
+
sliceLiteral = goTypedMapSliceLiteralFromArray(value, elemType)
|
|
629
|
+
} else if (elemType) {
|
|
630
|
+
sliceLiteral = goTypedSliceLiteralFromArray(value, elemType)
|
|
631
|
+
} else {
|
|
632
|
+
sliceLiteral = goArrayLiteralFromArray(value)
|
|
633
|
+
}
|
|
634
|
+
lines.push(`\t\t${goField}: ${sliceLiteral},`)
|
|
625
635
|
} else if (value && typeof value === 'object') {
|
|
626
636
|
// Plain object → Go `map[string]any` literal (#1407 follow-up).
|
|
627
637
|
// Used by `jsx-spread-rest-prop` to populate the input-bag
|
|
@@ -658,7 +668,11 @@ function goSliceElemType(
|
|
|
658
668
|
new RegExp(`type ${componentName}Input struct \\{([\\s\\S]*?)\\n\\}`),
|
|
659
669
|
)
|
|
660
670
|
if (!struct) return null
|
|
661
|
-
|
|
671
|
+
// The element-type token can carry brackets/braces of its own (a map type
|
|
672
|
+
// like `map[string]interface{}`), not just word chars — broadened so the
|
|
673
|
+
// capture doesn't truncate at the first `[`. The token has no internal
|
|
674
|
+
// whitespace, so it still stops cleanly before a trailing `// comment`.
|
|
675
|
+
const field = struct[1].match(new RegExp(`\\n\\s*${goField}\\s+\\[\\]([\\w.[\\]{}]+)`))
|
|
662
676
|
if (!field) return null
|
|
663
677
|
const elem = field[1]
|
|
664
678
|
if (elem === 'any' || elem === 'interface{}') return null
|
|
@@ -683,6 +697,30 @@ function goTypedSliceLiteralFromArray(arr: unknown[], elemType: string): string
|
|
|
683
697
|
return `[]${elemType}{${entries.join(', ')}}`
|
|
684
698
|
}
|
|
685
699
|
|
|
700
|
+
/**
|
|
701
|
+
* Emit a typed Go slice-of-map literal (`[]map[string]interface{}{map[string]any{…}, …}`)
|
|
702
|
+
* for an untyped object-array Input field. Object elements become
|
|
703
|
+
* `map[string]any{"Field": val, …}` literals with PascalCase keys — Go
|
|
704
|
+
* template map lookup is case-sensitive, so `{{.Title}}` needs a capitalized
|
|
705
|
+
* key, same as the untyped `goArrayLiteralFromArray` fallback's object
|
|
706
|
+
* entries. `map[string]any` and `map[string]interface{}` are the identical
|
|
707
|
+
* type (`any` is the builtin alias for `interface{}`), so the emitted
|
|
708
|
+
* literal is assignable to `elemType` regardless of which spelling the
|
|
709
|
+
* Input struct field carries. (#2075, search-params-derived-filter)
|
|
710
|
+
*/
|
|
711
|
+
function goTypedMapSliceLiteralFromArray(arr: unknown[], elemType: string): string {
|
|
712
|
+
const entries = arr.map(v => {
|
|
713
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
714
|
+
return goMapLiteralFromObject(v as Record<string, unknown>, true)
|
|
715
|
+
}
|
|
716
|
+
if (typeof v === 'string') return `"${v.replace(/"/g, '\\"')}"`
|
|
717
|
+
if (typeof v === 'number' || typeof v === 'boolean') return String(v)
|
|
718
|
+
if (v === null) return 'nil'
|
|
719
|
+
return goArrayLiteralFromArray(v as unknown[])
|
|
720
|
+
})
|
|
721
|
+
return `[]${elemType}{${entries.join(', ')}}`
|
|
722
|
+
}
|
|
723
|
+
|
|
686
724
|
/**
|
|
687
725
|
* Emit a keyed Go struct literal (`Elem{Field: val, …}`) with PascalCase field
|
|
688
726
|
* names. Only the keys the caller supplied are set, so an omitted optional prop
|