rolldown-pnpm-config 0.2.2 → 0.4.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.
@@ -0,0 +1,88 @@
1
+ import { Effect } from "effect";
2
+ import { Range, SemVer } from "@effected/semver";
3
+
4
+ //#region src/cli/validate.ts
5
+ /**
6
+ * Whether at least one published version satisfies `range`.
7
+ *
8
+ * The predicate is deliberately "some published version satisfies this range",
9
+ * NOT "this exact version was published": `^3.4.0` is a valid lock-minor floor
10
+ * even when 3.4.0 itself never shipped but 3.4.1 did. Conversely `^3.0.0`
11
+ * against a package with only `3.0.0-next.*` releases matches nothing.
12
+ *
13
+ * Fails OPEN — an empty version list (fetch failure, fully age-gated package)
14
+ * or an unparseable range yields `true`. Validation is a safety net against
15
+ * ranges we DERIVED wrongly, not a gate on the author's own hand-written
16
+ * ranges, and rejecting everything the moment the registry is unreachable would
17
+ * break offline peer materialization.
18
+ *
19
+ * @internal
20
+ */
21
+ function rangeIsSatisfiable(range, versions) {
22
+ return Effect.gen(function* () {
23
+ if (versions.length === 0) return true;
24
+ const parsedRange = yield* Range.parse(range).pipe(Effect.catch(() => Effect.succeed(null)));
25
+ if (parsedRange === null) return true;
26
+ for (const v of versions) {
27
+ const sv = yield* SemVer.parse(v).pipe(Effect.catch(() => Effect.succeed(null)));
28
+ if (sv && parsedRange.test(sv)) return true;
29
+ }
30
+ return false;
31
+ });
32
+ }
33
+ /**
34
+ * Partition planned edits into those whose range some published version
35
+ * satisfies and those none does — ATOMICALLY per package. A catalog package
36
+ * with a `strategy` produces a `range` edit and a `peer` edit as a pair; if
37
+ * either is unsatisfiable, BOTH are rejected. Writing one half of the pair
38
+ * (say, an accepted range bump next to a rejected, stale peer) would leave the
39
+ * file internally inconsistent — the peer no longer matches what the strategy
40
+ * derives from the new range — and every subsequent run would re-report the
41
+ * same drift and re-reject it forever. A package is either fully updated or
42
+ * not touched at all.
43
+ *
44
+ * `versionsByPkg` MUST be the UNGATED version list. Validating against the
45
+ * release-age-gated list would spuriously reject a package whose only matching
46
+ * version was published inside the gate window.
47
+ *
48
+ * @internal
49
+ */
50
+ function validateEdits(edits, versionsByPkg) {
51
+ return Effect.gen(function* () {
52
+ const byPkg = /* @__PURE__ */ new Map();
53
+ for (const e of edits) {
54
+ const group = byPkg.get(e.pkg) ?? [];
55
+ group.push(e);
56
+ byPkg.set(e.pkg, group);
57
+ }
58
+ const accepted = [];
59
+ const rejected = [];
60
+ for (const [pkg, group] of byPkg) {
61
+ const versions = versionsByPkg.get(pkg) ?? [];
62
+ const checked = [];
63
+ for (const e of group) checked.push({
64
+ edit: e,
65
+ ok: yield* rangeIsSatisfiable(e.value, versions)
66
+ });
67
+ const failing = checked.filter((c) => !c.ok);
68
+ if (failing.length === 0) {
69
+ accepted.push(...group);
70
+ continue;
71
+ }
72
+ const failingKinds = failing.map((f) => f.edit.kind).join(" and ");
73
+ for (const { edit, ok } of checked) rejected.push({
74
+ pkg,
75
+ kind: edit.kind,
76
+ value: edit.value,
77
+ reason: ok ? `dropped along with its ${failingKinds} edit for ${pkg}, which is unsatisfiable` : `no published version of ${pkg} satisfies ${edit.value}`
78
+ });
79
+ }
80
+ return {
81
+ accepted,
82
+ rejected
83
+ };
84
+ });
85
+ }
86
+
87
+ //#endregion
88
+ export { rangeIsSatisfiable, validateEdits };
package/cli/walk-plan.js CHANGED
@@ -7,7 +7,8 @@ import { Effect } from "effect";
7
7
  /**
8
8
  * Build the interactive walk items: for each entry, its candidate list (from
9
9
  * planEntry against the resolved versions), an up-to-date flag (only the keep
10
- * candidate remains), and any peer drift resync target.
10
+ * candidate remains), any peer drift resync target, any peer to materialize,
11
+ * and any strategy/prerelease incompatibility warning.
11
12
  *
12
13
  * @internal
13
14
  */
