@barefootjs/jinja 0.31.1 → 0.31.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -4
- package/dist/render-divergences.d.ts.map +1 -1
- package/dist/vite.js +415 -100
- package/package.json +5 -5
- package/python/barefootjs/runtime.py +21 -6
- package/src/__tests__/jinja-reserved-word-child-prop.test.ts +58 -0
- package/src/render-divergences.ts +0 -16
- package/src/test-render.ts +74 -33
package/dist/index.js
CHANGED
|
@@ -189275,10 +189275,7 @@ var conformancePins = {
|
|
|
189275
189275
|
"date-method-uncatalogued": [{ code: "BF021", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2356" }]
|
|
189276
189276
|
};
|
|
189277
189277
|
// src/render-divergences.ts
|
|
189278
|
-
var renderDivergences = {
|
|
189279
|
-
"aliased-destructured-prop": "aliased destructured prop `{ n: count }` loses its rename — template vars, ssr-defaults, and the props bridge all key off the local name, so the prop is always undefined (https://github.com/piconic-ai/barefootjs/issues/2524)",
|
|
189280
|
-
"composite-row-child-aliased-prop": "same defect as `aliased-destructured-prop` (#2524), inside a keyed `.map()` loop row: the nested child's renamed prop (`{ n: count }`) is always undefined, so both rows render an empty count (https://github.com/piconic-ai/barefootjs/issues/2524)"
|
|
189281
|
-
};
|
|
189278
|
+
var renderDivergences = {};
|
|
189282
189279
|
export {
|
|
189283
189280
|
renderDivergences,
|
|
189284
189281
|
jinjaAdapter,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,iBAM/B,CAAA"}
|
package/dist/vite.js
CHANGED
|
@@ -4,8 +4,11 @@ import { dirname, resolve } from "node:path";
|
|
|
4
4
|
import { barefoot as coreBarefoot } from "@barefootjs/vite";
|
|
5
5
|
import { devModuleUrl, loadManifest, resolveDevOrigin, resolveScriptAssets, toPosixRelative } from "@barefootjs/vite";
|
|
6
6
|
|
|
7
|
+
// ../jsx/src/compiler.ts
|
|
8
|
+
import ts23 from "typescript";
|
|
9
|
+
|
|
7
10
|
// ../jsx/src/analyzer.ts
|
|
8
|
-
import
|
|
11
|
+
import ts9 from "typescript";
|
|
9
12
|
|
|
10
13
|
// ../jsx/src/expression-parser.ts
|
|
11
14
|
import ts from "typescript";
|
|
@@ -1974,6 +1977,29 @@ import ts5 from "typescript";
|
|
|
1974
1977
|
// ../jsx/src/ir-to-client-js/utils.ts
|
|
1975
1978
|
import ts3 from "typescript";
|
|
1976
1979
|
|
|
1980
|
+
// ../jsx/src/template-parts.ts
|
|
1981
|
+
function lookupPartToJsExpr(part, opts) {
|
|
1982
|
+
const key = opts?.useTemplate && part.templateKey ? part.templateKey : part.key;
|
|
1983
|
+
const obj = "{" + Object.entries(part.cases).map(([k, v]) => `${JSON.stringify(k)}: ${JSON.stringify(v)}`).join(", ") + "}";
|
|
1984
|
+
const typed = opts?.typed ? " as Record<string, string>" : "";
|
|
1985
|
+
return `(${obj}${typed})[${key}]`;
|
|
1986
|
+
}
|
|
1987
|
+
function templatePartsToJsExpr(parts, opts) {
|
|
1988
|
+
let result = "`";
|
|
1989
|
+
for (const part of parts) {
|
|
1990
|
+
if (part.type === "string") {
|
|
1991
|
+
result += opts?.useTemplate && part.templateValue ? part.templateValue : part.value;
|
|
1992
|
+
} else if (part.type === "ternary") {
|
|
1993
|
+
const cond = opts?.useTemplate && part.templateCondition ? part.templateCondition : part.condition;
|
|
1994
|
+
result += `\${${cond} ? '${part.whenTrue}' : '${part.whenFalse}'}`;
|
|
1995
|
+
} else if (part.type === "lookup") {
|
|
1996
|
+
result += `\${${lookupPartToJsExpr(part, opts)}}`;
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
result += "`";
|
|
2000
|
+
return result;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
1977
2003
|
// ../jsx/src/scanner/js-scanner.ts
|
|
1978
2004
|
import ts2 from "typescript";
|
|
1979
2005
|
|
|
@@ -2075,6 +2101,16 @@ function escapeHtml(text) {
|
|
|
2075
2101
|
}
|
|
2076
2102
|
// ../jsx/src/ir-to-client-js/csr-substitute.ts
|
|
2077
2103
|
import ts4 from "typescript";
|
|
2104
|
+
function extractFreeIdentifiersFromText(text) {
|
|
2105
|
+
if (!text || text.trim().length === 0)
|
|
2106
|
+
return new Set;
|
|
2107
|
+
const sf = ts4.createSourceFile("__free_ids__.ts", `(${text});`, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TS);
|
|
2108
|
+
const stmt = sf.statements[0];
|
|
2109
|
+
if (!stmt || !ts4.isExpressionStatement(stmt))
|
|
2110
|
+
return new Set;
|
|
2111
|
+
const expr = ts4.isParenthesizedExpression(stmt.expression) ? stmt.expression.expression : stmt.expression;
|
|
2112
|
+
return extractFreeIdentifiersFromNode(expr);
|
|
2113
|
+
}
|
|
2078
2114
|
|
|
2079
2115
|
// ../jsx/src/adapters/child-scope.ts
|
|
2080
2116
|
function derivesScopeFromSlot(comp) {
|
|
@@ -2125,6 +2161,27 @@ var SKELETON_PATH_FORCE_CLOSE_GROUPS = [
|
|
|
2125
2161
|
new Set(["li"])
|
|
2126
2162
|
];
|
|
2127
2163
|
|
|
2164
|
+
// ../jsx/src/props-binding.ts
|
|
2165
|
+
import ts6 from "typescript";
|
|
2166
|
+
function isIdentifierName(key) {
|
|
2167
|
+
if (key.length === 0)
|
|
2168
|
+
return false;
|
|
2169
|
+
for (let i = 0;i < key.length; ) {
|
|
2170
|
+
const cp = key.codePointAt(i);
|
|
2171
|
+
const ok = i === 0 ? ts6.isIdentifierStart(cp, ts6.ScriptTarget.Latest) : ts6.isIdentifierPart(cp, ts6.ScriptTarget.Latest);
|
|
2172
|
+
if (!ok)
|
|
2173
|
+
return false;
|
|
2174
|
+
i += cp > 65535 ? 2 : 1;
|
|
2175
|
+
}
|
|
2176
|
+
return true;
|
|
2177
|
+
}
|
|
2178
|
+
function propsDestructureBinding(p) {
|
|
2179
|
+
const callerKey = p.sourceName ?? p.name;
|
|
2180
|
+
const localName = p.name;
|
|
2181
|
+
const binding = callerKey === localName ? localName : `${isIdentifierName(callerKey) ? callerKey : JSON.stringify(callerKey)}: ${localName}`;
|
|
2182
|
+
return p.defaultValue ? `${binding} = ${p.defaultValue}` : binding;
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2128
2185
|
// ../jsx/src/instrumentation.ts
|
|
2129
2186
|
var _counters = freshCounters();
|
|
2130
2187
|
function freshCounters() {
|
|
@@ -2138,14 +2195,14 @@ function freshCounters() {
|
|
|
2138
2195
|
}
|
|
2139
2196
|
|
|
2140
2197
|
// ../jsx/src/analyzer-context.ts
|
|
2141
|
-
import
|
|
2198
|
+
import ts8 from "typescript";
|
|
2142
2199
|
|
|
2143
2200
|
// ../jsx/src/strip-types.ts
|
|
2144
|
-
import
|
|
2201
|
+
import ts7 from "typescript";
|
|
2145
2202
|
|
|
2146
2203
|
// ../jsx/src/analyzer-context.ts
|
|
2147
|
-
var _typePrinter =
|
|
2148
|
-
var _blankTypeSourceFile =
|
|
2204
|
+
var _typePrinter = ts8.createPrinter({ removeComments: true, omitTrailingSemicolon: true });
|
|
2205
|
+
var _blankTypeSourceFile = ts8.createSourceFile("__bf_types__.ts", "", ts8.ScriptTarget.Latest);
|
|
2149
2206
|
|
|
2150
2207
|
// ../jsx/src/errors.ts
|
|
2151
2208
|
var ErrorCodes = {
|
|
@@ -2161,6 +2218,7 @@ var ErrorCodes = {
|
|
|
2161
2218
|
JSX_IN_LOCAL_FUNCTION: "BF045",
|
|
2162
2219
|
COMPONENT_REQUIRED_PROP_MISSING: "BF046",
|
|
2163
2220
|
JSX_BRANCH_LOCAL_IN_CALLBACK: "BF047",
|
|
2221
|
+
SIBLING_COMPONENT_NOT_COMPILED: "BF048",
|
|
2164
2222
|
SHARED_PROGRAM_REQUIRED: "BF050",
|
|
2165
2223
|
WRONG_PACKAGE_IMPORT: "BF051",
|
|
2166
2224
|
BUILTIN_REQUIRES_IMPORT: "BF054",
|
|
@@ -2190,6 +2248,7 @@ var errorMessages = {
|
|
|
2190
2248
|
[ErrorCodes.JSX_IN_LOCAL_FUNCTION]: "Local function returns JSX but cannot be inlined. Extract it as a top-level PascalCase component or use a single return statement.",
|
|
2191
2249
|
[ErrorCodes.COMPONENT_REQUIRED_PROP_MISSING]: "Built-in component is missing a required prop.",
|
|
2192
2250
|
[ErrorCodes.JSX_BRANCH_LOCAL_IN_CALLBACK]: "JSX-typed local declared inside an `if`-block cannot be referenced from a callback body (ref / event handler). " + "Render it as a child instead: `<div ref={...}>{local}</div>`.",
|
|
2251
|
+
[ErrorCodes.SIBLING_COMPONENT_NOT_COMPILED]: "Referenced component did not compile to a template, so this reference would throw " + "`ReferenceError` at render time. Multi-return JSX dispatch (a `switch` or `if`/`else` " + "chain across multiple JSX-returning branches) cannot compile as a component in a " + `'use client' file. Extract it to a separate non-"use client" file (where it is preserved ` + "verbatim, see #932), or rewrite it as a single-return ternary/conditional chain so the " + "component pipeline can compile it.",
|
|
2193
2252
|
[ErrorCodes.SHARED_PROGRAM_REQUIRED]: "Shared ts.Program required for type-based reactivity classification. This source imports a Reactive<T>-branded library (e.g. @barefootjs/form) whose getters cannot be classified by regex alone. Pass `options.program` (built via `createProgramForCorpus`) so the analyzer can resolve the brand through the TypeChecker.",
|
|
2194
2253
|
[ErrorCodes.WRONG_PACKAGE_IMPORT]: "Import from wrong package.",
|
|
2195
2254
|
[ErrorCodes.BUILTIN_REQUIRES_IMPORT]: "Built-in <Async> / <Region> must be imported from '@barefootjs/client'. " + "The compiler recognises these tags by their import (not by tag name), " + "so an unimported tag with this name is treated as an undeclared component.",
|
|
@@ -2363,6 +2422,54 @@ var CLIENT_EXPORTS = new Set([
|
|
|
2363
2422
|
"Async",
|
|
2364
2423
|
"Region"
|
|
2365
2424
|
]);
|
|
2425
|
+
function extractFreeIdentifiersFromNode(node) {
|
|
2426
|
+
const ids = new Set;
|
|
2427
|
+
const boundNames = new Set;
|
|
2428
|
+
function addBindingNames(name, out) {
|
|
2429
|
+
if (ts9.isIdentifier(name))
|
|
2430
|
+
out.push(name.text);
|
|
2431
|
+
else if (ts9.isObjectBindingPattern(name))
|
|
2432
|
+
name.elements.forEach((e) => addBindingNames(e.name, out));
|
|
2433
|
+
else if (ts9.isArrayBindingPattern(name))
|
|
2434
|
+
name.elements.forEach((e) => {
|
|
2435
|
+
if (!ts9.isOmittedExpression(e))
|
|
2436
|
+
addBindingNames(e.name, out);
|
|
2437
|
+
});
|
|
2438
|
+
}
|
|
2439
|
+
function visit(n) {
|
|
2440
|
+
if (ts9.isTypeNode(n))
|
|
2441
|
+
return;
|
|
2442
|
+
if (ts9.isIdentifier(n)) {
|
|
2443
|
+
const parent = n.parent;
|
|
2444
|
+
if (parent && ts9.isPropertyAccessExpression(parent) && parent.name === n)
|
|
2445
|
+
return;
|
|
2446
|
+
if (parent && ts9.isPropertyAssignment(parent) && parent.name === n)
|
|
2447
|
+
return;
|
|
2448
|
+
if (parent && ts9.isParameter(parent) && parent.name === n)
|
|
2449
|
+
return;
|
|
2450
|
+
if (parent && ts9.isVariableDeclaration(parent) && parent.name === n)
|
|
2451
|
+
return;
|
|
2452
|
+
if (boundNames.has(n.text))
|
|
2453
|
+
return;
|
|
2454
|
+
ids.add(n.text);
|
|
2455
|
+
return;
|
|
2456
|
+
}
|
|
2457
|
+
if (ts9.isArrowFunction(n)) {
|
|
2458
|
+
const params = [];
|
|
2459
|
+
for (const p of n.parameters)
|
|
2460
|
+
addBindingNames(p.name, params);
|
|
2461
|
+
for (const name of params)
|
|
2462
|
+
boundNames.add(name);
|
|
2463
|
+
ts9.forEachChild(n, visit);
|
|
2464
|
+
for (const name of params)
|
|
2465
|
+
boundNames.delete(name);
|
|
2466
|
+
return;
|
|
2467
|
+
}
|
|
2468
|
+
ts9.forEachChild(n, visit);
|
|
2469
|
+
}
|
|
2470
|
+
visit(node);
|
|
2471
|
+
return ids;
|
|
2472
|
+
}
|
|
2366
2473
|
var BROWSER_ONLY_CLIENT_APIS = new Set([
|
|
2367
2474
|
"useContext",
|
|
2368
2475
|
"provideContext",
|
|
@@ -2381,7 +2488,7 @@ var REACTIVE_PRIMITIVES = new Set([
|
|
|
2381
2488
|
]);
|
|
2382
2489
|
|
|
2383
2490
|
// ../jsx/src/jsx-to-ir.ts
|
|
2384
|
-
import
|
|
2491
|
+
import ts12 from "typescript";
|
|
2385
2492
|
|
|
2386
2493
|
// ../jsx/src/types.ts
|
|
2387
2494
|
var SCOPE_FORBIDDEN = {
|
|
@@ -2463,10 +2570,10 @@ function findReachableNames(primaryRefs, declarations) {
|
|
|
2463
2570
|
}
|
|
2464
2571
|
|
|
2465
2572
|
// ../jsx/src/reactivity-checker.ts
|
|
2466
|
-
import
|
|
2573
|
+
import ts10 from "typescript";
|
|
2467
2574
|
|
|
2468
2575
|
// ../jsx/src/free-refs.ts
|
|
2469
|
-
import
|
|
2576
|
+
import ts11 from "typescript";
|
|
2470
2577
|
var _bindingMapCache = new WeakMap;
|
|
2471
2578
|
|
|
2472
2579
|
// ../jsx/src/to-locale-date-lowering.ts
|
|
@@ -2784,6 +2891,83 @@ var toLocaleDatePlugin = {
|
|
|
2784
2891
|
}
|
|
2785
2892
|
};
|
|
2786
2893
|
|
|
2894
|
+
// ../jsx/src/scope/binding-scope.ts
|
|
2895
|
+
class BindingScope {
|
|
2896
|
+
frames;
|
|
2897
|
+
static EMPTY = new BindingScope([]);
|
|
2898
|
+
constructor(frames) {
|
|
2899
|
+
this.frames = frames;
|
|
2900
|
+
}
|
|
2901
|
+
enterLoopRow(loop) {
|
|
2902
|
+
const bindings = new Map;
|
|
2903
|
+
if (loop.paramBindings && loop.paramBindings.length > 0) {
|
|
2904
|
+
for (const b of loop.paramBindings)
|
|
2905
|
+
bindings.set(b.name, { source: "destructure" });
|
|
2906
|
+
} else {
|
|
2907
|
+
bindings.set(loop.param, { source: "item" });
|
|
2908
|
+
}
|
|
2909
|
+
if (loop.index != null)
|
|
2910
|
+
bindings.set(loop.index, { source: "index" });
|
|
2911
|
+
for (const name of loop.preamble?.declaredNames ?? [])
|
|
2912
|
+
bindings.set(name, { source: "preamble" });
|
|
2913
|
+
const frame = { kind: "loop-row", bindings };
|
|
2914
|
+
return new BindingScope([frame, ...this.frames]);
|
|
2915
|
+
}
|
|
2916
|
+
enterCallback(params) {
|
|
2917
|
+
const bindings = new Map;
|
|
2918
|
+
for (const name of params)
|
|
2919
|
+
bindings.set(name, { source: "param" });
|
|
2920
|
+
const frame = { kind: "callback", bindings };
|
|
2921
|
+
return new BindingScope([frame, ...this.frames]);
|
|
2922
|
+
}
|
|
2923
|
+
isBound(name) {
|
|
2924
|
+
for (const frame of this.frames) {
|
|
2925
|
+
if (frame.bindings.has(name))
|
|
2926
|
+
return true;
|
|
2927
|
+
}
|
|
2928
|
+
return false;
|
|
2929
|
+
}
|
|
2930
|
+
lookup(name) {
|
|
2931
|
+
for (let depth = 0;depth < this.frames.length; depth++) {
|
|
2932
|
+
const frame = this.frames[depth];
|
|
2933
|
+
const binding = frame.bindings.get(name);
|
|
2934
|
+
if (binding)
|
|
2935
|
+
return { depth, frame, binding };
|
|
2936
|
+
}
|
|
2937
|
+
return null;
|
|
2938
|
+
}
|
|
2939
|
+
boundNames() {
|
|
2940
|
+
if (this.boundNamesCache)
|
|
2941
|
+
return this.boundNamesCache;
|
|
2942
|
+
const names = new Set;
|
|
2943
|
+
for (const frame of this.frames) {
|
|
2944
|
+
for (const name of frame.bindings.keys())
|
|
2945
|
+
names.add(name);
|
|
2946
|
+
}
|
|
2947
|
+
this.boundNamesCache = names;
|
|
2948
|
+
return names;
|
|
2949
|
+
}
|
|
2950
|
+
boundNamesCache;
|
|
2951
|
+
valueBoundNamesCache;
|
|
2952
|
+
valueBoundNames() {
|
|
2953
|
+
if (this.valueBoundNamesCache)
|
|
2954
|
+
return this.valueBoundNamesCache;
|
|
2955
|
+
const names = new Set;
|
|
2956
|
+
for (const frame of this.frames) {
|
|
2957
|
+
for (const [name, binding] of frame.bindings) {
|
|
2958
|
+
if (binding.source === "item" || binding.source === "index" || binding.source === "destructure") {
|
|
2959
|
+
names.add(name);
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
this.valueBoundNamesCache = names;
|
|
2964
|
+
return names;
|
|
2965
|
+
}
|
|
2966
|
+
asShadowPredicate() {
|
|
2967
|
+
return (name) => this.isBound(name);
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
|
|
2787
2971
|
// ../jsx/src/jsx-to-ir.ts
|
|
2788
2972
|
var EMPTY_BOUND = new Set;
|
|
2789
2973
|
var constInitializerCache = new WeakMap;
|
|
@@ -2880,13 +3064,13 @@ var KEYWORDS_AND_GLOBALS = new Set([
|
|
|
2880
3064
|
]);
|
|
2881
3065
|
|
|
2882
3066
|
// ../jsx/src/ir-to-client-js/walk-prop-accesses.ts
|
|
2883
|
-
import
|
|
3067
|
+
import ts13 from "typescript";
|
|
2884
3068
|
|
|
2885
3069
|
// ../jsx/src/value-references.ts
|
|
2886
|
-
import
|
|
3070
|
+
import ts14 from "typescript";
|
|
2887
3071
|
|
|
2888
3072
|
// ../jsx/src/relocate.ts
|
|
2889
|
-
import
|
|
3073
|
+
import ts15 from "typescript";
|
|
2890
3074
|
|
|
2891
3075
|
// ../jsx/src/lowering-registry.ts
|
|
2892
3076
|
var plugins = [];
|
|
@@ -3103,10 +3287,10 @@ function matchSearchParamsMethodCall(callee, args, localNames) {
|
|
|
3103
3287
|
}
|
|
3104
3288
|
|
|
3105
3289
|
// ../jsx/src/ir-to-client-js/prune-unused-prop-extractions.ts
|
|
3106
|
-
import
|
|
3290
|
+
import ts16 from "typescript";
|
|
3107
3291
|
|
|
3108
3292
|
// ../jsx/src/ir-to-client-js/control-flow/plan/lazy-preamble.ts
|
|
3109
|
-
import
|
|
3293
|
+
import ts17 from "typescript";
|
|
3110
3294
|
var NO_PREAMBLE = {
|
|
3111
3295
|
lazySafe: true,
|
|
3112
3296
|
facts: { declaredNames: new Set, freeNames: new Set }
|
|
@@ -3156,7 +3340,7 @@ var INERT_BINDING_GLOBALS = new Set([
|
|
|
3156
3340
|
]);
|
|
3157
3341
|
|
|
3158
3342
|
// ../jsx/src/ir-to-client-js/emit-reactive.ts
|
|
3159
|
-
import
|
|
3343
|
+
import ts18 from "typescript";
|
|
3160
3344
|
|
|
3161
3345
|
// ../jsx/src/ir-to-client-js/control-flow/stringify/event-delegation.ts
|
|
3162
3346
|
var NON_BUBBLING_EVENTS = new Set([
|
|
@@ -3171,7 +3355,7 @@ var NON_BUBBLING_EVENTS = new Set([
|
|
|
3171
3355
|
]);
|
|
3172
3356
|
|
|
3173
3357
|
// ../jsx/src/ir-to-client-js/rewrite-props-object.ts
|
|
3174
|
-
import
|
|
3358
|
+
import ts19 from "typescript";
|
|
3175
3359
|
|
|
3176
3360
|
// ../jsx/src/ir-to-client-js/source-map.ts
|
|
3177
3361
|
var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
@@ -3262,15 +3446,15 @@ class SourceMapGenerator {
|
|
|
3262
3446
|
}
|
|
3263
3447
|
|
|
3264
3448
|
// ../jsx/src/preprocess-inline-jsx-callbacks.ts
|
|
3265
|
-
import
|
|
3449
|
+
import ts20 from "typescript";
|
|
3266
3450
|
|
|
3267
3451
|
// ../jsx/src/ssr-defaults.ts
|
|
3268
|
-
import
|
|
3452
|
+
import ts21 from "typescript";
|
|
3269
3453
|
var UNRESOLVED = Symbol("unresolved");
|
|
3270
3454
|
var NO_RETURN = Symbol("no-return");
|
|
3271
3455
|
|
|
3272
3456
|
// ../jsx/src/augment-inherited-props.ts
|
|
3273
|
-
import
|
|
3457
|
+
import ts22 from "typescript";
|
|
3274
3458
|
function collectContextConsumers(metadata) {
|
|
3275
3459
|
const constants = metadata.localConstants ?? [];
|
|
3276
3460
|
const contextDefaults = new Map;
|
|
@@ -3302,47 +3486,47 @@ function collectContextConsumers(metadata) {
|
|
|
3302
3486
|
}
|
|
3303
3487
|
function parseUseContextArg(source) {
|
|
3304
3488
|
const expr = parseSingleExpression(source);
|
|
3305
|
-
if (!expr || !
|
|
3489
|
+
if (!expr || !ts22.isCallExpression(expr))
|
|
3306
3490
|
return null;
|
|
3307
|
-
if (!
|
|
3491
|
+
if (!ts22.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
|
|
3308
3492
|
return null;
|
|
3309
3493
|
if (expr.arguments.length !== 1)
|
|
3310
3494
|
return null;
|
|
3311
3495
|
const arg = expr.arguments[0];
|
|
3312
|
-
return
|
|
3496
|
+
return ts22.isIdentifier(arg) ? arg.text : null;
|
|
3313
3497
|
}
|
|
3314
3498
|
function parseCreateContextDefault(source) {
|
|
3315
3499
|
const expr = parseSingleExpression(source);
|
|
3316
|
-
if (!expr || !
|
|
3500
|
+
if (!expr || !ts22.isCallExpression(expr))
|
|
3317
3501
|
return null;
|
|
3318
3502
|
if (expr.arguments.length === 0)
|
|
3319
3503
|
return null;
|
|
3320
3504
|
const arg = expr.arguments[0];
|
|
3321
|
-
if (
|
|
3505
|
+
if (ts22.isStringLiteral(arg) || ts22.isNoSubstitutionTemplateLiteral(arg))
|
|
3322
3506
|
return arg.text;
|
|
3323
|
-
if (
|
|
3507
|
+
if (ts22.isNumericLiteral(arg))
|
|
3324
3508
|
return Number(arg.text);
|
|
3325
|
-
if (arg.kind ===
|
|
3509
|
+
if (arg.kind === ts22.SyntaxKind.TrueKeyword)
|
|
3326
3510
|
return true;
|
|
3327
|
-
if (arg.kind ===
|
|
3511
|
+
if (arg.kind === ts22.SyntaxKind.FalseKeyword)
|
|
3328
3512
|
return false;
|
|
3329
3513
|
return null;
|
|
3330
3514
|
}
|
|
3331
3515
|
function isObjectLiteralCreateContextDefault(source) {
|
|
3332
3516
|
const expr = parseSingleExpression(source);
|
|
3333
|
-
if (!expr || !
|
|
3517
|
+
if (!expr || !ts22.isCallExpression(expr))
|
|
3334
3518
|
return false;
|
|
3335
3519
|
if (expr.arguments.length === 0)
|
|
3336
3520
|
return false;
|
|
3337
|
-
return
|
|
3521
|
+
return ts22.isObjectLiteralExpression(expr.arguments[0]);
|
|
3338
3522
|
}
|
|
3339
3523
|
function parseSingleExpression(source) {
|
|
3340
|
-
const sf =
|
|
3524
|
+
const sf = ts22.createSourceFile("__ctx.ts", `(${source})`, ts22.ScriptTarget.Latest, false);
|
|
3341
3525
|
const stmt = sf.statements[0];
|
|
3342
|
-
if (!stmt || !
|
|
3526
|
+
if (!stmt || !ts22.isExpressionStatement(stmt))
|
|
3343
3527
|
return null;
|
|
3344
3528
|
let e = stmt.expression;
|
|
3345
|
-
while (
|
|
3529
|
+
while (ts22.isParenthesizedExpression(e))
|
|
3346
3530
|
e = e.expression;
|
|
3347
3531
|
return e;
|
|
3348
3532
|
}
|
|
@@ -3367,25 +3551,25 @@ function augmentInheritedPropAccesses(ir) {
|
|
|
3367
3551
|
const pinCoalesceLiterals = (s) => {
|
|
3368
3552
|
if (!s || !s.includes(propsObj))
|
|
3369
3553
|
return;
|
|
3370
|
-
const sf =
|
|
3554
|
+
const sf = ts22.createSourceFile("__aug.ts", `(${s})`, ts22.ScriptTarget.Latest, false);
|
|
3371
3555
|
const visit = (n) => {
|
|
3372
|
-
if (
|
|
3556
|
+
if (ts22.isBinaryExpression(n) && (n.operatorToken.kind === ts22.SyntaxKind.QuestionQuestionToken || n.operatorToken.kind === ts22.SyntaxKind.BarBarToken)) {
|
|
3373
3557
|
let left = n.left;
|
|
3374
|
-
while (
|
|
3558
|
+
while (ts22.isParenthesizedExpression(left))
|
|
3375
3559
|
left = left.expression;
|
|
3376
|
-
if (
|
|
3560
|
+
if (ts22.isPropertyAccessExpression(left) && ts22.isIdentifier(left.expression) && left.expression.text === propsObj) {
|
|
3377
3561
|
const name = left.name.text;
|
|
3378
3562
|
let right = n.right;
|
|
3379
|
-
while (
|
|
3563
|
+
while (ts22.isParenthesizedExpression(right))
|
|
3380
3564
|
right = right.expression;
|
|
3381
|
-
if (
|
|
3565
|
+
if (ts22.isPrefixUnaryExpression(right))
|
|
3382
3566
|
right = right.operand;
|
|
3383
|
-
const kind =
|
|
3567
|
+
const kind = ts22.isNumericLiteral(right) ? "number" : right.kind === ts22.SyntaxKind.TrueKeyword || right.kind === ts22.SyntaxKind.FalseKeyword ? "boolean" : ts22.isStringLiteralLike(right) ? "string" : null;
|
|
3384
3568
|
if (kind && !coalesceLiteralTypes.has(name))
|
|
3385
3569
|
coalesceLiteralTypes.set(name, kind);
|
|
3386
3570
|
}
|
|
3387
3571
|
}
|
|
3388
|
-
|
|
3572
|
+
ts22.forEachChild(n, visit);
|
|
3389
3573
|
};
|
|
3390
3574
|
visit(sf);
|
|
3391
3575
|
};
|
|
@@ -3496,33 +3680,33 @@ function augmentInheritedPropAccesses(ir) {
|
|
|
3496
3680
|
}
|
|
3497
3681
|
}
|
|
3498
3682
|
function parseStaticStringConst(source) {
|
|
3499
|
-
const sf =
|
|
3683
|
+
const sf = ts22.createSourceFile("__const.ts", `const __x = (${source});`, ts22.ScriptTarget.Latest, false);
|
|
3500
3684
|
const stmt = sf.statements[0];
|
|
3501
|
-
if (!stmt || !
|
|
3685
|
+
if (!stmt || !ts22.isVariableStatement(stmt))
|
|
3502
3686
|
return null;
|
|
3503
3687
|
let init = stmt.declarationList.declarations[0]?.initializer;
|
|
3504
|
-
while (init &&
|
|
3688
|
+
while (init && ts22.isParenthesizedExpression(init))
|
|
3505
3689
|
init = init.expression;
|
|
3506
3690
|
if (!init)
|
|
3507
3691
|
return null;
|
|
3508
|
-
if (
|
|
3692
|
+
if (ts22.isStringLiteral(init) || ts22.isNoSubstitutionTemplateLiteral(init)) {
|
|
3509
3693
|
return init.text;
|
|
3510
3694
|
}
|
|
3511
3695
|
return evalStringArrayJoin(source);
|
|
3512
3696
|
}
|
|
3513
3697
|
function evalTemplateOfStringConsts(source, resolved) {
|
|
3514
|
-
const sf =
|
|
3698
|
+
const sf = ts22.createSourceFile("__const.ts", `const __x = (${source});`, ts22.ScriptTarget.Latest, false);
|
|
3515
3699
|
const stmt = sf.statements[0];
|
|
3516
|
-
if (!stmt || !
|
|
3700
|
+
if (!stmt || !ts22.isVariableStatement(stmt))
|
|
3517
3701
|
return null;
|
|
3518
3702
|
let init = stmt.declarationList.declarations[0]?.initializer;
|
|
3519
|
-
while (init &&
|
|
3703
|
+
while (init && ts22.isParenthesizedExpression(init))
|
|
3520
3704
|
init = init.expression;
|
|
3521
|
-
if (!init || !
|
|
3705
|
+
if (!init || !ts22.isTemplateExpression(init))
|
|
3522
3706
|
return null;
|
|
3523
3707
|
let out = init.head.text;
|
|
3524
3708
|
for (const span of init.templateSpans) {
|
|
3525
|
-
if (!
|
|
3709
|
+
if (!ts22.isIdentifier(span.expression))
|
|
3526
3710
|
return null;
|
|
3527
3711
|
const value = resolved.get(span.expression.text);
|
|
3528
3712
|
if (value === undefined)
|
|
@@ -3553,30 +3737,30 @@ function lookupStaticRecordLiteral(objectName, key, constants) {
|
|
|
3553
3737
|
const constInfo = (constants ?? []).find((c) => c.name === objectName && c.isModule);
|
|
3554
3738
|
if (constInfo?.value === undefined)
|
|
3555
3739
|
return null;
|
|
3556
|
-
const sf =
|
|
3740
|
+
const sf = ts22.createSourceFile("__rec.ts", `(${constInfo.value})`, ts22.ScriptTarget.Latest, true);
|
|
3557
3741
|
if (sf.statements.length !== 1)
|
|
3558
3742
|
return null;
|
|
3559
3743
|
const stmt = sf.statements[0];
|
|
3560
|
-
if (!
|
|
3744
|
+
if (!ts22.isExpressionStatement(stmt))
|
|
3561
3745
|
return null;
|
|
3562
3746
|
let parsed = stmt.expression;
|
|
3563
|
-
while (
|
|
3747
|
+
while (ts22.isParenthesizedExpression(parsed))
|
|
3564
3748
|
parsed = parsed.expression;
|
|
3565
|
-
if (!
|
|
3749
|
+
if (!ts22.isObjectLiteralExpression(parsed))
|
|
3566
3750
|
return null;
|
|
3567
3751
|
for (const prop of parsed.properties) {
|
|
3568
|
-
if (!
|
|
3752
|
+
if (!ts22.isPropertyAssignment(prop))
|
|
3569
3753
|
continue;
|
|
3570
3754
|
const name = prop.name;
|
|
3571
|
-
const propKey =
|
|
3755
|
+
const propKey = ts22.isIdentifier(name) || ts22.isStringLiteral(name) || ts22.isNoSubstitutionTemplateLiteral(name) ? name.text : null;
|
|
3572
3756
|
if (propKey !== key)
|
|
3573
3757
|
continue;
|
|
3574
3758
|
let v = prop.initializer;
|
|
3575
|
-
while (
|
|
3759
|
+
while (ts22.isParenthesizedExpression(v))
|
|
3576
3760
|
v = v.expression;
|
|
3577
|
-
if (
|
|
3761
|
+
if (ts22.isNumericLiteral(v))
|
|
3578
3762
|
return { kind: "number", text: v.text };
|
|
3579
|
-
if (
|
|
3763
|
+
if (ts22.isStringLiteral(v) || ts22.isNoSubstitutionTemplateLiteral(v)) {
|
|
3580
3764
|
return { kind: "string", text: v.text };
|
|
3581
3765
|
}
|
|
3582
3766
|
return null;
|
|
@@ -3584,28 +3768,28 @@ function lookupStaticRecordLiteral(objectName, key, constants) {
|
|
|
3584
3768
|
return null;
|
|
3585
3769
|
}
|
|
3586
3770
|
function evalStringArrayJoin(source) {
|
|
3587
|
-
const sf =
|
|
3771
|
+
const sf = ts22.createSourceFile("__join.ts", `const __x = (${source});`, ts22.ScriptTarget.Latest, false);
|
|
3588
3772
|
const stmt = sf.statements[0];
|
|
3589
|
-
if (!stmt || !
|
|
3773
|
+
if (!stmt || !ts22.isVariableStatement(stmt))
|
|
3590
3774
|
return null;
|
|
3591
3775
|
let node = stmt.declarationList.declarations[0]?.initializer;
|
|
3592
|
-
while (node &&
|
|
3776
|
+
while (node && ts22.isParenthesizedExpression(node))
|
|
3593
3777
|
node = node.expression;
|
|
3594
|
-
if (!node || !
|
|
3778
|
+
if (!node || !ts22.isCallExpression(node))
|
|
3595
3779
|
return null;
|
|
3596
3780
|
const callee = node.expression;
|
|
3597
|
-
if (!
|
|
3781
|
+
if (!ts22.isPropertyAccessExpression(callee))
|
|
3598
3782
|
return null;
|
|
3599
3783
|
if (callee.name.text !== "join")
|
|
3600
3784
|
return null;
|
|
3601
3785
|
let recv = callee.expression;
|
|
3602
|
-
while (
|
|
3786
|
+
while (ts22.isParenthesizedExpression(recv))
|
|
3603
3787
|
recv = recv.expression;
|
|
3604
|
-
if (!
|
|
3788
|
+
if (!ts22.isArrayLiteralExpression(recv))
|
|
3605
3789
|
return null;
|
|
3606
3790
|
const parts = [];
|
|
3607
3791
|
for (const el of recv.elements) {
|
|
3608
|
-
if (
|
|
3792
|
+
if (ts22.isStringLiteral(el) || ts22.isNoSubstitutionTemplateLiteral(el)) {
|
|
3609
3793
|
parts.push(el.text);
|
|
3610
3794
|
} else {
|
|
3611
3795
|
return null;
|
|
@@ -3614,7 +3798,7 @@ function evalStringArrayJoin(source) {
|
|
|
3614
3798
|
let sep = ",";
|
|
3615
3799
|
if (node.arguments.length >= 1) {
|
|
3616
3800
|
const arg = node.arguments[0];
|
|
3617
|
-
if (
|
|
3801
|
+
if (ts22.isStringLiteral(arg) || ts22.isNoSubstitutionTemplateLiteral(arg))
|
|
3618
3802
|
sep = arg.text;
|
|
3619
3803
|
else
|
|
3620
3804
|
return null;
|
|
@@ -3622,11 +3806,11 @@ function evalStringArrayJoin(source) {
|
|
|
3622
3806
|
return parts.join(sep);
|
|
3623
3807
|
}
|
|
3624
3808
|
function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
|
|
3625
|
-
if (!
|
|
3809
|
+
if (!ts22.isElementAccessExpression(val))
|
|
3626
3810
|
return null;
|
|
3627
3811
|
const obj = val.expression;
|
|
3628
3812
|
const arg = val.argumentExpression;
|
|
3629
|
-
if (!
|
|
3813
|
+
if (!ts22.isIdentifier(obj) || !ts22.isIdentifier(arg))
|
|
3630
3814
|
return null;
|
|
3631
3815
|
let indexPropName;
|
|
3632
3816
|
let defaultKey;
|
|
@@ -3642,35 +3826,35 @@ function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
|
|
|
3642
3826
|
const constInfo = localConstants.find((c) => c.name === obj.text && c.isModule);
|
|
3643
3827
|
if (constInfo?.value === undefined)
|
|
3644
3828
|
return null;
|
|
3645
|
-
const sf =
|
|
3829
|
+
const sf = ts22.createSourceFile("__rec.ts", `(${constInfo.value})`, ts22.ScriptTarget.Latest, true);
|
|
3646
3830
|
if (sf.statements.length !== 1)
|
|
3647
3831
|
return null;
|
|
3648
3832
|
const stmt = sf.statements[0];
|
|
3649
|
-
if (!
|
|
3833
|
+
if (!ts22.isExpressionStatement(stmt))
|
|
3650
3834
|
return null;
|
|
3651
3835
|
let parsed = stmt.expression;
|
|
3652
|
-
while (
|
|
3836
|
+
while (ts22.isParenthesizedExpression(parsed))
|
|
3653
3837
|
parsed = parsed.expression;
|
|
3654
|
-
if (!
|
|
3838
|
+
if (!ts22.isObjectLiteralExpression(parsed))
|
|
3655
3839
|
return null;
|
|
3656
3840
|
const entries = [];
|
|
3657
3841
|
for (const prop of parsed.properties) {
|
|
3658
|
-
if (!
|
|
3842
|
+
if (!ts22.isPropertyAssignment(prop))
|
|
3659
3843
|
return null;
|
|
3660
3844
|
let key;
|
|
3661
|
-
if (
|
|
3845
|
+
if (ts22.isIdentifier(prop.name)) {
|
|
3662
3846
|
key = prop.name.text;
|
|
3663
|
-
} else if (
|
|
3847
|
+
} else if (ts22.isStringLiteral(prop.name) || ts22.isNoSubstitutionTemplateLiteral(prop.name)) {
|
|
3664
3848
|
key = prop.name.text;
|
|
3665
3849
|
} else {
|
|
3666
3850
|
return null;
|
|
3667
3851
|
}
|
|
3668
3852
|
let v = prop.initializer;
|
|
3669
|
-
while (
|
|
3853
|
+
while (ts22.isParenthesizedExpression(v))
|
|
3670
3854
|
v = v.expression;
|
|
3671
|
-
if (
|
|
3855
|
+
if (ts22.isNumericLiteral(v)) {
|
|
3672
3856
|
entries.push({ key, value: { kind: "number", text: v.text } });
|
|
3673
|
-
} else if (
|
|
3857
|
+
} else if (ts22.isStringLiteral(v) || ts22.isNoSubstitutionTemplateLiteral(v)) {
|
|
3674
3858
|
entries.push({ key, value: { kind: "string", text: v.text } });
|
|
3675
3859
|
} else {
|
|
3676
3860
|
return null;
|
|
@@ -3726,7 +3910,7 @@ function computeSsrSeedPlan(metadata) {
|
|
|
3726
3910
|
// ../jsx/src/rich-type-refusal.ts
|
|
3727
3911
|
var EMPTY_BINDINGS2 = new Map;
|
|
3728
3912
|
// ../jsx/src/shared-program.ts
|
|
3729
|
-
import
|
|
3913
|
+
import ts24 from "typescript";
|
|
3730
3914
|
// ../jsx/src/adapters/interface.ts
|
|
3731
3915
|
class BaseAdapter {
|
|
3732
3916
|
renderChildren(children) {
|
|
@@ -3802,7 +3986,7 @@ class JsxAdapter extends BaseAdapter {
|
|
|
3802
3986
|
}
|
|
3803
3987
|
const rawInitialValue = preserveTypes ? signal.typedInitialValue ?? signal.initialValue : signal.initialValue;
|
|
3804
3988
|
const initialValue = rawInitialValue.trim().startsWith("{") ? `(${rawInitialValue})` : rawInitialValue;
|
|
3805
|
-
const needsTypeAssertion = preserveTypes && !signal.typedInitialValue && signal.type.kind !== "unknown" && signal.type.kind !== "primitive";
|
|
3989
|
+
const needsTypeAssertion = preserveTypes && !signal.typedInitialValue && signal.type.kind !== "unknown" && signal.type.kind !== "primitive" && signal.type.raw !== "object";
|
|
3806
3990
|
if (needsTypeAssertion) {
|
|
3807
3991
|
lines.push(` const ${signal.getter} = () => ${initialValue} as ${signal.type.raw}`);
|
|
3808
3992
|
} else {
|
|
@@ -3821,12 +4005,16 @@ class JsxAdapter extends BaseAdapter {
|
|
|
3821
4005
|
const computation = preserveTypes ? memo.typedComputation ?? memo.computation : memo.computation;
|
|
3822
4006
|
lines.push(` const ${memo.name} = ${computation}`);
|
|
3823
4007
|
}
|
|
4008
|
+
const moduleScopeNames = this.moduleScopeDeclarationNames(ir);
|
|
3824
4009
|
for (const constant of ir.metadata.localConstants) {
|
|
3825
4010
|
if (constant.isExported)
|
|
3826
4011
|
continue;
|
|
4012
|
+
if (moduleScopeNames.has(constant.name))
|
|
4013
|
+
continue;
|
|
3827
4014
|
const keyword = constant.declarationKind ?? "const";
|
|
3828
4015
|
if (!constant.value) {
|
|
3829
|
-
|
|
4016
|
+
const typeAnnotation = preserveTypes && (constant.typeAnnotation ?? constant.type) ? `: ${constant.typeAnnotation ?? constant.type?.raw}` : "";
|
|
4017
|
+
lines.push(` ${keyword} ${constant.name}${typeAnnotation}`);
|
|
3830
4018
|
continue;
|
|
3831
4019
|
}
|
|
3832
4020
|
const value = constant.value.trim();
|
|
@@ -3835,9 +4023,12 @@ class JsxAdapter extends BaseAdapter {
|
|
|
3835
4023
|
if (!reachable.has(constant.name))
|
|
3836
4024
|
continue;
|
|
3837
4025
|
const constValue = preserveTypes ? constant.typedValue ?? constant.value : constant.value;
|
|
3838
|
-
|
|
4026
|
+
const letTypeAnnotation = preserveTypes && keyword === "let" && constant.typeAnnotation ? `: ${constant.typeAnnotation}` : "";
|
|
4027
|
+
lines.push(` ${keyword} ${constant.name}${letTypeAnnotation} = ${constValue}`);
|
|
3839
4028
|
}
|
|
3840
4029
|
for (const func of localFunctions) {
|
|
4030
|
+
if (moduleScopeNames.has(func.name))
|
|
4031
|
+
continue;
|
|
3841
4032
|
if (!reachable.has(func.name))
|
|
3842
4033
|
continue;
|
|
3843
4034
|
const params = preserveTypes && func.typedParams !== undefined ? func.typedParams : func.params.map(formatParamWithType).join(", ");
|
|
@@ -3847,6 +4038,127 @@ class JsxAdapter extends BaseAdapter {
|
|
|
3847
4038
|
lines.push(` ${asyncKw}function ${func.name}(${params})${returnAnnotation} ${body}`);
|
|
3848
4039
|
}
|
|
3849
4040
|
return lines.join(`
|
|
4041
|
+
`);
|
|
4042
|
+
}
|
|
4043
|
+
moduleScopeNamesCache = new WeakMap;
|
|
4044
|
+
moduleScopeDeclarationNames(ir) {
|
|
4045
|
+
const cached = this.moduleScopeNamesCache.get(ir);
|
|
4046
|
+
if (cached)
|
|
4047
|
+
return cached;
|
|
4048
|
+
const componentScope = new Set;
|
|
4049
|
+
for (const sig of ir.metadata.signals) {
|
|
4050
|
+
if (sig.isModule)
|
|
4051
|
+
continue;
|
|
4052
|
+
componentScope.add(sig.getter);
|
|
4053
|
+
if (sig.setter)
|
|
4054
|
+
componentScope.add(sig.setter);
|
|
4055
|
+
}
|
|
4056
|
+
for (const memo of ir.metadata.memos) {
|
|
4057
|
+
if (!memo.isModule)
|
|
4058
|
+
componentScope.add(memo.name);
|
|
4059
|
+
}
|
|
4060
|
+
for (const p of ir.metadata.propsParams)
|
|
4061
|
+
componentScope.add(p.name);
|
|
4062
|
+
if (ir.metadata.propsObjectName)
|
|
4063
|
+
componentScope.add(ir.metadata.propsObjectName);
|
|
4064
|
+
if (ir.metadata.restPropsName)
|
|
4065
|
+
componentScope.add(ir.metadata.restPropsName);
|
|
4066
|
+
for (const c of ir.metadata.localConstants) {
|
|
4067
|
+
if (!c.isModule)
|
|
4068
|
+
componentScope.add(c.name);
|
|
4069
|
+
}
|
|
4070
|
+
for (const f of ir.metadata.localFunctions) {
|
|
4071
|
+
if (!f.isModule)
|
|
4072
|
+
componentScope.add(f.name);
|
|
4073
|
+
}
|
|
4074
|
+
const exported = new Set;
|
|
4075
|
+
const candidates = new Map;
|
|
4076
|
+
for (const c of ir.metadata.localConstants) {
|
|
4077
|
+
if (!c.isModule)
|
|
4078
|
+
continue;
|
|
4079
|
+
if (c.isJsx || c.isJsxFunction)
|
|
4080
|
+
continue;
|
|
4081
|
+
if (c.isExported) {
|
|
4082
|
+
exported.add(c.name);
|
|
4083
|
+
continue;
|
|
4084
|
+
}
|
|
4085
|
+
candidates.set(c.name, c.freeIdentifiers ?? extractFreeIdentifiersFromText(c.value ?? ""));
|
|
4086
|
+
}
|
|
4087
|
+
for (const f of ir.metadata.localFunctions) {
|
|
4088
|
+
if (!f.isModule)
|
|
4089
|
+
continue;
|
|
4090
|
+
if (f.isJsxFunction || f.isMultiReturnJsxHelper)
|
|
4091
|
+
continue;
|
|
4092
|
+
if (f.isExported) {
|
|
4093
|
+
exported.add(f.name);
|
|
4094
|
+
continue;
|
|
4095
|
+
}
|
|
4096
|
+
const params = f.typedParams !== undefined ? f.typedParams : f.params.map(formatParamWithType).join(", ");
|
|
4097
|
+
candidates.set(f.name, extractFreeIdentifiersFromText(`(${params}) => ${f.body}`));
|
|
4098
|
+
}
|
|
4099
|
+
const referencesAny = (refs, names) => {
|
|
4100
|
+
for (const ref of refs) {
|
|
4101
|
+
if (names.has(ref))
|
|
4102
|
+
return true;
|
|
4103
|
+
}
|
|
4104
|
+
return false;
|
|
4105
|
+
};
|
|
4106
|
+
let changed = true;
|
|
4107
|
+
while (changed) {
|
|
4108
|
+
changed = false;
|
|
4109
|
+
for (const [name, refs] of candidates) {
|
|
4110
|
+
if (referencesAny(refs, componentScope)) {
|
|
4111
|
+
candidates.delete(name);
|
|
4112
|
+
componentScope.add(name);
|
|
4113
|
+
changed = true;
|
|
4114
|
+
}
|
|
4115
|
+
}
|
|
4116
|
+
}
|
|
4117
|
+
const result = new Set([...exported, ...candidates.keys()]);
|
|
4118
|
+
this.moduleScopeNamesCache.set(ir, result);
|
|
4119
|
+
return result;
|
|
4120
|
+
}
|
|
4121
|
+
generateModuleScopeDeclarations(ir) {
|
|
4122
|
+
const { preserveTypes } = this.jsxConfig;
|
|
4123
|
+
const moduleNames = this.moduleScopeDeclarationNames(ir);
|
|
4124
|
+
const entries = [];
|
|
4125
|
+
for (const t of ir.metadata.typeDefinitions) {
|
|
4126
|
+
entries.push({ line: t.loc.start.line, text: t.definition });
|
|
4127
|
+
}
|
|
4128
|
+
for (const c of ir.metadata.localConstants) {
|
|
4129
|
+
if (!c.isModule || !moduleNames.has(c.name))
|
|
4130
|
+
continue;
|
|
4131
|
+
const keyword = c.declarationKind ?? "const";
|
|
4132
|
+
const exportKw = c.isExported ? "export " : "";
|
|
4133
|
+
if (!c.value) {
|
|
4134
|
+
const typeAnnotation = preserveTypes && (c.typeAnnotation ?? c.type) ? `: ${c.typeAnnotation ?? c.type?.raw}` : "";
|
|
4135
|
+
entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name}${typeAnnotation}` });
|
|
4136
|
+
continue;
|
|
4137
|
+
}
|
|
4138
|
+
const trimmed = c.value.trim();
|
|
4139
|
+
if (/^new WeakMap\b/.test(trimmed))
|
|
4140
|
+
continue;
|
|
4141
|
+
if (c.isExported && /^createContext\b/.test(trimmed))
|
|
4142
|
+
continue;
|
|
4143
|
+
const value = preserveTypes ? c.typedValue ?? c.value : c.value;
|
|
4144
|
+
const letTypeAnnotation = preserveTypes && keyword === "let" && c.typeAnnotation ? `: ${c.typeAnnotation}` : "";
|
|
4145
|
+
entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name}${letTypeAnnotation} = ${value}` });
|
|
4146
|
+
}
|
|
4147
|
+
for (const f of ir.metadata.localFunctions) {
|
|
4148
|
+
if (!f.isModule || !moduleNames.has(f.name))
|
|
4149
|
+
continue;
|
|
4150
|
+
const params = preserveTypes && f.typedParams !== undefined ? f.typedParams : f.params.map(formatParamWithType).join(", ");
|
|
4151
|
+
const returnAnnotation = preserveTypes && f.typedReturnType ? `: ${f.typedReturnType}` : "";
|
|
4152
|
+
const body = preserveTypes ? f.typedBody ?? f.body : f.body;
|
|
4153
|
+
const asyncKw = f.isAsync ? "async " : "";
|
|
4154
|
+
const exportKw = f.isExported ? "export " : "";
|
|
4155
|
+
entries.push({
|
|
4156
|
+
line: f.loc.start.line,
|
|
4157
|
+
text: `${exportKw}${asyncKw}function ${f.name}(${params})${returnAnnotation} ${body}`
|
|
4158
|
+
});
|
|
4159
|
+
}
|
|
4160
|
+
entries.sort((a, b) => a.line - b.line);
|
|
4161
|
+
return entries.map((e) => e.text).join(`
|
|
3850
4162
|
`);
|
|
3851
4163
|
}
|
|
3852
4164
|
renderNodeRaw(node) {
|
|
@@ -3858,6 +4170,15 @@ class JsxAdapter extends BaseAdapter {
|
|
|
3858
4170
|
}
|
|
3859
4171
|
return this.renderNode(node);
|
|
3860
4172
|
}
|
|
4173
|
+
renderTemplatePartsAsJs(parts) {
|
|
4174
|
+
return templatePartsToJsExpr(parts, { typed: this.jsxConfig.preserveTypes });
|
|
4175
|
+
}
|
|
4176
|
+
expressionValueToJs(value) {
|
|
4177
|
+
if (this.jsxConfig.preserveTypes && value.parts && value.expr === templatePartsToJsExpr(value.parts)) {
|
|
4178
|
+
return this.renderTemplatePartsAsJs(value.parts);
|
|
4179
|
+
}
|
|
4180
|
+
return value.expr;
|
|
4181
|
+
}
|
|
3861
4182
|
renderScopeMarker(instanceIdExpr) {
|
|
3862
4183
|
return `${BF_SCOPE}={${instanceIdExpr}}`;
|
|
3863
4184
|
}
|
|
@@ -3925,6 +4246,7 @@ class TestAdapter extends JsxAdapter {
|
|
|
3925
4246
|
generate(ir) {
|
|
3926
4247
|
this.componentName = ir.metadata.componentName;
|
|
3927
4248
|
const imports = this.generateImports(ir);
|
|
4249
|
+
const moduleConstants = this.generateModuleScopeDeclarations(ir);
|
|
3928
4250
|
const types = this.generateTypes(ir);
|
|
3929
4251
|
const component = this.generateComponent(ir);
|
|
3930
4252
|
const defaultExport = ir.metadata.hasDefaultExport ? `
|
|
@@ -3933,9 +4255,11 @@ export default ${this.componentName}` : "";
|
|
|
3933
4255
|
imports,
|
|
3934
4256
|
types: types || "",
|
|
3935
4257
|
component,
|
|
3936
|
-
defaultExport
|
|
4258
|
+
defaultExport,
|
|
4259
|
+
moduleConstants,
|
|
4260
|
+
moduleConstantsIncludeExports: true
|
|
3937
4261
|
};
|
|
3938
|
-
const template = [imports, types, component].filter(Boolean).join(`
|
|
4262
|
+
const template = [imports, moduleConstants, types, component].filter(Boolean).join(`
|
|
3939
4263
|
|
|
3940
4264
|
`) + defaultExport;
|
|
3941
4265
|
return {
|
|
@@ -3966,9 +4290,6 @@ export default ${this.componentName}` : "";
|
|
|
3966
4290
|
}
|
|
3967
4291
|
generateTypes(ir) {
|
|
3968
4292
|
const lines = [];
|
|
3969
|
-
for (const typeDef of ir.metadata.typeDefinitions) {
|
|
3970
|
-
lines.push(typeDef.definition);
|
|
3971
|
-
}
|
|
3972
4293
|
const propsTypeName = ir.metadata.propsType?.raw;
|
|
3973
4294
|
if (propsTypeName && !ir.metadata.propsObjectName) {
|
|
3974
4295
|
lines.push("");
|
|
@@ -3991,7 +4312,7 @@ export default ${this.componentName}` : "";
|
|
|
3991
4312
|
const bodyRefText = [jsxBody, signalInits, scopeIdLine].join(`
|
|
3992
4313
|
`);
|
|
3993
4314
|
const bfScopeAlias = /\b__bfScope\b/.test(bodyRefText) ? "__bfScope" : "__bfScope: _bfScope";
|
|
3994
|
-
const propsParams = ir.metadata.propsParams.map((p) => p
|
|
4315
|
+
const propsParams = ir.metadata.propsParams.map((p) => propsDestructureBinding(p)).join(", ");
|
|
3995
4316
|
const restPropsName = ir.metadata.restPropsName;
|
|
3996
4317
|
const hydrationProps = `__instanceId, ${bfScopeAlias}`;
|
|
3997
4318
|
const parts = [];
|
|
@@ -4138,13 +4459,7 @@ export default ${this.componentName}` : "";
|
|
|
4138
4459
|
}
|
|
4139
4460
|
flattenTemplate(value) {
|
|
4140
4461
|
const v = value;
|
|
4141
|
-
return
|
|
4142
|
-
if (p.type === "string")
|
|
4143
|
-
return p.value;
|
|
4144
|
-
if (p.type === "ternary")
|
|
4145
|
-
return `\${${p.condition} ? '${p.whenTrue}' : '${p.whenFalse}'}`;
|
|
4146
|
-
return `\${(${JSON.stringify(p.cases)})[${p.key}]}`;
|
|
4147
|
-
}).join("") + "`";
|
|
4462
|
+
return this.renderTemplatePartsAsJs(v.parts);
|
|
4148
4463
|
}
|
|
4149
4464
|
renderComponentProps(comp) {
|
|
4150
4465
|
const parts = [];
|
|
@@ -4618,7 +4933,7 @@ function dangerousInnerHtmlDiagnostic(expr, loc, reason) {
|
|
|
4618
4933
|
};
|
|
4619
4934
|
}
|
|
4620
4935
|
// ../jsx/src/combine-client-js.ts
|
|
4621
|
-
import
|
|
4936
|
+
import ts25 from "typescript";
|
|
4622
4937
|
// ../jsx/src/loop-destructure.ts
|
|
4623
4938
|
function isLowerableLoopDestructure(loop) {
|
|
4624
4939
|
const bindings = loop.paramBindings;
|
|
@@ -4758,9 +5073,9 @@ function escapeRe(s) {
|
|
|
4758
5073
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4759
5074
|
}
|
|
4760
5075
|
// ../jsx/src/debug.ts
|
|
4761
|
-
import
|
|
5076
|
+
import ts26 from "typescript";
|
|
4762
5077
|
// ../jsx/src/profiler.ts
|
|
4763
|
-
import
|
|
5078
|
+
import ts27 from "typescript";
|
|
4764
5079
|
|
|
4765
5080
|
// ../jsx/src/index.ts
|
|
4766
5081
|
registerBuiltinLoweringPlugins();
|
|
@@ -5586,7 +5901,7 @@ function collectImportedLoopChildComponentErrors(ir, componentName) {
|
|
|
5586
5901
|
}
|
|
5587
5902
|
|
|
5588
5903
|
// src/adapter/spread/spread-codegen.ts
|
|
5589
|
-
import
|
|
5904
|
+
import ts28 from "typescript";
|
|
5590
5905
|
function conditionalSpreadToJinja(ctx, expr) {
|
|
5591
5906
|
if (!expr || expr.kind !== "conditional")
|
|
5592
5907
|
return null;
|
|
@@ -5641,7 +5956,7 @@ function recordIndexAccessToJinja(ctx, val) {
|
|
|
5641
5956
|
if (val.kind !== "index-access" || val.object.kind !== "identifier" || val.index.kind !== "identifier") {
|
|
5642
5957
|
return null;
|
|
5643
5958
|
}
|
|
5644
|
-
const tsVal =
|
|
5959
|
+
const tsVal = ts28.factory.createElementAccessExpression(ts28.factory.createIdentifier(val.object.name), ts28.factory.createIdentifier(val.index.name));
|
|
5645
5960
|
const parsed = parseRecordIndexAccess(tsVal, ctx.localConstants ?? [], ctx.propsParams);
|
|
5646
5961
|
if (!parsed)
|
|
5647
5962
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/jinja",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.3",
|
|
4
4
|
"description": "Jinja2 adapter for BarefootJS — compiles IR to .jinja templates and ships the Python BarefootJS rendering runtime; runs under any Python web framework (Flask, etc.)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"directory": "packages/adapter-jinja"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@barefootjs/shared": "0.31.
|
|
56
|
+
"@barefootjs/shared": "0.31.3"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@barefootjs/jsx": ">=0.2.0",
|
|
@@ -70,9 +70,9 @@
|
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@barefootjs/adapter-tests": "0.1.0",
|
|
73
|
-
"@barefootjs/jsx": "0.31.
|
|
74
|
-
"@barefootjs/vite": "0.31.
|
|
75
|
-
"@barefootjs/client": "0.31.
|
|
73
|
+
"@barefootjs/jsx": "0.31.3",
|
|
74
|
+
"@barefootjs/vite": "0.31.3",
|
|
75
|
+
"@barefootjs/client": "0.31.3",
|
|
76
76
|
"typescript": "^5.0.0",
|
|
77
77
|
"vite": "^6.0.0"
|
|
78
78
|
}
|
|
@@ -484,20 +484,35 @@ _DATE_EPOCH = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
|
|
|
484
484
|
def _derive_stash_from_defaults(defaults: dict, props: dict) -> dict:
|
|
485
485
|
"""Derive template-stash kvs from a manifest entry's `ssrDefaults`
|
|
486
486
|
section. See BarefootJS.pm's `_derive_stash_from_defaults` docstring for
|
|
487
|
-
the full field-shape contract.
|
|
487
|
+
the full field-shape contract.
|
|
488
|
+
|
|
489
|
+
`props` arrives ALREADY keyword-mangled (`render_child` / the manifest
|
|
490
|
+
renderer closure both mangle every prop key via `jinja_ident` before
|
|
491
|
+
calling in -- see their docstrings), but `defaults`' own keys and each
|
|
492
|
+
entry's `propName` are the RAW (un-mangled) spellings `extractSsrDefaults`
|
|
493
|
+
emitted. For a reserved-word prop (`as`, `for`, `class`, ...) an
|
|
494
|
+
unmangled lookup/output key misses the mangled `props` map entirely --
|
|
495
|
+
silently falling back to the static default even when the caller DID
|
|
496
|
+
supply a value -- and then writes the stash under the RAW key, which a
|
|
497
|
+
subsequent mangling pass (or the template itself, keyed by the mangled
|
|
498
|
+
name) never reads. Both the output key and the `propName` lookup are
|
|
499
|
+
mangled here so this resolves correctly regardless of whether the prop
|
|
500
|
+
name happens to collide with a reserved word (#2524 follow-up)."""
|
|
488
501
|
extra: dict = {}
|
|
489
502
|
for name, d in (defaults or {}).items():
|
|
503
|
+
key = jinja_ident(name)
|
|
490
504
|
if not isinstance(d, dict):
|
|
491
|
-
extra[
|
|
505
|
+
extra[key] = d
|
|
492
506
|
continue
|
|
493
507
|
if d.get("isRestProps"):
|
|
494
|
-
extra[
|
|
508
|
+
extra[key] = props[key] if key in props else d.get("value")
|
|
495
509
|
continue
|
|
496
510
|
prop_name = d.get("propName")
|
|
497
|
-
if prop_name is not None
|
|
498
|
-
|
|
511
|
+
mangled_prop_name = jinja_ident(prop_name) if prop_name is not None else None
|
|
512
|
+
if mangled_prop_name is not None and props.get(mangled_prop_name) is not None:
|
|
513
|
+
extra[key] = props[mangled_prop_name]
|
|
499
514
|
else:
|
|
500
|
-
extra[
|
|
515
|
+
extra[key] = d.get("value")
|
|
501
516
|
return extra
|
|
502
517
|
|
|
503
518
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression for the review finding on #2524's SSR half (BLOCKER 1): a
|
|
3
|
+
* reserved-word aliased/defaulted prop passed to a CHILD component used to
|
|
4
|
+
* be silently clobbered by the child's static default.
|
|
5
|
+
*
|
|
6
|
+
* `child_props` arrives at `_derive_stash_from_defaults` already
|
|
7
|
+
* keyword-mangled (`jinja_ident`, `runtime.py`) — `as` becomes `as_` — but
|
|
8
|
+
* the serialised `_defaults` map's own keys and each entry's `propName` were
|
|
9
|
+
* the RAW (un-mangled) spellings `extractSsrDefaults` emits. For a
|
|
10
|
+
* reserved-word prop, `_derive_stash_from_defaults` looked up `props['as']`
|
|
11
|
+
* (never present — the real key is `as_`), missed, and fell back to the
|
|
12
|
+
* static default under the RAW key `as`; after `_vars = {**child_props,
|
|
13
|
+
* **_extra}`, the WRONG static default won over the real caller value.
|
|
14
|
+
*
|
|
15
|
+
* `_derive_stash_from_defaults` now mangles both the output key and the
|
|
16
|
+
* `propName` lookup (mirrors the Rust runtime's `resolve_child_vars`), so
|
|
17
|
+
* this resolves correctly. See `runtime.py`'s `_derive_stash_from_defaults`
|
|
18
|
+
* docstring.
|
|
19
|
+
*/
|
|
20
|
+
import { test, expect } from 'bun:test'
|
|
21
|
+
import { JinjaAdapter } from '../adapter'
|
|
22
|
+
import { renderJinjaComponent, PythonNotAvailableError } from '../test-render'
|
|
23
|
+
|
|
24
|
+
const SOURCE = `
|
|
25
|
+
import { Tag } from './Tag'
|
|
26
|
+
export function Wrapper() {
|
|
27
|
+
return <Tag as="section" label="hi" />
|
|
28
|
+
}
|
|
29
|
+
`
|
|
30
|
+
|
|
31
|
+
const TAG_SOURCE = `
|
|
32
|
+
export function Tag({ as = 'span', label }: { as?: string; label: string }) {
|
|
33
|
+
return <div>{as}:{label}</div>
|
|
34
|
+
}
|
|
35
|
+
`
|
|
36
|
+
|
|
37
|
+
test('a reserved-word aliased/defaulted prop survives the child-render path (jinja, #2524 follow-up)', async () => {
|
|
38
|
+
let html: string
|
|
39
|
+
try {
|
|
40
|
+
html = await renderJinjaComponent({
|
|
41
|
+
source: SOURCE,
|
|
42
|
+
adapter: new JinjaAdapter(),
|
|
43
|
+
components: { './Tag': TAG_SOURCE },
|
|
44
|
+
})
|
|
45
|
+
} catch (err) {
|
|
46
|
+
if (err instanceof PythonNotAvailableError) {
|
|
47
|
+
console.log(`Skipping: ${err.message}`)
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
throw err
|
|
51
|
+
}
|
|
52
|
+
// The caller passed `as="section"` — it must win over the child's own
|
|
53
|
+
// `as = 'span'` destructure default. Text is wrapped in bf slot markers,
|
|
54
|
+
// so match each interpolated value rather than the literal `x:y` shape.
|
|
55
|
+
expect(html).toContain('>section<')
|
|
56
|
+
expect(html).toContain('>hi<')
|
|
57
|
+
expect(html).not.toContain('>span<')
|
|
58
|
+
})
|
|
@@ -20,20 +20,4 @@ export const renderDivergences: RenderDivergences = {
|
|
|
20
20
|
// instead of a fixed regex-shape catalogue) now correctly seeds `todos`
|
|
21
21
|
// from `(props.initialTodos ?? []).map(t => ({ ...t, editing: false }))`.
|
|
22
22
|
|
|
23
|
-
// Onboarding TSX-fidelity fixtures (PR #2461): `expectedHtml` was
|
|
24
|
-
// hand-authored to the CORRECT output while the emission bug lived in
|
|
25
|
-
// the shared compiler layer — every adapter, including the Hono
|
|
26
|
-
// reference, used to emit the broken form. That shared-layer defect
|
|
27
|
-
// (#2460) is now FIXED (b4f5075): `expectedHtml` is generated from the
|
|
28
|
-
// Hono reference like any other fixture. The remaining gap is
|
|
29
|
-
// per-template-adapter — this adapter still keys its template vars /
|
|
30
|
-
// ssr-defaults / props bridge off the local binding instead of
|
|
31
|
-
// `sourceName ?? name` — tracked by #2524 (the 7 silent template
|
|
32
|
-
// adapters). Graduate by applying the same `sourceName ?? name` fix to
|
|
33
|
-
// this adapter's emission path and deleting these lines (and the
|
|
34
|
-
// matching hono `skipJsx` entries, already gone).
|
|
35
|
-
'aliased-destructured-prop':
|
|
36
|
-
'aliased destructured prop `{ n: count }` loses its rename — template vars, ssr-defaults, and the props bridge all key off the local name, so the prop is always undefined (https://github.com/piconic-ai/barefootjs/issues/2524)',
|
|
37
|
-
'composite-row-child-aliased-prop':
|
|
38
|
-
'same defect as `aliased-destructured-prop` (#2524), inside a keyed `.map()` loop row: the nested child\'s renamed prop (`{ n: count }`) is always undefined, so both rows render an empty count (https://github.com/piconic-ai/barefootjs/issues/2524)',
|
|
39
23
|
}
|
package/src/test-render.ts
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
* generated render script (Python, not Perl) and its literal syntax differ.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { compileJSX, extractSsrDefaults, importsSearchParams, evaluateSignalInit
|
|
20
|
-
import type { ComponentIR } from '@barefootjs/jsx'
|
|
19
|
+
import { compileJSX, extractSsrDefaults, deriveStashFromDefaults, importsSearchParams, evaluateSignalInit } from '@barefootjs/jsx'
|
|
20
|
+
import type { ComponentIR, SsrDefault } from '@barefootjs/jsx'
|
|
21
21
|
import { mkdir, rm } from 'node:fs/promises'
|
|
22
22
|
import { resolve } from 'node:path'
|
|
23
23
|
|
|
@@ -252,7 +252,7 @@ import uuid
|
|
|
252
252
|
|
|
253
253
|
from barefootjs import BarefootJS, SearchParams
|
|
254
254
|
from barefootjs.backend_jinja import JinjaBackend
|
|
255
|
-
from barefootjs.runtime import jinja_ident
|
|
255
|
+
from barefootjs.runtime import jinja_ident, _derive_stash_from_defaults
|
|
256
256
|
|
|
257
257
|
# Single Jinja2 backend over the temp template dir.
|
|
258
258
|
backend = JinjaBackend(paths=[${pyStr(tempDir)}])
|
|
@@ -333,7 +333,16 @@ function collectImportedComponentNames(ir: ComponentIR): string[] {
|
|
|
333
333
|
* `runtime.BarefootJS.render_child`'s docstring. So the rest-bag routing
|
|
334
334
|
* below and the `_bf_slot` / `key` / `children` pops all compare against
|
|
335
335
|
* ALREADY-mangled key spellings; the `keep` set mangles the child's declared
|
|
336
|
-
* param names to match.
|
|
336
|
+
* param (CALLER-facing, `sourceName ?? name`) names to match.
|
|
337
|
+
*
|
|
338
|
+
* Template vars seed through the production `_derive_stash_from_defaults`
|
|
339
|
+
* (`barefootjs.runtime`, the same function `register_components_from_
|
|
340
|
+
* manifest`'s `make_renderer` calls) rather than a flat `{**_defaults,
|
|
341
|
+
* **child_props}` merge: the flat merge never resolves an aliased
|
|
342
|
+
* destructured prop's CALLER-facing key (`n`) onto its local template var
|
|
343
|
+
* (`count`) — `_defaults` carries the FULL `{value, propName?,
|
|
344
|
+
* isRestProps?}` shape (not flattened to bare values) so the propName
|
|
345
|
+
* resolution has something to read (#2524 SSR half).
|
|
337
346
|
*/
|
|
338
347
|
function buildChildRenderers(
|
|
339
348
|
childTemplates: Map<string, { template: string; ir: ComponentIR }>,
|
|
@@ -348,12 +357,14 @@ function buildChildRenderers(
|
|
|
348
357
|
const snakeName = toSnakeCase(componentName)
|
|
349
358
|
const fnSuffix = snakeName.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
350
359
|
// Statically-derived ssrDefaults the child template's vars seed from
|
|
351
|
-
// (prop defaults + signal / memo initial values), serialised
|
|
352
|
-
// Python dict literal
|
|
360
|
+
// (prop defaults + signal / memo initial values), serialised VERBATIM
|
|
361
|
+
// (propName / isRestProps intact) to a Python dict literal — resolved
|
|
362
|
+
// per-call against the REAL child props by `_derive_stash_from_defaults`
|
|
363
|
+
// below, not flattened here.
|
|
353
364
|
const ssrDefaults = extractSsrDefaults(childIR.metadata) ?? {}
|
|
354
365
|
const defaultsPy = ssrDefaultsToPy(ssrDefaults)
|
|
355
366
|
const restPropsName = childIR.metadata.restPropsName
|
|
356
|
-
const paramNames = (childIR.metadata.propsParams ?? []).map(p => p.name)
|
|
367
|
+
const paramNames = (childIR.metadata.propsParams ?? []).map(p => p.sourceName ?? p.name)
|
|
357
368
|
|
|
358
369
|
lines.push(`def _make_child_renderer_${fnSuffix}():`)
|
|
359
370
|
lines.push(` _defaults = ${defaultsPy}`)
|
|
@@ -404,8 +415,15 @@ function buildChildRenderers(
|
|
|
404
415
|
lines.push(` child_bf._child_renderers(bf._child_renderers())`)
|
|
405
416
|
lines.push(` child_bf._scripts(bf._scripts())`)
|
|
406
417
|
lines.push(` child_bf._script_seen(bf._script_seen())`)
|
|
407
|
-
// Seed template vars
|
|
408
|
-
|
|
418
|
+
// Seed template vars through the production `_derive_stash_from_defaults`
|
|
419
|
+
// — resolves each entry's `propName` against the REAL `child_props`
|
|
420
|
+
// (already keyword-mangled above), falling back to the static `value`;
|
|
421
|
+
// `isRestProps` entries pass the already-routed rest bag through. Caller
|
|
422
|
+
// props still win overall via the final merge (mirrors the ERB
|
|
423
|
+
// harness's `child_props.merge(extra)` and the production
|
|
424
|
+
// `register_components_from_manifest` renderer closure).
|
|
425
|
+
lines.push(` _extra = _derive_stash_from_defaults(_defaults, child_props)`)
|
|
426
|
+
lines.push(` _vars = {**child_props, **_extra}`)
|
|
409
427
|
lines.push(` rendered = backend.render_named(${pyStr(snakeName)}, child_bf, _vars)`)
|
|
410
428
|
lines.push(` if isinstance(rendered, str) and rendered.endswith('\\n'):`)
|
|
411
429
|
lines.push(` rendered = rendered[:-1]`)
|
|
@@ -424,22 +442,31 @@ function pyList(names: string[]): string {
|
|
|
424
442
|
return `[${names.map(pyStr).join(', ')}]`
|
|
425
443
|
}
|
|
426
444
|
|
|
427
|
-
/**
|
|
428
|
-
|
|
445
|
+
/**
|
|
446
|
+
* Serialise an ssrDefaults map to a Python dict literal, VERBATIM —
|
|
447
|
+
* `value` / `propName` / `isRestProps` intact, exactly the shape
|
|
448
|
+
* `_derive_stash_from_defaults` expects (and exactly what the production
|
|
449
|
+
* build manifest embeds). Do NOT flatten to bare `value`s here: that was
|
|
450
|
+
* the #2524 SSR-half bug — a flattened entry has nothing left for the
|
|
451
|
+
* propName-aware resolution to read, so an aliased destructured prop's
|
|
452
|
+
* caller-facing key is silently dropped.
|
|
453
|
+
*/
|
|
454
|
+
function ssrDefaultsToPy(defaults: Record<string, SsrDefault>): string {
|
|
429
455
|
const entries: string[] = []
|
|
430
456
|
for (const [name, d] of Object.entries(defaults)) {
|
|
431
|
-
|
|
432
|
-
// bare value. The child renderer's caller props win, so we only need
|
|
433
|
-
// the static fallback `value` here.
|
|
434
|
-
let value: unknown = d
|
|
435
|
-
if (d && typeof d === 'object' && 'value' in (d as Record<string, unknown>)) {
|
|
436
|
-
value = (d as Record<string, unknown>).value
|
|
437
|
-
}
|
|
438
|
-
entries.push(`${pyStr(name)}: ${toPyLiteral(value)}`)
|
|
457
|
+
entries.push(`${pyStr(name)}: ${ssrDefaultEntryToPy(d)}`)
|
|
439
458
|
}
|
|
440
459
|
return `{${entries.join(', ')}}`
|
|
441
460
|
}
|
|
442
461
|
|
|
462
|
+
/** Serialise a single `SsrDefault` entry to a Python dict literal. */
|
|
463
|
+
function ssrDefaultEntryToPy(d: SsrDefault): string {
|
|
464
|
+
const parts: string[] = [`'value': ${toPyLiteral(d.value)}`]
|
|
465
|
+
if (d.propName !== undefined) parts.push(`'propName': ${pyStr(d.propName)}`)
|
|
466
|
+
if (d.isRestProps) parts.push(`'isRestProps': True`)
|
|
467
|
+
return `{${parts.join(', ')}}`
|
|
468
|
+
}
|
|
469
|
+
|
|
443
470
|
/**
|
|
444
471
|
* Convert PascalCase to snake_case for template naming (matches the
|
|
445
472
|
* adapter's `toTemplateName`).
|
|
@@ -465,23 +492,29 @@ function buildPythonProps(
|
|
|
465
492
|
entries.push(`${pyStr('scope_id')}: ${pyStr(explicitScope)}`)
|
|
466
493
|
|
|
467
494
|
// Prop params with defaults (before signals, so signals can reference them).
|
|
495
|
+
// Seeded through the shared `deriveStashFromDefaults` (the TS twin of the
|
|
496
|
+
// production `_derive_stash_from_defaults` this harness ALSO calls at
|
|
497
|
+
// render time for child components) so an aliased destructured prop's
|
|
498
|
+
// CALLER-facing key (`propName`, e.g. `n` for `{ n: count }`) is honoured,
|
|
499
|
+
// not just the local template var name (#2524 SSR half). `props` is keyed
|
|
500
|
+
// by the caller-facing name, exactly what `propName` resolves against.
|
|
501
|
+
const rootSsrDefaults = extractSsrDefaults(ir.metadata) ?? {}
|
|
502
|
+
const derivedProps = deriveStashFromDefaults(rootSsrDefaults, props ?? {})
|
|
468
503
|
for (const param of ir.metadata.propsParams) {
|
|
469
|
-
if (
|
|
470
|
-
if (param.defaultValue) {
|
|
471
|
-
const result = tryEvaluateSignalInit(param.defaultValue.trim(), props)
|
|
472
|
-
if (result.ok) {
|
|
473
|
-
entries.push(`${pyStr(param.name)}: ${toPyLiteral(result.value)}`)
|
|
474
|
-
continue
|
|
475
|
-
}
|
|
476
|
-
}
|
|
504
|
+
if (param.isRest) continue
|
|
477
505
|
// No default + no caller value: pass `None` so Jinja's var lookup for
|
|
478
506
|
// an optional prop doesn't fault before its falsy branch elides.
|
|
479
|
-
|
|
507
|
+
const value = derivedProps[param.name] ?? null
|
|
508
|
+
entries.push(`${pyStr(param.name)}: ${toPyLiteral(value)}`)
|
|
480
509
|
}
|
|
481
510
|
|
|
482
511
|
// Route undeclared props into the rest bag (`bf.spread_attrs($<rest>)`).
|
|
483
512
|
const restPropsName = ir.metadata.restPropsName
|
|
484
|
-
|
|
513
|
+
// Caller-facing keys — an aliased destructured prop's DECLARED set for
|
|
514
|
+
// rest-bag routing must match what the caller actually sent (`n`), not
|
|
515
|
+
// the local binding (`count`), or the caller's own prop silently gets
|
|
516
|
+
// swept into the rest bag as an undeclared extra (#2524).
|
|
517
|
+
const declaredParams = new Set(ir.metadata.propsParams.map(p => p.sourceName ?? p.name))
|
|
485
518
|
const restBagEntries: Array<[string, unknown]> = []
|
|
486
519
|
if (restPropsName && props) {
|
|
487
520
|
for (const [key, value] of Object.entries(props)) {
|
|
@@ -496,11 +529,20 @@ function buildPythonProps(
|
|
|
496
529
|
entries.push(`${pyStr(restPropsName)}: ${toPyLiteral(Object.fromEntries(restBagEntries))}`)
|
|
497
530
|
}
|
|
498
531
|
|
|
499
|
-
// User props.
|
|
532
|
+
// User props. Skip a key that's already a declared param's LOCAL template
|
|
533
|
+
// var name — `derivedProps` above already resolved that var correctly
|
|
534
|
+
// (through `propName`, for an aliased prop); re-pushing the raw `props[key]`
|
|
535
|
+
// here would silently clobber it with an UNRELATED value whenever a caller
|
|
536
|
+
// happens to also pass a same-spelled-as-local-name prop that isn't this
|
|
537
|
+
// param's actual `propName` (#2524 — surfaced by the aliased-destructured-prop
|
|
538
|
+
// generated data points, which pass both `n` (the real propName) and an
|
|
539
|
+
// incidental `count` key).
|
|
540
|
+
const localParamNames = new Set(ir.metadata.propsParams.map(p => p.name))
|
|
500
541
|
if (props) {
|
|
501
542
|
for (const [key, value] of Object.entries(props)) {
|
|
502
543
|
if (key.startsWith('__')) continue
|
|
503
544
|
if (routedKeys.has(key)) continue
|
|
545
|
+
if (localParamNames.has(key)) continue
|
|
504
546
|
if (typeof value === 'string') {
|
|
505
547
|
entries.push(`${pyStr(key)}: ${pyStr(value)}`)
|
|
506
548
|
} else if (typeof value === 'number') {
|
|
@@ -526,10 +568,9 @@ function buildPythonProps(
|
|
|
526
568
|
|
|
527
569
|
// Memo values seeded from the statically-evaluated ssrDefaults, same
|
|
528
570
|
// as the production plugin's before_render hook.
|
|
529
|
-
const ssrDefaults = extractSsrDefaults(ir.metadata) ?? {}
|
|
530
571
|
for (const memo of ir.metadata.memos) {
|
|
531
|
-
const entry =
|
|
532
|
-
const value = entry
|
|
572
|
+
const entry = rootSsrDefaults[memo.name]
|
|
573
|
+
const value = entry ? entry.value : 0
|
|
533
574
|
entries.push(`${pyStr(memo.name)}: ${toPyLiteral(value ?? 0)}`)
|
|
534
575
|
}
|
|
535
576
|
|