@barefootjs/jsx 0.13.0 → 0.15.0

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 (46) hide show
  1. package/dist/adapters/env-signal.d.ts +40 -0
  2. package/dist/adapters/env-signal.d.ts.map +1 -0
  3. package/dist/adapters/parsed-expr-emitter.d.ts +2 -1
  4. package/dist/adapters/parsed-expr-emitter.d.ts.map +1 -1
  5. package/dist/analyzer.d.ts.map +1 -1
  6. package/dist/augment-inherited-props.d.ts +42 -1
  7. package/dist/augment-inherited-props.d.ts.map +1 -1
  8. package/dist/builtins.d.ts +33 -0
  9. package/dist/builtins.d.ts.map +1 -0
  10. package/dist/compiler.d.ts.map +1 -1
  11. package/dist/errors.d.ts +1 -0
  12. package/dist/errors.d.ts.map +1 -1
  13. package/dist/expression-parser.d.ts +48 -1
  14. package/dist/expression-parser.d.ts.map +1 -1
  15. package/dist/index.d.ts +5 -4
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +411 -26
  18. package/dist/ir-to-client-js/imports.d.ts.map +1 -1
  19. package/dist/jsx-to-ir.d.ts.map +1 -1
  20. package/dist/profiler.d.ts +37 -0
  21. package/dist/profiler.d.ts.map +1 -1
  22. package/dist/ssr-defaults.d.ts.map +1 -1
  23. package/dist/types.d.ts +16 -0
  24. package/dist/types.d.ts.map +1 -1
  25. package/package.json +2 -2
  26. package/src/__tests__/compiler-stress-1244.test.ts +4 -2
  27. package/src/__tests__/expression-parser.test.ts +92 -1
  28. package/src/__tests__/ir-async.test.ts +8 -0
  29. package/src/__tests__/ir-builtin-import-scope.test.ts +188 -0
  30. package/src/__tests__/ir-region.test.ts +86 -0
  31. package/src/__tests__/profiler.test.ts +69 -0
  32. package/src/__tests__/ssr-defaults.test.ts +25 -0
  33. package/src/adapters/env-signal.ts +75 -0
  34. package/src/adapters/parsed-expr-emitter.ts +11 -0
  35. package/src/analyzer.ts +9 -0
  36. package/src/augment-inherited-props.ts +170 -2
  37. package/src/builtins.ts +63 -0
  38. package/src/compiler.ts +6 -2
  39. package/src/errors.ts +10 -0
  40. package/src/expression-parser.ts +156 -2
  41. package/src/index.ts +5 -2
  42. package/src/ir-to-client-js/imports.ts +5 -0
  43. package/src/jsx-to-ir.ts +189 -8
  44. package/src/profiler.ts +63 -0
  45. package/src/ssr-defaults.ts +55 -17
  46. package/src/types.ts +16 -0
