rolldown-pnpm-config 2.0.4 → 2.1.1
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/catalogs.js +7 -1
- package/cli/version.js +1 -1
- package/index.d.ts +44 -2
- package/package.json +11 -11
- package/plugin/allowed-versions.js +1 -2
- package/plugin/freeze.js +8 -2
- package/plugin/overrides-from-catalogs.js +69 -0
package/catalogs.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
//#region src/catalogs.ts
|
|
2
2
|
/**
|
|
3
|
+
* The install range of a catalog spec (bare string or `{ range }` object).
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
const rangeOf = (spec) => typeof spec === "string" ? spec : spec.range;
|
|
8
|
+
/**
|
|
3
9
|
* Normalize declarative catalog input into the resolved `{ catalog → pkg → range }`
|
|
4
10
|
* map consumed by the runtime. Pure: the base catalog uses each package's
|
|
5
11
|
* `range` (or bare string); a peers catalog is emitted only for packages
|
|
@@ -25,4 +31,4 @@ function normalizeCatalogs(input) {
|
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
//#endregion
|
|
28
|
-
export { normalizeCatalogs };
|
|
34
|
+
export { normalizeCatalogs, rangeOf };
|
package/cli/version.js
CHANGED
package/index.d.ts
CHANGED
|
@@ -70,6 +70,40 @@ interface AllowedVersionsFromCatalogs {
|
|
|
70
70
|
readonly prefix?: string | null;
|
|
71
71
|
}
|
|
72
72
|
//#endregion
|
|
73
|
+
//#region src/plugin/overrides-from-catalogs.d.ts
|
|
74
|
+
/**
|
|
75
|
+
* A directive that derives pnpm `overrides` from a catalog, so every member of
|
|
76
|
+
* a release family resolves to the catalog's exact version everywhere in a
|
|
77
|
+
* consumer's graph: direct requests, transitive requests from other packages,
|
|
78
|
+
* and peer edges alike. A catalog range alone cannot do that, since a caret
|
|
79
|
+
* admits a newer member and a catalog never reaches a dependency's own
|
|
80
|
+
* dependencies. For each member whose range is a simple operator on a plain
|
|
81
|
+
* `X.Y.Z` version, an entry `"<name>@<selector>": "<X.Y.Z>"` is emitted.
|
|
82
|
+
*
|
|
83
|
+
* @remarks
|
|
84
|
+
* Under the default `scope: "line"` the selector is the member's release line,
|
|
85
|
+
* `^X.0.0` (`^0.Y.0` on a 0.x line, `~0.0.0` on 0.0.x), so a request for a *different* major
|
|
86
|
+
* (or a prerelease, which no caret line admits) is left alone. Prerelease and
|
|
87
|
+
* compound-range members are skipped rather than widened. An override entry
|
|
88
|
+
* authored by hand wins over a derived one on the same selector, and the
|
|
89
|
+
* consumer's own `overrides` still win over the plugin's at install time.
|
|
90
|
+
*
|
|
91
|
+
* @public
|
|
92
|
+
*/
|
|
93
|
+
interface OverridesFromCatalogs {
|
|
94
|
+
/** The catalog whose members are pinned. */
|
|
95
|
+
readonly catalog: string;
|
|
96
|
+
/** Members left out, e.g. a tool that rides the catalog but is not part of the release family. */
|
|
97
|
+
readonly exclude?: readonly string[];
|
|
98
|
+
/**
|
|
99
|
+
* How each selector is scoped. `"line"` (the default) qualifies the package
|
|
100
|
+
* name with its caret line, so only requests on that line are pinned;
|
|
101
|
+
* `"any"` emits the bare package name, which pins every request regardless
|
|
102
|
+
* of the line it asked for.
|
|
103
|
+
*/
|
|
104
|
+
readonly scope?: "line" | "any";
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
73
107
|
//#region src/runtime/types.d.ts
|
|
74
108
|
/**
|
|
75
109
|
* How a field's divergences are enforced: silent, console warning, or a thrown
|
|
@@ -131,7 +165,7 @@ interface PluginConfig {
|
|
|
131
165
|
* folder detection is independent and unaffected.
|
|
132
166
|
*/
|
|
133
167
|
readonly localPatchesDir?: string;
|
|
134
|
-
} & { readonly [K in keyof PluginConfig]?: PluginConfig[K] | LocalDirective<PluginConfig[K]>; };
|
|
168
|
+
} & { readonly [K in Exclude<keyof PluginConfig, "overridesFromCatalogs">]?: PluginConfig[K] | LocalDirective<PluginConfig[K]>; };
|
|
135
169
|
/** Whether pnpm prompts before purging `node_modules`. */
|
|
136
170
|
readonly confirmModulesPurge?: FieldInput<boolean>;
|
|
137
171
|
/** Per-package manifest overrides merged into the dependency graph. */
|
|
@@ -166,6 +200,14 @@ interface PluginConfig {
|
|
|
166
200
|
readonly auditConfig?: FieldInput<Record<string, string[]>>;
|
|
167
201
|
/** Security version overrides, keyed by package selector. */
|
|
168
202
|
readonly overrides?: FieldInput<Record<string, string>>;
|
|
203
|
+
/**
|
|
204
|
+
* Derive `overrides` from a catalog (or several), pinning every member of a
|
|
205
|
+
* release family to its exact catalog version across the whole consumer
|
|
206
|
+
* graph. Resolved and merged under `overrides` at build time, so the
|
|
207
|
+
* pnpmfile, `export` and `preview` all carry the result; an authored
|
|
208
|
+
* `overrides` entry wins on a selector clash. Not a pnpm field itself.
|
|
209
|
+
*/
|
|
210
|
+
readonly overridesFromCatalogs?: OverridesFromCatalogs | readonly OverridesFromCatalogs[];
|
|
169
211
|
/** Peer dependency rules: allowed versions plus ignore/allow-any lists. */
|
|
170
212
|
readonly peerDependencyRules?: FieldInput<{
|
|
171
213
|
readonly allowedVersions?: Record<string, string>;
|
|
@@ -420,5 +462,5 @@ interface PluginConfig {
|
|
|
420
462
|
*/
|
|
421
463
|
export declare function PnpmConfigPlugin(config: PluginConfig): Plugin;
|
|
422
464
|
//#endregion
|
|
423
|
-
export type { AllowedVersionsFromCatalogs, CatalogDeclaration, CatalogPackageSpec, Enforcement, FieldInput, LocalDirective, PeerStrategy, PluginConfig, VersionSource };
|
|
465
|
+
export type { AllowedVersionsFromCatalogs, CatalogDeclaration, CatalogPackageSpec, Enforcement, FieldInput, LocalDirective, OverridesFromCatalogs, PeerStrategy, PluginConfig, VersionSource };
|
|
424
466
|
//# sourceMappingURL=index.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rolldown-pnpm-config",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A dogfooding example of our plugin",
|
|
6
6
|
"repository": {
|
|
@@ -35,16 +35,16 @@
|
|
|
35
35
|
"rolldown-pnpm-config": "bin/rolldown-pnpm-config.js"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@effect/platform-node": "4.0.
|
|
39
|
-
"@effected/cli": "^0.
|
|
40
|
-
"@effected/env": "^0.
|
|
41
|
-
"@effected/glob": "^0.
|
|
42
|
-
"@effected/npm": "^0.
|
|
43
|
-
"@effected/semver": "^0.
|
|
44
|
-
"@effected/walker": "^0.
|
|
45
|
-
"@effected/workspaces": "^0.
|
|
46
|
-
"@effected/yaml": "^0.
|
|
47
|
-
"effect": "4.0.
|
|
38
|
+
"@effect/platform-node": "^4.0.3",
|
|
39
|
+
"@effected/cli": "^0.17.0",
|
|
40
|
+
"@effected/env": "^0.2.0",
|
|
41
|
+
"@effected/glob": "^0.11.0",
|
|
42
|
+
"@effected/npm": "^0.21.0",
|
|
43
|
+
"@effected/semver": "^0.12.0",
|
|
44
|
+
"@effected/walker": "^0.16.0",
|
|
45
|
+
"@effected/workspaces": "^0.33.0",
|
|
46
|
+
"@effected/yaml": "^0.20.0",
|
|
47
|
+
"effect": "^4.0.3",
|
|
48
48
|
"ink": "^8.0.0",
|
|
49
49
|
"oxc-parser": "^0.153.0",
|
|
50
50
|
"react": "^19.3.0"
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
+
import { rangeOf } from "../catalogs.js";
|
|
1
2
|
import { bareVersion } from "../semver-util.js";
|
|
2
3
|
|
|
3
4
|
//#region src/plugin/allowed-versions.ts
|
|
4
5
|
/** An exact version: no range operator, no wildcard — `X.Y.Z` with an optional prerelease. */
|
|
5
6
|
const EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
6
|
-
/** The install range of a catalog spec (bare string or `{ range }` object). */
|
|
7
|
-
const rangeOf = (spec) => typeof spec === "string" ? spec : spec.range;
|
|
8
7
|
/** Apply the `prefix` transform to a peer value: verbatim, re-prefixed, or stripped to exact. */
|
|
9
8
|
function applyPrefix(peerValue, prefix) {
|
|
10
9
|
if (prefix === void 0) return peerValue;
|
package/plugin/freeze.js
CHANGED
|
@@ -2,6 +2,7 @@ import { normalizeCatalogs } from "../catalogs.js";
|
|
|
2
2
|
import { DESCRIPTORS, deriveSchemas } from "../descriptors/index.js";
|
|
3
3
|
import { FIELD_REGISTRY } from "../registry.js";
|
|
4
4
|
import { resolvePeerDependencyRules } from "./allowed-versions.js";
|
|
5
|
+
import { resolveOverrides } from "./overrides-from-catalogs.js";
|
|
5
6
|
import { Data, Effect, Schema } from "effect";
|
|
6
7
|
|
|
7
8
|
//#region src/plugin/freeze.ts
|
|
@@ -62,15 +63,20 @@ function freeze(config) {
|
|
|
62
63
|
enforcement: "warn"
|
|
63
64
|
};
|
|
64
65
|
if (typeof config.name !== "string" || config.name.trim() === "") return yield* Effect.fail(new ConfigError({ message: "Config `name` is required and must be a non-empty string" }));
|
|
66
|
+
const directiveError = (error) => new ConfigError({ message: error instanceof Error ? error.message : String(error) });
|
|
65
67
|
for (const [field, reg] of Object.entries(FIELD_REGISTRY)) {
|
|
66
68
|
if (field === "catalogs") continue;
|
|
67
|
-
|
|
69
|
+
let raw = config[field];
|
|
70
|
+
if (raw === void 0 && field === "overrides" && config.overridesFromCatalogs !== void 0) raw = {};
|
|
68
71
|
if (raw === void 0) continue;
|
|
69
72
|
const decl = normalizeField(raw);
|
|
70
73
|
const schema = FIELD_SCHEMAS[field];
|
|
71
74
|
const value = field === "peerDependencyRules" ? yield* Effect.try({
|
|
72
75
|
try: () => resolvePeerDependencyRules(decl.value, config.catalogs),
|
|
73
|
-
catch:
|
|
76
|
+
catch: directiveError
|
|
77
|
+
}) : field === "overrides" ? yield* Effect.try({
|
|
78
|
+
try: () => resolveOverrides(decl.value, config.overridesFromCatalogs, config.catalogs),
|
|
79
|
+
catch: directiveError
|
|
74
80
|
}) : decl.value;
|
|
75
81
|
base[field] = schema ? yield* Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError((error) => new ConfigError({ message: `Invalid ${field}: ${String(error)}` }))) : value;
|
|
76
82
|
manifest[field] = {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { rangeOf } from "../catalogs.js";
|
|
2
|
+
import { bareVersion } from "../semver-util.js";
|
|
3
|
+
|
|
4
|
+
//#region src/plugin/overrides-from-catalogs.ts
|
|
5
|
+
/** A plain release version: `X.Y.Z`, no prerelease, no build metadata. */
|
|
6
|
+
const PLAIN_VERSION = /^(\d+)\.(\d+)\.(\d+)$/;
|
|
7
|
+
/** A simple-operator range: an optional `^`, `~`, `>=` or `=` on one version. */
|
|
8
|
+
const SIMPLE_RANGE = /^(?:\^|~|>=|=)?\s*\d/;
|
|
9
|
+
/**
|
|
10
|
+
* The release line a plain version belongs to: `^X.0.0`, or `^0.Y.0` when the
|
|
11
|
+
* major is 0. A `0.0.Z` version has no caret line (`^0.0.0` admits only
|
|
12
|
+
* `0.0.0` itself), so it takes the tilde line `~0.0.0`, which covers every
|
|
13
|
+
* `0.0.Z` request.
|
|
14
|
+
*/
|
|
15
|
+
function releaseLine(major, minor) {
|
|
16
|
+
if (major !== "0") return `^${major}.0.0`;
|
|
17
|
+
return minor === "0" ? "~0.0.0" : `^0.${minor}.0`;
|
|
18
|
+
}
|
|
19
|
+
/** Derive the overrides for one directive. Pure; throws when the catalog or an excluded member is absent. */
|
|
20
|
+
function deriveOne(catalogs, { catalog, exclude = [], scope = "line" }) {
|
|
21
|
+
const decl = catalogs[catalog];
|
|
22
|
+
if (decl === void 0) throw new Error(`overridesFromCatalogs: catalog "${catalog}" is not declared`);
|
|
23
|
+
for (const name of exclude) if (!(name in decl.packages)) throw new Error(`overridesFromCatalogs: excluded member "${name}" is not in catalog "${catalog}"`);
|
|
24
|
+
const excluded = new Set(exclude);
|
|
25
|
+
const table = {};
|
|
26
|
+
for (const [name, spec] of Object.entries(decl.packages)) {
|
|
27
|
+
if (excluded.has(name)) continue;
|
|
28
|
+
const range = rangeOf(spec);
|
|
29
|
+
if (!SIMPLE_RANGE.test(range) || /\s/.test(range.trim())) continue;
|
|
30
|
+
const version = bareVersion(range);
|
|
31
|
+
const match = PLAIN_VERSION.exec(version);
|
|
32
|
+
if (match === null) continue;
|
|
33
|
+
const selector = scope === "any" ? name : `${name}@${releaseLine(match[1], match[2])}`;
|
|
34
|
+
table[selector] = version;
|
|
35
|
+
}
|
|
36
|
+
return table;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Merge the derived overrides for one or more directives (later directives win
|
|
40
|
+
* on a selector clash).
|
|
41
|
+
*
|
|
42
|
+
* @internal
|
|
43
|
+
*/
|
|
44
|
+
function deriveOverrides(catalogs, directives) {
|
|
45
|
+
const list = Array.isArray(directives) ? directives : [directives];
|
|
46
|
+
const out = {};
|
|
47
|
+
for (const d of list) Object.assign(out, deriveOne(catalogs, d));
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the `overrides` value a plugin ships: the derived table from the
|
|
52
|
+
* directive, with the authored entries merged over it so a hand-written
|
|
53
|
+
* selector wins. Without a directive the authored value passes through
|
|
54
|
+
* untouched (by reference, so an absent field stays absent).
|
|
55
|
+
*
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
function resolveOverrides(authored, directives, catalogs) {
|
|
59
|
+
if (directives === void 0) return authored;
|
|
60
|
+
const derived = deriveOverrides(catalogs, directives);
|
|
61
|
+
const manual = authored !== null && typeof authored === "object" ? authored : {};
|
|
62
|
+
return {
|
|
63
|
+
...derived,
|
|
64
|
+
...manual
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//#endregion
|
|
69
|
+
export { deriveOverrides, resolveOverrides };
|