@langri-sha/projen-project 0.8.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -1,7 +1,10 @@
1
+ import * as path from 'node:path'
2
+
1
3
  import {
2
4
  Project as BaseProject,
3
5
  type ProjectOptions as BaseProjectOptions,
4
- YamlFile,
6
+ IgnoreFile,
7
+ IgnoreFileOptions,
5
8
  javascript,
6
9
  } from 'projen'
7
10
 
@@ -26,8 +29,18 @@ import {
26
29
  type TypeScriptConfigOptions,
27
30
  } from '@langri-sha/projen-typescript-config'
28
31
  import { Beachball, BeachballOptions } from '@langri-sha/projen-beachball'
29
-
30
- import { ProjenrcFile } from './lib/index.js'
32
+ import { License } from '@langri-sha/projen-license'
33
+
34
+ import { NodePackage, NodePackageOptions, ProjenrcFile } from './lib/index.js'
35
+ import { JestConfig, JestConfigOptions } from '@langri-sha/projen-jest-config'
36
+ import { Prettier, PrettierOptions } from '@langri-sha/projen-prettier'
37
+ import { ESLint, ESLintOptions } from '@langri-sha/projen-eslint'
38
+ import { LintStaged, LintStagedOptions } from '@langri-sha/projen-lint-staged'
39
+ import { Babel, BabelOptions } from '@langri-sha/projen-babel'
40
+ import {
41
+ PnpmWorkspace,
42
+ PnpmWorkspaceOptions,
43
+ } from '@langri-sha/projen-pnpm-workspace'
31
44
 
32
45
  export * from '@langri-sha/projen-typescript-config'
33
46
 
@@ -36,60 +49,105 @@ export interface ProjectOptions
36
49
  /*
37
50
  * Pass in to set up Beachball.
38
51
  */
39
- beachballOptions?: BeachballOptions
52
+ babel?: BabelOptions
40
53
 
41
54
  /*
42
55
  * Pass in to set up Beachball.
43
56
  */
44
- codeownersOptions?: CodeownersOptions
57
+ beachball?: BeachballOptions
58
+
59
+ /*
60
+ * Pass in to set up Beachball.
61
+ */
62
+ codeowners?: CodeownersOptions
45
63
 
46
64
  /**
47
65
  * EditorConfig options.
48
66
  */
49
- editorConfigOptions?: EditorConfigOptions
67
+ editorConfig?: EditorConfigOptions
68
+
69
+ /**
70
+ * Pass in to configure ESLint.
71
+ */
72
+ eslint?: ESLintOptions
50
73
 
51
74
  /**
52
75
  * Husky options.
53
76
  */
54
- huskyOptions?: HuskyOptions
77
+ husky?: HuskyOptions
55
78
 
56
- /*
57
- * Pass in to configure Renovate.
79
+ /**
80
+ * Configures Jest, when provided.
81
+ */
82
+ jestConfig?: JestConfigOptions
83
+
84
+ /**
85
+ * Configures `lint-staged`, when provided.
58
86
  */
59
- renovateOptions?: RenovateOptions
87
+ lintStaged?: LintStagedOptions
60
88
 
61
89
  /*
62
90
  * Options for the linting synthesized files.
63
91
  */
64
- lintSynthesizedOptions?: LintSynthesizedOptions
92
+ lintSynthesized?: LintSynthesizedOptions
65
93
 
66
94
  /**
67
95
  * Package configuration options.
68
96
  */
69
- package?: javascript.NodePackageOptions
97
+ package?: {
98
+ /**
99
+ * License copyright year.
100
+ *
101
+ * @default "Current full year"
102
+ */
103
+ copyrightYear?: string
104
+ } & NodePackageOptions
105
+
106
+ /*
107
+ * PNPM workspaces to generate, if provided.
108
+ */
109
+ pnpmWorkspace?: PnpmWorkspaceOptions
70
110
 
71
111
  /**
72
- * TypeScript configuration options.
112
+ * Pass in to configure Prettier.
73
113
  */
74
- typeScriptConfigOptions?: TypeScriptConfigOptions
114
+ prettier?: PrettierOptions
115
+
116
+ /**
117
+ * Pass in to configure NPM ignore options.
118
+ */
119
+ npmIgnore?: IgnoreFileOptions
75
120
 
76
121
  /*
77
- * Whether to use Terrafom.
122
+ * Pass in to configure Renovate.
78
123
  */
