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 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
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @internal
8
8
  */
9
- const CLI_VERSION = "2.0.4";
9
+ const CLI_VERSION = "2.1.1";
10
10
 
11
11
  //#endregion
12
12
  export { CLI_VERSION };
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.0.4",
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.2",
39
- "@effected/cli": "^0.16.2",
40
- "@effected/env": "^0.1.2",
41
- "@effected/glob": "^0.10.1",
42
- "@effected/npm": "^0.20.1",
43
- "@effected/semver": "^0.11.1",
44
- "@effected/walker": "^0.15.1",
45
- "@effected/workspaces": "^0.32.1",
46
- "@effected/yaml": "^0.19.2",
47
- "effect": "4.0.2",
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
- const raw = config[field];
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: (error) => new ConfigError({ message: error instanceof Error ? error.message : String(error) })
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 };