0
# Package Management
1
2
Package.json manipulation and dependency resolution with version management for npm packages and project configuration.
3
4
## Capabilities
5
6
### Dependency Management
7
8
Add and resolve dependencies to package.json with automatic version resolution.
9
10
```typescript { .api }
11
/**
12
* Add dependencies to the destination package.json
13
* Environment watches for package.json changes and triggers package manager install
14
* @param dependencies - Dependencies to add (string, array, or object)
15
* @returns Promise resolving to resolved dependency versions
16
*/
17
async addDependencies(dependencies: string | string[] | Record<string, string>): Promise<Record<string, string>>;
18
19
/**
20
* Add development dependencies to the destination package.json
21
* Environment watches for package.json changes and triggers package manager install
22
* @param devDependencies - Dev dependencies to add (string, array, or object)
23
* @returns Promise resolving to resolved dependency versions
24
*/
25
async addDevDependencies(devDependencies: string | string[] | Record<string, string>): Promise<Record<string, string>>;
26
```
27
28
**Usage Examples:**
29
30
```typescript
31
export default class MyGenerator extends Generator {
32
async configuring() {
33
// Add single dependency (latest version)
34
await this.addDependencies('express');
35
36
// Add multiple dependencies
37
await this.addDependencies([
38
'express',
39
'body-parser',
40
'cors'
41
]);
42
43
// Add dependencies with specific versions
44
await this.addDependencies({
45
'express': '^4.18.0',
46
'lodash': '~4.17.21',
47
'moment': '2.29.4'
48
});
49
50
// Add mixed format (package@version)
51
await this.addDependencies([
52
'react@18.2.0',
53
'react-dom@18.2.0',
54
'typescript' // Latest version
55
]);
56
}
57
58
async writing() {
59
// Add dev dependencies conditionally
60
if (this.answers.includeTests) {
61
await this.addDevDependencies([
62
'jest',
63
'@types/jest',
64
'supertest'
65
]);
66
}
67
68
if (this.answers.useTypeScript) {
69
await this.addDevDependencies({
70
'typescript': '^5.0.0',
71
'@types/node': '^18.0.0',
72
'ts-node': '^10.9.0'
73
});
74
}
75
76
// Add build tools
77
await this.addDevDependencies([
78
'webpack@5.75.0',
79
'webpack-cli',
80
'babel-loader'
81
]);
82
}
83
}
84
```
85
86
### Version Resolution
87
88
Automatic resolution of package versions to latest available versions.
89
90
```typescript { .api }
91
/**
92
* Resolve dependencies to their latest versions
93
* Internal method used by addDependencies and addDevDependencies
94
* @param dependencies - Dependencies to resolve
95
* @returns Promise resolving to dependency name/version map
96
*/
97
async _resolvePackageJsonDependencies(
98
dependencies: string | string[] | Record<string, string>
99
): Promise<Record<string, string>>;
100
```
101
102
**Usage Example:**
103
104
```typescript
105
export default class MyGenerator extends Generator {
106
async configuring() {
107
// Manual version resolution (usually not needed)
108
const resolved = await this._resolvePackageJsonDependencies([
109
'express',
110
'lodash@4.17.20', // Specific version kept
111
'moment' // Will resolve to latest
112
]);
113
114
this.log('Resolved versions:', resolved);
115
// Output: { express: '4.18.2', lodash: '4.17.20', moment: '2.29.4' }
116
117
// Use resolved versions
118
this.packageJson.merge({
119
dependencies: resolved
120
});
121
}
122
}
123
```
124
125
### Package.json Integration
126
127
Direct manipulation of package.json through the Storage interface.
128
129
```typescript { .api }
130
/**
131
* Package.json Storage resolved to this.destinationPath('package.json')
132
* Environment watches for package.json changes at this.env.cwd and triggers install
133
* If package.json is at different folder, propagate to Environment: this.env.cwd = this.destinationPath()
134
*/
135
readonly packageJson: Storage;
136
```
137
138
**Usage Examples:**
139
140
```typescript
141
export default class MyGenerator extends Generator {
142
configuring() {
143
// Basic package.json setup
144
this.packageJson.merge({
145
name: this.answers.name,
146
version: '1.0.0',
147
description: this.answers.description,
148
main: 'index.js',
149
license: this.answers.license
150
});
151
152
// Add scripts
153
this.packageJson.merge({
154
scripts: {
155
start: 'node index.js',
156
test: 'npm run test:unit',
157
'test:unit': 'jest',
158
build: 'webpack --mode production',
159
dev: 'webpack --mode development --watch'
160
}
161
});
162
163
// Add repository information
164
if (this.answers.repository) {
165
this.packageJson.merge({
166
repository: {
167
type: 'git',
168
url: this.answers.repository
169
}
170
});
171
}
172
173
// Add keywords
174
if (this.answers.keywords) {
175
this.packageJson.merge({
176
keywords: this.answers.keywords.split(',').map(k => k.trim())
177
});
178
}
179
}
180
181
async writing() {
182
// Conditional package.json modifications
183
if (this.answers.useTypeScript) {
184
this.packageJson.merge({
185
main: 'dist/index.js',
186
types: 'dist/index.d.ts',
187
scripts: {
188
build: 'tsc',
189
'build:watch': 'tsc --watch'
190
}
191
});
192
}
193
194
// Add peer dependencies for library projects
195
if (this.answers.projectType === 'library') {
196
this.packageJson.merge({
197
peerDependencies: {
198
'react': '>=16.8.0',
199
'react-dom': '>=16.8.0'
200
}
201
});
202
}
203
}
204
}
205
```
206
207
### Environment Integration
208
209
Automatic package manager integration through yeoman-environment.
210
211
**Usage Examples:**
212
213
```typescript
214
export default class MyGenerator extends Generator {
215
configuring() {
216
// Change package.json location (if not in destination root)
217
if (this.answers.monorepo) {
218
// Tell environment where to watch for package.json changes
219
this.env.cwd = this.destinationPath('packages', this.answers.name);
220
221
// Create package.json in subdirectory
222
const subPackageJson = this.createStorage(
223
this.destinationPath('packages', this.answers.name, 'package.json')
224
);
225
226
subPackageJson.merge({
227
name: `@${this.answers.org}/${this.answers.name}`,
228
version: '1.0.0',
229
private: true
230
});
231
}
232
}
233
234
async install() {
235
// Package manager install is triggered automatically by environment
236
// when package.json changes are committed to disk
237
238
// You can still run manual installs
239
if (this.options.yarn) {
240
await this.spawnCommand('yarn', ['install']);
241
} else {
242
await this.spawnCommand('npm', ['install']);
243
}
244
}
245
}
246
```
247
248
### Advanced Package Management
249
250
Complex dependency management scenarios.
251
252
**Usage Examples:**
253
254
```typescript
255
export default class MyGenerator extends Generator {
256
async configuring() {
257
// Framework-specific dependencies
258
const frameworkDeps = {
259
react: {
260
deps: ['react', 'react-dom'],
261
devDeps: ['@types/react', '@types/react-dom']
262
},
263
vue: {
264
deps: ['vue'],
265
devDeps: ['@vue/cli-service']
266
},
267
angular: {
268
deps: ['@angular/core', '@angular/common'],
269
devDeps: ['@angular/cli']
270
}
271
};
272
273
const framework = frameworkDeps[this.answers.framework];
274
if (framework) {
275
await this.addDependencies(framework.deps);
276
await this.addDevDependencies(framework.devDeps);
277
}
278
279
// Conditional tool dependencies
280
const toolDeps = [];
281
const toolDevDeps = [];
282
283
if (this.answers.useLinting) {
284
toolDevDeps.push('eslint', '@eslint/js');
285
if (this.answers.useTypeScript) {
286
toolDevDeps.push('@typescript-eslint/parser', '@typescript-eslint/eslint-plugin');
287
}
288
}
289
290
if (this.answers.useFormatting) {
291
toolDevDeps.push('prettier');
292
}
293
294
if (this.answers.useTesting) {
295
toolDevDeps.push('jest', '@types/jest');
296
if (this.answers.framework === 'react') {
297
toolDevDeps.push('@testing-library/react', '@testing-library/jest-dom');
298
}
299
}
300
301
if (toolDeps.length) await this.addDependencies(toolDeps);
302
if (toolDevDeps.length) await this.addDevDependencies(toolDevDeps);
303
}
304
305
writing() {
306
// Update package.json based on added dependencies
307
const currentPkg = this.packageJson.getAll();
308
309
// Add scripts based on dependencies
310
const scripts = {};
311
312
if (currentPkg.devDependencies?.eslint) {
313
scripts.lint = 'eslint src/**/*.{js,ts}';
314
scripts['lint:fix'] = 'eslint src/**/*.{js,ts} --fix';
315
}
316
317
if (currentPkg.devDependencies?.prettier) {
318
scripts.format = 'prettier --write src/**/*.{js,ts}';
319
}
320
321
if (currentPkg.devDependencies?.jest) {
322
scripts.test = 'jest';
323
scripts['test:watch'] = 'jest --watch';
324
scripts['test:coverage'] = 'jest --coverage';
325
}
326
327
if (Object.keys(scripts).length) {
328
this.packageJson.merge({ scripts });
329
}
330
}
331
}
332
```
333
334
### Package Manager Selection
335
336
Support for different package managers.
337
338
**Usage Examples:**
339
340
```typescript
341
export default class MyGenerator extends Generator {
342
constructor(args, opts) {
343
super(args, opts);
344
345
this.option('package-manager', {
346
type: String,
347
alias: 'pm',
348
description: 'Package manager to use',
349
choices: ['npm', 'yarn', 'pnpm'],
350
default: 'npm'
351
});
352
}
353
354
async configuring() {
355
// Add dependencies using selected package manager format
356
await this.addDependencies(['express', 'lodash']);
357
358
// Package manager specific configurations
359
if (this.options.packageManager === 'yarn') {
360
// Add yarn-specific configurations
361
this.packageJson.merge({
362
engines: {
363
yarn: '>=1.22.0'
364
}
365
});
366
367
// Create .yarnrc if needed
368
this.writeDestination('.yarnrc', 'save-prefix "^"\n');
369
}
370
371
if (this.options.packageManager === 'pnpm') {
372
// Add pnpm-specific configurations
373
this.packageJson.merge({
374
engines: {
375
pnpm: '>=7.0.0'
376
}
377
});
378
379
// Create .npmrc for pnpm
380
this.writeDestination('.npmrc', 'package-manager=pnpm\n');
381
}
382
}
383
384
async install() {
385
// Install using selected package manager
386
const pm = this.options.packageManager;
387
388
switch (pm) {
389
case 'yarn':
390
await this.spawnCommand('yarn', ['install']);
391
break;
392
case 'pnpm':
393
await this.spawnCommand('pnpm', ['install']);
394
break;
395
default:
396
await this.spawnCommand('npm', ['install']);
397
break;
398
}
399
}
400
}
401
```
402
403
## Types
404
405
```typescript { .api }
406
// Dependency input formats
407
type DependencyInput = string | string[] | Record<string, string>;
408
409
// Resolved dependency map
410
type ResolvedDependencies = Record<string, string>;
411
```