@barefootjs/xslate 0.17.0 → 0.17.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/adapter/expr/array-method.d.ts +10 -0
- package/dist/adapter/expr/array-method.d.ts.map +1 -1
- package/dist/adapter/expr/emitters.d.ts +6 -7
- package/dist/adapter/expr/emitters.d.ts.map +1 -1
- package/dist/adapter/index.js +42 -55
- package/dist/adapter/lib/ir-scope.d.ts +0 -5
- package/dist/adapter/lib/ir-scope.d.ts.map +1 -1
- package/dist/adapter/memo/seed.d.ts +14 -14
- package/dist/adapter/memo/seed.d.ts.map +1 -1
- package/dist/adapter/xslate-adapter.d.ts.map +1 -1
- package/dist/build.js +42 -55
- package/dist/index.js +42 -55
- package/lib/BarefootJS/Backend/Xslate.pm +1 -1
- package/package.json +3 -3
- package/src/__tests__/xslate-adapter.test.ts +148 -11
- package/src/adapter/expr/array-method.ts +21 -0
- package/src/adapter/expr/emitters.ts +30 -12
- package/src/adapter/lib/ir-scope.ts +0 -11
- package/src/adapter/memo/seed.ts +25 -72
- package/src/adapter/xslate-adapter.ts +21 -3
package/src/adapter/memo/seed.ts
CHANGED
|
@@ -13,13 +13,10 @@ import {
|
|
|
13
13
|
type ComponentIR,
|
|
14
14
|
type ContextConsumer,
|
|
15
15
|
collectContextConsumers,
|
|
16
|
-
|
|
17
|
-
isSupported,
|
|
18
|
-
parseExpression,
|
|
16
|
+
computeSsrSeedPlan,
|
|
19
17
|
} from '@barefootjs/jsx'
|
|
20
18
|
|
|
21
19
|
import type { XslateMemoContext } from '../emit-context.ts'
|
|
22
|
-
import { referencedVarsAreAvailable } from '../lib/ir-scope.ts'
|
|
23
20
|
|
|
24
21
|
/** Kolon literal for a context-consumer's `createContext` default. */
|
|
25
22
|
export function contextDefaultKolon(c: ContextConsumer): string {
|
|
@@ -49,77 +46,33 @@ export function generateContextConsumerSeed(ir: ComponentIR): string {
|
|
|
49
46
|
}
|
|
50
47
|
|
|
51
48
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* this
|
|
56
|
-
*
|
|
57
|
-
*
|
|
49
|
+
* Emit `: my $<name> = <kolon>;` line-statements for every `derived` step of
|
|
50
|
+
* the backend-neutral SSR seed plan — the scope/availability/ordering
|
|
51
|
+
* analysis lives in `computeSsrSeedPlan` (packages/jsx/src/ssr-seed-plan.ts);
|
|
52
|
+
* this only lowers each step's expression to Kolon and applies the
|
|
53
|
+
* backend-specific emit guards: skip an empty lowering, skip a lowering that
|
|
54
|
+
* references no `$var` at all (a constant init/body — e.g. a `derived` step
|
|
55
|
+
* with empty `frees` — keeps the existing static ssr-defaults seed instead),
|
|
56
|
+
* and skip a self-referencing lowering (Kolon's `my` shadows the RHS, so
|
|
57
|
+
* `: my $x = … $x …` would read the just-declared undefined lexical rather
|
|
58
|
+
* than the render var — the plan rules out SOURCE-level self-refs, but a
|
|
59
|
+
* lowered canonical name could still collide, so this stays as the cheap
|
|
60
|
+
* defense it always was). `env-reader` and `opaque` steps emit nothing (the
|
|
61
|
+
* runtime supplies the reader, or the adapter's ssr-defaults path already
|
|
62
|
+
* covers it). (#1297, #2075)
|
|
58
63
|
*/
|
|
59
64
|
export function generateDerivedMemoSeed(ctx: XslateMemoContext, ir: ComponentIR): string {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const available = new Set<string>(ir.metadata.propsParams.map(p => p.name))
|
|
65
|
+
// Package G attached this to metadata at compile time; the `??` fallback
|
|
66
|
+
// only covers hand-built metadata in older tests that predate the attached
|
|
67
|
+
// plan — same shared function, so there's no divergence from the compiler.
|
|
68
|
+
const plan = ir.metadata.ssrSeedPlan ?? computeSsrSeedPlan(ir.metadata)
|
|
65
69
|
const lines: string[] = []
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
for (const signal of signals) {
|
|
73
|
-
const kolon = tryLowerToKolon(ctx, signal.initialValue, available)
|
|
74
|
-
// Kolon can't express `: my $x = … $x …` — declaring `my $x` makes the
|
|
75
|
-
// RHS `$x` an undefined lexical rather than the render var. A same-name
|
|
76
|
-
// signal (`createSignal(props.x ?? d)`, getter == prop) is just the prop
|
|
77
|
-
// with a default, which the harness already seeds correctly from the
|
|
78
|
-
// passed prop — skip the in-template seed for it. (Different-name
|
|
79
|
-
// prop-derived signals like toggle's `on` from `defaultOn` are unaffected.)
|
|
80
|
-
const refsSelf = kolon !== null && new RegExp(`\\$${signal.getter}\\b`).test(kolon)
|
|
81
|
-
if (kolon !== null && !refsSelf) lines.push(`: my $${signal.getter} = ${kolon};`)
|
|
82
|
-
available.add(signal.getter)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
for (const memo of memos) {
|
|
86
|
-
// Seed every memo whose body lowers cleanly — not just the ones whose
|
|
87
|
-
// static SSR default is null. A statically-foldable prop-derived memo
|
|
88
|
-
// (`createMemo(() => props.disabled ?? false)` → default `false`)
|
|
89
|
-
// still depends on the per-call prop: the static stash seed bakes in
|
|
90
|
-
// the absent-prop fold, so a caller passing `disabled => 1` would
|
|
91
|
-
// render the default branch (#1897, select's disabled item). The
|
|
92
|
-
// in-template recomputation reads the prop lexical already in scope;
|
|
93
|
-
// block-bodied arrows / out-of-scope references fall back to the
|
|
94
|
-
// static ssr-defaults seed. Same self-reference guard as the signal
|
|
95
|
-
// loop above — Kolon's `my` shadows the render var on the RHS.
|
|
96
|
-
const body = extractArrowBodyExpression(memo.computation)
|
|
97
|
-
if (body !== null) {
|
|
98
|
-
const kolon = tryLowerToKolon(ctx, body, available)
|
|
99
|
-
const refsSelf = kolon !== null && new RegExp(`\\$${memo.name}\\b`).test(kolon)
|
|
100
|
-
if (kolon !== null && !refsSelf) lines.push(`: my $${memo.name} = ${kolon};`)
|
|
101
|
-
}
|
|
102
|
-
available.add(memo.name)
|
|
70
|
+
for (const step of plan.steps) {
|
|
71
|
+
if (step.kind !== 'derived') continue
|
|
72
|
+
const kolon = ctx.convertExpressionToKolon(step.expr, step.parsed)
|
|
73
|
+
if (kolon === '' || !/\$[A-Za-z_]\w*/.test(kolon)) continue
|
|
74
|
+
if (new RegExp(`\\$${step.name}\\b`).test(kolon)) continue
|
|
75
|
+
lines.push(`: my $${step.name} = ${kolon};`)
|
|
103
76
|
}
|
|
104
77
|
return lines.length > 0 ? lines.join('\n') + '\n' : ''
|
|
105
78
|
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Lower a signal init / memo body to Kolon for an in-template SSR seed, or
|
|
109
|
-
* `null` when it shouldn't be seeded this way: not a supported shape
|
|
110
|
-
* (`isSupported` pre-check, so object/array literals don't fail the build),
|
|
111
|
-
* references no in-scope var (a constant — keep ssr-defaults seeding), or
|
|
112
|
-
* references an out-of-scope binding. (#1297)
|
|
113
|
-
*/
|
|
114
|
-
export function tryLowerToKolon(
|
|
115
|
-
ctx: XslateMemoContext,
|
|
116
|
-
expr: string,
|
|
117
|
-
available: ReadonlySet<string>,
|
|
118
|
-
): string | null {
|
|
119
|
-
const trimmed = expr.trim()
|
|
120
|
-
if (!trimmed) return null
|
|
121
|
-
if (!isSupported(parseExpression(trimmed)).supported) return null
|
|
122
|
-
const kolon = ctx.convertExpressionToKolon(trimmed)
|
|
123
|
-
if (kolon === '' || !/\$[A-Za-z_]\w*/.test(kolon)) return null
|
|
124
|
-
return referencedVarsAreAvailable(kolon, available) ? kolon : null
|
|
125
|
-
}
|
|
@@ -568,8 +568,15 @@ export class XslateAdapter extends BaseAdapter implements IRNodeEmitter<XslateRe
|
|
|
568
568
|
// ===========================================================================
|
|
569
569
|
|
|
570
570
|
renderLoop(loop: IRLoop): string {
|
|
571
|
-
//
|
|
572
|
-
|
|
571
|
+
// clientOnly loops must not render items at SSR time, but must still emit
|
|
572
|
+
// the `loop:`/`/loop:` boundary marker pair (Hono and Go parity) so the
|
|
573
|
+
// client runtime's mapArray() can locate the insertion anchor when
|
|
574
|
+
// hydrating the array. Without the markers, mapArray() resolves
|
|
575
|
+
// anchor = null and appends after sibling markers (#872). The marker id
|
|
576
|
+
// disambiguates sibling `.map()` calls under the same parent (#1087).
|
|
577
|
+
if (loop.clientOnly) {
|
|
578
|
+
return `<: $bf.comment("loop:${loop.markerId}") | mark_raw :><: $bf.comment("/loop:${loop.markerId}") | mark_raw :>`
|
|
579
|
+
}
|
|
573
580
|
|
|
574
581
|
// An array/object-destructure loop param (`([emoji, users]) => ...` or
|
|
575
582
|
// `({ name, age }) => ...`) lowers to invalid Kolon — Kolon's `for LIST
|
|
@@ -1147,7 +1154,18 @@ export class XslateAdapter extends BaseAdapter implements IRNodeEmitter<XslateRe
|
|
|
1147
1154
|
param: string,
|
|
1148
1155
|
localVarMap: Map<string, string> = new Map(),
|
|
1149
1156
|
): string {
|
|
1150
|
-
return emitParsedExpr(
|
|
1157
|
+
return emitParsedExpr(
|
|
1158
|
+
expr,
|
|
1159
|
+
new XslateFilterEmitter(
|
|
1160
|
+
param,
|
|
1161
|
+
localVarMap,
|
|
1162
|
+
n => this._isStringValueName(n),
|
|
1163
|
+
// A nested callback method inside the predicate has no Kolon scalar
|
|
1164
|
+
// form — surface BF101 (#2038) instead of silently degrading it to
|
|
1165
|
+
// its receiver.
|
|
1166
|
+
(message, reason) => this._recordExprBF101(message, reason),
|
|
1167
|
+
),
|
|
1168
|
+
)
|
|
1151
1169
|
}
|
|
1152
1170
|
|
|
1153
1171
|
// ===========================================================================
|