@barefootjs/jsx 0.8.0 → 0.9.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/augment-inherited-props.d.ts +141 -0
- package/dist/augment-inherited-props.d.ts.map +1 -0
- package/dist/compiler.d.ts +22 -0
- package/dist/compiler.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +329 -17
- package/dist/ssr-defaults.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/augment-inherited-props.test.ts +97 -0
- package/src/__tests__/merge-template-imports.test.ts +68 -0
- package/src/__tests__/ssr-defaults.test.ts +24 -0
- package/src/augment-inherited-props.ts +366 -0
- package/src/compiler.ts +75 -14
- package/src/index.ts +4 -0
- package/src/ssr-defaults.ts +80 -9
package/src/ssr-defaults.ts
CHANGED
|
@@ -121,6 +121,20 @@ export function extractSsrDefaults(metadata: IRMetadata): Record<string, SsrDefa
|
|
|
121
121
|
// fed into the bindings map so subsequent memos can reference earlier
|
|
122
122
|
// signals (Counter's `doubled = createMemo(() => count() * 2)`).
|
|
123
123
|
const bindings: Record<string, EvalResult> = {}
|
|
124
|
+
|
|
125
|
+
// (#checkbox) Seed module-scope constants so a memo template-literal that
|
|
126
|
+
// references them resolves to a concrete string. Checkbox's `classes` memo
|
|
127
|
+
// interpolates `baseClasses` / `focusClasses` / `errorClasses` (pure string
|
|
128
|
+
// consts) and `stateClasses` (`[...].join(' ')`). Without these in scope the
|
|
129
|
+
// memo evaluates to `null` and the SSR `class="..."` renders empty, diverging
|
|
130
|
+
// from Hono. Only module-scope consts are seeded (component-scope locals can
|
|
131
|
+
// depend on signals/props and are evaluated lazily elsewhere).
|
|
132
|
+
for (const c of metadata.localConstants ?? []) {
|
|
133
|
+
if (!c.isModule || c.value === undefined) continue
|
|
134
|
+
if (c.name in bindings) continue
|
|
135
|
+
const v = tryStaticEval(c.value, { bindings, propsLike })
|
|
136
|
+
if (v !== UNRESOLVED) bindings[c.name] = v
|
|
137
|
+
}
|
|
124
138
|
for (const sig of metadata.signals) {
|
|
125
139
|
if (!sig.getter || sig.isModule) continue
|
|
126
140
|
const value = tryStaticEval(sig.initialValue, { bindings, propsLike })
|
|
@@ -178,12 +192,33 @@ function evalNode(node: ts.Expression, ctx: EvalContext): EvalResult {
|
|
|
178
192
|
if (ts.isNonNullExpression(node)) return evalNode(node.expression, ctx)
|
|
179
193
|
|
|
180
194
|
// `createMemo(() => count() * 2)` stores the full arrow expression
|
|
181
|
-
// string. For evaluation, we only care about the body
|
|
182
|
-
// expression-bodied form (`() => expr`)
|
|
183
|
-
// (`() => {
|
|
195
|
+
// string. For evaluation, we only care about the body. The
|
|
196
|
+
// expression-bodied form (`() => expr`) evaluates its body directly; a
|
|
197
|
+
// block-bodied arrow (`() => { const v = …; return \`…\` }`, the Toggle
|
|
198
|
+
// `classes` memo) evaluates its leading `const` declarations into a local
|
|
199
|
+
// binding scope and then its single `return` expression. We don't attempt
|
|
200
|
+
// branch tracking — any control flow before the `return` leaves it
|
|
201
|
+
// unresolved.
|
|
184
202
|
if (ts.isArrowFunction(node)) {
|
|
185
|
-
if (node.parameters.length
|
|
186
|
-
|
|
203
|
+
if (node.parameters.length !== 0) return UNRESOLVED
|
|
204
|
+
if (!ts.isBlock(node.body)) return evalNode(node.body as ts.Expression, ctx)
|
|
205
|
+
const localBindings: Record<string, EvalResult> = { ...ctx.bindings }
|
|
206
|
+
const localCtx: EvalContext = { ...ctx, bindings: localBindings }
|
|
207
|
+
for (const stmt of node.body.statements) {
|
|
208
|
+
if (ts.isVariableStatement(stmt)) {
|
|
209
|
+
for (const d of stmt.declarationList.declarations) {
|
|
210
|
+
if (!ts.isIdentifier(d.name) || !d.initializer) continue
|
|
211
|
+
const v = evalNode(d.initializer, localCtx)
|
|
212
|
+
// Leave unresolved locals unbound; only the `return` referencing
|
|
213
|
+
// one would then surface UNRESOLVED.
|
|
214
|
+
if (v !== UNRESOLVED) localBindings[d.name.text] = v
|
|
215
|
+
}
|
|
216
|
+
} else if (ts.isReturnStatement(stmt)) {
|
|
217
|
+
return stmt.expression ? evalNode(stmt.expression, localCtx) : UNRESOLVED
|
|
218
|
+
} else {
|
|
219
|
+
// Any other statement (a branch, a side-effecting call) — bail.
|
|
220
|
+
return UNRESOLVED
|
|
221
|
+
}
|
|
187
222
|
}
|
|
188
223
|
return UNRESOLVED
|
|
189
224
|
}
|
|
@@ -245,10 +280,27 @@ function evalNode(node: ts.Expression, ctx: EvalContext): EvalResult {
|
|
|
245
280
|
return arr
|
|
246
281
|
}
|
|
247
282
|
|
|
248
|
-
if (ts.
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
//
|
|
283
|
+
if (ts.isElementAccessExpression(node)) {
|
|
284
|
+
// Index into a resolved object / array with a resolved scalar key — the
|
|
285
|
+
// Toggle `classes` memo's `variantClasses[variant]` where `variantClasses`
|
|
286
|
+
// is a seeded module-const object and `variant` resolved to `'default'`.
|
|
287
|
+
const base = evalNode(node.expression, ctx)
|
|
288
|
+
if (base === undefined) return undefined // `props['X']` → undefined
|
|
289
|
+
if (base === UNRESOLVED || base === null || typeof base !== 'object') return UNRESOLVED
|
|
290
|
+
if (!node.argumentExpression) return UNRESOLVED
|
|
291
|
+
const key = evalNode(node.argumentExpression, ctx)
|
|
292
|
+
if (key === UNRESOLVED || key === undefined || key === null) return UNRESOLVED
|
|
293
|
+
const k = String(key as string | number)
|
|
294
|
+
// Missing key → JS `undefined` (the `??`/`||` defaulting flows through it).
|
|
295
|
+
return Object.prototype.hasOwnProperty.call(base, k)
|
|
296
|
+
? (base as Record<string, unknown>)[k]
|
|
297
|
+
: undefined
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
301
|
+
// `props.X` / `props?.X` — read of a binding we know nothing about, so
|
|
302
|
+
// resolve to `undefined`. Chained access (`a.b.c`) collapses the same way
|
|
303
|
+
// because the base read is already undefined.
|
|
252
304
|
const baseResult = evalNode(node.expression, ctx)
|
|
253
305
|
if (baseResult === undefined) return undefined
|
|
254
306
|
return UNRESOLVED
|
|
@@ -264,6 +316,25 @@ function evalNode(node: ts.Expression, ctx: EvalContext): EvalResult {
|
|
|
264
316
|
) {
|
|
265
317
|
return ctx.bindings[node.expression.text]
|
|
266
318
|
}
|
|
319
|
+
// `<array>.join(<sep?>)` — evaluate when the receiver resolves to an array
|
|
320
|
+
// and the separator (default `,`) is a string. Covers `stateClasses =
|
|
321
|
+
// [...].join(' ')` (#checkbox). Other array methods stay unresolved.
|
|
322
|
+
if (
|
|
323
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
324
|
+
node.expression.name.text === 'join'
|
|
325
|
+
) {
|
|
326
|
+
const recv = evalNode(node.expression.expression, ctx)
|
|
327
|
+
if (Array.isArray(recv)) {
|
|
328
|
+
let sep = ','
|
|
329
|
+
if (node.arguments.length >= 1) {
|
|
330
|
+
const sepVal = evalNode(node.arguments[0], ctx)
|
|
331
|
+
if (typeof sepVal !== 'string') return UNRESOLVED
|
|
332
|
+
sep = sepVal
|
|
333
|
+
}
|
|
334
|
+
return recv.map(x => (x === null || x === undefined ? '' : `${x}`)).join(sep)
|
|
335
|
+
}
|
|
336
|
+
return UNRESOLVED
|
|
337
|
+
}
|
|
267
338
|
return UNRESOLVED
|
|
268
339
|
}
|
|
269
340
|
|