79
- withTerraform?: boolean
124
+ renovate?: RenovateOptions
125
+
126
+ /**
127
+ * TypeScript configuration options.
128
+ */
129
+ typeScriptConfig?: TypeScriptConfigOptions
80
130
 
81
131
  /*
82
- * PNPM workspaces to generate.
132
+ * Whether to use Terrafom.
83
133
  */
84
- workspaces?: string[]
134
+ withTerraform?: boolean
85
135
  }
86
136
 
87
137
  export class Project extends BaseProject {
138
+ babel?: Babel
88
139
  beachball?: Beachball
89
140
  codeowners?: Codeowners
90
141
  editorConfig?: EditorConfig
142
+ eslint?: ESLint
91
143
  husky?: Husky
92
- package?: javascript.NodePackage
144
+ jestConfig?: JestConfig
145
+ license?: License
146
+ lintStaged?: LintStaged
147
+ npmIgnore?: IgnoreFile
148
+ package?: NodePackage
149
+ pnpmWorkspace?: PnpmWorkspace
150
+ prettier?: Prettier
93
151
  projenrc?: ProjenrcFile
94
152
  renovate?: Renovate
95
153
  typeScriptConfig?: TypeScriptConfig
@@ -111,6 +169,7 @@ export class Project extends BaseProject {
111
169
 
112
170
  this.#configurePackage(options)
113
171
  this.#configureTypeScript(options)
172
+ this.#configureProjenrc()
114
173
 
115
174
  if (this.parent) {
116
175
  this.tasks.tryFind('default')?.reset()
@@ -118,59 +177,90 @@ export class Project extends BaseProject {
118
177
  this.tasks.tryFind('install:ci')?.reset()
119
178
  }
120
179
 
180
+ this.#configureESLint(options)
181
+ this.#configurePrettier(options)
182
+
183
+ this.#configureBabel(options)
121
184
  this.#configureBeachball(options)
122
185
  this.#configureCodeowners(options)
123
186
  this.#configureEditorConfig(options)
124
187
  this.#configureHusky(options)
188
+ this.#configureJestConfig(options)
189
+ this.#configureLicense(options)
190
+ this.#configureLintStaged(options)
125
191
  this.#configureLintSynthesized(options)
126
- this.#configureProjenrc()
192
+ this.#configureNpmIgnore(options)
193
+ this.#configurePnpmWorkspace(options)
127
194
  this.#configureRenovate(options)
128
- this.#createPnpmWorkspaces(options)
195
+ }
196
+
197
+ override preSynthesize(): void {
198
+ super.preSynthesize()
199
+
200
+ this.#populateTypeScriptProjectReferencesFromDependencies()
201
+ }
202
+
203
+ get allSubprojects(): BaseProject[] {
204
+ return this.root.node
205
+ .findAll(0)
206
+ .filter((node) => node !== this.root)
207
+ .filter((node): node is BaseProject => node instanceof BaseProject)
208
+ }
209
+
210
+ get allSubprojectsKind(): Project[] {
211
+ return this.allSubprojects.filter(
212
+ (project): project is Project => project instanceof Project,
213
+ )
129
214
  }
130
215
 
131
216
  /**
132
217
  * Add a subproject.
133
218
  */
134
- addSubproject(projectOptions: ProjectOptions) {
135
- return new Project({
219
+ addSubproject(
220
+ projectOptions: ProjectOptions,
221
+ ...compose: Array<(project: Project) => void>
222
+ ) {
223
+ const project = new Project({
136
224
  parent: this,
137
225
  ...projectOptions,
138
226
  })
227
+
228
+ for (const callback of compose) {
229
+ callback(project)
230
+ }
231
+
232
+ return project
139
233
  }
140
234
 
141
235
  /**
142
236
  * Find a project by it's name, e.g. `@acme/some`.
143
237
  */
144
- findSubproject(name: string): Project {
145
- const subprojects: Array<BaseProject> = []
146
-
147
- const addSubproject = (project: BaseProject) => {
148
- for (const subproject of project.subprojects) {
149
- subprojects.push(subproject)
238
+ findSubproject(name: string): Project | undefined {
239
+ return this.allSubprojectsKind.find((project) => project.name === name)
240
+ }
150
241
 
151
- if (subproject.subprojects.length) {
152
- addSubproject(subproject)
153
- }
154
- }
242
+ #configureBabel({ babel }: ProjectOptions) {
243
+ if (!babel) {
244
+ return
155
245
  }
156
246
 
157
- addSubproject(this.root)
158
-
159
- const result = subprojects.find((subproject) => subproject.name === name)
160
-
161
- if (result instanceof Project) {
162
- return result
247
+ const defaults: BabelOptions = {
248
+ options: {
249
+ presets: ['@langri-sha/babel-preset'],
250
+ },
163
251
  }
164
252
 
165
- throw new Error(`Cannot find subproject ${name}`)
253
+ this.babel = new Babel(this, deepMerge(defaults, babel))
254
+
255
+ this.typeScriptConfig?.addFile(this.babel.path)
166
256
  }
167
257
 
168
- #configureBeachball({ beachballOptions }: ProjectOptions) {
169
- if (!beachballOptions || this.parent) {
258
+ #configureBeachball({ beachball }: ProjectOptions) {
259
+ if (!beachball || this.parent) {
170
260
  return
171
261
  }
172
262
 
173
- const options = deepMerge(beachballOptions, {
263
+ const options = deepMerge(beachball, {
174
264
  branch: 'origin/main',
175
265
  gitTags: false,
176
266
  ignorePatterns: [
@@ -184,11 +274,12 @@ export class Project extends BaseProject {
184
274
 
185
275
  this.beachball = new Beachball(this, options)
186
276
 
277
+ this.prettier?.ignore.addPatterns('CHANGELOG.md')
187
278
  this.package?.addDevDeps('beachball@2.43.1')
188
279
  this.typeScriptConfig?.addFile('beachball.config.js')
189
280
  }
190
281
 
191
- #configureCodeowners({ codeownersOptions }: ProjectOptions) {
282
+ #configureCodeowners({ codeowners: codeownersOptions }: ProjectOptions) {
192
283
  if (!codeownersOptions) {
193
284
  return
194
285
  }
@@ -196,7 +287,9 @@ export class Project extends BaseProject {
196
287
  this.codeowners = new Codeowners(this, codeownersOptions)
197
288
  }
198
289
 
199
- #configureEditorConfig({ editorConfigOptions }: ProjectOptions) {
290
+ #configureEditorConfig({
291
+ editorConfig: editorConfigOptions,
292
+ }: ProjectOptions) {
200
293
  if (!editorConfigOptions || this.parent) {
201
294
  return
202
295
  }
@@ -219,9 +312,31 @@ export class Project extends BaseProject {
219
312
  this,
220
313
  deepMerge(editorConfigOptions ?? {}, defaults),
221
314
  )
315
+
316
+ this.prettier?.ignore.addPatterns('!.editorconfig')
317
+ }
318
+
319
+ #configureESLint({ eslint: eslintOptions }: ProjectOptions) {
320
+ if (!eslintOptions) {
321
+ return
322
+ }
323
+
324
+ const defaults: ESLintOptions = {
325
+ filename: 'eslint.config.mjs',
326
+ ignorePatterns: ['.*'],
327
+ extends: '@langri-sha/eslint-config',
328
+ }
329
+
330
+ this.eslint = new ESLint(this, deepMerge(defaults, eslintOptions))
331
+
332
+ if (this.projenrc?.filePath) {
333
+ this.eslint.ignorePatterns.push(`!${this.projenrc.filePath}`)
334
+ }
335
+
336
+ this.typeScriptConfig?.addFile(this.eslint.path)
222
337
  }
223
338
 
224
- #configureHusky({ huskyOptions }: ProjectOptions) {
339
+ #configureHusky({ husky: huskyOptions }: ProjectOptions) {
225
340
  if (!huskyOptions || this.parent) {
226
341
  return
227
342
  }
@@ -233,14 +348,93 @@ export class Project extends BaseProject {
233
348
  this.tryFindObjectFile('package.json')?.addDeletionOverride('pnpm')
234
349
  }
235
350
 
236
- #configureLintSynthesized({ lintSynthesizedOptions }: ProjectOptions) {
237
- new LintSynthesized(
351
+ #configureJestConfig({ jestConfig: jestConfigOptions }: ProjectOptions) {
352
+ if (!jestConfigOptions || this.parent) {
353
+ return
354
+ }
355
+
356
+ this.jestConfig = new JestConfig(this, jestConfigOptions)
357
+
358
+ this.typeScriptConfig?.addFile(this.jestConfig.path)
359
+ }
360
+
361
+ #configureLicense({ package: pkg }: ProjectOptions) {
362
+ if (!pkg?.license) {
363
+ return
364
+ }
365
+
366
+ if (!pkg.authorName) {
367
+ throw new Error(
368
+ 'Missing package author name. Set `package.authorName` in the project',
369
+ )
370
+ }
371
+
372
+ this.license = new License(this, {
373
+ spdx: pkg.license,
374
+ copyrightHolder: [
375
+ pkg.authorName,
376
+ pkg.authorEmail ? `<${pkg.authorEmail}>` : undefined,
377
+ pkg.authorUrl ? `(${pkg.authorUrl})` : undefined,
378
+ ]
379
+ .filter(Boolean)
380
+ .join(' '),
381
+ year: pkg.copyrightYear ?? new Date().getFullYear().toString(),
382
+ })
383
+ }
384
+
385
+ #configureLintStaged({ lintStaged: lintStagedOptions }: ProjectOptions) {
386
+ if (!lintStagedOptions) {
387
+ return
388
+ }
389
+
390
+ const defaults: LintStagedOptions = {
391
+ filename: 'lint-staged.config.mjs',
392
+ extends: '@langri-sha/lint-staged',
393
+ }
394
+
395
+ this.lintStaged = new LintStaged(
238
396
  this,
239
- lintSynthesizedOptions ?? {
240
- 'package.json': 'pnpx sort-package-json',
241
- '*.{js,jsx,ts,tsx}': 'pnpm eslint --fix',
242
- '*': 'pnpm prettier --write --ignore-unknown',
243
- },
397
+ deepMerge(defaults, lintStagedOptions),
398
+ )
399
+
400
+ this.typeScriptConfig?.addFile(this.lintStaged!.path)
401
+ }
402
+
403
+ #configureLintSynthesized({ lintSynthesized }: ProjectOptions) {
404
+ if (!lintSynthesized) {
405
+ return
406
+ }
407
+
408
+ const defaults: LintSynthesizedOptions = {
409
+ 'package.json': 'pnpx sort-package-json',
410
+ '*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}': 'pnpm eslint --fix',
411
+ '*': 'pnpm prettier --write --ignore-unknown',
412
+ }
413
+
414
+ new LintSynthesized(this, deepMerge(defaults, lintSynthesized))
415
+ }
416
+
417
+ #configureNpmIgnore({
418
+ typeScriptConfig,
419
+ jestConfig,
420
+ npmIgnore,
421
+ }: ProjectOptions) {
422
+ if (!npmIgnore) {
423
+ return
424
+ }
425
+
426
+ const defaults: IgnoreFileOptions = {
427
+ ignorePatterns: [
428
+ '.*',
429
+ ...(jestConfig ? ['*.test.*', '__snapshots__/'] : []),
430
+ ...(typeScriptConfig ? ['tsconfig*.json'] : []),
431
+ ],
432
+ }
433
+
434
+ this.npmIgnore = new IgnoreFile(
435
+ this,
436
+ '.npmignore',
437
+ deepMerge(defaults, npmIgnore),
244
438
  )
245
439
  }
246
440
 
@@ -249,17 +443,16 @@ export class Project extends BaseProject {
249
443
  return
250
444
  }
251
445
 
252
- const defaults: javascript.NodePackageOptions = {
446
+ const defaults: NodePackageOptions = {
253
447
  entrypoint: 'src/index.ts',
254
448
  packageManager: javascript.NodePackageManager.PNPM,
255
- minNodeVersion: '20.12.0',
256
449
  }
257
450
 
258
- this.package = new javascript.NodePackage(this, deepMerge(defaults, pkg))
451
+ this.package = new NodePackage(this, deepMerge(defaults, pkg))
259
452
 
260
453
  if (!this.parent) {
261
454
  this.package.addDevDeps('@langri-sha/projen-project@*')
262
- this.package.addDevDeps('projen@0.81.15')
455
+ this.package.addDevDeps('projen@0.82.4')
263
456
  }
264
457
 
265
458
  this.package.removeScript('start')
@@ -270,15 +463,52 @@ export class Project extends BaseProject {
270
463
  }
271
464
  }
272
465
 
273
- #configureProjenrc() {
274
- if (this.parent) {
466
+ #configurePnpmWorkspace({ pnpmWorkspace }: ProjectOptions) {
467
+ if (!pnpmWorkspace) {
468
+ return
469
+ }
470
+
471
+ this.pnpmWorkspace = new PnpmWorkspace(this, pnpmWorkspace)
472
+
473
+ for (const workspace of pnpmWorkspace.packages ?? []) {
474
+ this.prettier?.ignore.exclude(`${workspace}/lib/`)
475
+ this.eslint?.ignorePatterns.push(`${workspace}/lib/`)
476
+ }
477
+ }
478
+
479
+ #configurePrettier({ prettier: prettierOptions }: ProjectOptions) {
480
+ if (!prettierOptions || this.parent) {
275
481
  return
276
482
  }
277
483
 
484
+ const defaults: PrettierOptions = {
485
+ filename: 'prettier.config.mjs',
486
+ extends: '@langri-sha/prettier',
487
+ ignorePatterns: ['.*'],
488
+ }
489
+
490
+ this.prettier = new Prettier(this, deepMerge(defaults, prettierOptions))
491
+
492
+ if (this.projenrc?.filePath) {
493
+ this.prettier.ignore.include(this.projenrc.filePath)
494
+ }
495
+
496
+ if (this.package?.packageManager === javascript.NodePackageManager.PNPM) {
497
+ this.prettier.ignore.addPatterns('pnpm-lock.yaml')
498
+ }
499
+
500
+ this.typeScriptConfig?.addFile(this.prettier!.path)
501
+ }
502
+
503
+ #configureProjenrc() {
278
504
  this.projenrc = new ProjenrcFile(this, {})
505
+
506
+ if (!this.parent) {
507
+ this.typeScriptConfig?.addFile(this.projenrc.filePath)
508
+ }
279
509
  }
280
510
 
281
- #configureRenovate({ renovateOptions }: ProjectOptions) {
511
+ #configureRenovate({ renovate: renovateOptions }: ProjectOptions) {
282
512
  if (!renovateOptions || this.parent) {
283
513
  return
284
514
  }
@@ -288,16 +518,42 @@ export class Project extends BaseProject {
288
518
  extends: ['config:recommended'],
289
519
  labels: ['dependencies'],
290
520
  reviewersFromCodeOwners: true,
521
+ lockFileMaintenance: {
522
+ enabled: true,
523
+ },
291
524
  customManagers: [
525
+ {
526
+ customType: 'regex',
527
+ datasourceTemplate: 'node',
528
+ depNameTemplate: 'node',
529
+ versioningTemplate: 'node',
530
+ currentValueTemplate: '>= {{currentValue}}',
531
+ fileMatch: ['\\.?projen.*.(js|cjs|mjs|ts|mts|cts)$'],
532
+ matchStrings: ["minNodeVersion:\\s*'(?<currentValue>[^']+)'"],
533
+ },
534
+ {
535
+ customType: 'regex',
536
+ datasourceTemplate: 'npm',
537
+ fileMatch: ['\\.?projen.*.(js|cjs|mjs|ts|mts|cts)$'],
538
+ matchStringsStrategy: 'recursive',
539
+ matchStrings: [
540
+ '\\.(?<depType>addDeps|addDevDeps|addPeerDeps)\\([^)]*\\)',
541
+ "'(?<depName>@?[\\w-\\/]+)@(?<currentValue>[^']+)'",
542
+ ],
543
+ depTypeTemplate:
544
+ "{{#if (equals depType 'addDeps')}}dependencies{{else if (equals depType 'addDevDeps')}}devDependencies{{else}}peerDependencies{{/if}}",
545
+ },
292
546
  {
293
547
  customType: 'regex',
294
548
  datasourceTemplate: 'npm',
295
549
  fileMatch: ['\\.?projen.*.(js|cjs|mjs|ts|mts|cts)$'],
550
+ matchStringsStrategy: 'recursive',
296
551
  matchStrings: [
297
- "\\.(?<depType>addDep|addDevDep|addPeerDep)\\('(?<depName>[a-zA-Z0-9-]+)@(?<currentValue>[^']+)'\\)",
552
+ '(?<depType>deps|devDeps|peerDeps):\\s*\\[[^\\]]*\\]',
553
+ "'(?<depName>@?[\\w-\\/]+)@(?<currentValue>[^']+)'",
298
554
  ],
299
555
  depTypeTemplate:
300
- "{{#if (equals depType 'addDevDep')}}devDependencies{{else if (equals depType 'addPeerDep')}}peerDependencies{{else}}dependencies{{/if}}",
556
+ "{{#if (equals depType 'deps')}}dependencies{{else if (equals depType 'devDeps')}}devDependencies{{else}}peerDependencies{{/if}}",
301
557
  },
302
558
  {
303
559
  customType: 'regex',
@@ -310,10 +566,18 @@ export class Project extends BaseProject {
310
566
  ],
311
567
  }
312
568
 
569
+ this.addTask('renovate', {
570
+ description: 'Run Renovate locally for debugging',
571
+ exec: 'pnpx renovate --platform=local --repository-cache=reset --dry-run=full',
572
+ })
573
+
313
574
  this.renovate = new Renovate(this, deepMerge(defaults, renovateOptions))
314
575
  }
315
576
 
316
- #configureTypeScript({ parent, typeScriptConfigOptions }: ProjectOptions) {
577
+ #configureTypeScript({
578
+ parent,
579
+ typeScriptConfig: typeScriptConfigOptions,
580
+ }: ProjectOptions) {
317
581
  if (!typeScriptConfigOptions) {
318
582
  return
319
583
  }
@@ -344,30 +608,50 @@ export class Project extends BaseProject {
344
608
  this.package?.addDevDeps('ts-node@10.9.2')
345
609
  }
346
610
 
347
- this.package?.addDevDeps('@langri-sha/tsconfig@*')
611
+ if (this.name !== '@langri-sha/tsconfig') {
612
+ this.package?.addDevDeps('@langri-sha/tsconfig@*')
613
+ }
348
614
  }
349
615
 
350
- #createPnpmWorkspaces({ workspaces }: ProjectOptions) {
351
- if (!workspaces) {
616
+ #populateTypeScriptProjectReferencesFromDependencies() {
617
+ if (this.parent || !this.package || !this.typeScriptConfig) {
352
618
  return
353
619
  }
354
620
 
355
- new YamlFile(this, 'pnpm-workspace.yaml', {
356
- readonly: true,
357
- marker: true,
358
- obj: {
359
- packages: workspaces,
360
- },
361
- })
621
+ const subprojects = this.allSubprojectsKind.filter(
622
+ (project) => project.package && project.typeScriptConfig,
623
+ )
624
+
625
+ const root = path.dirname(this.package!.file.absolutePath)
626
+
627
+ for (const project of subprojects) {
628
+ const from = path.dirname(project.package!.file.absolutePath)
629
+
630
+ if (project.name !== '@langri-sha/tsconfig') {
631
+ this.typeScriptConfig!.addReference(path.relative(root, from))
632
+ }
633
+
634
+ for (const dep of project.deps.all) {
635
+ const reference = subprojects.find(({ name }) => name === dep.name)
636
+
637
+ if (!reference) {
638
+ continue
639
+ }
640
+
641
+ const to = path.dirname(reference.package!.file.absolutePath)
642
+
643
+ project.typeScriptConfig!.addReference(path.relative(from, to))
644
+ }
645
+ }
362
646
  }
363
647
  }
364
648
 
365
649
  const getGitIgnoreOptions = ({
366
- huskyOptions,
367
- typeScriptConfigOptions,
368
- withTerraform,
369
- parent,
370
650
  gitIgnoreOptions,
651
+ husky: huskyOptions,
652
+ parent,
653
+ typeScriptConfig: typeScriptConfigOptions,
654
+ withTerraform,
371
655
  ...options
372
656
  }: ProjectOptions): ProjectOptions['gitIgnoreOptions'] =>
373
657
  parent
@@ -393,7 +677,8 @@ const getGitIgnoreOptions = ({
393
677
  !.github/
394
678
  ${huskyOptions ? '!.husky/' : ''}
395
679
  !.projen/
396
- ${options.workspaces?.map((workspace) => `${workspace}/lib/`).join('\n') ?? ''}
680
+ dist/
681
+ ${options.pnpmWorkspace?.packages?.map((workspace) => `${workspace}/lib/`).join('\n') ?? ''}
397
682
  node_modules/
398
683
  `
399
684
  .split('\n')
@@ -403,4 +688,11 @@ const getGitIgnoreOptions = ({
403
688
  ],
404
689
  }
405
690
 
406
- const deepMerge = R.mergeDeepWith(R.concat)
691
+ const deepMerge = R.mergeDeepWith(
692
+ R.cond([
693
+ // When both arguments are strings, use the right string.
694
+ [R.allPass([R.is(String), R.is(String)]), R.nthArg(1)],
695
+ // Otherwise, concatenate.
696
+ [R.T, R.concat],
697
+ ]),
698
+ )
package/src/lib/index.ts CHANGED
@@ -1 +1,2 @@
1
+ export * from './node-package.js'
1
2
  export * from './projenrc.js'