@orkestrel/scaffold 0.0.78 → 0.0.79

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/bin/main.js CHANGED
@@ -502,6 +502,55 @@ function manifestToWritableDependencies(manifest, blueprint) {
502
502
  };
503
503
  }
504
504
  /**
505
+ * Reads the manifest scripts a writing verb may write.
506
+ *
507
+ * @param manifest - The target manifest text.
508
+ * @param blueprint - The workspace shape that supplies the planned scripts.
509
+ * @returns The blueprint's writable scripts, followed by the planned `test` chain when the
510
+ * declared chain is a generated predecessor of it.
511
+ *
512
+ * @remarks
513
+ * A declared `test` chain is a generated predecessor when it runs fewer `&&` steps than the
514
+ * planned chain and every step it runs is a planned step, in the planned order. That is the chain
515
+ * a birth or an earlier write left before a structural fact planned another step, such as a root
516
+ * `tests/setupBrowser.test.ts` proof planning `npm run test:setup:browser`, so the write lands the
517
+ * planned chain in its place. A chain running a step the plan does not generate, or running the
518
+ * planned steps in another order, is one the package owns, and no entry names it.
519
+ *
520
+ * The planned chain joins only when every step it adds runs a script the manifest declares or the
521
+ * writable scripts supply. The `test:src` and `test:app` aggregates sit outside the writable
522
+ * scripts, so a planned chain adding an aggregate the manifest lacks stays with the package rather
523
+ * than landing a step that names a missing script.
524
+ */
525
+ function manifestToWritableScripts(manifest, blueprint) {
526
+ const writable = blueprintToWritableScripts(blueprint);
527
+ const planned = blueprintToScripts(blueprint).test;
528
+ const parsed = parseJSON(manifest);
529
+ const scripts = isRecord(parsed) && isRecord(parsed.scripts) ? parsed.scripts : {};
530
+ const declared = scripts.test;
531
+ if (planned === void 0 || !isString(declared)) return writable;
532
+ const steps = declared.split("&&").map((step) => step.trim());
533
+ const expected = planned.split("&&").map((step) => step.trim());
534
+ if (steps.length >= expected.length) return writable;
535
+ let cursor = 0;
536
+ for (const step of steps) {
537
+ while (cursor < expected.length && expected[cursor] !== step) cursor += 1;
538
+ if (cursor === expected.length) return writable;
539
+ cursor += 1;
540
+ }
541
+ const supplied = new Set(writable.map(({ name }) => name));
542
+ for (const step of expected) {
543
+ if (steps.includes(step)) continue;
544
+ const name = /^npm run (\S+)$/.exec(step)?.[1];
545
+ if (name === void 0 || !supplied.has(name) && !isString(scripts[name])) return writable;
546
+ }
547
+ return [...writable, {
548
+ name: "test",
549
+ command: planned,
550
+ accepted: [declared]
551
+ }];
552
+ }
553
+ /**
505
554
  * Keeps only dependencies published by the Orkestrel fleet.
506
555
  *
507
556
  * @param dependencies - The declared dependencies to inspect.
@@ -1447,7 +1496,7 @@ var CLI = class {
1447
1496
  if (refusal !== void 0) throw refusal;
1448
1497
  const result = mergeResults(host.materializer.repair(plan, audit, target), host.materializer.declare({
1449
1498
  pins: versions.pins,
1450
- scripts: blueprintToWritableScripts(blueprint)
1499
+ scripts: manifestToWritableScripts(this.#manifest(target), blueprint)
1451
1500
  }, target));
1452
1501
  const terminalBlueprint = this.#derive(target);
1453
1502
  const [measured] = this.#survey(host.materializer, terminalBlueprint, target, groups);
@@ -1549,7 +1598,7 @@ var CLI = class {
1549
1598
  }
1550
1599
  const declared = {
1551
1600
  pins: manifestToWritableDependencies(this.#manifest(target), blueprint),
1552
- scripts: blueprintToWritableScripts(blueprint)
1601
+ scripts: manifestToWritableScripts(this.#manifest(target), blueprint)
1553
1602
  };
1554
1603
  const local = mergeResults(host.materializer.repair(plan, audit, target), host.materializer.remove(plan, audit, command.dirty === true ? {
1555
1604
  tracked: worktree.tracked,
@@ -1878,7 +1927,8 @@ var CLI = class {
1878
1927
  }
1879
1928
  #projectQuestion(target, blueprint, writing = false) {
1880
1929
  const text = this.#manifest(target);
1881
- const manifest = replaceManifestScripts(text, blueprintToWritableScripts(blueprint)) ?? text;
1930
+ const writable = writing ? manifestToWritableScripts(text, blueprint) : blueprintToWritableScripts(blueprint);
1931
+ const manifest = replaceManifestScripts(text, writable) ?? text;
1882
1932
  const planned = blueprintToRootVite(blueprint);
1883
1933
  const parsed = parseJSON(manifest);
1884
1934
  const scripts = isRecord(parsed) && isRecord(parsed.scripts) ? parsed.scripts : {};
@@ -1984,7 +2034,7 @@ var CLI = class {
1984
2034
  const direct = names.map((project) => `test:${project}`);
1985
2035
  const declared = direct.filter((script) => isString(written[script]));
1986
2036
  const undeclared = direct.filter((script) => !isString(written[script]));
1987
- const remedies = [declared.length === 0 ? "" : `${declared.join(", ")} ${declared.length === 1 ? "is" : "are"} already declared, so the gate is missing rather than the script: invoke ${declared.length === 1 ? "it" : "each of them"} by name from the ${gateNames} chain.`, undeclared.length === 0 ? "" : `${undeclared.join(", ")} ${undeclared.length === 1 ? "is" : "are"} not declared, so the script is missing as well as the gate: declare ${undeclared.length === 1 ? "it" : "each of them"} and invoke ${undeclared.length === 1 ? "it" : "each of them"} by name from the ${gateNames} chain.`];
2037
+ const remedies = !writing && manifestToWritableScripts(text, blueprint).some(({ name }) => name === "test") ? ["The test chain runs generated steps only, so repair and overwrite write the planned chain and every missing direct script."] : [declared.length === 0 ? "" : `${declared.join(", ")} ${declared.length === 1 ? "is" : "are"} already declared, so the gate is missing rather than the script: invoke ${declared.length === 1 ? "it" : "each of them"} by name from the ${gateNames} chain.`, undeclared.length === 0 ? "" : `${undeclared.join(", ")} ${undeclared.length === 1 ? "is" : "are"} not declared, so the script is missing as well as the gate: declare ${undeclared.length === 1 ? "it" : "each of them"} and invoke ${undeclared.length === 1 ? "it" : "each of them"} by name from the ${gateNames} chain.`];
1988
2038
  return {
1989
2039
  field: "projects",
1990
2040
  message: `${writing ? "The configs group is blocked because " : ""}the manifest at ${target} does not reach ${names.length === 1 ? "a Vitest project" : "Vitest projects"} the planned configuration registers: ${names.join(", ")}. No chain from ${gateNames} invokes ${names.length === 1 ? "it" : "them"}. ${remedies.filter((remedy) => remedy !== "").join(" ")}${writing ? " Exclude configs from --groups to write another group." : ""}`,