package/dist/index.js CHANGED
@@ -2784,6 +2784,7 @@ var ErrorCodes = {
2784
2784
  JSX_BRANCH_LOCAL_IN_CALLBACK: "BF047",
2785
2785
  SHARED_PROGRAM_REQUIRED: "BF050",
2786
2786
  WRONG_PACKAGE_IMPORT: "BF051",
2787
+ BUILTIN_REQUIRES_IMPORT: "BF054",
2787
2788
  UNDECLARED_INIT_STATEMENT_REFERENCE: "BF052",
2788
2789
  STRIPPED_CLIENT_IMPORT_REFERENCED: "BF053",
2789
2790
  STAGE_REACTIVE_IN_TEMPLATE: "BF060",
@@ -2807,6 +2808,7 @@ var errorMessages = {
2807
2808
  [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>`.",
2808
2809
  [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.",
2809
2810
  [ErrorCodes.WRONG_PACKAGE_IMPORT]: "Import from wrong package.",
2811
+ [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.",
2810
2812
  [ErrorCodes.UNDECLARED_INIT_STATEMENT_REFERENCE]: "Init statement references an undeclared identifier. Declare it at module scope, inside the component, or import it — otherwise ESM strict mode throws ReferenceError at runtime.",
2811
2813
  [ErrorCodes.STRIPPED_CLIENT_IMPORT_REFERENCED]: "Import was stripped from the client bundle but its binding is still referenced. Client components ('use client' .tsx) are not callable as plain functions from imperative .ts modules — render them as JSX from a 'use client' parent instead. If the flagged name is a local shadow rather than the stripped import, please file an issue.",
2812
2814
  [ErrorCodes.STAGE_REACTIVE_IN_TEMPLATE]: "Reactive binding (signal getter or memo) referenced from template scope. The template lambda runs at module scope without the reactive context, so the value cannot be evaluated at SSR. Wrap the JSX expression in /* @client */ to defer it to hydrate, or restructure so the template uses a prop or static value.",
@@ -3822,7 +3824,10 @@ var CLIENT_EXPORTS = new Set([
3822
3824
  "createPortal",
3823
3825
  "isSSRPortal",
3824
3826
  "findSiblingSlot",
3825
- "cleanupPortalPlaceholder"
3827
+ "cleanupPortalPlaceholder",
3828
+ "searchParams",
3829
+ "Async",
3830
+ "Region"
3826
3831
  ]);
3827
3832
  function collectAmbientGlobals(node, ctx) {
3828
3833
  if (ts7.isVariableStatement(node)) {
@@ -3898,7 +3903,8 @@ function collectImport(node, ctx) {
3898
3903
  name: element.propertyName?.text ?? element.name.text,
3899
3904
  alias: element.propertyName ? element.name.text : null,
3900
3905
  isDefault: false,
3901
- isNamespace: false
3906
+ isNamespace: false,
3907
+ isTypeOnly: element.isTypeOnly
3902
3908
  });
3903
3909
  }
3904
3910
  }
@@ -5382,6 +5388,60 @@ function extractArrowBodyExpression(source) {
5382
5388
  return null;
5383
5389
  return expr.body.getText(sf).trim();
5384
5390
  }
5391
+ function parseProviderObjectLiteral(source) {
5392
+ const sf = ts8.createSourceFile("__provider__.ts", `const __x = (${source});`, ts8.ScriptTarget.Latest, true);
5393
+ const stmt = sf.statements[0];
5394
+ if (!stmt || !ts8.isVariableStatement(stmt))
5395
+ return null;
5396
+ let init = stmt.declarationList.declarations[0]?.initializer;
5397
+ while (init && ts8.isParenthesizedExpression(init))
5398
+ init = init.expression;
5399
+ if (!init || !ts8.isObjectLiteralExpression(init))
5400
+ return null;
5401
+ const isFunctionShaped = (e) => {
5402
+ let v = e;
5403
+ while (ts8.isParenthesizedExpression(v))
5404
+ v = v.expression;
5405
+ if (ts8.isArrowFunction(v) || ts8.isFunctionExpression(v))
5406
+ return true;
5407
+ if (ts8.isBinaryExpression(v) && (v.operatorToken.kind === ts8.SyntaxKind.QuestionQuestionToken || v.operatorToken.kind === ts8.SyntaxKind.BarBarToken)) {
5408
+ return isFunctionShaped(v.left) || isFunctionShaped(v.right);
5409
+ }
5410
+ return false;
5411
+ };
5412
+ const members = [];
5413
+ for (const prop of init.properties) {
5414
+ if (ts8.isShorthandPropertyAssignment(prop)) {
5415
+ members.push({ name: prop.name.text, kind: "expression", expr: prop.name.text });
5416
+ continue;
5417
+ }
5418
+ if (ts8.isMethodDeclaration(prop)) {
5419
+ const name2 = ts8.isIdentifier(prop.name) || ts8.isStringLiteral(prop.name) ? prop.name.text : null;
5420
+ if (name2 === null)
5421
+ return null;
5422
+ members.push({ name: name2, kind: "function" });
5423
+ continue;
5424
+ }
5425
+ if (!ts8.isPropertyAssignment(prop))
5426
+ return null;
5427
+ const name = ts8.isIdentifier(prop.name) || ts8.isStringLiteral(prop.name) ? prop.name.text : null;
5428
+ if (name === null)
5429
+ return null;
5430
+ let v = prop.initializer;
5431
+ while (ts8.isParenthesizedExpression(v))
5432
+ v = v.expression;
5433
+ if (ts8.isArrowFunction(v) && v.parameters.length === 0 && !ts8.isBlock(v.body)) {
5434
+ members.push({ name, kind: "getter", body: v.body.getText(sf).trim() });
5435
+ continue;
5436
+ }
5437
+ if (isFunctionShaped(v)) {
5438
+ members.push({ name, kind: "function" });
5439
+ continue;
5440
+ }
5441
+ members.push({ name, kind: "expression", expr: v.getText(sf).trim() });
5442
+ }
5443
+ return members;
5444
+ }
5385
5445
  function cssKebabCase(name) {
5386
5446
  return name.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase()).replace(/^ms-/, "-ms-");
5387
5447
  }
@@ -5532,6 +5592,9 @@ function convertNode(node, raw) {
5532
5592
  if (callee.property === "trim") {
5533
5593
  return { kind: "array-method", method: "trim", object: callee.object, args };
5534
5594
  }
5595
+ if (callee.property === "toFixed") {
5596
+ return { kind: "array-method", method: "toFixed", object: callee.object, args };
5597
+ }
5535
5598
  if (callee.property === "split") {
5536
5599
  return { kind: "array-method", method: "split", object: callee.object, args };
5537
5600
  }
@@ -5665,13 +5728,19 @@ function convertNode(node, raw) {
5665
5728
  if (ts8.isElementAccessExpression(node)) {
5666
5729
  const object = convertNode(node.expression, raw);
5667
5730
  const argNode = node.argumentExpression;
5731
+ if (!argNode) {
5732
+ return { kind: "unsupported", raw, reason: "Element access with no index expression" };
5733
+ }
5668
5734
  if (ts8.isNumericLiteral(argNode)) {
5669
5735
  return { kind: "member", object, property: argNode.text, computed: true };
5670
5736
  }
5671
5737
  if (ts8.isStringLiteral(argNode)) {
5672
5738
  return { kind: "member", object, property: argNode.text, computed: true };
5673
5739
  }
5674
- return { kind: "unsupported", raw, reason: "Complex computed property access" };
5740
+ const index = convertNode(argNode, raw);
5741
+ if (index.kind === "unsupported")
5742
+ return index;
5743
+ return { kind: "index-access", object, index };
5675
5744
  }
5676
5745
  if (ts8.isBinaryExpression(node)) {
5677
5746
  const left = convertNode(node.left, raw);
@@ -6147,6 +6216,8 @@ function findImpureDefaultNode(expr) {
6147
6216
  return null;
6148
6217
  case "member":
6149
6218
  return findImpureDefaultNode(expr.object);
6219
+ case "index-access":
6220
+ return findImpureDefaultNode(expr.object) ?? findImpureDefaultNode(expr.index);
6150
6221
  case "unary":
6151
6222
  return findImpureDefaultNode(expr.argument);
6152
6223
  case "binary":
@@ -6300,6 +6371,10 @@ function collectIdentifiers(expr, out) {
6300
6371
  case "member":
6301
6372
  collectIdentifiers(expr.object, out);
6302
6373
  return;
6374
+ case "index-access":
6375
+ collectIdentifiers(expr.object, out);
6376
+ collectIdentifiers(expr.index, out);
6377
+ return;
6303
6378
  case "binary":
6304
6379
  case "logical":
6305
6380
  collectIdentifiers(expr.left, out);
@@ -6366,6 +6441,8 @@ function substituteDestructuredFields(expr, fieldMap, syntheticParam, restName)
6366
6441
  };
6367
6442
  }
6368
6443
  return { kind: "member", object: walk(e.object), property: e.property, computed: e.computed };
6444
+ case "index-access":
6445
+ return { kind: "index-access", object: walk(e.object), index: walk(e.index) };
6369
6446
  case "binary":
6370
6447
  return { kind: "binary", op: e.op, left: walk(e.left), right: walk(e.right) };
6371
6448
  case "logical":
@@ -6544,6 +6621,15 @@ function checkSupport(expr) {
6544
6621
  }
6545
6622
  return { supported: true, level: "L2" };
6546
6623
  }
6624
+ case "index-access": {
6625
+ const objSupport = checkSupport(expr.object);
6626
+ if (!objSupport.supported)
6627
+ return objSupport;
6628
+ const indexSupport = checkSupport(expr.index);
6629
+ if (!indexSupport.supported)
6630
+ return indexSupport;
6631
+ return { supported: true, level: "L2" };
6632
+ }
6547
6633
  case "binary": {
6548
6634
  const leftSupport = checkSupport(expr.left);
6549
6635
  if (!leftSupport.supported)
@@ -6614,6 +6700,8 @@ function containsHigherOrder(expr) {
6614
6700
  return expr.args.some(containsHigherOrder) || containsHigherOrder(expr.callee);
6615
6701
  case "member":
6616
6702
  return containsHigherOrder(expr.object);
6703
+ case "index-access":
6704
+ return containsHigherOrder(expr.object) || containsHigherOrder(expr.index);
6617
6705
  case "binary":
6618
6706
  return containsHigherOrder(expr.left) || containsHigherOrder(expr.right);
6619
6707
  case "unary":
@@ -6722,6 +6810,8 @@ function exprToString(expr) {
6722
6810
  return `${exprToString(expr.callee)}(${expr.args.map(exprToString).join(", ")})`;
6723
6811
  case "member":
6724
6812
  return `${exprToString(expr.object)}.${expr.property}`;
6813
+ case "index-access":
6814
+ return `${exprToString(expr.object)}[${exprToString(expr.index)}]`;
6725
6815
  case "binary":
6726
6816
  return `${exprToString(expr.left)} ${expr.op} ${exprToString(expr.right)}`;
6727
6817
  case "unary":
@@ -6781,6 +6871,8 @@ function stringifyParsedExpr(expr) {
6781
6871
  const key = /^-?\d+$/.test(expr.property) ? expr.property : JSON.stringify(expr.property);
6782
6872
  return `${obj}[${key}]`;
6783
6873
  }
6874
+ case "index-access":
6875
+ return `${stringifyParsedExpr(expr.object)}[${stringifyParsedExpr(expr.index)}]`;
6784
6876
  case "binary":
6785
6877
  return `${stringifyParsedExpr(expr.left)} ${expr.op} ${stringifyParsedExpr(expr.right)}`;
6786
6878
  case "unary":
@@ -6831,6 +6923,26 @@ function identifierPath(callee) {
6831
6923
  return null;
6832
6924
  }
6833
6925
 
6926
+ // src/builtins.ts
6927
+ var CLIENT_BUILTIN_SOURCE = "@barefootjs/client";
6928
+ function isClientBuiltinName(name) {
6929
+ return name === "Async" || name === "Region";
6930
+ }
6931
+ function stripClientBuiltinImports(imports) {
6932
+ const result = [];
6933
+ for (const imp of imports) {
6934
+ if (imp.source !== CLIENT_BUILTIN_SOURCE || imp.isTypeOnly || imp.specifiers.length === 0) {
6935
+ result.push(imp);
6936
+ continue;
6937
+ }
6938
+ const kept = imp.specifiers.filter((spec) => spec.isDefault || spec.isNamespace || spec.isTypeOnly || !isClientBuiltinName(spec.name));
6939
+ if (kept.length === 0)
6940
+ continue;
6941
+ result.push(kept.length === imp.specifiers.length ? imp : { ...imp, specifiers: kept });
6942
+ }
6943
+ return result;
6944
+ }
6945
+
6834
6946
  // src/reactivity-checker.ts
6835
6947
  import ts9 from "typescript";
6836
6948
  var REACTIVE_BRAND = "__reactive";
@@ -7269,6 +7381,7 @@ function createTransformContext(analyzer) {
7269
7381
  filePath: analyzer.filePath,
7270
7382
  slotIdCounter: 0,
7271
7383
  asyncIdCounter: 0,
7384
+ regionIdCounter: 0,
7272
7385
  loopMarkerCounter: 0,
7273
7386
  spreadIdCounter: 0,
7274
7387
  isRoot: true,
@@ -7456,14 +7569,72 @@ function transformNode(node, ctx) {
7456
7569
  }
7457
7570
  return null;
7458
7571
  }
7572
+ function clientBuiltinTags(ctx) {
7573
+ if (ctx._clientBuiltinTags)
7574
+ return ctx._clientBuiltinTags;
7575
+ const map = new Map;
7576
+ for (const imp of ctx.analyzer.imports) {
7577
+ if (imp.source !== CLIENT_BUILTIN_SOURCE || imp.isTypeOnly)
7578
+ continue;
7579
+ for (const spec of imp.specifiers) {
7580
+ if (spec.isDefault || spec.isNamespace || spec.isTypeOnly)
7581
+ continue;
7582
+ if (isClientBuiltinName(spec.name)) {
7583
+ map.set(spec.alias ?? spec.name, spec.name);
7584
+ }
7585
+ }
7586
+ }
7587
+ ctx._clientBuiltinTags = map;
7588
+ return map;
7589
+ }
7590
+ function isNameBound(ctx, name) {
7591
+ const a = ctx.analyzer;
7592
+ if (a.ambientGlobals.has(name))
7593
+ return true;
7594
+ if (a.localFunctions.some((f) => f.name === name))
7595
+ return true;
7596
+ if (a.localConstants.some((c) => c.name === name))
7597
+ return true;
7598
+ for (const imp of a.imports) {
7599
+ if (imp.isTypeOnly)
7600
+ continue;
7601
+ for (const spec of imp.specifiers) {
7602
+ if (spec.isTypeOnly)
7603
+ continue;
7604
+ if ((spec.alias ?? spec.name) === name)
7605
+ return true;
7606
+ }
7607
+ }
7608
+ return false;
7609
+ }
7610
+ function reportBuiltinNotImported(ctx, node, tagName) {
7611
+ ctx.analyzer.errors.push(createError(ErrorCodes.BUILTIN_REQUIRES_IMPORT, getSourceLocation(node, ctx.sourceFile, ctx.filePath), {
7612
+ severity: "error",
7613
+ message: `<${tagName}> must be imported from '${CLIENT_BUILTIN_SOURCE}' to be recognised as a compiler built-in.`,
7614
+ suggestion: {
7615
+ message: `Add: import { ${tagName} } from '${CLIENT_BUILTIN_SOURCE}'`
7616
+ }
7617
+ }));
7618
+ }
7619
+ function dispatchClientBuiltin(tagName, ctx, diagNode, transformAsync, transformRegion) {
7620
+ const builtin = clientBuiltinTags(ctx).get(tagName);
7621
+ if (builtin === "Async")
7622
+ return transformAsync();
7623
+ if (builtin === "Region")
7624
+ return transformRegion();
7625
+ if (isClientBuiltinName(tagName) && !isNameBound(ctx, tagName)) {
7626
+ reportBuiltinNotImported(ctx, diagNode, tagName);
7627
+ }
7628
+ return null;
7629
+ }
7459
7630
  function transformJsxElement(node, ctx) {
7460
7631
  const tagName = node.openingElement.tagName.getText(ctx.sourceFile);
7461
7632
  if (tagName.endsWith(".Provider") && /^[A-Z]/.test(tagName)) {
7462
7633
  return transformProviderElement(node, ctx, tagName);
7463
7634
  }
7464
- if (tagName === "Async") {
7465
- return transformAsyncElement(node, ctx);
7466
- }
7635
+ const builtin = dispatchClientBuiltin(tagName, ctx, node.openingElement, () => transformAsyncElement(node, ctx), () => transformRegionElement(node, ctx));
7636
+ if (builtin)
7637
+ return builtin;
7467
7638
  const isComponent = /^[A-Z]/.test(tagName);
7468
7639
  if (isComponent) {
7469
7640
  const resolved = resolveMemberExpressionTag(node.openingElement.tagName, ctx);
@@ -7498,9 +7669,9 @@ function transformSelfClosingElement(node, ctx) {
7498
7669
  if (tagName.endsWith(".Provider") && /^[A-Z]/.test(tagName)) {
7499
7670
  return transformSelfClosingProviderElement(node, ctx, tagName);
7500
7671
  }
7501
- if (tagName === "Async") {
7502
- return transformSelfClosingAsyncElement(node, ctx);
7503
- }
7672
+ const builtin = dispatchClientBuiltin(tagName, ctx, node, () => transformSelfClosingAsyncElement(node, ctx), () => transformSelfClosingRegionElement(node, ctx));
7673
+ if (builtin)
7674
+ return builtin;
7504
7675
  const isComponent = /^[A-Z]/.test(tagName);
7505
7676
  if (isComponent) {
7506
7677
  const resolved = resolveMemberExpressionTag(node.tagName, ctx);
@@ -7624,6 +7795,44 @@ function transformSelfClosingAsyncElement(node, ctx) {
7624
7795
  loc: getSourceLocation(node, ctx.sourceFile, ctx.filePath)
7625
7796
  };
7626
7797
  }
7798
+ function regionId(ctx) {
7799
+ return `${computeFileScope(ctx.filePath)}:${ctx.regionIdCounter++}`;
7800
+ }
7801
+ function transformRegionElement(node, ctx) {
7802
+ const id = regionId(ctx);
7803
+ const needsScope = ctx.isRoot;
7804
+ ctx.isRoot = false;
7805
+ const children = transformChildren(node.children, ctx);
7806
+ return {
7807
+ type: "element",
7808
+ tag: "div",
7809
+ attrs: [],
7810
+ events: [],
7811
+ ref: null,
7812
+ children,
7813
+ slotId: null,
7814
+ needsScope,
7815
+ regionId: id,
7816
+ loc: getSourceLocation(node, ctx.sourceFile, ctx.filePath)
7817
+ };
7818
+ }
7819
+ function transformSelfClosingRegionElement(node, ctx) {
7820
+ const id = regionId(ctx);
7821
+ const needsScope = ctx.isRoot;
7822
+ ctx.isRoot = false;
7823
+ return {
7824
+ type: "element",
7825
+ tag: "div",
7826
+ attrs: [],
7827
+ events: [],
7828
+ ref: null,
7829
+ children: [],
7830
+ slotId: null,
7831
+ needsScope,
7832
+ regionId: id,
7833
+ loc: getSourceLocation(node, ctx.sourceFile, ctx.filePath)
7834
+ };
7835
+ }
7627
7836
  function transformComponentElement(node, ctx, name) {
7628
7837
  const props = processComponentProps(node.openingElement.attributes, ctx);
7629
7838
  ctx.isRoot = false;
@@ -11406,6 +11615,8 @@ function collectUserDomImports(ir) {
11406
11615
  if (runtimeSources.has(imp.source) && !imp.isTypeOnly) {
11407
11616
  for (const spec of imp.specifiers) {
11408
11617
  if (!spec.isDefault && !spec.isNamespace) {
11618
+ if (isClientBuiltinName(spec.name))
11619
+ continue;
11409
11620
  userImports.push(spec.alias ? `${spec.name} as ${spec.alias}` : spec.name);
11410
11621
  }
11411
11622
  }
@@ -16842,6 +17053,7 @@ function buildSyntheticDeclaration(name, arrow, sourceFile) {
16842
17053
  // src/ssr-defaults.ts
16843
17054
  import ts16 from "typescript";
16844
17055
  var UNRESOLVED = Symbol("unresolved");
17056
+ var NO_RETURN = Symbol("no-return");
16845
17057
  function extractSsrDefaults(metadata) {
16846
17058
  const out = {};
16847
17059
  const propsLike = new Set;
@@ -16938,6 +17150,34 @@ function tryStaticEval(expr, ctx) {
16938
17150
  return UNRESOLVED;
16939
17151
  return evalNode(node, ctx);
16940
17152
  }
17153
+ function evalStatementsForReturn(statements, ctx) {
17154
+ for (const stmt of statements) {
17155
+ if (ts16.isVariableStatement(stmt)) {
17156
+ for (const d of stmt.declarationList.declarations) {
17157
+ if (!ts16.isIdentifier(d.name) || !d.initializer)
17158
+ continue;
17159
+ const v = evalNode(d.initializer, ctx);
17160
+ if (v !== UNRESOLVED)
17161
+ ctx.bindings[d.name.text] = v;
17162
+ }
17163
+ } else if (ts16.isReturnStatement(stmt)) {
17164
+ return stmt.expression ? evalNode(stmt.expression, ctx) : UNRESOLVED;
17165
+ } else if (ts16.isIfStatement(stmt)) {
17166
+ const cond = evalNode(stmt.expression, ctx);
17167
+ if (cond === UNRESOLVED)
17168
+ return UNRESOLVED;
17169
+ const branch = cond ? stmt.thenStatement : stmt.elseStatement;
17170
+ if (branch) {
17171
+ const taken = evalStatementsForReturn(ts16.isBlock(branch) ? branch.statements : [branch], ctx);
17172
+ if (taken !== NO_RETURN)
17173
+ return taken;
17174
+ }
17175
+ } else {
17176
+ return UNRESOLVED;
17177
+ }
17178
+ }
17179
+ return NO_RETURN;
17180
+ }
16941
17181
  function parseExpression2(expr) {
16942
17182
  const sf = ts16.createSourceFile("__ssr_default__.ts", `(${expr})`, ts16.ScriptTarget.Latest, false, ts16.ScriptKind.TS);
16943
17183
  const stmt = sf.statements[0];
@@ -16964,22 +17204,8 @@ function evalNode(node, ctx) {
16964
17204
  return evalNode(node.body, ctx);
16965
17205
  const localBindings = { ...ctx.bindings };
16966
17206
  const localCtx = { ...ctx, bindings: localBindings };
16967
- for (const stmt of node.body.statements) {
16968
- if (ts16.isVariableStatement(stmt)) {
16969
- for (const d of stmt.declarationList.declarations) {
16970
- if (!ts16.isIdentifier(d.name) || !d.initializer)
16971
- continue;
16972
- const v = evalNode(d.initializer, localCtx);
16973
- if (v !== UNRESOLVED)
16974
- localBindings[d.name.text] = v;
16975
- }
16976
- } else if (ts16.isReturnStatement(stmt)) {
16977
- return stmt.expression ? evalNode(stmt.expression, localCtx) : UNRESOLVED;
16978
- } else {
16979
- return UNRESOLVED;
16980
- }
16981
- }
16982
- return UNRESOLVED;
17207
+ const result = evalStatementsForReturn(node.body.statements, localCtx);
17208
+ return result === NO_RETURN ? UNRESOLVED : result;
16983
17209
  }
16984
17210
  if (ts16.isNumericLiteral(node))
16985
17211
  return Number(node.text);
@@ -17488,7 +17714,7 @@ function buildMetadata(ctx) {
17488
17714
  onMounts: ctx.onMounts,
17489
17715
  initStatements: ctx.initStatements,
17490
17716
  imports: ctx.imports,
17491
- templateImports: ctx.imports,
17717
+ templateImports: stripClientBuiltinImports(ctx.imports),
17492
17718
  namedExports: ctx.namedExports,
17493
17719
  localFunctions: ctx.localFunctions,
17494
17720
  localConstants: ctx.localConstants
@@ -18144,6 +18370,8 @@ function emitParsedExpr(expr, emitter) {
18144
18370
  return emitter.call(expr.callee, expr.args, emit);
18145
18371
  case "member":
18146
18372
  return emitter.member(expr.object, expr.property, expr.computed, emit);
18373
+ case "index-access":
18374
+ return emitter.indexAccess(expr.object, expr.index, emit);
18147
18375
  case "binary":
18148
18376
  return emitter.binary(expr.op, expr.left, expr.right, emit);
18149
18377
  case "unary":
@@ -18182,6 +18410,33 @@ function emitParsedExpr(expr, emitter) {
18182
18410
  }
18183
18411
  }
18184
18412
  }
18413
+ // src/adapters/env-signal.ts
18414
+ function searchParamsLocalNames(metadata) {
18415
+ const names = new Set;
18416
+ for (const imp of metadata.imports) {
18417
+ if (imp.source !== "@barefootjs/client" || imp.isTypeOnly)
18418
+ continue;
18419
+ for (const s of imp.specifiers) {
18420
+ if (s.isTypeOnly || s.isNamespace || s.isDefault)
18421
+ continue;
18422
+ if (s.name === "searchParams")
18423
+ names.add(s.alias ?? s.name);
18424
+ }
18425
+ }
18426
+ return names;
18427
+ }
18428
+ function importsSearchParams(metadata) {
18429
+ return searchParamsLocalNames(metadata).size > 0;
18430
+ }
18431
+ function matchSearchParamsMethodCall(callee, args, localNames) {
18432
+ if (callee.kind !== "member" || callee.computed)
18433
+ return null;
18434
+ const recv = callee.object;
18435
+ if (recv.kind !== "call" || recv.args.length !== 0 || recv.callee.kind !== "identifier" || !localNames.has(recv.callee.name)) {
18436
+ return null;
18437
+ }
18438
+ return { method: callee.property, args };
18439
+ }
18185
18440
  // src/adapters/ir-node-emitter.ts
18186
18441
  function emitIRNode(node, emitter, ctx) {
18187
18442
  const emit = (child, childCtx) => emitIRNode(child, emitter, childCtx);
@@ -19836,6 +20091,7 @@ function findSourceFile2(meta) {
19836
20091
  }
19837
20092
  // src/profiler.ts
19838
20093
  import ts20 from "typescript";
20094
+ var PROFILE_SCHEMA_VERSION = 1;
19839
20095
  var DEFAULT_FANOUT_THRESHOLD = 8;
19840
20096
  function buildStaticBudget(source, filePath, componentName, options = {}) {
19841
20097
  const threshold = options.fanOutThreshold ?? DEFAULT_FANOUT_THRESHOLD;
@@ -19847,11 +20103,18 @@ function buildStaticBudget(source, filePath, componentName, options = {}) {
19847
20103
  const { direct, total } = subscriberCounts(graph, s.name);
19848
20104
  return { signal: s.name, subscribers: total, direct, hot: direct >= threshold, loc: s.loc };
19849
20105
  }).sort((a, b) => b.subscribers - a.subscribers);
20106
+ const handlers = graph.domBindings.filter((b) => b.type === "event").flatMap((b) => {
20107
+ if (!b.loc)
20108
+ return [];
20109
+ const eventName = b.label.match(/^(\w+)\s+handler/)?.[1] ?? "event";
20110
+ return [{ name: `${eventName}@${b.slotId}`, loc: { file: b.loc.file, line: b.loc.start.line } }];
20111
+ });
19850
20112
  const { depth, chain } = longestMemoChain(graph);
19851
20113
  const hasReactiveState = graph.signals.length > 0 || graph.memos.length > 0;
19852
20114
  const observedInComponent = graph.signals.some((s) => transitiveSubscriberCount(graph, s.name) > 0) || graph.memos.some((m) => transitiveSubscriberCount(graph, m.name) > 0);
19853
20115
  const crossComponentOnly = hasReactiveState && !observedInComponent;
19854
20116
  return {
20117
+ schemaVersion: PROFILE_SCHEMA_VERSION,
19855
20118
  componentName: summary.componentName,
19856
20119
  sourceFile: summary.sourceFile,
19857
20120
  kind: "static-budget",
@@ -19863,6 +20126,7 @@ function buildStaticBudget(source, filePath, componentName, options = {}) {
19863
20126
  memoChainDepth: depth,
19864
20127
  memoChainLongest: chain,
19865
20128
  fanOut,
20129
+ handlers,
19866
20130
  crossComponentOnly
19867
20131
  };
19868
20132
  }
@@ -19939,6 +20203,13 @@ function formatStaticBudget(b) {
19939
20203
  lines.push(` ${f.signal.padEnd(12)} → ${f.subscribers} subscribers${detail}${f.hot ? " ⚠ high" : ""}`);
19940
20204
  }
19941
20205
  }
20206
+ if (b.handlers.length > 0) {
20207
+ lines.push(` handlers (${b.handlers.length}):`);
20208
+ for (const h of b.handlers) {
20209
+ const file = h.loc.file.split("/").pop() ?? h.loc.file;
20210
+ lines.push(` ${h.name.padEnd(16)} ${file}:${h.loc.line}`);
20211
+ }
20212
+ }
19942
20213
  if (b.crossComponentOnly) {
19943
20214
  lines.push(` ⓘ compound: ${b.signals} signal(s) / ${b.memos} memo(s) but 0 in-component subscriptions —`);
19944
20215
  lines.push(" consumers are likely in composed child components (or it is read only from handlers);");
@@ -19962,6 +20233,7 @@ function diffStaticBudget(base, head) {
19962
20233
  fanOut.sort((a, b) => b.after - b.before - (a.after - a.before));
19963
20234
  const d = {
19964
20235
  kind: "diff",
20236
+ schemaVersion: PROFILE_SCHEMA_VERSION,
19965
20237
  componentName: head.componentName,
19966
20238
  signals: head.signals - base.signals,
19967
20239
  memos: head.memos - base.memos,
@@ -20531,6 +20803,7 @@ function buildProfileReport(input) {
20531
20803
  }
20532
20804
  return {
20533
20805
  kind: "profile",
20806
+ schemaVersion: PROFILE_SCHEMA_VERSION,
20534
20807
  componentName: primary.componentName,
20535
20808
  sourceFile: primary.sourceFile,
20536
20809
  scenario,
@@ -21002,6 +21275,18 @@ function augmentInheritedPropAccesses(ir) {
21002
21275
  const el = node;
21003
21276
  for (const attr of el.attrs ?? []) {
21004
21277
  const v = attr.value;
21278
+ if (v?.parts) {
21279
+ for (const part of v.parts) {
21280
+ if (part.type === "string")
21281
+ scan(part.value);
21282
+ else if (part.type === "ternary") {
21283
+ scan(part.condition);
21284
+ scan(part.whenTrue);
21285
+ scan(part.whenFalse);
21286
+ } else if (part.type === "lookup")
21287
+ scan(part.key);
21288
+ }
21289
+ }
21005
21290
  if (v?.kind === "expression" && typeof v.expr === "string") {
21006
21291
  scan(v.expr);
21007
21292
  const expr = v.expr.trim();
@@ -21021,6 +21306,11 @@ function augmentInheritedPropAccesses(ir) {
21021
21306
  const c = child;
21022
21307
  walk(c.element ?? child);
21023
21308
  }
21309
+ const branchy = node;
21310
+ walk(branchy.whenTrue);
21311
+ walk(branchy.whenFalse);
21312
+ walk(branchy.consequent);
21313
+ walk(branchy.alternate);
21024
21314
  };
21025
21315
  walk(ir.root);
21026
21316
  for (const name of accessed) {
@@ -21038,6 +21328,94 @@ function augmentInheritedPropAccesses(ir) {
21038
21328
  existing.add(name);
21039
21329
  }
21040
21330
  }
21331
+ function parseStaticStringConst(source) {
21332
+ const sf = ts21.createSourceFile("__const.ts", `const __x = (${source});`, ts21.ScriptTarget.Latest, false);
21333
+ const stmt = sf.statements[0];
21334
+ if (!stmt || !ts21.isVariableStatement(stmt))
21335
+ return null;
21336
+ let init = stmt.declarationList.declarations[0]?.initializer;
21337
+ while (init && ts21.isParenthesizedExpression(init))
21338
+ init = init.expression;
21339
+ if (!init)
21340
+ return null;
21341
+ if (ts21.isStringLiteral(init) || ts21.isNoSubstitutionTemplateLiteral(init)) {
21342
+ return init.text;
21343
+ }
21344
+ return evalStringArrayJoin(source);
21345
+ }
21346
+ function evalTemplateOfStringConsts(source, resolved) {
21347
+ const sf = ts21.createSourceFile("__const.ts", `const __x = (${source});`, ts21.ScriptTarget.Latest, false);
21348
+ const stmt = sf.statements[0];
21349
+ if (!stmt || !ts21.isVariableStatement(stmt))
21350
+ return null;
21351
+ let init = stmt.declarationList.declarations[0]?.initializer;
21352
+ while (init && ts21.isParenthesizedExpression(init))
21353
+ init = init.expression;
21354
+ if (!init || !ts21.isTemplateExpression(init))
21355
+ return null;
21356
+ let out = init.head.text;
21357
+ for (const span of init.templateSpans) {
21358
+ if (!ts21.isIdentifier(span.expression))
21359
+ return null;
21360
+ const value = resolved.get(span.expression.text);
21361
+ if (value === undefined)
21362
+ return null;
21363
+ out += value + span.literal.text;
21364
+ }
21365
+ return out;
21366
+ }
21367
+ function collectModuleStringConsts(constants) {
21368
+ const map = new Map;
21369
+ const candidates = (constants ?? []).filter((c) => c.isModule && c.value !== undefined);
21370
+ let progressed = true;
21371
+ while (progressed) {
21372
+ progressed = false;
21373
+ for (const c of candidates) {
21374
+ if (map.has(c.name))
21375
+ continue;
21376
+ const literal = parseStaticStringConst(c.value) ?? evalTemplateOfStringConsts(c.value, map);
21377
+ if (literal !== null) {
21378
+ map.set(c.name, literal);
21379
+ progressed = true;
21380
+ }
21381
+ }
21382
+ }
21383
+ return map;
21384
+ }
21385
+ function lookupStaticRecordLiteral(objectName, key, constants) {
21386
+ const constInfo = (constants ?? []).find((c) => c.name === objectName && c.isModule);
21387
+ if (constInfo?.value === undefined)
21388
+ return null;
21389
+ const sf = ts21.createSourceFile("__rec.ts", `(${constInfo.value})`, ts21.ScriptTarget.Latest, true);
21390
+ if (sf.statements.length !== 1)
21391
+ return null;
21392
+ const stmt = sf.statements[0];
21393
+ if (!ts21.isExpressionStatement(stmt))
21394
+ return null;
21395
+ let parsed = stmt.expression;
21396
+ while (ts21.isParenthesizedExpression(parsed))
21397
+ parsed = parsed.expression;
21398
+ if (!ts21.isObjectLiteralExpression(parsed))
21399
+ return null;
21400
+ for (const prop of parsed.properties) {
21401
+ if (!ts21.isPropertyAssignment(prop))
21402
+ continue;
21403
+ const name = prop.name;
21404
+ const propKey = ts21.isIdentifier(name) || ts21.isStringLiteral(name) || ts21.isNoSubstitutionTemplateLiteral(name) ? name.text : null;
21405
+ if (propKey !== key)
21406
+ continue;
21407
+ let v = prop.initializer;
21408
+ while (ts21.isParenthesizedExpression(v))
21409
+ v = v.expression;
21410
+ if (ts21.isNumericLiteral(v))
21411
+ return { kind: "number", text: v.text };
21412
+ if (ts21.isStringLiteral(v) || ts21.isNoSubstitutionTemplateLiteral(v)) {
21413
+ return { kind: "string", text: v.text };
21414
+ }
21415
+ return null;
21416
+ }
21417
+ return null;
21418
+ }
21041
21419
  function evalStringArrayJoin(source) {
21042
21420
  const sf = ts21.createSourceFile("__join.ts", `const __x = (${source});`, ts21.ScriptTarget.Latest, false);
21043
21421
  const stmt = sf.statements[0];
@@ -21137,6 +21515,7 @@ export {
21137
21515
  traceUpdatePath,
21138
21516
  testAdapter,
21139
21517
  stringifyParsedExpr,
21518
+ searchParamsLocalNames,
21140
21519
  rewriteImportsForTemplate,
21141
21520
  resolveSetters,
21142
21521
  resetCompilerCounters,
@@ -21144,11 +21523,14 @@ export {
21144
21523
  profileToJSON,
21145
21524
  parseStyleObjectEntries,
21146
21525
  parseRecordIndexAccess,
21526
+ parseProviderObjectLiteral,
21147
21527
  parseProfilerId,
21148
21528
  parseExpression,
21149
21529
  parseBlockBody,
21150
21530
  needsTypeBasedDetection,
21531
+ matchSearchParamsMethodCall,
21151
21532
  makeIdCallRegex,
21533
+ lookupStaticRecordLiteral,
21152
21534
  listComponentFunctions as listExportedComponents,
21153
21535
  listComponentFunctions,
21154
21536
  jsxToIR,
@@ -21156,6 +21538,7 @@ export {
21156
21538
  isSupported,
21157
21539
  isLowerableObjectRestDestructure,
21158
21540
  isBooleanAttr,
21541
+ importsSearchParams,
21159
21542
  identifierPath,
21160
21543
  graphToJSON,
21161
21544
  getCompilerCounters,
@@ -21204,6 +21587,7 @@ export {
21204
21587
  containsHigherOrder,
21205
21588
  compileJSX,
21206
21589
  combineParentChildClientJs,
21590
+ collectModuleStringConsts,
21207
21591
  collectContextConsumers,
21208
21592
  buildWhyUpdate,
21209
21593
  buildStaticBudget,
@@ -21231,6 +21615,7 @@ export {
21231
21615
  TestAdapter,
21232
21616
  SourceMapGenerator,
21233
21617
  REACTIVE_PRIMITIVES,
21618
+ PROFILE_SCHEMA_VERSION,
21234
21619
  JsxAdapter,
21235
21620
  ErrorCodes,
21236
21621
  BaseAdapter,