@@ -17,14 +18,21 @@ function buildWalkItems(entries, versionsByPkg) {
17
18
  for (const entry of entries) {
18
19
  const candidates = yield* planEntry(entry, [...versionsByPkg.get(entry.pkg) ?? []]);
19
20
  const driftPeer = yield* detectPeerDrift(entry);
20
- const materializePeer = entry.strategy && !entry.peer ? yield* derivePeerRange(entry.currentRange, entry.strategy) : null;
21
+ let peerWarning = null;
22
+ let materializePeer = null;
23
+ if (entry.strategy && entry.strategy !== "interop") {
24
+ const derived = yield* derivePeerRange(entry.currentRange, entry.strategy);
25
+ peerWarning = derived.warning;
26
+ if (!entry.peer) materializePeer = derived.range;
27
+ }
21
28
  const upToDate = candidates.length === 1 && driftPeer === null && materializePeer === null;
22
29
  items.push({
23
30
  entry,
24
31
  candidates,
25
32
  upToDate,
26
33
  driftPeer,
27
- materializePeer
34
+ materializePeer,
35
+ peerWarning
28
36
  });
29
37
  }
30
38
  return items;
@@ -1,61 +1,116 @@
1
1
  //#region src/cli/walk-reducer.ts
2
- /** Index of the next actionable (not up-to-date) item at or after `from`, or -1. */
3
- function nextActionable(items, from) {
4
- for (let i = from; i < items.length; i++) if (!items[i].upToDate) return i;
5
- return -1;
2
+ const ORDER = {
3
+ keep: 0,
4
+ "in-range": 1,
5
+ latest: 2
6
+ };
7
+ /**
8
+ * The row's options in display order: keep first (always index 0, always the
9
+ * default), then the in-range bump, then the latest-overall bump. `planEntry`
10
+ * emits them in the opposite order, with keep last.
11
+ *
12
+ * @internal
13
+ */
14
+ function displayCandidates(item) {
15
+ return [...item.candidates].sort((a, b) => ORDER[a.kind] - ORDER[b.kind]);
6
16
  }
7
17
  /**
8
- * Initialize the walk on the first actionable item (auto-skipping up-to-date
9
- * items). If none are actionable, the walk is immediately done.
18
+ * Initialize the table: cursor on the first row, keep selected on every row, so
19
+ * the default state applies nothing.
10
20
  *
11
21
  * @internal
12
22
  */
13
- function initWalk(items) {
14
- const index = nextActionable(items, 0);
23
+ function initTable(items) {
15
24
  return {
16
- index: index === -1 ? items.length : index,
17
25
  cursor: 0,
18
- decisions: [],
19
- done: index === -1
26
+ picks: items.map(() => 0),
27
+ done: false,
28
+ cancelled: false
20
29
  };
21
30
  }
31
+ const clamp = (n, max) => n < 0 ? 0 : n > max ? max : n;
22
32
  /**
23
- * Advance the walk by a key. up/down move the cursor within the current item's
24
- * candidates; enter records the highlighted candidate and moves to the next
25
- * actionable item (or completes).
33
+ * Advance the table by a key. up/down move between rows; left/right move the
34
+ * radio selection within the row under the cursor; submit applies; cancel exits
35
+ * without applying. Both axes clamp at their ends rather than wrapping.
26
36
  *
27
37
  * @internal
28
38
  */
29
- function walkStep(state, items, key) {
39
+ function tableStep(state, items, key) {
30
40
  if (state.done) return state;
31
- const item = items[state.index];
32
- const count = item.candidates.length;
33
- if (key === "up") return {
41
+ if (key === "submit") return {
34
42
  ...state,
35
- cursor: (state.cursor - 1 + count) % count
43
+ done: true
36
44
  };
37
- if (key === "down") return {
45
+ if (key === "cancel") return {
38
46
  ...state,
39
- cursor: (state.cursor + 1) % count
47
+ done: true,
48
+ cancelled: true
40
49
  };
41
- const chosen = item.candidates[state.cursor];
42
- const decisions = [...state.decisions, {
43
- item,
44
- chosen
45
- }];
46
- const next = nextActionable(items, state.index + 1);
47
- if (next === -1) return {
50
+ if (items.length === 0) return state;
51
+ if (key === "up") return {
48
52
  ...state,
49
- decisions,
50
- done: true
53
+ cursor: clamp(state.cursor - 1, items.length - 1)
54
+ };
55
+ if (key === "down") return {
56
+ ...state,
57
+ cursor: clamp(state.cursor + 1, items.length - 1)
51
58
  };
59
+ const count = displayCandidates(items[state.cursor]).length;
60
+ const delta = key === "right" ? 1 : -1;
61
+ const picks = [...state.picks];
62
+ picks[state.cursor] = clamp(picks[state.cursor] + delta, count - 1);
52
63
  return {
53
- index: next,
54
- cursor: 0,
55
- decisions,
56
- done: false
64
+ ...state,
65
+ picks
57
66
  };
58
67
  }
68
+ /**
69
+ * Project the table's selections into decisions. A cancelled table yields none,
70
+ * so nothing is written.
71
+ *
72
+ * @internal
73
+ */
74
+ function tableDecisions(state, items) {
75
+ if (state.cancelled) return [];
76
+ return items.map((item, i) => ({
77
+ item,
78
+ chosen: displayCandidates(item)[state.picks[i] ?? 0]
79
+ }));
80
+ }
81
+ /**
82
+ * The peer range that would be written for a row's currently chosen candidate.
83
+ * Non-keep candidates carry their own recomputed `peerRange`; keep reuses
84
+ * whichever peer source the item already resolved (a drift resync, a
85
+ * materialize target, or the entry's existing literal), falling back to an
86
+ * em dash placeholder when none applies.
87
+ *
88
+ * @internal
89
+ */
90
+ function peerFor(item, chosen) {
91
+ if (chosen.kind !== "keep") return chosen.peerRange ?? "—";
92
+ if (item.driftPeer) return item.driftPeer;
93
+ if (item.materializePeer) return item.materializePeer;
94
+ return item.entry.peer?.value ?? "—";
95
+ }
96
+ /**
97
+ * The Ink color for one candidate cell, or null for the terminal's default.
98
+ *
99
+ * Only a SELECTED UPGRADE is colored — green in-range, yellow for a major. A
100
+ * selected KEEP is deliberately left uncolored: it is the current value, not a
101
+ * change, and coloring it dim made the column the eye lands on first read as
102
+ * disabled. Unselected cells are always default.
103
+ *
104
+ * Extracted from the render so it can be unit-tested: `ink-testing-library`
105
+ * strips ANSI from `lastFrame()`, so a color assertion is impossible against
106
+ * the rendered output.
107
+ *
108
+ * @internal
109
+ */
110
+ function cellColor(candidate, selected) {
111
+ if (!selected || candidate.kind === "keep") return null;
112
+ return candidate.isMajor ? "yellow" : "green";
113
+ }
59
114
 
60
115
  //#endregion
61
- export { initWalk, walkStep };
116
+ export { cellColor, displayCandidates, initTable, peerFor, tableDecisions, tableStep };
@@ -1,14 +1,14 @@
1
+ import { Effect } from "effect";
1
2
  import { existsSync } from "node:fs";
2
3
  import { dirname, join } from "node:path";
3
- import { parse, stringify } from "yaml";
4
+ import { Yaml, YamlStringifyOptions } from "@effected/yaml";
4
5
 
5
6
  //#region src/cli/workspace-file.ts
6
7
  const FILENAME = "pnpm-workspace.yaml";
7
- const STRINGIFY_OPTIONS = {
8
+ const STRINGIFY_OPTIONS = YamlStringifyOptions.make({
8
9
  indent: 2,
9
- lineWidth: 0,
10
- singleQuote: false
11
- };
10
+ lineWidth: 0
11
+ });
12
12
  /** True when every element is a string/number/boolean (safe to sort). */
13
13
  function allPrimitive(arr) {
14
14
  return arr.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
@@ -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 = parse(source);
49
+ const parsed = Effect.runSync(Yaml.parse(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 stringify(canonicalize(obj), STRINGIFY_OPTIONS);
54
+ return Effect.runSync(Yaml.stringify(canonicalize(obj), STRINGIFY_OPTIONS));
55
55
  }
56
56
 
57
57
  //#endregion
@@ -104,7 +104,12 @@ const build = {
104
104
  anchor: "nodeoptions"
105
105
  },
106
106
  verifyDepsBeforeRun: {
107
- schema: Schema.Union(Schema.Literal("install", "warn", "error", "prompt"), Schema.Boolean),
107
+ schema: Schema.Union([Schema.Literals([
108
+ "install",
109
+ "warn",
110
+ "error",
111
+ "prompt"
112
+ ]), Schema.Boolean]),
108
113
  kind: "union",
109
114
  strategy: "scalar",
110
115
  enforcement: "absent",
@@ -41,7 +41,11 @@ const hoisting = {
41
41
  anchor: "shamefullyhoist"
42
42
  },
43
43
  hoistingLimits: {
44
- schema: Schema.Literal("none", "workspaces", "dependencies"),
44
+ schema: Schema.Literals([
45
+ "none",
46
+ "workspaces",
47
+ "dependencies"
48
+ ]),
45
49
  kind: "enum",
46
50
  strategy: "scalar",
47
51
  enforcement: "absent",
@@ -63,7 +67,11 @@ const hoisting = {
63
67
  anchor: "modulesdir"
64
68
  },
65
69
  nodeLinker: {
66
- schema: Schema.Literal("isolated", "hoisted", "pnp"),
70
+ schema: Schema.Literals([
71
+ "isolated",
72
+ "hoisted",
73
+ "pnp"
74
+ ]),
67
75
  kind: "enum",
68
76
  strategy: "scalar",
69
77
  enforcement: "absent",
@@ -121,7 +129,13 @@ const hoisting = {
121
129
  anchor: "virtualstoreonly"
122
130
  },
123
131
  packageImportMethod: {
124
- schema: Schema.Literal("auto", "hardlink", "copy", "clone", "clone-or-copy"),
132
+ schema: Schema.Literals([
133
+ "auto",
134
+ "hardlink",
135
+ "copy",
136
+ "clone",
137
+ "clone-or-copy"
138
+ ]),
125
139
  kind: "enum",
126
140
  strategy: "scalar",
127
141
  enforcement: "absent",
@@ -5,7 +5,11 @@ import { Schema } from "effect";
5
5
  /** The 14 resolution/misc preference fields. @internal */
6
6
  const misc = {
7
7
  resolutionMode: {
8
- schema: Schema.Literal("highest", "time-based", "lowest-direct"),
8
+ schema: Schema.Literals([
9
+ "highest",
10
+ "time-based",
11
+ "lowest-direct"
12
+ ]),
9
13
  kind: "enum",
10
14
  strategy: "scalar",
11
15
  enforcement: "absent",
@@ -18,7 +22,11 @@ const misc = {
18
22
  }
19
23
  },
20
24
  savePrefix: {
21
- schema: Schema.Literal("^", "~", ""),
25
+ schema: Schema.Literals([
26
+ "^",
27
+ "~",
28
+ ""
29
+ ]),
22
30
  kind: "enum",
23
31
  strategy: "scalar",
24
32
  enforcement: "absent",
@@ -19,10 +19,7 @@ const PeerRulesSchema = Schema.Struct({
19
19
  */
20
20
  const resolution = {
21
21
  catalogs: {
22
- schema: Schema.Record({
23
- key: Str,
24
- value: StringRecord
25
- }),
22
+ schema: Schema.Record(Str, StringRecord),
26
23
  kind: "object",
27
24
  strategy: "catalogs",
28
25
  enforcement: "warn",
@@ -205,7 +202,7 @@ const resolution = {
205
202
  anchor: "minimumreleaseageignoremissingtime"
206
203
  },
207
204
  trustPolicy: {
208
- schema: Schema.Literal("off", "no-downgrade"),
205
+ schema: Schema.Literals(["off", "no-downgrade"]),
209
206
  kind: "enum",
210
207
  strategy: "scalar",
211
208
  enforcement: "warn",
@@ -32,7 +32,12 @@ const runtimeCfg = {
32
32
  anchor: "managepackagemanagerversions"
33
33
  },
34
34
  pmOnFail: {
35
- schema: Schema.Literal("download", "error", "warn", "ignore"),
35
+ schema: Schema.Literals([
36
+ "download",
37
+ "error",
38
+ "warn",
39
+ "ignore"
40
+ ]),
36
41
  kind: "enum",
37
42
  strategy: "scalar",
38
43
  enforcement: "absent",
@@ -45,7 +50,12 @@ const runtimeCfg = {
45
50
  }
46
51
  },
47
52
  runtimeOnFail: {
48
- schema: Schema.Literal("download", "error", "warn", "ignore"),
53
+ schema: Schema.Literals([
54
+ "download",
55
+ "error",
56
+ "warn",
57
+ "ignore"
58
+ ]),
49
59
  kind: "enum",
50
60
  strategy: "scalar",
51
61
  enforcement: "absent",
@@ -5,22 +5,10 @@ import { Schema } from "effect";
5
5
  /** @internal */ const Num = Schema.Number;
6
6
  /** @internal */ const Str = Schema.String;
7
7
  /** @internal */ const StringArray = Schema.Array(Schema.String);
8
- /** @internal */ const StringRecord = Schema.Record({
9
- key: Schema.String,
10
- value: Schema.String
11
- });
12
- /** @internal */ const BooleanRecord = Schema.Record({
13
- key: Schema.String,
14
- value: Schema.Boolean
15
- });
16
- /** @internal */ const UnknownRecord = Schema.Record({
17
- key: Schema.String,
18
- value: Schema.Unknown
19
- });
20
- /** @internal */ const StringArrayRecord = Schema.Record({
21
- key: Schema.String,
22
- value: StringArray
23
- });
8
+ /** @internal */ const StringRecord = Schema.Record(Schema.String, Schema.String);
9
+ /** @internal */ const BooleanRecord = Schema.Record(Schema.String, Schema.Boolean);
10
+ /** @internal */ const UnknownRecord = Schema.Record(Schema.String, Schema.Unknown);
11
+ /** @internal */ const StringArrayRecord = Schema.Record(Schema.String, StringArray);
24
12
 
25
13
  //#endregion
26
14
  export { Bool, BooleanRecord, Num, Str, StringArray, StringArrayRecord, StringRecord, UnknownRecord };
@@ -5,7 +5,11 @@ import { Schema } from "effect";
5
5
  /** The 10 catalog + workspace + audit fields. @internal */
6
6
  const workspace = {
7
7
  catalogMode: {
8
- schema: Schema.Literal("strict", "prefer", "manual"),
8
+ schema: Schema.Literals([
9
+ "strict",
10
+ "prefer",
11
+ "manual"
12
+ ]),
9
13
  kind: "enum",
10
14
  strategy: "scalar",
11
15
  enforcement: "absent",
@@ -27,7 +31,7 @@ const workspace = {
27
31
  anchor: "cleanupunusedcatalogs"
28
32
  },
29
33
  linkWorkspacePackages: {
30
- schema: Schema.Union(Schema.Boolean, Schema.Literal("deep")),
34
+ schema: Schema.Union([Schema.Boolean, Schema.Literals(["deep"])]),
31
35
  kind: "union",
32
36
  strategy: "scalar",
33
37
  enforcement: "absent",
@@ -49,7 +53,7 @@ const workspace = {
49
53
  anchor: "preferworkspacepackages"
50
54
  },
51
55
  saveWorkspaceProtocol: {
52
- schema: Schema.Union(Schema.Boolean, Schema.Literal("rolling")),
56
+ schema: Schema.Union([Schema.Boolean, Schema.Literals(["rolling"])]),
53
57
  kind: "union",
54
58
  strategy: "scalar",
55
59
  enforcement: "absent",
@@ -98,7 +102,12 @@ const workspace = {
98
102
  anchor: "workspaceconcurrency"
99
103
  },
100
104
  auditLevel: {
101
- schema: Schema.Literal("low", "moderate", "high", "critical"),
105
+ schema: Schema.Literals([
106
+ "low",
107
+ "moderate",
108
+ "high",
109
+ "critical"
110
+ ]),
102
111
  kind: "enum",
103
112
  strategy: "scalar",
104
113
  enforcement: "absent",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rolldown-pnpm-config",
3
- "version": "0.2.2",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "description": "A dogfooding example of our plugin",
6
6
  "repository": {
@@ -33,26 +33,16 @@
33
33
  "rolldown-pnpm-config": "bin/rolldown-pnpm-config.js"
34
34
  },
35
35
  "dependencies": {
36
- "@effect/cli": "^0.75.2",
37
- "@effect/cluster": "^0.59.0",
38
- "@effect/experimental": "^0.60.0",
39
- "@effect/platform": "^0.96.2",
40
- "@effect/platform-node": "^0.107.0",
41
- "@effect/printer": "^0.49.0",
42
- "@effect/printer-ansi": "^0.49.0",
43
- "@effect/rpc": "^0.75.1",
44
- "@effect/sql": "^0.51.1",
45
- "@effect/typeclass": "^0.40.0",
46
- "@effect/workflow": "^0.18.2",
47
- "effect": "^3.21.4",
36
+ "@effect/platform-node": "4.0.0-beta.98",
37
+ "@effected/semver": "^0.1.0",
38
+ "@effected/yaml": "^0.1.0",
39
+ "effect": "4.0.0-beta.98",
48
40
  "ink": "^7.1.0",
49
41
  "ink-tab": "^5.2.0",
50
- "oxc-parser": "^0.139.0",
42
+ "oxc-parser": "^0.140.0",
51
43
  "react": "^19.2.7",
52
- "semver-effect": "^0.3.1",
53
44
  "std-env": "^4.2.0",
54
- "std-osc8": "^0.2.0",
55
- "yaml": "^2.9.0"
45
+ "std-osc8": "^0.2.0"
56
46
  },
57
47
  "peerDependencies": {
58
48
  "rolldown": "^1.1.0"
package/plugin/freeze.js CHANGED
@@ -48,7 +48,7 @@ function freeze(config) {
48
48
  return Effect.gen(function* () {
49
49
  const base = {};
50
50
  const manifest = {};
51
- base.catalogs = yield* Schema.decodeUnknown(CatalogsSchema)(normalizeCatalogs(config.catalogs)).pipe(Effect.mapError((error) => new ConfigError({ message: `Invalid catalogs: ${String(error)}` })));
51
+ base.catalogs = yield* Schema.decodeUnknownEffect(CatalogsSchema)(normalizeCatalogs(config.catalogs)).pipe(Effect.mapError((error) => new ConfigError({ message: `Invalid catalogs: ${String(error)}` })));
52
52
  manifest.catalogs = {
53
53
  strategy: "catalogs",
54
54
  enforcement: "warn"
@@ -60,7 +60,7 @@ function freeze(config) {
60
60
  if (raw === void 0) continue;
61
61
  const decl = normalizeField(raw);
62
62
  const schema = FIELD_SCHEMAS[field];
63
- base[field] = schema ? yield* Schema.decodeUnknown(schema)(decl.value).pipe(Effect.mapError((error) => new ConfigError({ message: `Invalid ${field}: ${String(error)}` }))) : decl.value;
63
+ base[field] = schema ? yield* Schema.decodeUnknownEffect(schema)(decl.value).pipe(Effect.mapError((error) => new ConfigError({ message: `Invalid ${field}: ${String(error)}` }))) : decl.value;
64
64
  manifest[field] = {
65
65
  strategy: reg.strategy,
66
66
  enforcement: decl.enforcement ?? reg.enforcement,
package/virtual.d.ts CHANGED
@@ -9,8 +9,18 @@
9
9
  // per-module boilerplate.
10
10
 
11
11
  declare module "rolldown-pnpm-config/virtual/pnpmfile" {
12
- import type { PnpmHooks } from "rolldown-pnpm-config/runtime";
13
- export const hooks: PnpmHooks;
12
+ // Inlined rather than imported from "rolldown-pnpm-config/runtime": this file is
13
+ // shipped byte-for-byte (never compiled), so any cross-module import here resolves
14
+ // against this package's own *source* `exports` map during the build's declaration
15
+ // pass and pulls a raw .ts file into API Extractor's analysis (ae-wrong-input-file-type).
16
+ // Keep this shape in sync with `PnpmConfig`/`PnpmHooks` in `src/runtime/types.ts`.
17
+ interface PnpmConfig {
18
+ catalogs?: Record<string, Record<string, string>>;
19
+ [key: string]: unknown;
20
+ }
21
+ export const hooks: {
22
+ updateConfig(config: PnpmConfig): PnpmConfig;
23
+ };
14
24
  }
15
25
 
16
26
  declare module "rolldown-pnpm-config/virtual/catalogs" {