or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

command-execution.mdcommand-line.mdconfiguration.mdcore-generator.mdfile-system.mdgit-integration.mdindex.mdpackage-management.mdtask-lifecycle.mduser-interaction.md

package-management.mddocs/

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

```