@barefootjs/jsx 0.9.6 → 0.10.1

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/index.js CHANGED
@@ -16775,8 +16775,41 @@ function extractSsrDefaults(metadata) {
16775
16775
  out[memo.name] = { value: resultToJsonable(value) };
16776
16776
  bindings[memo.name] = value;
16777
16777
  }
16778
+ if (metadata.propsObjectName !== null) {
16779
+ const referenced = new Set;
16780
+ for (const sig of metadata.signals) {
16781
+ if (!sig.getter || sig.isModule)
16782
+ continue;
16783
+ collectPropRefs(sig.initialValue, metadata.propsObjectName, referenced);
16784
+ }
16785
+ for (const memo of metadata.memos) {
16786
+ if (memo.isModule)
16787
+ continue;
16788
+ collectPropRefs(memo.computation, metadata.propsObjectName, referenced);
16789
+ }
16790
+ for (const name of referenced) {
16791
+ if (name in out)
16792
+ continue;
16793
+ out[name] = { propName: name, value: null };
16794
+ }
16795
+ }
16778
16796
  return Object.keys(out).length === 0 ? undefined : out;
16779
16797
  }
16798
+ function collectPropRefs(expr, propsObjectName, out) {
16799
+ if (!expr || !expr.trim())
16800
+ return;
16801
+ const node = parseExpression2(expr);
16802
+ if (!node)
16803
+ return;
16804
+ const visit3 = (n) => {
16805
+ if (ts16.isPropertyAccessExpression(n) && ts16.isIdentifier(n.expression) && n.expression.text === propsObjectName && ts16.isIdentifier(n.name)) {
16806
+ out.add(n.name.text);
16807
+ return;
16808
+ }
16809
+ ts16.forEachChild(n, visit3);
16810
+ };
16811
+ visit3(node);
16812
+ }
16780
16813
  function resultToJsonable(v) {
16781
16814
  if (v === UNRESOLVED)
16782
16815
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-defaults.d.ts","sourceRoot":"","sources":["../src/ssr-defaults.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,KAAK,EAAE,OAAO,CAAA;IACd;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAYD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,CAsE/F"}
1
+ {"version":3,"file":"ssr-defaults.d.ts","sourceRoot":"","sources":["../src/ssr-defaults.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,KAAK,EAAE,OAAO,CAAA;IACd;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAYD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,CAmG/F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/jsx",
3
- "version": "0.9.6",
3
+ "version": "0.10.1",
4
4
  "description": "JSX compiler for BarefootJS - transforms JSX to server HTML + client JS",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -53,7 +53,7 @@
53
53
  "directory": "packages/jsx"
54
54
  },
55
55
  "dependencies": {
56
- "@barefootjs/shared": "0.9.6"
56
+ "@barefootjs/shared": "0.10.1"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "@barefootjs/client": ">=0.2.0",
@@ -58,6 +58,30 @@ describe('extractSsrDefaults', () => {
58
58
  expect(defaults?.count).toEqual({ value: 99 })
59
59
  })
60
60
 
61
+ test('seeds a bare-props prop a signal initializer reads (`props.initial`)', () => {
62
+ // The #1297 prop-derived seeding lowers `createSignal(props.initial ?? 0)`
63
+ // to a *bare scalar* recompute in the template (`my $count = ($initial
64
+ // // 0)`), so `$initial` must be a stash var or Perl strict aborts with
65
+ // `Global symbol "$initial" requires explicit package name`. The
66
+ // bare-props-arg form previously skipped all props; this regression
67
+ // guards that the referenced prop is now seeded (as undef → the
68
+ // recompute's `?? 0` supplies the real fallback).
69
+ const metadata = metadataFor(`
70
+ 'use client'
71
+ import { createSignal, createMemo } from '@barefootjs/client'
72
+ function Counter(props: { initial?: number }) {
73
+ const [count, setCount] = createSignal(props.initial ?? 0)
74
+ const doubled = createMemo(() => count() * 2)
75
+ return <p>{count()}{doubled()}</p>
76
+ }
77
+ `)
78
+
79
+ const defaults = extractSsrDefaults(metadata)
80
+ expect(defaults?.count).toEqual({ value: 0 })
81
+ expect(defaults?.doubled).toEqual({ value: 0 })
82
+ expect(defaults?.initial).toEqual({ propName: 'initial', value: null })
83
+ })
84
+
61
85
  test('memo derived from a signal evaluates through the chain', () => {
62
86
  const metadata = metadataFor(`
63
87
  'use client'
@@ -149,9 +149,74 @@ export function extractSsrDefaults(metadata: IRMetadata): Record<string, SsrDefa
149
149
  bindings[memo.name] = value
150
150
  }
151
151
 
152
+ // Bare-props-arg form (`function Foo(props: Props)`): a signal / memo
153
+ // whose initializer reads `props.X` is lowered by template-stash
154
+ // adapters to a *bare scalar* recompute (`my $count = ($initial // 0)`,
155
+ // the #1297 prop-derived seeding) — not a `$props->{X}` hash read. That
156
+ // bare `$initial` has to exist in the stash or Perl's strict mode aborts
157
+ // the render with `Global symbol "$initial" requires explicit package
158
+ // name`. The top block skips bare-props props on the assumption they're
159
+ // only ever read via `$props->{X}`, which the prop-derived seeding
160
+ // violates — so seed every prop a signal / memo initializer references.
161
+ // Value is `null` (→ undef): the recompute's own `?? <literal>` supplies
162
+ // the real fallback, and a caller-passed prop still wins via `propName`.
163
+ if (metadata.propsObjectName !== null) {
164
+ const referenced = new Set<string>()
165
+ for (const sig of metadata.signals) {
166
+ if (!sig.getter || sig.isModule) continue
167
+ collectPropRefs(sig.initialValue, metadata.propsObjectName, referenced)
168
+ }
169
+ for (const memo of metadata.memos) {
170
+ if (memo.isModule) continue
171
+ collectPropRefs(memo.computation, metadata.propsObjectName, referenced)
172
+ }
173
+ for (const name of referenced) {
174
+ // Don't clobber a signal / memo (or already-seeded prop) of the same
175
+ // name — those carry a resolved value the recompute relies on.
176
+ if (name in out) continue
177
+ out[name] = { propName: name, value: null }
178
+ }
179
+ }
180
+
152
181
  return Object.keys(out).length === 0 ? undefined : out
153
182
  }
154
183
 
184
+ /**
185
+ * Collect the first-level property name of every `propsObjectName.X` access
186
+ * within `expr` (e.g. `props.initial ?? 0` → `initial`). Used to seed the
187
+ * stash vars a template-stash adapter's bare-scalar signal/memo recompute
188
+ * references. A deeper chain (`props.a.b`) still contributes its *base*
189
+ * prop `a`: adapters lower that to `$a->{b}`, so the bare `$a` needs
190
+ * seeding just the same — the walk stops at the first
191
+ * `propsObjectName.<name>` match and collects `a` (not `b`).
192
+ */
193
+ function collectPropRefs(
194
+ expr: string | undefined,
195
+ propsObjectName: string,
196
+ out: Set<string>,
197
+ ): void {
198
+ if (!expr || !expr.trim()) return
199
+ const node = parseExpression(expr)
200
+ if (!node) return
201
+ const visit = (n: ts.Node): void => {
202
+ if (
203
+ ts.isPropertyAccessExpression(n) &&
204
+ ts.isIdentifier(n.expression) &&
205
+ n.expression.text === propsObjectName &&
206
+ ts.isIdentifier(n.name)
207
+ ) {
208
+ // `propsObjectName.<name>` — collect <name> and stop. For a deeper
209
+ // chain (`props.a.b`) this node is the inner `props.a`, so we collect
210
+ // the base prop `a` (which lowers to `$a->{b}`); the outer `.b`
211
+ // access has no further `props.` reference to find.
212
+ out.add(n.name.text)
213
+ return
214
+ }
215
+ ts.forEachChild(n, visit)
216
+ }
217
+ visit(node)
218
+ }
219
+
155
220
  function resultToJsonable(v: EvalResult): unknown {
156
221
  if (v === UNRESOLVED) return null
157
222
  if (v === undefined) return null