@barefootjs/xslate 0.31.9 → 0.31.10
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/index.js +5 -1
- package/dist/adapter/memo/seed.d.ts +13 -7
- package/dist/adapter/memo/seed.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/render-divergences.d.ts.map +1 -1
- package/dist/vite.js +40 -3
- package/lib/BarefootJS/Backend/Xslate.pm +1 -1
- package/package.json +5 -5
- package/src/__tests__/xslate-adapter.test.ts +7 -4
- package/src/adapter/memo/seed.ts +28 -8
- package/src/render-divergences.ts +8 -2
- package/src/test-render.ts +16 -0
package/dist/adapter/index.js
CHANGED
|
@@ -188198,8 +188198,12 @@ function generateDerivedMemoSeed(ctx, ir) {
|
|
|
188198
188198
|
const kolon = ctx.convertExpressionToKolon(step.expr, step.parsed);
|
|
188199
188199
|
if (kolon === "" || !/\$[A-Za-z_]\w*/.test(kolon))
|
|
188200
188200
|
continue;
|
|
188201
|
-
if (new RegExp(`\\$${step.name}\\b`).test(kolon))
|
|
188201
|
+
if (new RegExp(`\\$${step.name}\\b`).test(kolon)) {
|
|
188202
|
+
const tmp = `__bf_seed_${step.name}`;
|
|
188203
|
+
lines.push(`: my $${tmp} = ${kolon};`);
|
|
188204
|
+
lines.push(`: my $${step.name} = $${tmp};`);
|
|
188202
188205
|
continue;
|
|
188206
|
+
}
|
|
188203
188207
|
lines.push(`: my $${step.name} = ${kolon};`);
|
|
188204
188208
|
}
|
|
188205
188209
|
return lines.length > 0 ? lines.join(`
|
|
@@ -26,13 +26,19 @@ export declare function generateContextConsumerSeed(ir: ComponentIR): string;
|
|
|
26
26
|
* backend-specific emit guards: skip an empty lowering, skip a lowering that
|
|
27
27
|
* references no `$var` at all (a constant init/body — e.g. a `derived` step
|
|
28
28
|
* with empty `frees` — keeps the existing static ssr-defaults seed instead),
|
|
29
|
-
* and
|
|
30
|
-
* `: my $x = … $x …` would read the just-declared
|
|
31
|
-
* than the render var — the plan rules out
|
|
32
|
-
* lowered canonical name could still collide,
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
29
|
+
* and capture-before-shadow a self-referencing lowering (Kolon's `my`
|
|
30
|
+
* shadows the RHS, so `: my $x = … $x …` would read the just-declared
|
|
31
|
+
* undefined lexical rather than the render var — the plan rules out
|
|
32
|
+
* SOURCE-level self-refs, but a lowered canonical name could still collide,
|
|
33
|
+
* e.g. `createMemo(() => props.size ?? 'icon')` lowers `props.size` straight
|
|
34
|
+
* to the stash var `$size`, colliding with the memo's own name). The
|
|
35
|
+
* self-referencing case still emits the SAME verbatim Kolon — nothing about
|
|
36
|
+
* `kolon` changes — just as the RHS of a throwaway `__bf_seed_*` local
|
|
37
|
+
* declared BEFORE `$<name>` is shadowed, so `$<name>` inside `kolon` still
|
|
38
|
+
* resolves to the stash var, not the fresh lexical; a second statement then
|
|
39
|
+
* binds the real name off the already-evaluated capture. `env-reader` and
|
|
40
|
+
* `opaque` steps emit nothing (the runtime supplies the reader, or the
|
|
41
|
+
* adapter's ssr-defaults path already covers it). (#1297, #2075, #2679)
|
|
36
42
|
*/
|
|
37
43
|
export declare function generateDerivedMemoSeed(ctx: XslateMemoContext, ir: ComponentIR): string;
|
|
38
44
|
//# sourceMappingURL=seed.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../../src/adapter/memo/seed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,eAAe,EAGrB,MAAM,iBAAiB,CAAA;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAE3D,sEAAsE;AACtE,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CAM9D;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,WAAW,GAAG,MAAM,CAWnE;AAED
|
|
1
|
+
{"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../../src/adapter/memo/seed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,eAAe,EAGrB,MAAM,iBAAiB,CAAA;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAE3D,sEAAsE;AACtE,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CAM9D;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,WAAW,GAAG,MAAM,CAWnE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,iBAAiB,EAAE,EAAE,EAAE,WAAW,GAAG,MAAM,CA4BvF"}
|
package/dist/index.js
CHANGED
|
@@ -188198,8 +188198,12 @@ function generateDerivedMemoSeed(ctx, ir) {
|
|
|
188198
188198
|
const kolon = ctx.convertExpressionToKolon(step.expr, step.parsed);
|
|
188199
188199
|
if (kolon === "" || !/\$[A-Za-z_]\w*/.test(kolon))
|
|
188200
188200
|
continue;
|
|
188201
|
-
if (new RegExp(`\\$${step.name}\\b`).test(kolon))
|
|
188201
|
+
if (new RegExp(`\\$${step.name}\\b`).test(kolon)) {
|
|
188202
|
+
const tmp = `__bf_seed_${step.name}`;
|
|
188203
|
+
lines.push(`: my $${tmp} = ${kolon};`);
|
|
188204
|
+
lines.push(`: my $${step.name} = $${tmp};`);
|
|
188202
188205
|
continue;
|
|
188206
|
+
}
|
|
188203
188207
|
lines.push(`: my $${step.name} = ${kolon};`);
|
|
188204
188208
|
}
|
|
188205
188209
|
return lines.length > 0 ? lines.join(`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AASxD,eAAO,MAAM,iBAAiB,EAAE,iBAAsB,CAAA"}
|
package/dist/vite.js
CHANGED
|
@@ -3916,6 +3916,38 @@ function classify(name, origin, expr, parsed, available) {
|
|
|
3916
3916
|
}
|
|
3917
3917
|
return { kind: "derived", name, origin, expr, parsed, frees: [...frees] };
|
|
3918
3918
|
}
|
|
3919
|
+
function localConstExprsByName(metadata) {
|
|
3920
|
+
const out = new Map;
|
|
3921
|
+
for (const c of metadata.localConstants ?? []) {
|
|
3922
|
+
if (c.isModule || c.declarationKind !== "const" || c.value === undefined)
|
|
3923
|
+
continue;
|
|
3924
|
+
out.set(c.name, c.parsed ?? parseExpression(c.value.trim()));
|
|
3925
|
+
}
|
|
3926
|
+
return out;
|
|
3927
|
+
}
|
|
3928
|
+
function resolveThroughLocalConsts(parsed, localConsts) {
|
|
3929
|
+
let current = parsed;
|
|
3930
|
+
const maxIter = localConsts.size + 1;
|
|
3931
|
+
for (let i = 0;i < maxIter; i++) {
|
|
3932
|
+
const frees = freeIdentifiers(current);
|
|
3933
|
+
if (frees === null)
|
|
3934
|
+
break;
|
|
3935
|
+
let changed = false;
|
|
3936
|
+
for (const name of frees) {
|
|
3937
|
+
const value = localConsts.get(name);
|
|
3938
|
+
if (!value)
|
|
3939
|
+
continue;
|
|
3940
|
+
const inlined = inlineBinding(current, name, value);
|
|
3941
|
+
if (inlined === null)
|
|
3942
|
+
continue;
|
|
3943
|
+
current = inlined;
|
|
3944
|
+
changed = true;
|
|
3945
|
+
}
|
|
3946
|
+
if (!changed)
|
|
3947
|
+
break;
|
|
3948
|
+
}
|
|
3949
|
+
return current;
|
|
3950
|
+
}
|
|
3919
3951
|
function computeSsrSeedPlan(metadata) {
|
|
3920
3952
|
const baseScope = metadata.propsParams.map((p) => p.name);
|
|
3921
3953
|
if (metadata.propsObjectName)
|
|
@@ -3924,6 +3956,7 @@ function computeSsrSeedPlan(metadata) {
|
|
|
3924
3956
|
baseScope.push(name);
|
|
3925
3957
|
}
|
|
3926
3958
|
const available = new Set(baseScope);
|
|
3959
|
+
const localConsts = localConstExprsByName(metadata);
|
|
3927
3960
|
const steps = [];
|
|
3928
3961
|
for (const signal of metadata.signals) {
|
|
3929
3962
|
if (signal.envReader) {
|
|
@@ -3935,13 +3968,13 @@ function computeSsrSeedPlan(metadata) {
|
|
|
3935
3968
|
}
|
|
3936
3969
|
}
|
|
3937
3970
|
const expr = signal.initialValue.trim();
|
|
3938
|
-
steps.push(expr === "" ? { kind: "opaque", name: signal.getter, origin: "signal" } : classify(signal.getter, "signal", expr, parseExpression(expr), available));
|
|
3971
|
+
steps.push(expr === "" ? { kind: "opaque", name: signal.getter, origin: "signal" } : classify(signal.getter, "signal", expr, resolveThroughLocalConsts(parseExpression(expr), localConsts), available));
|
|
3939
3972
|
available.add(signal.getter);
|
|
3940
3973
|
}
|
|
3941
3974
|
for (const memo of metadata.memos) {
|
|
3942
3975
|
const body = extractArrowBodyExpression(memo.computation);
|
|
3943
3976
|
const expr = body?.trim() ?? "";
|
|
3944
|
-
steps.push(expr === "" ? { kind: "opaque", name: memo.name, origin: "memo" } : classify(memo.name, "memo", expr, memo.parsed ?? parseExpression(expr), available));
|
|
3977
|
+
steps.push(expr === "" ? { kind: "opaque", name: memo.name, origin: "memo" } : classify(memo.name, "memo", expr, resolveThroughLocalConsts(memo.parsed ?? parseExpression(expr), localConsts), available));
|
|
3945
3978
|
available.add(memo.name);
|
|
3946
3979
|
}
|
|
3947
3980
|
return { baseScope, steps };
|
|
@@ -6004,8 +6037,12 @@ function generateDerivedMemoSeed(ctx, ir) {
|
|
|
6004
6037
|
const kolon = ctx.convertExpressionToKolon(step.expr, step.parsed);
|
|
6005
6038
|
if (kolon === "" || !/\$[A-Za-z_]\w*/.test(kolon))
|
|
6006
6039
|
continue;
|
|
6007
|
-
if (new RegExp(`\\$${step.name}\\b`).test(kolon))
|
|
6040
|
+
if (new RegExp(`\\$${step.name}\\b`).test(kolon)) {
|
|
6041
|
+
const tmp = `__bf_seed_${step.name}`;
|
|
6042
|
+
lines.push(`: my $${tmp} = ${kolon};`);
|
|
6043
|
+
lines.push(`: my $${step.name} = $${tmp};`);
|
|
6008
6044
|
continue;
|
|
6045
|
+
}
|
|
6009
6046
|
lines.push(`: my $${step.name} = ${kolon};`);
|
|
6010
6047
|
}
|
|
6011
6048
|
return lines.length > 0 ? lines.join(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/xslate",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.10",
|
|
4
4
|
"description": "Text::Xslate (Kolon) adapter for BarefootJS — compiles IR to .tx templates and ships the Xslate rendering backend; runs under any PSGI/Plack app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"directory": "packages/adapter-xslate"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@barefootjs/shared": "0.31.
|
|
58
|
+
"@barefootjs/shared": "0.31.10"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"@barefootjs/jsx": ">=0.2.0",
|
|
@@ -72,9 +72,9 @@
|
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@barefootjs/adapter-tests": "0.1.0",
|
|
75
|
-
"@barefootjs/jsx": "0.31.
|
|
76
|
-
"@barefootjs/vite": "0.31.
|
|
77
|
-
"@barefootjs/client": "0.31.
|
|
75
|
+
"@barefootjs/jsx": "0.31.10",
|
|
76
|
+
"@barefootjs/vite": "0.31.10",
|
|
77
|
+
"@barefootjs/client": "0.31.10",
|
|
78
78
|
"typescript": "^5.0.0",
|
|
79
79
|
"vite": "^6.0.0"
|
|
80
80
|
}
|
|
@@ -199,9 +199,11 @@ export function Item(props: { defaultOn?: boolean }) {
|
|
|
199
199
|
expect(template).toContain(': my $on = ($defaultOn // 0);')
|
|
200
200
|
})
|
|
201
201
|
|
|
202
|
-
// Kolon can't `: my $x = … $x
|
|
203
|
-
//
|
|
204
|
-
|
|
202
|
+
// Kolon can't `: my $x = … $x …` directly (the declaration shadows the
|
|
203
|
+
// RHS's own reference), so a same-name signal captures the lowered
|
|
204
|
+
// expression into a throwaway `__bf_seed_<name>` local BEFORE `$x` is
|
|
205
|
+
// declared, then binds `$x` off that capture (#2679).
|
|
206
|
+
test('in-template-seeds a same-name signal via capture-before-shadow', () => {
|
|
205
207
|
const { template } = compileAndGenerate(`
|
|
206
208
|
'use client'
|
|
207
209
|
import { createSignal } from '@barefootjs/client'
|
|
@@ -210,7 +212,8 @@ export function C(props: { x?: number }) {
|
|
|
210
212
|
return <span>{x()}</span>
|
|
211
213
|
}
|
|
212
214
|
`)
|
|
213
|
-
expect(template).
|
|
215
|
+
expect(template).toContain(': my $__bf_seed_x = ($x // 7);')
|
|
216
|
+
expect(template).toContain(': my $x = $__bf_seed_x;')
|
|
214
217
|
})
|
|
215
218
|
|
|
216
219
|
test('emits data_key_attr on the component root', () => {
|
package/src/adapter/memo/seed.ts
CHANGED
|
@@ -53,13 +53,19 @@ export function generateContextConsumerSeed(ir: ComponentIR): string {
|
|
|
53
53
|
* backend-specific emit guards: skip an empty lowering, skip a lowering that
|
|
54
54
|
* references no `$var` at all (a constant init/body — e.g. a `derived` step
|
|
55
55
|
* with empty `frees` — keeps the existing static ssr-defaults seed instead),
|
|
56
|
-
* and
|
|
57
|
-
* `: my $x = … $x …` would read the just-declared
|
|
58
|
-
* than the render var — the plan rules out
|
|
59
|
-
* lowered canonical name could still collide,
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
56
|
+
* and capture-before-shadow a self-referencing lowering (Kolon's `my`
|
|
57
|
+
* shadows the RHS, so `: my $x = … $x …` would read the just-declared
|
|
58
|
+
* undefined lexical rather than the render var — the plan rules out
|
|
59
|
+
* SOURCE-level self-refs, but a lowered canonical name could still collide,
|
|
60
|
+
* e.g. `createMemo(() => props.size ?? 'icon')` lowers `props.size` straight
|
|
61
|
+
* to the stash var `$size`, colliding with the memo's own name). The
|
|
62
|
+
* self-referencing case still emits the SAME verbatim Kolon — nothing about
|
|
63
|
+
* `kolon` changes — just as the RHS of a throwaway `__bf_seed_*` local
|
|
64
|
+
* declared BEFORE `$<name>` is shadowed, so `$<name>` inside `kolon` still
|
|
65
|
+
* resolves to the stash var, not the fresh lexical; a second statement then
|
|
66
|
+
* binds the real name off the already-evaluated capture. `env-reader` and
|
|
67
|
+
* `opaque` steps emit nothing (the runtime supplies the reader, or the
|
|
68
|
+
* adapter's ssr-defaults path already covers it). (#1297, #2075, #2679)
|
|
63
69
|
*/
|
|
64
70
|
export function generateDerivedMemoSeed(ctx: XslateMemoContext, ir: ComponentIR): string {
|
|
65
71
|
// Package G attached this to metadata at compile time; the `??` fallback
|
|
@@ -71,7 +77,21 @@ export function generateDerivedMemoSeed(ctx: XslateMemoContext, ir: ComponentIR)
|
|
|
71
77
|
if (step.kind !== 'derived') continue
|
|
72
78
|
const kolon = ctx.convertExpressionToKolon(step.expr, step.parsed)
|
|
73
79
|
if (kolon === '' || !/\$[A-Za-z_]\w*/.test(kolon)) continue
|
|
74
|
-
if (new RegExp(`\\$${step.name}\\b`).test(kolon))
|
|
80
|
+
if (new RegExp(`\\$${step.name}\\b`).test(kolon)) {
|
|
81
|
+
// Self-referencing lowering (#2679): capture the RHS into a
|
|
82
|
+
// throwaway local BEFORE `$<name>` is declared — `$<name>` inside
|
|
83
|
+
// `kolon` still reads the stash var at this point, nothing is
|
|
84
|
+
// shadowed yet — then bind the real name off the capture. The
|
|
85
|
+
// `__bf_seed_` prefix follows the adapter's established internal-
|
|
86
|
+
// temporary convention (`__bf_item` / `__bf_pair` in the loop
|
|
87
|
+
// lowering); suffixing with `step.name` keeps two derived steps in
|
|
88
|
+
// the same template from colliding, since `step.name` is itself
|
|
89
|
+
// unique per plan (each step declares a distinct local).
|
|
90
|
+
const tmp = `__bf_seed_${step.name}`
|
|
91
|
+
lines.push(`: my $${tmp} = ${kolon};`)
|
|
92
|
+
lines.push(`: my $${step.name} = $${tmp};`)
|
|
93
|
+
continue
|
|
94
|
+
}
|
|
75
95
|
lines.push(`: my $${step.name} = ${kolon};`)
|
|
76
96
|
}
|
|
77
97
|
return lines.length > 0 ? lines.join('\n') + '\n' : ''
|
|
@@ -9,5 +9,11 @@
|
|
|
9
9
|
|
|
10
10
|
import type { RenderDivergences } from '@barefootjs/jsx'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
// #2679 graduated (capture-before-shadow in `generateDerivedMemoSeed`,
|
|
13
|
+
// packages/adapter-xslate/src/adapter/memo/seed.ts): a self-referencing
|
|
14
|
+
// derived signal/memo now seeds a throwaway `__bf_seed_<name>` local from
|
|
15
|
+
// the RAW-stash-var Kolon lowering BEFORE `$<name>` is declared, then binds
|
|
16
|
+
// the real name off that capture — the same in-template recompute the other
|
|
17
|
+
// six template-stash backends already had. Keep the file even when the set
|
|
18
|
+
// is empty — the next divergence lands here, not in a re-created file.
|
|
19
|
+
export const renderDivergences: RenderDivergences = {}
|
package/src/test-render.ts
CHANGED
|
@@ -568,6 +568,16 @@ function buildPerlProps(
|
|
|
568
568
|
// Env signals (#2057) are bound below via `search_params('')`, not from a
|
|
569
569
|
// static initial value.
|
|
570
570
|
if (signal.envReader) continue
|
|
571
|
+
// #2669: a self-derivation collision (the signal's own initializer
|
|
572
|
+
// derives from a same-named prop, e.g. `createSignal(props.label ??
|
|
573
|
+
// 'Default')`) already got the correct RAW-prop seed above via
|
|
574
|
+
// `derivedProps` / the user-props loop — `extractSsrDefaults` marks that
|
|
575
|
+
// with a `propName`-carrying entry (see its docstring's invariant).
|
|
576
|
+
// Re-seeding here with this harness's OWN recompute
|
|
577
|
+
// (`evaluateSignalInit`) would clobber the correctly-derived caller
|
|
578
|
+
// value with the harness's evaluated-from-static-props number, papering
|
|
579
|
+
// over the exact production bug this fixture exists to catch.
|
|
580
|
+
if (rootSsrDefaults[signal.getter]?.propName !== undefined) continue
|
|
571
581
|
const value = evaluateSignalInit(signal.initialValue.trim(), props)
|
|
572
582
|
if (value !== null) {
|
|
573
583
|
entries.push(`${signal.getter} => ${toPerlLiteral(value)}`)
|
|
@@ -577,6 +587,12 @@ function buildPerlProps(
|
|
|
577
587
|
// Memo values seeded from the statically-evaluated ssrDefaults, same
|
|
578
588
|
// as the production plugin's before_render hook.
|
|
579
589
|
for (const memo of ir.metadata.memos) {
|
|
590
|
+
// #2669: same self-derivation skip as the signal loop above — a
|
|
591
|
+
// `propName`-carrying memo entry already stands as the correctly
|
|
592
|
+
// prop-derived seed; don't clobber it with the memo's OWN evaluated
|
|
593
|
+
// value (which for a non-idempotent derivation is already the WRONG,
|
|
594
|
+
// double-applied number).
|
|
595
|
+
if (rootSsrDefaults[memo.name]?.propName !== undefined) continue
|
|
580
596
|
const entry = rootSsrDefaults[memo.name]
|
|
581
597
|
const value = entry ? entry.value : 0
|
|
582
598
|
entries.push(`${memo.name} => ${toPerlLiteral(value ?? 0)}`)
|