@theholocron/cli 5.0.0-alpha.31 → 5.0.0-alpha.33

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/cli.mjs CHANGED
@@ -5348,13 +5348,20 @@ async function runSetup(input) {
5348
5348
  await source.writeRepoFile("turbo.json", content);
5349
5349
  }));
5350
5350
  print(formatStep(steps[steps.length - 1]));
5351
- const rootPkg = await readFile(join(input.context.repoRoot, "package.json"), "utf8").then((raw) => JSON.parse(raw)).catch(() => null);
5351
+ const rootPkgRaw = await readFile(join(input.context.repoRoot, "package.json"), "utf8").catch(() => null);
5352
+ const rootPkg = rootPkgRaw ? JSON.parse(rootPkgRaw) : null;
5352
5353
  const workspaceYaml = await readFile(join(input.context.repoRoot, "pnpm-workspace.yaml"), "utf8").catch(() => "");
5353
5354
  const rootScripts = Object.keys(rootPkg?.scripts ?? {});
5354
5355
  const workspaceFix = astromechTurbo.ensureRootWorkspaceMember(workspaceYaml, rootScripts);
5355
5356
  if (workspaceFix.changed) steps.push(await runStep("source", "fix pnpm-workspace.yaml (root workspace member, #692)", dryRun, async () => {
5356
5357
  await source.writeRepoFile("pnpm-workspace.yaml", workspaceFix.content);
5357
5358
  }));
5359
+ if (rootPkgRaw) {
5360
+ const depFix = astromechTurbo.ensureTurboDependency(rootPkgRaw, workspaceFix.content);
5361
+ if (depFix.changed) steps.push(await runStep("source", "add turbo devDependency", dryRun, async () => {
5362
+ await source.writeRepoFile("package.json", depFix.content);
5363
+ }));
5364
+ }
5358
5365
  }
5359
5366
  print(formatStep(steps[steps.length - 1]));
5360
5367
  }
@@ -6574,13 +6581,16 @@ async function runSync(input) {
6574
6581
  } else {
6575
6582
  const tasksConfig = { tasks: config.tasks };
6576
6583
  if (config.holocronScript !== void 0) tasksConfig.holocronScript = config.holocronScript;
6577
- const desired = createAstromech({
6584
+ const astromechScripts = createAstromech({
6578
6585
  cwd: input.context.repoRoot,
6579
6586
  config: tasksConfig,
6580
6587
  logger
6581
- }).packageScripts();
6588
+ });
6589
+ const desired = astromechScripts.packageScripts();
6590
+ const turboContent = astromechScripts.turboConfig();
6591
+ const turboEligible = new Set(turboContent ? Object.keys(JSON.parse(turboContent).tasks) : []);
6582
6592
  steps.push(await runSyncStep("local", "sync scripts", dryRun, async () => {
6583
- const changed = await mergePackageJsonScripts(input.context.repoRoot, desired);
6593
+ const changed = await mergePackageJsonScripts(input.context.repoRoot, desired, turboEligible);
6584
6594
  if (changed === null) return "no package.json";
6585
6595
  if (changed.length === 0) return `${Object.keys(desired).length} scripts already current`;
6586
6596
  return `${changed.join(", ")} set`;
@@ -6607,7 +6617,8 @@ async function runSync(input) {
6607
6617
  await writeFile(join(input.context.repoRoot, "turbo.json"), content, "utf8");
6608
6618
  }));
6609
6619
  print(formatSyncStep(steps[steps.length - 1]));
6610
- const rootPkg = await readFile(join(input.context.repoRoot, "package.json"), "utf8").then((raw) => JSON.parse(raw)).catch(() => null);
6620
+ const rootPkgRaw = await readFile(join(input.context.repoRoot, "package.json"), "utf8").catch(() => null);
6621
+ const rootPkg = rootPkgRaw ? JSON.parse(rootPkgRaw) : null;
6611
6622
  const workspaceYaml = await readFile(join(input.context.repoRoot, "pnpm-workspace.yaml"), "utf8").catch(() => "");
6612
6623
  const rootScripts = Object.keys(rootPkg?.scripts ?? {});
6613
6624
  const workspaceFix = astromechTurbo.ensureRootWorkspaceMember(workspaceYaml, rootScripts);
@@ -6617,6 +6628,15 @@ async function runSync(input) {
6617
6628
  }));
