@barefootjs/jinja 0.31.1 → 0.31.2
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 +334 -99
- 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
|
|
@@ -2880,13 +2987,13 @@ var KEYWORDS_AND_GLOBALS = new Set([
|
|
|
2880
2987
|
]);
|
|
2881
2988
|
|
|
2882
2989
|
// ../jsx/src/ir-to-client-js/walk-prop-accesses.ts
|
|
2883
|
-
import
|
|
2990
|
+
import ts13 from "typescript";
|
|
2884
2991
|
|
|
2885
2992
|
// ../jsx/src/value-references.ts
|
|
2886
|
-
import
|
|
2993
|
+
import ts14 from "typescript";
|
|
2887
2994
|
|
|
2888
2995
|
// ../jsx/src/relocate.ts
|
|
2889
|
-
import
|
|
2996
|
+
import ts15 from "typescript";
|
|
2890
2997
|
|
|
2891
2998
|
// ../jsx/src/lowering-registry.ts
|
|
2892
2999
|
var plugins = [];
|
|
@@ -3103,10 +3210,10 @@ function matchSearchParamsMethodCall(callee, args, localNames) {
|
|
|
3103
3210
|
}
|
|
3104
3211
|
|
|
3105
3212
|
// ../jsx/src/ir-to-client-js/prune-unused-prop-extractions.ts
|
|
3106
|
-
import
|
|
3213
|
+
import ts16 from "typescript";
|
|
3107
3214
|
|
|
3108
3215
|
// ../jsx/src/ir-to-client-js/control-flow/plan/lazy-preamble.ts
|
|
3109
|
-
import
|
|
3216
|
+
import ts17 from "typescript";
|
|
3110
3217
|
var NO_PREAMBLE = {
|
|
3111
3218
|
lazySafe: true,
|
|
3112
3219
|
facts: { declaredNames: new Set, freeNames: new Set }
|
|
@@ -3156,7 +3263,7 @@ var INERT_BINDING_GLOBALS = new Set([
|
|
|
3156
3263
|
]);
|
|
3157
3264
|
|
|
3158
3265
|
// ../jsx/src/ir-to-client-js/emit-reactive.ts
|
|
3159
|
-
import
|
|
3266
|
+
import ts18 from "typescript";
|
|
3160
3267
|
|
|
3161
3268
|
// ../jsx/src/ir-to-client-js/control-flow/stringify/event-delegation.ts
|
|
3162
3269
|
var NON_BUBBLING_EVENTS = new Set([
|
|
@@ -3171,7 +3278,7 @@ var NON_BUBBLING_EVENTS = new Set([
|
|
|
3171
3278
|
]);
|
|
3172
3279
|
|
|
3173
3280
|
// ../jsx/src/ir-to-client-js/rewrite-props-object.ts
|
|
3174
|
-
import
|
|
3281
|
+
import ts19 from "typescript";
|
|
3175
3282
|
|
|
3176
3283
|
// ../jsx/src/ir-to-client-js/source-map.ts
|
|
3177
3284
|
var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
@@ -3262,15 +3369,15 @@ class SourceMapGenerator {
|
|
|
3262
3369
|
}
|
|
3263
3370
|
|
|
3264
3371
|
// ../jsx/src/preprocess-inline-jsx-callbacks.ts
|
|
3265
|
-
import
|
|
3372
|
+
import ts20 from "typescript";
|
|
3266
3373
|
|
|
3267
3374
|
// ../jsx/src/ssr-defaults.ts
|
|
3268
|
-
import
|
|
3375
|
+
import ts21 from "typescript";
|
|
3269
3376
|
var UNRESOLVED = Symbol("unresolved");
|
|
3270
3377
|
var NO_RETURN = Symbol("no-return");
|
|
3271
3378
|
|
|
3272
3379
|
// ../jsx/src/augment-inherited-props.ts
|
|
3273
|
-
import
|
|
3380
|
+
import ts22 from "typescript";
|
|
3274
3381
|
function collectContextConsumers(metadata) {
|
|
3275
3382
|
const constants = metadata.localConstants ?? [];
|
|
3276
3383
|
const contextDefaults = new Map;
|
|
@@ -3302,47 +3409,47 @@ function collectContextConsumers(metadata) {
|
|
|
3302
3409
|
}
|
|
3303
3410
|
function parseUseContextArg(source) {
|
|
3304
3411
|
const expr = parseSingleExpression(source);
|
|
3305
|
-
if (!expr || !
|
|
3412
|
+
if (!expr || !ts22.isCallExpression(expr))
|
|
3306
3413
|
return null;
|
|
3307
|
-
if (!
|
|
3414
|
+
if (!ts22.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
|
|
3308
3415
|
return null;
|
|
3309
3416
|
if (expr.arguments.length !== 1)
|
|
3310
3417
|
return null;
|
|
3311
3418
|
const arg = expr.arguments[0];
|
|
3312
|
-
return
|
|
3419
|
+
return ts22.isIdentifier(arg) ? arg.text : null;
|
|
3313
3420
|
}
|
|
3314
3421
|
function parseCreateContextDefault(source) {
|
|
3315
3422
|
const expr = parseSingleExpression(source);
|
|
3316
|
-
if (!expr || !
|
|
3423
|
+
if (!expr || !ts22.isCallExpression(expr))
|
|
3317
3424
|
return null;
|
|
3318
3425
|
if (expr.arguments.length === 0)
|
|
3319
3426
|
return null;
|
|
3320
3427
|
const arg = expr.arguments[0];
|
|
3321
|
-
if (
|
|
3428
|
+
if (ts22.isStringLiteral(arg) || ts22.isNoSubstitutionTemplateLiteral(arg))
|
|
3322
3429
|
return arg.text;
|
|
3323
|
-
if (
|
|
3430
|
+
if (ts22.isNumericLiteral(arg))
|
|
3324
3431
|
return Number(arg.text);
|
|
3325
|
-
if (arg.kind ===
|
|
3432
|
+
if (arg.kind === ts22.SyntaxKind.TrueKeyword)
|
|
3326
3433
|
return true;
|
|
3327
|
-
if (arg.kind ===
|
|
3434
|
+
if (arg.kind === ts22.SyntaxKind.FalseKeyword)
|
|
3328
3435
|
return false;
|
|
3329
3436
|
return null;
|
|
3330
3437
|
}
|
|
3331
3438
|
function isObjectLiteralCreateContextDefault(source) {
|
|
3332
3439
|
const expr = parseSingleExpression(source);
|
|
3333
|
-
if (!expr || !
|
|
3440
|
+
if (!expr || !ts22.isCallExpression(expr))
|
|
3334
3441
|
return false;
|
|
3335
3442
|
if (expr.arguments.length === 0)
|
|
3336
3443
|
return false;
|
|
3337
|
-
return
|
|
3444
|
+
return ts22.isObjectLiteralExpression(expr.arguments[0]);
|
|
3338
3445
|
}
|
|
3339
3446
|
function parseSingleExpression(source) {
|
|
3340
|
-
const sf =
|
|
3447
|
+
const sf = ts22.createSourceFile("__ctx.ts", `(${source})`, ts22.ScriptTarget.Latest, false);
|
|
3341
3448
|
const stmt = sf.statements[0];
|
|
3342
|
-
if (!stmt || !
|
|
3449
|
+
if (!stmt || !ts22.isExpressionStatement(stmt))
|
|
3343
3450
|
return null;
|
|
3344
3451
|
let e = stmt.expression;
|
|
3345
|
-
while (
|
|
3452
|
+
while (ts22.isParenthesizedExpression(e))
|
|
3346
3453
|
e = e.expression;
|
|
3347
3454
|
return e;
|
|
3348
3455
|
}
|
|
@@ -3367,25 +3474,25 @@ function augmentInheritedPropAccesses(ir) {
|
|
|
3367
3474
|
const pinCoalesceLiterals = (s) => {
|
|
3368
3475
|
if (!s || !s.includes(propsObj))
|
|
3369
3476
|
return;
|
|
3370
|
-
const sf =
|
|
3477
|
+
const sf = ts22.createSourceFile("__aug.ts", `(${s})`, ts22.ScriptTarget.Latest, false);
|
|
3371
3478
|
const visit = (n) => {
|
|
3372
|
-
if (
|
|
3479
|
+
if (ts22.isBinaryExpression(n) && (n.operatorToken.kind === ts22.SyntaxKind.QuestionQuestionToken || n.operatorToken.kind === ts22.SyntaxKind.BarBarToken)) {
|
|
3373
3480
|
let left = n.left;
|
|
3374
|
-
while (
|
|
3481
|
+
while (ts22.isParenthesizedExpression(left))
|
|
3375
3482
|
left = left.expression;
|
|
3376
|
-
if (
|
|
3483
|
+
if (ts22.isPropertyAccessExpression(left) && ts22.isIdentifier(left.expression) && left.expression.text === propsObj) {
|
|
3377
3484
|
const name = left.name.text;
|
|
3378
3485
|
let right = n.right;
|
|
3379
|
-
while (
|
|
3486
|
+
while (ts22.isParenthesizedExpression(right))
|
|
3380
3487
|
right = right.expression;
|
|
3381
|
-
if (
|
|
3488
|
+
if (ts22.isPrefixUnaryExpression(right))
|
|
3382
3489
|
right = right.operand;
|
|
3383
|
-
const kind =
|
|
3490
|
+
const kind = ts22.isNumericLiteral(right) ? "number" : right.kind === ts22.SyntaxKind.TrueKeyword || right.kind === ts22.SyntaxKind.FalseKeyword ? "boolean" : ts22.isStringLiteralLike(right) ? "string" : null;
|
|
3384
3491
|
if (kind && !coalesceLiteralTypes.has(name))
|
|
3385
3492
|
coalesceLiteralTypes.set(name, kind);
|
|
3386
3493
|
}
|
|
3387
3494
|
}
|
|
3388
|
-
|
|
3495
|
+
ts22.forEachChild(n, visit);
|
|
3389
3496
|
};
|
|
3390
3497
|
visit(sf);
|
|
3391
3498
|
};
|
|
@@ -3496,33 +3603,33 @@ function augmentInheritedPropAccesses(ir) {
|
|
|
3496
3603
|
}
|
|
3497
3604
|
}
|
|
3498
3605
|
function parseStaticStringConst(source) {
|
|
3499
|
-
const sf =
|
|
3606
|
+
const sf = ts22.createSourceFile("__const.ts", `const __x = (${source});`, ts22.ScriptTarget.Latest, false);
|
|
3500
3607
|
const stmt = sf.statements[0];
|
|
3501
|
-
if (!stmt || !
|
|
3608
|
+
if (!stmt || !ts22.isVariableStatement(stmt))
|
|
3502
3609
|
return null;
|
|
3503
3610
|
let init = stmt.declarationList.declarations[0]?.initializer;
|
|
3504
|
-
while (init &&
|
|
3611
|
+
while (init && ts22.isParenthesizedExpression(init))
|
|
3505
3612
|
init = init.expression;
|
|
3506
3613
|
if (!init)
|
|
3507
3614
|
return null;
|
|
3508
|
-
if (
|
|
3615
|
+
if (ts22.isStringLiteral(init) || ts22.isNoSubstitutionTemplateLiteral(init)) {
|
|
3509
3616
|
return init.text;
|
|
3510
3617
|
}
|
|
3511
3618
|
return evalStringArrayJoin(source);
|
|
3512
3619
|
}
|
|
3513
3620
|
function evalTemplateOfStringConsts(source, resolved) {
|
|
3514
|
-
const sf =
|
|
3621
|
+
const sf = ts22.createSourceFile("__const.ts", `const __x = (${source});`, ts22.ScriptTarget.Latest, false);
|
|
3515
3622
|
const stmt = sf.statements[0];
|
|
3516
|
-
if (!stmt || !
|
|
3623
|
+
if (!stmt || !ts22.isVariableStatement(stmt))
|
|
3517
3624
|
return null;
|
|
3518
3625
|
let init = stmt.declarationList.declarations[0]?.initializer;
|
|
3519
|
-
while (init &&
|
|
3626
|
+
while (init && ts22.isParenthesizedExpression(init))
|
|
3520
3627
|
init = init.expression;
|
|
3521
|
-
if (!init || !
|
|
3628
|
+
if (!init || !ts22.isTemplateExpression(init))
|
|
3522
3629
|
return null;
|
|
3523
3630
|
let out = init.head.text;
|
|
3524
3631
|
for (const span of init.templateSpans) {
|
|
3525
|
-
if (!
|
|
3632
|
+
if (!ts22.isIdentifier(span.expression))
|
|
3526
3633
|
return null;
|
|
3527
3634
|
const value = resolved.get(span.expression.text);
|
|
3528
3635
|
if (value === undefined)
|
|
@@ -3553,30 +3660,30 @@ function lookupStaticRecordLiteral(objectName, key, constants) {
|
|
|
3553
3660
|
const constInfo = (constants ?? []).find((c) => c.name === objectName && c.isModule);
|
|
3554
3661
|
if (constInfo?.value === undefined)
|
|
3555
3662
|
return null;
|
|
3556
|
-
const sf =
|
|
3663
|
+
const sf = ts22.createSourceFile("__rec.ts", `(${constInfo.value})`, ts22.ScriptTarget.Latest, true);
|
|
3557
3664
|
if (sf.statements.length !== 1)
|
|
3558
3665
|
return null;
|
|
3559
3666
|
const stmt = sf.statements[0];
|
|
3560
|
-
if (!
|
|
3667
|
+
if (!ts22.isExpressionStatement(stmt))
|
|
3561
3668
|
return null;
|
|
3562
3669
|
let parsed = stmt.expression;
|
|
3563
|
-
while (
|
|
3670
|
+
while (ts22.isParenthesizedExpression(parsed))
|
|
3564
3671
|
parsed = parsed.expression;
|
|
3565
|
-
if (!
|
|
3672
|
+
if (!ts22.isObjectLiteralExpression(parsed))
|
|
3566
3673
|
return null;
|
|
3567
3674
|
for (const prop of parsed.properties) {
|
|
3568
|
-
if (!
|
|
3675
|
+
if (!ts22.isPropertyAssignment(prop))
|
|
3569
3676
|
continue;
|
|
3570
3677
|
const name = prop.name;
|
|
3571
|
-
const propKey =
|
|
3678
|
+
const propKey = ts22.isIdentifier(name) || ts22.isStringLiteral(name) || ts22.isNoSubstitutionTemplateLiteral(name) ? name.text : null;
|
|
3572
3679
|
if (propKey !== key)
|
|
3573
3680
|
continue;
|
|
3574
3681
|
let v = prop.initializer;
|
|
3575
|
-
while (
|
|
3682
|
+
while (ts22.isParenthesizedExpression(v))
|
|
3576
3683
|
v = v.expression;
|
|
3577
|
-
if (
|
|
3684
|
+
if (ts22.isNumericLiteral(v))
|
|
3578
3685
|
return { kind: "number", text: v.text };
|
|
3579
|
-
if (
|
|
3686
|
+
if (ts22.isStringLiteral(v) || ts22.isNoSubstitutionTemplateLiteral(v)) {
|
|
3580
3687
|
return { kind: "string", text: v.text };
|
|
3581
3688
|
}
|
|
3582
3689
|
return null;
|
|
@@ -3584,28 +3691,28 @@ function lookupStaticRecordLiteral(objectName, key, constants) {
|
|
|
3584
3691
|
return null;
|
|
3585
3692
|
}
|
|
3586
3693
|
function evalStringArrayJoin(source) {
|
|
3587
|
-
const sf =
|
|
3694
|
+
const sf = ts22.createSourceFile("__join.ts", `const __x = (${source});`, ts22.ScriptTarget.Latest, false);
|
|
3588
3695
|
const stmt = sf.statements[0];
|
|
3589
|
-
if (!stmt || !
|
|
3696
|
+
if (!stmt || !ts22.isVariableStatement(stmt))
|
|
3590
3697
|
return null;
|
|
3591
3698
|
let node = stmt.declarationList.declarations[0]?.initializer;
|
|
3592
|
-
while (node &&
|
|
3699
|
+
while (node && ts22.isParenthesizedExpression(node))
|
|
3593
3700
|
node = node.expression;
|
|
3594
|
-
if (!node || !
|
|
3701
|
+
if (!node || !ts22.isCallExpression(node))
|
|
3595
3702
|
return null;
|
|
3596
3703
|
const callee = node.expression;
|
|
3597
|
-
if (!
|
|
3704
|
+
if (!ts22.isPropertyAccessExpression(callee))
|
|
3598
3705
|
return null;
|
|
3599
3706
|
if (callee.name.text !== "join")
|
|
3600
3707
|
return null;
|
|
3601
3708
|
let recv = callee.expression;
|
|
3602
|
-
while (
|
|
3709
|
+
while (ts22.isParenthesizedExpression(recv))
|
|
3603
3710
|
recv = recv.expression;
|
|
3604
|
-
if (!
|
|
3711
|
+
if (!ts22.isArrayLiteralExpression(recv))
|
|
3605
3712
|
return null;
|
|
3606
3713
|
const parts = [];
|
|
3607
3714
|
for (const el of recv.elements) {
|
|
3608
|
-
if (
|
|
3715
|
+
if (ts22.isStringLiteral(el) || ts22.isNoSubstitutionTemplateLiteral(el)) {
|
|
3609
3716
|
parts.push(el.text);
|
|
3610
3717
|
} else {
|
|
3611
3718
|
return null;
|
|
@@ -3614,7 +3721,7 @@ function evalStringArrayJoin(source) {
|
|
|
3614
3721
|
let sep = ",";
|
|
3615
3722
|
if (node.arguments.length >= 1) {
|
|
3616
3723
|
const arg = node.arguments[0];
|
|
3617
|
-
if (
|
|
3724
|
+
if (ts22.isStringLiteral(arg) || ts22.isNoSubstitutionTemplateLiteral(arg))
|
|
3618
3725
|
sep = arg.text;
|
|
3619
3726
|
else
|
|
3620
3727
|
return null;
|
|
@@ -3622,11 +3729,11 @@ function evalStringArrayJoin(source) {
|
|
|
3622
3729
|
return parts.join(sep);
|
|
3623
3730
|
}
|
|
3624
3731
|
function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
|
|
3625
|
-
if (!
|
|
3732
|
+
if (!ts22.isElementAccessExpression(val))
|
|
3626
3733
|
return null;
|
|
3627
3734
|
const obj = val.expression;
|
|
3628
3735
|
const arg = val.argumentExpression;
|
|
3629
|
-
if (!
|
|
3736
|
+
if (!ts22.isIdentifier(obj) || !ts22.isIdentifier(arg))
|
|
3630
3737
|
return null;
|
|
3631
3738
|
let indexPropName;
|
|
3632
3739
|
let defaultKey;
|
|
@@ -3642,35 +3749,35 @@ function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
|
|
|
3642
3749
|
const constInfo = localConstants.find((c) => c.name === obj.text && c.isModule);
|
|
3643
3750
|
if (constInfo?.value === undefined)
|
|
3644
3751
|
return null;
|
|
3645
|
-
const sf =
|
|
3752
|
+
const sf = ts22.createSourceFile("__rec.ts", `(${constInfo.value})`, ts22.ScriptTarget.Latest, true);
|
|
3646
3753
|
if (sf.statements.length !== 1)
|
|
3647
3754
|
return null;
|
|
3648
3755
|
const stmt = sf.statements[0];
|
|
3649
|
-
if (!
|
|
3756
|
+
if (!ts22.isExpressionStatement(stmt))
|
|
3650
3757
|
return null;
|
|
3651
3758
|
let parsed = stmt.expression;
|
|
3652
|
-
while (
|
|
3759
|
+
while (ts22.isParenthesizedExpression(parsed))
|
|
3653
3760
|
parsed = parsed.expression;
|
|
3654
|
-
if (!
|
|
3761
|
+
if (!ts22.isObjectLiteralExpression(parsed))
|
|
3655
3762
|
return null;
|
|
3656
3763
|
const entries = [];
|
|
3657
3764
|
for (const prop of parsed.properties) {
|
|
3658
|
-
if (!
|
|
3765
|
+
if (!ts22.isPropertyAssignment(prop))
|
|
3659
3766
|
return null;
|
|
3660
3767
|
let key;
|
|
3661
|
-
if (
|
|
3768
|
+
if (ts22.isIdentifier(prop.name)) {
|
|
3662
3769
|
key = prop.name.text;
|
|
3663
|
-
} else if (
|
|
3770
|
+
} else if (ts22.isStringLiteral(prop.name) || ts22.isNoSubstitutionTemplateLiteral(prop.name)) {
|
|
3664
3771
|
key = prop.name.text;
|
|
3665
3772
|
} else {
|
|
3666
3773
|
return null;
|
|
3667
3774
|
}
|
|
3668
3775
|
let v = prop.initializer;
|
|
3669
|
-
while (
|
|
3776
|
+
while (ts22.isParenthesizedExpression(v))
|
|
3670
3777
|
v = v.expression;
|
|
3671
|
-
if (
|
|
3778
|
+
if (ts22.isNumericLiteral(v)) {
|
|
3672
3779
|
entries.push({ key, value: { kind: "number", text: v.text } });
|
|
3673
|
-
} else if (
|
|
3780
|
+
} else if (ts22.isStringLiteral(v) || ts22.isNoSubstitutionTemplateLiteral(v)) {
|
|
3674
3781
|
entries.push({ key, value: { kind: "string", text: v.text } });
|
|
3675
3782
|
} else {
|
|
3676
3783
|
return null;
|
|
@@ -3726,7 +3833,7 @@ function computeSsrSeedPlan(metadata) {
|
|
|
3726
3833
|
// ../jsx/src/rich-type-refusal.ts
|
|
3727
3834
|
var EMPTY_BINDINGS2 = new Map;
|
|
3728
3835
|
// ../jsx/src/shared-program.ts
|
|
3729
|
-
import
|
|
3836
|
+
import ts24 from "typescript";
|
|
3730
3837
|
// ../jsx/src/adapters/interface.ts
|
|
3731
3838
|
class BaseAdapter {
|
|
3732
3839
|
renderChildren(children) {
|
|
@@ -3802,7 +3909,7 @@ class JsxAdapter extends BaseAdapter {
|
|
|
3802
3909
|
}
|
|
3803
3910
|
const rawInitialValue = preserveTypes ? signal.typedInitialValue ?? signal.initialValue : signal.initialValue;
|
|
3804
3911
|
const initialValue = rawInitialValue.trim().startsWith("{") ? `(${rawInitialValue})` : rawInitialValue;
|
|
3805
|
-
const needsTypeAssertion = preserveTypes && !signal.typedInitialValue && signal.type.kind !== "unknown" && signal.type.kind !== "primitive";
|
|
3912
|
+
const needsTypeAssertion = preserveTypes && !signal.typedInitialValue && signal.type.kind !== "unknown" && signal.type.kind !== "primitive" && signal.type.raw !== "object";
|
|
3806
3913
|
if (needsTypeAssertion) {
|
|
3807
3914
|
lines.push(` const ${signal.getter} = () => ${initialValue} as ${signal.type.raw}`);
|
|
3808
3915
|
} else {
|
|
@@ -3821,12 +3928,16 @@ class JsxAdapter extends BaseAdapter {
|
|
|
3821
3928
|
const computation = preserveTypes ? memo.typedComputation ?? memo.computation : memo.computation;
|
|
3822
3929
|
lines.push(` const ${memo.name} = ${computation}`);
|
|
3823
3930
|
}
|
|
3931
|
+
const moduleScopeNames = this.moduleScopeDeclarationNames(ir);
|
|
3824
3932
|
for (const constant of ir.metadata.localConstants) {
|
|
3825
3933
|
if (constant.isExported)
|
|
3826
3934
|
continue;
|
|
3935
|
+
if (moduleScopeNames.has(constant.name))
|
|
3936
|
+
continue;
|
|
3827
3937
|
const keyword = constant.declarationKind ?? "const";
|
|
3828
3938
|
if (!constant.value) {
|
|
3829
|
-
|
|
3939
|
+
const typeAnnotation = preserveTypes && constant.type ? `: ${constant.type.raw}` : "";
|
|
3940
|
+
lines.push(` ${keyword} ${constant.name}${typeAnnotation}`);
|
|
3830
3941
|
continue;
|
|
3831
3942
|
}
|
|
3832
3943
|
const value = constant.value.trim();
|
|
@@ -3838,6 +3949,8 @@ class JsxAdapter extends BaseAdapter {
|
|
|
3838
3949
|
lines.push(` ${keyword} ${constant.name} = ${constValue}`);
|
|
3839
3950
|
}
|
|
3840
3951
|
for (const func of localFunctions) {
|
|
3952
|
+
if (moduleScopeNames.has(func.name))
|
|
3953
|
+
continue;
|
|
3841
3954
|
if (!reachable.has(func.name))
|
|
3842
3955
|
continue;
|
|
3843
3956
|
const params = preserveTypes && func.typedParams !== undefined ? func.typedParams : func.params.map(formatParamWithType).join(", ");
|
|
@@ -3847,6 +3960,125 @@ class JsxAdapter extends BaseAdapter {
|
|
|
3847
3960
|
lines.push(` ${asyncKw}function ${func.name}(${params})${returnAnnotation} ${body}`);
|
|
3848
3961
|
}
|
|
3849
3962
|
return lines.join(`
|
|
3963
|
+
`);
|
|
3964
|
+
}
|
|
3965
|
+
moduleScopeNamesCache = new WeakMap;
|
|
3966
|
+
moduleScopeDeclarationNames(ir) {
|
|
3967
|
+
const cached = this.moduleScopeNamesCache.get(ir);
|
|
3968
|
+
if (cached)
|
|
3969
|
+
return cached;
|
|
3970
|
+
const componentScope = new Set;
|
|
3971
|
+
for (const sig of ir.metadata.signals) {
|
|
3972
|
+
if (sig.isModule)
|
|
3973
|
+
continue;
|
|
3974
|
+
componentScope.add(sig.getter);
|
|
3975
|
+
if (sig.setter)
|
|
3976
|
+
componentScope.add(sig.setter);
|
|
3977
|
+
}
|
|
3978
|
+
for (const memo of ir.metadata.memos) {
|
|
3979
|
+
if (!memo.isModule)
|
|
3980
|
+
componentScope.add(memo.name);
|
|
3981
|
+
}
|
|
3982
|
+
for (const p of ir.metadata.propsParams)
|
|
3983
|
+
componentScope.add(p.name);
|
|
3984
|
+
if (ir.metadata.propsObjectName)
|
|
3985
|
+
componentScope.add(ir.metadata.propsObjectName);
|
|
3986
|
+
if (ir.metadata.restPropsName)
|
|
3987
|
+
componentScope.add(ir.metadata.restPropsName);
|
|
3988
|
+
for (const c of ir.metadata.localConstants) {
|
|
3989
|
+
if (!c.isModule)
|
|
3990
|
+
componentScope.add(c.name);
|
|
3991
|
+
}
|
|
3992
|
+
for (const f of ir.metadata.localFunctions) {
|
|
3993
|
+
if (!f.isModule)
|
|
3994
|
+
componentScope.add(f.name);
|
|
3995
|
+
}
|
|
3996
|
+
const exported = new Set;
|
|
3997
|
+
const candidates = new Map;
|
|
3998
|
+
for (const c of ir.metadata.localConstants) {
|
|
3999
|
+
if (!c.isModule)
|
|
4000
|
+
continue;
|
|
4001
|
+
if (c.isJsx || c.isJsxFunction)
|
|
4002
|
+
continue;
|
|
4003
|
+
if (c.isExported) {
|
|
4004
|
+
exported.add(c.name);
|
|
4005
|
+
continue;
|
|
4006
|
+
}
|
|
4007
|
+
candidates.set(c.name, c.freeIdentifiers ?? extractFreeIdentifiersFromText(c.value ?? ""));
|
|
4008
|
+
}
|
|
4009
|
+
for (const f of ir.metadata.localFunctions) {
|
|
4010
|
+
if (!f.isModule)
|
|
4011
|
+
continue;
|
|
4012
|
+
if (f.isJsxFunction || f.isMultiReturnJsxHelper)
|
|
4013
|
+
continue;
|
|
4014
|
+
if (f.isExported) {
|
|
4015
|
+
exported.add(f.name);
|
|
4016
|
+
continue;
|
|
4017
|
+
}
|
|
4018
|
+
const params = f.typedParams !== undefined ? f.typedParams : f.params.map(formatParamWithType).join(", ");
|
|
4019
|
+
candidates.set(f.name, extractFreeIdentifiersFromText(`(${params}) => ${f.body}`));
|
|
4020
|
+
}
|
|
4021
|
+
const referencesAny = (refs, names) => {
|
|
4022
|
+
for (const ref of refs) {
|
|
4023
|
+
if (names.has(ref))
|
|
4024
|
+
return true;
|
|
4025
|
+
}
|
|
4026
|
+
return false;
|
|
4027
|
+
};
|
|
4028
|
+
let changed = true;
|
|
4029
|
+
while (changed) {
|
|
4030
|
+
changed = false;
|
|
4031
|
+
for (const [name, refs] of candidates) {
|
|
4032
|
+
if (referencesAny(refs, componentScope)) {
|
|
4033
|
+
candidates.delete(name);
|
|
4034
|
+
componentScope.add(name);
|
|
4035
|
+
changed = true;
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
}
|
|
4039
|
+
const result = new Set([...exported, ...candidates.keys()]);
|
|
4040
|
+
this.moduleScopeNamesCache.set(ir, result);
|
|
4041
|
+
return result;
|
|
4042
|
+
}
|
|
4043
|
+
generateModuleScopeDeclarations(ir) {
|
|
4044
|
+
const { preserveTypes } = this.jsxConfig;
|
|
4045
|
+
const moduleNames = this.moduleScopeDeclarationNames(ir);
|
|
4046
|
+
const entries = [];
|
|
4047
|
+
for (const t of ir.metadata.typeDefinitions) {
|
|
4048
|
+
entries.push({ line: t.loc.start.line, text: t.definition });
|
|
4049
|
+
}
|
|
4050
|
+
for (const c of ir.metadata.localConstants) {
|
|
4051
|
+
if (!c.isModule || !moduleNames.has(c.name))
|
|
4052
|
+
continue;
|
|
4053
|
+
const keyword = c.declarationKind ?? "const";
|
|
4054
|
+
const exportKw = c.isExported ? "export " : "";
|
|
4055
|
+
if (!c.value) {
|
|
4056
|
+
entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name}` });
|
|
4057
|
+
continue;
|
|
4058
|
+
}
|
|
4059
|
+
const trimmed = c.value.trim();
|
|
4060
|
+
if (/^new WeakMap\b/.test(trimmed))
|
|
4061
|
+
continue;
|
|
4062
|
+
if (c.isExported && /^createContext\b/.test(trimmed))
|
|
4063
|
+
continue;
|
|
4064
|
+
const value = preserveTypes ? c.typedValue ?? c.value : c.value;
|
|
4065
|
+
entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name} = ${value}` });
|
|
4066
|
+
}
|
|
4067
|
+
for (const f of ir.metadata.localFunctions) {
|
|
4068
|
+
if (!f.isModule || !moduleNames.has(f.name))
|
|
4069
|
+
continue;
|
|
4070
|
+
const params = preserveTypes && f.typedParams !== undefined ? f.typedParams : f.params.map(formatParamWithType).join(", ");
|
|
4071
|
+
const returnAnnotation = preserveTypes && f.typedReturnType ? `: ${f.typedReturnType}` : "";
|
|
4072
|
+
const body = preserveTypes ? f.typedBody ?? f.body : f.body;
|
|
4073
|
+
const asyncKw = f.isAsync ? "async " : "";
|
|
4074
|
+
const exportKw = f.isExported ? "export " : "";
|
|
4075
|
+
entries.push({
|
|
4076
|
+
line: f.loc.start.line,
|
|
4077
|
+
text: `${exportKw}${asyncKw}function ${f.name}(${params})${returnAnnotation} ${body}`
|
|
4078
|
+
});
|
|
4079
|
+
}
|
|
4080
|
+
entries.sort((a, b) => a.line - b.line);
|
|
4081
|
+
return entries.map((e) => e.text).join(`
|
|
3850
4082
|
`);
|
|
3851
4083
|
}
|
|
3852
4084
|
renderNodeRaw(node) {
|
|
@@ -3858,6 +4090,15 @@ class JsxAdapter extends BaseAdapter {
|
|
|
3858
4090
|
}
|
|
3859
4091
|
return this.renderNode(node);
|
|
3860
4092
|
}
|
|
4093
|
+
renderTemplatePartsAsJs(parts) {
|
|
4094
|
+
return templatePartsToJsExpr(parts, { typed: this.jsxConfig.preserveTypes });
|
|
4095
|
+
}
|
|
4096
|
+
expressionValueToJs(value) {
|
|
4097
|
+
if (this.jsxConfig.preserveTypes && value.parts && value.expr === templatePartsToJsExpr(value.parts)) {
|
|
4098
|
+
return this.renderTemplatePartsAsJs(value.parts);
|
|
4099
|
+
}
|
|
4100
|
+
return value.expr;
|
|
4101
|
+
}
|
|
3861
4102
|
renderScopeMarker(instanceIdExpr) {
|
|
3862
4103
|
return `${BF_SCOPE}={${instanceIdExpr}}`;
|
|
3863
4104
|
}
|
|
@@ -3925,6 +4166,7 @@ class TestAdapter extends JsxAdapter {
|
|
|
3925
4166
|
generate(ir) {
|
|
3926
4167
|
this.componentName = ir.metadata.componentName;
|
|
3927
4168
|
const imports = this.generateImports(ir);
|
|
4169
|
+
const moduleConstants = this.generateModuleScopeDeclarations(ir);
|
|
3928
4170
|
const types = this.generateTypes(ir);
|
|
3929
4171
|
const component = this.generateComponent(ir);
|
|
3930
4172
|
const defaultExport = ir.metadata.hasDefaultExport ? `
|
|
@@ -3933,9 +4175,11 @@ export default ${this.componentName}` : "";
|
|
|
3933
4175
|
imports,
|
|
3934
4176
|
types: types || "",
|
|
3935
4177
|
component,
|
|
3936
|
-
defaultExport
|
|
4178
|
+
defaultExport,
|
|
4179
|
+
moduleConstants,
|
|
4180
|
+
moduleConstantsIncludeExports: true
|
|
3937
4181
|
};
|
|
3938
|
-
const template = [imports, types, component].filter(Boolean).join(`
|
|
4182
|
+
const template = [imports, moduleConstants, types, component].filter(Boolean).join(`
|
|
3939
4183
|
|
|
3940
4184
|
`) + defaultExport;
|
|
3941
4185
|
return {
|
|
@@ -3966,9 +4210,6 @@ export default ${this.componentName}` : "";
|
|
|
3966
4210
|
}
|
|
3967
4211
|
generateTypes(ir) {
|
|
3968
4212
|
const lines = [];
|
|
3969
|
-
for (const typeDef of ir.metadata.typeDefinitions) {
|
|
3970
|
-
lines.push(typeDef.definition);
|
|
3971
|
-
}
|
|
3972
4213
|
const propsTypeName = ir.metadata.propsType?.raw;
|
|
3973
4214
|
if (propsTypeName && !ir.metadata.propsObjectName) {
|
|
3974
4215
|
lines.push("");
|
|
@@ -3991,7 +4232,7 @@ export default ${this.componentName}` : "";
|
|
|
3991
4232
|
const bodyRefText = [jsxBody, signalInits, scopeIdLine].join(`
|
|
3992
4233
|
`);
|
|
3993
4234
|
const bfScopeAlias = /\b__bfScope\b/.test(bodyRefText) ? "__bfScope" : "__bfScope: _bfScope";
|
|
3994
|
-
const propsParams = ir.metadata.propsParams.map((p) => p
|
|
4235
|
+
const propsParams = ir.metadata.propsParams.map((p) => propsDestructureBinding(p)).join(", ");
|
|
3995
4236
|
const restPropsName = ir.metadata.restPropsName;
|
|
3996
4237
|
const hydrationProps = `__instanceId, ${bfScopeAlias}`;
|
|
3997
4238
|
const parts = [];
|
|
@@ -4138,13 +4379,7 @@ export default ${this.componentName}` : "";
|
|
|
4138
4379
|
}
|
|
4139
4380
|
flattenTemplate(value) {
|
|
4140
4381
|
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("") + "`";
|
|
4382
|
+
return this.renderTemplatePartsAsJs(v.parts);
|
|
4148
4383
|
}
|
|
4149
4384
|
renderComponentProps(comp) {
|
|
4150
4385
|
const parts = [];
|
|
@@ -4618,7 +4853,7 @@ function dangerousInnerHtmlDiagnostic(expr, loc, reason) {
|
|
|
4618
4853
|
};
|
|
4619
4854
|
}
|
|
4620
4855
|
// ../jsx/src/combine-client-js.ts
|
|
4621
|
-
import
|
|
4856
|
+
import ts25 from "typescript";
|
|
4622
4857
|
// ../jsx/src/loop-destructure.ts
|
|
4623
4858
|
function isLowerableLoopDestructure(loop) {
|
|
4624
4859
|
const bindings = loop.paramBindings;
|
|
@@ -4758,9 +4993,9 @@ function escapeRe(s) {
|
|
|
4758
4993
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4759
4994
|
}
|
|
4760
4995
|
// ../jsx/src/debug.ts
|
|
4761
|
-
import
|
|
4996
|
+
import ts26 from "typescript";
|
|
4762
4997
|
// ../jsx/src/profiler.ts
|
|
4763
|
-
import
|
|
4998
|
+
import ts27 from "typescript";
|
|
4764
4999
|
|
|
4765
5000
|
// ../jsx/src/index.ts
|
|
4766
5001
|
registerBuiltinLoweringPlugins();
|
|
@@ -5586,7 +5821,7 @@ function collectImportedLoopChildComponentErrors(ir, componentName) {
|
|
|
5586
5821
|
}
|
|
5587
5822
|
|
|
5588
5823
|
// src/adapter/spread/spread-codegen.ts
|
|
5589
|
-
import
|
|
5824
|
+
import ts28 from "typescript";
|
|
5590
5825
|
function conditionalSpreadToJinja(ctx, expr) {
|
|
5591
5826
|
if (!expr || expr.kind !== "conditional")
|
|
5592
5827
|
return null;
|
|
@@ -5641,7 +5876,7 @@ function recordIndexAccessToJinja(ctx, val) {
|
|
|
5641
5876
|
if (val.kind !== "index-access" || val.object.kind !== "identifier" || val.index.kind !== "identifier") {
|
|
5642
5877
|
return null;
|
|
5643
5878
|
}
|
|
5644
|
-
const tsVal =
|
|
5879
|
+
const tsVal = ts28.factory.createElementAccessExpression(ts28.factory.createIdentifier(val.object.name), ts28.factory.createIdentifier(val.index.name));
|
|
5645
5880
|
const parsed = parseRecordIndexAccess(tsVal, ctx.localConstants ?? [], ctx.propsParams);
|
|
5646
5881
|
if (!parsed)
|
|
5647
5882
|
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.2",
|
|
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.2"
|
|
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.2",
|
|
74
|
+
"@barefootjs/vite": "0.31.2",
|
|
75
|
+
"@barefootjs/client": "0.31.2",
|
|
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
|
|