@barefootjs/go-template 0.33.4 → 0.34.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/emit-context.d.ts +12 -0
- package/dist/adapter/emit-context.d.ts.map +1 -1
- package/dist/adapter/expr/url-builder.d.ts +53 -1
- package/dist/adapter/expr/url-builder.d.ts.map +1 -1
- package/dist/adapter/go-template-adapter.d.ts +153 -7
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +253 -99
- package/dist/adapter/lib/compile-state.d.ts +17 -0
- package/dist/adapter/lib/compile-state.d.ts.map +1 -1
- package/dist/adapter/value/parsed-literal-to-go.d.ts.map +1 -1
- package/dist/adapter/value/value-lowering.d.ts.map +1 -1
- package/dist/conformance-pins.d.ts.map +1 -1
- package/dist/index.js +261 -103
- package/dist/render-divergences.d.ts +10 -0
- package/dist/render-divergences.d.ts.map +1 -1
- package/dist/vite.js +420 -214
- package/package.json +5 -5
- package/src/__tests__/go-template-adapter.test.ts +333 -9
- package/src/__tests__/lowering-plugin.test.ts +38 -0
- package/src/__tests__/query-href.test.ts +126 -0
- package/src/adapter/emit-context.ts +14 -0
- package/src/adapter/expr/url-builder.ts +114 -8
- package/src/adapter/go-template-adapter.ts +514 -107
- package/src/adapter/lib/compile-state.ts +18 -0
- package/src/adapter/value/parsed-literal-to-go.ts +11 -7
- package/src/adapter/value/value-lowering.ts +17 -0
- package/src/conformance-pins.ts +19 -0
- package/src/render-divergences.ts +12 -6
- package/src/test-render.ts +32 -15
|
@@ -38,6 +38,24 @@ export class CompileState {
|
|
|
38
38
|
* that's resolvable and non-colliding. */
|
|
39
39
|
referencedDerivedConsts: Set<string> = new Set()
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Root-scope field names (signal getters, memo names, props, derived
|
|
43
|
+
* consts) the SSR template actually READ while rendering — recorded by
|
|
44
|
+
* `rootFieldRef`, the single door every such read passes through
|
|
45
|
+
* (`identifier`/`call`/`member`'s zero-arg-getter and bare-name lowering,
|
|
46
|
+
* plus the typed-expression emitter). `generateTypes` (#2700) consults
|
|
47
|
+
* this AFTER `generate()` has rendered the template to decide whether a
|
|
48
|
+
* signal whose object-literal initializer the constructor-time baker
|
|
49
|
+
* can't reproduce (`convertInitialValue` returning null) needs a loud
|
|
50
|
+
* BF101 refusal: a deferred bake that is NEVER read by the template (e.g.
|
|
51
|
+
* only feeds a spread-attrs bag, which routes through its own
|
|
52
|
+
* `.Spread_<slot>` field, never this set) silently keeping its Go zero
|
|
53
|
+
* value is harmless, so refusing it would be a false positive — see
|
|
54
|
+
* `refuseUnbakeableDerivedObjectLiteral`'s docstring for why the check is
|
|
55
|
+
* call-site-aware instead of firing at the bake site itself.
|
|
56
|
+
*/
|
|
57
|
+
templateReadRootFields: Set<string> = new Set()
|
|
58
|
+
|
|
41
59
|
templateVarCounter: number = 0
|
|
42
60
|
|
|
43
61
|
/**
|
|
@@ -28,13 +28,17 @@ import { goFieldNameForKey } from '../lib/go-naming.ts'
|
|
|
28
28
|
import { typeInfoToGo } from '../type/type-codegen.ts'
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* Look up a struct property's declared `TypeInfo` by source key
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
31
|
+
* Look up a struct property's declared `TypeInfo` by source key. Resolves
|
|
32
|
+
* against ANY registered `TypeDefinition` — a user-written type, a #2674
|
|
33
|
+
* anonymous-object synthesis, or (#2800) an untyped-literal signal struct
|
|
34
|
+
* synthesized by `synthesizeStructFromSignal`, which pushes one
|
|
35
|
+
* `TypeDefinition` per struct (top-level and nested) via the same
|
|
36
|
+
* `registerSynthStruct` door #2674 uses, specifically so a nested
|
|
37
|
+
* array-of-objects field's declared element type is findable here exactly
|
|
38
|
+
* like a user-declared nested property's is. Returns undefined when the
|
|
39
|
+
* struct name isn't registered or the key isn't declared — callers treat
|
|
40
|
+
* that as "nested type unknown" and fall back to the generic/inline
|
|
41
|
+
* lowering.
|
|
38
42
|
*/
|
|
39
43
|
function structPropertyType(ctx: GoEmitContext, structGoType: string, key: string): TypeInfo | undefined {
|
|
40
44
|
const td = ctx.state.currentTypeDefinitions.find((t: TypeDefinition) => t.name === structGoType)
|
|
@@ -94,6 +94,23 @@ export function convertInitialValue(
|
|
|
94
94
|
if (param) {
|
|
95
95
|
return propRef(param)
|
|
96
96
|
}
|
|
97
|
+
// Module-const seed (#2794): a signal seeded from a bare identifier
|
|
98
|
+
// that refers to a module-level const (`const PAYLOAD = 'x';
|
|
99
|
+
// createSignal(PAYLOAD)`) types `unknown` — the analyzer's type
|
|
100
|
+
// inference is text-shaped and never chases an identifier to its
|
|
101
|
+
// declaration — so none of the typed branches below ever see it and
|
|
102
|
+
// this used to fall through to the final `nil`. Checked AFTER the
|
|
103
|
+
// prop lookup so a destructured prop that happens to share the
|
|
104
|
+
// const's name still wins (shadowing). Both resolvers return null
|
|
105
|
+
// for anything that isn't a statically-inlinable module const
|
|
106
|
+
// (a call-initialized const, a loop var, a component-scope const),
|
|
107
|
+
// leaving that case on the pre-existing `nil` path unchanged.
|
|
108
|
+
const inlinedStr = ctx.resolveModuleStringConst(value)
|
|
109
|
+
if (inlinedStr !== null) return inlinedStr
|
|
110
|
+
const inlinedNum = ctx.resolveModuleNumericConst(value)
|
|
111
|
+
if (inlinedNum !== null) return inlinedNum
|
|
112
|
+
const inlinedBool = ctx.resolveModuleBooleanConst(value)
|
|
113
|
+
if (inlinedBool !== null) return inlinedBool
|
|
97
114
|
}
|
|
98
115
|
|
|
99
116
|
const propName = ctx.extractPropNameFromInitialValue(value, preParsed)
|
package/src/conformance-pins.ts
CHANGED
|
@@ -76,4 +76,23 @@ export const conformancePins: ConformancePins = {
|
|
|
76
76
|
issue: 'https://github.com/piconic-ai/barefootjs/issues/2805',
|
|
77
77
|
unescapable: { issue: 'https://github.com/piconic-ai/barefootjs/issues/2805' },
|
|
78
78
|
}],
|
|
79
|
+
// #2700: a `derived` signal/memo (non-empty free set) seeded from an
|
|
80
|
+
// object literal the constructor-time baker can't reproduce (identifier/
|
|
81
|
+
// member/call operands defer, `parsed-literal-to-go.ts`) now refuses
|
|
82
|
+
// loudly instead of silently keeping the Go zero value — this fixture's
|
|
83
|
+
// `merged().id` / `merged().done` reads are exactly that shape. A working
|
|
84
|
+
// `/* @client */` escape twin exists (`signal-object-spread-init-client`),
|
|
85
|
+
// verified to render correctly, so no `unescapable`.
|
|
86
|
+
'signal-object-spread-init': [{
|
|
87
|
+
code: 'BF101',
|
|
88
|
+
severity: 'error',
|
|
89
|
+
issue: 'https://github.com/piconic-ai/barefootjs/issues/2700',
|
|
90
|
+
}],
|
|
91
|
+
// #2771: a reactive primitive invoked through a namespace import
|
|
92
|
+
// (`import * as bf from '@barefootjs/client'`, `bf.createSignal(...)`)
|
|
93
|
+
// that the analyzer's checker-less fast path cannot recognize refuses
|
|
94
|
+
// loudly (BF013) instead of silently dropping the declaration — fired
|
|
95
|
+
// in the shared analyzer pass ahead of any adapter's `adapter.generate()`,
|
|
96
|
+
// so all nine adapters (including Hono) pin this identically.
|
|
97
|
+
'namespace-import-primitive': [{ code: 'BF013', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2771' }],
|
|
79
98
|
}
|
|
@@ -23,6 +23,16 @@
|
|
|
23
23
|
* Go, so it moved off this table. Dynamic delivery for named jsx-children
|
|
24
24
|
* props (the actual capability gap) is tracked separately at
|
|
25
25
|
* https://github.com/piconic-ai/barefootjs/issues/2703.)
|
|
26
|
+
*
|
|
27
|
+
* (#2700's `signal-object-spread-init` divergence graduated the same way —
|
|
28
|
+
* by reclassification, not a lowering fix: a `derived` signal/memo's
|
|
29
|
+
* object-literal initializer referencing a live prop/signal has no
|
|
30
|
+
* live-template-expression lowering on Go, only a static constructor-time
|
|
31
|
+
* baker — now a loud `BF101` refusal (`conformance-pins.ts`) with a
|
|
32
|
+
* verified `/* @client *\/` escape twin (`signal-object-spread-init-client`),
|
|
33
|
+
* instead of a silent wrong render. Teaching the baker to emit
|
|
34
|
+
* prop-referencing Go expressions — the actual capability gap — stays
|
|
35
|
+
* tracked at https://github.com/piconic-ai/barefootjs/issues/2700.)
|
|
26
36
|
*/
|
|
27
37
|
|
|
28
38
|
import type { RenderDivergences } from '@barefootjs/jsx'
|
|
@@ -30,10 +40,6 @@ import type { RenderDivergences } from '@barefootjs/jsx'
|
|
|
30
40
|
export const renderDivergences: RenderDivergences = {
|
|
31
41
|
'children-passthrough-renamed':
|
|
32
42
|
'A `children` prop destructured under a different name (`const { children: kids } = props`) does not reach the SSR template on Go, tracked as https://github.com/piconic-ai/barefootjs/issues/2788. The same fixture also fails on Mojolicious, where the mechanism IS isolated: the `.html.ep` interpolates the LOCAL alias (`$kids`) while the stash defines only the caller-facing `children`, so the Perl render dies inside `Mojo::Template::process`. Go\'s own failure mode has NOT been read — `go` is not reachable from the local test process (the conformance case prints "go command not found" and skips), so this entry is declared from the CI failure on #2787 alone, not from a local reproduction. Whoever graduates this should read Go\'s actual output first rather than assume it shares Mojo\'s mechanism. Same alias family as `aliased-destructured-prop` (`{ n: count }`), whose Go half graduated in #2525 — worth checking whether the reserved `children` slot bypasses that fix or never had it. `children-passthrough-renamed` asserts the CORRECT (Hono-generated) output, so deleting this entry is the graduation.',
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'textarea-row-breakout':
|
|
36
|
-
'A signal seeded from a bare identifier referencing a MODULE-LEVEL const (`const PAYLOAD = \'...\'; createSignal(PAYLOAD)`) bakes to `nil` in the generated `New<Component>Props` constructor instead of the const\'s literal value: `convertInitialValue` (`value-lowering.ts`) only resolves a direct prop reference or a literal expression for a bare identifier, and the analyzer types this signal `{ kind: \'unknown\' }`, so every typed branch falls through to the final `nil` fallback. `resolveModuleStringConst` exists on the adapter for exactly this resolution (used by `template-interp.ts` for live template expressions) but isn\'t wired into this signal-baking path. Unrelated to what this fixture exists to cover (#2765\'s loop-row textarea-escaping fix, verified correct here) — every other adapter renders the fixture\'s controlled `<textarea>` correctly. Tracked at https://github.com/piconic-ai/barefootjs/issues/2794; graduate by wiring `resolveModuleStringConst` into `convertInitialValue`\'s bare-identifier case.',
|
|
37
|
-
'nested-loop-ref-const':
|
|
38
|
-
'A signal-backed object array whose elements have a NESTED array-of-objects field (`children: [{...}]`, producing this fixture\'s depth-2 `.map()`) bakes to `nil` in `New<Component>Props`, leaving the whole `{{range .Items}}` body empty on real Go — `synthesizeStructFromSignal` (`go-template-adapter.ts`) only synthesizes a struct when EVERY property value is a scalar literal (`scalarParsedGoType`), so a `children` array property aborts synthesis for the entire signal; `parsedLiteralToGo` then has no named struct to bake an object-literal element against and falls through to `nil`. Reproduced identically with #2750\'s fix reverted, confirming this predates and is independent of #2750 (which only touches client-JS reachability, never SSR baking) — every other adapter (confirmed: Hono) renders the nested loop correctly. Tracked at https://github.com/piconic-ai/barefootjs/issues/2800; graduate by teaching `synthesizeStructFromSignal` to recursively synthesize a nested struct for an array-valued property instead of bailing to `null`.',
|
|
43
|
+
'aliased-loop-source':
|
|
44
|
+
'A `.map()` loop whose source is a local const alias of a signal getter (`const items__alias = items`) fails template execution on real Go (`can\'t evaluate field Items__alias in type main.AliasedLoopSourceProps`) — the zero-arg-call-to-field lowering routes `items__alias()` to a `.Items__alias` struct field that was never seeded, since seeding only knows about `items`, the real signal name; the alias hop is never resolved. This is the SSR-side twin of #2778 (fixed for the CSR client-JS template in the same PR that added this fixture) — that fix only touches client-JS emission, not Go\'s field-routing/seeding. Tracked at https://github.com/piconic-ai/barefootjs/issues/2813; graduate by resolving the alias hop at field-routing time using the same `resolveAliasOrigin`/`resolveGetterAliases` mechanism #2778 introduced, rather than a third alias-hop walker.',
|
|
39
45
|
}
|
package/src/test-render.ts
CHANGED
|
@@ -514,11 +514,10 @@ function collectImportedComponentNames(
|
|
|
514
514
|
if (!imp.source.startsWith('.') && !childSpecifiers?.has(imp.source)) continue
|
|
515
515
|
for (const spec of imp.specifiers ?? []) {
|
|
516
516
|
if (spec.isNamespace) continue
|
|
517
|
-
//
|
|
518
|
-
//
|
|
519
|
-
//
|
|
520
|
-
|
|
521
|
-
names.push(spec.alias ?? spec.name)
|
|
517
|
+
// #2822: push `spec.name` (the child's own declared name), not
|
|
518
|
+
// `spec.alias ?? spec.name` — `childArtifacts` is keyed by each
|
|
519
|
+
// child's declared name, not the caller-local JSX/import binding.
|
|
520
|
+
names.push(spec.name)
|
|
522
521
|
}
|
|
523
522
|
}
|
|
524
523
|
return names
|
|
@@ -595,16 +594,34 @@ function findBaseSignalGetter(expr: ParsedExpr | undefined, signalGetters: Reado
|
|
|
595
594
|
/**
|
|
596
595
|
* (#2630) Resolve a `isPropDerived` loop's `arrayParsed` to the JS-level
|
|
597
596
|
* prop name it reads. `isArrayExprDirectPropRef` (`jsx-to-ir.ts`) is what
|
|
598
|
-
* sets `isPropDerivedArray` in the first place, and
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
*
|
|
602
|
-
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
605
|
-
* `
|
|
606
|
-
* `
|
|
607
|
-
*
|
|
597
|
+
* sets `isPropDerivedArray` in the first place, and — even after #2724
|
|
598
|
+
* taught it to resolve a bare `const x = y` alias-hop chain (including one
|
|
599
|
+
* that reaches the WHOLE props object, e.g. `const p = props; p.items`) —
|
|
600
|
+
* `arrayParsed` (this function's input) is `loop.array`'s own written
|
|
601
|
+
* structure, not the alias-resolved origin: a hop through a destructured
|
|
602
|
+
* prop's alias ends up here as a bare identifier (the LOCAL alias name, not
|
|
603
|
+
* the prop's own name — resolved below via `propsParams`), and a hop that
|
|
604
|
+
* lands on `props.<key>` OR `<aliasOfProps>.<key>` both end up here as the
|
|
605
|
+
* same member shape (`.property` is the source key either way; this
|
|
606
|
+
* function never inspects `.object.name`, so it doesn't matter whether the
|
|
607
|
+
* object identifier IS `props` or an alias of it).
|
|
608
|
+
*
|
|
609
|
+
* `renderLoop`'s own BF101 gate (`go-template-adapter.ts`, the
|
|
610
|
+
* `arrayName`/`localConstants` check) refuses a BARE-IDENTIFIER loop array
|
|
611
|
+
* that is a local computed value — including an alias-hop identifier —
|
|
612
|
+
* before this function would ever see one for that shape. But that gate's
|
|
613
|
+
* `arrayName` regex (`^[A-Za-z_$][\w$]*$`) never matches a MEMBER-ACCESS
|
|
614
|
+
* array text at all, so a `<propsAlias>.<key>` loop array (unlike a bare
|
|
615
|
+
* identifier one) is NOT screened by BF101 and can reach this function for
|
|
616
|
+
* real — which is fine: the member branch below resolves it correctly
|
|
617
|
+
* regardless, per the previous paragraph. Unlike `findBaseSignalGetter`
|
|
618
|
+
* there is no `call`/chain walking to do here either way — whatever hop
|
|
619
|
+
* resolution happened, already happened in `isArrayExprDirectPropRef`.
|
|
620
|
+
* Returns the prop's LOCAL binding name; the caller resolves it to the
|
|
621
|
+
* CALLER-FACING field (`sourceName ?? name`) via `propsParams`, matching
|
|
622
|
+
* how `generateInputStruct`/`generateNewPropsFunction` key the Go field and
|
|
623
|
+
* how `buildGoPropsInit` keys the harness's own prop initializer (both by
|
|
624
|
+
* the JS `props` object's key, not the local destructure binding).
|
|
608
625
|
*/
|
|
609
626
|
function findLoopPropField(expr: ParsedExpr | undefined): string | null {
|
|
610
627
|
if (!expr) return null
|