rolldown-pnpm-config 1.0.10 → 2.0.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/README.md CHANGED
@@ -89,6 +89,8 @@ For repos that develop the plugin itself and cannot consume it as a config depen
89
89
 
90
90
  The `rolldown-pnpm-config preview` command opens an interactive tabbed view — Changes, Full and Simulated — without writing anything. In a non-interactive terminal it falls back to printing the Changes diff, so it is safe to run in CI.
91
91
 
92
+ Every command adapts its output to who is reading it: colour and interactive screens for a person, plain escape-free text for a coding agent (`--agent`) or a CI job (`--ci`), detected automatically when no flag is given. Exit codes are scriptable — `0` success, `1` drift or failure, `64` a usage error, `130` a quit from an interactive screen. See [upgrading catalogs](https://github.com/spencerbeggs/rolldown-pnpm-config/blob/main/docs/05-upgrading-catalogs.md#output-audience-and-exit-codes).
93
+
92
94
  The optional `local` field on `PluginConfig` adjusts managed settings for this repo's export only — the built pnpmfile and its runtime behavior are unaffected. A bare value overwrites the managed value; the directive form merges it:
93
95
 
94
96
  ```ts
@@ -1,18 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { exportCommand } from "../cli/commands/export.js";
3
- import { previewCommand } from "../cli/commands/preview.js";
4
- import { upgradeCommand } from "../cli/commands/upgrade.js";
5
- import { Effect } from "effect";
6
- import { NodeRuntime, NodeServices } from "@effect/platform-node";
7
- import { Command } from "effect/cli";
2
+ import { main } from "../cli/main.js";
8
3
 
9
4
  //#region src/cli/bin.ts
10
- const root = Command.make("rolldown-pnpm-config").pipe(Command.withSubcommands([
11
- upgradeCommand,
12
- exportCommand,
13
- previewCommand
14
- ]));
15
- Command.run(root, { version: "1.0.10" }).pipe(Effect.provide(NodeServices.layer), NodeRuntime.runMain);
5
+ main();
16
6
 
17
7
  //#endregion
18
8
  export { };
@@ -2,20 +2,22 @@ import { discoverOwnedPatches, withResolvedBuildPatches } from "../../patches/bu
2
2
  import { DESCRIPTORS } from "../../descriptors/index.js";
3
3
  import { freeze } from "../../plugin/freeze.js";
4
4
  import { resolveRootName } from "../../runtime/ctx.js";
5
+ import { canonicalize, renderWorkspace } from "../workspace-file.js";
6
+ import { WorkingDirectory, resolveWorkspacePath } from "../cwd.js";
5
7
  import { reconcilePatches } from "../../patches/reconcile.js";
6
8
  import { buildDiff } from "../diff/build.js";
7
9
  import { renderExportDiff } from "../diff/render.js";
8
10
  import { effectiveManaged } from "../effective.js";
9
- import { canonicalize, renderWorkspace } from "../workspace-file.js";
10
11
  import { loadConfigAndWorkspace } from "../load-config.js";
12
+ import { legend } from "../render/legend.js";
13
+ import { printView } from "../render/print.js";
14
+ import { failureDoc } from "../render/report.js";
11
15
  import { findConfigFiles, pickConfigCandidate } from "../select-file.js";
12
- import { toAnsi } from "../ui/ansi.js";
13
- import { detectCapabilities } from "../ui/env.js";
14
- import { legendLines } from "../ui/legend.js";
15
16
  import { overlayWorkspace } from "../workspace-overlay.js";
16
17
  import { Data, Effect, Option, Predicate } from "effect";
17
18
  import { existsSync, writeFileSync } from "node:fs";
18
19
  import { dirname, relative } from "node:path";
20
+ import { CliDoc, CliMessage } from "@effected/cli";
19
21
  import { Argument, Command, Flag } from "effect/cli";
20
22
 
21
23
  //#region src/cli/commands/export.ts
@@ -24,7 +26,12 @@ import { Argument, Command, Flag } from "effect/cli";
24
26
  *
25
27
  * @internal
26
28
  */
27
- var ExportError = class extends Data.TaggedError("ExportError") {};
29
+ var ExportError = class extends Data.TaggedError("ExportError") {
30
+ /** The failure report `CliRuntime.main` prints: the message, without the class name. */
31
+ [CliDoc]() {
32
+ return failureDoc(this.message);
33
+ }
34
+ };
28
35
  /**
29
36
  * The set of pnpm config keys that belong in pnpm-workspace.yaml
30
37
  * (i.e. descriptors with `workspaceYaml: true`).
@@ -96,9 +103,9 @@ function runExport(opts) {
96
103
  };
97
104
  });
98
105
  }
99
- const pathArg = Argument.File("path").pipe(Argument.optional);
100
- const dryRunFlag = Flag.Boolean("dry-run").pipe(Flag.withDefault(false));
101
- const fullFlag = Flag.Boolean("full").pipe(Flag.withDefault(false));
106
+ const pathArg = Argument.File("path").pipe(Argument.withDescription("The pnpm-workspace.yaml to write (the nearest one upward when omitted)"), Argument.optional);
107
+ const dryRunFlag = Flag.Boolean("dry-run").pipe(Flag.withDescription("Print the diff instead of writing the file"), Flag.withDefault(false));
108
+ const fullFlag = Flag.Boolean("full").pipe(Flag.withDescription("Show every line of the diff, not just the changes and their context"), Flag.withDefault(false));
102
109
  /**
103
110
  * The "export" command. Materializes the plugin config into pnpm-workspace.yaml.
104
111
  * An optional path argument overrides the auto-detected workspace file. --dry-run
@@ -112,26 +119,22 @@ const exportCommand = Command.make("export", {
112
119
  dryRun: dryRunFlag,
113
120
  full: fullFlag
114
121
  }, ({ path, dryRun, full }) => Effect.gen(function* () {
115
- const matches = yield* findConfigFiles(process.cwd());
122
+ const matches = yield* findConfigFiles(yield* WorkingDirectory);
116
123
  const picked = pickConfigCandidate(matches);
117
124
  if (!picked.ok) return yield* Effect.fail(new ExportError({ message: picked.message }));
118
- const workspacePath = Option.getOrUndefined(path);
119
125
  const result = yield* runExport({
120
126
  configFile: picked.file,
121
- ...workspacePath !== void 0 ? { workspacePath } : {},
127
+ workspacePath: yield* resolveWorkspacePath(Option.getOrUndefined(path)),
122
128
  preview: dryRun,
123
129
  full
124
130
  });
125
- yield* Effect.sync(() => {
126
- if (dryRun) {
127
- const caps = detectCapabilities();
128
- process.stdout.write(`${result.path} (dry run — not written)\n\n`);
129
- const legend = caps.color ? `${toAnsi(legendLines(), { color: caps.color })}\n\n` : "";
130
- process.stdout.write(`${legend}${toAnsi(result.diff, { color: caps.color })}\n`);
131
- } else process.stdout.write(`Exported to ${result.path}\n`);
132
- for (const k of result.report.staleEntries) process.stderr.write(`warning: patch entry "${k}" has no file on disk\n`);
133
- for (const k of result.report.keyMismatches) process.stderr.write(`warning: patch entry "${k}" does not match its filename\n`);
131
+ if (dryRun) yield* printView(result.diff, {
132
+ legend: legend(),
133
+ heading: `${result.path} (dry run — not written)`
134
134
  });
135
+ else yield* CliMessage.success(`Exported to ${result.path}`);
136
+ for (const k of result.report.staleEntries) yield* CliMessage.warning(`patch entry "${k}" has no file on disk`);
137
+ for (const k of result.report.keyMismatches) yield* CliMessage.warning(`patch entry "${k}" does not match its filename`);
135
138
  })).pipe(Command.withDescription("Materialize the plugin config into pnpm-workspace.yaml (--dry-run to preview)"));
136
139
 
137
140
  //#endregion
@@ -1,22 +1,31 @@
1
1
  import { freeze } from "../../plugin/freeze.js";
2
2
  import { resolveRootName } from "../../runtime/ctx.js";
3
+ import { WorkingDirectory, resolveWorkspacePath } from "../cwd.js";
3
4
  import { loadConfigAndWorkspace } from "../load-config.js";
5
+ import { legend } from "../render/legend.js";
6
+ import { printView } from "../render/print.js";
7
+ import { failureDoc } from "../render/report.js";
4
8
  import { findConfigFiles, pickConfigCandidate } from "../select-file.js";
5
- import { toAnsi } from "../ui/ansi.js";
6
- import { detectCapabilities } from "../ui/env.js";
7
- import { legendLines } from "../ui/legend.js";
8
9
  import { WORKSPACE_FIELDS } from "./export.js";
9
10
  import { buildPreviewViews } from "../preview-views.js";
11
+ import { previewScreen } from "../ui/screens.js";
10
12
  import { Data, Effect, Option } from "effect";
11
13
  import { dirname } from "node:path";
14
+ import { CliDoc, CliInteractive } from "@effected/cli";
12
15
  import { Argument, Command } from "effect/cli";
16
+ import { CliUi } from "@effected/cli/ui";
13
17
 
14
18
  //#region src/cli/commands/preview.ts
15
19
  /** Typed failure for the preview run. @internal */
16
- var PreviewError = class extends Data.TaggedError("PreviewError") {};
20
+ var PreviewError = class extends Data.TaggedError("PreviewError") {
21
+ /** The failure report `CliRuntime.main` prints: the message, without the class name. */
22
+ [CliDoc]() {
23
+ return failureDoc(this.message);
24
+ }
25
+ };
17
26
  /**
18
27
  * Build the three preview views from a config + workspace file. Pure of any
19
- * terminal interaction; the command wraps this with interactive/non-TTY output.
28
+ * terminal interaction; the command wraps this with interactive/non-interactive output.
20
29
  *
21
30
  * @internal
22
31
  */
@@ -35,31 +44,25 @@ function runPreviewViews(opts) {
35
44
  });
36
45
  });
37
46
  }
38
- const pathArg = Argument.File("path").pipe(Argument.optional);
47
+ const pathArg = Argument.File("path").pipe(Argument.withDescription("The pnpm-workspace.yaml to preview against (the nearest one upward when omitted)"), Argument.optional);
39
48
  /**
40
- * The "preview" command: interactive ink-tab explorer of the export diff
41
- * (Changes / Full / Simulated). Falls back to printing the Changes view when
42
- * the terminal is non-interactive.
49
+ * The "preview" command: an interactive tabbed explorer of the export diff
50
+ * (Changes / Full / Simulated). When the run cannot prompt — a pipe, CI, an
51
+ * agent — it prints the Changes view instead. The explorer is read-only, so
52
+ * closing it any way (q, Enter, Esc, Ctrl-C) is a normal exit.
43
53
  *
44
54
  * @internal
45
55
  */
46
56
  const previewCommand = Command.make("preview", { path: pathArg }, ({ path }) => Effect.gen(function* () {
47
- const matches = yield* findConfigFiles(process.cwd());
57
+ const matches = yield* findConfigFiles(yield* WorkingDirectory);
48
58
  const picked = pickConfigCandidate(matches);
49
59
  if (!picked.ok) return yield* Effect.fail(new PreviewError({ message: picked.message }));
50
- const workspacePath = Option.getOrUndefined(path);
51
60
  const views = yield* runPreviewViews({
52
61
  configFile: picked.file,
53
- ...workspacePath !== void 0 ? { workspacePath } : {}
54
- });
55
- const caps = detectCapabilities();
56
- if (caps.interactive) {
57
- const { runPreview } = yield* Effect.promise(() => import("../ui/run-preview.js"));
58
- yield* runPreview(views);
59
- } else yield* Effect.sync(() => {
60
- const legend = caps.color ? `${toAnsi(legendLines(), { color: caps.color })}\n\n` : "";
61
- process.stdout.write(`${legend}${toAnsi(views.changes, { color: caps.color })}\n`);
62
+ workspacePath: yield* resolveWorkspacePath(Option.getOrUndefined(path))
62
63
  });
64
+ if (!(yield* CliInteractive)) return yield* printView(views.changes, { legend: legend() });
65
+ yield* CliUi.run(previewScreen(views)).pipe(Effect.catchTag("Cancelled", () => Effect.void));
63
66
  })).pipe(Command.withDescription("Interactively preview how pnpm-workspace.yaml would change"));
64
67
 
65
68
  //#endregion