@barefootjs/erb 0.31.4 → 0.31.5
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/erb-adapter.d.ts +16 -10
- package/dist/adapter/erb-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +16 -35
- package/dist/index.js +16 -35
- package/dist/vite.js +147 -119
- package/package.json +5 -5
- package/src/adapter/erb-adapter.ts +53 -60
- package/src/adapter/expr/emitters.ts +5 -5
|
@@ -128,17 +128,23 @@ export declare class ErbAdapter extends BaseAdapter implements IRNodeEmitter<Erb
|
|
|
128
128
|
*/
|
|
129
129
|
private localConstants;
|
|
130
130
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* for TWO things in the ERB adapter
|
|
135
|
-
* only used it to guard
|
|
136
|
-
* fundamental `v[:name]` vs
|
|
137
|
-
* `ErbTopLevelEmitter.identifier` —
|
|
138
|
-
* `isLoopBoundName` docstring for why ERB's
|
|
139
|
-
* where Perl's uniform `$name` sigil does
|
|
131
|
+
* The one canonical, position-accurate "names bound by an enclosing loop
|
|
132
|
+
* callback" service (#2482 Stage 2) — replaces the ref-counted
|
|
133
|
+
* `Map<string, number>` this adapter used to push/pop around
|
|
134
|
+
* `renderLoop`'s body. Load-bearing for TWO things in the ERB adapter
|
|
135
|
+
* (more than the Mojo original, which only used it to guard
|
|
136
|
+
* const-inlining): it also decides the fundamental `v[:name]` vs
|
|
137
|
+
* bare-Ruby-local rendering choice in `ErbTopLevelEmitter.identifier` —
|
|
138
|
+
* see `emit-context.ts`'s `isLoopBoundName` docstring for why ERB's
|
|
139
|
+
* two-locals model needs this where Perl's uniform `$name` sigil does
|
|
140
|
+
* not. Threaded via save/restore-by-reference around
|
|
141
|
+
* `renderChildren(loop.children)` in `renderLoop` (immutable — no
|
|
142
|
+
* ref-count bookkeeping), mirroring the Stage 1a/1b `ctx.scope`
|
|
143
|
+
* precedent in `jsx-to-ir.ts`. `IRLoop` already structurally satisfies
|
|
144
|
+
* `LoopBindingSource` (`param`/`index`/`paramBindings`/`preamble`), so
|
|
145
|
+
* `renderLoop` passes the loop node straight to `enterLoopRow`.
|
|
140
146
|
*/
|
|
141
|
-
private
|
|
147
|
+
private scope;
|
|
142
148
|
/**
|
|
143
149
|
* Prop names whose value is `nil` in the template body when the caller
|
|
144
150
|
* omits them — so a bare-reference attribute should be dropped rather
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"erb-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/erb-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EACV,WAAW,EAEX,MAAM,EACN,SAAS,EACT,MAAM,EACN,YAAY,EACZ,aAAa,EACb,MAAM,EACN,WAAW,EACX,UAAU,EACV,MAAM,EACN,aAAa,EACb,UAAU,EACV,OAAO,EAMP,yBAAyB,EAC1B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAE3B,KAAK,aAAa,EAClB,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"erb-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/erb-adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/adapter/index.js
CHANGED
|
@@ -187314,7 +187314,8 @@ import {
|
|
|
187314
187314
|
dangerousInnerHtmlMetacharViolation,
|
|
187315
187315
|
dangerousInnerHtmlDiagnostic,
|
|
187316
187316
|
resolveStaticLoopSource,
|
|
187317
|
-
derivesScopeFromSlot
|
|
187317
|
+
derivesScopeFromSlot,
|
|
187318
|
+
BindingScope
|
|
187318
187319
|
} from "@barefootjs/jsx";
|
|
187319
187320
|
|
|
187320
187321
|
// src/adapter/boolean-result.ts
|
|
@@ -188372,7 +188373,7 @@ class ErbAdapter extends BaseAdapter {
|
|
|
188372
188373
|
_loweringMatchers = [];
|
|
188373
188374
|
moduleStringConsts = new Map;
|
|
188374
188375
|
localConstants = [];
|
|
188375
|
-
|
|
188376
|
+
scope = BindingScope.EMPTY;
|
|
188376
188377
|
nullableOptionalProps = new Set;
|
|
188377
188378
|
constructor(options = {}) {
|
|
188378
188379
|
super();
|
|
@@ -188394,7 +188395,7 @@ class ErbAdapter extends BaseAdapter {
|
|
|
188394
188395
|
this._searchParamsLocals = searchParamsLocalNames(ir.metadata);
|
|
188395
188396
|
this._loweringMatchers = prepareLoweringMatchers(ir.metadata);
|
|
188396
188397
|
this.localConstants = ir.metadata.localConstants ?? [];
|
|
188397
|
-
this.
|
|
188398
|
+
this.scope = BindingScope.EMPTY;
|
|
188398
188399
|
this.errors = [];
|
|
188399
188400
|
this.childrenCaptureCounter = 0;
|
|
188400
188401
|
if (!options?.siblingTemplatesRegistered) {
|
|
@@ -188452,7 +188453,7 @@ class ErbAdapter extends BaseAdapter {
|
|
|
188452
188453
|
};
|
|
188453
188454
|
}
|
|
188454
188455
|
resolveLiteralConst(name) {
|
|
188455
|
-
if (this.
|
|
188456
|
+
if (this.scope.isBound(name))
|
|
188456
188457
|
return null;
|
|
188457
188458
|
const c = (this.localConstants ?? []).find((lc) => lc.name === name);
|
|
188458
188459
|
if (c?.value === undefined)
|
|
@@ -188466,15 +188467,13 @@ class ErbAdapter extends BaseAdapter {
|
|
|
188466
188467
|
return null;
|
|
188467
188468
|
}
|
|
188468
188469
|
resolveStaticRecordLiteral(objectName, key) {
|
|
188469
|
-
|
|
188470
|
-
return null;
|
|
188471
|
-
const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants);
|
|
188470
|
+
const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants, (name) => this.scope.isBound(name));
|
|
188472
188471
|
if (!hit)
|
|
188473
188472
|
return null;
|
|
188474
188473
|
return hit.kind === "number" ? hit.text : rubyStringLiteral(hit.text);
|
|
188475
188474
|
}
|
|
188476
188475
|
resolveModuleStringConst(name) {
|
|
188477
|
-
if (this.
|
|
188476
|
+
if (this.scope.isBound(name))
|
|
188478
188477
|
return null;
|
|
188479
188478
|
const value = this.moduleStringConsts.get(name);
|
|
188480
188479
|
if (value === undefined)
|
|
@@ -188482,7 +188481,7 @@ class ErbAdapter extends BaseAdapter {
|
|
|
188482
188481
|
return rubyStringLiteral(value);
|
|
188483
188482
|
}
|
|
188484
188483
|
isLoopBoundName(name) {
|
|
188485
|
-
return this.
|
|
188484
|
+
return this.scope.isBound(name);
|
|
188486
188485
|
}
|
|
188487
188486
|
generateScriptRegistrations(ir, scriptBaseName, scriptAssets, preloadAssets) {
|
|
188488
188487
|
if (scriptAssets) {
|
|
@@ -188759,12 +188758,12 @@ ${whenTrue}
|
|
|
188759
188758
|
});
|
|
188760
188759
|
}
|
|
188761
188760
|
const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
|
|
188762
|
-
isNameShadowed:
|
|
188761
|
+
isNameShadowed: this.scope.asShadowPredicate()
|
|
188763
188762
|
});
|
|
188764
188763
|
const staticArray = staticItems !== null ? staticValueToRuby(staticItems) : null;
|
|
188765
188764
|
if (staticArray === null && loop.arrayParsed?.kind === "identifier") {
|
|
188766
188765
|
const arrayName = loop.arrayParsed.name;
|
|
188767
|
-
const isUnresolvableLocalConst = !this.
|
|
188766
|
+
const isUnresolvableLocalConst = !this.scope.isBound(arrayName) && this.resolveModuleStringConst(arrayName) === null && this.resolveLiteralConst(arrayName) === null && this.localConstants.some((c) => c.name === arrayName && !c.isModule);
|
|
188768
188767
|
if (isUnresolvableLocalConst) {
|
|
188769
188768
|
this._recordExprBF101(`Loop array \`${arrayName}\` is a component-scope const computed from a runtime expression the ERB adapter cannot evaluate at SSR render time.`, `Options:
|
|
188770
188769
|
1. Inline the array expression directly in the .map() call instead of a preceding const.
|
|
@@ -188781,13 +188780,9 @@ ${whenTrue}
|
|
|
188781
188780
|
}
|
|
188782
188781
|
const param = loop.param;
|
|
188783
188782
|
const indexVar = loop.iterationShape === "keys" ? rubyLocal(param) : rubyLocal(loop.index ?? "_i");
|
|
188784
|
-
const loopBound = loop.objectIteration === "entries" ? [param, loop.index ?? "_k"] : loop.objectIteration === "keys" || loop.objectIteration === "values" || loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
188785
188783
|
const preambleDecls = loop.preamble?.declarations ?? [];
|
|
188786
|
-
|
|
188787
|
-
|
|
188788
|
-
for (const n of loopBound) {
|
|
188789
|
-
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
|
|
188790
|
-
}
|
|
188784
|
+
const prevScope = this.scope;
|
|
188785
|
+
this.scope = prevScope.enterLoopRow(loop);
|
|
188791
188786
|
const prevLoopKeyDepth = this.currentLoopKeyDepth;
|
|
188792
188787
|
this.currentLoopKeyDepth = loop.depth;
|
|
188793
188788
|
const renderedChildren = this.renderChildren(loop.children);
|
|
@@ -188797,13 +188792,7 @@ ${renderedChildren}` : renderedChildren;
|
|
|
188797
188792
|
const lines = [];
|
|
188798
188793
|
lines.push(`<%= bf.comment("loop:${loop.markerId}") %>`);
|
|
188799
188794
|
if (sortedHoist && loop.sortComparator) {
|
|
188800
|
-
|
|
188801
|
-
const c = (this.loopBoundNames.get(n) ?? 1) - 1;
|
|
188802
|
-
if (c <= 0)
|
|
188803
|
-
this.loopBoundNames.delete(n);
|
|
188804
|
-
else
|
|
188805
|
-
this.loopBoundNames.set(n, c);
|
|
188806
|
-
}
|
|
188795
|
+
this.scope = prevScope;
|
|
188807
188796
|
const sortEmit = (e) => this.convertExpressionToRuby("", e);
|
|
188808
188797
|
const sortArrow = loop.sortComparator.arrow;
|
|
188809
188798
|
let sorted = null;
|
|
@@ -188819,9 +188808,7 @@ ${renderedChildren}` : renderedChildren;
|
|
|
188819
188808
|
this._recordExprBF101(`.sort(...) loop comparator is not lowerable to a template sort`, `Pre-sort the array in the route handler, or mark the loop @client-only.`);
|
|
188820
188809
|
sorted = rawArray;
|
|
188821
188810
|
}
|
|
188822
|
-
|
|
188823
|
-
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
|
|
188824
|
-
}
|
|
188811
|
+
this.scope = prevScope.enterLoopRow(loop);
|
|
188825
188812
|
lines.push(`<%- ${sortedHoist} = ${sorted} -%>`);
|
|
188826
188813
|
}
|
|
188827
188814
|
if (loop.objectIteration) {
|
|
@@ -188867,13 +188854,7 @@ ${renderedChildren}` : renderedChildren;
|
|
|
188867
188854
|
lines.push(...preambleLines);
|
|
188868
188855
|
lines.push(children);
|
|
188869
188856
|
}
|
|
188870
|
-
|
|
188871
|
-
const c = (this.loopBoundNames.get(n) ?? 1) - 1;
|
|
188872
|
-
if (c <= 0)
|
|
188873
|
-
this.loopBoundNames.delete(n);
|
|
188874
|
-
else
|
|
188875
|
-
this.loopBoundNames.set(n, c);
|
|
188876
|
-
}
|
|
188857
|
+
this.scope = prevScope;
|
|
188877
188858
|
lines.push(`<%- end -%>`);
|
|
188878
188859
|
lines.push(`<%= bf.comment("/loop:${loop.markerId}") %>`);
|
|
188879
188860
|
return lines.join(`
|
|
@@ -189035,7 +189016,7 @@ ${children}`;
|
|
|
189035
189016
|
if (ternaryHash !== null) {
|
|
189036
189017
|
return `<%= bf.spread_attrs(${ternaryHash}) %>`;
|
|
189037
189018
|
}
|
|
189038
|
-
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.
|
|
189019
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.scope.isBound(trimmed)) {
|
|
189039
189020
|
const localConst = this.localConstants.find((c) => c.name === trimmed && !c.isModule);
|
|
189040
189021
|
if (localConst?.value !== undefined) {
|
|
189041
189022
|
const initTrimmed = localConst.value.trim();
|
package/dist/index.js
CHANGED
|
@@ -187314,7 +187314,8 @@ import {
|
|
|
187314
187314
|
dangerousInnerHtmlMetacharViolation,
|
|
187315
187315
|
dangerousInnerHtmlDiagnostic,
|
|
187316
187316
|
resolveStaticLoopSource,
|
|
187317
|
-
derivesScopeFromSlot
|
|
187317
|
+
derivesScopeFromSlot,
|
|
187318
|
+
BindingScope
|
|
187318
187319
|
} from "@barefootjs/jsx";
|
|
187319
187320
|
|
|
187320
187321
|
// src/adapter/boolean-result.ts
|
|
@@ -188372,7 +188373,7 @@ class ErbAdapter extends BaseAdapter {
|
|
|
188372
188373
|
_loweringMatchers = [];
|
|
188373
188374
|
moduleStringConsts = new Map;
|
|
188374
188375
|
localConstants = [];
|
|
188375
|
-
|
|
188376
|
+
scope = BindingScope.EMPTY;
|
|
188376
188377
|
nullableOptionalProps = new Set;
|
|
188377
188378
|
constructor(options = {}) {
|
|
188378
188379
|
super();
|
|
@@ -188394,7 +188395,7 @@ class ErbAdapter extends BaseAdapter {
|
|
|
188394
188395
|
this._searchParamsLocals = searchParamsLocalNames(ir.metadata);
|
|
188395
188396
|
this._loweringMatchers = prepareLoweringMatchers(ir.metadata);
|
|
188396
188397
|
this.localConstants = ir.metadata.localConstants ?? [];
|
|
188397
|
-
this.
|
|
188398
|
+
this.scope = BindingScope.EMPTY;
|
|
188398
188399
|
this.errors = [];
|
|
188399
188400
|
this.childrenCaptureCounter = 0;
|
|
188400
188401
|
if (!options?.siblingTemplatesRegistered) {
|
|
@@ -188452,7 +188453,7 @@ class ErbAdapter extends BaseAdapter {
|
|
|
188452
188453
|
};
|
|
188453
188454
|
}
|
|
188454
188455
|
resolveLiteralConst(name) {
|
|
188455
|
-
if (this.
|
|
188456
|
+
if (this.scope.isBound(name))
|
|
188456
188457
|
return null;
|
|
188457
188458
|
const c = (this.localConstants ?? []).find((lc) => lc.name === name);
|
|
188458
188459
|
if (c?.value === undefined)
|
|
@@ -188466,15 +188467,13 @@ class ErbAdapter extends BaseAdapter {
|
|
|
188466
188467
|
return null;
|
|
188467
188468
|
}
|
|
188468
188469
|
resolveStaticRecordLiteral(objectName, key) {
|
|
188469
|
-
|
|
188470
|
-
return null;
|
|
188471
|
-
const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants);
|
|
188470
|
+
const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants, (name) => this.scope.isBound(name));
|
|
188472
188471
|
if (!hit)
|
|
188473
188472
|
return null;
|
|
188474
188473
|
return hit.kind === "number" ? hit.text : rubyStringLiteral(hit.text);
|
|
188475
188474
|
}
|
|
188476
188475
|
resolveModuleStringConst(name) {
|
|
188477
|
-
if (this.
|
|
188476
|
+
if (this.scope.isBound(name))
|
|
188478
188477
|
return null;
|
|
188479
188478
|
const value = this.moduleStringConsts.get(name);
|
|
188480
188479
|
if (value === undefined)
|
|
@@ -188482,7 +188481,7 @@ class ErbAdapter extends BaseAdapter {
|
|
|
188482
188481
|
return rubyStringLiteral(value);
|
|
188483
188482
|
}
|
|
188484
188483
|
isLoopBoundName(name) {
|
|
188485
|
-
return this.
|
|
188484
|
+
return this.scope.isBound(name);
|
|
188486
188485
|
}
|
|
188487
188486
|
generateScriptRegistrations(ir, scriptBaseName, scriptAssets, preloadAssets) {
|
|
188488
188487
|
if (scriptAssets) {
|
|
@@ -188759,12 +188758,12 @@ ${whenTrue}
|
|
|
188759
188758
|
});
|
|
188760
188759
|
}
|
|
188761
188760
|
const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
|
|
188762
|
-
isNameShadowed:
|
|
188761
|
+
isNameShadowed: this.scope.asShadowPredicate()
|
|
188763
188762
|
});
|
|
188764
188763
|
const staticArray = staticItems !== null ? staticValueToRuby(staticItems) : null;
|
|
188765
188764
|
if (staticArray === null && loop.arrayParsed?.kind === "identifier") {
|
|
188766
188765
|
const arrayName = loop.arrayParsed.name;
|
|
188767
|
-
const isUnresolvableLocalConst = !this.
|
|
188766
|
+
const isUnresolvableLocalConst = !this.scope.isBound(arrayName) && this.resolveModuleStringConst(arrayName) === null && this.resolveLiteralConst(arrayName) === null && this.localConstants.some((c) => c.name === arrayName && !c.isModule);
|
|
188768
188767
|
if (isUnresolvableLocalConst) {
|
|
188769
188768
|
this._recordExprBF101(`Loop array \`${arrayName}\` is a component-scope const computed from a runtime expression the ERB adapter cannot evaluate at SSR render time.`, `Options:
|
|
188770
188769
|
1. Inline the array expression directly in the .map() call instead of a preceding const.
|
|
@@ -188781,13 +188780,9 @@ ${whenTrue}
|
|
|
188781
188780
|
}
|
|
188782
188781
|
const param = loop.param;
|
|
188783
188782
|
const indexVar = loop.iterationShape === "keys" ? rubyLocal(param) : rubyLocal(loop.index ?? "_i");
|
|
188784
|
-
const loopBound = loop.objectIteration === "entries" ? [param, loop.index ?? "_k"] : loop.objectIteration === "keys" || loop.objectIteration === "values" || loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
188785
188783
|
const preambleDecls = loop.preamble?.declarations ?? [];
|
|
188786
|
-
|
|
188787
|
-
|
|
188788
|
-
for (const n of loopBound) {
|
|
188789
|
-
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
|
|
188790
|
-
}
|
|
188784
|
+
const prevScope = this.scope;
|
|
188785
|
+
this.scope = prevScope.enterLoopRow(loop);
|
|
188791
188786
|
const prevLoopKeyDepth = this.currentLoopKeyDepth;
|
|
188792
188787
|
this.currentLoopKeyDepth = loop.depth;
|
|
188793
188788
|
const renderedChildren = this.renderChildren(loop.children);
|
|
@@ -188797,13 +188792,7 @@ ${renderedChildren}` : renderedChildren;
|
|
|
188797
188792
|
const lines = [];
|
|
188798
188793
|
lines.push(`<%= bf.comment("loop:${loop.markerId}") %>`);
|
|
188799
188794
|
if (sortedHoist && loop.sortComparator) {
|
|
188800
|
-
|
|
188801
|
-
const c = (this.loopBoundNames.get(n) ?? 1) - 1;
|
|
188802
|
-
if (c <= 0)
|
|
188803
|
-
this.loopBoundNames.delete(n);
|
|
188804
|
-
else
|
|
188805
|
-
this.loopBoundNames.set(n, c);
|
|
188806
|
-
}
|
|
188795
|
+
this.scope = prevScope;
|
|
188807
188796
|
const sortEmit = (e) => this.convertExpressionToRuby("", e);
|
|
188808
188797
|
const sortArrow = loop.sortComparator.arrow;
|
|
188809
188798
|
let sorted = null;
|
|
@@ -188819,9 +188808,7 @@ ${renderedChildren}` : renderedChildren;
|
|
|
188819
188808
|
this._recordExprBF101(`.sort(...) loop comparator is not lowerable to a template sort`, `Pre-sort the array in the route handler, or mark the loop @client-only.`);
|
|
188820
188809
|
sorted = rawArray;
|
|
188821
188810
|
}
|
|
188822
|
-
|
|
188823
|
-
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
|
|
188824
|
-
}
|
|
188811
|
+
this.scope = prevScope.enterLoopRow(loop);
|
|
188825
188812
|
lines.push(`<%- ${sortedHoist} = ${sorted} -%>`);
|
|
188826
188813
|
}
|
|
188827
188814
|
if (loop.objectIteration) {
|
|
@@ -188867,13 +188854,7 @@ ${renderedChildren}` : renderedChildren;
|
|
|
188867
188854
|
lines.push(...preambleLines);
|
|
188868
188855
|
lines.push(children);
|
|
188869
188856
|
}
|
|
188870
|
-
|
|
188871
|
-
const c = (this.loopBoundNames.get(n) ?? 1) - 1;
|
|
188872
|
-
if (c <= 0)
|
|
188873
|
-
this.loopBoundNames.delete(n);
|
|
188874
|
-
else
|
|
188875
|
-
this.loopBoundNames.set(n, c);
|
|
188876
|
-
}
|
|
188857
|
+
this.scope = prevScope;
|
|
188877
188858
|
lines.push(`<%- end -%>`);
|
|
188878
188859
|
lines.push(`<%= bf.comment("/loop:${loop.markerId}") %>`);
|
|
188879
188860
|
return lines.join(`
|
|
@@ -189035,7 +189016,7 @@ ${children}`;
|
|
|
189035
189016
|
if (ternaryHash !== null) {
|
|
189036
189017
|
return `<%= bf.spread_attrs(${ternaryHash}) %>`;
|
|
189037
189018
|
}
|
|
189038
|
-
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.
|
|
189019
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.scope.isBound(trimmed)) {
|
|
189039
189020
|
const localConst = this.localConstants.find((c) => c.name === trimmed && !c.isModule);
|
|
189040
189021
|
if (localConst?.value !== undefined) {
|
|
189041
189022
|
const initTrimmed = localConst.value.trim();
|
package/dist/vite.js
CHANGED
|
@@ -5,7 +5,7 @@ import { barefoot as coreBarefoot } from "@barefootjs/vite";
|
|
|
5
5
|
import { devModuleUrl, loadManifest, resolveDevOrigin, resolveScriptAssets, toPosixRelative } from "@barefootjs/vite";
|
|
6
6
|
|
|
7
7
|
// ../jsx/src/compiler.ts
|
|
8
|
-
import
|
|
8
|
+
import ts24 from "typescript";
|
|
9
9
|
|
|
10
10
|
// ../jsx/src/analyzer.ts
|
|
11
11
|
import ts9 from "typescript";
|
|
@@ -2527,7 +2527,7 @@ var REACTIVE_PRIMITIVES = new Set([
|
|
|
2527
2527
|
]);
|
|
2528
2528
|
|
|
2529
2529
|
// ../jsx/src/jsx-to-ir.ts
|
|
2530
|
-
import
|
|
2530
|
+
import ts13 from "typescript";
|
|
2531
2531
|
|
|
2532
2532
|
// ../jsx/src/types.ts
|
|
2533
2533
|
var SCOPE_FORBIDDEN = {
|
|
@@ -2577,6 +2577,7 @@ function preambleAnalysisTemplateText(p) {
|
|
|
2577
2577
|
}
|
|
2578
2578
|
|
|
2579
2579
|
// ../jsx/src/module-exports.ts
|
|
2580
|
+
import ts10 from "typescript";
|
|
2580
2581
|
function formatParamWithType(p) {
|
|
2581
2582
|
const rest = p.isRest ? "..." : "";
|
|
2582
2583
|
const optional = p.optional ? "?" : "";
|
|
@@ -2607,12 +2608,55 @@ function findReachableNames(primaryRefs, declarations) {
|
|
|
2607
2608
|
}
|
|
2608
2609
|
return reachable;
|
|
2609
2610
|
}
|
|
2611
|
+
function findAssignedNames(bodyText, candidates) {
|
|
2612
|
+
const assigned = new Set;
|
|
2613
|
+
if (candidates.size === 0)
|
|
2614
|
+
return assigned;
|
|
2615
|
+
const sf = ts10.createSourceFile("bf-assignment-scan.tsx", bodyText, ts10.ScriptTarget.Latest, false, ts10.ScriptKind.TSX);
|
|
2616
|
+
const record = (target) => {
|
|
2617
|
+
if (ts10.isIdentifier(target) && candidates.has(target.text)) {
|
|
2618
|
+
assigned.add(target.text);
|
|
2619
|
+
}
|
|
2620
|
+
};
|
|
2621
|
+
const visit = (node) => {
|
|
2622
|
+
if (ts10.isBinaryExpression(node) && isAssignmentOperator(node.operatorToken.kind)) {
|
|
2623
|
+
record(node.left);
|
|
2624
|
+
} else if ((ts10.isPrefixUnaryExpression(node) || ts10.isPostfixUnaryExpression(node)) && (node.operator === ts10.SyntaxKind.PlusPlusToken || node.operator === ts10.SyntaxKind.MinusMinusToken)) {
|
|
2625
|
+
record(node.operand);
|
|
2626
|
+
}
|
|
2627
|
+
ts10.forEachChild(node, visit);
|
|
2628
|
+
};
|
|
2629
|
+
ts10.forEachChild(sf, visit);
|
|
2630
|
+
return assigned;
|
|
2631
|
+
}
|
|
2632
|
+
function closeOverWritersOfMutableBindings(primaryRefs, declarations, mutableNames) {
|
|
2633
|
+
let reachable = findReachableNames(primaryRefs, declarations);
|
|
2634
|
+
if (mutableNames.size === 0)
|
|
2635
|
+
return reachable;
|
|
2636
|
+
let seedText = primaryRefs;
|
|
2637
|
+
for (let round = 0;round <= declarations.length; round++) {
|
|
2638
|
+
const survivingMutables = new Set([...reachable].filter((name) => mutableNames.has(name)));
|
|
2639
|
+
if (survivingMutables.size === 0)
|
|
2640
|
+
return reachable;
|
|
2641
|
+
const added = declarations.filter((d) => !reachable.has(d.name)).filter((d) => findAssignedNames(d.body, survivingMutables).size > 0).map((d) => d.name);
|
|
2642
|
+
if (added.length === 0)
|
|
2643
|
+
return reachable;
|
|
2644
|
+
seedText += `
|
|
2645
|
+
` + added.join(`
|
|
2646
|
+
`);
|
|
2647
|
+
reachable = findReachableNames(seedText, declarations);
|
|
2648
|
+
}
|
|
2649
|
+
return reachable;
|
|
2650
|
+
}
|
|
2651
|
+
function isAssignmentOperator(kind) {
|
|
2652
|
+
return kind >= ts10.SyntaxKind.FirstAssignment && kind <= ts10.SyntaxKind.LastAssignment;
|
|
2653
|
+
}
|
|
2610
2654
|
|
|
2611
2655
|
// ../jsx/src/reactivity-checker.ts
|
|
2612
|
-
import
|
|
2656
|
+
import ts11 from "typescript";
|
|
2613
2657
|
|
|
2614
2658
|
// ../jsx/src/free-refs.ts
|
|
2615
|
-
import
|
|
2659
|
+
import ts12 from "typescript";
|
|
2616
2660
|
var _bindingMapCache = new WeakMap;
|
|
2617
2661
|
|
|
2618
2662
|
// ../jsx/src/to-locale-date-lowering.ts
|
|
@@ -3026,13 +3070,13 @@ var KEYWORDS_AND_GLOBALS = new Set([
|
|
|
3026
3070
|
]);
|
|
3027
3071
|
|
|
3028
3072
|
// ../jsx/src/ir-to-client-js/walk-prop-accesses.ts
|
|
3029
|
-
import
|
|
3073
|
+
import ts14 from "typescript";
|
|
3030
3074
|
|
|
3031
3075
|
// ../jsx/src/value-references.ts
|
|
3032
|
-
import
|
|
3076
|
+
import ts15 from "typescript";
|
|
3033
3077
|
|
|
3034
3078
|
// ../jsx/src/relocate.ts
|
|
3035
|
-
import
|
|
3079
|
+
import ts16 from "typescript";
|
|
3036
3080
|
|
|
3037
3081
|
// ../jsx/src/lowering-registry.ts
|
|
3038
3082
|
var plugins = [];
|
|
@@ -3249,10 +3293,10 @@ function matchSearchParamsMethodCall(callee, args, localNames) {
|
|
|
3249
3293
|
}
|
|
3250
3294
|
|
|
3251
3295
|
// ../jsx/src/ir-to-client-js/prune-unused-prop-extractions.ts
|
|
3252
|
-
import
|
|
3296
|
+
import ts17 from "typescript";
|
|
3253
3297
|
|
|
3254
3298
|
// ../jsx/src/ir-to-client-js/control-flow/plan/lazy-preamble.ts
|
|
3255
|
-
import
|
|
3299
|
+
import ts18 from "typescript";
|
|
3256
3300
|
var NO_PREAMBLE = {
|
|
3257
3301
|
lazySafe: true,
|
|
3258
3302
|
facts: { declaredNames: new Set, freeNames: new Set }
|
|
@@ -3302,7 +3346,7 @@ var INERT_BINDING_GLOBALS = new Set([
|
|
|
3302
3346
|
]);
|
|
3303
3347
|
|
|
3304
3348
|
// ../jsx/src/ir-to-client-js/emit-reactive.ts
|
|
3305
|
-
import
|
|
3349
|
+
import ts19 from "typescript";
|
|
3306
3350
|
|
|
3307
3351
|
// ../jsx/src/ir-to-client-js/control-flow/stringify/event-delegation.ts
|
|
3308
3352
|
var NON_BUBBLING_EVENTS = new Set([
|
|
@@ -3317,7 +3361,7 @@ var NON_BUBBLING_EVENTS = new Set([
|
|
|
3317
3361
|
]);
|
|
3318
3362
|
|
|
3319
3363
|
// ../jsx/src/ir-to-client-js/rewrite-props-object.ts
|
|
3320
|
-
import
|
|
3364
|
+
import ts20 from "typescript";
|
|
3321
3365
|
|
|
3322
3366
|
// ../jsx/src/ir-to-client-js/source-map.ts
|
|
3323
3367
|
var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
@@ -3408,15 +3452,15 @@ class SourceMapGenerator {
|
|
|
3408
3452
|
}
|
|
3409
3453
|
|
|
3410
3454
|
// ../jsx/src/preprocess-inline-jsx-callbacks.ts
|
|
3411
|
-
import
|
|
3455
|
+
import ts21 from "typescript";
|
|
3412
3456
|
|
|
3413
3457
|
// ../jsx/src/ssr-defaults.ts
|
|
3414
|
-
import
|
|
3458
|
+
import ts22 from "typescript";
|
|
3415
3459
|
var UNRESOLVED = Symbol("unresolved");
|
|
3416
3460
|
var NO_RETURN = Symbol("no-return");
|
|
3417
3461
|
|
|
3418
3462
|
// ../jsx/src/augment-inherited-props.ts
|
|
3419
|
-
import
|
|
3463
|
+
import ts23 from "typescript";
|
|
3420
3464
|
function collectContextConsumers(metadata) {
|
|
3421
3465
|
const constants = metadata.localConstants ?? [];
|
|
3422
3466
|
const contextDefaults = new Map;
|
|
@@ -3448,47 +3492,47 @@ function collectContextConsumers(metadata) {
|
|
|
3448
3492
|
}
|
|
3449
3493
|
function parseUseContextArg(source) {
|
|
3450
3494
|
const expr = parseSingleExpression(source);
|
|
3451
|
-
if (!expr || !
|
|
3495
|
+
if (!expr || !ts23.isCallExpression(expr))
|
|
3452
3496
|
return null;
|
|
3453
|
-
if (!
|
|
3497
|
+
if (!ts23.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
|
|
3454
3498
|
return null;
|
|
3455
3499
|
if (expr.arguments.length !== 1)
|
|
3456
3500
|
return null;
|
|
3457
3501
|
const arg = expr.arguments[0];
|
|
3458
|
-
return
|
|
3502
|
+
return ts23.isIdentifier(arg) ? arg.text : null;
|
|
3459
3503
|
}
|
|
3460
3504
|
function parseCreateContextDefault(source) {
|
|
3461
3505
|
const expr = parseSingleExpression(source);
|
|
3462
|
-
if (!expr || !
|
|
3506
|
+
if (!expr || !ts23.isCallExpression(expr))
|
|
3463
3507
|
return null;
|
|
3464
3508
|
if (expr.arguments.length === 0)
|
|
3465
3509
|
return null;
|
|
3466
3510
|
const arg = expr.arguments[0];
|
|
3467
|
-
if (
|
|
3511
|
+
if (ts23.isStringLiteral(arg) || ts23.isNoSubstitutionTemplateLiteral(arg))
|
|
3468
3512
|
return arg.text;
|
|
3469
|
-
if (
|
|
3513
|
+
if (ts23.isNumericLiteral(arg))
|
|
3470
3514
|
return Number(arg.text);
|
|
3471
|
-
if (arg.kind ===
|
|
3515
|
+
if (arg.kind === ts23.SyntaxKind.TrueKeyword)
|
|
3472
3516
|
return true;
|
|
3473
|
-
if (arg.kind ===
|
|
3517
|
+
if (arg.kind === ts23.SyntaxKind.FalseKeyword)
|
|
3474
3518
|
return false;
|
|
3475
3519
|
return null;
|
|
3476
3520
|
}
|
|
3477
3521
|
function isObjectLiteralCreateContextDefault(source) {
|
|
3478
3522
|
const expr = parseSingleExpression(source);
|
|
3479
|
-
if (!expr || !
|
|
3523
|
+
if (!expr || !ts23.isCallExpression(expr))
|
|
3480
3524
|
return false;
|
|
3481
3525
|
if (expr.arguments.length === 0)
|
|
3482
3526
|
return false;
|
|
3483
|
-
return
|
|
3527
|
+
return ts23.isObjectLiteralExpression(expr.arguments[0]);
|
|
3484
3528
|
}
|
|
3485
3529
|
function parseSingleExpression(source) {
|
|
3486
|
-
const sf =
|
|
3530
|
+
const sf = ts23.createSourceFile("__ctx.ts", `(${source})`, ts23.ScriptTarget.Latest, false);
|
|
3487
3531
|
const stmt = sf.statements[0];
|
|
3488
|
-
if (!stmt || !
|
|
3532
|
+
if (!stmt || !ts23.isExpressionStatement(stmt))
|
|
3489
3533
|
return null;
|
|
3490
3534
|
let e = stmt.expression;
|
|
3491
|
-
while (
|
|
3535
|
+
while (ts23.isParenthesizedExpression(e))
|
|
3492
3536
|
e = e.expression;
|
|
3493
3537
|
return e;
|
|
3494
3538
|
}
|
|
@@ -3513,25 +3557,25 @@ function augmentInheritedPropAccesses(ir) {
|
|
|
3513
3557
|
const pinCoalesceLiterals = (s) => {
|
|
3514
3558
|
if (!s || !s.includes(propsObj))
|
|
3515
3559
|
return;
|
|
3516
|
-
const sf =
|
|
3560
|
+
const sf = ts23.createSourceFile("__aug.ts", `(${s})`, ts23.ScriptTarget.Latest, false);
|
|
3517
3561
|
const visit = (n) => {
|
|
3518
|
-
if (
|
|
3562
|
+
if (ts23.isBinaryExpression(n) && (n.operatorToken.kind === ts23.SyntaxKind.QuestionQuestionToken || n.operatorToken.kind === ts23.SyntaxKind.BarBarToken)) {
|
|
3519
3563
|
let left = n.left;
|
|
3520
|
-
while (
|
|
3564
|
+
while (ts23.isParenthesizedExpression(left))
|
|
3521
3565
|
left = left.expression;
|
|
3522
|
-
if (
|
|
3566
|
+
if (ts23.isPropertyAccessExpression(left) && ts23.isIdentifier(left.expression) && left.expression.text === propsObj) {
|
|
3523
3567
|
const name = left.name.text;
|
|
3524
3568
|
let right = n.right;
|
|
3525
|
-
while (
|
|
3569
|
+
while (ts23.isParenthesizedExpression(right))
|
|
3526
3570
|
right = right.expression;
|
|
3527
|
-
if (
|
|
3571
|
+
if (ts23.isPrefixUnaryExpression(right))
|
|
3528
3572
|
right = right.operand;
|
|
3529
|
-
const kind =
|
|
3573
|
+
const kind = ts23.isNumericLiteral(right) ? "number" : right.kind === ts23.SyntaxKind.TrueKeyword || right.kind === ts23.SyntaxKind.FalseKeyword ? "boolean" : ts23.isStringLiteralLike(right) ? "string" : null;
|
|
3530
3574
|
if (kind && !coalesceLiteralTypes.has(name))
|
|
3531
3575
|
coalesceLiteralTypes.set(name, kind);
|
|
3532
3576
|
}
|
|
3533
3577
|
}
|
|
3534
|
-
|
|
3578
|
+
ts23.forEachChild(n, visit);
|
|
3535
3579
|
};
|
|
3536
3580
|
visit(sf);
|
|
3537
3581
|
};
|
|
@@ -3642,33 +3686,33 @@ function augmentInheritedPropAccesses(ir) {
|
|
|
3642
3686
|
}
|
|
3643
3687
|
}
|
|
3644
3688
|
function parseStaticStringConst(source) {
|
|
3645
|
-
const sf =
|
|
3689
|
+
const sf = ts23.createSourceFile("__const.ts", `const __x = (${source});`, ts23.ScriptTarget.Latest, false);
|
|
3646
3690
|
const stmt = sf.statements[0];
|
|
3647
|
-
if (!stmt || !
|
|
3691
|
+
if (!stmt || !ts23.isVariableStatement(stmt))
|
|
3648
3692
|
return null;
|
|
3649
3693
|
let init = stmt.declarationList.declarations[0]?.initializer;
|
|
3650
|
-
while (init &&
|
|
3694
|
+
while (init && ts23.isParenthesizedExpression(init))
|
|
3651
3695
|
init = init.expression;
|
|
3652
3696
|
if (!init)
|
|
3653
3697
|
return null;
|
|
3654
|
-
if (
|
|
3698
|
+
if (ts23.isStringLiteral(init) || ts23.isNoSubstitutionTemplateLiteral(init)) {
|
|
3655
3699
|
return init.text;
|
|
3656
3700
|
}
|
|
3657
3701
|
return evalStringArrayJoin(source);
|
|
3658
3702
|
}
|
|
3659
3703
|
function evalTemplateOfStringConsts(source, resolved) {
|
|
3660
|
-
const sf =
|
|
3704
|
+
const sf = ts23.createSourceFile("__const.ts", `const __x = (${source});`, ts23.ScriptTarget.Latest, false);
|
|
3661
3705
|
const stmt = sf.statements[0];
|
|
3662
|
-
if (!stmt || !
|
|
3706
|
+
if (!stmt || !ts23.isVariableStatement(stmt))
|
|
3663
3707
|
return null;
|
|
3664
3708
|
let init = stmt.declarationList.declarations[0]?.initializer;
|
|
3665
|
-
while (init &&
|
|
3709
|
+
while (init && ts23.isParenthesizedExpression(init))
|
|
3666
3710
|
init = init.expression;
|
|
3667
|
-
if (!init || !
|
|
3711
|
+
if (!init || !ts23.isTemplateExpression(init))
|
|
3668
3712
|
return null;
|
|
3669
3713
|
let out = init.head.text;
|
|
3670
3714
|
for (const span of init.templateSpans) {
|
|
3671
|
-
if (!
|
|
3715
|
+
if (!ts23.isIdentifier(span.expression))
|
|
3672
3716
|
return null;
|
|
3673
3717
|
const value = resolved.get(span.expression.text);
|
|
3674
3718
|
if (value === undefined)
|
|
@@ -3695,34 +3739,36 @@ function collectModuleStringConsts(constants) {
|
|
|
3695
3739
|
}
|
|
3696
3740
|
return map;
|
|
3697
3741
|
}
|
|
3698
|
-
function lookupStaticRecordLiteral(objectName, key, constants) {
|
|
3742
|
+
function lookupStaticRecordLiteral(objectName, key, constants, isShadowed) {
|
|
3743
|
+
if (isShadowed(objectName))
|
|
3744
|
+
return null;
|
|
3699
3745
|
const constInfo = (constants ?? []).find((c) => c.name === objectName && c.isModule);
|
|
3700
3746
|
if (constInfo?.value === undefined)
|
|
3701
3747
|
return null;
|
|
3702
|
-
const sf =
|
|
3748
|
+
const sf = ts23.createSourceFile("__rec.ts", `(${constInfo.value})`, ts23.ScriptTarget.Latest, true);
|
|
3703
3749
|
if (sf.statements.length !== 1)
|
|
3704
3750
|
return null;
|
|
3705
3751
|
const stmt = sf.statements[0];
|
|
3706
|
-
if (!
|
|
3752
|
+
if (!ts23.isExpressionStatement(stmt))
|
|
3707
3753
|
return null;
|
|
3708
3754
|
let parsed = stmt.expression;
|
|
3709
|
-
while (
|
|
3755
|
+
while (ts23.isParenthesizedExpression(parsed))
|
|
3710
3756
|
parsed = parsed.expression;
|
|
3711
|
-
if (!
|
|
3757
|
+
if (!ts23.isObjectLiteralExpression(parsed))
|
|
3712
3758
|
return null;
|
|
3713
3759
|
for (const prop of parsed.properties) {
|
|
3714
|
-
if (!
|
|
3760
|
+
if (!ts23.isPropertyAssignment(prop))
|
|
3715
3761
|
continue;
|
|
3716
3762
|
const name = prop.name;
|
|
3717
|
-
const propKey =
|
|
3763
|
+
const propKey = ts23.isIdentifier(name) || ts23.isStringLiteral(name) || ts23.isNoSubstitutionTemplateLiteral(name) ? name.text : null;
|
|
3718
3764
|
if (propKey !== key)
|
|
3719
3765
|
continue;
|
|
3720
3766
|
let v = prop.initializer;
|
|
3721
|
-
while (
|
|
3767
|
+
while (ts23.isParenthesizedExpression(v))
|
|
3722
3768
|
v = v.expression;
|
|
3723
|
-
if (
|
|
3769
|
+
if (ts23.isNumericLiteral(v))
|
|
3724
3770
|
return { kind: "number", text: v.text };
|
|
3725
|
-
if (
|
|
3771
|
+
if (ts23.isStringLiteral(v) || ts23.isNoSubstitutionTemplateLiteral(v)) {
|
|
3726
3772
|
return { kind: "string", text: v.text };
|
|
3727
3773
|
}
|
|
3728
3774
|
return null;
|
|
@@ -3730,28 +3776,28 @@ function lookupStaticRecordLiteral(objectName, key, constants) {
|
|
|
3730
3776
|
return null;
|
|
3731
3777
|
}
|
|
3732
3778
|
function evalStringArrayJoin(source) {
|
|
3733
|
-
const sf =
|
|
3779
|
+
const sf = ts23.createSourceFile("__join.ts", `const __x = (${source});`, ts23.ScriptTarget.Latest, false);
|
|
3734
3780
|
const stmt = sf.statements[0];
|
|
3735
|
-
if (!stmt || !
|
|
3781
|
+
if (!stmt || !ts23.isVariableStatement(stmt))
|
|
3736
3782
|
return null;
|
|
3737
3783
|
let node = stmt.declarationList.declarations[0]?.initializer;
|
|
3738
|
-
while (node &&
|
|
3784
|
+
while (node && ts23.isParenthesizedExpression(node))
|
|
3739
3785
|
node = node.expression;
|
|
3740
|
-
if (!node || !
|
|
3786
|
+
if (!node || !ts23.isCallExpression(node))
|
|
3741
3787
|
return null;
|
|
3742
3788
|
const callee = node.expression;
|
|
3743
|
-
if (!
|
|
3789
|
+
if (!ts23.isPropertyAccessExpression(callee))
|
|
3744
3790
|
return null;
|
|
3745
3791
|
if (callee.name.text !== "join")
|
|
3746
3792
|
return null;
|
|
3747
3793
|
let recv = callee.expression;
|
|
3748
|
-
while (
|
|
3794
|
+
while (ts23.isParenthesizedExpression(recv))
|
|
3749
3795
|
recv = recv.expression;
|
|
3750
|
-
if (!
|
|
3796
|
+
if (!ts23.isArrayLiteralExpression(recv))
|
|
3751
3797
|
return null;
|
|
3752
3798
|
const parts = [];
|
|
3753
3799
|
for (const el of recv.elements) {
|
|
3754
|
-
if (
|
|
3800
|
+
if (ts23.isStringLiteral(el) || ts23.isNoSubstitutionTemplateLiteral(el)) {
|
|
3755
3801
|
parts.push(el.text);
|
|
3756
3802
|
} else {
|
|
3757
3803
|
return null;
|
|
@@ -3760,7 +3806,7 @@ function evalStringArrayJoin(source) {
|
|
|
3760
3806
|
let sep = ",";
|
|
3761
3807
|
if (node.arguments.length >= 1) {
|
|
3762
3808
|
const arg = node.arguments[0];
|
|
3763
|
-
if (
|
|
3809
|
+
if (ts23.isStringLiteral(arg) || ts23.isNoSubstitutionTemplateLiteral(arg))
|
|
3764
3810
|
sep = arg.text;
|
|
3765
3811
|
else
|
|
3766
3812
|
return null;
|
|
@@ -3768,11 +3814,11 @@ function evalStringArrayJoin(source) {
|
|
|
3768
3814
|
return parts.join(sep);
|
|
3769
3815
|
}
|
|
3770
3816
|
function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
|
|
3771
|
-
if (!
|
|
3817
|
+
if (!ts23.isElementAccessExpression(val))
|
|
3772
3818
|
return null;
|
|
3773
3819
|
const obj = val.expression;
|
|
3774
3820
|
const arg = val.argumentExpression;
|
|
3775
|
-
if (!
|
|
3821
|
+
if (!ts23.isIdentifier(obj) || !ts23.isIdentifier(arg))
|
|
3776
3822
|
return null;
|
|
3777
3823
|
let indexPropName;
|
|
3778
3824
|
let defaultKey;
|
|
@@ -3788,35 +3834,35 @@ function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
|
|
|
3788
3834
|
const constInfo = localConstants.find((c) => c.name === obj.text && c.isModule);
|
|
3789
3835
|
if (constInfo?.value === undefined)
|
|
3790
3836
|
return null;
|
|
3791
|
-
const sf =
|
|
3837
|
+
const sf = ts23.createSourceFile("__rec.ts", `(${constInfo.value})`, ts23.ScriptTarget.Latest, true);
|
|
3792
3838
|
if (sf.statements.length !== 1)
|
|
3793
3839
|
return null;
|
|
3794
3840
|
const stmt = sf.statements[0];
|
|
3795
|
-
if (!
|
|
3841
|
+
if (!ts23.isExpressionStatement(stmt))
|
|
3796
3842
|
return null;
|
|
3797
3843
|
let parsed = stmt.expression;
|
|
3798
|
-
while (
|
|
3844
|
+
while (ts23.isParenthesizedExpression(parsed))
|
|
3799
3845
|
parsed = parsed.expression;
|
|
3800
|
-
if (!
|
|
3846
|
+
if (!ts23.isObjectLiteralExpression(parsed))
|
|
3801
3847
|
return null;
|
|
3802
3848
|
const entries = [];
|
|
3803
3849
|
for (const prop of parsed.properties) {
|
|
3804
|
-
if (!
|
|
3850
|
+
if (!ts23.isPropertyAssignment(prop))
|
|
3805
3851
|
return null;
|
|
3806
3852
|
let key;
|
|
3807
|
-
if (
|
|
3853
|
+
if (ts23.isIdentifier(prop.name)) {
|
|
3808
3854
|
key = prop.name.text;
|
|
3809
|
-
} else if (
|
|
3855
|
+
} else if (ts23.isStringLiteral(prop.name) || ts23.isNoSubstitutionTemplateLiteral(prop.name)) {
|
|
3810
3856
|
key = prop.name.text;
|
|
3811
3857
|
} else {
|
|
3812
3858
|
return null;
|
|
3813
3859
|
}
|
|
3814
3860
|
let v = prop.initializer;
|
|
3815
|
-
while (
|
|
3861
|
+
while (ts23.isParenthesizedExpression(v))
|
|
3816
3862
|
v = v.expression;
|
|
3817
|
-
if (
|
|
3863
|
+
if (ts23.isNumericLiteral(v)) {
|
|
3818
3864
|
entries.push({ key, value: { kind: "number", text: v.text } });
|
|
3819
|
-
} else if (
|
|
3865
|
+
} else if (ts23.isStringLiteral(v) || ts23.isNoSubstitutionTemplateLiteral(v)) {
|
|
3820
3866
|
entries.push({ key, value: { kind: "string", text: v.text } });
|
|
3821
3867
|
} else {
|
|
3822
3868
|
return null;
|
|
@@ -3872,7 +3918,7 @@ function computeSsrSeedPlan(metadata) {
|
|
|
3872
3918
|
// ../jsx/src/rich-type-refusal.ts
|
|
3873
3919
|
var EMPTY_BINDINGS2 = new Map;
|
|
3874
3920
|
// ../jsx/src/shared-program.ts
|
|
3875
|
-
import
|
|
3921
|
+
import ts25 from "typescript";
|
|
3876
3922
|
// ../jsx/src/adapters/interface.ts
|
|
3877
3923
|
class BaseAdapter {
|
|
3878
3924
|
renderChildren(children) {
|
|
@@ -3925,7 +3971,7 @@ class JsxAdapter extends BaseAdapter {
|
|
|
3925
3971
|
...localFunctions.map((f) => ({ name: f.name, body: f.body })),
|
|
3926
3972
|
...localConstants.map((c) => ({ name: c.name, body: c.value }))
|
|
3927
3973
|
];
|
|
3928
|
-
const reachable =
|
|
3974
|
+
const reachable = closeOverWritersOfMutableBindings(primaryRefText, declarations, new Set(ir.metadata.localConstants.filter((c) => (c.declarationKind ?? "const") !== "const").map((c) => c.name)));
|
|
3929
3975
|
const reachableBodies = [...reachable].map((name) => {
|
|
3930
3976
|
const func = localFunctions.find((f) => f.name === name);
|
|
3931
3977
|
if (func)
|
|
@@ -3957,7 +4003,8 @@ class JsxAdapter extends BaseAdapter {
|
|
|
3957
4003
|
if (signal.setter) {
|
|
3958
4004
|
const setterUsed = identifierPattern(signal.setter).test(setterRefText);
|
|
3959
4005
|
if (setterUsed) {
|
|
3960
|
-
|
|
4006
|
+
const setterType = preserveTypes && signal.type.kind !== "unknown" ? `(valueOrFn: ${signal.type.raw} | ((prev: ${signal.type.raw}) => ${signal.type.raw})) => void` : null;
|
|
4007
|
+
lines.push(setterType ? ` const ${signal.setter}: ${setterType} = () => {}` : ` const ${signal.setter} = (..._args: any[]) => {}`);
|
|
3961
4008
|
}
|
|
3962
4009
|
}
|
|
3963
4010
|
}
|
|
@@ -4153,7 +4200,7 @@ class JsxAdapter extends BaseAdapter {
|
|
|
4153
4200
|
}
|
|
4154
4201
|
|
|
4155
4202
|
// ../jsx/src/adapters/template-imports.ts
|
|
4156
|
-
import
|
|
4203
|
+
import ts26 from "typescript";
|
|
4157
4204
|
var CLIENT_PACKAGE_SOURCES = new Set([
|
|
4158
4205
|
"@barefootjs/client",
|
|
4159
4206
|
"@barefootjs/client/runtime"
|
|
@@ -4292,7 +4339,8 @@ export default ${this.componentName}` : "";
|
|
|
4292
4339
|
const noArgDefault = hasRequiredProps ? "" : ` = {} as ${propsTypeExpr}`;
|
|
4293
4340
|
const lines = [];
|
|
4294
4341
|
const exportPrefix = ir.metadata.isExported === false ? "" : "export ";
|
|
4295
|
-
|
|
4342
|
+
const typeParameters = ir.metadata.typeParameters ?? "";
|
|
4343
|
+
lines.push(`${exportPrefix}function ${name}${typeParameters}(${fullPropsDestructure}${typeAnnotation}${noArgDefault}) {`);
|
|
4296
4344
|
if (hasClientInteractivity) {
|
|
4297
4345
|
lines.push(` const __scopeId = (/_s\\d/.test(__bfScope || '') ? __bfScope : null) || __instanceId || \`${name}_\${Math.random().toString(36).slice(2, 8)}\``);
|
|
4298
4346
|
} else {
|
|
@@ -4827,7 +4875,7 @@ function dangerousInnerHtmlDiagnostic(expr, loc, reason) {
|
|
|
4827
4875
|
};
|
|
4828
4876
|
}
|
|
4829
4877
|
// ../jsx/src/combine-client-js.ts
|
|
4830
|
-
import
|
|
4878
|
+
import ts27 from "typescript";
|
|
4831
4879
|
// ../jsx/src/loop-destructure.ts
|
|
4832
4880
|
function isLowerableLoopDestructure(loop) {
|
|
4833
4881
|
const bindings = loop.paramBindings;
|
|
@@ -4967,9 +5015,9 @@ function escapeRe(s) {
|
|
|
4967
5015
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4968
5016
|
}
|
|
4969
5017
|
// ../jsx/src/debug.ts
|
|
4970
|
-
import ts27 from "typescript";
|
|
4971
|
-
// ../jsx/src/profiler.ts
|
|
4972
5018
|
import ts28 from "typescript";
|
|
5019
|
+
// ../jsx/src/profiler.ts
|
|
5020
|
+
import ts29 from "typescript";
|
|
4973
5021
|
|
|
4974
5022
|
// ../jsx/src/index.ts
|
|
4975
5023
|
registerBuiltinLoweringPlugins();
|
|
@@ -5840,7 +5888,7 @@ function collectImportedLoopChildComponentErrors(ir, componentName) {
|
|
|
5840
5888
|
}
|
|
5841
5889
|
|
|
5842
5890
|
// src/adapter/spread/spread-codegen.ts
|
|
5843
|
-
import
|
|
5891
|
+
import ts30 from "typescript";
|
|
5844
5892
|
function conditionalSpreadToRuby(ctx, expr) {
|
|
5845
5893
|
if (!expr || expr.kind !== "conditional")
|
|
5846
5894
|
return null;
|
|
@@ -5895,7 +5943,7 @@ function recordIndexAccessToRuby(ctx, val) {
|
|
|
5895
5943
|
if (val.kind !== "index-access" || val.object.kind !== "identifier" || val.index.kind !== "identifier") {
|
|
5896
5944
|
return null;
|
|
5897
5945
|
}
|
|
5898
|
-
const tsVal =
|
|
5946
|
+
const tsVal = ts30.factory.createElementAccessExpression(ts30.factory.createIdentifier(val.object.name), ts30.factory.createIdentifier(val.index.name));
|
|
5899
5947
|
const parsed = parseRecordIndexAccess(tsVal, ctx.localConstants, ctx.propsParams);
|
|
5900
5948
|
if (!parsed)
|
|
5901
5949
|
return null;
|
|
@@ -6008,7 +6056,7 @@ class ErbAdapter extends BaseAdapter {
|
|
|
6008
6056
|
_loweringMatchers = [];
|
|
6009
6057
|
moduleStringConsts = new Map;
|
|
6010
6058
|
localConstants = [];
|
|
6011
|
-
|
|
6059
|
+
scope = BindingScope.EMPTY;
|
|
6012
6060
|
nullableOptionalProps = new Set;
|
|
6013
6061
|
constructor(options = {}) {
|
|
6014
6062
|
super();
|
|
@@ -6030,7 +6078,7 @@ class ErbAdapter extends BaseAdapter {
|
|
|
6030
6078
|
this._searchParamsLocals = searchParamsLocalNames(ir.metadata);
|
|
6031
6079
|
this._loweringMatchers = prepareLoweringMatchers(ir.metadata);
|
|
6032
6080
|
this.localConstants = ir.metadata.localConstants ?? [];
|
|
6033
|
-
this.
|
|
6081
|
+
this.scope = BindingScope.EMPTY;
|
|
6034
6082
|
this.errors = [];
|
|
6035
6083
|
this.childrenCaptureCounter = 0;
|
|
6036
6084
|
if (!options?.siblingTemplatesRegistered) {
|
|
@@ -6088,7 +6136,7 @@ class ErbAdapter extends BaseAdapter {
|
|
|
6088
6136
|
};
|
|
6089
6137
|
}
|
|
6090
6138
|
resolveLiteralConst(name) {
|
|
6091
|
-
if (this.
|
|
6139
|
+
if (this.scope.isBound(name))
|
|
6092
6140
|
return null;
|
|
6093
6141
|
const c = (this.localConstants ?? []).find((lc) => lc.name === name);
|
|
6094
6142
|
if (c?.value === undefined)
|
|
@@ -6102,15 +6150,13 @@ class ErbAdapter extends BaseAdapter {
|
|
|
6102
6150
|
return null;
|
|
6103
6151
|
}
|
|
6104
6152
|
resolveStaticRecordLiteral(objectName, key) {
|
|
6105
|
-
|
|
6106
|
-
return null;
|
|
6107
|
-
const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants);
|
|
6153
|
+
const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants, (name) => this.scope.isBound(name));
|
|
6108
6154
|
if (!hit)
|
|
6109
6155
|
return null;
|
|
6110
6156
|
return hit.kind === "number" ? hit.text : rubyStringLiteral(hit.text);
|
|
6111
6157
|
}
|
|
6112
6158
|
resolveModuleStringConst(name) {
|
|
6113
|
-
if (this.
|
|
6159
|
+
if (this.scope.isBound(name))
|
|
6114
6160
|
return null;
|
|
6115
6161
|
const value = this.moduleStringConsts.get(name);
|
|
6116
6162
|
if (value === undefined)
|
|
@@ -6118,7 +6164,7 @@ class ErbAdapter extends BaseAdapter {
|
|
|
6118
6164
|
return rubyStringLiteral(value);
|
|
6119
6165
|
}
|
|
6120
6166
|
isLoopBoundName(name) {
|
|
6121
|
-
return this.
|
|
6167
|
+
return this.scope.isBound(name);
|
|
6122
6168
|
}
|
|
6123
6169
|
generateScriptRegistrations(ir, scriptBaseName, scriptAssets, preloadAssets) {
|
|
6124
6170
|
if (scriptAssets) {
|
|
@@ -6395,12 +6441,12 @@ ${whenTrue}
|
|
|
6395
6441
|
});
|
|
6396
6442
|
}
|
|
6397
6443
|
const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
|
|
6398
|
-
isNameShadowed:
|
|
6444
|
+
isNameShadowed: this.scope.asShadowPredicate()
|
|
6399
6445
|
});
|
|
6400
6446
|
const staticArray = staticItems !== null ? staticValueToRuby(staticItems) : null;
|
|
6401
6447
|
if (staticArray === null && loop.arrayParsed?.kind === "identifier") {
|
|
6402
6448
|
const arrayName = loop.arrayParsed.name;
|
|
6403
|
-
const isUnresolvableLocalConst = !this.
|
|
6449
|
+
const isUnresolvableLocalConst = !this.scope.isBound(arrayName) && this.resolveModuleStringConst(arrayName) === null && this.resolveLiteralConst(arrayName) === null && this.localConstants.some((c) => c.name === arrayName && !c.isModule);
|
|
6404
6450
|
if (isUnresolvableLocalConst) {
|
|
6405
6451
|
this._recordExprBF101(`Loop array \`${arrayName}\` is a component-scope const computed from a runtime expression the ERB adapter cannot evaluate at SSR render time.`, `Options:
|
|
6406
6452
|
1. Inline the array expression directly in the .map() call instead of a preceding const.
|
|
@@ -6417,13 +6463,9 @@ ${whenTrue}
|
|
|
6417
6463
|
}
|
|
6418
6464
|
const param = loop.param;
|
|
6419
6465
|
const indexVar = loop.iterationShape === "keys" ? rubyLocal(param) : rubyLocal(loop.index ?? "_i");
|
|
6420
|
-
const loopBound = loop.objectIteration === "entries" ? [param, loop.index ?? "_k"] : loop.objectIteration === "keys" || loop.objectIteration === "values" || loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
6421
6466
|
const preambleDecls = loop.preamble?.declarations ?? [];
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
for (const n of loopBound) {
|
|
6425
|
-
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
|
|
6426
|
-
}
|
|
6467
|
+
const prevScope = this.scope;
|
|
6468
|
+
this.scope = prevScope.enterLoopRow(loop);
|
|
6427
6469
|
const prevLoopKeyDepth = this.currentLoopKeyDepth;
|
|
6428
6470
|
this.currentLoopKeyDepth = loop.depth;
|
|
6429
6471
|
const renderedChildren = this.renderChildren(loop.children);
|
|
@@ -6433,13 +6475,7 @@ ${renderedChildren}` : renderedChildren;
|
|
|
6433
6475
|
const lines = [];
|
|
6434
6476
|
lines.push(`<%= bf.comment("loop:${loop.markerId}") %>`);
|
|
6435
6477
|
if (sortedHoist && loop.sortComparator) {
|
|
6436
|
-
|
|
6437
|
-
const c = (this.loopBoundNames.get(n) ?? 1) - 1;
|
|
6438
|
-
if (c <= 0)
|
|
6439
|
-
this.loopBoundNames.delete(n);
|
|
6440
|
-
else
|
|
6441
|
-
this.loopBoundNames.set(n, c);
|
|
6442
|
-
}
|
|
6478
|
+
this.scope = prevScope;
|
|
6443
6479
|
const sortEmit = (e) => this.convertExpressionToRuby("", e);
|
|
6444
6480
|
const sortArrow = loop.sortComparator.arrow;
|
|
6445
6481
|
let sorted = null;
|
|
@@ -6455,9 +6491,7 @@ ${renderedChildren}` : renderedChildren;
|
|
|
6455
6491
|
this._recordExprBF101(`.sort(...) loop comparator is not lowerable to a template sort`, `Pre-sort the array in the route handler, or mark the loop @client-only.`);
|
|
6456
6492
|
sorted = rawArray;
|
|
6457
6493
|
}
|
|
6458
|
-
|
|
6459
|
-
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
|
|
6460
|
-
}
|
|
6494
|
+
this.scope = prevScope.enterLoopRow(loop);
|
|
6461
6495
|
lines.push(`<%- ${sortedHoist} = ${sorted} -%>`);
|
|
6462
6496
|
}
|
|
6463
6497
|
if (loop.objectIteration) {
|
|
@@ -6503,13 +6537,7 @@ ${renderedChildren}` : renderedChildren;
|
|
|
6503
6537
|
lines.push(...preambleLines);
|
|
6504
6538
|
lines.push(children);
|
|
6505
6539
|
}
|
|
6506
|
-
|
|
6507
|
-
const c = (this.loopBoundNames.get(n) ?? 1) - 1;
|
|
6508
|
-
if (c <= 0)
|
|
6509
|
-
this.loopBoundNames.delete(n);
|
|
6510
|
-
else
|
|
6511
|
-
this.loopBoundNames.set(n, c);
|
|
6512
|
-
}
|
|
6540
|
+
this.scope = prevScope;
|
|
6513
6541
|
lines.push(`<%- end -%>`);
|
|
6514
6542
|
lines.push(`<%= bf.comment("/loop:${loop.markerId}") %>`);
|
|
6515
6543
|
return lines.join(`
|
|
@@ -6671,7 +6699,7 @@ ${children}`;
|
|
|
6671
6699
|
if (ternaryHash !== null) {
|
|
6672
6700
|
return `<%= bf.spread_attrs(${ternaryHash}) %>`;
|
|
6673
6701
|
}
|
|
6674
|
-
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.
|
|
6702
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.scope.isBound(trimmed)) {
|
|
6675
6703
|
const localConst = this.localConstants.find((c) => c.name === trimmed && !c.isModule);
|
|
6676
6704
|
if (localConst?.value !== undefined) {
|
|
6677
6705
|
const initTrimmed = localConst.value.trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/erb",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.5",
|
|
4
4
|
"description": "ERB (Embedded Ruby) adapter for BarefootJS — compiles IR to .erb templates and ships the Ruby rendering backend; runs under any Rack app (Sinatra, Rails)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"directory": "packages/adapter-erb"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@barefootjs/shared": "0.31.
|
|
57
|
+
"@barefootjs/shared": "0.31.5"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"@barefootjs/jsx": ">=0.2.0",
|
|
@@ -71,9 +71,9 @@
|
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@barefootjs/adapter-tests": "0.1.0",
|
|
74
|
-
"@barefootjs/jsx": "0.31.
|
|
75
|
-
"@barefootjs/vite": "0.31.
|
|
76
|
-
"@barefootjs/client": "0.31.
|
|
74
|
+
"@barefootjs/jsx": "0.31.5",
|
|
75
|
+
"@barefootjs/vite": "0.31.5",
|
|
76
|
+
"@barefootjs/client": "0.31.5",
|
|
77
77
|
"typescript": "^5.0.0",
|
|
78
78
|
"vite": "^6.0.0"
|
|
79
79
|
}
|
|
@@ -94,6 +94,7 @@ import {
|
|
|
94
94
|
dangerousInnerHtmlDiagnostic,
|
|
95
95
|
resolveStaticLoopSource,
|
|
96
96
|
derivesScopeFromSlot,
|
|
97
|
+
BindingScope,
|
|
97
98
|
} from '@barefootjs/jsx'
|
|
98
99
|
import { isAriaBooleanAttr, isBooleanResultExpr, isExplicitStringCall } from './boolean-result.ts'
|
|
99
100
|
import type { ParsedExpr, LoweringMatcher, LoopBindingPathSegment } from '@barefootjs/jsx'
|
|
@@ -248,17 +249,23 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
|
|
|
248
249
|
*/
|
|
249
250
|
private localConstants: IRMetadata['localConstants'] = []
|
|
250
251
|
/**
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
* for TWO things in the ERB adapter
|
|
255
|
-
* only used it to guard
|
|
256
|
-
* fundamental `v[:name]` vs
|
|
257
|
-
* `ErbTopLevelEmitter.identifier` —
|
|
258
|
-
* `isLoopBoundName` docstring for why ERB's
|
|
259
|
-
* where Perl's uniform `$name` sigil does
|
|
252
|
+
* The one canonical, position-accurate "names bound by an enclosing loop
|
|
253
|
+
* callback" service (#2482 Stage 2) — replaces the ref-counted
|
|
254
|
+
* `Map<string, number>` this adapter used to push/pop around
|
|
255
|
+
* `renderLoop`'s body. Load-bearing for TWO things in the ERB adapter
|
|
256
|
+
* (more than the Mojo original, which only used it to guard
|
|
257
|
+
* const-inlining): it also decides the fundamental `v[:name]` vs
|
|
258
|
+
* bare-Ruby-local rendering choice in `ErbTopLevelEmitter.identifier` —
|
|
259
|
+
* see `emit-context.ts`'s `isLoopBoundName` docstring for why ERB's
|
|
260
|
+
* two-locals model needs this where Perl's uniform `$name` sigil does
|
|
261
|
+
* not. Threaded via save/restore-by-reference around
|
|
262
|
+
* `renderChildren(loop.children)` in `renderLoop` (immutable — no
|
|
263
|
+
* ref-count bookkeeping), mirroring the Stage 1a/1b `ctx.scope`
|
|
264
|
+
* precedent in `jsx-to-ir.ts`. `IRLoop` already structurally satisfies
|
|
265
|
+
* `LoopBindingSource` (`param`/`index`/`paramBindings`/`preamble`), so
|
|
266
|
+
* `renderLoop` passes the loop node straight to `enterLoopRow`.
|
|
260
267
|
*/
|
|
261
|
-
private
|
|
268
|
+
private scope: BindingScope = BindingScope.EMPTY
|
|
262
269
|
/**
|
|
263
270
|
* Prop names whose value is `nil` in the template body when the caller
|
|
264
271
|
* omits them — so a bare-reference attribute should be dropped rather
|
|
@@ -298,7 +305,7 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
|
|
|
298
305
|
this._searchParamsLocals = searchParamsLocalNames(ir.metadata)
|
|
299
306
|
this._loweringMatchers = prepareLoweringMatchers(ir.metadata)
|
|
300
307
|
this.localConstants = ir.metadata.localConstants ?? []
|
|
301
|
-
this.
|
|
308
|
+
this.scope = BindingScope.EMPTY
|
|
302
309
|
this.errors = []
|
|
303
310
|
this.childrenCaptureCounter = 0
|
|
304
311
|
|
|
@@ -440,7 +447,7 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
|
|
|
440
447
|
* would read nil.
|
|
441
448
|
*/
|
|
442
449
|
private resolveLiteralConst(name: string): string | null {
|
|
443
|
-
if (this.
|
|
450
|
+
if (this.scope.isBound(name)) return null
|
|
444
451
|
const c = (this.localConstants ?? []).find(lc => lc.name === name)
|
|
445
452
|
if (c?.value === undefined) return null
|
|
446
453
|
const v = c.value.trim()
|
|
@@ -451,8 +458,7 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
|
|
|
451
458
|
}
|
|
452
459
|
|
|
453
460
|
private resolveStaticRecordLiteral(objectName: string, key: string): string | null {
|
|
454
|
-
|
|
455
|
-
const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants)
|
|
461
|
+
const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants, name => this.scope.isBound(name))
|
|
456
462
|
if (!hit) return null
|
|
457
463
|
return hit.kind === 'number' ? hit.text : rubyStringLiteral(hit.text)
|
|
458
464
|
}
|
|
@@ -460,7 +466,7 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
|
|
|
460
466
|
private resolveModuleStringConst(name: string): string | null {
|
|
461
467
|
// A loop body introduces block-param bindings that shadow a module
|
|
462
468
|
// const of the same name — never inline inside one.
|
|
463
|
-
if (this.
|
|
469
|
+
if (this.scope.isBound(name)) return null
|
|
464
470
|
const value = this.moduleStringConsts.get(name)
|
|
465
471
|
if (value === undefined) return null
|
|
466
472
|
return rubyStringLiteral(value)
|
|
@@ -469,7 +475,7 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
|
|
|
469
475
|
/** Whether `name` currently names a loop-bound Ruby local. See
|
|
470
476
|
* `ErbEmitContext.isLoopBoundName`'s docstring. */
|
|
471
477
|
private isLoopBoundName(name: string): boolean {
|
|
472
|
-
return this.
|
|
478
|
+
return this.scope.isBound(name)
|
|
473
479
|
}
|
|
474
480
|
|
|
475
481
|
// ===========================================================================
|
|
@@ -957,15 +963,17 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
|
|
|
957
963
|
// `unsupported` object-literal path (BF101, "Expression not
|
|
958
964
|
// supported"), which is what this loop-specific check now also does
|
|
959
965
|
// deliberately, up front, for both shapes.
|
|
966
|
+
// Canonical, position-accurate predicate (#2482 Stage 2) — the
|
|
967
|
+
// enclosing loop scope's own membership.
|
|
960
968
|
const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
|
|
961
|
-
isNameShadowed:
|
|
969
|
+
isNameShadowed: this.scope.asShadowPredicate(),
|
|
962
970
|
})
|
|
963
971
|
const staticArray = staticItems !== null ? staticValueToRuby(staticItems) : null
|
|
964
972
|
|
|
965
973
|
if (staticArray === null && loop.arrayParsed?.kind === 'identifier') {
|
|
966
974
|
const arrayName = loop.arrayParsed.name
|
|
967
975
|
const isUnresolvableLocalConst =
|
|
968
|
-
!this.
|
|
976
|
+
!this.scope.isBound(arrayName) &&
|
|
969
977
|
this.resolveModuleStringConst(arrayName) === null &&
|
|
970
978
|
this.resolveLiteralConst(arrayName) === null &&
|
|
971
979
|
this.localConstants.some(c => c.name === arrayName && !c.isModule)
|
|
@@ -994,29 +1002,22 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
|
|
|
994
1002
|
const indexVar = loop.iterationShape === 'keys'
|
|
995
1003
|
? rubyLocal(param)
|
|
996
1004
|
: rubyLocal(loop.index ?? '_i')
|
|
997
|
-
//
|
|
998
|
-
//
|
|
999
|
-
//
|
|
1000
|
-
//
|
|
1001
|
-
//
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
//
|
|
1010
|
-
// declaration (#2447). The names must be loop-bound for the whole body,
|
|
1011
|
-
// or `ErbTopLevelEmitter.identifier` renders each read as `v[:cls]` —
|
|
1012
|
-
// a vars-Hash key nothing ever seeds, i.e. the empty attribute this
|
|
1013
|
-
// fixes. Phase 1 guarantees `declarations` is present or the loop was
|
|
1014
|
-
// already refused, so there is no partial-lowering case here.
|
|
1005
|
+
// This loop's row scope. Guards module-const inlining (and, in ERB, the
|
|
1006
|
+
// fundamental v[:name]-vs-local rendering choice) for the whole body
|
|
1007
|
+
// (children + key + filter) so a same-named loop variable isn't
|
|
1008
|
+
// replaced by the const literal / a vars-Hash read. `IRLoop` already
|
|
1009
|
+
// structurally satisfies `LoopBindingSource`
|
|
1010
|
+
// (`param`/`index`/`paramBindings`/`preamble`) — `enterLoopRow(loop)`
|
|
1011
|
+
// binds exactly what this renderLoop's header + preamble locals
|
|
1012
|
+
// introduce (#2482 Stage 2 — replaces the ref-counted `Map` this
|
|
1013
|
+
// adapter used to carry). Restored by reference (immutable — no
|
|
1014
|
+
// ref-count bookkeeping) at the matching pop below, once the WHOLE
|
|
1015
|
+
// body (including the filter predicate) has been rendered — except for
|
|
1016
|
+
// one temporary drop-to-outer window around the hoisted sort-comparator
|
|
1017
|
+
// emission below, since that runs OUTSIDE this loop.
|
|
1015
1018
|
const preambleDecls = loop.preamble?.declarations ?? []
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1)
|
|
1019
|
-
}
|
|
1019
|
+
const prevScope = this.scope
|
|
1020
|
+
this.scope = prevScope.enterLoopRow(loop)
|
|
1020
1021
|
const prevLoopKeyDepth = this.currentLoopKeyDepth
|
|
1021
1022
|
this.currentLoopKeyDepth = loop.depth
|
|
1022
1023
|
const renderedChildren = this.renderChildren(loop.children)
|
|
@@ -1042,18 +1043,16 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
|
|
|
1042
1043
|
// fall back to the structured `bf.sort` for a comparator the
|
|
1043
1044
|
// evaluator can't model (e.g. `localeCompare`).
|
|
1044
1045
|
//
|
|
1045
|
-
// The hoisted sort runs OUTSIDE this loop, so this loop's
|
|
1046
|
+
// The hoisted sort runs OUTSIDE this loop, so this loop's row scope
|
|
1046
1047
|
// must not shadow the comparator's captured free vars while emitting
|
|
1047
1048
|
// the env — otherwise a captured var that happens to share a
|
|
1048
1049
|
// loop-param name is blocked from inlining its module const / from
|
|
1049
1050
|
// reading `v[:name]` and instead resolves to the (out-of-scope) loop
|
|
1050
|
-
// local. Drop
|
|
1051
|
-
// restore (a nested loop's outer
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
else this.loopBoundNames.set(n, c)
|
|
1056
|
-
}
|
|
1051
|
+
// local. Drop back to the outer (pre-row) scope for the sort emit,
|
|
1052
|
+
// then restore the row scope below (a nested loop's own outer
|
|
1053
|
+
// bindings stay in effect throughout, since `prevScope` already
|
|
1054
|
+
// carries them).
|
|
1055
|
+
this.scope = prevScope
|
|
1057
1056
|
const sortEmit = (e: ParsedExpr) => this.convertExpressionToRuby('', e)
|
|
1058
1057
|
const sortArrow = loop.sortComparator.arrow
|
|
1059
1058
|
let sorted: string | null = null
|
|
@@ -1074,9 +1073,7 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
|
|
|
1074
1073
|
)
|
|
1075
1074
|
sorted = rawArray
|
|
1076
1075
|
}
|
|
1077
|
-
|
|
1078
|
-
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1)
|
|
1079
|
-
}
|
|
1076
|
+
this.scope = prevScope.enterLoopRow(loop)
|
|
1080
1077
|
lines.push(`<%- ${sortedHoist} = ${sorted} -%>`)
|
|
1081
1078
|
}
|
|
1082
1079
|
if (loop.objectIteration) {
|
|
@@ -1184,12 +1181,8 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
|
|
|
1184
1181
|
lines.push(children)
|
|
1185
1182
|
}
|
|
1186
1183
|
|
|
1187
|
-
// Body fully rendered —
|
|
1188
|
-
|
|
1189
|
-
const c = (this.loopBoundNames.get(n) ?? 1) - 1
|
|
1190
|
-
if (c <= 0) this.loopBoundNames.delete(n)
|
|
1191
|
-
else this.loopBoundNames.set(n, c)
|
|
1192
|
-
}
|
|
1184
|
+
// Body fully rendered — restore the outer (pre-row) scope.
|
|
1185
|
+
this.scope = prevScope
|
|
1193
1186
|
|
|
1194
1187
|
lines.push(`<%- end -%>`)
|
|
1195
1188
|
lines.push(`<%= bf.comment("/loop:${loop.markerId}") %>`)
|
|
@@ -1558,11 +1551,11 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
|
|
|
1558
1551
|
// function-scope (`!isModule`) consts whose value is NOT itself a
|
|
1559
1552
|
// bare identifier (loop guard) are considered.
|
|
1560
1553
|
//
|
|
1561
|
-
// `
|
|
1562
|
-
// param can shadow this outer const's name (`.map((attrs) => <p
|
|
1554
|
+
// `this.scope` shadow guard (#2489): an enclosing `.map()` callback's
|
|
1555
|
+
// own param can shadow this outer const's name (`.map((attrs) => <p
|
|
1563
1556
|
// {...attrs} />)`) — without the guard this forwarded the OUTER
|
|
1564
1557
|
// const's value at every iteration instead of the per-item value.
|
|
1565
|
-
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.
|
|
1558
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.scope.isBound(trimmed)) {
|
|
1566
1559
|
const localConst = this.localConstants.find(
|
|
1567
1560
|
c => c.name === trimmed && !c.isModule,
|
|
1568
1561
|
)
|
|
@@ -352,11 +352,11 @@ export class ErbTopLevelEmitter implements ParsedExprEmitter {
|
|
|
352
352
|
if (name === 'undefined' || name === 'null') return 'nil'
|
|
353
353
|
// A loop-bound name (this identifier resolves to a bare Ruby local
|
|
354
354
|
// introduced by an enclosing loop, not a vars-Hash entry) takes
|
|
355
|
-
// priority over const inlining — mirrors the Mojo adapter's
|
|
356
|
-
//
|
|
357
|
-
//
|
|
358
|
-
//
|
|
359
|
-
//
|
|
355
|
+
// priority over const inlining — mirrors the Mojo adapter's threaded
|
|
356
|
+
// scope shadow guard, but here it ALSO decides the fundamental
|
|
357
|
+
// v[:name]-vs-bare-local rendering, not just the const fast path
|
|
358
|
+
// (ERB's two-locals variable model needs this distinction; Perl's
|
|
359
|
+
// uniform `$name` sigil does not — see `ErbEmitContext`).
|
|
360
360
|
if (this.ctx.isLoopBoundName(name)) return rubyLocal(name)
|
|
361
361
|
// Module pure-string const (e.g. `const baseClasses = '...'` used in a
|
|
362
362
|
// className template literal): inline the literal value rather than
|