@bamboocss/parser 1.48.5 → 1.50.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.
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- let ts_morph = require("ts-morph");
2
+ let _bamboocss_ts_ast = require("@bamboocss/ts-ast");
3
3
  let _bamboocss_extractor = require("@bamboocss/extractor");
4
4
  let _bamboocss_shared = require("@bamboocss/shared");
5
5
  let node_crypto = require("node:crypto");
@@ -411,20 +411,6 @@ function createReportMaps() {
411
411
  };
412
412
  }
413
413
  //#endregion
414
- //#region src/get-module-specifier-value.ts
415
- /**
416
- * Both declaration kinds carry a specifier, and `export { x } from './m'` is how a barrel
417
- * forwards a recipe — so this reads either. An `export { x }` with no `from` returns
418
- * undefined, which is the same answer the throwing case gives.
419
- */
420
- const getModuleSpecifierValue = (node) => {
421
- try {
422
- return node.getModuleSpecifierValue();
423
- } catch {
424
- return;
425
- }
426
- };
427
- //#endregion
428
414
  //#region src/imported-recipes.ts
429
415
  /**
430
416
  * A module's exported recipes, memoized.
@@ -440,15 +426,15 @@ const clearImportedRecipeCache = () => {
440
426
  /** The names a file bound `cva`/`sva` to, or empty when it imports neither. */
