@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 +33 -0
- package/dist/ssr-defaults.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/ssr-defaults.test.ts +24 -0
- package/src/ssr-defaults.ts +65 -0
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,
|
|
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.
|
|
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.
|
|
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'
|
package/src/ssr-defaults.ts
CHANGED
|
@@ -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
|