0
# Package Manager Support
1
2
Multi-package-manager abstraction supporting npm, Yarn (Classic/Berry), pnpm, and Bun with unified configuration and command generation.
3
4
## Capabilities
5
6
### Package Manager Detection
7
8
Functions for detecting and configuring package managers.
9
10
```javascript { .api }
11
/**
12
* Get package manager configuration for a project
13
* @param rootDirectory - Project root directory
14
* @param package_ - Package.json object
15
* @returns Package manager configuration object
16
*/
17
function getPackageManagerConfig(
18
rootDirectory: string,
19
package_: {packageManager?: string}
20
): PackageManagerConfig;
21
22
/**
23
* Find lockfile for a specific package manager
24
* @param rootDirectory - Project root directory
25
* @param config - Package manager configuration
26
* @returns Lockfile path or undefined if not found
27
*/
28
function findLockfile(
29
rootDirectory: string,
30
config: PackageManagerConfig
31
): string | undefined;
32
33
/**
34
* Format command for display purposes
35
* @param command - Command tuple [cli, arguments]
36
* @returns Formatted command string
37
*/
38
function printCommand([cli, arguments_]: [string, string[]]): string;
39
```
40
41
**Usage Examples:**
42
43
```javascript
44
import { getPackageManagerConfig, findLockfile, printCommand } from "np/source/package-manager/index.js";
45
46
// Detect package manager
47
const packageManager = getPackageManagerConfig(process.cwd(), packageJson);
48
console.log(`Using package manager: ${packageManager.id}`);
49
50
// Find lockfile
51
const lockfile = findLockfile(process.cwd(), packageManager);
52
if (lockfile) {
53
console.log(`Found lockfile: ${lockfile}`);
54
}
55
56
// Format command for display
57
const installCmd = packageManager.installCommand;
58
console.log(`Install command: ${printCommand(installCmd)}`);
59
```
60
61
### Package Manager Configuration Interface
62
63
Unified configuration interface for all supported package managers.
64
65
```javascript { .api }
66
interface PackageManagerConfig {
67
/** Package manager identifier */
68
id: 'npm' | 'yarn' | 'pnpm' | 'bun';
69
70
/** CLI command name */
71
cli: string;
72
73
/** Install command with lockfile */
74
installCommand: [string, string[]];
75
76
/** Install command without lockfile */
77
installCommandNoLockfile: [string, string[]];
78
79
/** Generate version bump command */
80
versionCommand(input: string): [string, string[]];
81
82
/** Custom publish command (optional) */
83
publishCommand?: (args: string[]) => [string, string[]];
84
85
/** Whether to throw error on external registries */
86
throwOnExternalRegistry?: boolean;
87
}
88
```
89
90
### Supported Package Managers
91
92
Configuration objects for each supported package manager.
93
94
```javascript { .api }
95
/**
96
* NPM package manager configuration
97
*/
98
const npmConfig: PackageManagerConfig = {
99
id: 'npm',
100
cli: 'npm',
101
installCommand: ['npm', ['ci']],
102
installCommandNoLockfile: ['npm', ['install']],
103
versionCommand: (input) => ['npm', ['version', input]],
104
throwOnExternalRegistry: false
105
};
106
107
/**
108
* PNPM package manager configuration
109
*/
110
const pnpmConfig: PackageManagerConfig = {
111
id: 'pnpm',
112
cli: 'pnpm',
113
installCommand: ['pnpm', ['install', '--frozen-lockfile']],
114
installCommandNoLockfile: ['pnpm', ['install', '--no-lockfile']],
115
versionCommand: (input) => ['pnpm', ['version', input]],
116
throwOnExternalRegistry: true
117
};
118
119
/**
120
* Yarn Classic package manager configuration
121
*/
122
const yarnConfig: PackageManagerConfig = {
123
id: 'yarn',
124
cli: 'yarn',
125
installCommand: ['yarn', ['install', '--frozen-lockfile']],
126
installCommandNoLockfile: ['yarn', ['install', '--no-lockfile']],
127
versionCommand: (input) => ['yarn', ['version', '--new-version', input]],
128
throwOnExternalRegistry: false
129
};
130
131
/**
132
* Yarn Berry package manager configuration
133
*/
134
const yarnBerryConfig: PackageManagerConfig = {
135
id: 'yarn',
136
cli: 'yarn',
137
installCommand: ['yarn', ['install', '--immutable']],
138
installCommandNoLockfile: ['yarn', ['install']],
139
versionCommand: (input) => ['yarn', ['version', input]],
140
publishCommand: (args) => ['yarn', ['npm', 'publish', ...args]],
141
throwOnExternalRegistry: false
142
};
143
144
/**
145
* Bun package manager configuration
146
*/
147
const bunConfig: PackageManagerConfig = {
148
id: 'bun',
149
cli: 'bun',
150
installCommand: ['bun', ['install', '--frozen-lockfile']],
151
installCommandNoLockfile: ['bun', ['install', '--no-save']],
152
versionCommand: (input) => ['npm', ['version', input]], // Bun uses npm for versioning
153
throwOnExternalRegistry: false
154
};
155
```
156
157
**Usage Examples:**
158
159
```javascript
160
import { npmConfig, pnpmConfig, yarnConfig } from "np/source/package-manager/configs.js";
161
162
// Use specific package manager
163
const config = pnpmConfig;
164
const [cli, args] = config.versionCommand('patch');
165
console.log(`Version command: ${cli} ${args.join(' ')}`);
166
167
// Check external registry support
168
if (config.throwOnExternalRegistry && isExternalRegistry(packageJson)) {
169
throw new Error(`External registry not supported with ${config.id}`);
170
}
171
```
172
173
## Package Manager Detection
174
175
### Detection Priority
176
177
Package manager detection follows this priority:
178
179
1. **CLI flag**: `--package-manager` option
180
2. **Package.json field**: `packageManager` field specification
181
3. **Lockfile detection**: Based on existing lockfiles
182
4. **Default**: Falls back to npm
183
184
### PackageManager Field
185
186
Support for package.json `packageManager` field:
187
188
```json
189
{
190
"packageManager": "pnpm@8.5.0",
191
"packageManager": "yarn@3.4.1",
192
"packageManager": "npm@9.0.0"
193
}
194
```
195
196
### Lockfile Detection
197
198
Automatic detection based on lockfiles:
199
200
```javascript
201
// Lockfile to package manager mapping
202
const lockfileMap = {
203
'package-lock.json': 'npm',
204
'yarn.lock': 'yarn',
205
'pnpm-lock.yaml': 'pnpm',
206
'bun.lockb': 'bun'
207
};
208
209
// Detection logic
210
function detectFromLockfile(rootDirectory) {
211
for (const [lockfile, manager] of Object.entries(lockfileMap)) {
212
if (fs.existsSync(path.join(rootDirectory, lockfile))) {
213
return manager;
214
}
215
}
216
return 'npm'; // Default fallback
217
}
218
```
219
220
## Command Generation
221
222
### Install Commands
223
224
Different install behavior based on lockfile presence:
225
226
```javascript
227
// With lockfile (exact dependencies)
228
config.installCommand // ['pnpm', ['install', '--frozen-lockfile']]
229
230
// Without lockfile (resolve latest)
231
config.installCommandNoLockfile // ['pnpm', ['install', '--no-lockfile']]
232
```
233
234
### Version Commands
235
236
Version bump commands vary by package manager:
237
238
```javascript
239
// NPM
240
npmConfig.versionCommand('patch') // ['npm', ['version', 'patch']]
241
242
// Yarn Classic
243
yarnConfig.versionCommand('patch') // ['yarn', ['version', '--new-version', 'patch']]
244
245
// PNPM
246
pnpmConfig.versionCommand('patch') // ['pnpm', ['version', 'patch']]
247
248
// Bun (uses npm for versioning)
249
bunConfig.versionCommand('patch') // ['npm', ['version', 'patch']]
250
```
251
252
### Publish Commands
253
254
Publishing commands with custom handling:
255
256
```javascript
257
// Standard (npm, pnpm, yarn classic)
258
const args = ['publish', '--tag', 'beta'];
259
// Uses: npm publish --tag beta
260
261
// Yarn Berry (custom publish command)
262
yarnBerryConfig.publishCommand(args) // ['yarn', ['npm', 'publish', '--tag', 'beta']]
263
```
264
265
## Package Manager Specific Features
266
267
### NPM Features
268
269
- **Registry support**: Full external registry support
270
- **2FA integration**: Native 2FA support
271
- **Scoped packages**: Built-in scoped package handling
272
- **Dist-tags**: Full dist-tag management
273
274
### Yarn Features
275
276
#### Yarn Classic
277
- **Workspace support**: Monorepo workspace detection
278
- **Registry config**: Custom registry via .yarnrc
279
- **Version handling**: Different version command syntax
280
- **Publish workflow**: Standard npm publish compatibility
281
282
#### Yarn Berry
283
- **Plugin system**: Modern plugin architecture
284
- **Workspace protocols**: Advanced workspace linking
285
- **Custom publish**: Uses `yarn npm publish` command
286
- **Configuration**: .yarnrc.yml configuration format
287
288
### PNPM Features
289
290
- **External registry restriction**: Throws on external registries
291
- **Workspace support**: Built-in monorepo support
292
- **Strict lockfile**: Immutable lockfile enforcement
293
- **Space efficiency**: Content-addressable storage
294
295
### Bun Features
296
297
- **Fast installs**: Ultra-fast package installation
298
- **Version delegation**: Uses npm for version bumping
299
- **Binary lockfile**: Compact binary lockfile format
300
- **Runtime integration**: Built-in JavaScript runtime
301
302
## Workspace Support
303
304
### Monorepo Detection
305
306
Package managers handle monorepos differently:
307
308
```javascript
309
// Yarn workspaces
310
{
311
"workspaces": [
312
"packages/*",
313
"apps/*"
314
]
315
}
316
317
// PNPM workspaces (pnpm-workspace.yaml)
318
packages:
319
- 'packages/*'
320
- 'apps/*'
321
322
// NPM workspaces
323
{
324
"workspaces": [
325
"packages/*"
326
]
327
}
328
```
329
330
### Workspace Publishing
331
332
Publishing from workspace packages:
333
334
```javascript
335
// Detect if in workspace
336
const isWorkspace = packageJson.workspaces ||
337
fs.existsSync('pnpm-workspace.yaml');
338
339
// Adjust commands for workspace context
340
if (isWorkspace) {
341
// Use workspace-aware commands
342
const publishCmd = config.publishCommand || defaultPublishCommand;
343
}
344
```
345
346
## Configuration Integration
347
348
### CLI Integration
349
350
Package manager selection in CLI:
351
352
```bash
353
# Force specific package manager
354
np --package-manager pnpm
355
356
# Auto-detect from lockfile
357
np # Uses detected package manager
358
359
# Override packageManager field
360
np --package-manager yarn
361
```
362
363
### Programmatic Usage
364
365
```javascript
366
// Force specific package manager
367
const options = {
368
packageManager: 'pnpm'
369
};
370
371
// Let np auto-detect
372
const packageManager = getPackageManagerConfig(rootDirectory, packageJson);
373
374
// Use in publishing
375
await np('patch', {...options, packageManager}, context);
376
```
377
378
## Error Handling
379
380
### Package Manager Errors
381
382
**Detection Errors:**
383
- Multiple lockfiles present
384
- Unknown package manager specified
385
- Package manager not installed
386
387
**Command Errors:**
388
- Invalid command syntax
389
- Package manager version incompatibility
390
- Missing dependencies
391
392
**Registry Errors:**
393
- External registry not supported (pnpm)
394
- Authentication failures
395
- Network connectivity issues
396
397
### Error Recovery
398
399
Package manager error handling strategies:
400
401
```javascript
402
// Package manager not found
403
if (error.code === 'ENOENT') {
404
throw new Error(`Package manager '${config.id}' not found. Please install it first.`);
405
}
406
407
// External registry restriction
408
if (config.throwOnExternalRegistry && isExternalRegistry(package_)) {
409
throw new Error(`External registry not supported with ${config.id}`);
410
}
411
412
// Version mismatch
413
if (error.message.includes('version')) {
414
console.warn(`${config.id} version may be incompatible. Consider upgrading.`);
415
}
416
```
417
418
### Compatibility Matrix
419
420
Package manager compatibility with np features:
421
422
| Feature | npm | Yarn Classic | Yarn Berry | pnpm | Bun |
423
|---------|-----|-------------|------------|------|-----|
424
| External Registry | ✅ | ✅ | ✅ | ❌ | ✅ |
425
| 2FA Setup | ✅ | ✅ | ✅ | ✅ | ✅ |
426
| Workspaces | ✅ | ✅ | ✅ | ✅ | ✅ |
427
| Custom Publish | ✅ | ✅ | ✅ | ✅ | ✅ |
428
| Version Bumping | ✅ | ✅ | ✅ | ✅ | ➡️ npm |
429
430
✅ Full support, ❌ Not supported, ➡️ Delegates to another tool