rolldown-pnpm-config 1.0.11 → 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 +2 -0
- package/bin/rolldown-pnpm-config.js +2 -12
- package/cli/commands/export.js +23 -20
- package/cli/commands/preview.js +23 -20
- package/cli/commands/upgrade.js +273 -181
- package/cli/cwd.js +25 -0
- package/cli/diff/render.js +19 -37
- package/cli/load-config.js +4 -5
- package/cli/main.js +37 -0
- package/cli/preview-views.js +2 -2
- package/cli/render/legend.js +42 -0
- package/cli/render/print.js +50 -0
- package/cli/render/report.js +38 -0
- package/cli/render/tone.js +43 -0
- package/cli/root.js +22 -0
- package/cli/simulated-view.js +12 -53
- package/cli/summary.js +19 -96
- package/cli/ui/preview-screen.js +93 -0
- package/cli/ui/progress-view.js +47 -0
- package/cli/ui/progress.js +31 -0
- package/cli/ui/screens.js +40 -0
- package/cli/ui/walk-screen.js +176 -0
- package/cli/version.js +12 -0
- package/cli/walk-reducer.js +13 -24
- package/package.json +6 -4
- package/cli/ui/Preview.js +0 -65
- package/cli/ui/Walk.js +0 -89
- package/cli/ui/ansi.js +0 -20
- package/cli/ui/env.js +0 -18
- package/cli/ui/legend.js +0 -80
- package/cli/ui/run-preview.js +0 -22
- package/cli/ui/run-walk.js +0 -33
- package/cli/ui/styled.js +0 -36
package/cli/cwd.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { findWorkspaceFile } from "./workspace-file.js";
|
|
2
|
+
import { Context, Effect } from "effect";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
//#region src/cli/cwd.ts
|
|
6
|
+
/**
|
|
7
|
+
* The directory the CLI was invoked from. `main.ts` reads `process.cwd()` once
|
|
8
|
+
* and provides it here, so no command reads `process` itself.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
var WorkingDirectory = class extends Context.Service()("rolldown-pnpm-config/WorkingDirectory") {};
|
|
13
|
+
/**
|
|
14
|
+
* The `pnpm-workspace.yaml` a command targets: the explicit path when given,
|
|
15
|
+
* else the nearest one upward from the working directory, else the working
|
|
16
|
+
* directory's own (to be created).
|
|
17
|
+
*
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
function resolveWorkspacePath(explicit) {
|
|
21
|
+
return Effect.map(WorkingDirectory, (cwd) => explicit ?? findWorkspaceFile(cwd) ?? join(cwd, "pnpm-workspace.yaml"));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
export { WorkingDirectory, resolveWorkspacePath };
|
package/cli/diff/render.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { row, tone } from "../render/tone.js";
|
|
2
|
+
import { Doc } from "@effected/cli";
|
|
3
|
+
|
|
1
4
|
//#region src/cli/diff/render.ts
|
|
2
5
|
const CONTEXT = 2;
|
|
3
6
|
const GUTTER = {
|
|
@@ -6,7 +9,7 @@ const GUTTER = {
|
|
|
6
9
|
changed: "~",
|
|
7
10
|
unchanged: " "
|
|
8
11
|
};
|
|
9
|
-
const
|
|
12
|
+
const TONE = {
|
|
10
13
|
added: "added",
|
|
11
14
|
removed: "removed",
|
|
12
15
|
changed: "changed",
|
|
@@ -22,18 +25,11 @@ function flatten(node, depth, inheritedUnmanaged = false) {
|
|
|
22
25
|
const tag = node.tag;
|
|
23
26
|
const changed = node.kind !== "unchanged";
|
|
24
27
|
const unmanaged = inheritedUnmanaged || node.tag === "unmanaged" && node.kind === "unchanged";
|
|
25
|
-
const
|
|
28
|
+
const t = node.kind === "unchanged" && unmanaged ? "unmanaged" : TONE[node.kind];
|
|
26
29
|
const isArrayEl = node.arrayElement === true;
|
|
27
30
|
if (node.children) return [{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
gutter,
|
|
31
|
-
segments: [{
|
|
32
|
-
text: `${node.key}:`,
|
|
33
|
-
style
|
|
34
|
-
}],
|
|
35
|
-
...tag ? { tag } : {}
|
|
36
|
-
},
|
|
31
|
+
row: row(gutter, indent, [tone(`${node.key}:`, t)], tag),
|
|
32
|
+
indent,
|
|
37
33
|
changed
|
|
38
34
|
}, ...node.children.flatMap((c) => flatten(c, depth + 1, unmanaged))];
|
|
39
35
|
let text;
|
|
@@ -41,28 +37,21 @@ function flatten(node, depth, inheritedUnmanaged = false) {
|
|
|
41
37
|
else if (isArrayEl) text = `- ${node.key}`;
|
|
42
38
|
else text = `${node.key}: ${scalarText(node.after ?? node.before)}`;
|
|
43
39
|
return [{
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
gutter,
|
|
47
|
-
segments: [{
|
|
48
|
-
text,
|
|
49
|
-
style
|
|
50
|
-
}],
|
|
51
|
-
...tag ? { tag } : {}
|
|
52
|
-
},
|
|
40
|
+
row: row(gutter, indent, [tone(text, t)], tag),
|
|
41
|
+
indent,
|
|
53
42
|
changed
|
|
54
43
|
}];
|
|
55
44
|
}
|
|
56
45
|
/**
|
|
57
|
-
* Render a diff tree to
|
|
58
|
-
* unchanged lines outside a 2-line window around changes into a
|
|
59
|
-
* "… N unchanged" marker; `full` keeps every line.
|
|
46
|
+
* Render a diff tree to a `Lines` block in canonical-YAML shape. Default
|
|
47
|
+
* collapses unchanged lines outside a 2-line window around changes into a
|
|
48
|
+
* single "… N unchanged" marker; `full` keeps every line.
|
|
60
49
|
*
|
|
61
50
|
* @internal
|
|
62
51
|
*/
|
|
63
52
|
function renderExportDiff(root, opts) {
|
|
64
53
|
const flats = (root.children ?? []).flatMap((c) => flatten(c, 0));
|
|
65
|
-
if (opts.full) return flats.map((f) => f.
|
|
54
|
+
if (opts.full) return Doc.lines(flats.map((f) => f.row));
|
|
66
55
|
const keep = new Array(flats.length).fill(false);
|
|
67
56
|
flats.forEach((f, i) => {
|
|
68
57
|
if (!f.changed) return;
|
|
@@ -70,35 +59,28 @@ function renderExportDiff(root, opts) {
|
|
|
70
59
|
});
|
|
71
60
|
for (let i = 0; i < flats.length; i++) {
|
|
72
61
|
if (!keep[i]) continue;
|
|
73
|
-
let depth = flats[i].
|
|
74
|
-
for (let j = i - 1; j >= 0 && depth > 0; j--) if (flats[j].
|
|
62
|
+
let depth = flats[i].indent;
|
|
63
|
+
for (let j = i - 1; j >= 0 && depth > 0; j--) if (flats[j].indent < depth) {
|
|
75
64
|
keep[j] = true;
|
|
76
|
-
depth = flats[j].
|
|
65
|
+
depth = flats[j].indent;
|
|
77
66
|
}
|
|
78
67
|
}
|
|
79
68
|
const out = [];
|
|
80
69
|
let dropped = 0;
|
|
81
70
|
const flushDropped = () => {
|
|
82
71
|
if (dropped > 0) {
|
|
83
|
-
out.push({
|
|
84
|
-
indent: 0,
|
|
85
|
-
gutter: " ",
|
|
86
|
-
segments: [{
|
|
87
|
-
text: `… ${dropped} unchanged`,
|
|
88
|
-
style: "unchanged"
|
|
89
|
-
}]
|
|
90
|
-
});
|
|
72
|
+
out.push(row(" ", 0, [tone(`… ${dropped} unchanged`, "unchanged")]));
|
|
91
73
|
dropped = 0;
|
|
92
74
|
}
|
|
93
75
|
};
|
|
94
76
|
flats.forEach((f, i) => {
|
|
95
77
|
if (keep[i]) {
|
|
96
78
|
flushDropped();
|
|
97
|
-
out.push(f.
|
|
79
|
+
out.push(f.row);
|
|
98
80
|
} else dropped++;
|
|
99
81
|
});
|
|
100
82
|
flushDropped();
|
|
101
|
-
return out;
|
|
83
|
+
return Doc.lines(out);
|
|
102
84
|
}
|
|
103
85
|
|
|
104
86
|
//#endregion
|
package/cli/load-config.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
+
import { parseWorkspace } from "./workspace-file.js";
|
|
1
2
|
import { evaluatePluginConfig } from "./evaluate.js";
|
|
2
|
-
import { findWorkspaceFile, parseWorkspace } from "./workspace-file.js";
|
|
3
3
|
import { Effect, Predicate } from "effect";
|
|
4
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
-
import { join } from "node:path";
|
|
6
5
|
|
|
7
6
|
//#region src/cli/load-config.ts
|
|
8
7
|
/**
|
|
9
8
|
* The prologue shared by `export` and `preview`: read and statically evaluate
|
|
10
|
-
* the config file (failing on a missing call or non-literal values),
|
|
11
|
-
* the workspace file
|
|
9
|
+
* the config file (failing on a missing call or non-literal values), and
|
|
10
|
+
* parse the workspace file when present. `mkError` wraps each failure
|
|
12
11
|
* message in the command's own error type.
|
|
13
12
|
*
|
|
14
13
|
* @internal
|
|
@@ -22,7 +21,7 @@ function loadConfigAndWorkspace(opts, mkError) {
|
|
|
22
21
|
const { config, errors } = evaluatePluginConfig(configSource, opts.configFile);
|
|
23
22
|
if (config === null) return yield* Effect.fail(mkError(`No PnpmConfigPlugin call found in ${opts.configFile}`));
|
|
24
23
|
if (errors.length > 0) return yield* Effect.fail(mkError(`Non-literal config values: ${errors.join("; ")}`));
|
|
25
|
-
const path = opts.workspacePath
|
|
24
|
+
const path = opts.workspacePath;
|
|
26
25
|
const parsed = existsSync(path) ? yield* Effect.try({
|
|
27
26
|
try: () => parseWorkspace(readFileSync(path, "utf8")),
|
|
28
27
|
catch: (e) => mkError(`Cannot read or parse ${path}: ${String(e)}`)
|
package/cli/main.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { WorkingDirectory } from "./cwd.js";
|
|
2
|
+
import { rootCommand } from "./root.js";
|
|
3
|
+
import { CLI_VERSION } from "./version.js";
|
|
4
|
+
import { Effect } from "effect";
|
|
5
|
+
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
6
|
+
import { CliAudience, CliRuntime } from "@effected/cli";
|
|
7
|
+
|
|
8
|
+
//#region src/cli/main.ts
|
|
9
|
+
/**
|
|
10
|
+
* Assemble and run the CLI. One of the three files allowed to read `process`:
|
|
11
|
+
* the working directory, argv for the build-time log format, and stderr's
|
|
12
|
+
* terminal state are read here and passed down as plain values.
|
|
13
|
+
*
|
|
14
|
+
* `CliRuntime.main` builds the audience, terminal and theme from `env`,
|
|
15
|
+
* decides whether the run may prompt, reports every failure on stderr for the
|
|
16
|
+
* run's audience, and maps the exit code (usage 64, cancel 130, otherwise 1).
|
|
17
|
+
*
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
function main() {
|
|
21
|
+
const program = CliAudience.run(rootCommand, { version: CLI_VERSION }).pipe(Effect.provideService(WorkingDirectory, process.cwd()));
|
|
22
|
+
NodeRuntime.runMain(CliRuntime.main(program, {
|
|
23
|
+
platform: NodeServices.layer,
|
|
24
|
+
helpOnUsageError: "stderr",
|
|
25
|
+
env: {
|
|
26
|
+
audienceEnvVar: "ROLLDOWN_PNPM_CONFIG_AUDIENCE",
|
|
27
|
+
log: {
|
|
28
|
+
envVar: "ROLLDOWN_PNPM_CONFIG_LOG_LEVEL",
|
|
29
|
+
argv: process.argv.slice(2)
|
|
30
|
+
},
|
|
31
|
+
stderrIsTerminal: Effect.sync(() => process.stderr.isTTY === true)
|
|
32
|
+
}
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//#endregion
|
|
37
|
+
export { main };
|
package/cli/preview-views.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
+
import { canonicalize } from "./workspace-file.js";
|
|
1
2
|
import { buildDiff } from "./diff/build.js";
|
|
2
3
|
import { renderExportDiff } from "./diff/render.js";
|
|
3
4
|
import { effectiveManaged, vanillaManaged } from "./effective.js";
|
|
4
|
-
import { canonicalize } from "./workspace-file.js";
|
|
5
5
|
import { overlayWorkspace } from "./workspace-overlay.js";
|
|
6
6
|
import { WORKSPACE_FIELDS } from "./commands/export.js";
|
|
7
7
|
import { renderSimulated } from "./simulated-view.js";
|
|
8
8
|
|
|
9
9
|
//#region src/cli/preview-views.ts
|
|
10
10
|
/**
|
|
11
|
-
* Build the three preview views as
|
|
11
|
+
* Build the three preview views as `Doc` blocks: Changes (parsed→merged diff,
|
|
12
12
|
* with local + preserve + excludeByRepo), Full (same tree, full verbosity), and
|
|
13
13
|
* Simulated (the fresh-consumer output rendered as a plain calculated file — not
|
|
14
14
|
* a diff — with per-field merge/overwrite + enforcement annotations).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { tone } from "./tone.js";
|
|
2
|
+
import { Doc } from "@effected/cli";
|
|
3
|
+
|
|
4
|
+
//#region src/cli/render/legend.ts
|
|
5
|
+
function swatches(entries) {
|
|
6
|
+
return Doc.line([Doc.text(" Legend: "), ...entries.map(([label, t], i) => tone(`■ ${label}${i < entries.length - 1 ? " " : ""}`, t))]);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The colour legend for the config diff (the `preview` command and
|
|
10
|
+
* `export --dry-run`). Each swatch carries the matching tone, so the legend
|
|
11
|
+
* tracks the palette. Printed only when the output is coloured — without
|
|
12
|
+
* colour the swatches are indistinguishable and the legend is noise.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
function legend() {
|
|
17
|
+
return swatches([
|
|
18
|
+
["added", "added"],
|
|
19
|
+
["removed", "removed"],
|
|
20
|
+
["modified", "changed"],
|
|
21
|
+
["unchanged", "unchanged"],
|
|
22
|
+
["unmanaged", "unmanaged"]
|
|
23
|
+
]);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The legend for the Simulated view. That view is not a diff — nothing is
|
|
27
|
+
* added or removed — so it has its own vocabulary: how each field is combined
|
|
28
|
+
* (`merge`/`overwrite`) and how divergence is enforced (`warn`/`error`).
|
|
29
|
+
*
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
function simulatedLegend() {
|
|
33
|
+
return swatches([
|
|
34
|
+
["merge", "merge"],
|
|
35
|
+
["overwrite", "overwrite"],
|
|
36
|
+
["warn", "changed"],
|
|
37
|
+
["error", "warn"]
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
export { legend, simulatedLegend };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { CliTheme, Doc } from "@effected/cli";
|
|
3
|
+
import { Audience } from "@effected/env";
|
|
4
|
+
|
|
5
|
+
//#region src/cli/render/print.ts
|
|
6
|
+
/**
|
|
7
|
+
* Whether stdout is painted for a person: a human audience at a colour level
|
|
8
|
+
* above `none`. A legend is only worth printing then — without colour its
|
|
9
|
+
* swatches are indistinguishable.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
const coloured = Effect.gen(function* () {
|
|
14
|
+
const theme = yield* CliTheme;
|
|
15
|
+
return (yield* Audience).kind === "human" && theme.color !== "none";
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* Print a document with no width limit, for every audience. Diffs, summaries
|
|
19
|
+
* and package lists are line-oriented data: a hard wrap at a person's width (80
|
|
20
|
+
* when output is piped) would split a row away from its gutter and break a
|
|
21
|
+
* grep. A terminal still soft-wraps a long line when it shows it.
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
function printDoc(doc, options = {}) {
|
|
26
|
+
return Doc.print(doc, {
|
|
27
|
+
...options,
|
|
28
|
+
width: Number.POSITIVE_INFINITY
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Print a diff-shaped view to stdout: an optional heading line, the legend
|
|
33
|
+
* when the output is coloured, then the view. The audience picks the
|
|
34
|
+
* renderer (ANSI for a person, plain for an agent, the GitHub log under
|
|
35
|
+
* Actions).
|
|
36
|
+
*
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
function printView(view, options = {}) {
|
|
40
|
+
return Effect.gen(function* () {
|
|
41
|
+
const doc = [];
|
|
42
|
+
if (options.heading !== void 0) doc.push(Doc.line(options.heading), Doc.line(""));
|
|
43
|
+
if (options.legend !== void 0 && (yield* coloured)) doc.push(options.legend, Doc.line(""));
|
|
44
|
+
doc.push(view);
|
|
45
|
+
yield* printDoc(doc);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
//#endregion
|
|
50
|
+
export { coloured, printDoc, printView };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Doc, Status } from "@effected/cli";
|
|
2
|
+
|
|
3
|
+
//#region src/cli/render/report.ts
|
|
4
|
+
function statusDoc(status, message) {
|
|
5
|
+
const [first = "", ...rest] = message.split("\n");
|
|
6
|
+
const blocks = [Doc.line([
|
|
7
|
+
Doc.status(Status.core, status),
|
|
8
|
+
" ",
|
|
9
|
+
first
|
|
10
|
+
])];
|
|
11
|
+
if (rest.length > 0) blocks.push(Doc.verbatim(rest.join("\n")));
|
|
12
|
+
return blocks;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The failure report for one of the CLI's own errors: a status line carrying
|
|
16
|
+
* the message's first line, then any further lines as they were written (a
|
|
17
|
+
* list of packages, say). Used by each error's `CliDoc` method, so
|
|
18
|
+
* `CliRuntime.main` draws the message without the error class's name.
|
|
19
|
+
*
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
function failureDoc(message) {
|
|
23
|
+
return statusDoc("failure", message);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A multi-line warning drawn like a failure report but with the warning mark:
|
|
27
|
+
* the first line beside the glyph, the rest as written. For a warning longer
|
|
28
|
+
* than the one line `CliMessage.warning` takes (a list of packages, say);
|
|
29
|
+
* printed to stderr.
|
|
30
|
+
*
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
function warningDoc(message) {
|
|
34
|
+
return statusDoc("warning", message);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
export { failureDoc, warningDoc };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Doc, Token } from "@effected/cli";
|
|
2
|
+
|
|
3
|
+
//#region src/cli/render/tone.ts
|
|
4
|
+
/**
|
|
5
|
+
* The palette: one theme token (or explicit style) per tone. Semantic tones
|
|
6
|
+
* use the theme's tokens so a theme override restyles them; `unmanaged` is a
|
|
7
|
+
* fixed dark gray (distinct from the dim `unchanged`) and `local`/`overwrite`
|
|
8
|
+
* are magenta. `plain` is unstyled.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
const TONES = {
|
|
13
|
+
added: "success",
|
|
14
|
+
removed: "failure",
|
|
15
|
+
changed: "warning",
|
|
16
|
+
unchanged: "muted",
|
|
17
|
+
warn: "failure",
|
|
18
|
+
local: Token.named("magenta"),
|
|
19
|
+
unmanaged: Token.hex("#585858"),
|
|
20
|
+
merge: "accent",
|
|
21
|
+
overwrite: Token.named("magenta")
|
|
22
|
+
};
|
|
23
|
+
/** A run of text in a tone. @internal */
|
|
24
|
+
function tone(text, t) {
|
|
25
|
+
return t === "plain" ? Doc.text(text) : Doc.text(text, TONES[t]);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* One diff-shaped row: `<gutter><space><2-space indent><content><tag>`. The
|
|
29
|
+
* gutter and the tag are plain characters, so a row keeps its meaning when the
|
|
30
|
+
* renderer drops colour (an agent, a pipe, `NO_COLOR`).
|
|
31
|
+
*
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
function row(gutter, indent, content, tag) {
|
|
35
|
+
return [
|
|
36
|
+
Doc.text(`${gutter} ${" ".repeat(indent)}`),
|
|
37
|
+
...content,
|
|
38
|
+
...tag === void 0 ? [] : [tone(` (${tag})`, tag)]
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
export { TONES, row, tone };
|
package/cli/root.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { exportCommand } from "./commands/export.js";
|
|
2
|
+
import { previewCommand } from "./commands/preview.js";
|
|
3
|
+
import { upgradeCommand } from "./commands/upgrade.js";
|
|
4
|
+
import { CliAudience } from "@effected/cli";
|
|
5
|
+
import { Command } from "effect/cli";
|
|
6
|
+
|
|
7
|
+
//#region src/cli/root.ts
|
|
8
|
+
/**
|
|
9
|
+
* The `rolldown-pnpm-config` command tree, with the audience flags
|
|
10
|
+
* (`--audience`, `--human`, `--agent`, `--ci`) shared from the root so every
|
|
11
|
+
* subcommand accepts them.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
const rootCommand = Command.make("rolldown-pnpm-config").pipe(Command.withSharedFlags(CliAudience.flags()), Command.withSubcommands([
|
|
16
|
+
upgradeCommand,
|
|
17
|
+
exportCommand,
|
|
18
|
+
previewCommand
|
|
19
|
+
]));
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { rootCommand };
|
package/cli/simulated-view.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { scalarText } from "./diff/render.js";
|
|
2
1
|
import { canonicalize } from "./workspace-file.js";
|
|
2
|
+
import { row, tone } from "./render/tone.js";
|
|
3
|
+
import { scalarText } from "./diff/render.js";
|
|
4
|
+
import { Doc } from "@effected/cli";
|
|
3
5
|
|
|
4
6
|
//#region src/cli/simulated-view.ts
|
|
5
7
|
/**
|
|
@@ -22,14 +24,8 @@ const STRATEGY_VERB = {
|
|
|
22
24
|
};
|
|
23
25
|
/** The enforcement suffix, e.g. " · warn" / " · error"; empty when silent. */
|
|
24
26
|
function enforcementSegs(e) {
|
|
25
|
-
if (e === "warn") return [
|
|
26
|
-
|
|
27
|
-
style: "changed"
|
|
28
|
-
}];
|
|
29
|
-
if (e === "error") return [{
|
|
30
|
-
text: " · error",
|
|
31
|
-
style: "warn"
|
|
32
|
-
}];
|
|
27
|
+
if (e === "warn") return [tone(" · warn", "changed")];
|
|
28
|
+
if (e === "error") return [tone(" · error", "warn")];
|
|
33
29
|
return [];
|
|
34
30
|
}
|
|
35
31
|
/** The trailing "(merge)" / "(overwrite · error)" annotation for one field. */
|
|
@@ -37,54 +33,17 @@ function annotation(entry) {
|
|
|
37
33
|
if (!entry) return [];
|
|
38
34
|
const verb = STRATEGY_VERB[entry.strategy] ?? "merge";
|
|
39
35
|
return [
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
style: "unchanged"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
text: verb,
|
|
46
|
-
style: verb
|
|
47
|
-
},
|
|
36
|
+
tone(" (", "unchanged"),
|
|
37
|
+
tone(verb, verb),
|
|
48
38
|
...enforcementSegs(entry.enforcement),
|
|
49
|
-
|
|
50
|
-
text: ")",
|
|
51
|
-
style: "unchanged"
|
|
52
|
-
}
|
|
39
|
+
tone(")", "unchanged")
|
|
53
40
|
];
|
|
54
41
|
}
|
|
55
42
|
/** Flatten one key/value into YAML-shaped plain lines; `ann` annotates the head. */
|
|
56
43
|
function flatten(key, value, depth, ann) {
|
|
57
|
-
if (Array.isArray(value)) return [{
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
segments: [{
|
|
61
|
-
text: `${key}:`,
|
|
62
|
-
style: "plain"
|
|
63
|
-
}, ...ann]
|
|
64
|
-
}, ...value.map((el) => ({
|
|
65
|
-
indent: depth + 1,
|
|
66
|
-
gutter: " ",
|
|
67
|
-
segments: [{
|
|
68
|
-
text: `- ${scalarText(el)}`,
|
|
69
|
-
style: "plain"
|
|
70
|
-
}]
|
|
71
|
-
}))];
|
|
72
|
-
if (value !== null && typeof value === "object") return [{
|
|
73
|
-
indent: depth,
|
|
74
|
-
gutter: " ",
|
|
75
|
-
segments: [{
|
|
76
|
-
text: `${key}:`,
|
|
77
|
-
style: "plain"
|
|
78
|
-
}, ...ann]
|
|
79
|
-
}, ...Object.entries(value).flatMap(([k, v]) => flatten(k, v, depth + 1, []))];
|
|
80
|
-
return [{
|
|
81
|
-
indent: depth,
|
|
82
|
-
gutter: " ",
|
|
83
|
-
segments: [{
|
|
84
|
-
text: `${key}: ${scalarText(value)}`,
|
|
85
|
-
style: "plain"
|
|
86
|
-
}, ...ann]
|
|
87
|
-
}];
|
|
44
|
+
if (Array.isArray(value)) return [row(" ", depth, [tone(`${key}:`, "plain"), ...ann]), ...value.map((el) => row(" ", depth + 1, [tone(`- ${scalarText(el)}`, "plain")]))];
|
|
45
|
+
if (value !== null && typeof value === "object") return [row(" ", depth, [tone(`${key}:`, "plain"), ...ann]), ...Object.entries(value).flatMap(([k, v]) => flatten(k, v, depth + 1, []))];
|
|
46
|
+
return [row(" ", depth, [tone(`${key}: ${scalarText(value)}`, "plain"), ...ann])];
|
|
88
47
|
}
|
|
89
48
|
/**
|
|
90
49
|
* Render the Simulated view: the calculated fresh-consumer config as a plain
|
|
@@ -96,7 +55,7 @@ function flatten(key, value, depth, ann) {
|
|
|
96
55
|
*/
|
|
97
56
|
function renderSimulated(vanilla, manifest) {
|
|
98
57
|
const canon = canonicalize(vanilla);
|
|
99
|
-
return Object.entries(canon).flatMap(([k, v]) => flatten(k, v, 0, annotation(manifest[k])));
|
|
58
|
+
return Doc.lines(Object.entries(canon).flatMap(([k, v]) => flatten(k, v, 0, annotation(manifest[k]))));
|
|
100
59
|
}
|
|
101
60
|
|
|
102
61
|
//#endregion
|
package/cli/summary.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { displayCandidates, peerFor, tableLayout } from "./walk-reducer.js";
|
|
1
|
+
import { row, tone } from "./render/tone.js";
|
|
2
|
+
import { cellTone, displayCandidates, peerFor, tableLayout } from "./walk-reducer.js";
|
|
3
|
+
import { Doc } from "@effected/cli";
|
|
3
4
|
|
|
4
5
|
//#region src/cli/summary.ts
|
|
5
6
|
/**
|
|
6
|
-
* Build the pending-decisions summary as
|
|
7
|
+
* Build the pending-decisions summary as a document: one table row per
|
|
7
8
|
* decision — mirroring the interactive selection table, catalog headers,
|
|
8
9
|
* chosen bubble filled — then a dim tally, interop conflicts, and any
|
|
9
|
-
* rejected edits. Pure;
|
|
10
|
+
* rejected edits. Pure; the renderer the audience picks decides colour.
|
|
10
11
|
*
|
|
11
12
|
* @internal
|
|
12
13
|
*/
|
|
13
|
-
function
|
|
14
|
+
function summaryDoc(decisions, interop, rejected) {
|
|
14
15
|
const lines = [];
|
|
15
16
|
let toUpdate = 0;
|
|
16
17
|
let major = 0;
|
|
@@ -23,49 +24,18 @@ function summaryLines(decisions, interop, rejected) {
|
|
|
23
24
|
const { entry } = item;
|
|
24
25
|
if (entry.catalog !== lastCatalog) {
|
|
25
26
|
lastCatalog = entry.catalog;
|
|
26
|
-
lines.push({
|
|
27
|
-
indent: 0,
|
|
28
|
-
gutter: " ",
|
|
29
|
-
segments: [{
|
|
30
|
-
text: `── catalog: ${entry.catalog} ──`,
|
|
31
|
-
style: "unchanged"
|
|
32
|
-
}]
|
|
33
|
-
});
|
|
27
|
+
lines.push(row(" ", 0, [tone(`── catalog: ${entry.catalog} ──`, "unchanged")]));
|
|
34
28
|
}
|
|
35
29
|
const cells = displayCandidates(item);
|
|
36
|
-
const
|
|
37
|
-
text: entry.pkg.padEnd(pkgWidth + 2),
|
|
38
|
-
style: "plain"
|
|
39
|
-
}];
|
|
30
|
+
const content = [tone(entry.pkg.padEnd(pkgWidth + 2), "plain")];
|
|
40
31
|
for (const c of cells) {
|
|
41
32
|
const selected = c.kind === chosen.kind;
|
|
42
|
-
|
|
43
|
-
segments.push({
|
|
44
|
-
text: cellText(c, selected),
|
|
45
|
-
style
|
|
46
|
-
});
|
|
33
|
+
content.push(tone(cellText(c, selected), cellTone(c, selected) ?? "unchanged"));
|
|
47
34
|
}
|
|
48
|
-
for (let ci = cells.length; ci < maxCells; ci++)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
segments.push({
|
|
53
|
-
text: `│ ${peerFor(item, chosen)}`,
|
|
54
|
-
style: "unchanged"
|
|
55
|
-
});
|
|
56
|
-
lines.push({
|
|
57
|
-
indent: 0,
|
|
58
|
-
gutter: chosen.kind === "keep" ? " " : "~",
|
|
59
|
-
segments
|
|
60
|
-
});
|
|
61
|
-
if (item.peerWarning) lines.push({
|
|
62
|
-
indent: 1,
|
|
63
|
-
gutter: "⚠",
|
|
64
|
-
segments: [{
|
|
65
|
-
text: item.peerWarning.message,
|
|
66
|
-
style: "warn"
|
|
67
|
-
}]
|
|
68
|
-
});
|
|
35
|
+
for (let ci = cells.length; ci < maxCells; ci++) content.push(tone(blankCell, "plain"));
|
|
36
|
+
content.push(tone(`│ ${peerFor(item, chosen)}`, "unchanged"));
|
|
37
|
+
lines.push(row(chosen.kind === "keep" ? " " : "~", 0, content));
|
|
38
|
+
if (item.peerWarning) lines.push(row("⚠", 1, [tone(item.peerWarning.message, "warn")]));
|
|
69
39
|
if (chosen.kind !== "keep") {
|
|
70
40
|
toUpdate++;
|
|
71
41
|
if (chosen.isMajor) major++;
|
|
@@ -74,59 +44,12 @@ function summaryLines(decisions, interop, rejected) {
|
|
|
74
44
|
else if (!entry.peer && item.materializePeer) materialize++;
|
|
75
45
|
else upToDate++;
|
|
76
46
|
}
|
|
77
|
-
lines.push({
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
style: "unchanged"
|
|
83
|
-
}]
|
|
84
|
-
});
|
|
85
|
-
if (interop) for (const c of interop.conflicts) lines.push({
|
|
86
|
-
indent: 0,
|
|
87
|
-
gutter: "⚠",
|
|
88
|
-
segments: [{
|
|
89
|
-
text: `${c.pkg} (kept ${c.ceiling}) blocked by ${c.blockedBy}`,
|
|
90
|
-
style: "warn"
|
|
91
|
-
}]
|
|
92
|
-
});
|
|
93
|
-
if (rejected && rejected.length > 0) {
|
|
94
|
-
lines.push({
|
|
95
|
-
indent: 0,
|
|
96
|
-
gutter: " ",
|
|
97
|
-
segments: [{
|
|
98
|
-
text: "",
|
|
99
|
-
style: "plain"
|
|
100
|
-
}]
|
|
101
|
-
});
|
|
102
|
-
lines.push({
|
|
103
|
-
indent: 0,
|
|
104
|
-
gutter: "⚠",
|
|
105
|
-
segments: [{
|
|
106
|
-
text: "Rejected (no published version satisfies these):",
|
|
107
|
-
style: "warn"
|
|
108
|
-
}]
|
|
109
|
-
});
|
|
110
|
-
for (const r of rejected) lines.push({
|
|
111
|
-
indent: 1,
|
|
112
|
-
gutter: "⚠",
|
|
113
|
-
segments: [{
|
|
114
|
-
text: `${r.pkg} ${r.kind} ${r.value} — ${r.reason}`,
|
|
115
|
-
style: "warn"
|
|
116
|
-
}]
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
return lines;
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Render the summary to a string. Color defaults off so non-TTY/test callers
|
|
123
|
-
* get clean text; the upgrade command passes the detected color flag.
|
|
124
|
-
*
|
|
125
|
-
* @internal
|
|
126
|
-
*/
|
|
127
|
-
function renderSummary(decisions, interop, opts, rejected) {
|
|
128
|
-
return toAnsi(summaryLines(decisions, interop, rejected), { color: opts?.color ?? false });
|
|
47
|
+
lines.push(row(" ", 0, [tone(`${toUpdate} to update · ${major} major · ${resync} resync · ${materialize} new peer · ${upToDate} up to date`, "unchanged")]));
|
|
48
|
+
if (interop) for (const c of interop.conflicts) lines.push(row("⚠", 0, [tone(`${c.pkg} (kept ${c.ceiling}) blocked by ${c.blockedBy}`, "warn")]));
|
|
49
|
+
const blocks = [Doc.lines(lines)];
|
|
50
|
+
if (rejected && rejected.length > 0) blocks.push(Doc.line(""), Doc.lines([row("⚠", 0, [tone("Rejected (no published version satisfies these):", "warn")]), ...rejected.map((r) => row("⚠", 1, [tone(`${r.pkg} ${r.kind} ${r.value} — ${r.reason}`, "warn")]))]));
|
|
51
|
+
return blocks;
|
|
129
52
|
}
|
|
130
53
|
|
|
131
54
|
//#endregion
|
|
132
|
-
export {
|
|
55
|
+
export { summaryDoc };
|