@barefootjs/erb 0.31.0 → 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 CHANGED
@@ -189284,9 +189284,7 @@ var conformancePins = {
189284
189284
  "date-method-uncatalogued": [{ code: "BF021", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2356" }]
189285
189285
  };
189286
189286
  // src/render-divergences.ts
189287
- var renderDivergences = {
189288
- "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)"
189289
- };
189287
+ var renderDivergences = {};
189290
189288
  export {
189291
189289
  renderDivergences,
189292
189290
  erbAdapter,
@@ -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,iBAoB/B,CAAA"}
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 ts8 from "typescript";
11
+ import ts9 from "typescript";
9
12
 
10
13
  // ../jsx/src/expression-parser.ts
11
14
  import ts from "typescript";
@@ -1922,6 +1925,29 @@ import ts5 from "typescript";
1922
1925
  // ../jsx/src/ir-to-client-js/utils.ts
1923
1926
  import ts3 from "typescript";
1924
1927
 
1928
+ // ../jsx/src/template-parts.ts
1929
+ function lookupPartToJsExpr(part, opts) {
1930
+ const key = opts?.useTemplate && part.templateKey ? part.templateKey : part.key;
1931
+ const obj = "{" + Object.entries(part.cases).map(([k, v]) => `${JSON.stringify(k)}: ${JSON.stringify(v)}`).join(", ") + "}";
1932
+ const typed = opts?.typed ? " as Record<string, string>" : "";
1933
+ return `(${obj}${typed})[${key}]`;
1934
+ }
1935
+ function templatePartsToJsExpr(parts, opts) {
1936
+ let result = "`";
1937
+ for (const part of parts) {
1938
+ if (part.type === "string") {
1939
+ result += opts?.useTemplate && part.templateValue ? part.templateValue : part.value;
1940
+ } else if (part.type === "ternary") {
1941
+ const cond = opts?.useTemplate && part.templateCondition ? part.templateCondition : part.condition;
1942
+ result += `\${${cond} ? '${part.whenTrue}' : '${part.whenFalse}'}`;
1943
+ } else if (part.type === "lookup") {
1944
+ result += `\${${lookupPartToJsExpr(part, opts)}}`;
1945
+ }
1946
+ }
1947
+ result += "`";
1948
+ return result;
1949
+ }
1950
+
1925
1951
  // ../jsx/src/scanner/js-scanner.ts
1926
1952
  import ts2 from "typescript";
1927
1953
 
@@ -2023,6 +2049,16 @@ function escapeHtml(text) {
2023
2049
  }
2024
2050
  // ../jsx/src/ir-to-client-js/csr-substitute.ts
2025
2051
  import ts4 from "typescript";
2052
+ function extractFreeIdentifiersFromText(text) {
2053
+ if (!text || text.trim().length === 0)
2054
+ return new Set;
2055
+ const sf = ts4.createSourceFile("__free_ids__.ts", `(${text});`, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TS);
2056
+ const stmt = sf.statements[0];
2057
+ if (!stmt || !ts4.isExpressionStatement(stmt))
2058
+ return new Set;
2059
+ const expr = ts4.isParenthesizedExpression(stmt.expression) ? stmt.expression.expression : stmt.expression;
2060
+ return extractFreeIdentifiersFromNode(expr);
2061
+ }
2026
2062
 
2027
2063
  // ../jsx/src/adapters/child-scope.ts
2028
2064
  function derivesScopeFromSlot(comp) {
@@ -2073,6 +2109,27 @@ var SKELETON_PATH_FORCE_CLOSE_GROUPS = [
2073
2109
  new Set(["li"])
2074
2110
  ];
2075
2111
 
2112
+ // ../jsx/src/props-binding.ts
2113
+ import ts6 from "typescript";
2114
+ function isIdentifierName(key) {
2115
+ if (key.length === 0)
2116
+ return false;
2117
+ for (let i = 0;i < key.length; ) {
2118
+ const cp = key.codePointAt(i);
2119
+ const ok = i === 0 ? ts6.isIdentifierStart(cp, ts6.ScriptTarget.Latest) : ts6.isIdentifierPart(cp, ts6.ScriptTarget.Latest);
2120
+ if (!ok)
2121
+ return false;
2122
+ i += cp > 65535 ? 2 : 1;
2123
+ }
2124
+ return true;
2125
+ }
2126
+ function propsDestructureBinding(p) {
2127
+ const callerKey = p.sourceName ?? p.name;
2128
+ const localName = p.name;
2129
+ const binding = callerKey === localName ? localName : `${isIdentifierName(callerKey) ? callerKey : JSON.stringify(callerKey)}: ${localName}`;
2130
+ return p.defaultValue ? `${binding} = ${p.defaultValue}` : binding;
2131
+ }
2132
+
2076
2133
  // ../jsx/src/instrumentation.ts
2077
2134
  var _counters = freshCounters();
2078
2135
  function freshCounters() {
@@ -2086,14 +2143,14 @@ function freshCounters() {
2086
2143
  }
2087
2144
 
2088
2145
  // ../jsx/src/analyzer-context.ts
2089
- import ts7 from "typescript";
2146
+ import ts8 from "typescript";
2090
2147
 
2091
2148
  // ../jsx/src/strip-types.ts
2092
- import ts6 from "typescript";
2149
+ import ts7 from "typescript";
2093
2150
 
2094
2151
  // ../jsx/src/analyzer-context.ts
2095
- var _typePrinter = ts7.createPrinter({ removeComments: true, omitTrailingSemicolon: true });
2096
- var _blankTypeSourceFile = ts7.createSourceFile("__bf_types__.ts", "", ts7.ScriptTarget.Latest);
2152
+ var _typePrinter = ts8.createPrinter({ removeComments: true, omitTrailingSemicolon: true });
2153
+ var _blankTypeSourceFile = ts8.createSourceFile("__bf_types__.ts", "", ts8.ScriptTarget.Latest);
2097
2154
 
2098
2155
  // ../jsx/src/errors.ts
2099
2156
  var ErrorCodes = {
@@ -2109,6 +2166,7 @@ var ErrorCodes = {
2109
2166
  JSX_IN_LOCAL_FUNCTION: "BF045",
2110
2167
  COMPONENT_REQUIRED_PROP_MISSING: "BF046",
2111
2168
  JSX_BRANCH_LOCAL_IN_CALLBACK: "BF047",
2169
+ SIBLING_COMPONENT_NOT_COMPILED: "BF048",
2112
2170
  SHARED_PROGRAM_REQUIRED: "BF050",
2113
2171
  WRONG_PACKAGE_IMPORT: "BF051",
2114
2172
  BUILTIN_REQUIRES_IMPORT: "BF054",
@@ -2138,6 +2196,7 @@ var errorMessages = {
2138
2196
  [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.",
2139
2197
  [ErrorCodes.COMPONENT_REQUIRED_PROP_MISSING]: "Built-in component is missing a required prop.",
2140
2198
  [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>`.",
2199
+ [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.",
2141
2200
  [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.",
2142
2201
  [ErrorCodes.WRONG_PACKAGE_IMPORT]: "Import from wrong package.",
2143
2202
  [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.",
@@ -2311,6 +2370,54 @@ var CLIENT_EXPORTS = new Set([
2311
2370
  "Async",
2312
2371
  "Region"
2313
2372
  ]);
2373
+ function extractFreeIdentifiersFromNode(node) {
2374
+ const ids = new Set;
2375
+ const boundNames = new Set;
2376
+ function addBindingNames(name, out) {
2377
+ if (ts9.isIdentifier(name))
2378
+ out.push(name.text);
2379
+ else if (ts9.isObjectBindingPattern(name))
2380
+ name.elements.forEach((e) => addBindingNames(e.name, out));
2381
+ else if (ts9.isArrayBindingPattern(name))
2382
+ name.elements.forEach((e) => {
2383
+ if (!ts9.isOmittedExpression(e))
2384
+ addBindingNames(e.name, out);
2385
+ });
2386
+ }
2387
+ function visit(n) {
2388
+ if (ts9.isTypeNode(n))
2389
+ return;
2390
+ if (ts9.isIdentifier(n)) {
2391
+ const parent = n.parent;
2392
+ if (parent && ts9.isPropertyAccessExpression(parent) && parent.name === n)
2393
+ return;
2394
+ if (parent && ts9.isPropertyAssignment(parent) && parent.name === n)
2395
+ return;
2396
+ if (parent && ts9.isParameter(parent) && parent.name === n)
2397
+ return;
2398
+ if (parent && ts9.isVariableDeclaration(parent) && parent.name === n)
2399
+ return;
2400
+ if (boundNames.has(n.text))
2401
+ return;
2402
+ ids.add(n.text);
2403
+ return;
2404
+ }
2405
+ if (ts9.isArrowFunction(n)) {
2406
+ const params = [];
2407
+ for (const p of n.parameters)
2408
+ addBindingNames(p.name, params);
2409
+ for (const name of params)
2410
+ boundNames.add(name);
2411
+ ts9.forEachChild(n, visit);
2412
+ for (const name of params)
2413
+ boundNames.delete(name);
2414
+ return;
2415
+ }
2416
+ ts9.forEachChild(n, visit);
2417
+ }
2418
+ visit(node);
2419
+ return ids;
2420
+ }
2314
2421
  var BROWSER_ONLY_CLIENT_APIS = new Set([
2315
2422
  "useContext",
2316
2423
  "provideContext",
@@ -2329,7 +2436,7 @@ var REACTIVE_PRIMITIVES = new Set([
2329
2436
  ]);
2330
2437
 
2331
2438
  // ../jsx/src/jsx-to-ir.ts
2332
- import ts11 from "typescript";
2439
+ import ts12 from "typescript";
2333
2440
 
2334
2441
  // ../jsx/src/types.ts
2335
2442
  var SCOPE_FORBIDDEN = {
@@ -2411,10 +2518,10 @@ function findReachableNames(primaryRefs, declarations) {
2411
2518
  }
2412
2519
 
2413
2520
  // ../jsx/src/reactivity-checker.ts
2414
- import ts9 from "typescript";
2521
+ import ts10 from "typescript";
2415
2522
 
2416
2523
  // ../jsx/src/free-refs.ts
2417
- import ts10 from "typescript";
2524
+ import ts11 from "typescript";
2418
2525
  var _bindingMapCache = new WeakMap;
2419
2526
 
2420
2527
  // ../jsx/src/to-locale-date-lowering.ts
@@ -2828,13 +2935,13 @@ var KEYWORDS_AND_GLOBALS = new Set([
2828
2935
  ]);
2829
2936
 
2830
2937
  // ../jsx/src/ir-to-client-js/walk-prop-accesses.ts
2831
- import ts12 from "typescript";
2938
+ import ts13 from "typescript";
2832
2939
 
2833
2940
  // ../jsx/src/value-references.ts
2834
- import ts13 from "typescript";
2941
+ import ts14 from "typescript";
2835
2942
 
2836
2943
  // ../jsx/src/relocate.ts
2837
- import ts14 from "typescript";
2944
+ import ts15 from "typescript";
2838
2945
 
2839
2946
  // ../jsx/src/lowering-registry.ts
2840
2947
  var plugins = [];
@@ -3051,10 +3158,10 @@ function matchSearchParamsMethodCall(callee, args, localNames) {
3051
3158
  }
3052
3159
 
3053
3160
  // ../jsx/src/ir-to-client-js/prune-unused-prop-extractions.ts
3054
- import ts15 from "typescript";
3161
+ import ts16 from "typescript";
3055
3162
 
3056
3163
  // ../jsx/src/ir-to-client-js/control-flow/plan/lazy-preamble.ts
3057
- import ts16 from "typescript";
3164
+ import ts17 from "typescript";
3058
3165
  var NO_PREAMBLE = {
3059
3166
  lazySafe: true,
3060
3167
  facts: { declaredNames: new Set, freeNames: new Set }
@@ -3104,7 +3211,7 @@ var INERT_BINDING_GLOBALS = new Set([
3104
3211
  ]);
3105
3212
 
3106
3213
  // ../jsx/src/ir-to-client-js/emit-reactive.ts
3107
- import ts17 from "typescript";
3214
+ import ts18 from "typescript";
3108
3215
 
3109
3216
  // ../jsx/src/ir-to-client-js/control-flow/stringify/event-delegation.ts
3110
3217
  var NON_BUBBLING_EVENTS = new Set([
@@ -3119,7 +3226,7 @@ var NON_BUBBLING_EVENTS = new Set([
3119
3226
  ]);
3120
3227
 
3121
3228
  // ../jsx/src/ir-to-client-js/rewrite-props-object.ts
3122
- import ts18 from "typescript";
3229
+ import ts19 from "typescript";
3123
3230
 
3124
3231
  // ../jsx/src/ir-to-client-js/source-map.ts
3125
3232
  var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -3210,15 +3317,15 @@ class SourceMapGenerator {
3210
3317
  }
3211
3318
 
3212
3319
  // ../jsx/src/preprocess-inline-jsx-callbacks.ts
3213
- import ts19 from "typescript";
3320
+ import ts20 from "typescript";
3214
3321
 
3215
3322
  // ../jsx/src/ssr-defaults.ts
3216
- import ts20 from "typescript";
3323
+ import ts21 from "typescript";
3217
3324
  var UNRESOLVED = Symbol("unresolved");
3218
3325
  var NO_RETURN = Symbol("no-return");
3219
3326
 
3220
3327
  // ../jsx/src/augment-inherited-props.ts
3221
- import ts21 from "typescript";
3328
+ import ts22 from "typescript";
3222
3329
  function collectContextConsumers(metadata) {
3223
3330
  const constants = metadata.localConstants ?? [];
3224
3331
  const contextDefaults = new Map;
@@ -3250,47 +3357,47 @@ function collectContextConsumers(metadata) {
3250
3357
  }
3251
3358
  function parseUseContextArg(source) {
3252
3359
  const expr = parseSingleExpression(source);
3253
- if (!expr || !ts21.isCallExpression(expr))
3360
+ if (!expr || !ts22.isCallExpression(expr))
3254
3361
  return null;
3255
- if (!ts21.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
3362
+ if (!ts22.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
3256
3363
  return null;
3257
3364
  if (expr.arguments.length !== 1)
3258
3365
  return null;
3259
3366
  const arg = expr.arguments[0];
3260
- return ts21.isIdentifier(arg) ? arg.text : null;
3367
+ return ts22.isIdentifier(arg) ? arg.text : null;
3261
3368
  }
3262
3369
  function parseCreateContextDefault(source) {
3263
3370
  const expr = parseSingleExpression(source);
3264
- if (!expr || !ts21.isCallExpression(expr))
3371
+ if (!expr || !ts22.isCallExpression(expr))
3265
3372
  return null;
3266
3373
  if (expr.arguments.length === 0)
3267
3374
  return null;
3268
3375
  const arg = expr.arguments[0];
3269
- if (ts21.isStringLiteral(arg) || ts21.isNoSubstitutionTemplateLiteral(arg))
3376
+ if (ts22.isStringLiteral(arg) || ts22.isNoSubstitutionTemplateLiteral(arg))
3270
3377
  return arg.text;
3271
- if (ts21.isNumericLiteral(arg))
3378
+ if (ts22.isNumericLiteral(arg))
3272
3379
  return Number(arg.text);
3273
- if (arg.kind === ts21.SyntaxKind.TrueKeyword)
3380
+ if (arg.kind === ts22.SyntaxKind.TrueKeyword)
3274
3381
  return true;
3275
- if (arg.kind === ts21.SyntaxKind.FalseKeyword)
3382
+ if (arg.kind === ts22.SyntaxKind.FalseKeyword)
3276
3383
  return false;
3277
3384
  return null;
3278
3385
  }
3279
3386
  function isObjectLiteralCreateContextDefault(source) {
3280
3387
  const expr = parseSingleExpression(source);
3281
- if (!expr || !ts21.isCallExpression(expr))
3388
+ if (!expr || !ts22.isCallExpression(expr))
3282
3389
  return false;
3283
3390
  if (expr.arguments.length === 0)
3284
3391
  return false;
3285
- return ts21.isObjectLiteralExpression(expr.arguments[0]);
3392
+ return ts22.isObjectLiteralExpression(expr.arguments[0]);
3286
3393
  }
3287
3394
  function parseSingleExpression(source) {
3288
- const sf = ts21.createSourceFile("__ctx.ts", `(${source})`, ts21.ScriptTarget.Latest, false);
3395
+ const sf = ts22.createSourceFile("__ctx.ts", `(${source})`, ts22.ScriptTarget.Latest, false);
3289
3396
  const stmt = sf.statements[0];
3290
- if (!stmt || !ts21.isExpressionStatement(stmt))
3397
+ if (!stmt || !ts22.isExpressionStatement(stmt))
3291
3398
  return null;
3292
3399
  let e = stmt.expression;
3293
- while (ts21.isParenthesizedExpression(e))
3400
+ while (ts22.isParenthesizedExpression(e))
3294
3401
  e = e.expression;
3295
3402
  return e;
3296
3403
  }
@@ -3315,25 +3422,25 @@ function augmentInheritedPropAccesses(ir) {
3315
3422
  const pinCoalesceLiterals = (s) => {
3316
3423
  if (!s || !s.includes(propsObj))
3317
3424
  return;
3318
- const sf = ts21.createSourceFile("__aug.ts", `(${s})`, ts21.ScriptTarget.Latest, false);
3425
+ const sf = ts22.createSourceFile("__aug.ts", `(${s})`, ts22.ScriptTarget.Latest, false);
3319
3426
  const visit = (n) => {
3320
- if (ts21.isBinaryExpression(n) && (n.operatorToken.kind === ts21.SyntaxKind.QuestionQuestionToken || n.operatorToken.kind === ts21.SyntaxKind.BarBarToken)) {
3427
+ if (ts22.isBinaryExpression(n) && (n.operatorToken.kind === ts22.SyntaxKind.QuestionQuestionToken || n.operatorToken.kind === ts22.SyntaxKind.BarBarToken)) {
3321
3428
  let left = n.left;
3322
- while (ts21.isParenthesizedExpression(left))
3429
+ while (ts22.isParenthesizedExpression(left))
3323
3430
  left = left.expression;
3324
- if (ts21.isPropertyAccessExpression(left) && ts21.isIdentifier(left.expression) && left.expression.text === propsObj) {
3431
+ if (ts22.isPropertyAccessExpression(left) && ts22.isIdentifier(left.expression) && left.expression.text === propsObj) {
3325
3432
  const name = left.name.text;
3326
3433
  let right = n.right;
3327
- while (ts21.isParenthesizedExpression(right))
3434
+ while (ts22.isParenthesizedExpression(right))
3328
3435
  right = right.expression;
3329
- if (ts21.isPrefixUnaryExpression(right))
3436
+ if (ts22.isPrefixUnaryExpression(right))
3330
3437
  right = right.operand;
3331
- const kind = ts21.isNumericLiteral(right) ? "number" : right.kind === ts21.SyntaxKind.TrueKeyword || right.kind === ts21.SyntaxKind.FalseKeyword ? "boolean" : ts21.isStringLiteralLike(right) ? "string" : null;
3438
+ const kind = ts22.isNumericLiteral(right) ? "number" : right.kind === ts22.SyntaxKind.TrueKeyword || right.kind === ts22.SyntaxKind.FalseKeyword ? "boolean" : ts22.isStringLiteralLike(right) ? "string" : null;
3332
3439
  if (kind && !coalesceLiteralTypes.has(name))
3333
3440
  coalesceLiteralTypes.set(name, kind);
3334
3441
  }
3335
3442
  }
3336
- ts21.forEachChild(n, visit);
3443
+ ts22.forEachChild(n, visit);
3337
3444
  };
3338
3445
  visit(sf);
3339
3446
  };
@@ -3444,33 +3551,33 @@ function augmentInheritedPropAccesses(ir) {
3444
3551
  }
3445
3552
  }
3446
3553
  function parseStaticStringConst(source) {
3447
- const sf = ts21.createSourceFile("__const.ts", `const __x = (${source});`, ts21.ScriptTarget.Latest, false);
3554
+ const sf = ts22.createSourceFile("__const.ts", `const __x = (${source});`, ts22.ScriptTarget.Latest, false);
3448
3555
  const stmt = sf.statements[0];
3449
- if (!stmt || !ts21.isVariableStatement(stmt))
3556
+ if (!stmt || !ts22.isVariableStatement(stmt))
3450
3557
  return null;
3451
3558
  let init = stmt.declarationList.declarations[0]?.initializer;
3452
- while (init && ts21.isParenthesizedExpression(init))
3559
+ while (init && ts22.isParenthesizedExpression(init))
3453
3560
  init = init.expression;
3454
3561
  if (!init)
3455
3562
  return null;
3456
- if (ts21.isStringLiteral(init) || ts21.isNoSubstitutionTemplateLiteral(init)) {
3563
+ if (ts22.isStringLiteral(init) || ts22.isNoSubstitutionTemplateLiteral(init)) {
3457
3564
  return init.text;
3458
3565
  }
3459
3566
  return evalStringArrayJoin(source);
3460
3567
  }
3461
3568
  function evalTemplateOfStringConsts(source, resolved) {
3462
- const sf = ts21.createSourceFile("__const.ts", `const __x = (${source});`, ts21.ScriptTarget.Latest, false);
3569
+ const sf = ts22.createSourceFile("__const.ts", `const __x = (${source});`, ts22.ScriptTarget.Latest, false);
3463
3570
  const stmt = sf.statements[0];
3464
- if (!stmt || !ts21.isVariableStatement(stmt))
3571
+ if (!stmt || !ts22.isVariableStatement(stmt))
3465
3572
  return null;
3466
3573
  let init = stmt.declarationList.declarations[0]?.initializer;
3467
- while (init && ts21.isParenthesizedExpression(init))
3574
+ while (init && ts22.isParenthesizedExpression(init))
3468
3575
  init = init.expression;
3469
- if (!init || !ts21.isTemplateExpression(init))
3576
+ if (!init || !ts22.isTemplateExpression(init))
3470
3577
  return null;
3471
3578
  let out = init.head.text;
3472
3579
  for (const span of init.templateSpans) {
3473
- if (!ts21.isIdentifier(span.expression))
3580
+ if (!ts22.isIdentifier(span.expression))
3474
3581
  return null;
3475
3582
  const value = resolved.get(span.expression.text);
3476
3583
  if (value === undefined)
@@ -3501,30 +3608,30 @@ function lookupStaticRecordLiteral(objectName, key, constants) {
3501
3608
  const constInfo = (constants ?? []).find((c) => c.name === objectName && c.isModule);
3502
3609
  if (constInfo?.value === undefined)
3503
3610
  return null;
3504
- const sf = ts21.createSourceFile("__rec.ts", `(${constInfo.value})`, ts21.ScriptTarget.Latest, true);
3611
+ const sf = ts22.createSourceFile("__rec.ts", `(${constInfo.value})`, ts22.ScriptTarget.Latest, true);
3505
3612
  if (sf.statements.length !== 1)
3506
3613
  return null;
3507
3614
  const stmt = sf.statements[0];
3508
- if (!ts21.isExpressionStatement(stmt))
3615
+ if (!ts22.isExpressionStatement(stmt))
3509
3616
  return null;
3510
3617
  let parsed = stmt.expression;
3511
- while (ts21.isParenthesizedExpression(parsed))
3618
+ while (ts22.isParenthesizedExpression(parsed))
3512
3619
  parsed = parsed.expression;
3513
- if (!ts21.isObjectLiteralExpression(parsed))
3620
+ if (!ts22.isObjectLiteralExpression(parsed))
3514
3621
  return null;
3515
3622
  for (const prop of parsed.properties) {
3516
- if (!ts21.isPropertyAssignment(prop))
3623
+ if (!ts22.isPropertyAssignment(prop))
3517
3624
  continue;
3518
3625
  const name = prop.name;
3519
- const propKey = ts21.isIdentifier(name) || ts21.isStringLiteral(name) || ts21.isNoSubstitutionTemplateLiteral(name) ? name.text : null;
3626
+ const propKey = ts22.isIdentifier(name) || ts22.isStringLiteral(name) || ts22.isNoSubstitutionTemplateLiteral(name) ? name.text : null;
3520
3627
  if (propKey !== key)
3521
3628
  continue;
3522
3629
  let v = prop.initializer;
3523
- while (ts21.isParenthesizedExpression(v))
3630
+ while (ts22.isParenthesizedExpression(v))
3524
3631
  v = v.expression;
3525
- if (ts21.isNumericLiteral(v))
3632
+ if (ts22.isNumericLiteral(v))
3526
3633
  return { kind: "number", text: v.text };
3527
- if (ts21.isStringLiteral(v) || ts21.isNoSubstitutionTemplateLiteral(v)) {
3634
+ if (ts22.isStringLiteral(v) || ts22.isNoSubstitutionTemplateLiteral(v)) {
3528
3635
  return { kind: "string", text: v.text };
3529
3636
  }
3530
3637
  return null;
@@ -3532,28 +3639,28 @@ function lookupStaticRecordLiteral(objectName, key, constants) {
3532
3639
  return null;
3533
3640
  }
3534
3641
  function evalStringArrayJoin(source) {
3535
- const sf = ts21.createSourceFile("__join.ts", `const __x = (${source});`, ts21.ScriptTarget.Latest, false);
3642
+ const sf = ts22.createSourceFile("__join.ts", `const __x = (${source});`, ts22.ScriptTarget.Latest, false);
3536
3643
  const stmt = sf.statements[0];
3537
- if (!stmt || !ts21.isVariableStatement(stmt))
3644
+ if (!stmt || !ts22.isVariableStatement(stmt))
3538
3645
  return null;
3539
3646
  let node = stmt.declarationList.declarations[0]?.initializer;
3540
- while (node && ts21.isParenthesizedExpression(node))
3647
+ while (node && ts22.isParenthesizedExpression(node))
3541
3648
  node = node.expression;
3542
- if (!node || !ts21.isCallExpression(node))
3649
+ if (!node || !ts22.isCallExpression(node))
3543
3650
  return null;
3544
3651
  const callee = node.expression;
3545
- if (!ts21.isPropertyAccessExpression(callee))
3652
+ if (!ts22.isPropertyAccessExpression(callee))
3546
3653
  return null;
3547
3654
  if (callee.name.text !== "join")
3548
3655
  return null;
3549
3656
  let recv = callee.expression;
3550
- while (ts21.isParenthesizedExpression(recv))
3657
+ while (ts22.isParenthesizedExpression(recv))
3551
3658
  recv = recv.expression;
3552
- if (!ts21.isArrayLiteralExpression(recv))
3659
+ if (!ts22.isArrayLiteralExpression(recv))
3553
3660
  return null;
3554
3661
  const parts = [];
3555
3662
  for (const el of recv.elements) {
3556
- if (ts21.isStringLiteral(el) || ts21.isNoSubstitutionTemplateLiteral(el)) {
3663
+ if (ts22.isStringLiteral(el) || ts22.isNoSubstitutionTemplateLiteral(el)) {
3557
3664
  parts.push(el.text);
3558
3665
  } else {
3559
3666
  return null;
@@ -3562,7 +3669,7 @@ function evalStringArrayJoin(source) {
3562
3669
  let sep = ",";
3563
3670
  if (node.arguments.length >= 1) {
3564
3671
  const arg = node.arguments[0];
3565
- if (ts21.isStringLiteral(arg) || ts21.isNoSubstitutionTemplateLiteral(arg))
3672
+ if (ts22.isStringLiteral(arg) || ts22.isNoSubstitutionTemplateLiteral(arg))
3566
3673
  sep = arg.text;
3567
3674
  else
3568
3675
  return null;
@@ -3570,11 +3677,11 @@ function evalStringArrayJoin(source) {
3570
3677
  return parts.join(sep);
3571
3678
  }
3572
3679
  function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
3573
- if (!ts21.isElementAccessExpression(val))
3680
+ if (!ts22.isElementAccessExpression(val))
3574
3681
  return null;
3575
3682
  const obj = val.expression;
3576
3683
  const arg = val.argumentExpression;
3577
- if (!ts21.isIdentifier(obj) || !ts21.isIdentifier(arg))
3684
+ if (!ts22.isIdentifier(obj) || !ts22.isIdentifier(arg))
3578
3685
  return null;
3579
3686
  let indexPropName;
3580
3687
  let defaultKey;
@@ -3590,35 +3697,35 @@ function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
3590
3697
  const constInfo = localConstants.find((c) => c.name === obj.text && c.isModule);
3591
3698
  if (constInfo?.value === undefined)
3592
3699
  return null;
3593
- const sf = ts21.createSourceFile("__rec.ts", `(${constInfo.value})`, ts21.ScriptTarget.Latest, true);
3700
+ const sf = ts22.createSourceFile("__rec.ts", `(${constInfo.value})`, ts22.ScriptTarget.Latest, true);
3594
3701
  if (sf.statements.length !== 1)
3595
3702
  return null;
3596
3703
  const stmt = sf.statements[0];
3597
- if (!ts21.isExpressionStatement(stmt))
3704
+ if (!ts22.isExpressionStatement(stmt))
3598
3705
  return null;
3599
3706
  let parsed = stmt.expression;
3600
- while (ts21.isParenthesizedExpression(parsed))
3707
+ while (ts22.isParenthesizedExpression(parsed))
3601
3708
  parsed = parsed.expression;
3602
- if (!ts21.isObjectLiteralExpression(parsed))
3709
+ if (!ts22.isObjectLiteralExpression(parsed))
3603
3710
  return null;
3604
3711
  const entries = [];
3605
3712
  for (const prop of parsed.properties) {
3606
- if (!ts21.isPropertyAssignment(prop))
3713
+ if (!ts22.isPropertyAssignment(prop))
3607
3714
  return null;
3608
3715
  let key;
3609
- if (ts21.isIdentifier(prop.name)) {
3716
+ if (ts22.isIdentifier(prop.name)) {
3610
3717
  key = prop.name.text;
3611
- } else if (ts21.isStringLiteral(prop.name) || ts21.isNoSubstitutionTemplateLiteral(prop.name)) {
3718
+ } else if (ts22.isStringLiteral(prop.name) || ts22.isNoSubstitutionTemplateLiteral(prop.name)) {
3612
3719
  key = prop.name.text;
3613
3720
  } else {
3614
3721
  return null;
3615
3722
  }
3616
3723
  let v = prop.initializer;
3617
- while (ts21.isParenthesizedExpression(v))
3724
+ while (ts22.isParenthesizedExpression(v))
3618
3725
  v = v.expression;
3619
- if (ts21.isNumericLiteral(v)) {
3726
+ if (ts22.isNumericLiteral(v)) {
3620
3727
  entries.push({ key, value: { kind: "number", text: v.text } });
3621
- } else if (ts21.isStringLiteral(v) || ts21.isNoSubstitutionTemplateLiteral(v)) {
3728
+ } else if (ts22.isStringLiteral(v) || ts22.isNoSubstitutionTemplateLiteral(v)) {
3622
3729
  entries.push({ key, value: { kind: "string", text: v.text } });
3623
3730
  } else {
3624
3731
  return null;
@@ -3674,7 +3781,7 @@ function computeSsrSeedPlan(metadata) {
3674
3781
  // ../jsx/src/rich-type-refusal.ts
3675
3782
  var EMPTY_BINDINGS2 = new Map;
3676
3783
  // ../jsx/src/shared-program.ts
3677
- import ts22 from "typescript";
3784
+ import ts24 from "typescript";
3678
3785
  // ../jsx/src/adapters/interface.ts
3679
3786
  class BaseAdapter {
3680
3787
  renderChildren(children) {
@@ -3750,7 +3857,7 @@ class JsxAdapter extends BaseAdapter {
3750
3857
  }
3751
3858
  const rawInitialValue = preserveTypes ? signal.typedInitialValue ?? signal.initialValue : signal.initialValue;
3752
3859
  const initialValue = rawInitialValue.trim().startsWith("{") ? `(${rawInitialValue})` : rawInitialValue;
3753
- const needsTypeAssertion = preserveTypes && !signal.typedInitialValue && signal.type.kind !== "unknown" && signal.type.kind !== "primitive";
3860
+ const needsTypeAssertion = preserveTypes && !signal.typedInitialValue && signal.type.kind !== "unknown" && signal.type.kind !== "primitive" && signal.type.raw !== "object";
3754
3861
  if (needsTypeAssertion) {
3755
3862
  lines.push(` const ${signal.getter} = () => ${initialValue} as ${signal.type.raw}`);
3756
3863
  } else {
@@ -3769,12 +3876,16 @@ class JsxAdapter extends BaseAdapter {
3769
3876
  const computation = preserveTypes ? memo.typedComputation ?? memo.computation : memo.computation;
3770
3877
  lines.push(` const ${memo.name} = ${computation}`);
3771
3878
  }
3879
+ const moduleScopeNames = this.moduleScopeDeclarationNames(ir);
3772
3880
  for (const constant of ir.metadata.localConstants) {
3773
3881
  if (constant.isExported)
3774
3882
  continue;
3883
+ if (moduleScopeNames.has(constant.name))
3884
+ continue;
3775
3885
  const keyword = constant.declarationKind ?? "const";
3776
3886
  if (!constant.value) {
3777
- lines.push(` ${keyword} ${constant.name}`);
3887
+ const typeAnnotation = preserveTypes && constant.type ? `: ${constant.type.raw}` : "";
3888
+ lines.push(` ${keyword} ${constant.name}${typeAnnotation}`);
3778
3889
  continue;
3779
3890
  }
3780
3891
  const value = constant.value.trim();
@@ -3786,6 +3897,8 @@ class JsxAdapter extends BaseAdapter {
3786
3897
  lines.push(` ${keyword} ${constant.name} = ${constValue}`);
3787
3898
  }
3788
3899
  for (const func of localFunctions) {
3900
+ if (moduleScopeNames.has(func.name))
3901
+ continue;
3789
3902
  if (!reachable.has(func.name))
3790
3903
  continue;
3791
3904
  const params = preserveTypes && func.typedParams !== undefined ? func.typedParams : func.params.map(formatParamWithType).join(", ");
@@ -3795,6 +3908,125 @@ class JsxAdapter extends BaseAdapter {
3795
3908
  lines.push(` ${asyncKw}function ${func.name}(${params})${returnAnnotation} ${body}`);
3796
3909
  }
3797
3910
  return lines.join(`
3911
+ `);
3912
+ }
3913
+ moduleScopeNamesCache = new WeakMap;
3914
+ moduleScopeDeclarationNames(ir) {
3915
+ const cached = this.moduleScopeNamesCache.get(ir);
3916
+ if (cached)
3917
+ return cached;
3918
+ const componentScope = new Set;
3919
+ for (const sig of ir.metadata.signals) {
3920
+ if (sig.isModule)
3921
+ continue;
3922
+ componentScope.add(sig.getter);
3923
+ if (sig.setter)
3924
+ componentScope.add(sig.setter);
3925
+ }
3926
+ for (const memo of ir.metadata.memos) {
3927
+ if (!memo.isModule)
3928
+ componentScope.add(memo.name);
3929
+ }
3930
+ for (const p of ir.metadata.propsParams)
3931
+ componentScope.add(p.name);
3932
+ if (ir.metadata.propsObjectName)
3933
+ componentScope.add(ir.metadata.propsObjectName);
3934
+ if (ir.metadata.restPropsName)
3935
+ componentScope.add(ir.metadata.restPropsName);
3936
+ for (const c of ir.metadata.localConstants) {
3937
+ if (!c.isModule)
3938
+ componentScope.add(c.name);
3939
+ }
3940
+ for (const f of ir.metadata.localFunctions) {
3941
+ if (!f.isModule)
3942
+ componentScope.add(f.name);
3943
+ }
3944
+ const exported = new Set;
3945
+ const candidates = new Map;
3946
+ for (const c of ir.metadata.localConstants) {
3947
+ if (!c.isModule)
3948
+ continue;
3949
+ if (c.isJsx || c.isJsxFunction)
3950
+ continue;
3951
+ if (c.isExported) {
3952
+ exported.add(c.name);
3953
+ continue;
3954
+ }
3955
+ candidates.set(c.name, c.freeIdentifiers ?? extractFreeIdentifiersFromText(c.value ?? ""));
3956
+ }
3957
+ for (const f of ir.metadata.localFunctions) {
3958
+ if (!f.isModule)
3959
+ continue;
3960
+ if (f.isJsxFunction || f.isMultiReturnJsxHelper)
3961
+ continue;
3962
+ if (f.isExported) {
3963
+ exported.add(f.name);
3964
+ continue;
3965
+ }
3966
+ const params = f.typedParams !== undefined ? f.typedParams : f.params.map(formatParamWithType).join(", ");
3967
+ candidates.set(f.name, extractFreeIdentifiersFromText(`(${params}) => ${f.body}`));
3968
+ }
3969
+ const referencesAny = (refs, names) => {
3970
+ for (const ref of refs) {
3971
+ if (names.has(ref))
3972
+ return true;
3973
+ }
3974
+ return false;
3975
+ };
3976
+ let changed = true;
3977
+ while (changed) {
3978
+ changed = false;
3979
+ for (const [name, refs] of candidates) {
3980
+ if (referencesAny(refs, componentScope)) {
3981
+ candidates.delete(name);
3982
+ componentScope.add(name);
3983
+ changed = true;
3984
+ }
3985
+ }
3986
+ }
3987
+ const result = new Set([...exported, ...candidates.keys()]);
3988
+ this.moduleScopeNamesCache.set(ir, result);
3989
+ return result;
3990
+ }
3991
+ generateModuleScopeDeclarations(ir) {
3992
+ const { preserveTypes } = this.jsxConfig;
3993
+ const moduleNames = this.moduleScopeDeclarationNames(ir);
3994
+ const entries = [];
3995
+ for (const t of ir.metadata.typeDefinitions) {
3996
+ entries.push({ line: t.loc.start.line, text: t.definition });
3997
+ }
3998
+ for (const c of ir.metadata.localConstants) {
3999
+ if (!c.isModule || !moduleNames.has(c.name))
4000
+ continue;
4001
+ const keyword = c.declarationKind ?? "const";
4002
+ const exportKw = c.isExported ? "export " : "";
4003
+ if (!c.value) {
4004
+ entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name}` });
4005
+ continue;
4006
+ }
4007
+ const trimmed = c.value.trim();
4008
+ if (/^new WeakMap\b/.test(trimmed))
4009
+ continue;
4010
+ if (c.isExported && /^createContext\b/.test(trimmed))
4011
+ continue;
4012
+ const value = preserveTypes ? c.typedValue ?? c.value : c.value;
4013
+ entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name} = ${value}` });
4014
+ }
4015
+ for (const f of ir.metadata.localFunctions) {
4016
+ if (!f.isModule || !moduleNames.has(f.name))
4017
+ continue;
4018
+ const params = preserveTypes && f.typedParams !== undefined ? f.typedParams : f.params.map(formatParamWithType).join(", ");
4019
+ const returnAnnotation = preserveTypes && f.typedReturnType ? `: ${f.typedReturnType}` : "";
4020
+ const body = preserveTypes ? f.typedBody ?? f.body : f.body;
4021
+ const asyncKw = f.isAsync ? "async " : "";
4022
+ const exportKw = f.isExported ? "export " : "";
4023
+ entries.push({
4024
+ line: f.loc.start.line,
4025
+ text: `${exportKw}${asyncKw}function ${f.name}(${params})${returnAnnotation} ${body}`
4026
+ });
4027
+ }
4028
+ entries.sort((a, b) => a.line - b.line);
4029
+ return entries.map((e) => e.text).join(`
3798
4030
  `);
3799
4031
  }
3800
4032
  renderNodeRaw(node) {
@@ -3806,6 +4038,15 @@ class JsxAdapter extends BaseAdapter {
3806
4038
  }
3807
4039
  return this.renderNode(node);
3808
4040
  }
4041
+ renderTemplatePartsAsJs(parts) {
4042
+ return templatePartsToJsExpr(parts, { typed: this.jsxConfig.preserveTypes });
4043
+ }
4044
+ expressionValueToJs(value) {
4045
+ if (this.jsxConfig.preserveTypes && value.parts && value.expr === templatePartsToJsExpr(value.parts)) {
4046
+ return this.renderTemplatePartsAsJs(value.parts);
4047
+ }
4048
+ return value.expr;
4049
+ }
3809
4050
  renderScopeMarker(instanceIdExpr) {
3810
4051
  return `${BF_SCOPE}={${instanceIdExpr}}`;
3811
4052
  }
@@ -3873,6 +4114,7 @@ class TestAdapter extends JsxAdapter {
3873
4114
  generate(ir) {
3874
4115
  this.componentName = ir.metadata.componentName;
3875
4116
  const imports = this.generateImports(ir);
4117
+ const moduleConstants = this.generateModuleScopeDeclarations(ir);
3876
4118
  const types = this.generateTypes(ir);
3877
4119
  const component = this.generateComponent(ir);
3878
4120
  const defaultExport = ir.metadata.hasDefaultExport ? `
@@ -3881,9 +4123,11 @@ export default ${this.componentName}` : "";
3881
4123
  imports,
3882
4124
  types: types || "",
3883
4125
  component,
3884
- defaultExport
4126
+ defaultExport,
4127
+ moduleConstants,
4128
+ moduleConstantsIncludeExports: true
3885
4129
  };
3886
- const template = [imports, types, component].filter(Boolean).join(`
4130
+ const template = [imports, moduleConstants, types, component].filter(Boolean).join(`
3887
4131
 
3888
4132
  `) + defaultExport;
3889
4133
  return {
@@ -3914,9 +4158,6 @@ export default ${this.componentName}` : "";
3914
4158
  }
3915
4159
  generateTypes(ir) {
3916
4160
  const lines = [];
3917
- for (const typeDef of ir.metadata.typeDefinitions) {
3918
- lines.push(typeDef.definition);
3919
- }
3920
4161
  const propsTypeName = ir.metadata.propsType?.raw;
3921
4162
  if (propsTypeName && !ir.metadata.propsObjectName) {
3922
4163
  lines.push("");
@@ -3939,7 +4180,7 @@ export default ${this.componentName}` : "";
3939
4180
  const bodyRefText = [jsxBody, signalInits, scopeIdLine].join(`
3940
4181
  `);
3941
4182
  const bfScopeAlias = /\b__bfScope\b/.test(bodyRefText) ? "__bfScope" : "__bfScope: _bfScope";
3942
- const propsParams = ir.metadata.propsParams.map((p) => p.defaultValue ? `${p.name} = ${p.defaultValue}` : p.name).join(", ");
4183
+ const propsParams = ir.metadata.propsParams.map((p) => propsDestructureBinding(p)).join(", ");
3943
4184
  const restPropsName = ir.metadata.restPropsName;
3944
4185
  const hydrationProps = `__instanceId, ${bfScopeAlias}`;
3945
4186
  const parts = [];
@@ -4086,13 +4327,7 @@ export default ${this.componentName}` : "";
4086
4327
  }
4087
4328
  flattenTemplate(value) {
4088
4329
  const v = value;
4089
- return "`" + v.parts.map((p) => {
4090
- if (p.type === "string")
4091
- return p.value;
4092
- if (p.type === "ternary")
4093
- return `\${${p.condition} ? '${p.whenTrue}' : '${p.whenFalse}'}`;
4094
- return `\${(${JSON.stringify(p.cases)})[${p.key}]}`;
4095
- }).join("") + "`";
4330
+ return this.renderTemplatePartsAsJs(v.parts);
4096
4331
  }
4097
4332
  renderComponentProps(comp) {
4098
4333
  const parts = [];
@@ -4497,7 +4732,7 @@ function dangerousInnerHtmlDiagnostic(expr, loc, reason) {
4497
4732
  };
4498
4733
  }
4499
4734
  // ../jsx/src/combine-client-js.ts
4500
- import ts23 from "typescript";
4735
+ import ts25 from "typescript";
4501
4736
  // ../jsx/src/loop-destructure.ts
4502
4737
  function isLowerableLoopDestructure(loop) {
4503
4738
  const bindings = loop.paramBindings;
@@ -4637,9 +4872,9 @@ function escapeRe(s) {
4637
4872
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4638
4873
  }
4639
4874
  // ../jsx/src/debug.ts
4640
- import ts24 from "typescript";
4875
+ import ts26 from "typescript";
4641
4876
  // ../jsx/src/profiler.ts
4642
- import ts25 from "typescript";
4877
+ import ts27 from "typescript";
4643
4878
 
4644
4879
  // ../jsx/src/index.ts
4645
4880
  registerBuiltinLoweringPlugins();
@@ -5510,7 +5745,7 @@ function collectImportedLoopChildComponentErrors(ir, componentName) {
5510
5745
  }
5511
5746
 
5512
5747
  // src/adapter/spread/spread-codegen.ts
5513
- import ts26 from "typescript";
5748
+ import ts28 from "typescript";
5514
5749
  function conditionalSpreadToRuby(ctx, expr) {
5515
5750
  if (!expr || expr.kind !== "conditional")
5516
5751
  return null;
@@ -5565,7 +5800,7 @@ function recordIndexAccessToRuby(ctx, val) {
5565
5800
  if (val.kind !== "index-access" || val.object.kind !== "identifier" || val.index.kind !== "identifier") {
5566
5801
  return null;
5567
5802
  }
5568
- const tsVal = ts26.factory.createElementAccessExpression(ts26.factory.createIdentifier(val.object.name), ts26.factory.createIdentifier(val.index.name));
5803
+ const tsVal = ts28.factory.createElementAccessExpression(ts28.factory.createIdentifier(val.object.name), ts28.factory.createIdentifier(val.index.name));
5569
5804
  const parsed = parseRecordIndexAccess(tsVal, ctx.localConstants, ctx.propsParams);
5570
5805
  if (!parsed)
5571
5806
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/erb",
3
- "version": "0.31.0",
3
+ "version": "0.31.2",
4
4
  "description": "ERB (Embedded Ruby) adapter for BarefootJS — compiles IR to .erb templates and ships the Ruby rendering backend; runs under any Rack app (Sinatra, Rails)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -54,7 +54,7 @@
54
54
  "directory": "packages/adapter-erb"
55
55
  },
56
56
  "dependencies": {
57
- "@barefootjs/shared": "0.31.0"
57
+ "@barefootjs/shared": "0.31.2"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "@barefootjs/jsx": ">=0.2.0",
@@ -71,9 +71,9 @@
71
71
  },
72
72
  "devDependencies": {
73
73
  "@barefootjs/adapter-tests": "0.1.0",
74
- "@barefootjs/jsx": "0.31.0",
75
- "@barefootjs/vite": "0.31.0",
76
- "@barefootjs/client": "0.31.0",
74
+ "@barefootjs/jsx": "0.31.2",
75
+ "@barefootjs/vite": "0.31.2",
76
+ "@barefootjs/client": "0.31.2",
77
77
  "typescript": "^5.0.0",
78
78
  "vite": "^6.0.0"
79
79
  }
@@ -20,18 +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 this line (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
23
  }
@@ -36,7 +36,7 @@
36
36
  * regression test.
37
37
  */
38
38
 
39
- import { compileJSX, extractSsrDefaults, importsSearchParams, evaluateSignalInit, tryEvaluateSignalInit } from '@barefootjs/jsx'
39
+ import { compileJSX, extractSsrDefaults, deriveStashFromDefaults, importsSearchParams, evaluateSignalInit } from '@barefootjs/jsx'
40
40
  import type { ComponentIR, SsrDefault } from '@barefootjs/jsx'
41
41
  import { mkdir, rm } from 'node:fs/promises'
42
42
  import { resolve } from 'node:path'
@@ -415,7 +415,11 @@ function buildChildRenderersRuby(
415
415
  // branch and JSX rest semantics: a caller prop the child didn't
416
416
  // destructure belongs in the bag, not as a top-level vars key the
417
417
  // template never reads.
418
- const paramNames = (childIR.metadata.propsParams ?? []).map(p => p.name)
418
+ // Caller-facing keys `child_props` (below) is keyed by whatever the
419
+ // CALLING template passed (the JSX attribute name), so the keep-set
420
+ // must match that spelling, not an aliased child's local binding
421
+ // (#2524).
422
+ const paramNames = (childIR.metadata.propsParams ?? []).map(p => p.sourceName ?? p.name)
419
423
  const keep = [...new Set([...paramNames, restPropsName, 'children', 'key', '_bf_slot'])]
420
424
  const keepList = keep.map(rubySymbol).join(', ')
421
425
  lines.push(` keep = [${keepList}]`)
@@ -499,24 +503,29 @@ function buildRubyProps(
499
503
  obj.scope_id = explicitScope
500
504
 
501
505
  // Prop params with defaults (before signals, so signals can reference them).
506
+ // Seeded through the shared `deriveStashFromDefaults` (the TS twin of the
507
+ // production `derive_vars_from_defaults` this harness ALSO calls below for
508
+ // child components) so an aliased destructured prop's CALLER-facing key
509
+ // (`propName`, e.g. `n` for `{ n: count }`) is honoured, not just the
510
+ // local template var name (#2524 SSR half). `props` is keyed by the
511
+ // caller-facing name, exactly what `propName` resolves against.
512
+ const rootSsrDefaults = extractSsrDefaults(ir.metadata) ?? {}
513
+ const derivedProps = deriveStashFromDefaults(rootSsrDefaults, props ?? {})
502
514
  for (const param of ir.metadata.propsParams) {
503
- if (props && param.name in props) continue
504
- if (param.defaultValue) {
505
- const result = tryEvaluateSignalInit(param.defaultValue.trim(), props)
506
- if (result.ok) {
507
- obj[param.name] = result.value
508
- continue
509
- }
510
- }
515
+ if (param.isRest) continue
511
516
  // No default + no caller value: seed `nil` so a bare reference to an
512
517
  // optional prop's vars-Hash key resolves (to nil) instead of the key
513
518
  // being wholly absent — matches the Perl harnesses' explicit `undef`.
514
- obj[param.name] = null
519
+ obj[param.name] = derivedProps[param.name] ?? null
515
520
  }
516
521
 
517
522
  // Route undeclared props into the rest bag (`spread_attrs(v[:<rest>])`).
518
523
  const restPropsName = ir.metadata.restPropsName
519
- const declaredParams = new Set(ir.metadata.propsParams.map(p => p.name))
524
+ // Caller-facing keys, per prop an aliased destructured prop's DECLARED
525
+ // set for rest-bag routing purposes must match what the caller actually
526
+ // sent (`n`), not the local binding (`count`), or the caller's own prop
527
+ // silently gets swept into the rest bag as an undeclared extra (#2524).
528
+ const declaredParams = new Set(ir.metadata.propsParams.map(p => p.sourceName ?? p.name))
520
529
  const restBagEntries: Array<[string, unknown]> = []
521
530
  if (restPropsName && props) {
522
531
  for (const [key, value] of Object.entries(props)) {
@@ -531,11 +540,20 @@ function buildRubyProps(
531
540
  obj[restPropsName] = Object.fromEntries(restBagEntries)
532
541
  }
533
542
 
534
- // User props.
543
+ // User props. Skip a key that's already a declared param's LOCAL template
544
+ // var name — `derivedProps` above already resolved that var correctly
545
+ // (through `propName`, for an aliased prop); re-assigning the raw
546
+ // `props[key]` here would silently clobber it with an UNRELATED value
547
+ // whenever a caller happens to also pass a same-spelled-as-local-name prop
548
+ // that isn't this param's actual `propName` (#2524 — surfaced by the
549
+ // aliased-destructured-prop generated data points, which pass both `n`
550
+ // (the real propName) and an incidental `count` key).
551
+ const localParamNames = new Set(ir.metadata.propsParams.map(p => p.name))
535
552
  if (props) {
536
553
  for (const [key, value] of Object.entries(props)) {
537
554
  if (key.startsWith('__')) continue
538
555
  if (routedKeys.has(key)) continue
556
+ if (localParamNames.has(key)) continue
539
557
  obj[key] = value
540
558
  }
541
559
  }
@@ -553,9 +571,8 @@ function buildRubyProps(
553
571
 
554
572
  // Memo values seeded from the statically-evaluated ssrDefaults, same
555
573
  // as the production plugin's before_render hook.
556
- const ssrDefaults = extractSsrDefaults(ir.metadata) ?? {}
557
574
  for (const memo of ir.metadata.memos) {
558
- obj[memo.name] = ssrDefaults[memo.name]?.value ?? 0
575
+ obj[memo.name] = rootSsrDefaults[memo.name]?.value ?? 0
559
576
  }
560
577
 
561
578
  const needsSearchParams = importsSearchParams(ir.metadata)