rolldown-pnpm-config 0.8.1 → 1.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/bin/rolldown-pnpm-config.js +1 -1
- package/cli/ast.js +54 -0
- package/cli/commands/export.js +14 -33
- package/cli/commands/preview.js +7 -20
- package/cli/commands/upgrade.js +72 -142
- package/cli/diff/build.js +3 -3
- package/cli/diff/render.js +2 -1
- package/cli/discover.js +5 -38
- package/cli/edits.js +40 -28
- package/cli/evaluate.js +2 -24
- package/cli/interop-live.js +5 -13
- package/cli/interop.js +55 -65
- package/cli/load-config.js +40 -0
- package/cli/local-merge.js +7 -8
- package/cli/plan.js +37 -51
- package/cli/release-age.js +3 -4
- package/cli/resolve.js +40 -63
- package/cli/simulated-view.js +1 -3
- package/cli/summary.js +13 -45
- package/cli/ui/Walk.js +3 -12
- package/cli/ui/env.js +3 -5
- package/cli/ui/run-preview.js +6 -7
- package/cli/ui/run-walk.js +7 -4
- package/cli/validate.js +12 -16
- package/cli/walk-plan.js +2 -2
- package/cli/walk-reducer.js +29 -1
- package/cli/workspace-file.js +3 -3
- package/cli/workspace-resolve.js +9 -17
- package/package.json +2 -3
- package/patches/build.js +24 -10
- package/plugin/allowed-versions.js +2 -2
- package/plugin/freeze.js +13 -6
- package/runtime/strategies/arrays.js +2 -1
- package/runtime/strategies/catalogs.js +5 -14
- package/runtime/strategies/overrides.js +11 -4
- package/runtime/strategies/scalar.js +4 -4
- package/runtime/strategies/table.js +1 -1
- package/semver-util.js +29 -0
package/cli/resolve.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Context, Data, Effect, Layer } from "effect";
|
|
1
|
+
import { Context, Data, Effect, Layer, Predicate } from "effect";
|
|
2
2
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
3
3
|
|
|
4
4
|
//#region src/cli/resolve.ts
|
|
@@ -17,6 +17,18 @@ var ResolveError = class extends Data.TaggedError("ResolveError") {};
|
|
|
17
17
|
* @internal
|
|
18
18
|
*/
|
|
19
19
|
var RegistryResolver = class extends Context.Service()("RegistryResolver") {};
|
|
20
|
+
/**
|
|
21
|
+
* Drop pnpm's own notice lines from captured stdout. pnpm prints e.g.
|
|
22
|
+
* `[WARN] This project is configured to use 12.5.0… Your current pnpm is v12.4.2`
|
|
23
|
+
* (a `packageManager` mismatch) to STDOUT ahead of the `--json` payload, which
|
|
24
|
+
* would otherwise make every `pnpm view` unparseable and report each real
|
|
25
|
+
* package as an unresolvable typo.
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
function stripPnpmNotices(stdout) {
|
|
30
|
+
return stdout.replace(/^[ \t]*(?:\[WARN\]|\[ERR\]|WARN\b|ERR_PNPM_\w+)[^\n]*\n?/gm, "");
|
|
31
|
+
}
|
|
20
32
|
/** Parse `pnpm view ... versions --json` stdout: a JSON array, or a single JSON string. */
|
|
21
33
|
function parseVersions(pkg, stdout) {
|
|
22
34
|
return Effect.try({
|
|
@@ -32,43 +44,31 @@ function parseVersions(pkg, stdout) {
|
|
|
32
44
|
})
|
|
33
45
|
});
|
|
34
46
|
}
|
|
35
|
-
/** Parse `pnpm view
|
|
36
|
-
function
|
|
47
|
+
/** Parse a `pnpm view ... --json` object of string values into a string record. */
|
|
48
|
+
function parseStringRecord(pkg, stdout, what) {
|
|
37
49
|
return Effect.try({
|
|
38
50
|
try: () => {
|
|
39
51
|
const json = JSON.parse(stdout);
|
|
40
|
-
if (json
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
throw new Error("unexpected shape");
|
|
52
|
+
if (!Predicate.isObject(json)) throw new Error("unexpected shape");
|
|
53
|
+
const out = {};
|
|
54
|
+
for (const [k, v] of Object.entries(json)) out[k] = String(v);
|
|
55
|
+
return out;
|
|
46
56
|
},
|
|
47
57
|
catch: () => new ResolveError({
|
|
48
58
|
pkg,
|
|
49
|
-
message: `Could not parse
|
|
59
|
+
message: `Could not parse ${what} for ${pkg}`
|
|
50
60
|
})
|
|
51
61
|
});
|
|
52
62
|
}
|
|
63
|
+
/** Parse `pnpm view <pkg> time --json` stdout: an object of version → ISO date. @internal */
|
|
64
|
+
function parseTimes(pkg, stdout) {
|
|
65
|
+
return parseStringRecord(pkg, stdout, "times");
|
|
66
|
+
}
|
|
53
67
|
/** Parse `pnpm view <pkg>@<version> peerDependencies --json`; empty stdout → {}. @internal */
|
|
54
68
|
function parsePeerDeps(pkg, stdout) {
|
|
55
69
|
const trimmed = stdout.trim();
|
|
56
70
|
if (trimmed === "") return Effect.succeed({});
|
|
57
|
-
return
|
|
58
|
-
try: () => {
|
|
59
|
-
const json = JSON.parse(trimmed);
|
|
60
|
-
if (json && typeof json === "object" && !Array.isArray(json)) {
|
|
61
|
-
const out = {};
|
|
62
|
-
for (const [k, v] of Object.entries(json)) out[k] = String(v);
|
|
63
|
-
return out;
|
|
64
|
-
}
|
|
65
|
-
throw new Error("unexpected shape");
|
|
66
|
-
},
|
|
67
|
-
catch: () => new ResolveError({
|
|
68
|
-
pkg,
|
|
69
|
-
message: `Could not parse peerDependencies for ${pkg}`
|
|
70
|
-
})
|
|
71
|
-
});
|
|
71
|
+
return parseStringRecord(pkg, trimmed, "peerDependencies");
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* Live RegistryResolver backed by `pnpm view <pkg> versions --json`.
|
|
@@ -77,53 +77,30 @@ function parsePeerDeps(pkg, stdout) {
|
|
|
77
77
|
*/
|
|
78
78
|
const RegistryResolverLive = Layer.effect(RegistryResolver, Effect.gen(function* () {
|
|
79
79
|
const spawner = yield* Spawner;
|
|
80
|
+
/** `pnpm view <spec> <field> --json` stdout, with a spawn failure typed against `pkg`. */
|
|
81
|
+
const pnpmView = (pkg, spec, field) => spawner.string(ChildProcess.make("pnpm", [
|
|
82
|
+
"view",
|
|
83
|
+
spec,
|
|
84
|
+
field,
|
|
85
|
+
"--json"
|
|
86
|
+
])).pipe(Effect.map(stripPnpmNotices), Effect.mapError((e) => new ResolveError({
|
|
87
|
+
pkg,
|
|
88
|
+
message: String(e)
|
|
89
|
+
})));
|
|
80
90
|
return {
|
|
81
|
-
versions: (pkg) => Effect.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
pkg,
|
|
85
|
-
"versions",
|
|
86
|
-
"--json"
|
|
87
|
-
]);
|
|
88
|
-
return yield* parseVersions(pkg, yield* spawner.string(cmd).pipe(Effect.mapError((e) => new ResolveError({
|
|
89
|
-
pkg,
|
|
90
|
-
message: String(e)
|
|
91
|
-
}))));
|
|
92
|
-
}),
|
|
93
|
-
times: (pkg) => Effect.gen(function* () {
|
|
94
|
-
const cmd = ChildProcess.make("pnpm", [
|
|
95
|
-
"view",
|
|
96
|
-
pkg,
|
|
97
|
-
"time",
|
|
98
|
-
"--json"
|
|
99
|
-
]);
|
|
100
|
-
return yield* parseTimes(pkg, yield* spawner.string(cmd).pipe(Effect.mapError((e) => new ResolveError({
|
|
101
|
-
pkg,
|
|
102
|
-
message: String(e)
|
|
103
|
-
}))));
|
|
104
|
-
}),
|
|
105
|
-
peerDependencies: (pkg, version) => Effect.gen(function* () {
|
|
106
|
-
const cmd = ChildProcess.make("pnpm", [
|
|
107
|
-
"view",
|
|
108
|
-
`${pkg}@${version}`,
|
|
109
|
-
"peerDependencies",
|
|
110
|
-
"--json"
|
|
111
|
-
]);
|
|
112
|
-
return yield* parsePeerDeps(pkg, yield* spawner.string(cmd).pipe(Effect.mapError((e) => new ResolveError({
|
|
113
|
-
pkg,
|
|
114
|
-
message: String(e)
|
|
115
|
-
}))));
|
|
116
|
-
}),
|
|
91
|
+
versions: (pkg) => pnpmView(pkg, pkg, "versions").pipe(Effect.flatMap((out) => parseVersions(pkg, out))),
|
|
92
|
+
times: (pkg) => pnpmView(pkg, pkg, "time").pipe(Effect.flatMap((out) => parseTimes(pkg, out))),
|
|
93
|
+
peerDependencies: (pkg, version) => pnpmView(pkg, `${pkg}@${version}`, "peerDependencies").pipe(Effect.flatMap((out) => parsePeerDeps(pkg, out))),
|
|
117
94
|
pnpmConfig: (key) => Effect.gen(function* () {
|
|
118
95
|
const cmd = ChildProcess.make("pnpm", [
|
|
119
96
|
"config",
|
|
120
97
|
"get",
|
|
121
98
|
key
|
|
122
99
|
]);
|
|
123
|
-
return yield* spawner.string(cmd).pipe(Effect.map((s) => s.trim()), Effect.
|
|
100
|
+
return yield* spawner.string(cmd).pipe(Effect.map((s) => stripPnpmNotices(s).trim()), Effect.orElseSucceed(() => null));
|
|
124
101
|
})
|
|
125
102
|
};
|
|
126
103
|
}));
|
|
127
104
|
|
|
128
105
|
//#endregion
|
|
129
|
-
export { RegistryResolver, RegistryResolverLive, ResolveError, parsePeerDeps, parseTimes, parseVersions };
|
|
106
|
+
export { RegistryResolver, RegistryResolverLive, ResolveError, parsePeerDeps, parseTimes, parseVersions, stripPnpmNotices };
|
package/cli/simulated-view.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { scalarText } from "./diff/render.js";
|
|
1
2
|
import { canonicalize } from "./workspace-file.js";
|
|
2
3
|
|
|
3
4
|
//#region src/cli/simulated-view.ts
|
|
@@ -51,9 +52,6 @@ function annotation(entry) {
|
|
|
51
52
|
}
|
|
52
53
|
];
|
|
53
54
|
}
|
|
54
|
-
function scalarText(v) {
|
|
55
|
-
return typeof v === "string" ? v : JSON.stringify(v);
|
|
56
|
-
}
|
|
57
55
|
/** Flatten one key/value into YAML-shaped plain lines; `ann` annotates the head. */
|
|
58
56
|
function flatten(key, value, depth, ann) {
|
|
59
57
|
if (Array.isArray(value)) return [{
|
package/cli/summary.js
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
import { toAnsi } from "./ui/ansi.js";
|
|
2
|
-
import { displayCandidates, peerFor } from "./walk-reducer.js";
|
|
2
|
+
import { displayCandidates, peerFor, tableLayout } from "./walk-reducer.js";
|
|
3
3
|
|
|
4
4
|
//#region src/cli/summary.ts
|
|
5
|
-
/** Trailing annotation appended to a major candidate's cell, e.g. " ⚠ major". */
|
|
6
|
-
const MAJOR_SUFFIX = " ⚠ major";
|
|
7
|
-
/** Filled / hollow radio glyphs. MUST match `ui/Walk.ts` — the summary mirrors the table. */
|
|
8
|
-
const SELECTED = "●";
|
|
9
|
-
const UNSELECTED = "○";
|
|
10
|
-
/** "● " / "○ " glyph-plus-space prefix width, common to every cell. */
|
|
11
|
-
const BUBBLE_WIDTH = 2;
|
|
12
5
|
/**
|
|
13
6
|
* Build the pending-decisions summary as styled lines: one table row per
|
|
14
7
|
* decision — mirroring the interactive selection table, catalog headers,
|
|
15
|
-
* chosen bubble filled — then a dim tally, interop
|
|
16
|
-
*
|
|
8
|
+
* chosen bubble filled — then a dim tally, interop conflicts, and any
|
|
9
|
+
* rejected edits. Pure; color is applied by `renderSummary`/`toAnsi`.
|
|
17
10
|
*
|
|
18
11
|
* @internal
|
|
19
12
|
*/
|
|
@@ -24,10 +17,7 @@ function summaryLines(decisions, interop, rejected) {
|
|
|
24
17
|
let resync = 0;
|
|
25
18
|
let materialize = 0;
|
|
26
19
|
let upToDate = 0;
|
|
27
|
-
const pkgWidth
|
|
28
|
-
const cellWidth = decisions.length ? Math.max(...decisions.flatMap((d) => displayCandidates(d.item).map((c) => c.range.length + (c.isMajor ? 8 : 0)))) : 0;
|
|
29
|
-
const maxCells = decisions.length ? Math.max(...decisions.map((d) => displayCandidates(d.item).length)) : 0;
|
|
30
|
-
const blankCell = `${" ".repeat(BUBBLE_WIDTH + cellWidth)} `;
|
|
20
|
+
const { pkgWidth, maxCells, blankCell, cellText } = tableLayout(decisions.map((d) => d.item));
|
|
31
21
|
let lastCatalog = null;
|
|
32
22
|
for (const { item, chosen } of decisions) {
|
|
33
23
|
const { entry } = item;
|
|
@@ -49,11 +39,9 @@ function summaryLines(decisions, interop, rejected) {
|
|
|
49
39
|
}];
|
|
50
40
|
for (const c of cells) {
|
|
51
41
|
const selected = c.kind === chosen.kind;
|
|
52
|
-
const bubble = selected ? SELECTED : UNSELECTED;
|
|
53
42
|
const style = !selected ? "unchanged" : c.kind === "keep" ? "unchanged" : c.isMajor ? "changed" : "added";
|
|
54
|
-
const content = `${c.range}${c.isMajor ? MAJOR_SUFFIX : ""}`.padEnd(cellWidth);
|
|
55
43
|
segments.push({
|
|
56
|
-
text:
|
|
44
|
+
text: cellText(c, selected),
|
|
57
45
|
style
|
|
58
46
|
});
|
|
59
47
|
}
|
|
@@ -94,34 +82,14 @@ function summaryLines(decisions, interop, rejected) {
|
|
|
94
82
|
style: "unchanged"
|
|
95
83
|
}]
|
|
96
84
|
});
|
|
97
|
-
if (interop) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}]
|
|
106
|
-
});
|
|
107
|
-
lines.push({
|
|
108
|
-
indent: 1,
|
|
109
|
-
gutter: "~",
|
|
110
|
-
segments: [{
|
|
111
|
-
text: `↳ peer → ${a.peer}`,
|
|
112
|
-
style: "changed"
|
|
113
|
-
}]
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
for (const c of interop.conflicts) lines.push({
|
|
117
|
-
indent: 0,
|
|
118
|
-
gutter: "⚠",
|
|
119
|
-
segments: [{
|
|
120
|
-
text: `${c.pkg} (kept ${c.ceiling}) blocked by ${c.blockedBy}`,
|
|
121
|
-
style: "warn"
|
|
122
|
-
}]
|
|
123
|
-
});
|
|
124
|
-
}
|
|
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
|
+
});
|
|
125
93
|
if (rejected && rejected.length > 0) {
|
|
126
94
|
lines.push({
|
|
127
95
|
indent: 0,
|
package/cli/ui/Walk.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { computeGroupPeers } from "../interop-live.js";
|
|
2
|
-
import { cellColor, displayCandidates, initTable, peerFor, tableDecisions, tableStep, truncateEnd } from "../walk-reducer.js";
|
|
2
|
+
import { cellColor, displayCandidates, initTable, peerFor, tableDecisions, tableLayout, tableStep, truncateEnd } from "../walk-reducer.js";
|
|
3
3
|
import { Box, Text, useApp, useInput } from "ink";
|
|
4
4
|
import { createElement, useEffect, useState } from "react";
|
|
5
5
|
|
|
@@ -37,13 +37,7 @@ function Walk({ items, onDone, dryRun = false, unresolved = [], interopModels =
|
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
39
|
if (state.done || items.length === 0) return createElement(Text, null, "Done.");
|
|
40
|
-
const pkgWidth =
|
|
41
|
-
const MAJOR_SUFFIX = " ⚠ major";
|
|
42
|
-
const SELECTED = "●";
|
|
43
|
-
const UNSELECTED = "○";
|
|
44
|
-
const cellWidth = Math.max(...items.flatMap((i) => displayCandidates(i).map((c) => c.range.length + (c.isMajor ? 8 : 0))));
|
|
45
|
-
const maxCells = Math.max(...items.map((i) => displayCandidates(i).length));
|
|
46
|
-
const blankCell = `${" ".repeat(2 + cellWidth)} `;
|
|
40
|
+
const { pkgWidth, cellWidth, maxCells, blankCell, cellText } = tableLayout(items);
|
|
47
41
|
const termCols = process.stdout.columns ?? 100;
|
|
48
42
|
const fixedPrefix = 2 + (pkgWidth + 2) + maxCells * (cellWidth + 4) + 2;
|
|
49
43
|
const start = Math.max(0, Math.min(state.cursor - Math.floor(VIEWPORT / 2), items.length - VIEWPORT));
|
|
@@ -74,14 +68,11 @@ function Walk({ items, onDone, dryRun = false, unresolved = [], interopModels =
|
|
|
74
68
|
const candidates = displayCandidates(item);
|
|
75
69
|
const cells = candidates.map((c, ci) => {
|
|
76
70
|
const selected = ci === pick;
|
|
77
|
-
const bubble = selected ? SELECTED : UNSELECTED;
|
|
78
|
-
const major = c.isMajor ? MAJOR_SUFFIX : "";
|
|
79
|
-
const content = `${c.range}${major}`.padEnd(cellWidth);
|
|
80
71
|
const color = cellColor(c, selected);
|
|
81
72
|
return createElement(Text, {
|
|
82
73
|
key: c.kind,
|
|
83
74
|
...color ? { color } : {}
|
|
84
|
-
},
|
|
75
|
+
}, cellText(c, selected));
|
|
85
76
|
});
|
|
86
77
|
for (let ci = candidates.length; ci < maxCells; ci++) cells.push(createElement(Text, { key: `blank-${ci}` }, blankCell));
|
|
87
78
|
const chosen = candidates[pick];
|
package/cli/ui/env.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { hasTTY, isAgent, isCI, isColorSupported } from "std-env";
|
|
2
|
-
import { supportsHyperlinks } from "std-osc8";
|
|
3
2
|
|
|
4
3
|
//#region src/cli/ui/env.ts
|
|
5
4
|
/**
|
|
6
|
-
* Detect color / interactivity
|
|
5
|
+
* Detect color / interactivity once, at the command edge.
|
|
7
6
|
* The render layer consumes the returned flags and never reads the environment.
|
|
8
7
|
*
|
|
9
8
|
* @internal
|
|
@@ -11,10 +10,9 @@ import { supportsHyperlinks } from "std-osc8";
|
|
|
11
10
|
function detectCapabilities() {
|
|
12
11
|
return {
|
|
13
12
|
color: isColorSupported,
|
|
14
|
-
interactive: hasTTY && !isCI && !isAgent
|
|
15
|
-
hyperlinks: supportsHyperlinks
|
|
13
|
+
interactive: hasTTY && !isCI && !isAgent
|
|
16
14
|
};
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
//#endregion
|
|
20
|
-
export { detectCapabilities
|
|
18
|
+
export { detectCapabilities };
|
package/cli/ui/run-preview.js
CHANGED
|
@@ -6,17 +6,16 @@ import { createElement } from "react";
|
|
|
6
6
|
//#region src/cli/ui/run-preview.ts
|
|
7
7
|
/**
|
|
8
8
|
* Render the interactive Preview inside an Effect, resolving once the user
|
|
9
|
-
* exits and Ink has fully torn down.
|
|
9
|
+
* exits and Ink has fully torn down. An Ink crash rejects `waitUntilExit()`,
|
|
10
|
+
* which `Effect.promise` surfaces as a defect rather than a hung fiber.
|
|
10
11
|
*
|
|
11
12
|
* @internal
|
|
12
13
|
*/
|
|
13
14
|
function runPreview(views) {
|
|
14
|
-
return Effect.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
})).waitUntilExit().then(() => resume(Effect.void)).catch((err) => resume(Effect.die(err)));
|
|
19
|
-
});
|
|
15
|
+
return Effect.promise(() => render(createElement(Preview, {
|
|
16
|
+
views,
|
|
17
|
+
onExit: () => {}
|
|
18
|
+
})).waitUntilExit());
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
//#endregion
|
package/cli/ui/run-walk.js
CHANGED
|
@@ -7,14 +7,16 @@ import { createElement } from "react";
|
|
|
7
7
|
/**
|
|
8
8
|
* Render the interactive table inside an Effect, resolving with the collected
|
|
9
9
|
* decisions once the user submits (or with an empty list when they cancel with
|
|
10
|
-
* Esc, or when no rows are actionable), after Ink has fully exited.
|
|
10
|
+
* Esc, or when no rows are actionable), after Ink has fully exited. An Ink
|
|
11
|
+
* crash rejects `waitUntilExit()`, which `Effect.promise` surfaces as a
|
|
12
|
+
* defect rather than a hung fiber.
|
|
11
13
|
*
|
|
12
14
|
* @internal
|
|
13
15
|
*/
|
|
14
16
|
function runWalk(items, dryRun = false, unresolved = [], interopModels = /* @__PURE__ */ new Map()) {
|
|
15
|
-
return Effect.
|
|
17
|
+
return Effect.promise(async () => {
|
|
16
18
|
let collected = [];
|
|
17
|
-
render(createElement(Walk, {
|
|
19
|
+
await render(createElement(Walk, {
|
|
18
20
|
items,
|
|
19
21
|
dryRun,
|
|
20
22
|
unresolved,
|
|
@@ -22,7 +24,8 @@ function runWalk(items, dryRun = false, unresolved = [], interopModels = /* @__P
|
|
|
22
24
|
onDone: (d) => {
|
|
23
25
|
collected = d;
|
|
24
26
|
}
|
|
25
|
-
})).waitUntilExit()
|
|
27
|
+
})).waitUntilExit();
|
|
28
|
+
return [...collected];
|
|
26
29
|
});
|
|
27
30
|
}
|
|
28
31
|
|
package/cli/validate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { parseRange, parseVersion } from "../semver-util.js";
|
|
1
2
|
import { Effect } from "effect";
|
|
2
|
-
import { Range, SemVer } from "@effected/semver";
|
|
3
3
|
|
|
4
4
|
//#region src/cli/validate.ts
|
|
5
5
|
/**
|
|
@@ -19,15 +19,12 @@ import { Range, SemVer } from "@effected/semver";
|
|
|
19
19
|
* @internal
|
|
20
20
|
*/
|
|
21
21
|
function rangeIsSatisfiable(range, versions) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (sv && parsedRange.test(sv)) return true;
|
|
29
|
-
}
|
|
30
|
-
return false;
|
|
22
|
+
if (versions.length === 0) return true;
|
|
23
|
+
const parsedRange = parseRange(range);
|
|
24
|
+
if (parsedRange === null) return true;
|
|
25
|
+
return versions.some((v) => {
|
|
26
|
+
const sv = parseVersion(v);
|
|
27
|
+
return sv !== null && parsedRange.test(sv);
|
|
31
28
|
});
|
|
32
29
|
}
|
|
33
30
|
/**
|
|
@@ -49,7 +46,7 @@ function rangeIsSatisfiable(range, versions) {
|
|
|
49
46
|
* @internal
|
|
50
47
|
*/
|
|
51
48
|
function validateEdits(edits, versionsByPkg) {
|
|
52
|
-
return Effect.
|
|
49
|
+
return Effect.sync(() => {
|
|
53
50
|
const byPkg = /* @__PURE__ */ new Map();
|
|
54
51
|
for (const e of edits) {
|
|
55
52
|
const key = e.versionKey ?? e.pkg;
|
|
@@ -62,11 +59,10 @@ function validateEdits(edits, versionsByPkg) {
|
|
|
62
59
|
for (const [key, group] of byPkg) {
|
|
63
60
|
const pkg = group[0]?.pkg ?? key;
|
|
64
61
|
const versions = versionsByPkg.get(key) ?? [];
|
|
65
|
-
const checked =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
});
|
|
62
|
+
const checked = group.map((edit) => ({
|
|
63
|
+
edit,
|
|
64
|
+
ok: rangeIsSatisfiable(edit.value, versions)
|
|
65
|
+
}));
|
|
70
66
|
const failing = checked.filter((c) => !c.ok);
|
|
71
67
|
if (failing.length === 0) {
|
|
72
68
|
accepted.push(...group);
|
package/cli/walk-plan.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { derivePeerRange } from "./peer-range.js";
|
|
2
|
-
import { detectPeerDrift } from "./drift.js";
|
|
3
1
|
import { versionKeyOf } from "./version-key.js";
|
|
2
|
+
import { derivePeerRange } from "./peer-range.js";
|
|
4
3
|
import { planEntry } from "./plan.js";
|
|
4
|
+
import { detectPeerDrift } from "./drift.js";
|
|
5
5
|
import { Effect } from "effect";
|
|
6
6
|
|
|
7
7
|
//#region src/cli/walk-plan.ts
|
package/cli/walk-reducer.js
CHANGED
|
@@ -5,6 +5,34 @@ const ORDER = {
|
|
|
5
5
|
minor: 2,
|
|
6
6
|
latest: 3
|
|
7
7
|
};
|
|
8
|
+
/** Trailing annotation appended to a major candidate's cell, e.g. " ⚠ major". @internal */
|
|
9
|
+
const MAJOR_SUFFIX = " ⚠ major";
|
|
10
|
+
/** Filled / hollow radio glyphs, matching `pnpm up -i`. @internal */
|
|
11
|
+
const SELECTED = "●";
|
|
12
|
+
/** @internal */
|
|
13
|
+
const UNSELECTED = "○";
|
|
14
|
+
/** "● " / "○ " glyph-plus-space prefix width, common to every cell. */
|
|
15
|
+
const BUBBLE_WIDTH = 2;
|
|
16
|
+
/**
|
|
17
|
+
* Compute the table geometry once for a set of rows. The range and the major
|
|
18
|
+
* suffix are padded TOGETHER so a major annotation never shifts a later
|
|
19
|
+
* column, and every row emits `maxCells` cells so the peer separator lands
|
|
20
|
+
* in the same column regardless of how many candidates a row has.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
function tableLayout(items) {
|
|
25
|
+
const cells = items.map(displayCandidates);
|
|
26
|
+
const pkgWidth = items.length ? Math.max(...items.map((i) => i.entry.pkg.length)) : 0;
|
|
27
|
+
const cellWidth = items.length ? Math.max(...cells.flatMap((cs) => cs.map((c) => c.range.length + (c.isMajor ? 8 : 0)))) : 0;
|
|
28
|
+
return {
|
|
29
|
+
pkgWidth,
|
|
30
|
+
cellWidth,
|
|
31
|
+
maxCells: items.length ? Math.max(...cells.map((cs) => cs.length)) : 0,
|
|
32
|
+
blankCell: `${" ".repeat(BUBBLE_WIDTH + cellWidth)} `,
|
|
33
|
+
cellText: (c, selected) => `${selected ? "●" : "○"} ${`${c.range}${c.isMajor ? MAJOR_SUFFIX : ""}`.padEnd(cellWidth)} `
|
|
34
|
+
};
|
|
35
|
+
}
|
|
8
36
|
/**
|
|
9
37
|
* Truncate `s` to at most `max` display columns, appending `…` when clipped.
|
|
10
38
|
* Keeps a long peer-conflict annotation from wrapping and breaking the table's
|
|
@@ -130,4 +158,4 @@ function cellColor(candidate, selected) {
|
|
|
130
158
|
}
|
|
131
159
|
|
|
132
160
|
//#endregion
|
|
133
|
-
export { cellColor, displayCandidates, initTable, peerFor, tableDecisions, tableStep, truncateEnd };
|
|
161
|
+
export { MAJOR_SUFFIX, cellColor, displayCandidates, initTable, peerFor, tableDecisions, tableLayout, tableStep, truncateEnd };
|
package/cli/workspace-file.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Result } from "effect";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { Yaml, YamlStringifyOptions } from "@effected/yaml";
|
|
@@ -46,12 +46,12 @@ function findWorkspaceFile(startDir) {
|
|
|
46
46
|
}
|
|
47
47
|
/** Parse pnpm-workspace.yaml source; empty/whitespace yields an empty object. @internal */
|
|
48
48
|
function parseWorkspace(source) {
|
|
49
|
-
const parsed =
|
|
49
|
+
const parsed = Result.getOrThrow(Yaml.parseResult(source));
|
|
50
50
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
51
51
|
}
|
|
52
52
|
/** Render a workspace object: deterministic key sort + yaml.stringify. @internal */
|
|
53
53
|
function renderWorkspace(obj) {
|
|
54
|
-
return
|
|
54
|
+
return Result.getOrThrow(Yaml.stringifyResult(canonicalize(obj), STRINGIFY_OPTIONS));
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
//#endregion
|
package/cli/workspace-resolve.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { parseVersion } from "../semver-util.js";
|
|
2
|
+
import { findWorkspaceFile } from "./workspace-file.js";
|
|
1
3
|
import { ResolveError } from "./resolve.js";
|
|
2
4
|
import { Effect, Layer } from "effect";
|
|
3
5
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
4
6
|
import { dirname, join, resolve } from "node:path";
|
|
5
|
-
import { SemVer } from "@effected/semver";
|
|
6
7
|
import { getWorkspacePackagesSync } from "@effected/workspaces";
|
|
7
8
|
import { nodeSyncOps } from "@effected/workspaces/node-sync";
|
|
8
9
|
|
|
@@ -96,12 +97,8 @@ function applyPendingBumps(rootDir, versions) {
|
|
|
96
97
|
for (const [pkg, bump] of bumps) {
|
|
97
98
|
const current = versions.get(pkg);
|
|
98
99
|
if (current === void 0) continue;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
parsed = Effect.runSync(SemVer.parse(current));
|
|
102
|
-
} catch {
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
100
|
+
const parsed = parseVersion(current);
|
|
101
|
+
if (parsed === null) continue;
|
|
105
102
|
versions.set(pkg, parsed.bump[bump]().toString());
|
|
106
103
|
}
|
|
107
104
|
}
|
|
@@ -112,9 +109,9 @@ function applyPendingBumps(rootDir, versions) {
|
|
|
112
109
|
*
|
|
113
110
|
* @internal
|
|
114
111
|
*/
|
|
115
|
-
function readWorkspaceVersions(rootDir) {
|
|
112
|
+
function readWorkspaceVersions(rootDir, manifests = readManifests(rootDir)) {
|
|
116
113
|
const out = /* @__PURE__ */ new Map();
|
|
117
|
-
for (const [name, manifest] of
|
|
114
|
+
for (const [name, manifest] of manifests) out.set(name, manifest.version);
|
|
118
115
|
applyPendingBumps(rootDir, out);
|
|
119
116
|
return out;
|
|
120
117
|
}
|
|
@@ -131,7 +128,7 @@ function makeWorkspaceResolver(rootDir) {
|
|
|
131
128
|
let versions;
|
|
132
129
|
const load = () => {
|
|
133
130
|
manifests ??= readManifests(rootDir);
|
|
134
|
-
versions ??= readWorkspaceVersions(rootDir);
|
|
131
|
+
versions ??= readWorkspaceVersions(rootDir, manifests);
|
|
135
132
|
return {
|
|
136
133
|
manifests,
|
|
137
134
|
versions
|
|
@@ -160,13 +157,8 @@ function makeWorkspaceResolver(rootDir) {
|
|
|
160
157
|
* @internal
|
|
161
158
|
*/
|
|
162
159
|
function findWorkspaceRoot(startDir) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (existsSync(join(dir, "pnpm-workspace.yaml"))) return dir;
|
|
166
|
-
const parent = dirname(dir);
|
|
167
|
-
if (parent === dir) return resolve(startDir);
|
|
168
|
-
dir = parent;
|
|
169
|
-
}
|
|
160
|
+
const file = findWorkspaceFile(resolve(startDir));
|
|
161
|
+
return file === null ? resolve(startDir) : dirname(file);
|
|
170
162
|
}
|
|
171
163
|
|
|
172
164
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rolldown-pnpm-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A dogfooding example of our plugin",
|
|
6
6
|
"repository": {
|
|
@@ -45,8 +45,7 @@
|
|
|
45
45
|
"ink-tab": "^5.2.0",
|
|
46
46
|
"oxc-parser": "^0.150.0",
|
|
47
47
|
"react": "^19.3.0",
|
|
48
|
-
"std-env": "^4.2.0"
|
|
49
|
-
"std-osc8": "^0.2.0"
|
|
48
|
+
"std-env": "^4.2.0"
|
|
50
49
|
},
|
|
51
50
|
"peerDependencies": {
|
|
52
51
|
"rolldown": "^1.2.0"
|
package/patches/build.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { discoverPatches } from "./discover.js";
|
|
2
|
+
import { Predicate } from "effect";
|
|
2
3
|
|
|
3
4
|
//#region src/patches/build.ts
|
|
4
5
|
/** True only for the `{ strategy: "rewrite" }` directive. @internal */
|
|
5
6
|
function isRewriteDirective(v) {
|
|
6
|
-
return
|
|
7
|
+
return Predicate.isObject(v) && Object.keys(v).length === 1 && v.strategy === "rewrite";
|
|
7
8
|
}
|
|
8
9
|
/** Read `local.localPatchesDir` when it is a string. @internal */
|
|
9
10
|
function readLocalPatchesDir(config) {
|
|
@@ -11,22 +12,35 @@ function readLocalPatchesDir(config) {
|
|
|
11
12
|
return typeof local?.localPatchesDir === "string" ? local.localPatchesDir : void 0;
|
|
12
13
|
}
|
|
13
14
|
/**
|
|
14
|
-
*
|
|
15
|
-
* `{ strategy: "rewrite" }` directive
|
|
16
|
-
*
|
|
17
|
-
* explicit map / wrapped value passes through untouched.
|
|
15
|
+
* Discover this plugin's own patches when `patchedDependencies` is absent or
|
|
16
|
+
* the `{ strategy: "rewrite" }` directive; undefined when an explicit map /
|
|
17
|
+
* wrapped value is declared (the escape hatch skips discovery entirely).
|
|
18
18
|
*
|
|
19
19
|
* @internal
|
|
20
20
|
*/
|
|
21
|
-
function
|
|
21
|
+
function discoverOwnedPatches(config, baseDir) {
|
|
22
22
|
const raw = config.patchedDependencies;
|
|
23
|
-
if (raw !== void 0 && !isRewriteDirective(raw)) return
|
|
23
|
+
if (raw !== void 0 && !isRewriteDirective(raw)) return void 0;
|
|
24
24
|
const localPatchesDir = readLocalPatchesDir(config);
|
|
25
|
-
|
|
25
|
+
return discoverPatches({
|
|
26
26
|
baseDir,
|
|
27
27
|
name: config.name,
|
|
28
28
|
...localPatchesDir !== void 0 ? { localPatchesDir } : {}
|
|
29
|
-
})
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve build-time `patchedDependencies`. When the field is absent or the
|
|
33
|
+
* `{ strategy: "rewrite" }` directive, run discovery and inject the distributed
|
|
34
|
+
* map (`name`-scoped `.pnpm-config` paths) so `freeze` sees a plain map. An
|
|
35
|
+
* explicit map / wrapped value passes through untouched. A caller that has
|
|
36
|
+
* already run discovery passes its result as `owned`.
|
|
37
|
+
*
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
function withResolvedBuildPatches(config, baseDir, owned = discoverOwnedPatches(config, baseDir)) {
|
|
41
|
+
const raw = config.patchedDependencies;
|
|
42
|
+
if (owned === void 0) return config;
|
|
43
|
+
const distributed = owned.filter((p) => p.distributed);
|
|
30
44
|
if (distributed.length === 0) {
|
|
31
45
|
if (raw === void 0) return config;
|
|
32
46
|
const { patchedDependencies: _drop, ...rest } = config;
|
|
@@ -41,4 +55,4 @@ function withResolvedBuildPatches(config, baseDir) {
|
|
|
41
55
|
}
|
|
42
56
|
|
|
43
57
|
//#endregion
|
|
44
|
-
export { isRewriteDirective, readLocalPatchesDir, withResolvedBuildPatches };
|
|
58
|
+
export { discoverOwnedPatches, isRewriteDirective, readLocalPatchesDir, withResolvedBuildPatches };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { bareVersion } from "../semver-util.js";
|
|
2
|
+
|
|
1
3
|
//#region src/plugin/allowed-versions.ts
|
|
2
4
|
/** An exact version: no range operator, no wildcard — `X.Y.Z` with an optional prerelease. */
|
|
3
5
|
const EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
4
6
|
/** The install range of a catalog spec (bare string or `{ range }` object). */
|
|
5
7
|
const rangeOf = (spec) => typeof spec === "string" ? spec : spec.range;
|
|
6
|
-
/** Strip a leading range operator to the bare version digits (e.g. `^3.17.0` → `3.17.0`). */
|
|
7
|
-
const bareVersion = (range) => range.replace(/^[\^~>=<\s]+/, "").split(/\s/)[0] ?? range;
|
|
8
8
|
/** Apply the `prefix` transform to a peer value: verbatim, re-prefixed, or stripped to exact. */
|
|
9
9
|
function applyPrefix(peerValue, prefix) {
|
|
10
10
|
if (prefix === void 0) return peerValue;
|