@barefootjs/test 0.23.0 → 0.25.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 (2) hide show
  1. package/dist/index.js +682 -104
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -189765,7 +189765,9 @@ var ErrorCodes = {
189765
189765
  INLINE_JSX_CALLBACK_CAPTURE: "BF080",
189766
189766
  UNRECOGNIZED_REACTIVE_FACTORY: "BF110",
189767
189767
  REACTIVE_FACTORY_RENAME_UNSUPPORTED: "BF111",
189768
- REACTIVE_FACTORY_MODULE_CAPTURE: "BF112"
189768
+ REACTIVE_FACTORY_MODULE_CAPTURE: "BF112",
189769
+ REACTIVE_FACTORY_IMPORT_COLLISION: "BF113",
189770
+ REACTIVE_FACTORY_PARAM_SHADOWED: "BF114"
189769
189771
  };
189770
189772
  var errorMessages = {
189771
189773
  [ErrorCodes.MISSING_USE_CLIENT]: "'use client' directive required for components with createSignal or event handlers",
@@ -189791,7 +189793,9 @@ var errorMessages = {
189791
189793
  [ErrorCodes.INLINE_JSX_CALLBACK_CAPTURE]: "Inline JSX-returning arrow function captures a non-module identifier. Extract the callback into a top-level 'use client' component (e.g. `function MyNode(n) { return <div/> }` then `renderNode={MyNode}`) or pass captured values via component props.",
189792
189794
  [ErrorCodes.UNRECOGNIZED_REACTIVE_FACTORY]: "Tuple destructuring of a non-reactive factory call. The compiler only recognizes createSignal / createMemo calls and same-file helpers that wrap them with a single `return [a, b]` exit.",
189793
189795
  [ErrorCodes.REACTIVE_FACTORY_RENAME_UNSUPPORTED]: "Reactive factory object return/destructure must use shorthand properties only. " + "Property renames (`{ lists: myLists }`), defaults, and rest elements are not " + "supported — destructure with the factory's own property names.",
189794
- [ErrorCodes.REACTIVE_FACTORY_MODULE_CAPTURE]: "Imported reactive factory references bindings from its own module scope, so its " + "body cannot be inlined into the component file. Move those helpers into the " + "component file, pass them to the factory as parameters, or define the factory " + "in the component file."
189796
+ [ErrorCodes.REACTIVE_FACTORY_MODULE_CAPTURE]: "Imported reactive factory references bindings from its own module scope, so its " + "body cannot be inlined into the component file. Move those helpers into the " + "component file, pass them to the factory as parameters, or define the factory " + "in the component file.",
189797
+ [ErrorCodes.REACTIVE_FACTORY_IMPORT_COLLISION]: "Inlining an imported reactive factory requires re-importing one of its helper " + "imports into this file, but that name is already bound here to something else. " + "Rename the conflicting binding in this file, or alias the import in the factory's own file.",
189798
+ [ErrorCodes.REACTIVE_FACTORY_PARAM_SHADOWED]: "Reactive factory parameter is shadowed by a nested declaration inside the factory body, so argument substitution at the inline site would be ambiguous. Rename the inner binding so it does not collide with the parameter."
189795
189799
  };
189796
189800
  function createError(code2, loc, options) {
189797
189801
  if (code2 === undefined || !(code2 in errorMessages)) {
@@ -192223,6 +192227,75 @@ function prescanReactiveFactoriesInSource(source, filePath) {
192223
192227
  prescanImportedReactiveFactories(sourceFile, filePath, result);
192224
192228
  return result;
192225
192229
  }
192230
+ function toComponentRelativeSpecifier(resolvedAbs, componentFilePath) {
192231
+ let rel = path_default.relative(path_default.dirname(componentFilePath), resolvedAbs).split(path_default.sep).join("/");
192232
+ rel = rel.replace(/\.(tsx|ts|jsx|js)$/, "");
192233
+ if (rel === "")
192234
+ rel = ".";
192235
+ if (!rel.startsWith("."))
192236
+ rel = "./" + rel;
192237
+ return rel;
192238
+ }
192239
+ function buildEntryImportIndex(sf, filePath) {
192240
+ const index = new Map;
192241
+ for (const stmt of sf.statements) {
192242
+ if (!import_typescript8.default.isImportDeclaration(stmt))
192243
+ continue;
192244
+ if (!import_typescript8.default.isStringLiteral(stmt.moduleSpecifier))
192245
+ continue;
192246
+ if (stmt.importClause?.isTypeOnly)
192247
+ continue;
192248
+ const src = stmt.moduleSpecifier.text;
192249
+ const targetKey = src.startsWith("./") || src.startsWith("../") ? resolveRelativeImportToFile(src, filePath) ?? "unresolved:" + src : src;
192250
+ const namedBindings = stmt.importClause?.namedBindings;
192251
+ if (namedBindings && import_typescript8.default.isNamedImports(namedBindings)) {
192252
+ for (const el of namedBindings.elements) {
192253
+ if (el.isTypeOnly)
192254
+ continue;
192255
+ index.set(el.name.text, { targetKey, exportedName: (el.propertyName ?? el.name).text });
192256
+ }
192257
+ }
192258
+ }
192259
+ return index;
192260
+ }
192261
+ function collectEntryBindingNames(sf) {
192262
+ const names = new Set;
192263
+ function visit2(node) {
192264
+ if (import_typescript8.default.isImportDeclaration(node) && node.importClause) {
192265
+ if (node.importClause.name)
192266
+ names.add(node.importClause.name.text);
192267
+ const namedBindings = node.importClause.namedBindings;
192268
+ if (namedBindings && import_typescript8.default.isNamedImports(namedBindings)) {
192269
+ for (const el of namedBindings.elements)
192270
+ names.add(el.name.text);
192271
+ }
192272
+ if (namedBindings && import_typescript8.default.isNamespaceImport(namedBindings)) {
192273
+ names.add(namedBindings.name.text);
192274
+ }
192275
+ }
192276
+ if (import_typescript8.default.isVariableDeclaration(node)) {
192277
+ const out = [];
192278
+ addBindingNames(node.name, out);
192279
+ for (const n of out)
192280
+ names.add(n);
192281
+ }
192282
+ if ((import_typescript8.default.isFunctionDeclaration(node) || import_typescript8.default.isClassDeclaration(node) || import_typescript8.default.isEnumDeclaration(node)) && node.name) {
192283
+ names.add(node.name.text);
192284
+ }
192285
+ if (import_typescript8.default.isFunctionLike(node)) {
192286
+ for (const p of node.parameters) {
192287
+ const out = [];
192288
+ addBindingNames(p.name, out);
192289
+ for (const n of out)
192290
+ names.add(n);
192291
+ }
192292
+ }
192293
+ import_typescript8.default.forEachChild(node, visit2);
192294
+ }
192295
+ visit2(sf);
192296
+ return names;
192297
+ }
192298
+ var MAX_REEXPORT_HOPS = 1;
192226
192299
  function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
192227
192300
  const candidateCallees = new Set;
192228
192301
  function collectCandidates(node) {
@@ -192263,29 +192336,31 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
192263
192336
  }
192264
192337
  if (importsToCheck.length === 0)
192265
192338
  return;
192266
- for (const { src, specs } of importsToCheck) {
192267
- const resolved = resolveRelativeImportToFile(src, filePath);
192268
- if (!resolved)
192269
- continue;
192339
+ const entryBindingNames = collectEntryBindingNames(entrySourceFile);
192340
+ const entryImportIndex = buildEntryImportIndex(entrySourceFile, filePath);
192341
+ const plannedInjections = new Map;
192342
+ const helperCache = new Map;
192343
+ function loadHelperFile(abs) {
192344
+ const cached = helperCache.get(abs);
192345
+ if (cached !== undefined)
192346
+ return cached;
192270
192347
  let content;
192271
192348
  try {
192272
- content = fs.readFileSync(resolved, "utf8");
192349
+ content = fs.readFileSync(abs, "utf8");
192273
192350
  } catch {
192274
- continue;
192351
+ helperCache.set(abs, null);
192352
+ return null;
192275
192353
  }
192276
- const alreadyKnown = (name) => result.factories.has(name) || result.declined.has(name) || result.reactiveShaped.has(name);
192277
192354
  const hasAnyPrimitiveText = [...REACTIVE_PRIMITIVES].some((p) => content.includes(p));
192278
- if (!hasAnyPrimitiveText) {
192279
- for (const spec of specs) {
192280
- if (!alreadyKnown(spec.local))
192281
- result.cleanFactoryImports.add(spec.local);
192282
- }
192283
- continue;
192355
+ const hasReexportText = content.includes("export") && content.includes("from");
192356
+ if (!hasAnyPrimitiveText && !hasReexportText) {
192357
+ helperCache.set(abs, "clean");
192358
+ return "clean";
192284
192359
  }
192285
- const helperSf = import_typescript8.default.createSourceFile(resolved + ".prescan", content, import_typescript8.default.ScriptTarget.Latest, true, import_typescript8.default.ScriptKind.TSX);
192360
+ const sf = import_typescript8.default.createSourceFile(abs + ".prescan", content, import_typescript8.default.ScriptTarget.Latest, true, import_typescript8.default.ScriptKind.TSX);
192286
192361
  const localFns = new Map;
192287
192362
  const exportedFns = new Map;
192288
- for (const stmt of helperSf.statements) {
192363
+ for (const stmt of sf.statements) {
192289
192364
  if (import_typescript8.default.isFunctionDeclaration(stmt) && stmt.name && stmt.body) {
192290
192365
  localFns.set(stmt.name.text, stmt);
192291
192366
  const hasExportModifier = stmt.modifiers?.some((m) => m.kind === import_typescript8.default.SyntaxKind.ExportKeyword) ?? false;
@@ -192295,27 +192370,89 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
192295
192370
  }
192296
192371
  }
192297
192372
  }
192298
- for (const stmt of helperSf.statements) {
192299
- if (import_typescript8.default.isExportDeclaration(stmt) && stmt.exportClause && import_typescript8.default.isNamedExports(stmt.exportClause) && !stmt.moduleSpecifier && !stmt.isTypeOnly) {
192373
+ const reexports = new Map;
192374
+ let hasStarReexport = false;
192375
+ for (const stmt of sf.statements) {
192376
+ if (!import_typescript8.default.isExportDeclaration(stmt) || stmt.isTypeOnly)
192377
+ continue;
192378
+ if (!stmt.moduleSpecifier) {
192379
+ if (stmt.exportClause && import_typescript8.default.isNamedExports(stmt.exportClause)) {
192380
+ for (const el of stmt.exportClause.elements) {
192381
+ if (el.isTypeOnly)
192382
+ continue;
192383
+ const fn = localFns.get((el.propertyName ?? el.name).text);
192384
+ if (fn)
192385
+ exportedFns.set(el.name.text, fn);
192386
+ }
192387
+ }
192388
+ continue;
192389
+ }
192390
+ if (!import_typescript8.default.isStringLiteral(stmt.moduleSpecifier))
192391
+ continue;
192392
+ if (stmt.exportClause && import_typescript8.default.isNamedExports(stmt.exportClause)) {
192300
192393
  for (const el of stmt.exportClause.elements) {
192301
192394
  if (el.isTypeOnly)
192302
192395
  continue;
192303
- const fn = localFns.get((el.propertyName ?? el.name).text);
192304
- if (fn)
192305
- exportedFns.set(el.name.text, fn);
192396
+ reexports.set(el.name.text, {
192397
+ source: stmt.moduleSpecifier.text,
192398
+ innerName: (el.propertyName ?? el.name).text
192399
+ });
192306
192400
  }
192307
- }
192401
+ } else if (!stmt.exportClause) {
192402
+ hasStarReexport = true;
192403
+ }
192404
+ }
192405
+ const moduleBindings = collectHelperModuleValueBindings(sf);
192406
+ const info = { sf, exportedFns, reexports, hasStarReexport, moduleBindings };
192407
+ helperCache.set(abs, info);
192408
+ return info;
192409
+ }
192410
+ function lookupExportedFactory(abs, exportedName, visited, hopsLeft) {
192411
+ if (visited.has(abs))
192412
+ return { kind: "unknown" };
192413
+ visited.add(abs);
192414
+ const file = loadHelperFile(abs);
192415
+ if (file === null)
192416
+ return { kind: "unknown" };
192417
+ if (file === "clean")
192418
+ return { kind: "clean" };
192419
+ const fn = file.exportedFns.get(exportedName);
192420
+ if (fn)
192421
+ return { kind: "fn", fn, file, definingPath: abs };
192422
+ const re = file.reexports.get(exportedName);
192423
+ if (re) {
192424
+ if (hopsLeft <= 0)
192425
+ return { kind: "unknown" };
192426
+ if (!re.source.startsWith("./") && !re.source.startsWith("../"))
192427
+ return { kind: "unknown" };
192428
+ const target = resolveRelativeImportToFile(re.source, abs);
192429
+ if (!target)
192430
+ return { kind: "unknown" };
192431
+ return lookupExportedFactory(target, re.innerName, visited, hopsLeft - 1);
192308
192432
  }
192309
- const moduleBindings = collectHelperModuleValueBindings(helperSf);
192433
+ if (file.hasStarReexport)
192434
+ return { kind: "unknown" };
192435
+ return { kind: "clean" };
192436
+ }
192437
+ for (const { src, specs } of importsToCheck) {
192438
+ const resolved = resolveRelativeImportToFile(src, filePath);
192439
+ if (!resolved)
192440
+ continue;
192441
+ const alreadyKnown = (name) => result.factories.has(name) || result.declined.has(name) || result.reactiveShaped.has(name);
192310
192442
  for (const spec of specs) {
192311
192443
  if (alreadyKnown(spec.local))
192312
192444
  continue;
192313
- const fn = exportedFns.get(spec.exported);
192314
- if (!fn) {
192445
+ const found = lookupExportedFactory(resolved, spec.exported, new Set, MAX_REEXPORT_HOPS);
192446
+ if (found.kind === "clean") {
192315
192447
  result.cleanFactoryImports.add(spec.local);
192316
192448
  continue;
192317
192449
  }
192318
- const det = detectReactiveFactory(fn, helperSf, resolved);
192450
+ if (found.kind === "unknown")
192451
+ continue;
192452
+ const { fn, file, definingPath } = found;
192453
+ const helperSf = file.sf;
192454
+ const moduleBindings = file.moduleBindings;
192455
+ const det = detectReactiveFactory(fn, helperSf, definingPath);
192319
192456
  if (!det) {
192320
192457
  result.cleanFactoryImports.add(spec.local);
192321
192458
  continue;
@@ -192328,17 +192465,64 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
192328
192465
  result.declined.set(spec.local, det.declined);
192329
192466
  break;
192330
192467
  case "factory": {
192331
- const offending = moduleCaptureCheck(fn, det.info, moduleBindings, fn.name.text);
192332
- if (offending.length > 0) {
192468
+ const capture = moduleCaptureCheck(fn, det.info, moduleBindings, fn.name.text);
192469
+ if (capture.captured.length > 0) {
192333
192470
  result.declined.set(spec.local, {
192334
192471
  code: "BF112",
192335
- detail: `'${offending.join("', '")}'`,
192472
+ detail: `'${capture.captured.join("', '")}'`,
192336
192473
  loc: det.info.loc
192337
192474
  });
192338
- } else {
192339
- det.info.sourceFilePath = resolved;
192340
- result.factories.set(spec.local, det.info);
192475
+ break;
192476
+ }
192477
+ const required = [];
192478
+ const pending = [];
192479
+ let declinedEntry = null;
192480
+ for (const ref of capture.importedRefs) {
192481
+ let specifier;
192482
+ let targetKey;
192483
+ if (ref.source.startsWith("./") || ref.source.startsWith("../")) {
192484
+ const abs = resolveRelativeImportToFile(ref.source, definingPath);
192485
+ if (!abs) {
192486
+ declinedEntry = {
192487
+ code: "BF112",
192488
+ detail: `'${ref.localName}' (import '${ref.source}' did not resolve from the helper file)`,
192489
+ loc: det.info.loc
192490
+ };
192491
+ break;
192492
+ }
192493
+ specifier = toComponentRelativeSpecifier(abs, filePath);
192494
+ targetKey = abs;
192495
+ } else {
192496
+ specifier = ref.source;
192497
+ targetKey = ref.source;
192498
+ }
192499
+ const existing = entryImportIndex.get(ref.localName);
192500
+ if (existing && existing.targetKey === targetKey && existing.exportedName === ref.exportedName) {
192501
+ continue;
192502
+ }
192503
+ const planned = plannedInjections.get(ref.localName);
192504
+ const collides = existing !== undefined || planned !== undefined && (planned.targetKey !== targetKey || planned.exportedName !== ref.exportedName) || planned === undefined && entryBindingNames.has(ref.localName);
192505
+ if (collides) {
192506
+ declinedEntry = {
192507
+ code: "BF113",
192508
+ detail: `'${ref.localName}' from '${specifier}'`,
192509
+ loc: det.info.loc
192510
+ };
192511
+ break;
192512
+ }
192513
+ pending.push([ref.localName, { targetKey, exportedName: ref.exportedName }]);
192514
+ required.push({ localName: ref.localName, exportedName: ref.exportedName, specifier });
192515
+ }
192516
+ if (declinedEntry) {
192517
+ result.declined.set(spec.local, declinedEntry);
192518
+ break;
192341
192519
  }
192520
+ for (const [name, id] of pending)
192521
+ plannedInjections.set(name, id);
192522
+ det.info.sourceFilePath = definingPath;
192523
+ if (required.length > 0)
192524
+ det.info.requiredImports = required;
192525
+ result.factories.set(spec.local, det.info);
192342
192526
  break;
192343
192527
  }
192344
192528
  }
@@ -192346,7 +192530,8 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
192346
192530
  }
192347
192531
  }
192348
192532
  function collectHelperModuleValueBindings(sf) {
192349
- const names = new Set;
192533
+ const local = new Set;
192534
+ const imported = new Map;
192350
192535
  for (const stmt of sf.statements) {
192351
192536
  if (import_typescript8.default.isVariableStatement(stmt)) {
192352
192537
  const out = [];
@@ -192354,11 +192539,11 @@ function collectHelperModuleValueBindings(sf) {
192354
192539
  addBindingNames(decl.name, out);
192355
192540
  }
192356
192541
  for (const n of out)
192357
- names.add(n);
192542
+ local.add(n);
192358
192543
  continue;
192359
192544
  }
192360
192545
  if ((import_typescript8.default.isFunctionDeclaration(stmt) || import_typescript8.default.isClassDeclaration(stmt) || import_typescript8.default.isEnumDeclaration(stmt)) && stmt.name) {
192361
- names.add(stmt.name.text);
192546
+ local.add(stmt.name.text);
192362
192547
  continue;
192363
192548
  }
192364
192549
  if (import_typescript8.default.isImportDeclaration(stmt)) {
@@ -192370,25 +192555,25 @@ function collectHelperModuleValueBindings(sf) {
192370
192555
  if (src === "@barefootjs/client" || src === "@barefootjs/client/runtime")
192371
192556
  continue;
192372
192557
  if (stmt.importClause?.name)
192373
- names.add(stmt.importClause.name.text);
192558
+ local.add(stmt.importClause.name.text);
192374
192559
  const namedBindings = stmt.importClause?.namedBindings;
192375
192560
  if (namedBindings && import_typescript8.default.isNamedImports(namedBindings)) {
192376
192561
  for (const el of namedBindings.elements) {
192377
192562
  if (el.isTypeOnly)
192378
192563
  continue;
192379
- names.add(el.name.text);
192564
+ imported.set(el.name.text, { source: src, exportedName: (el.propertyName ?? el.name).text });
192380
192565
  }
192381
192566
  }
192382
192567
  if (namedBindings && import_typescript8.default.isNamespaceImport(namedBindings)) {
192383
- names.add(namedBindings.name.text);
192568
+ local.add(namedBindings.name.text);
192384
192569
  }
192385
192570
  }
192386
192571
  }
192387
- return names;
192572
+ return { local, imported };
192388
192573
  }
192389
192574
  function moduleCaptureCheck(fn, info, moduleBindings, selfName) {
192390
192575
  if (!fn.body)
192391
- return [];
192576
+ return { captured: [], importedRefs: [] };
192392
192577
  const free = extractFreeIdentifiersFromNode(fn.body);
192393
192578
  const exclude = new Set(info.params);
192394
192579
  for (const b of info.localBindings)
@@ -192398,14 +192583,22 @@ function moduleCaptureCheck(fn, info, moduleBindings, selfName) {
192398
192583
  for (const p of REACTIVE_PRIMITIVES)
192399
192584
  exclude.add(p);
192400
192585
  exclude.add(selfName);
192401
- const offending = [];
192586
+ const captured = [];
192587
+ const importedRefs = [];
192402
192588
  for (const id of free) {
192403
192589
  if (exclude.has(id))
192404
192590
  continue;
192405
- if (moduleBindings.has(id))
192406
- offending.push(id);
192591
+ if (moduleBindings.local.has(id)) {
192592
+ captured.push(id);
192593
+ continue;
192594
+ }
192595
+ const imp = moduleBindings.imported.get(id);
192596
+ if (imp)
192597
+ importedRefs.push({ localName: id, source: imp.source, exportedName: imp.exportedName });
192407
192598
  }
192408
- return offending.sort();
192599
+ captured.sort();
192600
+ importedRefs.sort((a, b) => a.localName < b.localName ? -1 : 1);
192601
+ return { captured, importedRefs };
192409
192602
  }
192410
192603
  function detectReactiveFactory(node, sourceFile, filePath) {
192411
192604
  if (!node.body || !node.name)
@@ -192424,6 +192617,17 @@ function detectReactiveFactory(node, sourceFile, filePath) {
192424
192617
  if (!hasReactiveCall)
192425
192618
  return null;
192426
192619
  const loc = getSourceLocation(node, sourceFile, filePath);
192620
+ let totalReturnCount = 0;
192621
+ function countReturns(n) {
192622
+ if (import_typescript8.default.isFunctionLike(n))
192623
+ return;
192624
+ if (import_typescript8.default.isReturnStatement(n)) {
192625
+ totalReturnCount++;
192626
+ return;
192627
+ }
192628
+ import_typescript8.default.forEachChild(n, countReturns);
192629
+ }
192630
+ import_typescript8.default.forEachChild(node.body, countReturns);
192427
192631
  let returnExpr = null;
192428
192632
  let returnCount = 0;
192429
192633
  for (const stmt of node.body.statements) {
@@ -192441,7 +192645,7 @@ function detectReactiveFactory(node, sourceFile, filePath) {
192441
192645
  expr = expr.expression;
192442
192646
  returnExpr = expr;
192443
192647
  }
192444
- if (returnCount !== 1 || !returnExpr)
192648
+ if (totalReturnCount !== 1 || returnCount !== 1 || !returnExpr)
192445
192649
  return { kind: "reactive-shaped" };
192446
192650
  const returnTupleIdentifiers = [];
192447
192651
  let returnKind;
@@ -192475,6 +192679,14 @@ function detectReactiveFactory(node, sourceFile, filePath) {
192475
192679
  } else {
192476
192680
  return { kind: "reactive-shaped" };
192477
192681
  }
192682
+ const params = [];
192683
+ for (const p of node.parameters) {
192684
+ if (import_typescript8.default.isIdentifier(p.name)) {
192685
+ params.push(p.name.text);
192686
+ continue;
192687
+ }
192688
+ return { kind: "reactive-shaped" };
192689
+ }
192478
192690
  const localBindings = [];
192479
192691
  for (const stmt of node.body.statements) {
192480
192692
  if (import_typescript8.default.isVariableStatement(stmt)) {
@@ -192485,15 +192697,95 @@ function detectReactiveFactory(node, sourceFile, filePath) {
192485
192697
  localBindings.push(stmt.name.text);
192486
192698
  }
192487
192699
  }
192488
- const bodyStatements = node.body.statements.filter((s) => !import_typescript8.default.isReturnStatement(s)).map((s) => s.getText(sourceFile)).join(`
192700
+ const relevantNames = new Set([...params, ...localBindings, ...returnTupleIdentifiers]);
192701
+ const renameSites = [];
192702
+ let shadowedParam = null;
192703
+ function collectRenameSites(root, toBodyOffset) {
192704
+ function push(id, form) {
192705
+ renameSites.push({
192706
+ name: id.text,
192707
+ start: toBodyOffset(id.getStart(sourceFile)),
192708
+ end: toBodyOffset(id.getEnd()),
192709
+ form
192710
+ });
192711
+ }
192712
+ function classify(id) {
192713
+ if (!relevantNames.has(id.text))
192714
+ return;
192715
+ const p = id.parent;
192716
+ if (import_typescript8.default.isPropertyAccessExpression(p) && p.name === id)
192717
+ return;
192718
+ if (import_typescript8.default.isPropertyAssignment(p) && p.name === id)
192719
+ return;
192720
+ if (import_typescript8.default.isBindingElement(p) && p.propertyName === id)
192721
+ return;
192722
+ if ((import_typescript8.default.isMethodDeclaration(p) || import_typescript8.default.isGetAccessorDeclaration(p) || import_typescript8.default.isSetAccessorDeclaration(p) || import_typescript8.default.isPropertyDeclaration(p) || import_typescript8.default.isEnumMember(p)) && p.name === id)
192723
+ return;
192724
+ if (import_typescript8.default.isJsxAttribute(p) && p.name === id)
192725
+ return;
192726
+ if (import_typescript8.default.isLabeledStatement(p) && p.label === id || (import_typescript8.default.isBreakStatement(p) || import_typescript8.default.isContinueStatement(p)) && p.label === id)
192727
+ return;
192728
+ if ((import_typescript8.default.isJsxOpeningElement(p) || import_typescript8.default.isJsxSelfClosingElement(p) || import_typescript8.default.isJsxClosingElement(p)) && p.tagName === id && /^[a-z]/.test(id.text))
192729
+ return;
192730
+ if (import_typescript8.default.isShorthandPropertyAssignment(p) && p.name === id) {
192731
+ push(id, "shorthand");
192732
+ return;
192733
+ }
192734
+ if (import_typescript8.default.isBindingElement(p) && p.name === id && !p.propertyName && import_typescript8.default.isObjectBindingPattern(p.parent)) {
192735
+ if (params.includes(id.text))
192736
+ shadowedParam = id.text;
192737
+ push(id, "shorthand");
192738
+ return;
192739
+ }
192740
+ const isDecl = (import_typescript8.default.isVariableDeclaration(p) || import_typescript8.default.isParameter(p) || import_typescript8.default.isBindingElement(p) || import_typescript8.default.isFunctionDeclaration(p) || import_typescript8.default.isFunctionExpression(p) || import_typescript8.default.isClassDeclaration(p) || import_typescript8.default.isClassExpression(p)) && p.name === id;
192741
+ if (isDecl && params.includes(id.text))
192742
+ shadowedParam = id.text;
192743
+ push(id, "plain");
192744
+ }
192745
+ function visit2(n) {
192746
+ if (import_typescript8.default.isTypeNode(n) || import_typescript8.default.isTypeParameterDeclaration(n) || import_typescript8.default.isTypeAliasDeclaration(n) || import_typescript8.default.isInterfaceDeclaration(n))
192747
+ return;
192748
+ if (import_typescript8.default.isIdentifier(n)) {
192749
+ classify(n);
192750
+ return;
192751
+ }
192752
+ import_typescript8.default.forEachChild(n, visit2);
192753
+ }
192754
+ visit2(root);
192755
+ }
192756
+ const keptStatements = node.body.statements.filter((s) => !import_typescript8.default.isReturnStatement(s));
192757
+ const pieces = [];
192758
+ let base = 0;
192759
+ for (const stmt of keptStatements) {
192760
+ const text = stmt.getText(sourceFile);
192761
+ const stmtStart = stmt.getStart(sourceFile);
192762
+ collectRenameSites(stmt, (pos) => pos - stmtStart + base);
192763
+ pieces.push(text);
192764
+ base += text.length + 1;
192765
+ }
192766
+ const bodyStatements = pieces.join(`
192489
192767
  `);
192490
- const params = [];
192491
- for (const p of node.parameters) {
192492
- if (import_typescript8.default.isIdentifier(p.name)) {
192493
- params.push(p.name.text);
192494
- continue;
192768
+ if (shadowedParam !== null) {
192769
+ return {
192770
+ kind: "declined",
192771
+ declined: {
192772
+ code: "BF114",
192773
+ detail: `parameter '${shadowedParam}' of '${node.name.text}' is shadowed by a nested declaration inside the factory body`,
192774
+ loc
192775
+ }
192776
+ };
192777
+ }
192778
+ for (const site of renameSites) {
192779
+ if (bodyStatements.slice(site.start, site.end) !== site.name) {
192780
+ return {
192781
+ kind: "declined",
192782
+ declined: {
192783
+ code: "BF111",
192784
+ detail: `internal rename-site offset mismatch for '${site.name}' — this is a compiler bug, ` + `please report it`,
192785
+ loc
192786
+ }
192787
+ };
192495
192788
  }
192496
- return { kind: "reactive-shaped" };
192497
192789
  }
192498
192790
  return {
192499
192791
  kind: "factory",
@@ -192503,7 +192795,8 @@ function detectReactiveFactory(node, sourceFile, filePath) {
192503
192795
  returnTupleIdentifiers,
192504
192796
  returnKind,
192505
192797
  localBindings,
192506
- loc
192798
+ loc,
192799
+ renameSites
192507
192800
  }
192508
192801
  };
192509
192802
  }
@@ -192529,6 +192822,7 @@ function rewriteFactoryCallsInSource(source, prescan) {
192529
192822
  const { factories, sourceFile } = prescan;
192530
192823
  const edits = [];
192531
192824
  let callSiteIndex = 0;
192825
+ const inlinedFactories = new Set;
192532
192826
  function visitStmt(node, inComponent) {
192533
192827
  if (import_typescript8.default.isVariableStatement(node) && inComponent) {
192534
192828
  for (const decl of node.declarationList.declarations) {
@@ -192606,34 +192900,65 @@ function rewriteFactoryCallsInSource(source, prescan) {
192606
192900
  const argTexts = args.map((a) => a.getText(sourceFile));
192607
192901
  const thisCallIndex = callSiteIndex++;
192608
192902
  const suffix = `_bf${thisCallIndex}`;
192609
- let body = factory.bodySource;
192610
- const internalRenames = new Set(factory.localBindings);
192611
- for (const ex of excludeFromSuffixRename)
192612
- internalRenames.delete(ex);
192613
- for (const name of internalRenames) {
192614
- body = body.replace(new RegExp(`\\b${escapeRegex(name)}\\b`, "g"), name + suffix);
192903
+ const renames = new Map;
192904
+ for (const name of factory.localBindings) {
192905
+ if (!excludeFromSuffixRename.has(name))
192906
+ renames.set(name, name + suffix);
192907
+ }
192908
+ if (renameReturnToCallerNames) {
192909
+ for (const [n, caller] of renameReturnToCallerNames) {
192910
+ if (caller !== n)
192911
+ renames.set(n, caller);
192912
+ }
192615
192913
  }
192616
192914
  const atomicArg = /^(?:[\w$.]+|'[^'\\]*'|"[^"\\]*"|-?\d+(?:\.\d+)?)$/;
192617
192915
  for (let i2 = 0;i2 < factory.params.length; i2++) {
192618
192916
  const p = factory.params[i2];
192619
- const a = argTexts[i2] ?? "undefined";
192620
- const wrapped = atomicArg.test(a.trim()) ? a.trim() : `(${a})`;
192621
- body = body.replace(new RegExp(`\\b${escapeRegex(p)}\\b`, "g"), wrapped);
192917
+ const a = (argTexts[i2] ?? "undefined").trim();
192918
+ renames.set(p, atomicArg.test(a) ? a : `(${a})`);
192622
192919
  }
192623
- if (renameReturnToCallerNames) {
192624
- for (const [n, caller] of renameReturnToCallerNames) {
192625
- body = body.replace(new RegExp(`\\b${escapeRegex(n)}\\b`, "g"), caller);
192626
- }
192920
+ let body = factory.bodySource;
192921
+ for (let i2 = factory.renameSites.length - 1;i2 >= 0; i2--) {
192922
+ const site = factory.renameSites[i2];
192923
+ const repl = renames.get(site.name);
192924
+ if (repl === undefined)
192925
+ continue;
192926
+ const text = site.form === "shorthand" ? `${site.name}: ${repl}` : repl;
192927
+ body = body.slice(0, site.start) + text + body.slice(site.end);
192627
192928
  }
192628
192929
  edits.push({
192629
192930
  start: stmt.getStart(sourceFile),
192630
192931
  end: stmt.getEnd(),
192631
192932
  replacement: body
192632
192933
  });
192934
+ inlinedFactories.add(factory);
192633
192935
  }
192634
192936
  visitStmt(sourceFile, false);
192635
192937
  if (edits.length === 0)
192636
192938
  return source;
192939
+ const importsBySpecifier = new Map;
192940
+ for (const f of inlinedFactories) {
192941
+ for (const r of f.requiredImports ?? []) {
192942
+ let names = importsBySpecifier.get(r.specifier);
192943
+ if (!names) {
192944
+ names = new Map;
192945
+ importsBySpecifier.set(r.specifier, names);
192946
+ }
192947
+ names.set(r.localName, r.exportedName);
192948
+ }
192949
+ }
192950
+ if (importsBySpecifier.size > 0) {
192951
+ const lines = [...importsBySpecifier].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([spec, names]) => {
192952
+ const specifiers = [...names].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([local, exported]) => exported === local ? local : `${exported} as ${local}`);
192953
+ return `import { ${specifiers.join(", ")} } from '${spec}'`;
192954
+ });
192955
+ const at = factoryImportInsertionOffset(sourceFile);
192956
+ edits.push({ start: at, end: at, replacement: at === 0 ? lines.join(`
192957
+ `) + `
192958
+ ` : `
192959
+ ` + lines.join(`
192960
+ `) });
192961
+ }
192637
192962
  edits.sort((a, b) => b.start - a.start);
192638
192963
  let out = source;
192639
192964
  for (const e of edits) {
@@ -192641,6 +192966,20 @@ function rewriteFactoryCallsInSource(source, prescan) {
192641
192966
  }
192642
192967
  return out;
192643
192968
  }
192969
+ function factoryImportInsertionOffset(sf) {
192970
+ let lastImportEnd = -1;
192971
+ let directiveEnd = -1;
192972
+ for (const stmt of sf.statements) {
192973
+ if (import_typescript8.default.isImportDeclaration(stmt)) {
192974
+ lastImportEnd = stmt.getEnd();
192975
+ continue;
192976
+ }
192977
+ if (directiveEnd === -1 && import_typescript8.default.isExpressionStatement(stmt) && import_typescript8.default.isStringLiteral(stmt.expression) && stmt.expression.text === "use client") {
192978
+ directiveEnd = stmt.getEnd();
192979
+ }
192980
+ }
192981
+ return lastImportEnd >= 0 ? lastImportEnd : directiveEnd >= 0 ? directiveEnd : 0;
192982
+ }
192644
192983
  function isPascalCaseComponentFn(node) {
192645
192984
  if (import_typescript8.default.isFunctionDeclaration(node) && node.name) {
192646
192985
  return /^[A-Z]/.test(node.name.text);
@@ -192650,15 +192989,27 @@ function isPascalCaseComponentFn(node) {
192650
192989
  }
192651
192990
  return false;
192652
192991
  }
192653
- function escapeRegex(s) {
192654
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
192655
- }
192656
192992
  function declinedFactoryMessage(callee, d) {
192657
192993
  if (d.code === "BF112") {
192658
192994
  return `Reactive factory '${callee}' references ${d.detail} from its own module ` + `scope and cannot be inlined. Move the referenced helper(s) into this file, ` + `pass them as factory arguments, or inline the factory here.`;
192659
192995
  }
192996
+ if (d.code === "BF113") {
192997
+ return `Reactive factory '${callee}' cannot be inlined: it needs ${d.detail} ` + `imported into this file, but that name is already bound here to something ` + `else. Rename the conflicting binding in this file, or alias the import in ` + `the factory's own file (import { x as y }).`;
192998
+ }
192660
192999
  return `Reactive factory '${callee}' cannot be inlined: ${d.detail}.`;
192661
193000
  }
193001
+ function declinedFactoryErrorCode(code2) {
193002
+ switch (code2) {
193003
+ case "BF112":
193004
+ return ErrorCodes.REACTIVE_FACTORY_MODULE_CAPTURE;
193005
+ case "BF113":
193006
+ return ErrorCodes.REACTIVE_FACTORY_IMPORT_COLLISION;
193007
+ case "BF114":
193008
+ return ErrorCodes.REACTIVE_FACTORY_PARAM_SHADOWED;
193009
+ default:
193010
+ return ErrorCodes.REACTIVE_FACTORY_RENAME_UNSUPPORTED;
193011
+ }
193012
+ }
192662
193013
  function validateReactiveFactoryCalls(ctx) {
192663
193014
  if (!ctx.componentNode)
192664
193015
  return;
@@ -192682,7 +193033,7 @@ function validateReactiveFactoryCalls(ctx) {
192682
193033
  continue;
192683
193034
  const declinedEntry = ctx.declinedReactiveFactories.get(callee);
192684
193035
  if (declinedEntry) {
192685
- ctx.errors.push(createError(declinedEntry.code === "BF112" ? ErrorCodes.REACTIVE_FACTORY_MODULE_CAPTURE : ErrorCodes.REACTIVE_FACTORY_RENAME_UNSUPPORTED, loc, { severity: "error", message: declinedFactoryMessage(callee, declinedEntry) }));
193036
+ ctx.errors.push(createError(declinedFactoryErrorCode(declinedEntry.code), loc, { severity: "error", message: declinedFactoryMessage(callee, declinedEntry) }));
192686
193037
  continue;
192687
193038
  }
192688
193039
  const objectFactory = ctx.reactiveFactories.get(callee);
@@ -192739,7 +193090,7 @@ function validateObjectFactoryDestructure(ctx, pattern, callee, loc) {
192739
193090
  }
192740
193091
  const declinedEntry = ctx.declinedReactiveFactories.get(callee);
192741
193092
  if (declinedEntry) {
192742
- ctx.errors.push(createError(declinedEntry.code === "BF112" ? ErrorCodes.REACTIVE_FACTORY_MODULE_CAPTURE : ErrorCodes.REACTIVE_FACTORY_RENAME_UNSUPPORTED, loc, { severity: "error", message: declinedFactoryMessage(callee, declinedEntry) }));
193093
+ ctx.errors.push(createError(declinedFactoryErrorCode(declinedEntry.code), loc, { severity: "error", message: declinedFactoryMessage(callee, declinedEntry) }));
192743
193094
  return;
192744
193095
  }
192745
193096
  if (ctx.reactiveShapedHelpers.has(callee)) {
@@ -193180,19 +193531,83 @@ function resolveFreeRefs(node, env) {
193180
193531
  // ../jsx/src/to-locale-date-lowering.ts
193181
193532
  var TO_LOCALE_TZ_RE = /^(?:UTC|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/;
193182
193533
  var PROBE_UTC = new Date(Date.UTC(2001, 1, 3));
193183
- var patternCache = new Map;
193184
- function resolveLocaleDatePattern(locale) {
193185
- const cached = patternCache.get(locale);
193534
+ var formatCache = new Map;
193535
+ var namesCache = new Map;
193536
+ function deriveMonthNames(locale, ctx) {
193537
+ return deriveNamesCached(`${locale}|m|${ctx}`, () => {
193538
+ const months2 = (width) => Array.from({ length: 12 }, (_, m) => probePart(locale, ctx === "formatting" ? { month: width, day: "numeric" } : { month: width }, Date.UTC(2001, m, 15), "month"));
193539
+ return [...months2("long"), ...months2("short")];
193540
+ });
193541
+ }
193542
+ function deriveWeekdayNames(locale, ctx) {
193543
+ return deriveNamesCached(`${locale}|w|${ctx}`, () => {
193544
+ const weekdays = (width) => Array.from({ length: 7 }, (_, d) => probePart(locale, ctx === "formatting" ? { weekday: width, month: "numeric", day: "numeric" } : { weekday: width }, Date.UTC(2023, 0, 1 + d), "weekday"));
193545
+ return [...weekdays("long"), ...weekdays("short")];
193546
+ });
193547
+ }
193548
+ function probePart(locale, options, utc, type2) {
193549
+ const parts = new Intl.DateTimeFormat(locale, { ...options, timeZone: "UTC" }).formatToParts(new Date(utc));
193550
+ const found = parts.find((p) => p.type === type2);
193551
+ if (!found || !found.value)
193552
+ throw new Error("missing part");
193553
+ return found.value;
193554
+ }
193555
+ function deriveNamesCached(key, derive) {
193556
+ const cached = namesCache.get(key);
193186
193557
  if (cached !== undefined)
193187
193558
  return cached;
193188
- const derived = derivePattern(locale);
193189
- patternCache.set(locale, derived);
193559
+ let derived;
193560
+ try {
193561
+ derived = derive();
193562
+ } catch {
193563
+ derived = null;
193564
+ }
193565
+ namesCache.set(key, derived);
193190
193566
  return derived;
193191
193567
  }
193192
- function derivePattern(locale) {
193568
+ function resolveLocaleDateFormat(locale, probeOptions) {
193569
+ const key = `${locale}|${JSON.stringify(probeOptions, Object.keys(probeOptions).sort())}`;
193570
+ const cached = formatCache.get(key);
193571
+ if (cached !== undefined)
193572
+ return cached;
193573
+ const derived = deriveFormat(locale, probeOptions);
193574
+ formatCache.set(key, derived);
193575
+ return derived;
193576
+ }
193577
+ var VERIFY_UTC = new Date(Date.UTC(2001, 4, 13));
193578
+ function renderPatternAt(pattern, names, y, m, d, wd) {
193579
+ const pad2 = (n) => String(n).padStart(2, "0");
193580
+ return pattern.replace(/YYYY|MMMM|MMM|MM|DD|dddd|ddd|M|D/g, (token) => {
193581
+ switch (token) {
193582
+ case "YYYY":
193583
+ return String(y).padStart(4, "0");
193584
+ case "MMMM":
193585
+ return names[m - 1] ?? "";
193586
+ case "MMM":
193587
+ return names[12 + m - 1] ?? "";
193588
+ case "MM":
193589
+ return pad2(m);
193590
+ case "M":
193591
+ return String(m);
193592
+ case "DD":
193593
+ return pad2(d);
193594
+ case "D":
193595
+ return String(d);
193596
+ case "dddd":
193597
+ return names[24 + wd] ?? "";
193598
+ default:
193599
+ return names[31 + wd] ?? "";
193600
+ }
193601
+ });
193602
+ }
193603
+ function deriveFormat(locale, probeOptions) {
193604
+ let dtf;
193193
193605
  let parts;
193194
193606
  try {
193195
- const dtf = new Intl.DateTimeFormat(locale, { timeZone: "UTC" });
193607
+ dtf = new Intl.DateTimeFormat(locale, {
193608
+ ...probeOptions,
193609
+ timeZone: "UTC"
193610
+ });
193196
193611
  const resolved = dtf.resolvedOptions();
193197
193612
  if (resolved.calendar !== "gregory" || resolved.numberingSystem !== "latn")
193198
193613
  return null;
@@ -193200,7 +193615,18 @@ function derivePattern(locale) {
193200
193615
  } catch {
193201
193616
  return null;
193202
193617
  }
193618
+ const monthTables = [
193619
+ deriveMonthNames(locale, "formatting"),
193620
+ deriveMonthNames(locale, "standalone")
193621
+ ];
193622
+ const weekdayTables = [
193623
+ deriveWeekdayNames(locale, "formatting"),
193624
+ deriveWeekdayNames(locale, "standalone")
193625
+ ];
193626
+ let monthTable = null;
193627
+ let weekdayTable = null;
193203
193628
  let pattern = "";
193629
+ let usesNames = false;
193204
193630
  for (const part of parts) {
193205
193631
  switch (part.type) {
193206
193632
  case "year":
@@ -193208,14 +193634,27 @@ function derivePattern(locale) {
193208
193634
  return null;
193209
193635
  pattern += "YYYY";
193210
193636
  break;
193211
- case "month":
193212
- if (part.value === "2")
193637
+ case "month": {
193638
+ if (part.value === "2") {
193213
193639
  pattern += "M";
193214
- else if (part.value === "02")
193640
+ break;
193641
+ }
193642
+ if (part.value === "02") {
193215
193643
  pattern += "MM";
193644
+ break;
193645
+ }
193646
+ const wide = monthTables.find((t) => t && part.value === t[1]) ?? null;
193647
+ const abbr = wide ? null : monthTables.find((t) => t && part.value === t[12 + 1]) ?? null;
193648
+ if (wide)
193649
+ pattern += "MMMM";
193650
+ else if (abbr)
193651
+ pattern += "MMM";
193216
193652
  else
193217
193653
  return null;
193654
+ monthTable = wide ?? abbr;
193655
+ usesNames = true;
193218
193656
  break;
193657
+ }
193219
193658
  case "day":
193220
193659
  if (part.value === "3")
193221
193660
  pattern += "D";
@@ -193224,8 +193663,21 @@ function derivePattern(locale) {
193224
193663
  else
193225
193664
  return null;
193226
193665
  break;
193666
+ case "weekday": {
193667
+ const wide = weekdayTables.find((t) => t && part.value === t[6]) ?? null;
193668
+ const abbr = wide ? null : weekdayTables.find((t) => t && part.value === t[7 + 6]) ?? null;
193669
+ if (wide)
193670
+ pattern += "dddd";
193671
+ else if (abbr)
193672
+ pattern += "ddd";
193673
+ else
193674
+ return null;
193675
+ weekdayTable = wide ?? abbr;
193676
+ usesNames = true;
193677
+ break;
193678
+ }
193227
193679
  case "literal":
193228
- if (/[YMD]/.test(part.value))
193680
+ if (/[YMD]/.test(part.value) || /ddd/.test(part.value))
193229
193681
  return null;
193230
193682
  pattern += part.value;
193231
193683
  break;
@@ -193233,9 +193685,73 @@ function derivePattern(locale) {
193233
193685
  return null;
193234
193686
  }
193235
193687
  }
193236
- if (!pattern.includes("YYYY") || !/M/.test(pattern) || !/D/.test(pattern))
193688
+ if (!/YYYY|MMMM|MMM|MM|M/.test(pattern) && !/DD|D/.test(pattern))
193237
193689
  return null;
193238
- return pattern;
193690
+ if (!usesNames)
193691
+ return { pattern, names: null };
193692
+ const names = [
193693
+ ...monthTable ?? monthTables[0] ?? monthTables[1] ?? Array(24).fill(""),
193694
+ ...weekdayTable ?? weekdayTables[0] ?? weekdayTables[1] ?? Array(14).fill("")
193695
+ ];
193696
+ if (renderPatternAt(pattern, names, 2001, 5, 13, 0) !== dtf.format(VERIFY_UTC))
193697
+ return null;
193698
+ return { pattern, names };
193699
+ }
193700
+ function unionMemberLiteral(member) {
193701
+ const m = /^'([^'\\]*)'$|^"([^"\\]*)"$/.exec(member.raw.trim());
193702
+ return m ? m[1] ?? m[2] : null;
193703
+ }
193704
+ function resolveLocaleUnionMembers(locale, metadata) {
193705
+ let sourcePropName = null;
193706
+ if (metadata.propsObjectName) {
193707
+ if (locale.kind === "member" && !locale.computed && locale.object.kind === "identifier" && locale.object.name === metadata.propsObjectName) {
193708
+ sourcePropName = locale.property;
193709
+ }
193710
+ } else if (locale.kind === "identifier") {
193711
+ const name = locale.name;
193712
+ const param = metadata.propsParams?.find((pp) => pp.name === name);
193713
+ if (param)
193714
+ sourcePropName = param.sourceName ?? param.name;
193715
+ }
193716
+ if (!sourcePropName)
193717
+ return null;
193718
+ const target = sourcePropName;
193719
+ const prop = metadata.propsType?.properties?.find((p) => p.name === target);
193720
+ if (!prop || prop.optional)
193721
+ return null;
193722
+ const type2 = prop.type;
193723
+ if (type2.kind !== "union" || !type2.unionTypes || type2.unionTypes.length === 0)
193724
+ return null;
193725
+ const members = [];
193726
+ for (const member of type2.unionTypes) {
193727
+ const value = unionMemberLiteral(member);
193728
+ if (value === null)
193729
+ return null;
193730
+ members.push(value);
193731
+ }
193732
+ return members;
193733
+ }
193734
+ var strLit = (value) => ({ kind: "literal", value, literalType: "string" });
193735
+ function strArr(values) {
193736
+ return {
193737
+ kind: "array-literal",
193738
+ elements: values.map((v) => strLit(v)),
193739
+ raw: JSON.stringify(values)
193740
+ };
193741
+ }
193742
+ function foldMembers(locale, members, leaves, allEqual) {
193743
+ let expr = leaves[leaves.length - 1];
193744
+ if (allEqual)
193745
+ return expr;
193746
+ for (let i2 = leaves.length - 2;i2 >= 0; i2--) {
193747
+ expr = {
193748
+ kind: "conditional",
193749
+ test: { kind: "binary", op: "===", left: locale, right: strLit(members[i2]) },
193750
+ consequent: leaves[i2],
193751
+ alternate: expr
193752
+ };
193753
+ }
193754
+ return expr;
193239
193755
  }
193240
193756
  function matchToLocaleDateStringCall(callee, args, metadata) {
193241
193757
  if (callee.kind !== "member" || callee.computed)
@@ -193243,17 +193759,23 @@ function matchToLocaleDateStringCall(callee, args, metadata) {
193243
193759
  if (callee.property !== "toLocaleDateString" || args.length !== 2)
193244
193760
  return null;
193245
193761
  const [locale, options] = args;
193246
- if (locale.kind !== "literal" || locale.literalType !== "string")
193247
- return null;
193248
- if (options.kind !== "object-literal" || options.properties.length !== 1)
193762
+ if (options.kind !== "object-literal")
193249
193763
  return null;
193250
- const prop = options.properties[0];
193251
- if (prop.key !== "timeZone")
193252
- return null;
193253
- if (prop.value.kind !== "literal" || prop.value.literalType !== "string")
193254
- return null;
193255
- const tz = String(prop.value.value);
193256
- if (!TO_LOCALE_TZ_RE.test(tz))
193764
+ let tz = null;
193765
+ const probeOptions = {};
193766
+ for (const prop of options.properties) {
193767
+ if (prop.value.kind !== "literal" || prop.value.literalType !== "string")
193768
+ return null;
193769
+ const value = String(prop.value.value);
193770
+ if (prop.key === "timeZone") {
193771
+ if (!TO_LOCALE_TZ_RE.test(value))
193772
+ return null;
193773
+ tz = value;
193774
+ } else {
193775
+ probeOptions[prop.key] = value;
193776
+ }
193777
+ }
193778
+ if (tz === null)
193257
193779
  return null;
193258
193780
  const receiverType = resolveReceiverType(callee.object, metadata, new Map);
193259
193781
  if (!receiverType || receiverType.kind !== "interface")
@@ -193263,19 +193785,64 @@ function matchToLocaleDateStringCall(callee, args, metadata) {
193263
193785
  return null;
193264
193786
  if (metadata.typeDefinitions.some((d) => d.name === typeName))
193265
193787
  return null;
193266
- const pattern = resolveLocaleDatePattern(String(locale.value));
193267
- if (pattern === null)
193788
+ if (locale.kind === "literal" && locale.literalType === "string") {
193789
+ const format3 = resolveLocaleDateFormat(String(locale.value), probeOptions);
193790
+ if (format3 === null)
193791
+ return null;
193792
+ return {
193793
+ kind: "helper-call",
193794
+ helper: "format_date",
193795
+ args: [callee.object, strLit(format3.pattern), strLit(tz), strArr(format3.names ?? [])]
193796
+ };
193797
+ }
193798
+ const members = resolveLocaleUnionMembers(locale, metadata);
193799
+ if (!members)
193268
193800
  return null;
193801
+ const formats = [];
193802
+ for (const member of members) {
193803
+ const format3 = resolveLocaleDateFormat(member, probeOptions);
193804
+ if (format3 === null)
193805
+ return null;
193806
+ formats.push(format3);
193807
+ }
193808
+ const patterns = formats.map((f) => f.pattern);
193809
+ const nameTables = formats.map((f) => JSON.stringify(f.names ?? []));
193269
193810
  return {
193270
193811
  kind: "helper-call",
193271
193812
  helper: "format_date",
193272
193813
  args: [
193273
193814
  callee.object,
193274
- { kind: "literal", value: pattern, literalType: "string" },
193275
- { kind: "literal", value: tz, literalType: "string" }
193815
+ foldMembers(locale, members, patterns.map(strLit), new Set(patterns).size === 1),
193816
+ strLit(tz),
193817
+ foldMembers(locale, members, formats.map((f) => strArr(f.names ?? [])), new Set(nameTables).size === 1)
193276
193818
  ]
193277
193819
  };
193278
193820
  }
193821
+ function foldedArgToClientJs(arg, localeText) {
193822
+ if (arg.kind === "literal")
193823
+ return JSON.stringify(arg.value);
193824
+ if (arg.kind === "array-literal") {
193825
+ const values = [];
193826
+ for (const el of arg.elements) {
193827
+ if (el.kind !== "literal")
193828
+ return null;
193829
+ values.push(String(el.value));
193830
+ }
193831
+ return JSON.stringify(values);
193832
+ }
193833
+ if (arg.kind !== "conditional")
193834
+ return null;
193835
+ const t = arg.test;
193836
+ if (t.kind !== "binary" || t.op !== "===" || t.right.kind !== "literal")
193837
+ return null;
193838
+ if (arg.consequent.kind !== "literal" && arg.consequent.kind !== "array-literal")
193839
+ return null;
193840
+ const cons = foldedArgToClientJs(arg.consequent, localeText);
193841
+ const rest = foldedArgToClientJs(arg.alternate, localeText);
193842
+ if (cons === null || rest === null)
193843
+ return null;
193844
+ return `${localeText} === ${JSON.stringify(t.right.value)} ? ${cons} : ${rest}`;
193845
+ }
193279
193846
  var toLocaleDatePlugin = {
193280
193847
  name: "toLocaleDateString",
193281
193848
  prepare(metadata) {
@@ -193441,12 +194008,22 @@ function lowerToLocaleDateCalls(text, expr, ctx) {
193441
194008
  const node = matcher(tsNodeToParsedExpr(propAccess), call.arguments.map((a) => tsNodeToParsedExpr(a)));
193442
194009
  if (!node || node.kind !== "helper-call" || node.helper !== "format_date")
193443
194010
  continue;
193444
- const [, patternArg, tzArg] = node.args;
193445
- if (patternArg?.kind !== "literal" || tzArg?.kind !== "literal")
194011
+ const [, patternArg, tzArg, namesArg] = node.args;
194012
+ if (!patternArg || tzArg?.kind !== "literal")
193446
194013
  continue;
194014
+ const localeText = ctx.getJS(call.arguments[0]);
194015
+ const patternJs = foldedArgToClientJs(patternArg, localeText);
194016
+ if (patternJs === null)
194017
+ continue;
194018
+ let namesJs = null;
194019
+ if (namesArg && !(namesArg.kind === "array-literal" && namesArg.elements.length === 0)) {
194020
+ namesJs = foldedArgToClientJs(namesArg, localeText);
194021
+ if (namesJs === null)
194022
+ continue;
194023
+ }
193447
194024
  const receiverText = ctx.getJS(propAccess.expression);
193448
194025
  const matchText = ctx.getJS(call);
193449
- result = replaceProtectedCall(result, matchText, () => `formatDate(${receiverText}, ${JSON.stringify(patternArg.value)}, ${JSON.stringify(tzArg.value)})`);
194026
+ result = replaceProtectedCall(result, matchText, () => `formatDate(${receiverText}, ${patternJs}, ${JSON.stringify(tzArg.value)}${namesJs !== null ? `, ${namesJs}` : ""})`);
193450
194027
  }
193451
194028
  return restore(result);
193452
194029
  }
@@ -197507,15 +198084,16 @@ function isOmitBranch(node) {
197507
198084
  }
197508
198085
  // ../jsx/src/format-date-lowering.ts
197509
198086
  var UTC_LITERAL = { kind: "literal", value: "UTC", literalType: "string" };
198087
+ var EMPTY_NAMES = { kind: "array-literal", elements: [], raw: "[]" };
197510
198088
  function matchFormatDateCall(callee, args, locals) {
197511
198089
  if (callee.kind !== "identifier" || !locals.has(callee.name))
197512
198090
  return null;
197513
- if (args.length < 2 || args.length > 3)
198091
+ if (args.length < 2 || args.length > 4)
197514
198092
  return null;
197515
198093
  return {
197516
198094
  kind: "helper-call",
197517
198095
  helper: "format_date",
197518
- args: [args[0], args[1], args[2] ?? UTC_LITERAL]
198096
+ args: [args[0], args[1], args[2] ?? UTC_LITERAL, args[3] ?? EMPTY_NAMES]
197519
198097
  };
197520
198098
  }
197521
198099
  var formatDatePlugin = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/test",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "description": "Test utilities for BarefootJS - IR-based component testing without a browser",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -39,7 +39,7 @@
39
39
  "directory": "packages/test"
40
40
  },
41
41
  "dependencies": {
42
- "@barefootjs/jsx": "0.23.0"
42
+ "@barefootjs/jsx": "0.25.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "typescript": "^5.0.0"