@barefootjs/go-template 0.18.4 → 0.18.5

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.
@@ -42,8 +42,12 @@ export function convertInitialValue(
42
42
  return value === 'true' ? 'true' : 'false'
43
43
  }
44
44
  if (typeInfo.primitive === 'number') {
45
- if (/^\d+$/.test(value)) return value
46
- if (/^\d+\.\d+$/.test(value)) return value
45
+ // Leading `-` (#2168 math-methods: `createSignal(-7.6)`) without it,
46
+ // a negative initial value never matches either literal shape below
47
+ // and silently falls to the `0` zero-value fallback, regardless of the
48
+ // field's Go type.
49
+ if (/^-?\d+$/.test(value)) return value
50
+ if (/^-?\d+\.\d+$/.test(value)) return value
47
51
  return '0'
48
52
  }
49
53
  if (typeInfo.primitive === 'string') {
@@ -70,6 +74,27 @@ export function convertInitialValue(
70
74
  }
71
75
  return '""'
72
76
  }
77
+ // A struct-backed `interface` kind (an explicitly-typed object signal,
78
+ // `createSignal<User>({...})`) — #2168 signal-object-field. Mirrors the
79
+ // `array` branch above: `jsLiteralToGo` → `parsedLiteralToGo`'s
80
+ // object-literal case already bakes an object literal against a named
81
+ // local struct correctly (proven by the existing typed-array-of-objects
82
+ // test); it just wasn't reachable from a SCALAR struct signal, which
83
+ // fell straight through to `nil` — a compile error for a non-pointer
84
+ // struct field (`cannot use nil as User value in struct literal`), not
85
+ // merely a silently-dropped initial value.
86
+ if (ctx.state.localStructFields.has(typeInfo.raw)) {
87
+ const baked = jsLiteralToGo(ctx, typeInfo, preParsed)
88
+ if (baked !== null) return baked
89
+ // Baking failed (a non-literal initial value, or no `preParsed` tree)
90
+ // — `nil` is STILL invalid Go for this non-pointer struct field, so
91
+ // the same compile error would resurface for any such case (Copilot
92
+ // review, #2201). The struct's own zero value (`User{}`) is the
93
+ // correct fallback here — mirrors this function's own docstring
94
+ // ("falls back to the type's zero value") for every other typed
95
+ // branch above.
96
+ return `${typeInfo.raw}{}`
97
+ }
73
98
  }
74
99
 
75
100
  return 'nil'
@@ -138,9 +138,4 @@ export const conformancePins: ConformancePins = {
138
138
  // the shape loudly instead of emitting entity-escaped markup that
139
139
  // silently renders tags as text.
140
140
  'dangerous-inner-html': [{ code: 'BF101', severity: 'error' }],
141
- // Edge-case sweep (Priority 12): `.replaceAll` has no lowering yet —
142
- // only first-occurrence `.replace` is wired to the runtime helpers.
143
- // Refused with BF101 rather than reusing the first-only lowering,
144
- // which would silently change semantics.
145
- 'string-replaceall': [{ code: 'BF101', severity: 'error' }],
146
141
  }
@@ -17,34 +17,4 @@
17
17
  import type { RenderDivergences } from '@barefootjs/jsx'
18
18
 
19
19
  export const renderDivergences: RenderDivergences = {
20
- 'html-entity-text':
21
- '`&copy;` in JSX literal text: Hono decodes to `©`, this adapter re-emits the raw entity — same DOM, different bytes',
22
- 'string-concat-plus':
23
- "`'Hello, ' + name` renders \"0\" — JS string-concat `+` lowered through numeric addition",
24
- 'optional-chaining-prop':
25
- '`user?.name ?? …` on an object prop: generated Go fails to run (exit 1) — optional chaining into a struct/map prop has no lowering',
26
- 'number-tofixed':
27
- '`.toFixed(2)` on a number PROP: generated Go fails to run (exit 1)',
28
- 'math-methods':
29
- 'Math.min/max/abs over a signal: generated Go fails to run (exit 1) — only Math.floor is registered',
30
- 'static-attr-escape':
31
- 'static attribute values are not HTML-escaped (`title="Fish & Chips"` emitted raw; Hono escapes)',
32
- 'object-entries-map':
33
- '`Object.entries(prop).map(([k, v]) => …)`: generated Go fails to run (exit 1) — no object-iteration loop lowering',
34
- 'nested-loop-outer-binding':
35
- 'nested-loop inner items carry `data-key` where the reference emits the depth-suffixed `data-key-1`',
36
- 'jsx-element-prop':
37
- 'a JSX element passed as a NON-children prop renders an empty slot — the element value is silently dropped',
38
- 'grandchild-composition':
39
- "three-level composition: the grandchild's threaded prop renders EMPTY — prop forwarding through two template-render layers loses the value",
40
- 'child-primitive-props':
41
- 'numeric/boolean LITERAL props on a child (`count={5}` `active={true}`) render as Go zero values (0 / false)',
42
- 'memo-chain':
43
- 'a memo derived from another memo renders EMPTY for the second layer — the constructor folds only one derivation level',
44
- 'signal-object-field':
45
- 'object-valued signal (`user().name`): generated Go fails to run (exit 1) — no struct synthesis outside loops',
46
- 'string-slice':
47
- '`.slice()` on a STRING routes through the array `bf_slice` helper and renders "[]" instead of the substring',
48
- 'string-trim-sided':
49
- '`.trimStart()` / `.trimEnd()`: generated Go fails to run (exit 1) — only both-sides `bf_trim` exists',
50
20
  }
