0
# Package Management
1
2
Package management utilities for adding dependencies, managing package.json files, and ensuring required packages are available across npm, yarn, and pnpm.
3
4
## Capabilities
5
6
### Dependency Management
7
8
Add and remove dependencies from package.json with intelligent version resolution and conflict handling.
9
10
```typescript { .api }
11
/**
12
* Add dependencies and devDependencies to package.json
13
* Handles version conflicts and prevents duplicate dependencies
14
* @param tree - File system tree
15
* @param dependencies - Production dependencies to add
16
* @param devDependencies - Development dependencies to add
17
* @param packageJsonPath - Path to package.json (defaults to root)
18
* @param keepExistingVersions - Prevent upgrading existing packages
19
* @returns Callback to install packages
20
*/
21
function addDependenciesToPackageJson(
22
tree: Tree,
23
dependencies: Record<string, string>,
24
devDependencies: Record<string, string>,
25
packageJsonPath?: string,
26
keepExistingVersions?: boolean
27
): GeneratorCallback;
28
29
/**
30
* Remove dependencies from package.json
31
* @param tree - File system tree
32
* @param dependencies - Production dependencies to remove
33
* @param devDependencies - Development dependencies to remove
34
* @param packageJsonPath - Path to package.json (defaults to root)
35
* @returns Callback to run package manager operations
36
*/
37
function removeDependenciesFromPackageJson(
38
tree: Tree,
39
dependencies: string[],
40
devDependencies: string[],
41
packageJsonPath?: string
42
): GeneratorCallback;
43
```
44
45
**Usage Examples:**
46
47
```typescript
48
import {
49
Tree,
50
addDependenciesToPackageJson,
51
removeDependenciesFromPackageJson,
52
GeneratorCallback
53
} from "@nx/devkit";
54
55
export default function myGenerator(tree: Tree): GeneratorCallback {
56
// Add React dependencies
57
const addDepsTask = addDependenciesToPackageJson(
58
tree,
59
{
60
"react": "^18.2.0",
61
"react-dom": "^18.2.0"
62
},
63
{
64
"@types/react": "^18.0.15",
65
"@types/react-dom": "^18.0.6",
66
"@testing-library/react": "^13.3.0"
67
}
68
);
69
70
// Remove old dependencies
71
const removeDepsTask = removeDependenciesFromPackageJson(
72
tree,
73
["lodash"], // Remove from dependencies
74
["@types/lodash"] // Remove from devDependencies
75
);
76
77
// Return combined task
78
return async () => {
79
await addDepsTask();
80
await removeDepsTask();
81
};
82
}
83
```
84
85
### Package Installation
86
87
Manage package installation tasks and ensure required packages are available.
88
89
```typescript { .api }
90
/**
91
* Create a task to install packages using the detected package manager
92
* @param tree - File system tree
93
* @param alwaysRun - Force installation even if no changes detected
94
* @param cwd - Working directory for installation
95
* @param packageManager - Specific package manager to use
96
*/
97
function installPackagesTask(
98
tree: Tree,
99
alwaysRun?: boolean,
100
cwd?: string,
101
packageManager?: PackageManager
102
): void;
103
104
/**
105
* Ensure a package is installed and available for use
106
* Installs the package temporarily if not found
107
* @param pkg - Package name to ensure
108
* @param version - Version to install if missing
109
* @returns The required package module or null for ESM packages
110
*/
111
function ensurePackage<T = any>(pkg: string, version: string): T;
112
113
/**
114
* Current version of Nx being used
115
*/
116
const NX_VERSION: string;
117
```
118
119
**Usage Examples:**
120
121
```typescript
122
import {
123
Tree,
124
installPackagesTask,
125
ensurePackage,
126
NX_VERSION
127
} from "@nx/devkit";
128
129
export default function myGenerator(tree: Tree) {
130
// Schedule package installation
131
installPackagesTask(tree);
132
133
// Ensure a specific package is available
134
try {
135
const chalk = ensurePackage("chalk", "^4.1.0");
136
console.log(chalk.green("Package is available!"));
137
} catch (error) {
138
console.error("Failed to ensure package:", error.message);
139
}
140
141
// Use current Nx version
142
console.log(`Using Nx version: ${NX_VERSION}`);
143
}
144
```
145
146
### Package Manager Detection
147
148
Detect and work with different package managers (npm, yarn, pnpm).
149
150
```typescript { .api }
151
/**
152
* Detect which package manager is being used in the workspace
153
* @param dir - Directory to check (defaults to workspace root)
154
* @returns Detected package manager
155
*/
156
function detectPackageManager(dir?: string): PackageManager;
157
158
/**
159
* Get commands for the specified package manager
160
* @param packageManager - Package manager to get commands for
161
* @returns Object with package manager commands
162
*/
163
function getPackageManagerCommand(packageManager?: PackageManager): {
164
install: string;
165
add: string;
166
addDev: string;
167
rm: string;
168
exec: string;
169
dlx: string;
170
list: string;
171
run: (script: string, args?: string) => string;
172
preInstall?: string;
173
};
174
175
/**
176
* Get version of the specified package manager
177
* @param packageManager - Package manager to check
178
* @returns Version string
179
*/
180
function getPackageManagerVersion(packageManager?: PackageManager): string;
181
182
/**
183
* Check if workspaces are enabled for the package manager
184
* @param packageManager - Package manager to check
185
* @param root - Workspace root directory
186
* @returns Whether workspaces are enabled
187
*/
188
function isWorkspacesEnabled(
189
packageManager: PackageManager,
190
root: string
191
): boolean;
192
193
/**
194
* Supported package managers
195
*/
196
type PackageManager = "npm" | "yarn" | "pnpm";
197
```
198
199
**Usage Examples:**
200
201
```typescript
202
import {
203
detectPackageManager,
204
getPackageManagerCommand,
205
getPackageManagerVersion,
206
isWorkspacesEnabled,
207
workspaceRoot
208
} from "@nx/devkit";
209
210
function setupPackageManager() {
211
// Detect current package manager
212
const pm = detectPackageManager();
213
console.log(`Using package manager: ${pm}`);
214
215
// Get package manager commands
216
const commands = getPackageManagerCommand(pm);
217
console.log(`Install command: ${commands.install}`);
218
console.log(`Add dev dependency: ${commands.addDev}`);
219
220
// Check version
221
const version = getPackageManagerVersion(pm);
222
console.log(`${pm} version: ${version}`);
223
224
// Check workspace support
225
const workspacesEnabled = isWorkspacesEnabled(pm, workspaceRoot);
226
console.log(`Workspaces enabled: ${workspacesEnabled}`);
227
228
// Run package manager commands
229
const { execSync } = require("child_process");
230
231
// Install dependencies
232
execSync(commands.install, { cwd: workspaceRoot, stdio: "inherit" });
233
234
// Add a new dependency
235
execSync(`${commands.add} lodash@^4.17.21`, {
236
cwd: workspaceRoot,
237
stdio: "inherit"
238
});
239
240
// Run a script
241
const testCommand = commands.run("test", "--coverage");
242
execSync(testCommand, { cwd: workspaceRoot, stdio: "inherit" });
243
}
244
```
245
246
## Advanced Package Management
247
248
### Version Resolution Strategy
249
250
The dependency management functions use intelligent version resolution:
251
252
1. **Conflict Prevention**: Dependencies in devDependencies won't be added to dependencies if they already exist
253
2. **Version Upgrading**: By default, higher versions replace lower versions
254
3. **Version Preservation**: Use `keepExistingVersions: true` to prevent version bumps
255
4. **Semver Handling**: Supports standard semver ranges and special tags like "latest", "next"
256
257
```typescript
258
// Example of complex dependency management
259
export default function myGenerator(tree: Tree) {
260
const addDepsTask = addDependenciesToPackageJson(
261
tree,
262
{
263
"react": "^18.2.0", // Will upgrade from ^17.x.x
264
"lodash": "latest", // Uses latest tag
265
"moment": "~2.29.0" // Patch-level updates only
266
},
267
{
268
"@types/react": "^18.0.15",
269
"typescript": "next" // Uses next tag
270
},
271
"package.json",
272
false // Allow version upgrades
273
);
274
275
return addDepsTask;
276
}
277
```
278
279
### Monorepo Package Management
280
281
Working with packages in monorepo environments:
282
283
```typescript
284
// Add dependencies to a specific project
285
export default function addToProject(tree: Tree, options: { project: string }) {
286
const projectConfig = readProjectConfiguration(tree, options.project);
287
const packageJsonPath = `${projectConfig.root}/package.json`;
288
289
const addDepsTask = addDependenciesToPackageJson(
290
tree,
291
{ "axios": "^1.0.0" },
292
{ "@types/axios": "^1.0.0" },
293
packageJsonPath // Project-specific package.json
294
);
295
296
return addDepsTask;
297
}
298
```
299
300
### Package Manager Configuration
301
302
```typescript
303
// Configure package manager behavior
304
function configurePackageManager(tree: Tree, packageManager: PackageManager) {
305
const nxJson = readNxJson(tree);
306
307
updateNxJson(tree, {
308
...nxJson,
309
cli: {
310
...nxJson?.cli,
311
packageManager: packageManager
312
}
313
});
314
315
// Create package manager specific configuration files
316
switch (packageManager) {
317
case "yarn":
318
if (getPackageManagerVersion("yarn").startsWith("3.")) {
319
tree.write(".yarnrc.yml", "nodeLinker: node-modules\nenableScripts: false");
320
}
321
break;
322
case "pnpm":
323
tree.write(".npmrc", "shamefully-hoist=true");
324
break;
325
}
326
}
327
```
328
329
### Dynamic Package Loading
330
331
```typescript
332
// Dynamically load packages with fallback handling
333
async function loadPackageDynamic<T>(packageName: string, version: string): Promise<T | null> {
334
try {
335
// Try to require the package
336
return require(packageName);
337
} catch (error) {
338
if (error.code === 'MODULE_NOT_FOUND') {
339
// Package not found, ensure it's available
340
const pkg = ensurePackage<T>(packageName, version);
341
return pkg;
342
} else if (error.code === 'ERR_REQUIRE_ESM') {
343
// ESM package, use dynamic import
344
const pkg = await import(packageName);
345
return pkg.default || pkg;
346
}
347
throw error;
348
}
349
}
350
351
// Usage example
352
export default async function myGenerator(tree: Tree) {
353
// Load chalk dynamically
354
const chalk = await loadPackageDynamic("chalk", "^4.1.0");
355
if (chalk) {
356
console.log(chalk.green("Successfully loaded chalk!"));
357
}
358
359
// Load an ESM package
360
const prettier = await loadPackageDynamic("prettier", "^2.8.0");
361
if (prettier) {
362
const formatted = await prettier.format("const x=1;", { parser: "typescript" });
363
console.log("Formatted code:", formatted);
364
}
365
}
366
```