6618
6629
  print(formatSyncStep(steps[steps.length - 1]));
6619
6630
  }
6631
+ if (rootPkgRaw) {
6632
+ const depFix = astromechTurbo.ensureTurboDependency(rootPkgRaw, workspaceFix.content);
6633
+ if (depFix.changed) {
6634
+ steps.push(await runSyncStep("local", "add turbo devDependency", dryRun, async () => {
6635
+ await writeFile(join(input.context.repoRoot, "package.json"), depFix.content, "utf8");
6636
+ }));
6637
+ print(formatSyncStep(steps[steps.length - 1]));
6638
+ }
6639
+ }
6620
6640
  }
6621
6641
  }
6622
6642
  if (stepName === "wiki") {
@@ -6700,8 +6720,21 @@ async function writePackageJsonField(repoRoot, field, value) {
6700
6720
  * Merge `desired` script entries into `package.json#scripts` — sets only the
6701
6721
  * listed keys, never touches the rest. Returns the sorted list of keys that
6702
6722
  * drifted (empty when all current), or `null` when there is no `package.json`.
6723
+ *
6724
+ * `turboEligible` (#747): task names `turbo.json` fans out — the same set
6725
+ * {@link EnsureRootWorkspaceMemberResult} above already keys off. For those
6726
+ * only, an *existing* script that isn't the generated `holocron run <task>
6727
+ * --` wrapper is left untouched: root may genuinely be a directly-buildable
6728
+ * package (not an orchestrator) with its own real command (`tsc --noEmit`,
6729
+ * `vitest run`, …) for that task. Overwriting it would silently discard the
6730
+ * real command and, once `turbo.json` exists and root is a real workspace
6731
+ * member (#692), reintroduce infinite recursion — `turbo run <task>` →
6732
+ * root's own `<task>` script → the wrapper → `holocron run <task>` sees
6733
+ * `turbo.json` still defines the task → `turbo run <task>` again, forever.
6734
+ * Every other key (non-turbo tasks, `holocron`, `prepare`, …) has no
6735
+ * legitimate direct-command alternative and stays fully generator-owned.
6703
6736
  */
6704
- async function mergePackageJsonScripts(repoRoot, desired) {
6737
+ async function mergePackageJsonScripts(repoRoot, desired, turboEligible) {
6705
6738
  const pkgPath = join(repoRoot, "package.json");
6706
6739
  let content;
6707
6740
  try {
@@ -6712,15 +6745,31 @@ async function mergePackageJsonScripts(repoRoot, desired) {
6712
6745
  const pkg = JSON.parse(content);
6713
6746
  const scripts = pkg.scripts ?? {};
6714
6747
  const changed = [];
6715
- for (const [name, command] of Object.entries(desired)) if (scripts[name] !== command) {
6716
- scripts[name] = command;
6717
- changed.push(name);
6748
+ for (const [name, command] of Object.entries(desired)) {
6749
+ const existing = scripts[name];
6750
+ if (turboEligible.has(name) && existing !== void 0 && !isGeneratedRunWrapper(existing, name)) continue;
6751
+ if (existing !== command) {
6752
+ scripts[name] = command;
6753
+ changed.push(name);
6754
+ }
6718
6755
  }
6719
6756
  if (changed.length === 0) return [];
6720
6757
  pkg.scripts = scripts;
6721
6758
  await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
6722
6759
  return changed.sort();
6723
6760
  }
6761
+ /**
6762
+ * Matches the exact shape `packageScripts()` generates for `taskName` —
6763
+ * `<bin> run <taskName> --`, `<bin>` normally `holocron` but substitutable
6764
+ * (`holocronScript`, itself allowed to be multi-word, e.g. `"node
6765
+ packages/cli/dist/cli.mjs"` — see its own test). Checking the *suffix*
6766
+ * rather than the whole string means an old wrapper survives a
6767
+ * `holocronScript` rename intact — it's still recognized as a wrapper (safe
6768
+ * to update to the new one), not mistaken for a real command.
6769
+ */
6770
+ function isGeneratedRunWrapper(command, taskName) {
6771
+ return command.endsWith(` run ${taskName} --`);
6772
+ }
6724
6773
  const README_DESC_START = "<!-- holocron:description -->";
6725
6774
  const README_DESC_END = "<!-- /holocron:description -->";
6726
6775
  async function updateReadmeDescription(repoRoot, description) {