@barefootjs/go-template 0.31.0 → 0.31.2
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 +49 -41
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +167 -101
- package/dist/adapter/lib/types.d.ts +4 -1
- package/dist/adapter/lib/types.d.ts.map +1 -1
- package/dist/adapter/memo/memo-compute.d.ts +9 -0
- package/dist/adapter/memo/memo-compute.d.ts.map +1 -1
- package/dist/adapter/memo/memo-type.d.ts +2 -0
- package/dist/adapter/memo/memo-type.d.ts.map +1 -1
- package/dist/adapter/props/prop-types.d.ts +2 -1
- package/dist/adapter/props/prop-types.d.ts.map +1 -1
- package/dist/adapter/spread/spread-codegen.d.ts.map +1 -1
- package/dist/adapter/type/type-codegen.d.ts +22 -5
- package/dist/adapter/type/type-codegen.d.ts.map +1 -1
- package/dist/adapter/value/parsed-literal-to-go.d.ts +12 -0
- package/dist/adapter/value/parsed-literal-to-go.d.ts.map +1 -1
- package/dist/adapter/value/value-lowering.d.ts +3 -0
- package/dist/adapter/value/value-lowering.d.ts.map +1 -1
- package/dist/index.js +168 -104
- package/dist/render-divergences.d.ts +6 -0
- package/dist/render-divergences.d.ts.map +1 -1
- package/dist/vite.js +493 -192
- package/package.json +5 -5
- package/src/__tests__/go-template-adapter.test.ts +155 -10
- package/src/adapter/go-template-adapter.ts +213 -128
- package/src/adapter/lib/types.ts +4 -1
- package/src/adapter/memo/memo-compute.ts +21 -17
- package/src/adapter/memo/memo-type.ts +5 -5
- package/src/adapter/props/prop-types.ts +6 -5
- package/src/adapter/spread/spread-codegen.ts +12 -5
- package/src/adapter/type/type-codegen.ts +86 -25
- package/src/adapter/value/parsed-literal-to-go.ts +28 -10
- package/src/adapter/value/value-lowering.ts +65 -29
- package/src/render-divergences.ts +6 -15
- package/src/test-render.ts +6 -1
|
@@ -17,7 +17,7 @@ import type { GoEmitContext } from '../emit-context.ts'
|
|
|
17
17
|
import type { PropFallbackVar } from '../lib/types.ts'
|
|
18
18
|
import { capitalizeFieldName } from '../lib/go-naming.ts'
|
|
19
19
|
import { escapeGoString } from '../lib/go-emit.ts'
|
|
20
|
-
import { parsedLiteralToGo } from './parsed-literal-to-go.ts'
|
|
20
|
+
import { numberLiteralRawGo, parsedLiteralToGo } from './parsed-literal-to-go.ts'
|
|
21
21
|
import { collapseLiteralUnion } from '../type/type-codegen.ts'
|
|
22
22
|
|
|
23
23
|
/** Default for `getSignalInitialValueAsGo`'s optional fallback-var map. */
|
|
@@ -40,16 +40,25 @@ const EMPTY_PROP_FALLBACK_VARS: ReadonlyMap<string, PropFallbackVar> = new Map()
|
|
|
40
40
|
* undefined` half is source-level documentation of nullability, not a
|
|
41
41
|
* Go-representable branch, so it's unwrapped to its single non-
|
|
42
42
|
* undefined/null primitive branch.
|
|
43
|
+
*
|
|
44
|
+
* `param.name` is the LOCAL binding — `nillablePropNames` (a source-level
|
|
45
|
+
* analysis set, `collectNillablePropNames`) stays keyed by it — while the
|
|
46
|
+
* emitted `in.<Field>` reference is caller-facing (`sourceName ?? name`,
|
|
47
|
+
* #2525), so the two must resolve separately rather than off one name.
|
|
43
48
|
*/
|
|
44
|
-
function nillableAwarePropRef(
|
|
45
|
-
|
|
49
|
+
function nillableAwarePropRef(
|
|
50
|
+
ctx: GoEmitContext,
|
|
51
|
+
param: { name: string; sourceName?: string },
|
|
52
|
+
expectedType: TypeInfo,
|
|
53
|
+
): string {
|
|
54
|
+
const fieldRef = `in.${capitalizeFieldName(param.sourceName ?? param.name)}`
|
|
46
55
|
const scalar =
|
|
47
56
|
expectedType.kind === 'primitive'
|
|
48
57
|
? expectedType
|
|
49
58
|
: expectedType.kind === 'union' && expectedType.unionTypes?.length === 2
|
|
50
59
|
? expectedType.unionTypes.find(t => t.primitive !== 'undefined' && t.primitive !== 'null')
|
|
51
60
|
: undefined
|
|
52
|
-
if (ctx.state.nillablePropNames.has(
|
|
61
|
+
if (ctx.state.nillablePropNames.has(param.name) && scalar?.kind === 'primitive') {
|
|
53
62
|
const goType =
|
|
54
63
|
scalar.primitive === 'boolean' ? 'bool' :
|
|
55
64
|
scalar.primitive === 'number' ? 'float64' :
|
|
@@ -70,41 +79,62 @@ export function convertInitialValue(
|
|
|
70
79
|
ctx: GoEmitContext,
|
|
71
80
|
value: string,
|
|
72
81
|
_typeInfo: TypeInfo,
|
|
73
|
-
propsParams?: { name: string }[],
|
|
82
|
+
propsParams?: { name: string; sourceName?: string }[],
|
|
74
83
|
preParsed?: ParsedExpr,
|
|
75
84
|
): string {
|
|
76
85
|
// Literal unions collapse to their backing primitive the same way
|
|
77
86
|
// `typeInfoToGo` collapses the field's type — the two MUST agree, or a
|
|
78
87
|
// `string` field gets a `nil` seed (#2477's `go run` failure).
|
|
79
88
|
const typeInfo = collapseLiteralUnion(_typeInfo)
|
|
80
|
-
const propRef = (
|
|
89
|
+
const propRef = (param: { name: string; sourceName?: string }): string =>
|
|
90
|
+
nillableAwarePropRef(ctx, param, typeInfo)
|
|
81
91
|
|
|
82
92
|
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(value)) {
|
|
83
|
-
|
|
84
|
-
|
|
93
|
+
const param = propsParams?.find(p => p.name === value)
|
|
94
|
+
if (param) {
|
|
95
|
+
return propRef(param)
|
|
85
96
|
}
|
|
86
97
|
}
|
|
87
98
|
|
|
88
99
|
const propName = ctx.extractPropNameFromInitialValue(value, preParsed)
|
|
89
|
-
|
|
90
|
-
|
|
100
|
+
const param = propName ? propsParams?.find(p => p.name === propName) : undefined
|
|
101
|
+
if (param) {
|
|
102
|
+
return propRef(param)
|
|
91
103
|
}
|
|
92
104
|
|
|
93
105
|
if (typeInfo.kind === 'primitive') {
|
|
94
106
|
if (typeInfo.primitive === 'boolean') {
|
|
107
|
+
// Structural first: the SAME initial value, already parsed
|
|
108
|
+
// (`SignalInfo.parsed`/module-const `parsed`) — text-matching
|
|
109
|
+
// `value === 'true'` is the fallback for a caller with no `preParsed`
|
|
110
|
+
// (an unsupported shape `tsNodeToParsedExpr` couldn't represent).
|
|
111
|
+
if (preParsed?.kind === 'literal' && preParsed.literalType === 'boolean' && typeof preParsed.value === 'boolean') {
|
|
112
|
+
return preParsed.value ? 'true' : 'false'
|
|
113
|
+
}
|
|
95
114
|
return value === 'true' ? 'true' : 'false'
|
|
96
115
|
}
|
|
97
116
|
if (typeInfo.primitive === 'number') {
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
117
|
+
// Structural first — `numberLiteralRawGo` unwraps a leading unary minus
|
|
118
|
+
// (#2168 math-methods: `createSignal(-7.6)`) off the literal's OWN
|
|
119
|
+
// `raw` token (exact source spelling, never a re-stringified value).
|
|
120
|
+
const numGo = preParsed ? numberLiteralRawGo(preParsed) : null
|
|
121
|
+
if (numGo !== null) return numGo
|
|
122
|
+
// Text fallback for a caller with no `preParsed`. Leading `-` handled
|
|
123
|
+
// the same way (without it, a negative initial value never matches
|
|
124
|
+
// either literal shape below and silently falls to the `0`
|
|
125
|
+
// zero-value fallback, regardless of the field's Go type).
|
|
102
126
|
if (/^-?\d+$/.test(value)) return value
|
|
103
127
|
if (/^-?\d+\.\d+$/.test(value)) return value
|
|
104
128
|
return '0'
|
|
105
129
|
}
|
|
106
130
|
if (typeInfo.primitive === 'string') {
|
|
107
|
-
|
|
131
|
+
// Structural first: `JSON.stringify` re-quotes/escapes the literal's
|
|
132
|
+
// unquoted `value` for Go — correct for any embedded quote/backslash,
|
|
133
|
+
// unlike the text fallback's blind `'` → `"` swap below.
|
|
134
|
+
if (preParsed?.kind === 'literal' && preParsed.literalType === 'string' && typeof preParsed.value === 'string') {
|
|
135
|
+
return JSON.stringify(preParsed.value)
|
|
136
|
+
}
|
|
137
|
+
if (value.startsWith("'") && value.endsWith("'")) {
|
|
108
138
|
return value.replace(/'/g, '"')
|
|
109
139
|
}
|
|
110
140
|
if (value.startsWith('"') && value.endsWith('"')) {
|
|
@@ -227,24 +257,28 @@ export function objectLiteralToGoMap(ctx: GoEmitContext, expr: ParsedExpr): stri
|
|
|
227
257
|
export function getSignalInitialValueAsGo(
|
|
228
258
|
ctx: GoEmitContext,
|
|
229
259
|
initialValue: string,
|
|
230
|
-
propsParams: { name: string }[],
|
|
260
|
+
propsParams: { name: string; sourceName?: string }[],
|
|
231
261
|
propFallbackVars: ReadonlyMap<string, PropFallbackVar> = EMPTY_PROP_FALLBACK_VARS,
|
|
232
262
|
signalType?: TypeInfo,
|
|
233
263
|
): string {
|
|
234
|
-
const propRef = (
|
|
235
|
-
signalType
|
|
264
|
+
const propRef = (param: { name: string; sourceName?: string }): string =>
|
|
265
|
+
signalType
|
|
266
|
+
? nillableAwarePropRef(ctx, param, signalType)
|
|
267
|
+
: `in.${capitalizeFieldName(param.sourceName ?? param.name)}`
|
|
236
268
|
|
|
237
|
-
|
|
269
|
+
const directParam = propsParams.find(p => p.name === initialValue)
|
|
270
|
+
if (directParam) {
|
|
238
271
|
const hoisted = propFallbackVars.get(initialValue)
|
|
239
272
|
if (hoisted) return hoisted.varName
|
|
240
|
-
return propRef(
|
|
273
|
+
return propRef(directParam)
|
|
241
274
|
}
|
|
242
275
|
|
|
243
276
|
const propName = ctx.extractPropNameFromInitialValue(initialValue)
|
|
244
|
-
|
|
245
|
-
|
|
277
|
+
const param = propName ? propsParams.find(p => p.name === propName) : undefined
|
|
278
|
+
if (param) {
|
|
279
|
+
const hoisted = propFallbackVars.get(propName!)
|
|
246
280
|
if (hoisted) return hoisted.varName
|
|
247
|
-
return propRef(
|
|
281
|
+
return propRef(param)
|
|
248
282
|
}
|
|
249
283
|
|
|
250
284
|
// single quotes are normalized to Go double quotes
|
|
@@ -320,7 +354,7 @@ function resolveMapJoinBaseAsGo(
|
|
|
320
354
|
ctx: GoEmitContext,
|
|
321
355
|
object: ParsedExpr,
|
|
322
356
|
signals: { getter: string; initialValue: string; type?: TypeInfo; parsed?: ParsedExpr }[],
|
|
323
|
-
propsParams: { name: string }[],
|
|
357
|
+
propsParams: { name: string; sourceName?: string }[],
|
|
324
358
|
): string | null {
|
|
325
359
|
if (object.kind === 'call' && object.callee.kind === 'identifier' && object.args.length === 0) {
|
|
326
360
|
const calleeName = object.callee.name
|
|
@@ -337,8 +371,9 @@ function resolveMapJoinBaseAsGo(
|
|
|
337
371
|
: object.kind === 'identifier'
|
|
338
372
|
? object.name
|
|
339
373
|
: null
|
|
340
|
-
|
|
341
|
-
|
|
374
|
+
const param = propName ? propsParams.find(p => p.name === propName) : undefined
|
|
375
|
+
if (param) {
|
|
376
|
+
return `in.${capitalizeFieldName(param.sourceName ?? param.name)}`
|
|
342
377
|
}
|
|
343
378
|
|
|
344
379
|
if (object.kind === 'array-literal') {
|
|
@@ -376,7 +411,7 @@ export function mapJoinChainToGo(
|
|
|
376
411
|
ctx: GoEmitContext,
|
|
377
412
|
chain: { object: ParsedExpr; arrow: Extract<ParsedExpr, { kind: 'arrow' }>; sepArg?: ParsedExpr },
|
|
378
413
|
signals: { getter: string; initialValue: string; type?: TypeInfo; parsed?: ParsedExpr }[],
|
|
379
|
-
propsParams: { name: string }[],
|
|
414
|
+
propsParams: { name: string; sourceName?: string }[],
|
|
380
415
|
propFallbackVars: ReadonlyMap<string, PropFallbackVar>,
|
|
381
416
|
): string | null {
|
|
382
417
|
const itemsGo = resolveMapJoinBaseAsGo(ctx, chain.object, signals, propsParams)
|
|
@@ -393,11 +428,12 @@ export function mapJoinChainToGo(
|
|
|
393
428
|
for (const name of freeVars) {
|
|
394
429
|
const sig = signals.find(s => s.getter === name)
|
|
395
430
|
let goExpr: string | null = null
|
|
431
|
+
const freeVarParam = propsParams.find(p => p.name === name)
|
|
396
432
|
if (sig) {
|
|
397
433
|
goExpr = getSignalInitialValueAsGo(ctx, sig.initialValue, propsParams, propFallbackVars, sig.type)
|
|
398
|
-
} else if (
|
|
434
|
+
} else if (freeVarParam) {
|
|
399
435
|
const hoisted = propFallbackVars.get(name)
|
|
400
|
-
goExpr = hoisted ? hoisted.varName : `in.${capitalizeFieldName(name)}`
|
|
436
|
+
goExpr = hoisted ? hoisted.varName : `in.${capitalizeFieldName(freeVarParam.sourceName ?? name)}`
|
|
401
437
|
}
|
|
402
438
|
if (goExpr === null) return null
|
|
403
439
|
envEntries.push(`${JSON.stringify(name)}: ${goExpr}`)
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
* fixture-divergences section of `ui/compat.lock.json` — surfaced on
|
|
13
13
|
* the docs compatibility-matrix page. Graduating an entry means fixing
|
|
14
14
|
* the adapter (or the shared compiler layer) and deleting the line.
|
|
15
|
+
*
|
|
16
|
+
* Empty — the file's last live entry (`aliased-destructured-prop`, #2525)
|
|
17
|
+
* graduated: the Input struct field is now keyed by `sourceName ?? name`
|
|
18
|
+
* (caller-facing), so the caller-side composite literal `go run`s clean.
|
|
19
|
+
* Keep the file (and this header) when the set is empty — the next
|
|
20
|
+
* divergence lands here, not in a re-created file.
|
|
15
21
|
*/
|
|
16
22
|
|
|
17
23
|
import type { RenderDivergences } from '@barefootjs/jsx'
|
|
@@ -29,19 +35,4 @@ export const renderDivergences: RenderDivergences = {
|
|
|
29
35
|
// production). `buildDynamicChildLoopSeeding` (this package's
|
|
30
36
|
// `test-render.ts`) now replicates that documented contract for a
|
|
31
37
|
// signal-backed dynamic child-component loop.
|
|
32
|
-
|
|
33
|
-
// Onboarding TSX-fidelity fixtures (PR #2461): `expectedHtml` was
|
|
34
|
-
// hand-authored to the CORRECT output while the emission bug lived in
|
|
35
|
-
// the shared compiler layer — every adapter, including the Hono
|
|
36
|
-
// reference, used to emit the broken form. That shared-layer defect
|
|
37
|
-
// (#2460) is now FIXED (b4f5075): `expectedHtml` is generated from the
|
|
38
|
-
// Hono reference like any other fixture. The remaining gap is
|
|
39
|
-
// Go-specific — the Input struct field stays keyed by the local
|
|
40
|
-
// binding instead of `sourceName ?? name`, so the caller-side struct
|
|
41
|
-
// literal fails `go run` outright — tracked by #2525 (go-template's
|
|
42
|
-
// `go run` exit-1 failure). Graduate by keying the Input struct field
|
|
43
|
-
// by `sourceName ?? name` and deleting this line (and the matching
|
|
44
|
-
// hono `skipJsx` entry, already gone).
|
|
45
|
-
'aliased-destructured-prop':
|
|
46
|
-
'aliased destructured prop `{ n: count }` loses its rename — the Input struct field is Count `json:"count"`, so the caller-side struct literal keyed by the real prop name fails `go run` outright (unknown field N, exit 1) (https://github.com/piconic-ai/barefootjs/issues/2525)',
|
|
47
38
|
}
|
package/src/test-render.ts
CHANGED
|
@@ -673,7 +673,12 @@ function buildGoPropsInit(
|
|
|
673
673
|
// declared params are `className` / `type`) emits a top-level
|
|
674
674
|
// `Placeholder:` initializer and Go fails with `unknown field Placeholder
|
|
675
675
|
// in struct literal of type InputInput`. (#1467 Phase 2b)
|
|
676
|
-
|
|
676
|
+
// `key` is caller-facing (what the fixture's caller supplied), so this
|
|
677
|
+
// set must be built from `sourceName ?? name` — a local-only set routes
|
|
678
|
+
// an aliased param into the rest bag.
|
|
679
|
+
const declaredParams = new Set(
|
|
680
|
+
(ir?.metadata.propsParams ?? []).map(p => p.sourceName ?? p.name),
|
|
681
|
+
)
|
|
677
682
|
const restPropsName = ir?.metadata.restPropsName ?? null
|
|
678
683
|
const restBagField = restPropsName ? capitalizeFieldName(restPropsName) : null
|
|
679
684
|
|