@barefootjs/hono 0.31.1 → 0.31.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/adapter/hono-adapter.d.ts +67 -2
  2. package/dist/adapter/hono-adapter.d.ts.map +1 -1
  3. package/dist/adapter/index.js +46 -187398
  4. package/dist/app.js +0 -71
  5. package/dist/async.js +0 -71
  6. package/dist/client-shim.js +0 -71
  7. package/dist/dev-worker.js +0 -71
  8. package/dist/dialog-context.js +0 -71
  9. package/dist/index.js +46 -187398
  10. package/dist/jsx/jsx-dev-runtime/index.d.ts +3 -1
  11. package/dist/jsx/jsx-dev-runtime/index.d.ts.map +1 -1
  12. package/dist/jsx/jsx-dev-runtime/index.js +14 -69
  13. package/dist/jsx/jsx-runtime/index.d.ts +4 -1
  14. package/dist/jsx/jsx-runtime/index.d.ts.map +1 -1
  15. package/dist/jsx/jsx-runtime/index.js +24 -69
  16. package/dist/jsx/resolve-dangerously-set-inner-html.d.ts +2 -0
  17. package/dist/jsx/resolve-dangerously-set-inner-html.d.ts.map +1 -0
  18. package/dist/portal-ssr.js +0 -71
  19. package/dist/portals.js +0 -71
  20. package/dist/preload.js +0 -71
  21. package/dist/render.js +0 -71
  22. package/dist/request-env.js +0 -71
  23. package/dist/scripts.js +0 -71
  24. package/dist/utils.js +0 -71
  25. package/dist/vite.js +394 -143
  26. package/package.json +2 -2
  27. package/src/__tests__/aliased-destructured-prop.test.ts +8 -7
  28. package/src/__tests__/consumer-typecheck.test.ts +358 -51
  29. package/src/__tests__/corpus-typecheck.test.ts +130 -0
  30. package/src/__tests__/dangerously-set-inner-html.test.ts +70 -0
  31. package/src/__tests__/nested-ternary-bare-branch.test.ts +70 -0
  32. package/src/adapter/hono-adapter.ts +123 -166
  33. package/src/jsx/jsx-dev-runtime/index.ts +13 -1
  34. package/src/jsx/jsx-runtime/index.ts +24 -1
  35. package/src/jsx/resolve-dangerously-set-inner-html.ts +34 -0
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";
@@ -98,6 +101,29 @@ function buildLoopChainExpr(opts) {
98
101
  return `${opts.base}${sortExpr}${filterExpr}`;
99
102
  }
100
103
 
104
+ // ../jsx/src/template-parts.ts
105
+ function lookupPartToJsExpr(part, opts) {
106
+ const key = opts?.useTemplate && part.templateKey ? part.templateKey : part.key;
107
+ const obj = "{" + Object.entries(part.cases).map(([k, v]) => `${JSON.stringify(k)}: ${JSON.stringify(v)}`).join(", ") + "}";
108
+ const typed = opts?.typed ? " as Record<string, string>" : "";
109
+ return `(${obj}${typed})[${key}]`;
110
+ }
111
+ function templatePartsToJsExpr(parts, opts) {
112
+ let result = "`";
113
+ for (const part of parts) {
114
+ if (part.type === "string") {
115
+ result += opts?.useTemplate && part.templateValue ? part.templateValue : part.value;
116
+ } else if (part.type === "ternary") {
117
+ const cond = opts?.useTemplate && part.templateCondition ? part.templateCondition : part.condition;
118
+ result += `\${${cond} ? '${part.whenTrue}' : '${part.whenFalse}'}`;
119
+ } else if (part.type === "lookup") {
120
+ result += `\${${lookupPartToJsExpr(part, opts)}}`;
121
+ }
122
+ }
123
+ result += "`";
124
+ return result;
125
+ }
126
+
101
127
  // ../jsx/src/scanner/js-scanner.ts
102
128
  import ts2 from "typescript";
103
129
 
@@ -203,6 +229,16 @@ function escapeHtml(text) {
203
229
  }
204
230
  // ../jsx/src/ir-to-client-js/csr-substitute.ts
205
231
  import ts4 from "typescript";
232
+ function extractFreeIdentifiersFromText(text) {
233
+ if (!text || text.trim().length === 0)
234
+ return new Set;
235
+ const sf = ts4.createSourceFile("__free_ids__.ts", `(${text});`, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TS);
236
+ const stmt = sf.statements[0];
237
+ if (!stmt || !ts4.isExpressionStatement(stmt))
238
+ return new Set;
239
+ const expr = ts4.isParenthesizedExpression(stmt.expression) ? stmt.expression.expression : stmt.expression;
240
+ return extractFreeIdentifiersFromNode(expr);
241
+ }
206
242
 
207
243
  // ../jsx/src/ir-to-client-js/html-template.ts