441
427
  const recipeFactoryAliases = (sourceFile, imports) => {
442
428
  const aliases = /* @__PURE__ */ new Set();
443
- for (const declaration of sourceFile.getImportDeclarations()) {
444
- if (declaration.isTypeOnly()) continue;
445
- const mod = getModuleSpecifierValue(declaration);
429
+ for (const declaration of (0, _bamboocss_ts_ast.getImportDeclarations)(sourceFile)) {
430
+ if ((0, _bamboocss_ts_ast.isTypeOnly)(declaration)) continue;
431
+ const mod = (0, _bamboocss_ts_ast.getModuleSpecifierValue)(declaration);
446
432
  if (!mod) continue;
447
- for (const specifier of declaration.getNamedImports()) {
448
- if (specifier.isTypeOnly()) continue;
449
- const name = specifier.getNameNode().getText();
433
+ for (const specifier of (0, _bamboocss_ts_ast.getNamedImports)(declaration)) {
434
+ if ((0, _bamboocss_ts_ast.isTypeOnly)(specifier)) continue;
435
+ const name = (0, _bamboocss_ts_ast.nameNodeOf)(specifier)?.getText();
450
436
  if (name !== "cva" && name !== "sva") continue;
451
- const alias = specifier.getAliasNode()?.getText() || name;
437
+ const alias = (0, _bamboocss_ts_ast.getAliasNode)(specifier)?.getText() || name;
452
438
  if (!imports.match({
453
439
  name,
454
440
  alias,
@@ -469,21 +455,21 @@ const declaredRecipes = (sourceFile, imports) => {
469
455
  declared,
470
456
  exported
471
457
  };
472
- const filePath = sourceFile.getFilePath();
473
- for (const statement of sourceFile.compilerNode.statements) {
474
- if (!ts_morph.ts.isVariableStatement(statement)) continue;
475
- if (!(statement.declarationList.flags & ts_morph.ts.NodeFlags.Const)) continue;
458
+ const filePath = (0, _bamboocss_ts_ast.pathOf)(sourceFile);
459
+ for (const statement of sourceFile.statements) {
460
+ if (!_bamboocss_ts_ast.ts.isVariableStatement(statement)) continue;
461
+ if (!(statement.declarationList.flags & _bamboocss_ts_ast.ts.NodeFlags.Const)) continue;
476
462
  for (const declaration of statement.declarationList.declarations) {
477
463
  const initializer = declaration.initializer;
478
- if (!initializer || !ts_morph.ts.isCallExpression(initializer)) continue;
464
+ if (!initializer || !_bamboocss_ts_ast.ts.isCallExpression(initializer)) continue;
479
465
  const callee = initializer.expression;
480
- if (!ts_morph.ts.isIdentifier(callee) || !ts_morph.ts.isIdentifier(declaration.name)) continue;
466
+ if (!_bamboocss_ts_ast.ts.isIdentifier(callee) || !_bamboocss_ts_ast.ts.isIdentifier(declaration.name)) continue;
481
467
  if (!factories.has(callee.text)) continue;
482
468
  declared.set(declaration.name.text, {
483
469
  filePath,
484
470
  name: declaration.name.text
485
471
  });
486
- if (statement.modifiers?.some((modifier) => modifier.kind === ts_morph.ts.SyntaxKind.ExportKeyword)) exported.add(declaration.name.text);
472
+ if (statement.modifiers?.some((modifier) => modifier.kind === _bamboocss_ts_ast.ts.SyntaxKind.ExportKeyword)) exported.add(declaration.name.text);
487
473
  }
488
474
  }
489
475
  return {
@@ -530,13 +516,13 @@ const walkExports = (sourceFile, imports, resolveModule, seen) => {
530
516
  const imported = walkImports(sourceFile, imports, resolveModule, seen);
531
517
  complete &&= imported.complete;
532
518
  for (const [alias, origin] of imported.bindings) if (!local.has(alias)) local.set(alias, origin);
533
- for (const declaration of sourceFile.getExportDeclarations()) {
534
- if (declaration.isTypeOnly()) continue;
535
- const specifier = getModuleSpecifierValue(declaration);
519
+ for (const declaration of (0, _bamboocss_ts_ast.getExportDeclarations)(sourceFile)) {
520
+ if ((0, _bamboocss_ts_ast.isTypeOnly)(declaration)) continue;
521
+ const specifier = (0, _bamboocss_ts_ast.getModuleSpecifierValue)(declaration);
536
522
  const target = specifier ? resolveModule(specifier, sourceFile) : void 0;
537
523
  if (specifier && !target) continue;
538
- if (declaration.getNamespaceExport()) continue;
539
- if (declaration.isNamespaceExport()) {
524
+ if ((0, _bamboocss_ts_ast.getNamespaceExport)(declaration)) continue;
525
+ if ((0, _bamboocss_ts_ast.isStarExport)(declaration)) {
540
526
  if (!target) continue;
541
527
  const walk = walkExports(target, imports, resolveModule, seen);
542
528
  complete &&= walk.complete;
@@ -556,11 +542,11 @@ const walkExports = (sourceFile, imports, resolveModule, seen) => {
556
542
  complete &&= walk.complete;
557
543
  source = walk.names;
558
544
  }
559
- for (const exportSpecifier of declaration.getNamedExports()) {
560
- if (exportSpecifier.isTypeOnly()) continue;
561
- const name = exportSpecifier.getNameNode().getText();
562
- const origin = source.get(name);
563
- if (origin) names.set(exportSpecifier.getAliasNode()?.getText() || name, origin);
545
+ for (const exportSpecifier of (0, _bamboocss_ts_ast.getNamedExports)(declaration)) {
546
+ if ((0, _bamboocss_ts_ast.isTypeOnly)(exportSpecifier)) continue;
547
+ const name = (0, _bamboocss_ts_ast.nameNodeOf)(exportSpecifier)?.getText();
548
+ const origin = source.get(name ?? "");
549
+ if (origin) names.set((0, _bamboocss_ts_ast.getAliasNode)(exportSpecifier)?.getText() || (name ?? ""), origin);
564
550
  }
565
551
  }
566
552
  for (const [name, origin] of starred) if (!names.has(name) && !ambiguous.has(name)) names.set(name, origin);
@@ -580,11 +566,11 @@ const walkExports = (sourceFile, imports, resolveModule, seen) => {
580
566
  const walkImports = (sourceFile, imports, resolveModule, seen) => {
581
567
  const bindings = /* @__PURE__ */ new Map();
582
568
  let complete = true;
583
- for (const declaration of sourceFile.getImportDeclarations()) {
584
- if (declaration.isTypeOnly()) continue;
585
- const named = declaration.getNamedImports();
569
+ for (const declaration of (0, _bamboocss_ts_ast.getImportDeclarations)(sourceFile)) {
570
+ if ((0, _bamboocss_ts_ast.isTypeOnly)(declaration)) continue;
571
+ const named = (0, _bamboocss_ts_ast.getNamedImports)(declaration);
586
572
  if (named.length === 0) continue;
587
- const specifier = getModuleSpecifierValue(declaration);
573
+ const specifier = (0, _bamboocss_ts_ast.getModuleSpecifierValue)(declaration);
588
574
  if (!specifier) continue;
589
575
  const target = resolveModule(specifier, sourceFile);
590
576
  if (!target || target === sourceFile) continue;
@@ -593,10 +579,10 @@ const walkImports = (sourceFile, imports, resolveModule, seen) => {
593
579
  const { names } = walk;
594
580
  if (names.size === 0) continue;
595
581
  for (const importSpecifier of named) {
596
- if (importSpecifier.isTypeOnly()) continue;
597
- const origin = names.get(importSpecifier.getNameNode().getText());
582
+ if ((0, _bamboocss_ts_ast.isTypeOnly)(importSpecifier)) continue;
583
+ const origin = names.get((0, _bamboocss_ts_ast.nameNodeOf)(importSpecifier)?.getText() ?? "");
598
584
  if (!origin) continue;
599
- bindings.set(importSpecifier.getAliasNode()?.getText() || importSpecifier.getNameNode().getText(), origin);
585
+ bindings.set((0, _bamboocss_ts_ast.getAliasNode)(importSpecifier)?.getText() || ((0, _bamboocss_ts_ast.nameNodeOf)(importSpecifier)?.getText() ?? ""), origin);
600
586
  }
601
587
  }
602
588
  return {
@@ -645,7 +631,7 @@ const digestExportValue = (sourceFile, exportedName, resolveModule, onCrossing)
645
631
  try {
646
632
  const declaration = (0, _bamboocss_extractor.getExportedVarDeclarationWithName)(exportedName, sourceFile, [], boxCtx);
647
633
  if (!declaration) return "bamboo:export-missing";
648
- const initializer = declaration.getInitializer?.();
634
+ const initializer = (0, _bamboocss_ts_ast.childOf)(declaration, "initializer");
649
635
  if (!initializer) return void 0;
650
636
  const box = (0, _bamboocss_extractor.maybeBoxNode)(initializer, [], boxCtx);
651
637
  if (!box || Array.isArray(box)) return void 0;
@@ -660,17 +646,18 @@ const digestExportValue = (sourceFile, exportedName, resolveModule, onCrossing)
660
646
  };
661
647
  //#endregion
662
648
  //#region src/get-import-declarations.ts
663
- function getImportDeclarations(context, sourceFile) {
649
+ function getImportDeclarations$1(context, sourceFile) {
664
650
  const { imports, tsOptions } = context;
665
651
  const importDeclarations = [];
666
- sourceFile.getImportDeclarations().forEach((node) => {
667
- const mod = getModuleSpecifierValue(node);
652
+ (0, _bamboocss_ts_ast.getImportDeclarations)(sourceFile).forEach((node) => {
653
+ const mod = (0, _bamboocss_ts_ast.getModuleSpecifierValue)(node);
668
654
  if (!mod) return;
669
- node.getNamedImports().forEach((specifier) => {
670
- const name = specifier.getNameNode().getText();
655
+ (0, _bamboocss_ts_ast.getNamedImports)(node).forEach((specifier) => {
656
+ const name = (0, _bamboocss_ts_ast.nameNodeOf)(specifier)?.getText();
657
+ const alias = (0, _bamboocss_ts_ast.getAliasNode)(specifier)?.getText() || name;
671
658
  const result = {
672
- name,
673
- alias: specifier.getAliasNode()?.getText() || name,
659
+ name: name ?? "",
660
+ alias: alias ?? "",
674
661
  mod,
675
662
  kind: "named"
676
663
  };
@@ -680,7 +667,7 @@ function getImportDeclarations(context, sourceFile) {
680
667
  })) return;
681
668
  importDeclarations.push(result);
682
669
  });
683
- const namespace = node.getNamespaceImport();
670
+ const namespace = (0, _bamboocss_ts_ast.getNamespaceImport)(node);
684
671
  if (namespace) {
685
672
  const name = namespace.getText();
686
673
  const result = {
@@ -717,7 +704,7 @@ const findUnresolvable = (node, path, out, seen = /* @__PURE__ */ new Set()) =>
717
704
  }
718
705
  if (_bamboocss_extractor.box.isConditional(node)) return;
719
706
  if (_bamboocss_extractor.box.isLiteral(node) && node.value === void 0) {
720
- if (ts_morph.Node.isTemplateExpression(node.getNode())) out.push(path.join("."));
707
+ if (_bamboocss_ts_ast.Node.isTemplateExpression(node.getNode())) out.push(path.join("."));
721
708
  return;
722
709
  }
723
710
  if (_bamboocss_extractor.box.isMap(node)) {
@@ -740,17 +727,17 @@ const findUnresolvable = (node, path, out, seen = /* @__PURE__ */ new Set()) =>
740
727
  */
741
728
  const writtenProps = (node) => {
742
729
  let literal = node;
743
- if (literal && ts_morph.Node.isCallExpression(literal)) {
744
- const args = literal.getArguments();
730
+ if (literal && _bamboocss_ts_ast.Node.isCallExpression(literal)) {
731
+ const args = literal.arguments;
745
732
  if (args.length !== 1) return void 0;
746
733
  literal = args[0];
747
734
  }
748
- if (!literal || !ts_morph.Node.isObjectLiteralExpression(literal)) return void 0;
735
+ if (!literal || !_bamboocss_ts_ast.Node.isObjectLiteralExpression(literal)) return void 0;
749
736
  const names = [];
750
737
  let uncertain = false;
751
- for (const property of literal.getProperties()) {
752
- if (ts_morph.Node.isPropertyAssignment(property) || ts_morph.Node.isShorthandPropertyAssignment(property)) {
753
- const name = property.getName();
738
+ for (const property of literal.properties) {
739
+ if (_bamboocss_ts_ast.Node.isPropertyAssignment(property) || _bamboocss_ts_ast.Node.isShorthandPropertyAssignment(property)) {
740
+ const name = (0, _bamboocss_ts_ast.getName)(property) ?? "";
754
741
  if (name.startsWith("[")) {
755
742
  uncertain = true;
756
743
  continue;
@@ -786,11 +773,11 @@ const writtenProps = (node) => {
786
773
  const findUnresolvedInValue = (node, resolved, path, out) => {
787
774
  const value = node ? (0, _bamboocss_extractor.unwrapExpression)(node) : void 0;
788
775
  if (!value) return;
789
- if (ts_morph.Node.isArrayLiteralExpression(value)) {
790
- value.getElements().forEach((element, index) => {
776
+ if (_bamboocss_ts_ast.Node.isArrayLiteralExpression(value)) {
777
+ value.elements.forEach((element, index) => {
791
778
  const at = path ? `${path}.${index}` : String(index);
792
779
  const element_ = resolved?.[index];
793
- if (ts_morph.Node.isSpreadElement(element) && element_ == null) {
780
+ if (_bamboocss_ts_ast.Node.isSpreadElement(element) && element_ == null) {
794
781
  out.push({
795
782
  prop: at || void 0,
796
783
  reason: "unenumerable-keys"
@@ -801,17 +788,17 @@ const findUnresolvedInValue = (node, resolved, path, out) => {
801
788
  });
802
789
  return;
803
790
  }
804
- if (!ts_morph.Node.isObjectLiteralExpression(value)) return;
805
- const properties = value.getProperties();
791
+ if (!_bamboocss_ts_ast.Node.isObjectLiteralExpression(value)) return;
792
+ const properties = value.properties;
806
793
  const written = [];
807
794
  let uncertain = false;
808
795
  for (const property of properties) {
809
- if (ts_morph.Node.isSpreadAssignment(property)) {
810
- const spread = (0, _bamboocss_extractor.unwrapExpression)(property.getExpression());
811
- if (ts_morph.Node.isObjectLiteralExpression(spread)) {
812
- for (const inner of spread.getProperties()) {
813
- if (!ts_morph.Node.isPropertyAssignment(inner) && !ts_morph.Node.isShorthandPropertyAssignment(inner)) continue;
814
- const innerName = inner.getName();
796
+ if (_bamboocss_ts_ast.Node.isSpreadAssignment(property)) {
797
+ const spread = (0, _bamboocss_extractor.unwrapExpression)(property.expression);
798
+ if (_bamboocss_ts_ast.Node.isObjectLiteralExpression(spread)) {
799
+ for (const inner of spread.properties) {
800
+ if (!_bamboocss_ts_ast.Node.isPropertyAssignment(inner) && !_bamboocss_ts_ast.Node.isShorthandPropertyAssignment(inner)) continue;
801
+ const innerName = (0, _bamboocss_ts_ast.getName)(inner) ?? "";
815
802
  if (!innerName.startsWith("[")) written.push(innerName.replace(/^['"]|['"]$/g, ""));
816
803
  }
817
804
  continue;
@@ -819,8 +806,8 @@ const findUnresolvedInValue = (node, resolved, path, out) => {
819
806
  uncertain = true;
820
807
  continue;
821
808
  }
822
- if (!ts_morph.Node.isPropertyAssignment(property) && !ts_morph.Node.isShorthandPropertyAssignment(property)) continue;
823
- const name = property.getName();
809
+ if (!_bamboocss_ts_ast.Node.isPropertyAssignment(property) && !_bamboocss_ts_ast.Node.isShorthandPropertyAssignment(property)) continue;
810
+ const name = (0, _bamboocss_ts_ast.getName)(property) ?? "";
824
811
  if (name.startsWith("[")) {
825
812
  uncertain = true;
826
813
  continue;
@@ -837,10 +824,10 @@ const findUnresolvedInValue = (node, resolved, path, out) => {
837
824
  reason: "unenumerable-keys"
838
825
  });
839
826
  for (const property of properties) {
840
- if (!ts_morph.Node.isPropertyAssignment(property)) continue;
841
- const name = property.getName().replace(/^['"]|['"]$/g, "");
827
+ if (!_bamboocss_ts_ast.Node.isPropertyAssignment(property)) continue;
828
+ const name = ((0, _bamboocss_ts_ast.getName)(property) ?? "").replace(/^['"]|['"]$/g, "");
842
829
  if (name.startsWith("[")) continue;
843
- findUnresolvedInValue(property.getInitializer(), resolved?.[name], path ? `${path}.${name}` : name, out);
830
+ findUnresolvedInValue(property.initializer, resolved?.[name], path ? `${path}.${name}` : name, out);
844
831
  }
845
832
  };
846
833
  /** A recipe config the build could not fully read — see `findUnresolvedInValue`. */
@@ -849,20 +836,20 @@ const findUnresolvedRecipeStyles = (item) => {
849
836
  const sourceFile = node?.getSourceFile();
850
837
  if (!node || !sourceFile) return [];
851
838
  let argument = node;
852
- if (ts_morph.Node.isCallExpression(argument)) {
853
- const args = argument.getArguments();
839
+ if (_bamboocss_ts_ast.Node.isCallExpression(argument)) {
840
+ const args = argument.arguments;
854
841
  if (args.length !== 1) return [];
855
842
  argument = args[0];
856
843
  }
857
844
  const losses = [];
858
845
  const config = argument ? (0, _bamboocss_extractor.unwrapExpression)(argument) : void 0;
859
- if (!config || !ts_morph.Node.isObjectLiteralExpression(config)) losses.push({ reason: "unresolvable-value" });
846
+ if (!config || !_bamboocss_ts_ast.Node.isObjectLiteralExpression(config)) losses.push({ reason: "unresolvable-value" });
860
847
  else findUnresolvedInValue(config, item.data[0], "", losses);
861
848
  if (!losses.length) return [];
862
- const { line, column } = sourceFile.getLineAndColumnAtPos(node.getStart());
849
+ const { line, column } = (0, _bamboocss_ts_ast.getLineAndColumnAtPos)(sourceFile, node.getStart());
863
850
  return losses.map((loss) => ({
864
851
  column,
865
- filePath: sourceFile.getFilePath(),
852
+ filePath: (0, _bamboocss_ts_ast.pathOf)(sourceFile),
866
853
  kind: "recipe",
867
854
  line,
868
855
  ...loss
@@ -892,9 +879,9 @@ const findUnresolvedStyles = (item, kind) => {
892
879
  if (written.uncertain && !hasKeyOutside(resolved, written.names)) losses.push({ reason: "unenumerable-keys" });
893
880
  }
894
881
  if (!losses.length) return [];
895
- const { line, column } = sourceFile.getLineAndColumnAtPos(node.getStart());
882
+ const { line, column } = (0, _bamboocss_ts_ast.getLineAndColumnAtPos)(sourceFile, node.getStart());
896
883
  const at = {
897
- filePath: sourceFile.getFilePath(),
884
+ filePath: (0, _bamboocss_ts_ast.pathOf)(sourceFile),
898
885
  line,
899
886
  column
900
887
  };
@@ -1120,7 +1107,7 @@ var ParserResult = class {
1120
1107
  const visit = (node) => {
1121
1108
  if (!node || seen.has(node)) return;
1122
1109
  seen.add(node);
1123
- const path = node.getNode?.()?.getSourceFile().getFilePath().replaceAll("\\", "/");
1110
+ const path = (0, _bamboocss_ts_ast.pathOf)(node.getNode?.()?.getSourceFile())?.replaceAll("\\", "/");
1124
1111
  if (path && path !== own) paths.add(path);
1125
1112
  if (_bamboocss_extractor.box.isMap(node)) for (const child of node.value.values()) visit(child);
1126
1113
  else if (_bamboocss_extractor.box.isArray(node)) for (const child of node.value) visit(child);
@@ -1172,6 +1159,17 @@ const combineResult = (unboxed) => {
1172
1159
  ...unboxed.spreadConditions
1173
1160
  ];
1174
1161
  };
1162
+ /** The exact binding token accounting visits for a token call. */
1163
+ const tokenCalleeRange = (call) => {
1164
+ if (!_bamboocss_ts_ast.Node.isCallExpression(call)) return void 0;
1165
+ let current = call.expression;
1166
+ while (_bamboocss_ts_ast.Node.isPropertyAccessExpression(current)) current = current.expression;
1167
+ if (!_bamboocss_ts_ast.Node.isIdentifier(current)) return void 0;
1168
+ return {
1169
+ start: current.getStart(),
1170
+ end: current.getEnd()
1171
+ };
1172
+ };
1175
1173
  const defaultEnv = { preset: "ECMA" };
1176
1174
  /**
1177
1175
  * `fallback()` is generated into `styled-system/css`, so evaluating a call to it would mean
@@ -1185,23 +1183,23 @@ function createParser(context) {
1185
1183
  const { jsx, imports, recipes } = context;
1186
1184
  return function parse(sourceFile, encoder, options, resolveModule) {
1187
1185
  if (!sourceFile) return;
1188
- const importDeclarations = getImportDeclarations(context, sourceFile);
1186
+ const importDeclarations = getImportDeclarations$1(context, sourceFile);
1189
1187
  const file = imports.file(importDeclarations);
1190
- const filePath = sourceFile.getFilePath();
1188
+ const filePath = (0, _bamboocss_ts_ast.pathOf)(sourceFile);
1191
1189
  _bamboocss_logger.logger.debug("ast:import", !file.isEmpty() ? `Found import { ${file.toString()} } in ${filePath}` : `No import found in ${filePath}`);
1192
1190
  const parserResult = new ParserResult(context, encoder);
1193
1191
  const importedRecipes = resolveModule ? importedRecipeBindings(sourceFile, imports, resolveModule) : /* @__PURE__ */ new Map();
1194
1192
  if (file.isEmpty() && !jsx.isEnabled && importedRecipes.size === 0) return parserResult;
1195
1193
  parserResult.importedRecipes = importedRecipes;
1196
1194
  for (const binding of importedRecipes.keys()) file.addLocalRecipe(binding);
1197
- if (file.importsRecipeFactory()) for (const statement of sourceFile.compilerNode.statements) {
1198
- if (!ts_morph.ts.isVariableStatement(statement)) continue;
1199
- if (!(statement.declarationList.flags & ts_morph.ts.NodeFlags.Const)) continue;
1195
+ if (file.importsRecipeFactory()) for (const statement of sourceFile.statements) {
1196
+ if (!_bamboocss_ts_ast.ts.isVariableStatement(statement)) continue;
1197
+ if (!(statement.declarationList.flags & _bamboocss_ts_ast.ts.NodeFlags.Const)) continue;
1200
1198
  for (const declaration of statement.declarationList.declarations) {
1201
1199
  const initializer = declaration.initializer;
1202
- if (!initializer || !ts_morph.ts.isCallExpression(initializer)) continue;
1200
+ if (!initializer || !_bamboocss_ts_ast.ts.isCallExpression(initializer)) continue;
1203
1201
  const callee = initializer.expression;
1204
- if (!ts_morph.ts.isIdentifier(callee) || !ts_morph.ts.isIdentifier(declaration.name)) continue;
1202
+ if (!_bamboocss_ts_ast.ts.isIdentifier(callee) || !_bamboocss_ts_ast.ts.isIdentifier(declaration.name)) continue;
1205
1203
  const imported = file.getName(callee.text);
1206
1204
  if (imported !== "cva" && imported !== "sva") continue;
1207
1205
  if (!file.matchFn(callee.text)) continue;
@@ -1219,7 +1217,7 @@ function createParser(context) {
1219
1217
  * Defined out here rather than inside `getEvaluateOptions`, which runs per call node.
1220
1218
  */
1221
1219
  const reportUnresolvedRaw = (base, at) => {
1222
- const { line, column } = sourceFile.getLineAndColumnAtPos(at.getStart());
1220
+ const { line, column } = (0, _bamboocss_ts_ast.getLineAndColumnAtPos)(sourceFile, at.getStart());
1223
1221
  parserResult.unresolved.push({
1224
1222
  kind: "atomic",
1225
1223
  prop: base,
@@ -1261,14 +1259,14 @@ function createParser(context) {
1261
1259
  matchArg: () => true
1262
1260
  },
1263
1261
  getEvaluateOptions: (node) => {
1264
- if (!ts_morph.Node.isCallExpression(node)) return evaluateOptions;
1265
- const propAccessExpr = node.getExpression();
1266
- if (ts_morph.Node.isIdentifier(propAccessExpr)) {
1262
+ if (!_bamboocss_ts_ast.Node.isCallExpression(node)) return evaluateOptions;
1263
+ const propAccessExpr = node.expression;
1264
+ if (_bamboocss_ts_ast.Node.isIdentifier(propAccessExpr)) {
1267
1265
  const local = propAccessExpr.getText();
1268
1266
  if (!file.match(local) || file.getName(local) !== "fallback") return evaluateOptions;
1269
1267
  return { environment: Object.assign({}, defaultEnv, { extra: { [local]: fallbackImpl } }) };
1270
1268
  }
1271
- if (!ts_morph.Node.isPropertyAccessExpression(propAccessExpr)) return evaluateOptions;
1269
+ if (!_bamboocss_ts_ast.Node.isPropertyAccessExpression(propAccessExpr)) return evaluateOptions;
1272
1270
  let name = propAccessExpr.getText();
1273
1271
  const rawBase = name.endsWith(".raw") ? name.slice(0, -4) : void 0;
1274
1272
  if (rawBase && file.isLocalRecipe(rawBase)) {
@@ -1307,7 +1305,8 @@ function createParser(context) {
1307
1305
  if (query.kind === "call-expression") parserResult.setToken({
1308
1306
  name: "token.value",
1309
1307
  box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
1310
- data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
1308
+ data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0])),
1309
+ tokenCalleeRange: tokenCalleeRange(query.box.getNode())
1311
1310
  }, "tokenValue");
1312
1311
  });
1313
1312
  return;
@@ -1330,12 +1329,13 @@ function createParser(context) {
1330
1329
  if (query.kind === "call-expression") parserResult.setToken({
1331
1330
  name,
1332
1331
  box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
1333
- data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
1332
+ data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0])),
1333
+ tokenCalleeRange: tokenCalleeRange(query.box.getNode())
1334
1334
  });
1335
1335
  });
1336
1336
  }).when(file.isValidPattern, (name) => {
1337
1337
  result.queryList.forEach((query) => {
1338
- if (query.kind === "call-expression") parserResult.setPattern(name, {
1338
+ if (query.kind === "call-expression") parserResult.setPattern(name ?? "", {
1339
1339
  name,
1340
1340
  box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
1341
1341
  data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
@@ -1343,7 +1343,7 @@ function createParser(context) {
1343
1343
  });
1344
1344
  }).when(file.isValidRecipe, (name) => {
1345
1345
  result.queryList.forEach((query) => {
1346
- if (query.kind === "call-expression") parserResult.setRecipe(name, {
1346
+ if (query.kind === "call-expression") parserResult.setRecipe(name ?? "", {
1347
1347
  name,
1348
1348
  box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
1349
1349
  data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
@@ -1361,8 +1361,8 @@ function createParser(context) {
1361
1361
  } else if (jsx.isEnabled && result.kind === "component") result.queryList.forEach((query) => {
1362
1362
  const data = combineResult((0, _bamboocss_extractor.unbox)(query.box));
1363
1363
  for (const tag of [name, alias]) {
1364
- if (!jsx.isJsxTagRecipe(tag)) continue;
1365
- recipes.filter(tag).forEach((recipe) => {
1364
+ if (!jsx.isJsxTagRecipe(tag ?? "")) continue;
1365
+ recipes.filter(tag ?? "").forEach((recipe) => {
1366
1366
  parserResult.setRecipe(recipe.baseName, {
1367
1367
  type: "jsx-recipe",
1368
1368
  name: tag,
@@ -1375,19 +1375,16 @@ function createParser(context) {
1375
1375
  });
1376
1376
  });
1377
1377
  parserResult.deadCalls = file.getDeadCalls();
1378
- if (exportReadPairs.size) {
1379
- const project = sourceFile.getProject();
1380
- parserResult.setExportReads([...exportReadPairs].map((pair) => {
1381
- const at = pair.indexOf("\0");
1382
- const file = pair.slice(0, at);
1383
- const name = pair.slice(at + 1);
1384
- return {
1385
- file,
1386
- name,
1387
- digest: digestExportValue(project.getSourceFile(file), name, resolveModule)
1388
- };
1389
- }));
1390
- }
1378
+ if (exportReadPairs.size) parserResult.setExportReads([...exportReadPairs].map((pair) => {
1379
+ const at = pair.indexOf("\0");
1380
+ const file = pair.slice(0, at);
1381
+ const name = pair.slice(at + 1);
1382
+ return {
1383
+ file,
1384
+ name,
1385
+ digest: digestExportValue(resolveModule?.(file, sourceFile), name, resolveModule)
1386
+ };
1387
+ }));
1391
1388
  return parserResult;
1392
1389
  };
1393
1390
  }
@@ -1404,11 +1401,16 @@ const invalidateResolutions = () => {
1404
1401
  (0, _bamboocss_extractor.clearBoxNodeCache)();
1405
1402
  clearImportedRecipeCache();
1406
1403
  };
1407
- const normalizeCompilerOptions = (raw, basePath = process.cwd()) => {
1408
- if (!raw) return {};
1409
- const { options } = ts_morph.ts.convertCompilerOptionsFromJson(raw, basePath);
1410
- return options;
1411
- };
1404
+ /**
1405
+ * Compiler options as given.
1406
+ *
1407
+ * TypeScript 6 needed `convertCompilerOptionsFromJson` to turn a tsconfig's JSON spellings —
1408
+ * `"target": "esnext"`, relative `paths` — into the enum values and absolute paths a program
1409
+ * consumed. TypeScript 7 parses the tsconfig in the Go process and hands back options already
1410
+ * in that form, so there is nothing left to convert; the argument survives because callers pass
1411
+ * overrides that are already normalized.
1412
+ */
1413
+ const normalizeCompilerOptions = (raw, _basePath = process.cwd()) => raw ?? {};
1412
1414
  /** Snapshot the JSON-shaped ts-morph options while retaining opaque hosts and callbacks. */
1413
1415
  const snapshotProjectOption = (value) => {
1414
1416
  if (Array.isArray(value)) return value.map(snapshotProjectOption);
@@ -1438,7 +1440,7 @@ const prepareTsProjectOptions = (options, snapshotNested = false, compilerOption
1438
1440
  }
1439
1441
  };
1440
1442
  };
1441
- const createTsProject = (options) => new ts_morph.Project(options);
1443
+ const createTsProject = (options) => new _bamboocss_ts_ast.Project(options);
1442
1444
  /** Filesystem errors proving a lexical candidate cannot name a resolvable file. */
1443
1445
  const isMissingPathShapeError = (error) => {
1444
1446
  const code = error?.code;
@@ -1482,7 +1484,7 @@ const setProjectOnReceiver = (receiver, project) => {
1482
1484
  */
1483
1485
  const scriptKindFor = (filePath) => {
1484
1486
  const extension = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
1485
- return extension === ".ts" || extension === ".mts" || extension === ".cts" ? ts_morph.ScriptKind.TS : ts_morph.ScriptKind.TSX;
1487
+ return extension === ".ts" || extension === ".mts" || extension === ".cts" ? _bamboocss_ts_ast.ScriptKind.TS : _bamboocss_ts_ast.ScriptKind.TSX;
1486
1488
  };
1487
1489
  var Project = class {
1488
1490
  /**
@@ -1619,32 +1621,33 @@ var Project = class {
1619
1621
  this.#assertSourceFilesAccessor();
1620
1622
  const revision = this.#sourceFiles.revision;
1621
1623
  this.#sourceFiles.phase = "loading";
1624
+ let candidate;
1622
1625
  try {
1623
- const candidate = createTsProject(this.#sourceFiles.projectOptions);
1626
+ candidate = createTsProject(this.#sourceFiles.projectOptions);
1624
1627
  this.#assertSourceFilesTransaction(revision);
1625
1628
  let loaded = 0;
1629
+ const entries = [];
1626
1630
  for (const [index, file] of this.#sourceFiles.initialFiles.entries()) try {
1627
1631
  const content = read(file, index);
1628
1632
  this.#assertSourceFilesTransaction(revision);
1629
- candidate.createSourceFile(file, content, {
1630
- overwrite: true,
1631
- scriptKind: scriptKindFor(file)
1632
- });
1633
+ entries.push([file, content]);
1633
1634
  this.#assertSourceFilesTransaction(revision);
1634
1635
  loaded++;
1635
1636
  } catch (error) {
1636
1637
  this.#assertSourceFilesTransaction(revision);
1637
1638
  if (!skipMissing || error?.code !== "ENOENT") throw error;
1638
1639
  }
1640
+ candidate.addSourceFiles(entries);
1639
1641
  this.#assertSourceFilesTransaction(revision);
1640
1642
  if (loaded > 0) invalidateResolutions();
1641
- this.#moduleResolutionCache = void 0;
1643
+ this.#resolver = void 0;
1642
1644
  this.#fileTreeRevision++;
1643
1645
  this.#assertSourceFilesTransaction(revision);
1644
1646
  this.#sourceFiles.project = candidate;
1645
1647
  this.#sourceFiles.phase = "ready";
1646
1648
  return;
1647
1649
  } catch (error) {
1650
+ candidate?.dispose();
1648
1651
  this.#sourceFiles.project = void 0;
1649
1652
  this.#sourceFiles.phase = "pending";
1650
1653
  throw error;
@@ -1717,7 +1720,7 @@ var Project = class {
1717
1720
  sourceFilesRead: 0
1718
1721
  };
1719
1722
  resetResolutionState = () => {
1720
- this.#moduleResolutionCache = void 0;
1723
+ this.#resolver = void 0;
1721
1724
  this.#fileTreeRevision++;
1722
1725
  this.dependents = /* @__PURE__ */ new Map();
1723
1726
  this.dependencies = /* @__PURE__ */ new Map();
@@ -1758,12 +1761,24 @@ var Project = class {
1758
1761
  };
1759
1762
  getSourceFile = (filePath) => {
1760
1763
  this.#assertNotLoading();
1764
+ if (!this.project.has(filePath)) return void 0;
1761
1765
  return this.project.getSourceFile(filePath);
1762
1766
  };
1767
+ /**
1768
+ * How many syntax errors this file's parse produced.
1769
+ *
1770
+ * Asked by the token accounting, which may only speak for a file whose tree it can trust: a
1771
+ * construct the parser could not read leaves an ast that stops early, and every call below
1772
+ * the offending line silently ceases to exist. Zero means the file parsed as written.
1773
+ */
1774
+ getSyntacticDiagnosticCount = (filePath) => {
1775
+ this.#assertNotLoading();
1776
+ return this.project.getSyntacticDiagnosticCount(filePath);
1777
+ };
1763
1778
  /** ts-morph reports forward slashes; normalize callers' paths to match on Windows. */
1764
1779
  normalizePath = (filePath) => filePath.replaceAll("\\", "/");
1765
1780
  /** Shared filesystem-only resolution cache; no type checker is constructed. */
1766
- #moduleResolutionCache;
1781
+ #resolver;
1767
1782
  /**
1768
1783
  * Everything memoized against the shape of the file tree, including the negative half.
1769
1784
  *
@@ -1796,7 +1811,7 @@ var Project = class {
1796
1811
  }
1797
1812
  invalidateResolutions();
1798
1813
  if (fileTreeChanged) {
1799
- this.#moduleResolutionCache = void 0;
1814
+ this.#resolver = void 0;
1800
1815
  this.#fileTreeRevision++;
1801
1816
  }
1802
1817
  };
@@ -1819,17 +1834,10 @@ var Project = class {
1819
1834
  ...this.#sourceFiles.projectOptions,
1820
1835
  compilerOptions: snapshotProjectOption(next)
1821
1836
  };
1822
- if (this.#sourceFiles.phase === "ready") {
1823
- const current = this.project.getCompilerOptions();
1824
- const cleared = Object.fromEntries(Object.keys(current).map((key) => [key, void 0]));
1825
- this.project.compilerOptions.set({
1826
- ...cleared,
1827
- ...next
1828
- });
1829
- }
1837
+ this.#sourceFiles.project?.setCompilerOptions(next);
1830
1838
  }
1831
1839
  invalidateResolutions();
1832
- this.#moduleResolutionCache = void 0;
1840
+ this.#resolver = void 0;
1833
1841
  this.#fileTreeRevision++;
1834
1842
  this.sourcePreparations.clear();
1835
1843
  };
@@ -1875,33 +1883,33 @@ var Project = class {
1875
1883
  this.#assertNotLoading();
1876
1884
  const project = this.project;
1877
1885
  const compilerOptions = project.getCompilerOptions();
1878
- this.#moduleResolutionCache ??= ts_morph.ts.createModuleResolutionCache(project.getFileSystem().getCurrentDirectory(), (f) => f, compilerOptions);
1879
1886
  const configurationFiles = /* @__PURE__ */ new Set();
1880
- const resolutionHost = project.getModuleResolutionHost();
1881
1887
  const recordConfigurationFile = (filePath) => {
1882
1888
  const normalized = this.normalizePath(filePath);
1883
1889
  if (!normalized.endsWith("/package.json") || normalized.includes("/node_modules/")) return;
1884
1890
  if (!this.isInCheckout(normalized)) return;
1885
1891
  configurationFiles.add(normalized);
1886
1892
  };
1887
- const host = {
1888
- ...resolutionHost,
1889
- fileExists: (filePath) => {
1890
- recordConfigurationFile(filePath);
1891
- return resolutionHost.fileExists(filePath);
1892
- },
1893
- readFile: (filePath) => {
1894
- recordConfigurationFile(filePath);
1895
- return resolutionHost.readFile?.(filePath);
1893
+ this.#resolver ??= (0, _bamboocss_ts_ast.createResolver)({
1894
+ cwd: project.getCurrentDirectory(),
1895
+ fs: {
1896
+ fileExists: (filePath) => project.fileExists(filePath),
1897
+ readFile: (filePath) => project.readFile(filePath)
1896
1898
  }
1897
- };
1899
+ });
1900
+ const configured = this.#sourceFiles.projectOptions.compilerOptions ?? compilerOptions;
1898
1901
  this.resolutionWork.moduleResolutionsAttempted++;
1899
- const resolved = ts_morph.ts.resolveModuleName(moduleName, from.getFilePath(), compilerOptions, host, this.#moduleResolutionCache);
1900
- const failedLookupLocations = resolved.failedLookupLocations;
1901
- const affectingLocations = resolved.affectingLocations;
1902
- for (const filePath of affectingLocations ?? []) recordConfigurationFile(filePath);
1903
- const pendingCandidates = this.getLocalFailedLookupCandidates(failedLookupLocations);
1904
- const module = resolved.resolvedModule;
1902
+ const resolved = this.#resolver(moduleName, {
1903
+ importer: (0, _bamboocss_ts_ast.pathOf)(from),
1904
+ baseUrl: configured?.baseUrl,
1905
+ paths: configured?.paths
1906
+ });
1907
+ for (const filePath of resolved.affectingFiles) recordConfigurationFile(filePath);
1908
+ const pendingCandidates = this.getLocalFailedLookupCandidates(resolved.failedLookups);
1909
+ const module = resolved.path ? {
1910
+ resolvedFileName: resolved.path,
1911
+ isExternalLibraryImport: resolved.path.includes("/node_modules/")
1912
+ } : void 0;
1905
1913
  if (!module) return {
1906
1914
  configurationFiles: [...configurationFiles].sort(),
1907
1915
  local: this.isUnresolvedLocalSpecifier(moduleName, pendingCandidates) || moduleName.startsWith("#"),
@@ -1918,7 +1926,7 @@ var Project = class {
1918
1926
  local: this.isUnresolvedLocalSpecifier(moduleName, pendingCandidates),
1919
1927
  pendingCandidates
1920
1928
  };
1921
- const existing = project.getSourceFile(name);
1929
+ const existing = this.project.has(name) ? project.getSourceFile(name) : void 0;
1922
1930
  if (existing) {
1923
1931
  this.removedSourcePaths.delete(name);
1924
1932
  return {
@@ -1937,12 +1945,10 @@ var Project = class {
1937
1945
  try {
1938
1946
  this.resolutionWork.sourceFilesRead++;
1939
1947
  const content = project.getFileSystem().readFileSync(name);
1940
- const sourceFile = project.createSourceFile(name, content, {
1941
- overwrite: true,
1942
- scriptKind: scriptKindFor(name)
1943
- });
1948
+ const sourceFile = project.createSourceFile(name, content, { scriptKind: scriptKindFor(name) });
1949
+ if (!sourceFile) throw new Error(`bamboo: resolved ${name} but the project produced no source file`);
1944
1950
  this.resolutionWork.sourceFilesAdded++;
1945
- this.canonicalPaths.set(name, this.normalizePath(sourceFile.getFilePath()));
1951
+ this.canonicalPaths.set(name, this.normalizePath((0, _bamboocss_ts_ast.pathOf)(sourceFile)));
1946
1952
  return {
1947
1953
  configurationFiles: [...configurationFiles].sort(),
1948
1954
  local: true,
@@ -1996,28 +2002,28 @@ var Project = class {
1996
2002
  };
1997
2003
  ensureResolutionFacts = (sourceFile) => {
1998
2004
  this.#assertNotLoading();
1999
- const importer = this.normalizePath(sourceFile.getFilePath());
2005
+ const importer = this.normalizePath((0, _bamboocss_ts_ast.pathOf)(sourceFile));
2000
2006
  const text = sourceFile.getFullText();
2001
2007
  const cached = this.resolutionsByImporter.get(importer);
2002
2008
  if (cached?.sourceFile === sourceFile && cached.text === text && cached.treeRevision === this.#fileTreeRevision) return cached;
2003
- const declarations = [...sourceFile.getImportDeclarations().map((declaration) => ({
2009
+ const declarations = [...(0, _bamboocss_ts_ast.getImportDeclarations)(sourceFile).map((declaration) => ({
2004
2010
  declaration,
2005
2011
  kind: "import"
2006
- })), ...sourceFile.getExportDeclarations().map((declaration) => ({
2012
+ })), ...(0, _bamboocss_ts_ast.getExportDeclarations)(sourceFile).map((declaration) => ({
2007
2013
  declaration,
2008
2014
  kind: "export"
2009
- }))].filter(({ declaration }) => declaration.getModuleSpecifierValue() !== void 0).sort((left, right) => left.declaration.getStart() - right.declaration.getStart());
2015
+ }))].filter(({ declaration }) => (0, _bamboocss_ts_ast.getModuleSpecifierValue)(declaration) !== void 0).sort((left, right) => left.declaration.getStart() - right.declaration.getStart());
2010
2016
  const facts = [];
2011
2017
  const pendingCandidates = [];
2012
2018
  const configurationFiles = [];
2013
2019
  for (const [ordinal, { declaration, kind }] of declarations.entries()) {
2014
- const specifier = declaration.getModuleSpecifierValue();
2020
+ const specifier = (0, _bamboocss_ts_ast.getModuleSpecifierValue)(declaration);
2015
2021
  if (!specifier) continue;
2016
2022
  const resolved = this.resolveSpecifier(specifier, sourceFile);
2017
2023
  if (!resolved.local) continue;
2018
2024
  facts.push(Object.freeze({
2019
2025
  importer,
2020
- target: resolved.sourceFile ? this.normalizePath(resolved.sourceFile.getFilePath()) : null,
2026
+ target: resolved.sourceFile ? this.normalizePath((0, _bamboocss_ts_ast.pathOf)(resolved.sourceFile)) : null,
2021
2027
  specifier,
2022
2028
  kind,
2023
2029
  ordinal
@@ -2042,22 +2048,22 @@ var Project = class {
2042
2048
  };
2043
2049
  /** The sole cross-file source resolver supplied to parser and extractor. */
2044
2050
  resolveModule = (specifier, from) => {
2045
- const target = this.ensureResolutionFacts(from).facts.find((fact) => fact.specifier === specifier)?.target;
2051
+ const target = this.ensureResolutionFacts(from).facts.find((fact) => fact.specifier === specifier)?.target ?? (specifier.startsWith("/") ? specifier : void 0);
2046
2052
  if (!target) return;
2047
2053
  const sourceFile = this.project.getSourceFile(target);
2048
2054
  if (!sourceFile) return;
2049
- this.prepareEffectiveSource(sourceFile.getFilePath(), sourceFile);
2055
+ this.prepareEffectiveSource((0, _bamboocss_ts_ast.pathOf)(sourceFile), sourceFile);
2050
2056
  return sourceFile;
2051
2057
  };
2052
2058
  trackDependencies = (filePath, sourceFile) => {
2053
2059
  this.#assertNotLoading();
2054
- const importer = this.normalizePath(sourceFile.getFilePath());
2060
+ const importer = this.normalizePath((0, _bamboocss_ts_ast.pathOf)(sourceFile));
2055
2061
  this.canonicalPaths.set(this.normalizePath(filePath), importer);
2056
2062
  this.canonicalPaths.set(importer, importer);
2057
2063
  this.ensureResolutionFacts(sourceFile);
2058
2064
  };
2059
2065
  invalidateSourcePreparation = (filePath, sourceFile) => {
2060
- const sourcePath = this.normalizePath(sourceFile?.getFilePath() ?? filePath);
2066
+ const sourcePath = this.normalizePath((0, _bamboocss_ts_ast.pathOf)(sourceFile) ?? filePath);
2061
2067
  const preparation = this.sourcePreparations.get(sourcePath);
2062
2068
  if (preparation?.state === "preparing") preparation.invalidated = true;
2063
2069
  this.sourcePreparations.delete(sourcePath);
@@ -2073,7 +2079,7 @@ var Project = class {
2073
2079
  */
2074
2080
  prepareEffectiveSource = (filePath, sourceFile, hookFilePath = filePath) => {
2075
2081
  this.#assertNotLoading();
2076
- const sourcePath = this.normalizePath(sourceFile.getFilePath());
2082
+ const sourcePath = this.normalizePath((0, _bamboocss_ts_ast.pathOf)(sourceFile));
2077
2083
  const currentText = sourceFile.getFullText();
2078
2084
  const current = this.sourcePreparations.get(sourcePath);
2079
2085
  if (current?.state === "ready" && current.sourceFile === sourceFile && current.effectiveText === currentText) {
@@ -2118,7 +2124,7 @@ var Project = class {
2118
2124
  assertTransaction(currentText);
2119
2125
  const transformed = custom ?? this.transformFile(hookFilePath, currentText);
2120
2126
  assertTransaction(currentText);
2121
- if (currentText !== transformed) sourceFile.replaceWithText(transformed);
2127
+ if (currentText !== transformed) sourceFile = this.project.addSourceFile(filePath, transformed) ?? sourceFile;
2122
2128
  assertTransaction(transformed);
2123
2129
  this.trackDependencies(filePath, sourceFile);
2124
2130
  assertTransaction(transformed);
@@ -2135,7 +2141,7 @@ var Project = class {
2135
2141
  if (this.sourcePreparations.get(sourcePath) === transaction) this.sourcePreparations.delete(sourcePath);
2136
2142
  this.retractImporter(sourcePath);
2137
2143
  try {
2138
- if (sourceFile.getFullText() !== currentText) sourceFile.replaceWithText(currentText);
2144
+ if (sourceFile.getFullText() !== currentText) this.project.addSourceFile(filePath, currentText);
2139
2145
  } catch {}
2140
2146
  throw error;
2141
2147
  }
@@ -2162,7 +2168,7 @@ var Project = class {
2162
2168
  this.retractImporter(target);
2163
2169
  this.removedSourcePaths.add(target);
2164
2170
  this.canonicalPaths.set(target, target);
2165
- this.canonicalPaths.set(this.normalizePath(sourceFile.getFilePath()), target);
2171
+ this.canonicalPaths.set(this.normalizePath((0, _bamboocss_ts_ast.pathOf)(sourceFile)), target);
2166
2172
  };
2167
2173
  /**
2168
2174
  * Every file that transitively imports `filePath`, so a watcher can re-parse the
@@ -2171,7 +2177,7 @@ var Project = class {
2171
2177
  getDependents = (filePath) => {
2172
2178
  this.#assertNotLoading();
2173
2179
  const given = this.normalizePath(filePath);
2174
- const resolved = this.project.getSourceFile(filePath)?.getFilePath();
2180
+ const resolved = (0, _bamboocss_ts_ast.pathOf)(this.project.getSourceFile(filePath));
2175
2181
  const start = resolved ? this.normalizePath(resolved) : this.canonicalPaths.get(given) ?? given;
2176
2182
  const seen = /* @__PURE__ */ new Set();
2177
2183
  const queue = [start];
@@ -2199,7 +2205,7 @@ var Project = class {
2199
2205
  getDependencies = (filePath, targets) => {
2200
2206
  this.#assertNotLoading();
2201
2207
  const given = this.normalizePath(filePath);
2202
- const resolved = this.project.getSourceFile(filePath)?.getFilePath();
2208
+ const resolved = (0, _bamboocss_ts_ast.pathOf)(this.project.getSourceFile(filePath));
2203
2209
  const start = resolved ? this.normalizePath(resolved) : this.canonicalPaths.get(given) ?? given;
2204
2210
  const seen = /* @__PURE__ */ new Set();
2205
2211
  const importersByDependency = /* @__PURE__ */ new Map();
@@ -2220,7 +2226,7 @@ var Project = class {
2220
2226
  const selected = /* @__PURE__ */ new Set();
2221
2227
  const reverse = Array.from(targets, (target) => {
2222
2228
  const normalized = this.normalizePath(target);
2223
- const source = this.project.getSourceFile(target)?.getFilePath();
2229
+ const source = (0, _bamboocss_ts_ast.pathOf)(this.project.getSourceFile(target));
2224
2230
  return source ? this.normalizePath(source) : this.canonicalPaths.get(normalized) ?? normalized;
2225
2231
  }).filter((target) => seen.has(target));
2226
2232
  let reverseCursor = 0;
@@ -2246,7 +2252,7 @@ var Project = class {
2246
2252
  const selected = new Set(dependencies);
2247
2253
  const retained = new Set([...previous?.dependencies ?? [], ...previous?.pendingCandidates ?? []]);
2248
2254
  const given = this.normalizePath(filePath);
2249
- const resolved = this.project.getSourceFile(filePath)?.getFilePath();
2255
+ const resolved = (0, _bamboocss_ts_ast.pathOf)(this.project.getSourceFile(filePath));
2250
2256
  const start = resolved ? this.normalizePath(resolved) : this.canonicalPaths.get(given) ?? given;
2251
2257
  const pendingCandidates = /* @__PURE__ */ new Set();
2252
2258
  for (const importer of [start, ...dependencies]) {
@@ -2279,7 +2285,7 @@ var Project = class {
2279
2285
  const selected = new Set(dependencies);
2280
2286
  const retained = new Set(Array.from(previous, (file) => this.normalizePath(file)));
2281
2287
  const given = this.normalizePath(filePath);
2282
- const resolved = this.project.getSourceFile(filePath)?.getFilePath();
2288
+ const resolved = (0, _bamboocss_ts_ast.pathOf)(this.project.getSourceFile(filePath));
2283
2289
  const start = resolved ? this.normalizePath(resolved) : this.canonicalPaths.get(given) ?? given;
2284
2290
  const files = /* @__PURE__ */ new Set();
2285
2291
  let hasSemanticFact = false;
@@ -2306,10 +2312,9 @@ var Project = class {
2306
2312
  this.invalidateSourcePreparation(filePath, existing);
2307
2313
  this.removedSourcePaths.delete(this.normalizePath(filePath));
2308
2314
  this.invalidate();
2309
- return this.project.createSourceFile(filePath, content, {
2310
- overwrite: true,
2311
- scriptKind: scriptKindFor(filePath)
2312
- });
2315
+ const created = this.project.createSourceFile(filePath, content, { scriptKind: scriptKindFor(filePath) });
2316
+ if (!created) throw new Error(`bamboo: could not add ${filePath} to the project`);
2317
+ return created;
2313
2318
  };
2314
2319
  createSourceFiles = () => {
2315
2320
  this.#assertNotLoading();
@@ -2348,17 +2353,15 @@ var Project = class {
2348
2353
  * matches its own source, falls through, and is overwritten exactly as before.
2349
2354
  */
2350
2355
  if (existing && existing.getFullText() === content) {
2351
- this.markAuxiliary(existing.getFilePath(), options.auxiliary);
2356
+ this.markAuxiliary((0, _bamboocss_ts_ast.pathOf)(existing), options.auxiliary);
2352
2357
  return existing;
2353
2358
  }
2354
2359
  this.invalidateSourcePreparation(filePath, existing);
2355
2360
  this.removedSourcePaths.delete(this.normalizePath(filePath));
2356
- this.invalidate(!existing, existing?.getFilePath());
2357
- const sourceFile = this.project.createSourceFile(filePath, content, {
2358
- overwrite: true,
2359
- scriptKind: scriptKindFor(filePath)
2360
- });
2361
- this.markAuxiliary(sourceFile.getFilePath(), options.auxiliary);
2361
+ this.invalidate(!existing, (0, _bamboocss_ts_ast.pathOf)(existing));
2362
+ const sourceFile = this.project.createSourceFile(filePath, content, { scriptKind: scriptKindFor(filePath) });
2363
+ if (!sourceFile) throw new Error(`bamboo: could not add ${filePath} to the project`);
2364
+ this.markAuxiliary((0, _bamboocss_ts_ast.pathOf)(sourceFile), options.auxiliary);
2362
2365
  return sourceFile;
2363
2366
  };
2364
2367
  /** Claim or release compiler ownership of one source, in the ledger's own spelling. */
@@ -2374,10 +2377,10 @@ var Project = class {
2374
2377
  if (sourceFile) {
2375
2378
  this.invalidate();
2376
2379
  this.invalidateSourcePreparation(filePath, sourceFile);
2377
- this.markTargetRemoved(this.normalizePath(sourceFile.getFilePath()), sourceFile);
2378
- this.options.parserOptions.encoder.releaseFile(sourceFile.getFilePath());
2379
- this.auxiliarySources.delete(this.normalizePath(sourceFile.getFilePath()));
2380
- return this.project.removeSourceFile(sourceFile);
2380
+ this.markTargetRemoved(this.normalizePath((0, _bamboocss_ts_ast.pathOf)(sourceFile)), sourceFile);
2381
+ this.options.parserOptions.encoder.releaseFile((0, _bamboocss_ts_ast.pathOf)(sourceFile));
2382
+ this.auxiliarySources.delete(this.normalizePath((0, _bamboocss_ts_ast.pathOf)(sourceFile)));
2383
+ return this.project.removeSourceFile((0, _bamboocss_ts_ast.pathOf)(sourceFile));
2381
2384
  }
2382
2385
  return false;
2383
2386
  };
@@ -2396,10 +2399,10 @@ var Project = class {
2396
2399
  this.#assertNotLoading();
2397
2400
  this.#ensureSourceFiles();
2398
2401
  const sourceFile = this.getSourceFile(filePath);
2399
- this.invalidate(false, sourceFile?.getFilePath());
2402
+ this.invalidate(false, (0, _bamboocss_ts_ast.pathOf)(sourceFile));
2400
2403
  if (!sourceFile) return;
2401
2404
  this.invalidateSourcePreparation(filePath, sourceFile);
2402
- return sourceFile.refreshFromFileSystemSync();
2405
+ return this.project.reloadSourceFile(filePath);
2403
2406
  };
2404
2407
  reloadSourceFiles = () => {
2405
2408
  this.#assertNotLoading();
@@ -2410,7 +2413,7 @@ var Project = class {
2410
2413
  const source = this.getSourceFile(file);
2411
2414
  if (source) {
2412
2415
  this.invalidateSourcePreparation(file, source);
2413
- source.refreshFromFileSystemSync();
2416
+ this.project.reloadSourceFile(file);
2414
2417
  } else {
2415
2418
  this.invalidateSourcePreparation(file);
2416
2419
  this.removedSourcePaths.delete(this.normalizePath(file));
@@ -2452,10 +2455,12 @@ var Project = class {
2452
2455
  const { hooks } = this.options;
2453
2456
  const hookFilePath = options.hookFilePath ?? filePath;
2454
2457
  if (filePath.endsWith(".json")) return this.parseJson(filePath, encoder);
2455
- const sourceFile = this.project.getSourceFile(filePath);
2458
+ let sourceFile = this.project.getSourceFile(filePath);
2456
2459
  if (!sourceFile) return;
2457
- const { options: parserOptions } = this.prepareEffectiveSource(filePath, sourceFile, hookFilePath);
2458
- const result = (encoder ?? this.options.parserOptions.encoder).withOwner("parse", sourceFile.getFilePath(), () => this.parser(sourceFile, encoder, parserOptions, this.resolveModule))?.setFilePath(filePath);
2460
+ const prepared = this.prepareEffectiveSource(filePath, sourceFile, hookFilePath);
2461
+ const parserOptions = prepared.options;
2462
+ sourceFile = prepared.sourceFile ?? sourceFile;
2463
+ const result = (encoder ?? this.options.parserOptions.encoder).withOwner("parse", (0, _bamboocss_ts_ast.pathOf)(sourceFile), () => this.parser(sourceFile, encoder, parserOptions, this.resolveModule))?.setFilePath(filePath);
2459
2464
  hooks["parser:after"]?.({
2460
2465
  filePath: hookFilePath,
2461
2466
  result