@kurotako/core 0.1.0 → 0.1.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @kurotako/core
2
2
 
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 489a50d: Generated namespace root barrels now resolve overlapping public generator exports deterministically, so package declaration builds remain valid. The lexically first generator owns a colliding root export; every generator-specific subpath remains available.
8
+ - c575bc1: Integration of the IR union type. After merge, the non-fatal validation `info`
9
+ channel (`union_cycle`, `degenerate_union`) is logged once at `warn`.
10
+ `GenerateContext` gains `cycles: Set<string>` — the `${namespace}.${name}` of
11
+ every entity / type alias that takes part in a `ref` cycle in the merged IR
12
+ (from `@kurotako/ir`'s `refCycleMembers`) — so a generator can lazily wrap or
13
+ widen a reference instead of relying on a local heuristic.
14
+ - Updated dependencies [7a50439]
15
+ - @kurotako/ir@0.2.0
16
+
3
17
  ## 0.1.0
4
18
 
5
19
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -142,24 +142,24 @@ var DependencyCycleError = class extends TakoError {
142
142
  var OutputCollisionError = class extends TakoError {
143
143
  path;
144
144
  generators;
145
- constructor(path5, generators, hint) {
145
+ constructor(path6, generators, hint) {
146
146
  super(
147
147
  "output_collision",
148
- `generators '${generators[0]}' and '${generators[1]}' both emit '${path5}'${hint ? `. ${hint}` : ""}`
148
+ `generators '${generators[0]}' and '${generators[1]}' both emit '${path6}'${hint ? `. ${hint}` : ""}`
149
149
  );
150
- this.path = path5;
150
+ this.path = path6;
151
151
  this.generators = generators;
152
152
  }
153
153
  };
154
154
  var InvalidOutputPathError = class extends TakoError {
155
155
  path;
156
156
  generator;
157
- constructor(path5, generator) {
157
+ constructor(path6, generator) {
158
158
  super(
159
159
  "invalid_output_path",
160
- `generator '${generator}' emits '${path5}', which escapes the output root`
160
+ `generator '${generator}' emits '${path6}', which escapes the output root`
161
161
  );
162
- this.path = path5;
162
+ this.path = path6;
163
163
  this.generator = generator;
164
164
  }
165
165
  };
@@ -251,12 +251,12 @@ ${guidance}`
251
251
  };
252
252
  var OutputNotGeneratedError = class extends TakoError {
253
253
  path;
254
- constructor(path5) {
254
+ constructor(path6) {
255
255
  super(
256
256
  "output_not_generated",
257
- `refusing to wipe '${path5}': it is non-empty and its package.json lacks the '"//": "Generated by tako\u2026"' marker`
257
+ `refusing to wipe '${path6}': it is non-empty and its package.json lacks the '"//": "Generated by tako\u2026"' marker`
258
258
  );
259
- this.path = path5;
259
+ this.path = path6;
260
260
  }
261
261
  };
262
262
  var PackageInstallError = class extends TakoError {
@@ -323,6 +323,9 @@ function childLogger(base, prefixMeta) {
323
323
  };
324
324
  }
325
325
 
326
+ // src/run.ts
327
+ var import_ir2 = require("@kurotako/ir");
328
+
326
329
  // src/collect.ts
327
330
  var import_node_path = __toESM(require("path"), 1);
328
331
  function normalizePath(rawPath, generator) {
@@ -470,7 +473,7 @@ function findCycle(nodes, dependents) {
470
473
 
471
474
  // src/merge.ts
472
475
  var import_ir = require("@kurotako/ir");
473
- function mergeSources(entries) {
476
+ function mergeSources(entries, logger) {
474
477
  const sources = {};
475
478
  for (const { namespace, sourceIR } of entries) {
476
479
  if (sourceIR.namespace !== namespace) {
@@ -486,26 +489,27 @@ function mergeSources(entries) {
486
489
  sources[namespace] = validation.value;
487
490
  }
488
491
  const ir = { irVersion: import_ir.IR_VERSION, sources };
489
- try {
490
- (0, import_ir.assertIR)(ir);
491
- } catch (error) {
492
- if (error instanceof import_ir.IrValidationError) {
493
- throw new IrValidationError(error.issues);
494
- }
495
- throw error;
492
+ const result = (0, import_ir.validateIR)(ir);
493
+ if (!result.ok) {
494
+ throw new IrValidationError(result.issues);
495
+ }
496
+ for (const note of result.info ?? []) {
497
+ logger?.warn(
498
+ `IR ${note.code} at ${note.path === "" ? "<root>" : note.path}: ${note.message}`
499
+ );
496
500
  }
497
- return ir;
501
+ return result.value;
498
502
  }
499
503
 
500
504
  // src/writer/banner.ts
501
505
  var BANNER = "// Generated by tako. Do not edit.\n";
502
506
  var GITATTRIBUTES = "* linguist-generated=true\n";
503
507
  var COMMENTABLE_BASENAMES = /* @__PURE__ */ new Set(["tsconfig.json"]);
504
- function takesBanner(path5) {
505
- if (path5.endsWith(".ts") || path5.endsWith(".tsx")) {
508
+ function takesBanner(path6) {
509
+ if (path6.endsWith(".ts") || path6.endsWith(".tsx")) {
506
510
  return true;
507
511
  }
508
- const basename = path5.slice(path5.lastIndexOf("/") + 1);
512
+ const basename = path6.slice(path6.lastIndexOf("/") + 1);
509
513
  return COMMENTABLE_BASENAMES.has(basename);
510
514
  }
511
515
  function applyBanner(files) {
@@ -517,6 +521,10 @@ function applyBanner(files) {
517
521
  });
518
522
  }
519
523
 
524
+ // src/writer/barrel.ts
525
+ var import_node_path2 = __toESM(require("path"), 1);
526
+ var ts = __toESM(require("typescript"), 1);
527
+
520
528
  // src/writer/tree.ts
521
529
  function contributingGenerators(files) {
522
530
  const acc = /* @__PURE__ */ new Map();
@@ -544,65 +552,81 @@ function contributingGenerators(files) {
544
552
  }
545
553
 
546
554
  // src/writer/barrel.ts
547
- function synthesizeRootBarrels(files, artifactsByGenerator, logger) {
555
+ function synthesizeRootBarrels(files, _artifactsByGenerator, logger) {
548
556
  const contributors = contributingGenerators(files);
549
557
  const barrels = [];
550
558
  for (const namespace of [...contributors.keys()].sort()) {
551
559
  const generators = contributors.get(namespace) ?? [];
552
- if (artifactsByGenerator && logger) {
553
- warnAmbiguousReExports(
554
- namespace,
555
- generators,
556
- artifactsByGenerator,
557
- logger
560
+ const collisions = exportedNameCollisions(namespace, generators, files);
561
+ const content = [
562
+ ...generators.map((name) => `export * from './${name}';`),
563
+ ...[...collisions.entries()].map(
564
+ ([identifier, owners]) => `export { ${identifier} } from './${owners[0]}';`
565
+ ),
566
+ ""
567
+ ].join("\n");
568
+ for (const [identifier, owners] of collisions) {
569
+ logger?.warn(
570
+ `namespace '${namespace}': identifier '${identifier}' is re-exported by generators [${owners.join(
571
+ ", "
572
+ )}]; '${namespace}/index.ts' explicitly re-exports it from '${owners[0]}'. Import a generator subpath to select another declaration.`,
573
+ { namespace, identifier, generators: owners }
558
574
  );
559
575
  }
560
- const content = generators.map((name) => `export * from './${name}';
561
- `).join("");
562
576
  barrels.push({ path: `${namespace}/index.ts`, content });
563
577
  }
564
578
  return barrels;
565
579
  }
566
- function warnAmbiguousReExports(namespace, generators, artifactsByGenerator, logger) {
580
+ function exportedNameCollisions(namespace, generators, files) {
581
+ const sourceFiles = new Map(
582
+ files.filter((file) => file.path.startsWith(`${namespace}/`)).map((file) => [toVirtualPath(file.path), file.content])
583
+ );
584
+ const rootNames = generators.map((generator) => toVirtualPath(`${namespace}/${generator}/index.ts`)).filter((fileName) => sourceFiles.has(fileName));
585
+ if (rootNames.length < 2) {
586
+ return /* @__PURE__ */ new Map();
587
+ }
588
+ const options = {
589
+ module: ts.ModuleKind.NodeNext,
590
+ moduleResolution: ts.ModuleResolutionKind.NodeNext,
591
+ noLib: true
592
+ };
593
+ const host = ts.createCompilerHost(options, true);
594
+ host.fileExists = (fileName) => sourceFiles.has(fileName);
595
+ host.readFile = (fileName) => sourceFiles.get(fileName);
596
+ host.getSourceFile = (fileName, languageVersion) => {
597
+ const text = sourceFiles.get(fileName);
598
+ return text === void 0 ? void 0 : ts.createSourceFile(fileName, text, languageVersion, true);
599
+ };
600
+ host.directoryExists = (directoryName) => [...sourceFiles.keys()].some(
601
+ (fileName) => fileName.startsWith(`${directoryName}/`)
602
+ );
603
+ const program = ts.createProgram({ rootNames, options, host });
604
+ const checker = program.getTypeChecker();
567
605
  const owners = /* @__PURE__ */ new Map();
568
- for (const name of generators) {
569
- const artifact = artifactsByGenerator[name];
570
- if (!artifact) {
571
- continue;
572
- }
573
- const identifiers = /* @__PURE__ */ new Set();
574
- for (const [key, entity] of Object.entries(artifact.entities)) {
575
- const dot = key.indexOf(".");
576
- const entityNamespace = dot === -1 ? key : key.slice(0, dot);
577
- if (entityNamespace !== namespace) {
578
- continue;
579
- }
580
- for (const identifier of Object.values(entity.symbols)) {
581
- identifiers.add(identifier);
582
- }
583
- }
584
- for (const identifier of identifiers) {
585
- const list = owners.get(identifier) ?? [];
586
- list.push(name);
587
- owners.set(identifier, list);
588
- }
589
- }
590
- for (const [identifier, list] of owners) {
591
- if (list.length > 1) {
592
- const sorted = [...list].sort();
593
- logger.warn(
594
- `namespace '${namespace}': identifier '${identifier}' is re-exported by generators [${sorted.join(
595
- ", "
596
- )}]; the ambiguous star re-export from '${namespace}/index.ts' will be dropped. Import it from a generator subpath instead.`,
597
- { namespace, identifier, generators: sorted }
598
- );
606
+ for (const generator of generators) {
607
+ const source = program.getSourceFile(
608
+ toVirtualPath(`${namespace}/${generator}/index.ts`)
609
+ );
610
+ if (!source) continue;
611
+ const module2 = checker.getSymbolAtLocation(source);
612
+ if (!module2) continue;
613
+ for (const exported of checker.getExportsOfModule(module2)) {
614
+ const list = owners.get(exported.name) ?? [];
615
+ list.push(generator);
616
+ owners.set(exported.name, list);
599
617
  }
600
618
  }
619
+ return new Map(
620
+ [...owners].filter(([, owners2]) => owners2.length > 1).sort(([a], [b]) => a.localeCompare(b))
621
+ );
622
+ }
623
+ function toVirtualPath(fileName) {
624
+ return import_node_path2.default.posix.join("/", fileName);
601
625
  }
602
626
 
603
627
  // src/writer/directory.ts
604
628
  var import_promises = __toESM(require("fs/promises"), 1);
605
- var import_node_path2 = __toESM(require("path"), 1);
629
+ var import_node_path3 = __toESM(require("path"), 1);
606
630
  var GITATTRIBUTES2 = "* linguist-generated=true\n";
607
631
  function sortByPath(entries) {
608
632
  return [...entries].sort(
@@ -617,24 +641,24 @@ var directoryWriter = {
617
641
  "mode 'dir' requires 'output.dir'"
618
642
  );
619
643
  }
620
- const dir = import_node_path2.default.resolve(output.dir);
644
+ const dir = import_node_path3.default.resolve(output.dir);
621
645
  const planned = files.map((file) => ({
622
- path: import_node_path2.default.join(dir, file.path),
646
+ path: import_node_path3.default.join(dir, file.path),
623
647
  content: file.content
624
648
  }));
625
649
  planned.push({
626
- path: import_node_path2.default.join(dir, ".gitattributes"),
650
+ path: import_node_path3.default.join(dir, ".gitattributes"),
627
651
  content: GITATTRIBUTES2
628
652
  });
629
653
  return sortByPath(planned);
630
654
  },
631
655
  async write(input) {
632
656
  const planned = await this.plan(input);
633
- const dir = import_node_path2.default.resolve(input.output.dir);
657
+ const dir = import_node_path3.default.resolve(input.output.dir);
634
658
  await import_promises.default.rm(dir, { recursive: true, force: true });
635
659
  await import_promises.default.mkdir(dir, { recursive: true });
636
660
  for (const file of planned) {
637
- await import_promises.default.mkdir(import_node_path2.default.dirname(file.path), { recursive: true });
661
+ await import_promises.default.mkdir(import_node_path3.default.dirname(file.path), { recursive: true });
638
662
  await import_promises.default.writeFile(file.path, file.content, "utf8");
639
663
  }
640
664
  return planned.map((file) => file.path);
@@ -645,7 +669,7 @@ var directoryWriter = {
645
669
  var import_node_fs2 = require("fs");
646
670
  var import_promises2 = __toESM(require("fs/promises"), 1);
647
671
  var import_node_module = require("module");
648
- var import_node_path4 = __toESM(require("path"), 1);
672
+ var import_node_path5 = __toESM(require("path"), 1);
649
673
 
650
674
  // src/writer/peers.ts
651
675
  function collectPeerDependencies(artifactsByGenerator, files) {
@@ -689,7 +713,7 @@ function collectPeerDependencies(artifactsByGenerator, files) {
689
713
  // src/writer/pm.ts
690
714
  var import_node_child_process = require("child_process");
691
715
  var import_node_fs = require("fs");
692
- var import_node_path3 = __toESM(require("path"), 1);
716
+ var import_node_path4 = __toESM(require("path"), 1);
693
717
  var import_node_util = require("util");
694
718
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
695
719
  var PACKAGE_MANAGERS = [
@@ -709,10 +733,10 @@ function isPackageManager(value) {
709
733
  return PACKAGE_MANAGERS.includes(value);
710
734
  }
711
735
  function* ancestors(startDir) {
712
- let dir = import_node_path3.default.resolve(startDir);
736
+ let dir = import_node_path4.default.resolve(startDir);
713
737
  while (true) {
714
738
  yield dir;
715
- const parent = import_node_path3.default.dirname(dir);
739
+ const parent = import_node_path4.default.dirname(dir);
716
740
  if (parent === dir) {
717
741
  return;
718
742
  }
@@ -725,16 +749,16 @@ function resolvePackageManager(opts) {
725
749
  }
726
750
  for (const dir of ancestors(opts.startDir)) {
727
751
  for (const [file, pm] of LOCKFILES) {
728
- if ((0, import_node_fs.existsSync)(import_node_path3.default.join(dir, file))) {
752
+ if ((0, import_node_fs.existsSync)(import_node_path4.default.join(dir, file))) {
729
753
  return pm;
730
754
  }
731
755
  }
732
- if ((0, import_node_fs.existsSync)(import_node_path3.default.join(dir, ".git"))) {
756
+ if ((0, import_node_fs.existsSync)(import_node_path4.default.join(dir, ".git"))) {
733
757
  break;
734
758
  }
735
759
  }
736
760
  for (const dir of ancestors(opts.startDir)) {
737
- const pkgPath = import_node_path3.default.join(dir, "package.json");
761
+ const pkgPath = import_node_path4.default.join(dir, "package.json");
738
762
  if ((0, import_node_fs.existsSync)(pkgPath)) {
739
763
  try {
740
764
  const pkg = JSON.parse((0, import_node_fs.readFileSync)(pkgPath, "utf8"));
@@ -747,7 +771,7 @@ function resolvePackageManager(opts) {
747
771
  } catch {
748
772
  }
749
773
  }
750
- if ((0, import_node_fs.existsSync)(import_node_path3.default.join(dir, ".git"))) {
774
+ if ((0, import_node_fs.existsSync)(import_node_path4.default.join(dir, ".git"))) {
751
775
  break;
752
776
  }
753
777
  }
@@ -780,7 +804,7 @@ function computePackageLayout({
780
804
  "mode 'package' requires 'output.scope'"
781
805
  );
782
806
  }
783
- const packagesDir = import_node_path4.default.resolve(output.packagesDir);
807
+ const packagesDir = import_node_path5.default.resolve(output.packagesDir);
784
808
  const scope = output.scope;
785
809
  const scopeSlug = scope.replace(/^@/, "");
786
810
  const peersByNamespace = collectPeerDependencies(artifacts ?? {}, files);
@@ -803,17 +827,17 @@ function computePackageLayout({
803
827
  const sources = [...byNamespace.get(namespace) ?? []].sort(
804
828
  (a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0
805
829
  );
806
- const pkgDir = import_node_path4.default.join(packagesDir, `${scopeSlug}-${namespace}`);
830
+ const pkgDir = import_node_path5.default.join(packagesDir, `${scopeSlug}-${namespace}`);
807
831
  const entries = [];
808
832
  for (const src of sources) {
809
833
  planned.push({
810
- path: import_node_path4.default.join(pkgDir, "src", src.path),
834
+ path: import_node_path5.default.join(pkgDir, "src", src.path),
811
835
  content: src.content
812
836
  });
813
837
  entries.push(`src/${src.path}`);
814
838
  }
815
839
  planned.push({
816
- path: import_node_path4.default.join(pkgDir, "package.json"),
840
+ path: import_node_path5.default.join(pkgDir, "package.json"),
817
841
  content: buildPackageJson(
818
842
  scope,
819
843
  namespace,
@@ -821,22 +845,22 @@ function computePackageLayout({
821
845
  )
822
846
  });
823
847
  planned.push({
824
- path: import_node_path4.default.join(pkgDir, "tsconfig.json"),
848
+ path: import_node_path5.default.join(pkgDir, "tsconfig.json"),
825
849
  content: buildTsconfig(namespace)
826
850
  });
827
851
  const sortedEntries = entries.sort();
828
852
  entriesByNamespace.set(namespace, sortedEntries);
829
853
  planned.push({
830
- path: import_node_path4.default.join(pkgDir, "tsup.config.ts"),
854
+ path: import_node_path5.default.join(pkgDir, "tsup.config.ts"),
831
855
  content: buildTsupConfig(sortedEntries)
832
856
  });
833
857
  planned.push({
834
- path: import_node_path4.default.join(pkgDir, ".gitattributes"),
858
+ path: import_node_path5.default.join(pkgDir, ".gitattributes"),
835
859
  content: GITATTRIBUTES
836
860
  });
837
861
  }
838
862
  planned.push({
839
- path: import_node_path4.default.join(packagesDir, ".gitattributes"),
863
+ path: import_node_path5.default.join(packagesDir, ".gitattributes"),
840
864
  content: `${scopeSlug}-*/** linguist-generated=true
841
865
  `
842
866
  });
@@ -852,11 +876,11 @@ var packageWriter = {
852
876
  assertWorkspaceBaseFiles(packagesDir);
853
877
  await import_promises2.default.mkdir(packagesDir, { recursive: true });
854
878
  for (const namespace of namespaces) {
855
- await guardAndReset(import_node_path4.default.join(packagesDir, `${scopeSlug}-${namespace}`));
879
+ await guardAndReset(import_node_path5.default.join(packagesDir, `${scopeSlug}-${namespace}`));
856
880
  }
857
881
  const written = [];
858
882
  for (const file of planned) {
859
- await import_promises2.default.mkdir(import_node_path4.default.dirname(file.path), { recursive: true });
883
+ await import_promises2.default.mkdir(import_node_path5.default.dirname(file.path), { recursive: true });
860
884
  await import_promises2.default.writeFile(file.path, file.content, "utf8");
861
885
  written.push(file.path);
862
886
  }
@@ -878,13 +902,13 @@ var packageWriter = {
878
902
  };
879
903
  var TSUP_CONFIG_BASE_EXTENSIONS = [".ts", ".js", ".mjs", ".cjs"];
880
904
  function assertWorkspaceBaseFiles(packagesDir) {
881
- const workspaceRoot = import_node_path4.default.dirname(packagesDir);
905
+ const workspaceRoot = import_node_path5.default.dirname(packagesDir);
882
906
  const missing = [];
883
- if (!(0, import_node_fs2.existsSync)(import_node_path4.default.join(workspaceRoot, "tsconfig.base.json"))) {
907
+ if (!(0, import_node_fs2.existsSync)(import_node_path5.default.join(workspaceRoot, "tsconfig.base.json"))) {
884
908
  missing.push("tsconfig.base.json");
885
909
  }
886
910
  const hasTsupBase = TSUP_CONFIG_BASE_EXTENSIONS.some(
887
- (ext) => (0, import_node_fs2.existsSync)(import_node_path4.default.join(workspaceRoot, `tsup.config.base${ext}`))
911
+ (ext) => (0, import_node_fs2.existsSync)(import_node_path5.default.join(workspaceRoot, `tsup.config.base${ext}`))
888
912
  );
889
913
  if (!hasTsupBase) {
890
914
  missing.push("tsup.config.base.{ts,js,mjs,cjs}");
@@ -898,7 +922,7 @@ function assertWorkspaceBaseFiles(packagesDir) {
898
922
  }
899
923
  function isResolvableFrom(specifier, from) {
900
924
  try {
901
- (0, import_node_module.createRequire)(import_node_path4.default.join(from, "noop.js")).resolve(specifier);
925
+ (0, import_node_module.createRequire)(import_node_path5.default.join(from, "noop.js")).resolve(specifier);
902
926
  return true;
903
927
  } catch {
904
928
  return false;
@@ -920,7 +944,7 @@ async function guardAndReset(pkgDir) {
920
944
  function hasGeneratedMarker(pkgDir) {
921
945
  try {
922
946
  const pkg = JSON.parse(
923
- (0, import_node_fs2.readFileSync)(import_node_path4.default.join(pkgDir, "package.json"), "utf8")
947
+ (0, import_node_fs2.readFileSync)(import_node_path5.default.join(pkgDir, "package.json"), "utf8")
924
948
  );
925
949
  return typeof pkg["//"] === "string" && pkg["//"].startsWith("Generated by tako");
926
950
  } catch {
@@ -932,7 +956,7 @@ async function buildPackages(packagesDir, scopeSlug, namespaces, entriesByNamesp
932
956
  const originalCwd = process.cwd();
933
957
  try {
934
958
  for (const namespace of namespaces) {
935
- const pkgDir = import_node_path4.default.join(packagesDir, `${scopeSlug}-${namespace}`);
959
+ const pkgDir = import_node_path5.default.join(packagesDir, `${scopeSlug}-${namespace}`);
936
960
  process.chdir(pkgDir);
937
961
  try {
938
962
  await build({
@@ -952,13 +976,13 @@ async function buildPackages(packagesDir, scopeSlug, namespaces, entriesByNamesp
952
976
  entry: Object.fromEntries(
953
977
  (entriesByNamespace.get(namespace) ?? []).map((entry) => [
954
978
  entry.replace(/^src\//, "").replace(/\.ts$/, ""),
955
- import_node_path4.default.join(pkgDir, entry)
979
+ import_node_path5.default.join(pkgDir, entry)
956
980
  ])
957
981
  ),
958
- tsconfig: import_node_path4.default.join(pkgDir, "tsconfig.json"),
982
+ tsconfig: import_node_path5.default.join(pkgDir, "tsconfig.json"),
959
983
  format: ["esm", "cjs"],
960
984
  dts: { compilerOptions: { composite: false, incremental: false } },
961
- outDir: import_node_path4.default.join(pkgDir, "dist"),
985
+ outDir: import_node_path5.default.join(pkgDir, "dist"),
962
986
  silent: true
963
987
  });
964
988
  } catch (cause) {
@@ -971,10 +995,10 @@ async function buildPackages(packagesDir, scopeSlug, namespaces, entriesByNamesp
971
995
  }
972
996
  function findWorkspaceRoot(startDir) {
973
997
  for (const dir of ancestors(startDir)) {
974
- if ((0, import_node_fs2.existsSync)(import_node_path4.default.join(dir, "pnpm-workspace.yaml"))) {
998
+ if ((0, import_node_fs2.existsSync)(import_node_path5.default.join(dir, "pnpm-workspace.yaml"))) {
975
999
  return dir;
976
1000
  }
977
- const pkgPath = import_node_path4.default.join(dir, "package.json");
1001
+ const pkgPath = import_node_path5.default.join(dir, "package.json");
978
1002
  if ((0, import_node_fs2.existsSync)(pkgPath)) {
979
1003
  try {
980
1004
  const pkg = JSON.parse((0, import_node_fs2.readFileSync)(pkgPath, "utf8"));
@@ -1088,7 +1112,13 @@ async function run(config, opts) {
1088
1112
  }
1089
1113
  }
1090
1114
  checkSignal();
1091
- const ir = mergeSources(entries);
1115
+ const ir = mergeSources(entries, logger);
1116
+ const cycles = /* @__PURE__ */ new Set();
1117
+ for (const [namespace, source] of Object.entries(ir.sources)) {
1118
+ for (const member of (0, import_ir2.refCycleMembers)(source)) {
1119
+ cycles.add(`${namespace}.${member}`);
1120
+ }
1121
+ }
1092
1122
  checkSignal();
1093
1123
  const order = generatorOrder(config.generators);
1094
1124
  const artifacts = {};
@@ -1116,6 +1146,7 @@ async function run(config, opts) {
1116
1146
  const out = await generator.generate({
1117
1147
  ir: view,
1118
1148
  dependencies,
1149
+ cycles,
1119
1150
  logger: childLogger(logger, { generator: name })
1120
1151
  });
1121
1152
  artifacts[name] = out.artifact;