208
244
  var VOID_ELEMENTS = new Set([
@@ -248,6 +284,27 @@ var SKELETON_PATH_FORCE_CLOSE_GROUPS = [
248
284
  new Set(["li"])
249
285
  ];
250
286
 
287
+ // ../jsx/src/props-binding.ts
288
+ import ts6 from "typescript";
289
+ function isIdentifierName(key) {
290
+ if (key.length === 0)
291
+ return false;
292
+ for (let i = 0;i < key.length; ) {
293
+ const cp = key.codePointAt(i);
294
+ const ok = i === 0 ? ts6.isIdentifierStart(cp, ts6.ScriptTarget.Latest) : ts6.isIdentifierPart(cp, ts6.ScriptTarget.Latest);
295
+ if (!ok)
296
+ return false;
297
+ i += cp > 65535 ? 2 : 1;
298
+ }
299
+ return true;
300
+ }
301
+ function propsDestructureBinding(p) {
302
+ const callerKey = p.sourceName ?? p.name;
303
+ const localName = p.name;
304
+ const binding = callerKey === localName ? localName : `${isIdentifierName(callerKey) ? callerKey : JSON.stringify(callerKey)}: ${localName}`;
305
+ return p.defaultValue ? `${binding} = ${p.defaultValue}` : binding;
306
+ }
307
+
251
308
  // ../jsx/src/instrumentation.ts
252
309
  var _counters = freshCounters();
253
310
  function freshCounters() {
@@ -261,14 +318,14 @@ function freshCounters() {
261
318
  }
262
319
 
263
320
  // ../jsx/src/analyzer-context.ts
264
- import ts7 from "typescript";
321
+ import ts8 from "typescript";
265
322
 
266
323
  // ../jsx/src/strip-types.ts
267
- import ts6 from "typescript";
324
+ import ts7 from "typescript";
268
325
 
269
326
  // ../jsx/src/analyzer-context.ts
270
- var _typePrinter = ts7.createPrinter({ removeComments: true, omitTrailingSemicolon: true });
271
- var _blankTypeSourceFile = ts7.createSourceFile("__bf_types__.ts", "", ts7.ScriptTarget.Latest);
327
+ var _typePrinter = ts8.createPrinter({ removeComments: true, omitTrailingSemicolon: true });
328
+ var _blankTypeSourceFile = ts8.createSourceFile("__bf_types__.ts", "", ts8.ScriptTarget.Latest);
272
329
 
273
330
  // ../jsx/src/errors.ts
274
331
  var ErrorCodes = {
@@ -284,6 +341,7 @@ var ErrorCodes = {
284
341
  JSX_IN_LOCAL_FUNCTION: "BF045",
285
342
  COMPONENT_REQUIRED_PROP_MISSING: "BF046",
286
343
  JSX_BRANCH_LOCAL_IN_CALLBACK: "BF047",
344
+ SIBLING_COMPONENT_NOT_COMPILED: "BF048",
287
345
  SHARED_PROGRAM_REQUIRED: "BF050",
288
346
  WRONG_PACKAGE_IMPORT: "BF051",
289
347
  BUILTIN_REQUIRES_IMPORT: "BF054",
@@ -313,6 +371,7 @@ var errorMessages = {
313
371
  [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.",
314
372
  [ErrorCodes.COMPONENT_REQUIRED_PROP_MISSING]: "Built-in component is missing a required prop.",
315
373
  [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>`.",
374
+ [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.",
316
375
  [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.",
317
376
  [ErrorCodes.WRONG_PACKAGE_IMPORT]: "Import from wrong package.",
318
377
  [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.",
@@ -486,6 +545,54 @@ var CLIENT_EXPORTS = new Set([
486
545
  "Async",
487
546
  "Region"
488
547
  ]);
548
+ function extractFreeIdentifiersFromNode(node) {
549
+ const ids = new Set;
550
+ const boundNames = new Set;
551
+ function addBindingNames(name, out) {
552
+ if (ts9.isIdentifier(name))
553
+ out.push(name.text);
554
+ else if (ts9.isObjectBindingPattern(name))
555
+ name.elements.forEach((e) => addBindingNames(e.name, out));
556
+ else if (ts9.isArrayBindingPattern(name))
557
+ name.elements.forEach((e) => {
558
+ if (!ts9.isOmittedExpression(e))
559
+ addBindingNames(e.name, out);
560
+ });
561
+ }
562
+ function visit(n) {
563
+ if (ts9.isTypeNode(n))
564
+ return;
565
+ if (ts9.isIdentifier(n)) {
566
+ const parent = n.parent;
567
+ if (parent && ts9.isPropertyAccessExpression(parent) && parent.name === n)
568
+ return;
569
+ if (parent && ts9.isPropertyAssignment(parent) && parent.name === n)
570
+ return;
571
+ if (parent && ts9.isParameter(parent) && parent.name === n)
572
+ return;
573
+ if (parent && ts9.isVariableDeclaration(parent) && parent.name === n)
574
+ return;
575
+ if (boundNames.has(n.text))
576
+ return;
577
+ ids.add(n.text);
578
+ return;
579
+ }
580
+ if (ts9.isArrowFunction(n)) {
581
+ const params = [];
582
+ for (const p of n.parameters)
583
+ addBindingNames(p.name, params);
584
+ for (const name of params)
585
+ boundNames.add(name);
586
+ ts9.forEachChild(n, visit);
587
+ for (const name of params)
588
+ boundNames.delete(name);
589
+ return;
590
+ }
591
+ ts9.forEachChild(n, visit);
592
+ }
593
+ visit(node);
594
+ return ids;
595
+ }
489
596
  var BROWSER_ONLY_CLIENT_APIS = new Set([
490
597
  "useContext",
491
598
  "provideContext",
@@ -504,7 +611,7 @@ var REACTIVE_PRIMITIVES = new Set([
504
611
  ]);
505
612
 
506
613
  // ../jsx/src/jsx-to-ir.ts
507
- import ts11 from "typescript";
614
+ import ts12 from "typescript";
508
615
 
509
616
  // ../jsx/src/types.ts
510
617
  var SCOPE_FORBIDDEN = {
@@ -572,10 +679,10 @@ function findReachableNames(primaryRefs, declarations) {
572
679
  }
573
680
 
574
681
  // ../jsx/src/reactivity-checker.ts
575
- import ts9 from "typescript";
682
+ import ts10 from "typescript";
576
683
 
577
684
  // ../jsx/src/free-refs.ts
578
- import ts10 from "typescript";
685
+ import ts11 from "typescript";
579
686
  var _bindingMapCache = new WeakMap;
580
687
 
581
688
  // ../jsx/src/to-locale-date-lowering.ts
@@ -893,6 +1000,83 @@ var toLocaleDatePlugin = {
893
1000
  }
894
1001
  };
895
1002
 
1003
+ // ../jsx/src/scope/binding-scope.ts
1004
+ class BindingScope {
1005
+ frames;
1006
+ static EMPTY = new BindingScope([]);
1007
+ constructor(frames) {
1008
+ this.frames = frames;
1009
+ }
1010
+ enterLoopRow(loop) {
1011
+ const bindings = new Map;
1012
+ if (loop.paramBindings && loop.paramBindings.length > 0) {
1013
+ for (const b of loop.paramBindings)
1014
+ bindings.set(b.name, { source: "destructure" });
1015
+ } else {
1016
+ bindings.set(loop.param, { source: "item" });
1017
+ }
1018
+ if (loop.index != null)
1019
+ bindings.set(loop.index, { source: "index" });
1020
+ for (const name of loop.preamble?.declaredNames ?? [])
1021
+ bindings.set(name, { source: "preamble" });
1022
+ const frame = { kind: "loop-row", bindings };
1023
+ return new BindingScope([frame, ...this.frames]);
1024
+ }
1025
+ enterCallback(params) {
1026
+ const bindings = new Map;
1027
+ for (const name of params)
1028
+ bindings.set(name, { source: "param" });
1029
+ const frame = { kind: "callback", bindings };
1030
+ return new BindingScope([frame, ...this.frames]);
1031
+ }
1032
+ isBound(name) {
1033
+ for (const frame of this.frames) {
1034
+ if (frame.bindings.has(name))
1035
+ return true;
1036
+ }
1037
+ return false;
1038
+ }
1039
+ lookup(name) {
1040
+ for (let depth = 0;depth < this.frames.length; depth++) {
1041
+ const frame = this.frames[depth];
1042
+ const binding = frame.bindings.get(name);
1043
+ if (binding)
1044
+ return { depth, frame, binding };
1045
+ }
1046
+ return null;
1047
+ }
1048
+ boundNames() {
1049
+ if (this.boundNamesCache)
1050
+ return this.boundNamesCache;
1051
+ const names = new Set;
1052
+ for (const frame of this.frames) {
1053
+ for (const name of frame.bindings.keys())
1054
+ names.add(name);
1055
+ }
1056
+ this.boundNamesCache = names;
1057
+ return names;
1058
+ }
1059
+ boundNamesCache;
1060
+ valueBoundNamesCache;
1061
+ valueBoundNames() {
1062
+ if (this.valueBoundNamesCache)
1063
+ return this.valueBoundNamesCache;
1064
+ const names = new Set;
1065
+ for (const frame of this.frames) {
1066
+ for (const [name, binding] of frame.bindings) {
1067
+ if (binding.source === "item" || binding.source === "index" || binding.source === "destructure") {
1068
+ names.add(name);
1069
+ }
1070
+ }
1071
+ }
1072
+ this.valueBoundNamesCache = names;
1073
+ return names;
1074
+ }
1075
+ asShadowPredicate() {
1076
+ return (name) => this.isBound(name);
1077
+ }
1078
+ }
1079
+
896
1080
  // ../jsx/src/jsx-to-ir.ts
897
1081
  var EMPTY_BOUND = new Set;
898
1082
  var constInitializerCache = new WeakMap;
@@ -989,13 +1173,13 @@ var KEYWORDS_AND_GLOBALS = new Set([
989
1173
  ]);
990
1174
 
991
1175
  // ../jsx/src/ir-to-client-js/walk-prop-accesses.ts
992
- import ts12 from "typescript";
1176
+ import ts13 from "typescript";
993
1177
 
994
1178
  // ../jsx/src/value-references.ts
995
- import ts13 from "typescript";
1179
+ import ts14 from "typescript";
996
1180
 
997
1181
  // ../jsx/src/relocate.ts
998
- import ts14 from "typescript";
1182
+ import ts15 from "typescript";
999
1183
 
1000
1184
  // ../jsx/src/lowering-registry.ts
1001
1185
  var plugins = [];
@@ -1174,10 +1358,10 @@ function formatDateLocalNames(metadata) {
1174
1358
  }
1175
1359
 
1176
1360
  // ../jsx/src/ir-to-client-js/prune-unused-prop-extractions.ts
1177
- import ts15 from "typescript";
1361
+ import ts16 from "typescript";
1178
1362
 
1179
1363
  // ../jsx/src/ir-to-client-js/control-flow/plan/lazy-preamble.ts
1180
- import ts16 from "typescript";
1364
+ import ts17 from "typescript";
1181
1365
  var NO_PREAMBLE = {
1182
1366
  lazySafe: true,
1183
1367
  facts: { declaredNames: new Set, freeNames: new Set }
@@ -1227,7 +1411,7 @@ var INERT_BINDING_GLOBALS = new Set([
1227
1411
  ]);
1228
1412
 
1229
1413
  // ../jsx/src/ir-to-client-js/emit-reactive.ts
1230
- import ts17 from "typescript";
1414
+ import ts18 from "typescript";
1231
1415
 
1232
1416
  // ../jsx/src/ir-to-client-js/control-flow/stringify/event-delegation.ts
1233
1417
  var NON_BUBBLING_EVENTS = new Set([
@@ -1242,7 +1426,7 @@ var NON_BUBBLING_EVENTS = new Set([
1242
1426
  ]);
1243
1427
 
1244
1428
  // ../jsx/src/ir-to-client-js/rewrite-props-object.ts
1245
- import ts18 from "typescript";
1429
+ import ts19 from "typescript";
1246
1430
 
1247
1431
  // ../jsx/src/ir-to-client-js/source-map.ts
1248
1432
  var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -1333,20 +1517,20 @@ class SourceMapGenerator {
1333
1517
  }
1334
1518
 
1335
1519
  // ../jsx/src/preprocess-inline-jsx-callbacks.ts
1336
- import ts19 from "typescript";
1520
+ import ts20 from "typescript";
1337
1521
 
1338
1522
  // ../jsx/src/ssr-defaults.ts
1339
- import ts20 from "typescript";
1523
+ import ts21 from "typescript";
1340
1524
  var UNRESOLVED = Symbol("unresolved");
1341
1525
  var NO_RETURN = Symbol("no-return");
1342
1526
 
1343
1527
  // ../jsx/src/augment-inherited-props.ts
1344
- import ts21 from "typescript";
1528
+ import ts22 from "typescript";
1345
1529
 
1346
1530
  // ../jsx/src/rich-type-refusal.ts
1347
1531
  var EMPTY_BINDINGS2 = new Map;
1348
1532
  // ../jsx/src/shared-program.ts
1349
- import ts22 from "typescript";
1533
+ import ts24 from "typescript";
1350
1534
  // ../jsx/src/adapters/interface.ts
1351
1535
  class BaseAdapter {
1352
1536
  renderChildren(children) {
@@ -1422,7 +1606,7 @@ class JsxAdapter extends BaseAdapter {
1422
1606
  }
1423
1607
  const rawInitialValue = preserveTypes ? signal.typedInitialValue ?? signal.initialValue : signal.initialValue;
1424
1608
  const initialValue = rawInitialValue.trim().startsWith("{") ? `(${rawInitialValue})` : rawInitialValue;
1425
- const needsTypeAssertion = preserveTypes && !signal.typedInitialValue && signal.type.kind !== "unknown" && signal.type.kind !== "primitive";
1609
+ const needsTypeAssertion = preserveTypes && !signal.typedInitialValue && signal.type.kind !== "unknown" && signal.type.kind !== "primitive" && signal.type.raw !== "object";
1426
1610
  if (needsTypeAssertion) {
1427
1611
  lines.push(` const ${signal.getter} = () => ${initialValue} as ${signal.type.raw}`);
1428
1612
  } else {
@@ -1441,12 +1625,16 @@ class JsxAdapter extends BaseAdapter {
1441
1625
  const computation = preserveTypes ? memo.typedComputation ?? memo.computation : memo.computation;
1442
1626
  lines.push(` const ${memo.name} = ${computation}`);
1443
1627
  }
1628
+ const moduleScopeNames = this.moduleScopeDeclarationNames(ir);
1444
1629
  for (const constant of ir.metadata.localConstants) {
1445
1630
  if (constant.isExported)
1446
1631
  continue;
1632
+ if (moduleScopeNames.has(constant.name))
1633
+ continue;
1447
1634
  const keyword = constant.declarationKind ?? "const";
1448
1635
  if (!constant.value) {
1449
- lines.push(` ${keyword} ${constant.name}`);
1636
+ const typeAnnotation = preserveTypes && (constant.typeAnnotation ?? constant.type) ? `: ${constant.typeAnnotation ?? constant.type?.raw}` : "";
1637
+ lines.push(` ${keyword} ${constant.name}${typeAnnotation}`);
1450
1638
  continue;
1451
1639
  }
1452
1640
  const value = constant.value.trim();
@@ -1455,9 +1643,12 @@ class JsxAdapter extends BaseAdapter {
1455
1643
  if (!reachable.has(constant.name))
1456
1644
  continue;
1457
1645
  const constValue = preserveTypes ? constant.typedValue ?? constant.value : constant.value;
1458
- lines.push(` ${keyword} ${constant.name} = ${constValue}`);
1646
+ const letTypeAnnotation = preserveTypes && keyword === "let" && constant.typeAnnotation ? `: ${constant.typeAnnotation}` : "";
1647
+ lines.push(` ${keyword} ${constant.name}${letTypeAnnotation} = ${constValue}`);
1459
1648
  }
1460
1649
  for (const func of localFunctions) {
1650
+ if (moduleScopeNames.has(func.name))
1651
+ continue;
1461
1652
  if (!reachable.has(func.name))
1462
1653
  continue;
1463
1654
  const params = preserveTypes && func.typedParams !== undefined ? func.typedParams : func.params.map(formatParamWithType).join(", ");
@@ -1467,6 +1658,127 @@ class JsxAdapter extends BaseAdapter {
1467
1658
  lines.push(` ${asyncKw}function ${func.name}(${params})${returnAnnotation} ${body}`);
1468
1659
  }
1469
1660
  return lines.join(`
1661
+ `);
1662
+ }
1663
+ moduleScopeNamesCache = new WeakMap;
1664
+ moduleScopeDeclarationNames(ir) {
1665
+ const cached = this.moduleScopeNamesCache.get(ir);
1666
+ if (cached)
1667
+ return cached;
1668
+ const componentScope = new Set;
1669
+ for (const sig of ir.metadata.signals) {
1670
+ if (sig.isModule)
1671
+ continue;
1672
+ componentScope.add(sig.getter);
1673
+ if (sig.setter)
1674
+ componentScope.add(sig.setter);
1675
+ }
1676
+ for (const memo of ir.metadata.memos) {
1677
+ if (!memo.isModule)
1678
+ componentScope.add(memo.name);
1679
+ }
1680
+ for (const p of ir.metadata.propsParams)
1681
+ componentScope.add(p.name);
1682
+ if (ir.metadata.propsObjectName)
1683
+ componentScope.add(ir.metadata.propsObjectName);
1684
+ if (ir.metadata.restPropsName)
1685
+ componentScope.add(ir.metadata.restPropsName);
1686
+ for (const c of ir.metadata.localConstants) {
1687
+ if (!c.isModule)
1688
+ componentScope.add(c.name);
1689
+ }
1690
+ for (const f of ir.metadata.localFunctions) {
1691
+ if (!f.isModule)
1692
+ componentScope.add(f.name);
1693
+ }
1694
+ const exported = new Set;
1695
+ const candidates = new Map;
1696
+ for (const c of ir.metadata.localConstants) {
1697
+ if (!c.isModule)
1698
+ continue;
1699
+ if (c.isJsx || c.isJsxFunction)
1700
+ continue;
1701
+ if (c.isExported) {
1702
+ exported.add(c.name);
1703
+ continue;
1704
+ }
1705
+ candidates.set(c.name, c.freeIdentifiers ?? extractFreeIdentifiersFromText(c.value ?? ""));
1706
+ }
1707
+ for (const f of ir.metadata.localFunctions) {
1708
+ if (!f.isModule)
1709
+ continue;
1710
+ if (f.isJsxFunction || f.isMultiReturnJsxHelper)
1711
+ continue;
1712
+ if (f.isExported) {
1713
+ exported.add(f.name);
1714
+ continue;
1715
+ }
1716
+ const params = f.typedParams !== undefined ? f.typedParams : f.params.map(formatParamWithType).join(", ");
1717
+ candidates.set(f.name, extractFreeIdentifiersFromText(`(${params}) => ${f.body}`));
1718
+ }
1719
+ const referencesAny = (refs, names) => {
1720
+ for (const ref of refs) {
1721
+ if (names.has(ref))
1722
+ return true;
1723
+ }
1724
+ return false;
1725
+ };
1726
+ let changed = true;
1727
+ while (changed) {
1728
+ changed = false;
1729
+ for (const [name, refs] of candidates) {
1730
+ if (referencesAny(refs, componentScope)) {
1731
+ candidates.delete(name);
1732
+ componentScope.add(name);
1733
+ changed = true;
1734
+ }
1735
+ }
1736
+ }
1737
+ const result = new Set([...exported, ...candidates.keys()]);
1738
+ this.moduleScopeNamesCache.set(ir, result);
1739
+ return result;
1740
+ }
1741
+ generateModuleScopeDeclarations(ir) {
1742
+ const { preserveTypes } = this.jsxConfig;
1743
+ const moduleNames = this.moduleScopeDeclarationNames(ir);
1744
+ const entries = [];
1745
+ for (const t of ir.metadata.typeDefinitions) {
1746
+ entries.push({ line: t.loc.start.line, text: t.definition });
1747
+ }
1748
+ for (const c of ir.metadata.localConstants) {
1749
+ if (!c.isModule || !moduleNames.has(c.name))
1750
+ continue;
1751
+ const keyword = c.declarationKind ?? "const";
1752
+ const exportKw = c.isExported ? "export " : "";
1753
+ if (!c.value) {
1754
+ const typeAnnotation = preserveTypes && (c.typeAnnotation ?? c.type) ? `: ${c.typeAnnotation ?? c.type?.raw}` : "";
1755
+ entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name}${typeAnnotation}` });
1756
+ continue;
1757
+ }
1758
+ const trimmed = c.value.trim();
1759
+ if (/^new WeakMap\b/.test(trimmed))
1760
+ continue;
1761
+ if (c.isExported && /^createContext\b/.test(trimmed))
1762
+ continue;
1763
+ const value = preserveTypes ? c.typedValue ?? c.value : c.value;
1764
+ const letTypeAnnotation = preserveTypes && keyword === "let" && c.typeAnnotation ? `: ${c.typeAnnotation}` : "";
1765
+ entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name}${letTypeAnnotation} = ${value}` });
1766
+ }
1767
+ for (const f of ir.metadata.localFunctions) {
1768
+ if (!f.isModule || !moduleNames.has(f.name))
1769
+ continue;
1770
+ const params = preserveTypes && f.typedParams !== undefined ? f.typedParams : f.params.map(formatParamWithType).join(", ");
1771
+ const returnAnnotation = preserveTypes && f.typedReturnType ? `: ${f.typedReturnType}` : "";
1772
+ const body = preserveTypes ? f.typedBody ?? f.body : f.body;
1773
+ const asyncKw = f.isAsync ? "async " : "";
1774
+ const exportKw = f.isExported ? "export " : "";
1775
+ entries.push({
1776
+ line: f.loc.start.line,
1777
+ text: `${exportKw}${asyncKw}function ${f.name}(${params})${returnAnnotation} ${body}`
1778
+ });
1779
+ }
1780
+ entries.sort((a, b) => a.line - b.line);
1781
+ return entries.map((e) => e.text).join(`
1470
1782
  `);
1471
1783
  }
1472
1784
  renderNodeRaw(node) {
@@ -1478,6 +1790,15 @@ class JsxAdapter extends BaseAdapter {
1478
1790
  }
1479
1791
  return this.renderNode(node);
1480
1792
  }
1793
+ renderTemplatePartsAsJs(parts) {
1794
+ return templatePartsToJsExpr(parts, { typed: this.jsxConfig.preserveTypes });
1795
+ }
1796
+ expressionValueToJs(value) {
1797
+ if (this.jsxConfig.preserveTypes && value.parts && value.expr === templatePartsToJsExpr(value.parts)) {
1798
+ return this.renderTemplatePartsAsJs(value.parts);
1799
+ }
1800
+ return value.expr;
1801
+ }
1481
1802
  renderScopeMarker(instanceIdExpr) {
1482
1803
  return `${BF_SCOPE}={${instanceIdExpr}}`;
1483
1804
  }
@@ -1545,6 +1866,7 @@ class TestAdapter extends JsxAdapter {
1545
1866
  generate(ir) {
1546
1867
  this.componentName = ir.metadata.componentName;
1547
1868
  const imports = this.generateImports(ir);
1869
+ const moduleConstants = this.generateModuleScopeDeclarations(ir);
1548
1870
  const types = this.generateTypes(ir);
1549
1871
  const component = this.generateComponent(ir);
1550
1872
  const defaultExport = ir.metadata.hasDefaultExport ? `
@@ -1553,9 +1875,11 @@ export default ${this.componentName}` : "";
1553
1875
  imports,
1554
1876
  types: types || "",
1555
1877
  component,
1556
- defaultExport
1878
+ defaultExport,
1879
+ moduleConstants,
1880
+ moduleConstantsIncludeExports: true
1557
1881
  };
1558
- const template = [imports, types, component].filter(Boolean).join(`
1882
+ const template = [imports, moduleConstants, types, component].filter(Boolean).join(`
1559
1883
 
1560
1884
  `) + defaultExport;
1561
1885
  return {
@@ -1586,9 +1910,6 @@ export default ${this.componentName}` : "";
1586
1910
  }
1587
1911
  generateTypes(ir) {
1588
1912
  const lines = [];
1589
- for (const typeDef of ir.metadata.typeDefinitions) {
1590
- lines.push(typeDef.definition);
1591
- }
1592
1913
  const propsTypeName = ir.metadata.propsType?.raw;
1593
1914
  if (propsTypeName && !ir.metadata.propsObjectName) {
1594
1915
  lines.push("");
@@ -1611,7 +1932,7 @@ export default ${this.componentName}` : "";
1611
1932
  const bodyRefText = [jsxBody, signalInits, scopeIdLine].join(`
1612
1933
  `);
1613
1934
  const bfScopeAlias = /\b__bfScope\b/.test(bodyRefText) ? "__bfScope" : "__bfScope: _bfScope";
1614
- const propsParams = ir.metadata.propsParams.map((p) => p.defaultValue ? `${p.name} = ${p.defaultValue}` : p.name).join(", ");
1935
+ const propsParams = ir.metadata.propsParams.map((p) => propsDestructureBinding(p)).join(", ");
1615
1936
  const restPropsName = ir.metadata.restPropsName;
1616
1937
  const hydrationProps = `__instanceId, ${bfScopeAlias}`;
1617
1938
  const parts = [];
@@ -1758,13 +2079,7 @@ export default ${this.componentName}` : "";
1758
2079
  }
1759
2080
  flattenTemplate(value) {
1760
2081
  const v = value;
1761
- return "`" + v.parts.map((p) => {
1762
- if (p.type === "string")
1763
- return p.value;
1764
- if (p.type === "ternary")
1765
- return `\${${p.condition} ? '${p.whenTrue}' : '${p.whenFalse}'}`;
1766
- return `\${(${JSON.stringify(p.cases)})[${p.key}]}`;
1767
- }).join("") + "`";
2082
+ return this.renderTemplatePartsAsJs(v.parts);
1768
2083
  }
1769
2084
  renderComponentProps(comp) {
1770
2085
  const parts = [];
@@ -1940,17 +2255,16 @@ function emitAttrValue(value, emitter, name) {
1940
2255
  }
1941
2256
  }
1942
2257
  // ../jsx/src/combine-client-js.ts
1943
- import ts23 from "typescript";
2258
+ import ts25 from "typescript";
1944
2259
  // ../jsx/src/debug.ts
1945
- import ts24 from "typescript";
2260
+ import ts26 from "typescript";
1946
2261
  // ../jsx/src/profiler.ts
1947
- import ts25 from "typescript";
2262
+ import ts27 from "typescript";
1948
2263
 
1949
2264
  // ../jsx/src/index.ts
1950
2265
  registerBuiltinLoweringPlugins();
1951
2266
 
1952
2267
  // src/adapter/hono-adapter.ts
1953
- import ts26 from "typescript";
1954
2268
  function applyHonoLoopChain(loop) {
1955
2269
  return buildLoopChainExpr({
1956
2270
  base: loop.array,
@@ -1959,18 +2273,6 @@ function applyHonoLoopChain(loop) {
1959
2273
  chainOrder: loop.chainOrder
1960
2274
  });
1961
2275
  }
1962
- function isIdentifierName(key) {
1963
- if (key.length === 0)
1964
- return false;
1965
- for (let i = 0;i < key.length; ) {
1966
- const cp = key.codePointAt(i);
1967
- const ok = i === 0 ? ts26.isIdentifierStart(cp, ts26.ScriptTarget.Latest) : ts26.isIdentifierPart(cp, ts26.ScriptTarget.Latest);
1968
- if (!ok)
1969
- return false;
1970
- i += cp > 65535 ? 2 : 1;
1971
- }
1972
- return true;
1973
- }
1974
2276
 
1975
2277
  class HonoAdapter extends JsxAdapter {
1976
2278
  name = "hono";
@@ -2008,11 +2310,11 @@ class HonoAdapter extends JsxAdapter {
2008
2310
  this.preloadAssets = options?.preloadAssets;
2009
2311
  }
2010
2312
  const component = this.generateComponent(ir);
2011
- const types = this.generateTypes(ir, component);
2012
- const componentCode = [types, component].filter(Boolean).join(`
2313
+ const types = this.generateTypes(ir);
2314
+ const moduleConstants = this.generateModuleScopeDeclarations(ir);
2315
+ const componentCode = [moduleConstants, types, component].filter(Boolean).join(`
2013
2316
  `);
2014
2317
  const imports = this.generateImports(ir, componentCode);
2015
- const moduleConstants = this.generateModuleLevelContextBindings(ir);
2016
2318
  const defaultExport = ir.metadata.hasDefaultExport ? `
2017
2319
  export default ${this.componentName}` : "";
2018
2320
  const sections = {
@@ -2020,7 +2322,8 @@ export default ${this.componentName}` : "";
2020
2322
  types: types || "",
2021
2323
  component,
2022
2324
  defaultExport,
2023
- moduleConstants
2325
+ moduleConstants,
2326
+ moduleConstantsIncludeExports: true
2024
2327
  };
2025
2328
  const template = [imports, moduleConstants, types, component].filter(Boolean).join(`
2026
2329
 
@@ -2042,24 +2345,6 @@ export default ${this.componentName}` : "";
2042
2345
  hasPreloadAssets() {
2043
2346
  return this.hasScriptAssets() && !!this.preloadAssets && this.preloadAssets.length > 0;
2044
2347
  }
2045
- generateModuleLevelContextBindings(ir) {
2046
- const lines = [];
2047
- for (const c of ir.metadata.localConstants) {
2048
- if (!c.isModule)
2049
- continue;
2050
- if (c.isExported)
2051
- continue;
2052
- if (c.systemConstructKind !== "createContext")
2053
- continue;
2054
- if (!c.value)
2055
- continue;
2056
- const keyword = c.declarationKind ?? "const";
2057
- const value = this.jsxConfig.preserveTypes ? c.typedValue ?? c.value : c.value;
2058
- lines.push(`${keyword} ${c.name} = ${value}`);
2059
- }
2060
- return lines.join(`
2061
- `);
2062
- }
2063
2348
  generateImports(ir, componentCode) {
2064
2349
  const lines = [];
2065
2350
  const utilImports = [];
@@ -2101,50 +2386,10 @@ export default ${this.componentName}` : "";
2101
2386
  return lines.join(`
2102
2387
  `);
2103
2388
  }
2104
- generateTypes(ir, componentBody) {
2389
+ generateTypes(ir) {
2105
2390
  const lines = [];
2106
- if (componentBody && ir.metadata.typeDefinitions.length > 0) {
2107
- const propsTypeName2 = this.getPropsTypeName(ir);
2108
- const seedText = [
2109
- componentBody,
2110
- propsTypeName2 && !ir.metadata.propsObjectName ? propsTypeName2 : "",
2111
- ...ir.metadata.namedExports.filter((block) => block.source === null).flatMap((block) => block.specifiers.map((s) => s.name))
2112
- ].filter(Boolean).join(`
2113
- `);
2114
- const included = new Set;
2115
- for (const typeDef of ir.metadata.typeDefinitions) {
2116
- if (new RegExp(`\\b${typeDef.name}\\b`).test(seedText)) {
2117
- included.add(typeDef.name);
2118
- }
2119
- }
2120
- let changed = true;
2121
- while (changed) {
2122
- changed = false;
2123
- for (const typeDef of ir.metadata.typeDefinitions) {
2124
- if (included.has(typeDef.name))
2125
- continue;
2126
- for (const name of included) {
2127
- const includedDef = ir.metadata.typeDefinitions.find((t) => t.name === name);
2128
- if (includedDef && new RegExp(`\\b${typeDef.name}\\b`).test(includedDef.definition)) {
2129
- included.add(typeDef.name);
2130
- changed = true;
2131
- break;
2132
- }
2133
- }
2134
- }
2135
- }
2136
- for (const typeDef of ir.metadata.typeDefinitions) {
2137
- if (included.has(typeDef.name))
2138
- lines.push(typeDef.definition);
2139
- }
2140
- } else {
2141
- for (const typeDef of ir.metadata.typeDefinitions) {
2142
- lines.push(typeDef.definition);
2143
- }
2144
- }
2145
2391
  const propsTypeName = this.getPropsTypeName(ir);
2146
2392
  if (propsTypeName && !ir.metadata.propsObjectName) {
2147
- lines.push("");
2148
2393
  lines.push(`type ${this.componentName}PropsWithHydration = ${propsTypeName} & {`);
2149
2394
  lines.push(" __instanceId?: string");
2150
2395
  lines.push(" __bfScope?: string");
@@ -2224,12 +2469,7 @@ export default ${this.componentName}` : "";
2224
2469
  } else {
2225
2470
  const hydrationProps = `__instanceId, ${bfScopeAlias}, ${bfChildAlias}, ${bfParentPropsAlias}, ${bfParentAlias}, ${bfMountAlias}, ${dataKeyAlias}`;
2226
2471
  const parts = [];
2227
- const propsParams = ir.metadata.propsParams.map((p) => {
2228
- const callerKey = p.sourceName ?? p.name;
2229
- const localName = p.name;
2230
- const binding = callerKey === localName ? localName : `${isIdentifierName(callerKey) ? callerKey : JSON.stringify(callerKey)}: ${localName}`;
2231
- return p.defaultValue ? `${binding} = ${p.defaultValue}` : binding;
2232
- }).join(", ");
2472
+ const propsParams = ir.metadata.propsParams.map((p) => propsDestructureBinding(p)).join(", ");
2233
2473
  if (propsParams) {
2234
2474
  parts.push(propsParams);
2235
2475
  }
@@ -2270,7 +2510,8 @@ export default ${this.componentName}` : "";
2270
2510
  lines.push(` const __hydrateProps: Record<string, unknown> = {}`);
2271
2511
  for (const p of propsToSerialize) {
2272
2512
  const propAccess = propsObjectName ? `${propsObjectName}.${p.name}` : p.name;
2273
- lines.push(` if (typeof ${propAccess} !== 'function' && !(typeof ${propAccess} === 'object' && ${propAccess} !== null && 'isEscaped' in ${propAccess})) __hydrateProps['${p.name}'] = ${propAccess}`);
2513
+ const callerKey = p.sourceName ?? p.name;
2514
+ lines.push(` if (typeof ${propAccess} !== 'function' && !(typeof ${propAccess} === 'object' && ${propAccess} !== null && 'isEscaped' in ${propAccess})) __hydrateProps['${callerKey}'] = ${propAccess}`);
2274
2515
  }
2275
2516
  lines.push(` const __bfPropsJson = __bfParentProps || (Object.keys(__hydrateProps).length > 0 ? JSON.stringify(__hydrateProps) : undefined)`);
2276
2517
  } else if (hasClientInteractivity && isRootComponent) {
@@ -2335,6 +2576,7 @@ export default ${this.componentName}` : "";
2335
2576
  case "literal":
2336
2577
  return JSON.stringify(v.value);
2337
2578
  case "expression":
2579
+ return this.expressionValueToJs(v);
2338
2580
  case "spread":
2339
2581
  return v.expr;
2340
2582
  case "template":
@@ -2406,18 +2648,26 @@ export default ${this.componentName}` : "";
2406
2648
  if (cond.clientOnly && cond.slotId) {
2407
2649
  return `{bfComment("cond-start:${cond.slotId}")}{bfComment("cond-end:${cond.slotId}")}`;
2408
2650
  }
2651
+ return `{${this.renderConditionalBody(cond, ctx)}}`;
2652
+ }
2653
+ renderConditionalBody(cond, ctx) {
2409
2654
  const branchCtx = ctx?.isLoopItemRoot ? { isLoopItemRoot: true } : undefined;
2655
+ if (!cond.slotId) {
2656
+ const whenTrue2 = this.renderBareBranch(cond.whenTrue, branchCtx);
2657
+ let whenFalse2 = this.renderBareBranch(cond.whenFalse, branchCtx);
2658
+ if (!whenFalse2 || whenFalse2 === "" || whenFalse2 === "null") {
2659
+ whenFalse2 = "null";
2660
+ }
2661
+ return `${cond.condition} ? ${whenTrue2} : ${whenFalse2}`;
2662
+ }
2410
2663
  const whenTrue = this.renderNodeRawCtx(cond.whenTrue, branchCtx);
2411
2664
  let whenFalse = this.renderNodeRawCtx(cond.whenFalse, branchCtx);
2412
2665
  if (!whenFalse || whenFalse === "" || whenFalse === "null") {
2413
2666
  whenFalse = "null";
2414
2667
  }
2415
- if (cond.slotId) {
2416
- const trueWithMarker = this.wrapWithCondMarker(cond.whenTrue, whenTrue, cond.slotId);
2417
- const falseWithMarker = cond.whenFalse.type === "expression" && cond.whenFalse.expr === "null" ? `<>{bfComment("cond-start:${cond.slotId}")}{bfComment("cond-end:${cond.slotId}")}</>` : this.wrapWithCondMarker(cond.whenFalse, whenFalse, cond.slotId);
2418
- return `{${cond.condition} ? ${trueWithMarker} : ${falseWithMarker}}`;
2419
- }
2420
- return `{${cond.condition} ? ${whenTrue} : ${whenFalse}}`;
2668
+ const trueWithMarker = this.wrapWithCondMarker(cond.whenTrue, whenTrue, cond.slotId);
2669
+ const falseWithMarker = cond.whenFalse.type === "expression" && cond.whenFalse.expr === "null" ? `<>{bfComment("cond-start:${cond.slotId}")}{bfComment("cond-end:${cond.slotId}")}</>` : this.wrapWithCondMarker(cond.whenFalse, whenFalse, cond.slotId);
2670
+ return `${cond.condition} ? ${trueWithMarker} : ${falseWithMarker}`;
2421
2671
  }
2422
2672
  renderNodeRawCtx(node, ctx) {
2423
2673
  if (node.type === "expression") {
@@ -2427,6 +2677,17 @@ export default ${this.componentName}` : "";
2427
2677
  }
2428
2678
  return this.renderNode(node, ctx);
2429
2679
  }
2680
+ renderBareBranch(node, ctx) {
2681
+ if (node.type === "expression") {
2682
+ if (node.expr === "null" || node.expr === "undefined")
2683
+ return "null";
2684
+ return node.expr;
2685
+ }
2686
+ if (node.type === "conditional" && !(node.clientOnly && node.slotId)) {
2687
+ return this.renderConditionalBody(node, ctx);
2688
+ }
2689
+ return this.renderNode(node, ctx);
2690
+ }
2430
2691
  wrapWithCondMarker(node, content, condId) {
2431
2692
  if (node.type === "component") {
2432
2693
  return `<>{bfComment("cond-start:${condId}")}${content}{bfComment("cond-end:${condId}")}</>`;
@@ -2578,10 +2839,11 @@ export default ${this.componentName}` : "";
2578
2839
  elementAttrEmitter = {
2579
2840
  emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
2580
2841
  emitExpression: (value, name) => {
2842
+ const expr = this.expressionValueToJs(value);
2581
2843
  if (isBooleanAttr(name) || value.presenceOrUndefined) {
2582
- return `${name}={(${value.expr}) || undefined}`;
2844
+ return `${name}={(${expr}) || undefined}`;
2583
2845
  }
2584
- return `${name}={${value.expr}}`;
2846
+ return `${name}={${expr}}`;
2585
2847
  },
2586
2848
  emitBooleanAttr: (_value, name) => name,
2587
2849
  emitBooleanShorthand: () => "",
@@ -2591,7 +2853,7 @@ export default ${this.componentName}` : "";
2591
2853
  };
2592
2854
  componentPropEmitter = {
2593
2855
  emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
2594
- emitExpression: (value, name) => `${name}={${value.expr}}`,
2856
+ emitExpression: (value, name) => `${name}={${this.expressionValueToJs(value)}}`,
2595
2857
  emitBooleanAttr: (_value, name) => name,
2596
2858
  emitBooleanShorthand: (_value, name) => name,
2597
2859
  emitTemplate: (value, name) => `${name}={${this.renderTemplateLiteralParts(value.parts)}}`,
@@ -2639,6 +2901,7 @@ export default ${this.componentName}` : "";
2639
2901
  case "literal":
2640
2902
  return JSON.stringify(value.value);
2641
2903
  case "expression":
2904
+ return this.expressionValueToJs(value);
2642
2905
  case "spread":
2643
2906
  return value.expr;
2644
2907
  case "template":
@@ -2651,19 +2914,7 @@ export default ${this.componentName}` : "";
2651
2914
  }
2652
2915
  }
2653
2916
  renderTemplateLiteralParts(parts) {
2654
- let output = "`";
2655
- for (const part of parts) {
2656
- if (part.type === "string") {
2657
- output += part.value;
2658
- } else if (part.type === "ternary") {
2659
- output += `\${${part.condition} ? '${part.whenTrue}' : '${part.whenFalse}'}`;
2660
- } else if (part.type === "lookup") {
2661
- const obj = "{" + Object.entries(part.cases).map(([k, v]) => `${JSON.stringify(k)}: ${JSON.stringify(v)}`).join(", ") + "}";
2662
- output += `\${(${obj})[${part.key}]}`;
2663
- }
2664
- }
2665
- output += "`";
2666
- return output;
2917
+ return this.renderTemplatePartsAsJs(parts);
2667
2918
  }
2668
2919
  }
2669
2920
  var honoAdapter = new HonoAdapter;