@@ -9,7 +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
- import { capitalizeFieldName } from './adapter/lib/go-naming.ts'
12
+ import { capitalizeFieldName, goFieldNameForKey } from './adapter/lib/go-naming.ts'
13
13
  import { mkdir, rm } from 'node:fs/promises'
14
14
  import { resolve } from 'node:path'
15
15
 
@@ -572,9 +572,7 @@ function buildGoPropsInit(
572
572
  // in struct literal of type InputInput`. (#1467 Phase 2b)
573
573
  const declaredParams = new Set((ir?.metadata.propsParams ?? []).map(p => p.name))
574
574
  const restPropsName = ir?.metadata.restPropsName ?? null
575
- const restBagField = restPropsName
576
- ? restPropsName.charAt(0).toUpperCase() + restPropsName.slice(1)
577
- : null
575
+ const restBagField = restPropsName ? capitalizeFieldName(restPropsName) : null
578
576
 
579
577
  const lines: string[] = []
580
578
  const restBagEntries: Array<[string, unknown]> = []
@@ -594,8 +592,9 @@ function buildGoPropsInit(
594
592
  restBagEntries.push([key, value])
595
593
  continue
596
594
  }
597
- // Capitalize first letter for Go field name
598
- const goField = key.charAt(0).toUpperCase() + key.slice(1)
595
+ // Same Go-initialism-aware capitalizer as the real adapter (`id` → `ID`,
596
+ // not the naive `Id`) see `goMapLiteralFromObject`'s identical fix.
597
+ const goField = capitalizeFieldName(key)
599
598
  if (typeof value === 'string') {
600
599
  lines.push(`\t\t${goField}: "${value}",`)
601
600
  } else if (typeof value === 'number') {
@@ -729,7 +728,11 @@ function goTypedMapSliceLiteralFromArray(arr: unknown[], elemType: string): stri
729
728
  function goStructLiteral(obj: Record<string, unknown>, typeName: string): string {
730
729
  const fields: string[] = []
731
730
  for (const [k, v] of Object.entries(obj)) {
732
- const goField = capitalizeFieldName(k)
731
+ // `goFieldNameForKey`, not the bare `capitalizeFieldName` — a data-driven
732
+ // key here can be non-identifier-shaped (`'data-x'`), and the real
733
+ // adapter's own struct-literal baking (`parsed-literal-to-go.ts`)
734
+ // sanitizes those to `DataX`, not `Data-x` (Copilot review, #2202).
735
+ const goField = goFieldNameForKey(k)
733
736
  if (typeof v === 'string') fields.push(`${goField}: "${v.replace(/"/g, '\\"')}"`)
734
737
  else if (typeof v === 'number' || typeof v === 'boolean') fields.push(`${goField}: ${v}`)
735
738
  else if (v === null) fields.push(`${goField}: nil`)
@@ -766,7 +769,19 @@ function goMapLiteralFromObject(
766
769
  ): string {
767
770
  const entries: string[] = []
768
771
  for (const [k, v] of Object.entries(obj)) {
769
- const emittedKey = capitalizeKeys ? k.charAt(0).toUpperCase() + k.slice(1) : k
772
+ // `goFieldNameForKey`, not a naive first-letter uppercase and not the
773
+ // bare `capitalizeFieldName` — #2168 nested-loop-triple-depth: a naive
774
+ // capitalize disagrees with the real adapter's Go-initialism-aware
775
+ // field naming for a key like `id` (naive → "Id", adapter's generated
776
+ // struct/template field → "ID"), so the harness baked a literal the
777
+ // template's `{{.ID}}` lookup could never match — the fixture's
778
+ // rendered fields came back empty at EVERY nesting depth for this
779
+ // reason, not because of any depth limit. `capitalizeFieldName` alone
780
+ // fixes the initialism case but still mis-bakes a non-identifier key
781
+ // (`'data-x'` → `"Data-x"`, not the adapter's own `"DataX"` —
782
+ // Copilot review, #2202); `goFieldNameForKey` is what the real adapter
783
+ // uses for exactly this key-to-Go-field sanitization.
784
+ const emittedKey = capitalizeKeys ? goFieldNameForKey(k) : k
770
785
  const key = JSON.stringify(emittedKey)
771
786
  if (typeof v === 'string') entries.push(`${key}: "${v.replace(/"/g, '\\"')}"`)
772
787
  else if (typeof v === 'number') entries.push(`${key}: ${v}`)