Prune a pnpm-lock.yaml by removing unused dependencies and packages from the dependency graph
npx @tessl/cli install tessl/npm-pnpm--lockfile--pruner@0.0.00
# PNPM Lockfile Pruner
1
2
PNPM Lockfile Pruner provides functionality for pruning pnpm lockfiles (pnpm-lock.yaml files) by removing unused dependencies and packages from the dependency graph. It offers two main functions for pruning shared lockfiles across multiple workspace projects and individual project lockfiles, while maintaining lockfile integrity and handling optional dependencies appropriately.
3
4
## Package Information
5
6
- **Package Name**: @pnpm/lockfile.pruner
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `pnpm add @pnpm/lockfile.pruner`
10
11
## Core Imports
12
13
```typescript
14
import { pruneSharedLockfile, pruneLockfile } from "@pnpm/lockfile.pruner";
15
```
16
17
For CommonJS:
18
19
```javascript
20
const { pruneSharedLockfile, pruneLockfile } = require("@pnpm/lockfile.pruner");
21
```
22
23
All types from @pnpm/lockfile.types are also re-exported:
24
25
```typescript
26
import {
27
Lockfile,
28
PackageSnapshots,
29
ProjectSnapshot,
30
ResolvedDependencies
31
} from "@pnpm/lockfile.pruner";
32
```
33
34
## Basic Usage
35
36
```typescript
37
import { pruneLockfile, pruneSharedLockfile, type Lockfile } from "@pnpm/lockfile.pruner";
38
import { type PackageManifest, type ProjectId } from "@pnpm/types";
39
40
// Prune a lockfile for a specific project
41
const prunedLockfile = pruneLockfile(
42
lockfile, // The lockfile to prune
43
packageManifest, // The project's package.json content
44
'.' as ProjectId, // Project identifier (usually '.' for root)
45
{
46
warn: (msg: string) => console.warn(msg)
47
}
48
);
49
50
// Prune a shared lockfile (removes unused packages across all projects)
51
const prunedSharedLockfile = pruneSharedLockfile(lockfile, {
52
warn: (msg: string) => console.warn(msg)
53
});
54
```
55
56
## Capabilities
57
58
### Project-Specific Lockfile Pruning
59
60
Prunes a lockfile for a specific project/importer by removing packages not needed by that project while preserving necessary dependencies.
61
62
```typescript { .api }
63
/**
64
* Prunes a lockfile for a specific project/importer by removing packages
65
* not needed by that importer
66
* @param lockfile - The lockfile to prune
67
* @param pkg - The package.json manifest for the project
68
* @param importerId - The project ID (path identifier)
69
* @param opts - Pruning options
70
* @returns Pruned lockfile with unused packages removed
71
*/
72
function pruneLockfile(
73
lockfile: Lockfile,
74
pkg: PackageManifest,
75
importerId: ProjectId,
76
opts: {
77
warn?: (msg: string) => void;
78
dependenciesGraph?: DependenciesGraph;
79
}
80
): Lockfile;
81
```
82
83
### Shared Lockfile Pruning
84
85
Prunes shared lockfiles by removing unused packages while preserving packages needed by any importer in a workspace.
86
87
```typescript { .api }
88
/**
89
* Prunes shared lockfiles by removing unused packages while preserving
90
* packages needed by any importer
91
* @param lockfile - The lockfile to prune
92
* @param opts - Pruning options
93
* @returns Pruned lockfile with unused packages removed
94
*/
95
function pruneSharedLockfile(
96
lockfile: Lockfile,
97
opts?: {
98
dependenciesGraph?: DependenciesGraph;
99
warn?: (msg: string) => void;
100
}
101
): Lockfile;
102
```
103
104
## Types
105
106
### Core Lockfile Types
107
108
```typescript { .api }
109
interface Lockfile {
110
importers: Record<ProjectId, ProjectSnapshot>;
111
lockfileVersion: string;
112
packages?: PackageSnapshots;
113
time?: Record<string, string>;
114
catalogs?: CatalogSnapshots;
115
overrides?: Record<string, string>;
116
packageExtensionsChecksum?: string;
117
ignoredOptionalDependencies?: string[];
118
patchedDependencies?: Record<string, PatchFile>;
119
pnpmfileChecksum?: string;
120
settings?: LockfileSettings;
121
}
122
123
interface ProjectSnapshot {
124
specifiers: ResolvedDependencies;
125
dependencies?: ResolvedDependencies;
126
optionalDependencies?: ResolvedDependencies;
127
devDependencies?: ResolvedDependencies;
128
dependenciesMeta?: DependenciesMeta;
129
publishDirectory?: string;
130
}
131
132
interface PackageSnapshots {
133
[packagePath: DepPath]: PackageSnapshot;
134
}
135
136
interface PackageSnapshot {
137
resolution: LockfileResolution;
138
id?: string;
139
optional?: true;
140
patched?: true;
141
hasBin?: true;
142
name?: string;
143
version?: string;
144
dependencies?: ResolvedDependencies;
145
optionalDependencies?: ResolvedDependencies;
146
peerDependencies?: Record<string, string>;
147
peerDependenciesMeta?: Record<string, { optional: true }>;
148
transitivePeerDependencies?: string[];
149
bundledDependencies?: string[] | boolean;
150
engines?: Record<string, string> & { node: string };
151
os?: string[];
152
cpu?: string[];
153
libc?: string[];
154
deprecated?: string;
155
}
156
157
type ResolvedDependencies = Record<string, string>;
158
```
159
160
### Resolution Types
161
162
```typescript { .api }
163
type LockfileResolution = Resolution | { integrity: string };
164
165
type Resolution =
166
| TarballResolution
167
| GitRepositoryResolution
168
| DirectoryResolution;
169
170
interface TarballResolution {
171
type?: undefined;
172
tarball: string;
173
integrity?: string;
174
path?: string;
175
}
176
177
interface GitRepositoryResolution {
178
type: "git";
179
repo: string;
180
commit: string;
181
path?: string;
182
}
183
184
interface DirectoryResolution {
185
type: "directory";
186
directory: string;
187
}
188
```
189
190
### Supporting Types
191
192
```typescript { .api }
193
interface CatalogSnapshots {
194
[catalogName: string]: { [dependencyName: string]: ResolvedCatalogEntry };
195
}
196
197
interface ResolvedCatalogEntry {
198
readonly specifier: string; // The ^1.2.3 portion of catalog entry
199
readonly version: string; // The concrete resolved version: 1.2.3
200
}
201
202
interface LockfileSettings {
203
autoInstallPeers?: boolean;
204
excludeLinksFromLockfile?: boolean;
205
peersSuffixMaxLength?: number;
206
}
207
208
interface DependenciesMeta {
209
[dependencyName: string]: {
210
injected?: boolean;
211
node?: string;
212
patch?: string;
213
};
214
}
215
216
type DependenciesGraph = Record<DepPath, { optional?: boolean }>;
217
```
218
219
### Imported Types
220
221
```typescript { .api }
222
// From @pnpm/types
223
type DepPath = string & { __brand: "DepPath" };
224
type ProjectId = string & { __brand: "ProjectId" };
225
226
interface PackageManifest {
227
name?: string;
228
version?: string;
229
dependencies?: Record<string, string>;
230
devDependencies?: Record<string, string>;
231
optionalDependencies?: Record<string, string>;
232
peerDependencies?: Record<string, string>;
233
bundledDependencies?: string[];
234
// ... other package.json fields
235
}
236
237
// From @pnpm/patching.types (re-exported)
238
interface PatchFile {
239
path: string;
240
hash: string;
241
}
242
```
243
244
## Error Handling
245
246
Both pruning functions accept an optional `warn` callback that will be called when:
247
- A package resolution cannot be found in the lockfile
248
- Missing dependencies are encountered during pruning
249
- Inconsistencies in the dependency graph are detected
250
251
```typescript
252
const result = pruneLockfile(lockfile, pkg, importerId, {
253
warn: (message: string) => {
254
console.warn(`Pruning warning: ${message}`);
255
// Log to external system, accumulate errors, etc.
256
}
257
});
258
```
259
260
## Usage Patterns
261
262
### Removing Unused Dependencies
263
264
The primary use case is removing packages that are no longer referenced:
265
266
```typescript
267
import { pruneLockfile } from "@pnpm/lockfile.pruner";
268
269
// Remove packages not needed by the current project
270
const cleaned = pruneLockfile(lockfile, packageJson, '.' as ProjectId, {
271
warn: console.warn
272
});
273
```
274
275
### Workspace Pruning
276
277
For monorepos, use `pruneSharedLockfile` to remove packages not needed by any project:
278
279
```typescript
280
import { pruneSharedLockfile } from "@pnpm/lockfile.pruner";
281
282
// Remove packages not needed by any workspace project
283
const cleanedWorkspace = pruneSharedLockfile(lockfile, {
284
warn: console.warn
285
});
286
```
287
288
### Optional Dependency Handling
289
290
The pruning functions correctly handle optional dependencies:
291
- Optional packages are marked with `optional: true`
292
- Packages used both optionally and required are marked as required
293
- Optional-only packages maintain their optional status
294
295
```typescript
296
// The pruning functions automatically handle cases like:
297
// - Package used as both optional and regular dependency
298
// - Transitive optional dependencies
299
// - Optional dependencies that become required through other paths
300
```
301
302
### Linked Dependencies
303
304
Local linked dependencies (using `link:` protocol) are preserved even if not explicitly declared in package.json, maintaining workspace functionality:
305
306
```typescript
307
// Linked dependencies like "my-pkg": "link:../my-pkg" are preserved
308
// during pruning to maintain local workspace connections
309
```