@barefootjs/go-template 0.16.0 → 0.17.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.
Files changed (73) hide show
  1. package/dist/adapter/analysis/component-tree.d.ts +23 -0
  2. package/dist/adapter/analysis/component-tree.d.ts.map +1 -0
  3. package/dist/adapter/emit-context.d.ts +53 -0
  4. package/dist/adapter/emit-context.d.ts.map +1 -0
  5. package/dist/adapter/expr/helper-inline.d.ts +31 -0
  6. package/dist/adapter/expr/helper-inline.d.ts.map +1 -0
  7. package/dist/adapter/expr/url-builder.d.ts +23 -0
  8. package/dist/adapter/expr/url-builder.d.ts.map +1 -0
  9. package/dist/adapter/go-template-adapter.d.ts +291 -1070
  10. package/dist/adapter/go-template-adapter.d.ts.map +1 -1
  11. package/dist/adapter/index.js +2857 -2618
  12. package/dist/adapter/lib/compile-state.d.ts +116 -0
  13. package/dist/adapter/lib/compile-state.d.ts.map +1 -0
  14. package/dist/adapter/lib/constants.d.ts +11 -0
  15. package/dist/adapter/lib/constants.d.ts.map +1 -0
  16. package/dist/adapter/lib/go-emit.d.ts +117 -0
  17. package/dist/adapter/lib/go-emit.d.ts.map +1 -0
  18. package/dist/adapter/lib/go-naming.d.ts +39 -0
  19. package/dist/adapter/lib/go-naming.d.ts.map +1 -0
  20. package/dist/adapter/lib/ir-scope.d.ts +13 -0
  21. package/dist/adapter/lib/ir-scope.d.ts.map +1 -0
  22. package/dist/adapter/lib/types.d.ts +165 -0
  23. package/dist/adapter/lib/types.d.ts.map +1 -0
  24. package/dist/adapter/memo/ctor-lowering.d.ts +39 -0
  25. package/dist/adapter/memo/ctor-lowering.d.ts.map +1 -0
  26. package/dist/adapter/memo/memo-compute.d.ts +124 -0
  27. package/dist/adapter/memo/memo-compute.d.ts.map +1 -0
  28. package/dist/adapter/memo/memo-type.d.ts +38 -0
  29. package/dist/adapter/memo/memo-type.d.ts.map +1 -0
  30. package/dist/adapter/memo/memo-value.d.ts +64 -0
  31. package/dist/adapter/memo/memo-value.d.ts.map +1 -0
  32. package/dist/adapter/memo/template-interp.d.ts +43 -0
  33. package/dist/adapter/memo/template-interp.d.ts.map +1 -0
  34. package/dist/adapter/props/prop-types.d.ts +31 -0
  35. package/dist/adapter/props/prop-types.d.ts.map +1 -0
  36. package/dist/adapter/spread/spread-codegen.d.ts +40 -0
  37. package/dist/adapter/spread/spread-codegen.d.ts.map +1 -0
  38. package/dist/adapter/type/type-codegen.d.ts +25 -0
  39. package/dist/adapter/type/type-codegen.d.ts.map +1 -0
  40. package/dist/adapter/value/parsed-literal-to-go.d.ts +17 -0
  41. package/dist/adapter/value/parsed-literal-to-go.d.ts.map +1 -0
  42. package/dist/adapter/value/value-lowering.d.ts +46 -0
  43. package/dist/adapter/value/value-lowering.d.ts.map +1 -0
  44. package/dist/build.js +2856 -2617
  45. package/dist/index.js +2857 -2618
  46. package/dist/test-render.d.ts.map +1 -1
  47. package/package.json +3 -3
  48. package/src/__tests__/derived-state-memo.test.ts +155 -84
  49. package/src/__tests__/go-template-adapter.test.ts +118 -54
  50. package/src/__tests__/lowering-plugin.test.ts +109 -0
  51. package/src/__tests__/query-href.test.ts +179 -0
  52. package/src/adapter/analysis/component-tree.ts +174 -0
  53. package/src/adapter/emit-context.ts +59 -0
  54. package/src/adapter/expr/helper-inline.ts +274 -0
  55. package/src/adapter/expr/url-builder.ts +123 -0
  56. package/src/adapter/go-template-adapter.ts +2099 -5325
  57. package/src/adapter/lib/compile-state.ts +150 -0
  58. package/src/adapter/lib/constants.ts +24 -0
  59. package/src/adapter/lib/go-emit.ts +289 -0
  60. package/src/adapter/lib/go-naming.ts +84 -0
  61. package/src/adapter/lib/ir-scope.ts +31 -0
  62. package/src/adapter/lib/types.ts +182 -0
  63. package/src/adapter/memo/ctor-lowering.ts +267 -0
  64. package/src/adapter/memo/memo-compute.ts +451 -0
  65. package/src/adapter/memo/memo-type.ts +74 -0
  66. package/src/adapter/memo/memo-value.ts +197 -0
  67. package/src/adapter/memo/template-interp.ts +246 -0
  68. package/src/adapter/props/prop-types.ts +84 -0
  69. package/src/adapter/spread/spread-codegen.ts +458 -0
  70. package/src/adapter/type/type-codegen.ts +95 -0
  71. package/src/adapter/value/parsed-literal-to-go.ts +94 -0
  72. package/src/adapter/value/value-lowering.ts +162 -0
  73. package/src/test-render.ts +2 -14
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Value lowering: convert a JS signal/const initial value into a Go literal for
3
+ * the SSR data context — scalars, prop references, and fully-literal
4
+ * arrays/objects — falling back to `nil`/`0` for anything not reducible to a
5
+ * literal. Pure free functions over a {@link GoEmitContext}.
6
+ */
7
+
8
+ import type { ParsedExpr, TypeInfo } from '@barefootjs/jsx'
9
+
10
+ import type { GoEmitContext } from '../emit-context.ts'
11
+ import type { PropFallbackVar } from '../lib/types.ts'
12
+ import { capitalizeFieldName } from '../lib/go-naming.ts'
13
+ import { parsedLiteralToGo } from './parsed-literal-to-go.ts'
14
+
15
+ /** Default for `getSignalInitialValueAsGo`'s optional fallback-var map. */
16
+ const EMPTY_PROP_FALLBACK_VARS: ReadonlyMap<string, PropFallbackVar> = new Map()
17
+
18
+ /**
19
+ * Lower a signal/const initial value to its Go SSR literal: a prop reference
20
+ * becomes `in.<Field>`, a non-literal falls back to the type's zero value.
21
+ */
22
+ export function convertInitialValue(
23
+ ctx: GoEmitContext,
24
+ value: string,
25
+ typeInfo: TypeInfo,
26
+ propsParams?: { name: string }[],
27
+ preParsed?: ParsedExpr,
28
+ ): string {
29
+ if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(value)) {
30
+ if (propsParams?.some(p => p.name === value)) {
31
+ return `in.${capitalizeFieldName(value)}`
32
+ }
33
+ }
34
+
35
+ const propName = ctx.extractPropNameFromInitialValue(value)
36
+ if (propName && propsParams?.some(p => p.name === propName)) {
37
+ return `in.${capitalizeFieldName(propName)}`
38
+ }
39
+
40
+ if (typeInfo.kind === 'primitive') {
41
+ if (typeInfo.primitive === 'boolean') {
42
+ return value === 'true' ? 'true' : 'false'
43
+ }
44
+ if (typeInfo.primitive === 'number') {
45
+ if (/^\d+$/.test(value)) return value
46
+ if (/^\d+\.\d+$/.test(value)) return value
47
+ return '0'
48
+ }
49
+ if (typeInfo.primitive === 'string') {
50
+ if (value.startsWith("'") || value.endsWith("'")) {
51
+ return value.replace(/'/g, '"')
52
+ }
53
+ if (value.startsWith('"') && value.endsWith('"')) {
54
+ return value
55
+ }
56
+ return '""'
57
+ }
58
+ }
59
+
60
+ if (typeInfo.kind === 'array') {
61
+ return jsLiteralToGo(ctx, typeInfo, preParsed) ?? 'nil'
62
+ }
63
+
64
+ // A string type-alias keeps its string value instead of falling to nil.
65
+ if (typeInfo.kind === 'interface' && typeInfo.raw) {
66
+ const aliasBase = ctx.state.localTypeAliases.get(typeInfo.raw)
67
+ if (aliasBase === 'string') {
68
+ if (value.startsWith("'") || value.startsWith('"')) {
69
+ return value.replace(/'/g, '"')
70
+ }
71
+ return '""'
72
+ }
73
+ }
74
+
75
+ return 'nil'
76
+ }
77
+
78
+ /**
79
+ * Lower a fully-literal value — from the analyzer's carried `ParsedExpr` tree —
80
+ * to a Go literal typed as `typeInfo`:
81
+ *
82
+ * `["x", "y"]` (string[]) → `[]string{"x", "y"}`
83
+ * `["x", "y"]` (unknown[]) → `[]interface{}{"x", "y"}`
84
+ * `[{ id: "a" }]` (Item[]) → `[]Item{Item{ID: "a"}}`
85
+ *
86
+ * Returns null (caller keeps `nil`) for a non-literal, or a shape that can't be
87
+ * expressed in the target type (e.g. an object in a `[]interface{}`, unreachable
88
+ * via the template's struct-field access).
89
+ */
90
+ export function jsLiteralToGo(
91
+ ctx: GoEmitContext,
92
+ typeInfo: TypeInfo,
93
+ preParsed?: ParsedExpr,
94
+ ): string | null {
95
+ if (preParsed) {
96
+ const structured = parsedLiteralToGo(ctx, preParsed, typeInfo)
97
+ if (structured !== null) return structured
98
+ }
99
+ return null
100
+ }
101
+
102
+ /**
103
+ * Bake a flat object literal (`{ align: 'start' }`) into a Go
104
+ * `map[string]interface{}` keyed by SOURCE property names, so it round-trips
105
+ * through `bf_json` like `JSON.stringify` (only the supplied keys, no zero-filled
106
+ * struct fields). Used for an inline object passed to a child's optional object
107
+ * prop. Returns null for a non-object / shorthand / nested / empty object.
108
+ */
109
+ export function objectLiteralToGoMap(ctx: GoEmitContext, expr: ParsedExpr): string | null {
110
+ if (expr.kind !== 'object-literal') return null
111
+ const entries: string[] = []
112
+ for (const prop of expr.properties) {
113
+ if (prop.shorthand) return null
114
+ const val = parsedLiteralToGo(ctx, prop.value)
115
+ if (val === null) return null
116
+ entries.push(`${JSON.stringify(prop.key)}: ${val}`)
117
+ }
118
+ if (entries.length === 0) return null
119
+ return `map[string]interface{}{${entries.join(', ')}}`
120
+ }
121
+
122
+ /**
123
+ * Get a signal's initial value as Go code — a literal, or a props reference
124
+ * (`in.<Field>`, or the hoisted fallback var when `props.X ?? N` has one).
125
+ * Unrecognized values default to `0`.
126
+ */
127
+ export function getSignalInitialValueAsGo(
128
+ ctx: GoEmitContext,
129
+ initialValue: string,
130
+ propsParams: { name: string }[],
131
+ propFallbackVars: ReadonlyMap<string, PropFallbackVar> = EMPTY_PROP_FALLBACK_VARS,
132
+ ): string {
133
+ if (propsParams.some(p => p.name === initialValue)) {
134
+ const hoisted = propFallbackVars.get(initialValue)
135
+ if (hoisted) return hoisted.varName
136
+ return `in.${capitalizeFieldName(initialValue)}`
137
+ }
138
+
139
+ const propName = ctx.extractPropNameFromInitialValue(initialValue)
140
+ if (propName && propsParams.some(p => p.name === propName)) {
141
+ const hoisted = propFallbackVars.get(propName)
142
+ if (hoisted) return hoisted.varName
143
+ return `in.${capitalizeFieldName(propName)}`
144
+ }
145
+
146
+ // single quotes are normalized to Go double quotes
147
+ if (/^-?\d+$/.test(initialValue)) {
148
+ return initialValue
149
+ }
150
+ if (/^-?\d+\.\d+$/.test(initialValue)) {
151
+ return initialValue
152
+ }
153
+ if (initialValue === 'true' || initialValue === 'false') {
154
+ return initialValue
155
+ }
156
+ if ((initialValue.startsWith("'") && initialValue.endsWith("'")) ||
157
+ (initialValue.startsWith('"') && initialValue.endsWith('"'))) {
158
+ return initialValue.replace(/'/g, '"')
159
+ }
160
+
161
+ return '0'
162
+ }
@@ -9,19 +9,7 @@ import { compileJSX } from '@barefootjs/jsx'
9
9
  import type { TemplateAdapter, ComponentIR } from '@barefootjs/jsx'
10
10
  import { GoTemplateAdapter } from './adapter/go-template-adapter.ts'
11
11
  import { deduplicateGoTypes } from './build.ts'
12
-
13
- /**
14
- * Capitalize a JSON key to its Go struct field name using the same
15
- * initialism rules as the adapter (`capitalizeFieldName`): a whole-word Go
16
- * initialism uppercases entirely (`id` → `ID`), otherwise just the first
17
- * letter. Keeps harness-emitted struct literals (`ItemInput{ID: …}`) matching
18
- * the generated exported field names. (#1297)
19
- */
20
- function capitalizeGoField(name: string): string {
21
- if (!name) return name
22
- if (GoTemplateAdapter.GO_INITIALISMS.has(name.toLowerCase())) return name.toUpperCase()
23
- return name.charAt(0).toUpperCase() + name.slice(1)
24
- }
12
+ import { capitalizeFieldName } from './adapter/lib/go-naming.ts'
25
13
  import { mkdir, rm } from 'node:fs/promises'
26
14
  import { resolve } from 'node:path'
27
15
 
@@ -703,7 +691,7 @@ function goTypedSliceLiteralFromArray(arr: unknown[], elemType: string): string
703
691
  function goStructLiteral(obj: Record<string, unknown>, typeName: string): string {
704
692
  const fields: string[] = []
705
693
  for (const [k, v] of Object.entries(obj)) {
706
- const goField = capitalizeGoField(k)
694
+ const goField = capitalizeFieldName(k)
707
695
  if (typeof v === 'string') fields.push(`${goField}: "${v.replace(/"/g, '\\"')}"`)
708
696
  else if (typeof v === 'number' || typeof v === 'boolean') fields.push(`${goField}: ${v}`)
709
697
  else if (v === null) fields.push(`${goField}: nil`)