-
-
Notifications
You must be signed in to change notification settings - Fork 11.9k
Expand file tree
/
Copy path.pnpmfile.mjs
More file actions
149 lines (129 loc) · 5.36 KB
/
Copy path.pnpmfile.mjs
File metadata and controls
149 lines (129 loc) · 5.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import { glob, readFile } from 'node:fs/promises';
// Global pnpm hooks for the Ghost monorepo.
//
// `beforePacking` runs during `pnpm pack` / `pnpm publish` and mutates the
// package.json written *into the tarball* — the on-disk manifest is never
// touched. `readPackage` runs during resolution, so it *does* feed the shared
// lockfile.
//
// Applied to every packed/published package:
// - drop `nx` — Nx target config, meaningless to consumers
// - drop `devDependencies` — never installed from a dependency tarball; inert
// in a published manifest and only adds noise + phantom workspace refs
//
// Applied to the `ghost` package only (the Ghost-CLI release archive built by
// ghost/core/scripts/pack.mjs):
// - rewrite its workspace deps to the bundled `file:components/*.tgz`
// tarballs shipped in the archive (name→filename map via GHOST_COMPONENTS)
// - strip `scripts` to the runtime set — Ghost-CLI starts Ghost with `node`,
// not pnpm scripts, and the dev/build/test/lint scripts reference stripped
// devDependencies
//
// (`packageManager` is carried over separately, post-pack, by pack.js.)
// Scripts retained in the packaged `ghost` manifest. Empty today: Ghost has no
// runtime pnpm scripts. Add names here if that changes.
const GHOST_RUNTIME_SCRIPTS = new Set([]);
function beforePacking(pkg) {
delete pkg.nx;
delete pkg.devDependencies;
if (pkg.exports) {
// remove any source condition exports, since the packages don't ship
// the source files
for (const key of Object.keys(pkg.exports)) {
if (typeof pkg.exports[key] === 'object' && pkg.exports[key].source) {
delete pkg.exports[key].source;
}
}
}
if (pkg.name !== 'ghost') {
return pkg;
}
const components = JSON.parse(process.env.GHOST_COMPONENTS || '{}');
for (const section of ['dependencies', 'optionalDependencies']) {
if (!pkg[section]) {
continue;
}
for (const name of Object.keys(pkg[section])) {
if (components[name]) {
pkg[section][name] = `file:components/${components[name]}`;
}
}
}
if (pkg.scripts) {
pkg.scripts = Object.fromEntries(
Object.entries(pkg.scripts).filter(([name]) => GHOST_RUNTIME_SCRIPTS.has(name)),
);
}
return pkg;
}
function readPackage(pkg) {
// consolidate declares 48 template engines as optional peers. pnpm links any
// that another workspace package happens to satisfy, so react, react-dom and
// @babel/core rode into ghost's production deploy closure via
// nodemailer-mailgun-transport — the only thing that pulls consolidate in, and
// it never renders through it. packageExtensions can only add, so dropping the
// peers outright needs this hook.
if (pkg.name === 'consolidate') {
delete pkg.peerDependencies;
delete pkg.peerDependenciesMeta;
}
// knex declares sqlite3 as an optional peer dep, and we don't use it/don't
// want to install it in production, so we'll remove it from the knex peer
// deps
if (pkg.name === 'knex') {
delete pkg.peerDependencies?.sqlite3;
delete pkg.peerDependenciesMeta?.sqlite3;
}
// these deps pull in typescript as an optional peer dep, which ends up
// being included in Ghost's production image because of the way pnpm hoists
// optional peers. We don't want to ship ts in the prod image so we delete
// it from the manifest
//
// NOTE: auto-install-peers: false doesn't solve the problem here unfortunately,
// and it causes more issues with other deps
if (['viem', 'ox', 'abitype'].includes(pkg.name)) {
delete pkg.peerDependencies?.typescript;
delete pkg.peerDependenciesMeta?.typescript;
}
// abitype's zod peer is only used by its `abitype/zod` subpath, which nothing
// in the tree imports. Left in place it peer-forks abitype and everything
// above it: mppx resolves that chain against zod 4 and @x402/* against zod 3,
// so ghost's production closure carried two identical copies of viem (~2.9k
// files each), ox and abitype.
if (pkg.name === 'abitype') {
delete pkg.peerDependencies?.zod;
delete pkg.peerDependenciesMeta?.zod;
}
return pkg;
}
/**
* Dynamic config update function to automatically exclude "private" packages
* from pnpm's changelog detection. We can't remove the version fields
* because that would break workspace resolution, but we can dynamically add them
* to the versioning.ignore list so that they don't trigger changelog generation.
*/
async function updateConfig(config) {
const { packages, versioning = {} } = config;
const ignoredPackages = new Set(versioning.ignore ?? []);
// step 1: enumerate all workspace packages with glob
const exclude = packages.filter((p) => p.startsWith('!')).map((p) => p.slice(1));
const patterns = packages.filter((p) => !p.startsWith('!')).map((p) => `${p}/package.json`);
const files = await Array.fromAsync(glob(patterns, { exclude }));
// step 2: read each package.json and check for "private", if so add to
// the ignore set
await Promise.all(
files.map(async (file) => {
const pkg = JSON.parse(await readFile(file, 'utf-8'));
if (pkg.private) {
ignoredPackages.add(pkg.name);
}
}),
);
// step 3: update the config with the new ignore list
config.versioning = {
...versioning,
ignore: Array.from(ignoredPackages),
};
return config;
}
export const hooks = { beforePacking, readPackage, updateConfig };