rolldown-pnpm-config 0.1.0 → 0.2.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/README.md +15 -0
- package/bin/rolldown-pnpm-config.js +1 -1
- package/cli/commands/export.js +38 -5
- package/cli/local-merge.js +3 -2
- package/cli/plan.js +2 -1
- package/index.d.ts +18 -4
- package/package.json +12 -4
- package/patches/build.js +44 -0
- package/patches/discover.js +39 -0
- package/patches/keys.js +17 -0
- package/patches/paths.js +29 -0
- package/patches/reconcile.js +27 -0
- package/plugin/index.js +2 -1
package/README.md
CHANGED
|
@@ -113,6 +113,20 @@ publicHoistPattern: {
|
|
|
113
113
|
|
|
114
114
|
See [exporting to pnpm-workspace.yaml](https://github.com/spencerbeggs/rolldown-pnpm-config/blob/main/docs/06-exporting.md) for the full surface.
|
|
115
115
|
|
|
116
|
+
## Distributing dependency patches
|
|
117
|
+
|
|
118
|
+
A plugin author can ship pnpm dependency patches through the plugin. Author a `.patch` with stock pnpm into `public/patches/` — the bundler copies `public/` into the published package, so the file travels with the plugin. At build the plugin discovers each patch, rewrites its path to the consumer-resolved `node_modules/.pnpm-config/<name>/patches/<file>.patch` and bakes it into the emitted pnpmfile. Every consumer then applies it automatically through `updateConfig`, with no hand-written `patchedDependencies` entry:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
export const plugin = {
|
|
122
|
+
name: "@acme/pnpm-config",
|
|
123
|
+
// default once public/patches/ has files; a bare map is the manual escape hatch
|
|
124
|
+
patchedDependencies: { strategy: "rewrite" },
|
|
125
|
+
} satisfies PluginConfig;
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
A sibling `patches/` folder stays local to your repo and never ships. On `rolldown-pnpm-config export` your own `pnpm-workspace.yaml` gets the patches with their local on-disk paths, merged by key so sibling and hand-written entries survive. See [distributing dependency patches](https://github.com/spencerbeggs/rolldown-pnpm-config/blob/main/docs/07-distributing-patches.md) for the full surface.
|
|
129
|
+
|
|
116
130
|
## Documentation
|
|
117
131
|
|
|
118
132
|
- [Getting started](https://github.com/spencerbeggs/rolldown-pnpm-config/blob/main/docs/01-getting-started.md) — Wire the plugin into a vanilla rolldown build and emit a pnpmfile.
|
|
@@ -121,6 +135,7 @@ See [exporting to pnpm-workspace.yaml](https://github.com/spencerbeggs/rolldown-
|
|
|
121
135
|
- [pnpm settings coverage](https://github.com/spencerbeggs/rolldown-pnpm-config/blob/main/docs/04-pnpm-settings-coverage.md) — Every pnpm-workspace.yaml setting the plugin manages and the ones it leaves to each consumer.
|
|
122
136
|
- [Upgrading catalogs](https://github.com/spencerbeggs/rolldown-pnpm-config/blob/main/docs/05-upgrading-catalogs.md) — The `upgrade` CLI that rewrites catalog version ranges in place.
|
|
123
137
|
- [Exporting to pnpm-workspace.yaml](https://github.com/spencerbeggs/rolldown-pnpm-config/blob/main/docs/06-exporting.md) — The `export` and `preview` CLI, the `local` merge directive and per-repo `excludeByRepo` filtering.
|
|
138
|
+
- [Distributing dependency patches](https://github.com/spencerbeggs/rolldown-pnpm-config/blob/main/docs/07-distributing-patches.md) — Ship pnpm patches through the plugin so every consumer applies them automatically.
|
|
124
139
|
|
|
125
140
|
## License
|
|
126
141
|
|
|
@@ -14,7 +14,7 @@ const root = Command.make("rolldown-pnpm-config").pipe(Command.withSubcommands([
|
|
|
14
14
|
]));
|
|
15
15
|
Command.run(root, {
|
|
16
16
|
name: "rolldown-pnpm-config",
|
|
17
|
-
version: "0.1
|
|
17
|
+
version: "0.2.1"
|
|
18
18
|
})(process.argv).pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain);
|
|
19
19
|
|
|
20
20
|
//#endregion
|
package/cli/commands/export.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { discoverPatches } from "../../patches/discover.js";
|
|
2
|
+
import { isRewriteDirective, readLocalPatchesDir, withResolvedBuildPatches } from "../../patches/build.js";
|
|
1
3
|
import { DESCRIPTORS } from "../../descriptors/index.js";
|
|
2
4
|
import { freeze } from "../../plugin/freeze.js";
|
|
3
5
|
import { resolveRootName } from "../../runtime/ctx.js";
|
|
6
|
+
import { reconcilePatches } from "../../patches/reconcile.js";
|
|
4
7
|
import { buildDiff } from "../diff/build.js";
|
|
5
8
|
import { renderExportDiff } from "../diff/render.js";
|
|
6
9
|
import { effectiveManaged } from "../effective.js";
|
|
@@ -12,7 +15,7 @@ import { canonicalize, findWorkspaceFile, parseWorkspace, renderWorkspace } from
|
|
|
12
15
|
import { overlayWorkspace } from "../workspace-overlay.js";
|
|
13
16
|
import { Data, Effect, Option } from "effect";
|
|
14
17
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
15
|
-
import { dirname, join } from "node:path";
|
|
18
|
+
import { dirname, join, relative } from "node:path";
|
|
16
19
|
import { Args, Command, Options } from "@effect/cli";
|
|
17
20
|
|
|
18
21
|
//#region src/cli/commands/export.ts
|
|
@@ -45,7 +48,7 @@ function runExport(opts) {
|
|
|
45
48
|
}), opts.configFile);
|
|
46
49
|
if (config === null) return yield* Effect.fail(new ExportError({ message: `No PnpmConfigPlugin call found in ${opts.configFile}` }));
|
|
47
50
|
if (errors.length > 0) return yield* Effect.fail(new ExportError({ message: `Non-literal config values: ${errors.join("; ")}` }));
|
|
48
|
-
const { base, manifest } = yield* freeze(config).pipe(Effect.mapError((e) => new ExportError({ message: e.message })));
|
|
51
|
+
const { base, manifest } = yield* freeze(withResolvedBuildPatches(config, dirname(opts.configFile))).pipe(Effect.mapError((e) => new ExportError({ message: e.message })));
|
|
49
52
|
const managed = {};
|
|
50
53
|
for (const [k, v] of Object.entries(base)) if (WORKSPACE_FIELDS.has(k)) managed[k] = v;
|
|
51
54
|
const path = opts.workspacePath ?? findWorkspaceFile(process.cwd()) ?? join(process.cwd(), "pnpm-workspace.yaml");
|
|
@@ -54,7 +57,33 @@ function runExport(opts) {
|
|
|
54
57
|
catch: (e) => new ExportError({ message: `Cannot read or parse ${path}: ${String(e)}` })
|
|
55
58
|
}) : {};
|
|
56
59
|
const rootName = resolveRootName({ dir: dirname(path) });
|
|
57
|
-
const
|
|
60
|
+
const effective = effectiveManaged(managed, config.local && typeof config.local === "object" ? config.local : void 0, parsed, manifest, rootName);
|
|
61
|
+
const rawPatched = config.patchedDependencies;
|
|
62
|
+
const explicitPatchMap = rawPatched !== void 0 && !isRewriteDirective(rawPatched);
|
|
63
|
+
const contributed = {};
|
|
64
|
+
if (explicitPatchMap) {
|
|
65
|
+
const explicit = effective.patchedDependencies;
|
|
66
|
+
if (explicit !== null && typeof explicit === "object" && !Array.isArray(explicit)) Object.assign(contributed, explicit);
|
|
67
|
+
} else {
|
|
68
|
+
const localPatchesDir = readLocalPatchesDir(config);
|
|
69
|
+
const owned = discoverPatches({
|
|
70
|
+
baseDir: dirname(opts.configFile),
|
|
71
|
+
name: typeof config.name === "string" ? config.name : "",
|
|
72
|
+
...localPatchesDir !== void 0 ? { localPatchesDir } : {}
|
|
73
|
+
});
|
|
74
|
+
const workspaceRoot = dirname(path);
|
|
75
|
+
for (const p of owned) contributed[p.key] = relative(workspaceRoot, p.absPath).split(/[\\/]/).join("/");
|
|
76
|
+
}
|
|
77
|
+
if (Object.keys(contributed).length > 0) effective.patchedDependencies = {
|
|
78
|
+
...(parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed.patchedDependencies : void 0) ?? {},
|
|
79
|
+
...contributed
|
|
80
|
+
};
|
|
81
|
+
const report = reconcilePatches({
|
|
82
|
+
parsedPatched: effective.patchedDependencies ?? {},
|
|
83
|
+
root: dirname(path),
|
|
84
|
+
exists: existsSync
|
|
85
|
+
});
|
|
86
|
+
const merged = overlayWorkspace(effective, parsed);
|
|
58
87
|
const rendered = renderWorkspace(merged);
|
|
59
88
|
const localKeys = new Set(config.local && typeof config.local === "object" ? Object.keys(config.local) : []);
|
|
60
89
|
const diff = renderExportDiff(buildDiff(canonicalize(parsed), canonicalize(merged), {
|
|
@@ -65,7 +94,8 @@ function runExport(opts) {
|
|
|
65
94
|
path,
|
|
66
95
|
rendered,
|
|
67
96
|
written: false,
|
|
68
|
-
diff
|
|
97
|
+
diff,
|
|
98
|
+
report
|
|
69
99
|
};
|
|
70
100
|
yield* Effect.try({
|
|
71
101
|
try: () => writeFileSync(path, rendered, "utf8"),
|
|
@@ -75,7 +105,8 @@ function runExport(opts) {
|
|
|
75
105
|
path,
|
|
76
106
|
rendered,
|
|
77
107
|
written: true,
|
|
78
|
-
diff
|
|
108
|
+
diff,
|
|
109
|
+
report
|
|
79
110
|
};
|
|
80
111
|
});
|
|
81
112
|
}
|
|
@@ -111,6 +142,8 @@ const exportCommand = Command.make("export", {
|
|
|
111
142
|
process.stdout.write(`${toAnsi(result.diff, { color: caps.color })}\n`);
|
|
112
143
|
process.stdout.write("\n+ added ~ changed - removed (local) local override (unmanaged) not managed\n");
|
|
113
144
|
} else process.stdout.write(`Exported to ${result.path}\n`);
|
|
145
|
+
for (const k of result.report.staleEntries) process.stderr.write(`warning: patch entry "${k}" has no file on disk\n`);
|
|
146
|
+
for (const k of result.report.keyMismatches) process.stderr.write(`warning: patch entry "${k}" does not match its filename\n`);
|
|
114
147
|
});
|
|
115
148
|
})).pipe(Command.withDescription("Materialize the plugin config into pnpm-workspace.yaml (--dry-run to preview)"));
|
|
116
149
|
|
package/cli/local-merge.js
CHANGED
|
@@ -56,8 +56,9 @@ function combine(managed, value, strategy) {
|
|
|
56
56
|
function applyLocalDirective(managed, raw, parsed, field) {
|
|
57
57
|
const directive = isLocalDirective(raw) ? raw : { value: raw };
|
|
58
58
|
let result;
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
const strat = directive.strategy === "merge" ? "union" : directive.strategy;
|
|
60
|
+
if (strat && strat !== "rewrite" && directive.value !== void 0) result = combine(managed, directive.value, strat);
|
|
61
|
+
else if (directive.value !== void 0 && directive.strategy !== "rewrite") result = directive.value;
|
|
61
62
|
else result = managed;
|
|
62
63
|
if (field === "overrides") {
|
|
63
64
|
const protocols = directive.preserve ?? DEFAULT_PRESERVE;
|
package/cli/plan.js
CHANGED
|
@@ -24,7 +24,8 @@ function planEntry(entry, versions) {
|
|
|
24
24
|
}
|
|
25
25
|
parsed.sort((a, b) => a.compare(b));
|
|
26
26
|
const maxOf = (list) => list.length ? list[list.length - 1] : null;
|
|
27
|
-
const
|
|
27
|
+
const currentStripped = entry.currentRange.replace(/^[\^~]/, "");
|
|
28
|
+
const current = yield* parseOrNull(currentStripped);
|
|
28
29
|
const currentMajor = current?.major ?? 0;
|
|
29
30
|
const inRangeMax = range ? maxOf(parsed.filter((v) => range.test(v))) : null;
|
|
30
31
|
const overallMax = maxOf(parsed);
|
package/index.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ type FieldInput<T> = T | {
|
|
|
60
60
|
interface LocalDirective<T> {
|
|
61
61
|
readonly preserve?: readonly string[];
|
|
62
62
|
readonly value?: T;
|
|
63
|
-
readonly strategy?: "union" | "difference";
|
|
63
|
+
readonly strategy?: "union" | "difference" | "merge" | "rewrite";
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
66
|
* The declarative plugin configuration.
|
|
@@ -83,7 +83,14 @@ interface PluginConfig {
|
|
|
83
83
|
* generated field when running `rolldown-pnpm-config export`. Ignored by the
|
|
84
84
|
* build and the shipped pnpmfile.
|
|
85
85
|
*/
|
|
86
|
-
readonly local?: {
|
|
86
|
+
readonly local?: {
|
|
87
|
+
/**
|
|
88
|
+
* Override the local discovery root for distributed patches (default
|
|
89
|
+
* `public/patches/` adjacent to the build file). The local-only `patches/`
|
|
90
|
+
* folder detection is independent and unaffected.
|
|
91
|
+
*/
|
|
92
|
+
readonly localPatchesDir?: string;
|
|
93
|
+
} & { readonly [K in keyof PluginConfig]?: PluginConfig[K] | LocalDirective<PluginConfig[K]> };
|
|
87
94
|
/** Whether pnpm prompts before purging `node_modules`. */
|
|
88
95
|
readonly confirmModulesPurge?: FieldInput<boolean>;
|
|
89
96
|
/** Per-package manifest overrides merged into the dependency graph. */
|
|
@@ -242,8 +249,15 @@ interface PluginConfig {
|
|
|
242
249
|
readonly shellEmulator?: FieldInput<boolean>;
|
|
243
250
|
/** Scripts that must exist in every project matching the current filter. */
|
|
244
251
|
readonly requiredScripts?: FieldInput<string[]>;
|
|
245
|
-
/**
|
|
246
|
-
|
|
252
|
+
/**
|
|
253
|
+
* Patches applied to dependencies, keyed by package identifier. Pass a plain
|
|
254
|
+
* map for explicit control, or `{ strategy: "rewrite" }` (the default when
|
|
255
|
+
* `public/patches/` contains files) to auto-discover and rewrite patch paths
|
|
256
|
+
* to their distributed `node_modules/.pnpm-config/<name>/` location.
|
|
257
|
+
*/
|
|
258
|
+
readonly patchedDependencies?: FieldInput<Record<string, string>> | {
|
|
259
|
+
readonly strategy: "rewrite";
|
|
260
|
+
};
|
|
247
261
|
/** Whether unused patches (patches that apply to no installed package) are allowed. */
|
|
248
262
|
readonly allowUnusedPatches?: FieldInput<boolean>;
|
|
249
263
|
/** Whether non-applied patches (patches that fail to apply) are allowed. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rolldown-pnpm-config",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A dogfooding example of our plugin",
|
|
6
6
|
"repository": {
|
|
@@ -34,16 +34,24 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@effect/cli": "^0.75.2",
|
|
37
|
+
"@effect/cluster": "^0.59.0",
|
|
38
|
+
"@effect/experimental": "^0.60.0",
|
|
37
39
|
"@effect/platform": "^0.96.2",
|
|
38
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",
|
|
39
47
|
"effect": "^3.21.4",
|
|
40
48
|
"ink": "^7.1.0",
|
|
41
49
|
"ink-tab": "^5.2.0",
|
|
42
|
-
"oxc-parser": "^0.
|
|
50
|
+
"oxc-parser": "^0.138.0",
|
|
43
51
|
"react": "^19.2.7",
|
|
44
|
-
"semver-effect": "^0.
|
|
52
|
+
"semver-effect": "^0.3.1",
|
|
45
53
|
"std-env": "^4.1.0",
|
|
46
|
-
"std-osc8": "^0.
|
|
54
|
+
"std-osc8": "^0.2.0",
|
|
47
55
|
"yaml": "^2.9.0"
|
|
48
56
|
},
|
|
49
57
|
"peerDependencies": {
|
package/patches/build.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { discoverPatches } from "./discover.js";
|
|
2
|
+
|
|
3
|
+
//#region src/patches/build.ts
|
|
4
|
+
/** True only for the `{ strategy: "rewrite" }` directive. @internal */
|
|
5
|
+
function isRewriteDirective(v) {
|
|
6
|
+
return v !== null && typeof v === "object" && !Array.isArray(v) && Object.keys(v).length === 1 && v.strategy === "rewrite";
|
|
7
|
+
}
|
|
8
|
+
/** Read `local.localPatchesDir` when it is a string. @internal */
|
|
9
|
+
function readLocalPatchesDir(config) {
|
|
10
|
+
const local = config.local;
|
|
11
|
+
return typeof local?.localPatchesDir === "string" ? local.localPatchesDir : void 0;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolve build-time `patchedDependencies`. When the field is absent or the
|
|
15
|
+
* `{ strategy: "rewrite" }` directive, run discovery and inject the distributed
|
|
16
|
+
* map (`name`-scoped `.pnpm-config` paths) so `freeze` sees a plain map. An
|
|
17
|
+
* explicit map / wrapped value passes through untouched.
|
|
18
|
+
*
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
function withResolvedBuildPatches(config, baseDir) {
|
|
22
|
+
const raw = config.patchedDependencies;
|
|
23
|
+
if (raw !== void 0 && !isRewriteDirective(raw)) return config;
|
|
24
|
+
const localPatchesDir = readLocalPatchesDir(config);
|
|
25
|
+
const distributed = discoverPatches({
|
|
26
|
+
baseDir,
|
|
27
|
+
name: config.name,
|
|
28
|
+
...localPatchesDir !== void 0 ? { localPatchesDir } : {}
|
|
29
|
+
}).filter((p) => p.distributed);
|
|
30
|
+
if (distributed.length === 0) {
|
|
31
|
+
if (raw === void 0) return config;
|
|
32
|
+
const { patchedDependencies: _drop, ...rest } = config;
|
|
33
|
+
return rest;
|
|
34
|
+
}
|
|
35
|
+
const map = {};
|
|
36
|
+
for (const p of distributed) map[p.key] = p.distributedPath;
|
|
37
|
+
return {
|
|
38
|
+
...config,
|
|
39
|
+
patchedDependencies: map
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { isRewriteDirective, readLocalPatchesDir, withResolvedBuildPatches };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { patchKeyFromFileName } from "./keys.js";
|
|
2
|
+
import { distributedPatchPath, distributedRel } from "./paths.js";
|
|
3
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
4
|
+
import { isAbsolute, join } from "node:path";
|
|
5
|
+
|
|
6
|
+
//#region src/patches/discover.ts
|
|
7
|
+
/**
|
|
8
|
+
* Discover owned patches in the two convention folders adjacent to the build
|
|
9
|
+
* file: `public/patches/` (distributed, rewritten) and `patches/` (local-only).
|
|
10
|
+
* `localPatchesDir` overrides the distributed source root only. Read-only.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
function discoverPatches(opts) {
|
|
15
|
+
const distRoot = opts.localPatchesDir ? isAbsolute(opts.localPatchesDir) ? opts.localPatchesDir : join(opts.baseDir, opts.localPatchesDir) : join(opts.baseDir, "public", "patches");
|
|
16
|
+
const localOnlyRoot = join(opts.baseDir, "patches");
|
|
17
|
+
const out = [];
|
|
18
|
+
collect(distRoot, true);
|
|
19
|
+
collect(localOnlyRoot, false);
|
|
20
|
+
return out;
|
|
21
|
+
function collect(dir, distributed) {
|
|
22
|
+
if (!existsSync(dir)) return;
|
|
23
|
+
for (const fileName of readdirSync(dir).sort()) {
|
|
24
|
+
const key = patchKeyFromFileName(fileName);
|
|
25
|
+
if (key === null) continue;
|
|
26
|
+
const absPath = join(dir, fileName);
|
|
27
|
+
out.push({
|
|
28
|
+
key,
|
|
29
|
+
fileName,
|
|
30
|
+
distributed,
|
|
31
|
+
absPath,
|
|
32
|
+
...distributed ? { distributedPath: distributedPatchPath(opts.name, distributedRel(opts.baseDir, distRoot, fileName)) } : {}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
export { discoverPatches };
|
package/patches/keys.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/patches/keys.ts
|
|
2
|
+
/**
|
|
3
|
+
* Derive the `patchedDependencies` key from a `.patch` filename, reversing pnpm's
|
|
4
|
+
* `/`→`__` mangling (`@scope__pkg@1.0.0.patch` → `@scope/pkg@1.0.0`). Returns
|
|
5
|
+
* `null` when the name does not end in `.patch` or has an empty stem.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
function patchKeyFromFileName(fileName) {
|
|
10
|
+
if (!fileName.endsWith(".patch")) return null;
|
|
11
|
+
const stem = fileName.slice(0, -6);
|
|
12
|
+
if (stem.length === 0) return null;
|
|
13
|
+
return stem.replace(/__/g, "/");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
17
|
+
export { patchKeyFromFileName };
|
package/patches/paths.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { basename, join, posix, relative } from "node:path";
|
|
2
|
+
|
|
3
|
+
//#region src/patches/paths.ts
|
|
4
|
+
/**
|
|
5
|
+
* Consumer-resolved distributed patch path for a config dependency:
|
|
6
|
+
* `node_modules/.pnpm-config/<name>/<rel>`, POSIX separators. `<name>` is used
|
|
7
|
+
* verbatim (a scoped name keeps its `/`). Verify the prefix against a real
|
|
8
|
+
* install — see plan Task 1.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
function distributedPatchPath(name, rel) {
|
|
13
|
+
const segments = rel.split(/[\\/]/).filter(Boolean);
|
|
14
|
+
return posix.join("node_modules", ".pnpm-config", name, ...segments);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The patch's path relative to the bundler's `public/` directory — the subpath
|
|
18
|
+
* the bundler preserves when copying `public/` into `dist/`. Falls back to
|
|
19
|
+
* `<basename(distRoot)>/<fileName>` when `distRoot` is not under `public/`.
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
function distributedRel(baseDir, distRoot, fileName) {
|
|
24
|
+
const rel = relative(join(baseDir, "public"), distRoot);
|
|
25
|
+
return `${(rel === "" || rel.startsWith("..") ? basename(distRoot) : rel).split(/[\\/]/).filter(Boolean).join("/")}/${fileName}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { distributedPatchPath, distributedRel };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { patchKeyFromFileName } from "./keys.js";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
|
|
4
|
+
//#region src/patches/reconcile.ts
|
|
5
|
+
/**
|
|
6
|
+
* Report `patchedDependencies` entries whose file is missing (`staleEntries`) or
|
|
7
|
+
* whose key does not derive from its filename (`keyMismatches`). `exists` is
|
|
8
|
+
* injected so the function stays pure and testable.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
function reconcilePatches(args) {
|
|
13
|
+
const staleEntries = [];
|
|
14
|
+
const keyMismatches = [];
|
|
15
|
+
for (const [key, rel] of Object.entries(args.parsedPatched)) {
|
|
16
|
+
if (!args.exists(join(args.root, rel))) staleEntries.push(key);
|
|
17
|
+
const derived = patchKeyFromFileName(basename(rel));
|
|
18
|
+
if (derived !== null && derived !== key) keyMismatches.push(key);
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
staleEntries,
|
|
22
|
+
keyMismatches
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
export { reconcilePatches };
|
package/plugin/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withResolvedBuildPatches } from "../patches/build.js";
|
|
1
2
|
import { freeze } from "./freeze.js";
|
|
2
3
|
import { emitCatalogsModule, emitPnpmfileModule } from "./serialize.js";
|
|
3
4
|
import { Effect } from "effect";
|
|
@@ -14,7 +15,7 @@ const CATALOGS_SPEC = "rolldown-pnpm-config/virtual/catalogs";
|
|
|
14
15
|
*/
|
|
15
16
|
function createPnpmConfigPlugin(config, deps = { freeze }) {
|
|
16
17
|
let frozen;
|
|
17
|
-
const getFrozen = () => frozen ??= Effect.runPromise(deps.freeze(config));
|
|
18
|
+
const getFrozen = () => frozen ??= Effect.runPromise(deps.freeze(withResolvedBuildPatches(config, process.cwd())));
|
|
18
19
|
return {
|
|
19
20
|
name: "rolldown-pnpm-config",
|
|
20
21
|
resolveId(source) {
|