pob 28.4.0 → 29.0.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/CHANGELOG.md CHANGED
@@ -3,6 +3,32 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [29.0.0](https://github.com/christophehurpeau/pob/compare/pob@28.4.0...pob@29.0.0) (2025-08-24)
7
+
8
+ ### ⚠ BREAKING CHANGES
9
+
10
+ * drop node 20
11
+
12
+ ### Features
13
+
14
+ * drop node 20 ([bb57350](https://github.com/christophehurpeau/pob/commit/bb573502c71f7316a54b4ce75203c05c0ba2e681))
15
+ * update CI node version references ([df90418](https://github.com/christophehurpeau/pob/commit/df9041811b28c88e6334288f308f7a5d9b4bdd07))
16
+
17
+ ### Bug Fixes
18
+
19
+ * comment out removal of alp-rollup-plugin-config dependency in CommonBabelGenerator ([8e496f5](https://github.com/christophehurpeau/pob/commit/8e496f5d1981e5875aee148c063a0c0002a097b2))
20
+ * only apply tsconfig.test.ts if this.options.onlyLatestLTS is disabled ([da85d7a](https://github.com/christophehurpeau/pob/commit/da85d7a9077d2b23cbf35406a364b5500f942c10))
21
+ * refactor environment handling in CommonTranspilerGenerator to use envsWithDefault for improved clarity and maintainability ([4421be3](https://github.com/christophehurpeau/pob/commit/4421be37a8098fc13ad29175288a2d036a99264d))
22
+ * remove unnecessary tsconfig.test.json handling in MonorepoTypescriptGenerator ([453c5da](https://github.com/christophehurpeau/pob/commit/453c5da00a8269a8451e61ffb8b729d28169a020))
23
+ * update moduleResolution in tsconfig to use 'bundler' for better compatibility ([141e0d3](https://github.com/christophehurpeau/pob/commit/141e0d39b62894b1dadd9aef7256f8060c9cc1bf))
24
+ * update pkg options before to allow proper order and remove @babel/core @babel/runtime on pob-babel disable ([560002c](https://github.com/christophehurpeau/pob/commit/560002c2076e6f263cabd78c1a0dc9540a2f2b0a))
25
+
26
+ Version bump for dependency: @pob/sort-object
27
+ Version bump for dependency: @pob/sort-pkg
28
+ Version bump for dependency: yarn-workspace-utils
29
+ Version bump for dependency: @pob/root
30
+
31
+
6
32
  ## [28.4.0](https://github.com/christophehurpeau/pob/compare/pob@28.3.0...pob@28.4.0) (2025-08-23)
7
33
 
8
34
  ### Features
@@ -1,5 +1,5 @@
1
1
  import Generator from "yeoman-generator";
2
- import { latestLTS, maintenanceLTS } from "../../../utils/node.js";
2
+ import { latestLTS, maintenanceLTS } from "../../../utils/nodeVersions.js";
3
3
  import * as packageUtils from "../../../utils/package.js";
4
4
  import { copyAndFormatTpl } from "../../../utils/writeAndFormat.js";
5
5
 
@@ -106,7 +106,9 @@ export default class CommonBabelGenerator extends Generator {
106
106
  env.version === "16" ||
107
107
  env.version === "18" ||
108
108
  env.version === "20" ||
109
+ env.version === "22" ||
109
110
  (this.options.onlyLatestLTS &&
111
+ maintenanceLTS !== latestLTS &&
110
112
  env.version === `${maintenanceLTS}`)
111
113
  ) {
112
114
  return this.options.onlyLatestLTS
@@ -162,14 +164,14 @@ export default class CommonBabelGenerator extends Generator {
162
164
  default: nodeVersions,
163
165
  choices: [
164
166
  {
165
- name: "22 (Active LTS)",
167
+ name: `${latestLTS} (Active LTS)`,
166
168
  value: `${latestLTS}`,
167
169
  },
168
- {
169
- name: "20 (Maintenance LTS)",
170
+ latestLTS !== maintenanceLTS && {
171
+ name: `${maintenanceLTS} (Maintenance LTS)`,
170
172
  value: `${maintenanceLTS}`,
171
173
  },
172
- ],
174
+ ].filter(Boolean),
173
175
  },
174
176
 
175
177
  // {
@@ -312,7 +314,12 @@ export default class CommonBabelGenerator extends Generator {
312
314
  } else if (pkg.dependencies && pkg.dependencies["pob-babel"] && !useBabel) {
313
315
  packageUtils.removeDevDependencies(pkg, ["@babel/core"]);
314
316
  }
315
- packageUtils.addOrRemoveDevDependencies(pkg, useBabel, ["pob-babel"]);
317
+ if (!useBabel && pkg.devDependencies && pkg.devDependencies["pob-babel"]) {
318
+ packageUtils.removeDevDependencies(pkg, ["pob-babel", "@babel/core"]);
319
+ packageUtils.removeDependencies(pkg, ["@babel/runtime"]);
320
+ } else {
321
+ packageUtils.addOrRemoveDevDependencies(pkg, useBabel, ["pob-babel"]);
322
+ }
316
323
 
317
324
  if (pkg.dependencies && pkg.dependencies["pob-babel"]) {
318
325
  // update pob-babel in alp-dev
@@ -400,7 +407,7 @@ export default class CommonBabelGenerator extends Generator {
400
407
  /* pob-babel config */
401
408
 
402
409
  packageUtils.removeDevDependencies(pkg, ["@rollup/plugin-run"]);
403
- packageUtils.removeDependencies(pkg, ["alp-rollup-plugin-config"]);
410
+ // packageUtils.removeDependencies(pkg, ["alp-rollup-plugin-config"]); see TranspilerGenerator
404
411
 
405
412
  this.fs.delete("rollup.config.js");
406
413
  if (useBabel) {
@@ -1,4 +1,5 @@
1
1
  import Generator from "yeoman-generator";
2
+ import { latestLTS } from "../../../utils/nodeVersions.js";
2
3
  import * as packageUtils from "../../../utils/package.js";
3
4
 
4
5
  export default class CommonReleaseGenerator extends Generator {
@@ -93,6 +94,7 @@ export default class CommonReleaseGenerator extends Generator {
93
94
  isMonorepoIndependent:
94
95
  this.options.isMonorepo &&
95
96
  (!pkg.version || pkg.version === "0.0.0"),
97
+ nodeLatestMajorVersion: latestLTS,
96
98
  },
97
99
  );
98
100
  } else {
@@ -36,7 +36,7 @@ jobs:
36
36
 
37
37
  - uses: actions/setup-node@v4
38
38
  with:
39
- node-version: 22
39
+ node-version: <%= nodeLatestMajorVersion %>
40
40
  check-latest: true
41
41
 
42
42
  - name: Check git tags
@@ -2,7 +2,6 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import Generator from "yeoman-generator";
4
4
  import inMonorepo from "../../../utils/inMonorepo.js";
5
- import { latestLTS, maintenanceLTS } from "../../../utils/node.js";
6
5
  import * as packageUtils from "../../../utils/package.js";
7
6
  import {
8
7
  copyAndFormatTpl,
@@ -195,12 +194,10 @@ export default class CommonTestingGenerator extends Generator {
195
194
  if (testRunner === "vitest") return undefined;
196
195
  if (!withTypescript) return undefined;
197
196
  if (this.options.swc || isJestRunner) return "swc";
198
- if (this.options.onlyLatestLTS) return "node";
199
- return "ts-node";
197
+ return "node";
200
198
  })();
201
199
 
202
200
  const dependenciesForTestUtil = {
203
- "ts-node": { devDependenciesShared: ["ts-node"] },
204
201
  swc: {
205
202
  devDependenciesShared: ["@swc/core"],
206
203
  devDependenciesWithJest: ["@swc/jest"],
@@ -273,9 +270,7 @@ export default class CommonTestingGenerator extends Generator {
273
270
  const tsTestLoaderOption = (() => {
274
271
  switch (tsTestUtil) {
275
272
  case "node":
276
- return "--disable-warning=ExperimentalWarning --experimental-strip-types";
277
- case "ts-node":
278
- return "--loader=ts-node/esm --experimental-specifier-resolution=node";
273
+ return "";
279
274
  case "swc":
280
275
  return "--import=@swc-node/register/esm";
281
276
 
@@ -314,10 +309,6 @@ export default class CommonTestingGenerator extends Generator {
314
309
  }
315
310
  const experimentalTestCoverage = false; // todo configure src directory and remove test files
316
311
  return `TZ=UTC ${
317
- tsTestUtil === "ts-node"
318
- ? "TS_NODE_PROJECT=tsconfig.test.json "
319
- : ""
320
- }${
321
312
  coverage || coverageJson
322
313
  ? `npx c8${
323
314
  coverageJson
@@ -491,21 +482,7 @@ export default class CommonTestingGenerator extends Generator {
491
482
  }
492
483
  } else {
493
484
  const tsconfigTestPath = this.destinationPath("tsconfig.test.json");
494
- if (tsTestUtil === "ts-node" && withTypescript) {
495
- const nodeVersion = this.options.onlyLatestLTS
496
- ? `${latestLTS}`
497
- : `${maintenanceLTS}`;
498
- copyAndFormatTpl(
499
- this.fs,
500
- this.templatePath("tsconfig.test.json.ejs"),
501
- tsconfigTestPath,
502
- {
503
- nodeVersion,
504
- },
505
- );
506
- } else {
507
- this.fs.delete(tsconfigTestPath);
508
- }
485
+ this.fs.delete(tsconfigTestPath);
509
486
 
510
487
  if (globalTesting) {
511
488
  if (pkg.scripts) {
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import semver from "semver";
3
3
  import Generator from "yeoman-generator";
4
- import { latestLTS, maintenanceLTS } from "../../../utils/node.js";
4
+ import { latestLTS, maintenanceLTS } from "../../../utils/nodeVersions.js";
5
5
  import * as packageUtils from "../../../utils/package.js";
6
6
  import { copyAndFormatTpl } from "../../../utils/writeAndFormat.js";
7
7
 
@@ -328,15 +328,19 @@ export default class CommonTranspilerGenerator extends Generator {
328
328
  delete pkg["browser-dev"];
329
329
  delete pkg["module-dev"];
330
330
 
331
- const envs = pkg.pob.babelEnvs ||
332
- pkg.pob.envs || [
333
- {
334
- target: "node",
335
- version: "18",
336
- },
337
- ];
331
+ let envs = pkg.pob.babelEnvs || pkg.pob.envs;
332
+
333
+ const envsWithDefault =
334
+ pkg.pob.bundler === false
335
+ ? []
336
+ : envs || [
337
+ {
338
+ target: "node",
339
+ version: "22",
340
+ },
341
+ ];
338
342
 
339
- const esAllBrowserEnv = envs.find(
343
+ const esAllBrowserEnv = envsWithDefault.find(
340
344
  (env) =>
341
345
  env.target === "browser" &&
342
346
  env.version === undefined &&
@@ -386,108 +390,110 @@ export default class CommonTranspilerGenerator extends Generator {
386
390
 
387
391
  const defaultNodeEnv =
388
392
  withBabel || (withTypescript && pkg.pob?.typescript !== "check-only")
389
- ? envs.find((env) => env.target === "node")
393
+ ? envsWithDefault.find((env) => env.target === "node")
390
394
  : undefined;
391
395
 
392
396
  const defaultNodeEnvVersion = defaultNodeEnv && defaultNodeEnv.version;
393
397
 
394
- envs.forEach(
395
- ({
396
- target,
397
- version,
398
- formats,
399
- omitVersionInFileName = bundler === "tsc",
400
- }) => {
401
- if (target === "node" && entry === "browser") return;
402
-
403
- const exportTarget = {};
404
-
405
- switch (target) {
406
- case "node": {
407
- const cjsExt = pkg.type === "module" ? "cjs" : "cjs.js";
408
- const filenameWithoutExt = `${entryDistName}${
409
- omitTarget
410
- ? ""
411
- : `-${target}${omitVersionInFileName ? "" : version}`
412
- }`;
413
- if (bundler === "tsc") {
414
- if (formats) {
415
- throw new Error("tsc does not support formats");
398
+ if (envsWithDefault) {
399
+ envsWithDefault.forEach(
400
+ ({
401
+ target,
402
+ version,
403
+ formats,
404
+ omitVersionInFileName = bundler === "tsc",
405
+ }) => {
406
+ if (target === "node" && entry === "browser") return;
407
+
408
+ const exportTarget = {};
409
+
410
+ switch (target) {
411
+ case "node": {
412
+ const cjsExt = pkg.type === "module" ? "cjs" : "cjs.js";
413
+ const filenameWithoutExt = `${entryDistName}${
414
+ omitTarget
415
+ ? ""
416
+ : `-${target}${omitVersionInFileName ? "" : version}`
417
+ }`;
418
+ if (bundler === "tsc") {
419
+ if (formats) {
420
+ throw new Error("tsc does not support formats");
421
+ }
422
+ exportTarget.import = `./${this.options.buildDirectory}/${filenameWithoutExt}.js`;
423
+ } else if (!formats || formats.includes("es")) {
424
+ exportTarget.import = `./${this.options.buildDirectory}/${filenameWithoutExt}.mjs`;
425
+
426
+ if (formats && formats.includes("cjs")) {
427
+ exportTarget.require = `./${this.options.buildDirectory}/${filenameWithoutExt}.${cjsExt}`;
428
+ }
429
+ } else if (formats && formats.includes("cjs")) {
430
+ exportTarget.default = `./${this.options.buildDirectory}/${filenameWithoutExt}.${cjsExt}`;
431
+ }
432
+ // eslint: https://github.com/benmosher/eslint-plugin-import/issues/2132
433
+ // jest: https://github.com/facebook/jest/issues/9771
434
+ if (!pkg.main && exportName === ".") {
435
+ pkg.main =
436
+ pkg.type === "module"
437
+ ? exportTarget.import
438
+ : exportTarget.default ||
439
+ exportTarget.require ||
440
+ exportTarget.import;
441
+ }
442
+
443
+ break;
444
+ }
445
+ case "browser": {
446
+ if (!formats || formats.includes("es")) {
447
+ exportTarget.import = `./${
448
+ this.options.buildDirectory
449
+ }/${entryDistName}-${target}${version || ""}.es.js`;
416
450
  }
417
- exportTarget.import = `./${this.options.buildDirectory}/${filenameWithoutExt}.js`;
418
- } else if (!formats || formats.includes("es")) {
419
- exportTarget.import = `./${this.options.buildDirectory}/${filenameWithoutExt}.mjs`;
420
451
 
421
452
  if (formats && formats.includes("cjs")) {
422
- exportTarget.require = `./${this.options.buildDirectory}/${filenameWithoutExt}.${cjsExt}`;
453
+ exportTarget.require = `./${
454
+ this.options.buildDirectory
455
+ }/${entryDistName}-${target}${version || ""}.cjs.js`;
423
456
  }
424
- } else if (formats && formats.includes("cjs")) {
425
- exportTarget.default = `./${this.options.buildDirectory}/${filenameWithoutExt}.${cjsExt}`;
426
- }
427
- // eslint: https://github.com/benmosher/eslint-plugin-import/issues/2132
428
- // jest: https://github.com/facebook/jest/issues/9771
429
- if (!pkg.main && exportName === ".") {
430
- pkg.main =
431
- pkg.type === "module"
432
- ? exportTarget.import
433
- : exportTarget.default ||
434
- exportTarget.require ||
435
- exportTarget.import;
436
- }
437
457
 
438
- break;
439
- }
440
- case "browser": {
441
- if (!formats || formats.includes("es")) {
442
- exportTarget.import = `./${
443
- this.options.buildDirectory
444
- }/${entryDistName}-${target}${version || ""}.es.js`;
458
+ break;
445
459
  }
460
+ case "react-native": {
461
+ if (!formats || formats.includes("es")) {
462
+ exportTarget.import = `./${
463
+ this.options.buildDirectory
464
+ }/${entryDistName}-${target}.es.js`;
465
+ }
446
466
 
447
- if (formats && formats.includes("cjs")) {
448
- exportTarget.require = `./${
449
- this.options.buildDirectory
450
- }/${entryDistName}-${target}${version || ""}.cjs.js`;
451
- }
467
+ if (formats && formats.includes("cjs")) {
468
+ exportTarget.require = `./${
469
+ this.options.buildDirectory
470
+ }/${entryDistName}-${target}.cjs.js`;
471
+ }
452
472
 
453
- break;
454
- }
455
- case "react-native": {
456
- if (!formats || formats.includes("es")) {
457
- exportTarget.import = `./${
458
- this.options.buildDirectory
459
- }/${entryDistName}-${target}.es.js`;
473
+ break;
460
474
  }
461
-
462
- if (formats && formats.includes("cjs")) {
463
- exportTarget.require = `./${
464
- this.options.buildDirectory
465
- }/${entryDistName}-${target}.cjs.js`;
475
+ default: {
476
+ throw new Error(`Invalid target: ${target}`);
466
477
  }
467
-
468
- break;
469
- }
470
- default: {
471
- throw new Error(`Invalid target: ${target}`);
472
478
  }
473
- }
474
479
 
475
- if (
476
- !version ||
477
- (target === "node" && version === defaultNodeEnvVersion)
478
- ) {
479
- targets[target] = {
480
- ...targets[target],
481
- ...exportTarget,
482
- };
483
- } else {
484
- targets[target] = {
485
- [`${target}:${version}`]: exportTarget,
486
- ...targets[target],
487
- };
488
- }
489
- },
490
- );
480
+ if (
481
+ !version ||
482
+ (target === "node" && version === defaultNodeEnvVersion)
483
+ ) {
484
+ targets[target] = {
485
+ ...targets[target],
486
+ ...exportTarget,
487
+ };
488
+ } else {
489
+ targets[target] = {
490
+ [`${target}:${version}`]: exportTarget,
491
+ ...targets[target],
492
+ };
493
+ }
494
+ },
495
+ );
496
+ }
491
497
 
492
498
  pkg.exports[exportName] = targets;
493
499
  });
@@ -554,22 +560,23 @@ export default class CommonTranspilerGenerator extends Generator {
554
560
  pkg.exports["."] = { types: pkg.types, ...pkg.exports["."] };
555
561
  }
556
562
  }
563
+ if (
564
+ !pkg.pob.typescript &&
565
+ pkg.pob.bundler !== false &&
566
+ pkg.pob.bundler?.startsWith("rollup-") &&
567
+ pkg.pob.bundler !== "rollup-babel"
568
+ ) {
569
+ pkg.pob.typescript = true;
570
+ }
557
571
 
558
572
  Object.keys(pkg).forEach((key) => {
559
573
  if (!key.startsWith("module:") && !key.startsWith("webpack:")) return;
560
574
  delete pkg[key];
561
575
  });
562
576
 
563
- this.fs.writeJSON(this.destinationPath("package.json"), pkg);
564
- }
565
-
566
- writing() {
567
- const pkg = this.fs.readJSON(this.destinationPath("package.json"));
568
- const entries = pkg.pob.entries || ["index"];
569
- let envs = pkg.pob.envs || pkg.pob.babelEnvs;
570
577
  delete pkg.pob.withReact;
571
578
 
572
- if (envs) {
579
+ if (envs && pkg.pob.bundler !== false) {
573
580
  if (
574
581
  !envs.some(
575
582
  (env) =>
@@ -582,7 +589,7 @@ export default class CommonTranspilerGenerator extends Generator {
582
589
  envs.some(
583
590
  (env) =>
584
591
  env.target === "node" &&
585
- (["8", "6", "10", "12", "14", "16", "18"].includes(
592
+ (["8", "6", "10", "12", "14", "16", "18", "20"].includes(
586
593
  String(env.version),
587
594
  ) ||
588
595
  (this.options.onlyLatestLTS &&
@@ -594,7 +601,8 @@ export default class CommonTranspilerGenerator extends Generator {
594
601
  version: this.options.onlyLatestLTS
595
602
  ? `${latestLTS}`
596
603
  : `${maintenanceLTS}`,
597
- omitVersionInFileName: this.options.onlyLatestLTS ? true : undefined,
604
+ omitVersionInFileName:
605
+ this.options.onlyLatestLTS || envs.length === 1 ? true : undefined,
598
606
  });
599
607
  }
600
608
  envs = envs.filter(
@@ -611,6 +619,14 @@ export default class CommonTranspilerGenerator extends Generator {
611
619
  }
612
620
  }
613
621
 
622
+ this.fs.writeJSON(this.destinationPath("package.json"), pkg);
623
+ }
624
+
625
+ writing() {
626
+ const pkg = this.fs.readJSON(this.destinationPath("package.json"));
627
+ const entries = pkg.pob.entries || ["index"];
628
+ const envs = pkg.pob.envs || pkg.pob.babelEnvs;
629
+
614
630
  const hasTargetNode = envs && envs.some((env) => env.target === "node");
615
631
  const hasTargetBrowser =
616
632
  envs && envs.some((env) => env.target === "browser");
@@ -636,16 +652,17 @@ export default class CommonTranspilerGenerator extends Generator {
636
652
  case "16":
637
653
  case "18":
638
654
  case "20":
655
+ case "22":
639
656
  if (
640
657
  envs ||
641
658
  !pkg.engines.node ||
642
- !pkg.engines.node.startsWith(">=22")
659
+ !pkg.engines.node.startsWith(">=24")
643
660
  ) {
644
- pkg.engines.node = ">=20.11.0";
661
+ pkg.engines.node = ">=22.18.0";
645
662
  }
646
663
  break;
647
- case "22":
648
- pkg.engines.node = ">=22.14.0";
664
+ case "24":
665
+ pkg.engines.node = ">=24.0.0";
649
666
  break;
650
667
  default:
651
668
  throw new Error(`Invalid min node version: ${minNodeVersion}`);
@@ -669,7 +686,7 @@ export default class CommonTranspilerGenerator extends Generator {
669
686
  packageUtils.removeDevDependencies(pkg, ["@types/node"]);
670
687
 
671
688
  // Supports oldest current or active LTS version of node
672
- const minVersion = this.options.onlyLatestLTS ? "22.14.0" : "20.11.0";
689
+ const minVersion = this.options.onlyLatestLTS ? "22.18.0" : "22.18.0";
673
690
 
674
691
  if (
675
692
  !pkg.engines.node ||
@@ -680,13 +697,6 @@ export default class CommonTranspilerGenerator extends Generator {
680
697
  }
681
698
 
682
699
  this.fs.delete("rollup.config.js");
683
- if (
684
- !pkg.pob.typescript &&
685
- pkg.pob.bundler?.startsWith("rollup-") &&
686
- pkg.pob.bundler !== "rollup-babel"
687
- ) {
688
- pkg.pob.typescript = true;
689
- }
690
700
  if (
691
701
  pkg.pob.typescript === true &&
692
702
  pkg.pob.rollup !== false &&
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import Generator from "yeoman-generator";
3
3
  import inMonorepo from "../../../utils/inMonorepo.js";
4
- import { latestLTS, maintenanceLTS } from "../../../utils/node.js";
4
+ import { latestLTS, maintenanceLTS } from "../../../utils/nodeVersions.js";
5
5
  import * as packageUtils from "../../../utils/package.js";
6
6
  import { copyAndFormatTpl } from "../../../utils/writeAndFormat.js";
7
7
 
@@ -67,7 +67,7 @@
67
67
  <% if(nextConfig) { -%>
68
68
  "strictNullChecks": true,
69
69
  "module": "esnext",
70
- "moduleResolution": "node",
70
+ "moduleResolution": "bundler",
71
71
  "lib": ["dom", "esnext"],
72
72
  "esModuleInterop": true,
73
73
  "isolatedModules": true,
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import Generator from "yeoman-generator";
3
3
  import inMonorepo from "../../../utils/inMonorepo.js";
4
- import { latestLTS, maintenanceLTS } from "../../../utils/node.js";
4
+ import { latestLTS, maintenanceLTS } from "../../../utils/nodeVersions.js";
5
5
  import * as packageUtils from "../../../utils/package.js";
6
6
  import { copyAndFormatTpl } from "../../../utils/writeAndFormat.js";
7
7
 
@@ -170,6 +170,8 @@ export default class CoreCIGenerator extends Generator {
170
170
  inMonorepo &&
171
171
  inMonorepo.root &&
172
172
  inMonorepo.pobConfig?.project?.type === "lib",
173
+ nodeLatestMajorVersion: latestLTS,
174
+ nodeMaintenanceMajorVersion: maintenanceLTS,
173
175
  },
174
176
  );
175
177
 
@@ -207,6 +209,7 @@ export default class CoreCIGenerator extends Generator {
207
209
  testing: this.options.testing,
208
210
  testRunner: this.options.testRunner,
209
211
  typedoc: this.options.documentation && this.options.typescript,
212
+ nodeLatestMajorVersion: latestLTS,
210
213
  },
211
214
  );
212
215
  } else {
@@ -12,7 +12,7 @@ jobs:
12
12
 
13
13
  - uses: actions/setup-node@v4
14
14
  with:
15
- node-version: 22
15
+ node-version: <%= nodeLatestMajorVersion %>
16
16
  check-latest: true
17
17
 
18
18
  <% if (packageManager === 'yarn') { -%>
@@ -12,7 +12,7 @@ jobs:
12
12
 
13
13
  - uses: actions/setup-node@v4
14
14
  with:
15
- node-version: 22
15
+ node-version: <%= nodeLatestMajorVersion %>
16
16
  check-latest: true
17
17
 
18
18
  - name: Enable Corepack
@@ -42,7 +42,7 @@ jobs:
42
42
 
43
43
  - uses: actions/setup-node@v4
44
44
  with:
45
- node-version: 22
45
+ node-version: <%= nodeLatestMajorVersion %>
46
46
  check-latest: true
47
47
 
48
48
  - name: Enable Corepack
@@ -74,7 +74,7 @@ jobs:
74
74
 
75
75
  - uses: actions/setup-node@v4
76
76
  with:
77
- node-version: 22
77
+ node-version: <%= nodeLatestMajorVersion %>
78
78
  check-latest: true
79
79
 
80
80
  - name: Enable Corepack
@@ -104,7 +104,7 @@ jobs:
104
104
 
105
105
  strategy:
106
106
  matrix:
107
- node-version: [20, 22]
107
+ node-version: [<% if (!onlyLatestLTS && nodeMaintenanceMajorVersion !== nodeLatestMajorVersion) { -%><%= nodeMaintenanceMajorVersion %>, <% } -%><%= nodeLatestMajorVersion %>]
108
108
 
109
109
  steps:
110
110
  - uses: actions/checkout@v4
@@ -135,17 +135,17 @@ jobs:
135
135
  <% if (codecov) { -%>
136
136
  - name: Test
137
137
  run: <%= packageManager %> run test
138
- if: matrix.node-version != 22
138
+ if: matrix.node-version != <%= nodeLatestMajorVersion %>
139
139
 
140
140
  - name: Generate Test Coverage
141
141
  run: <%= packageManager %> run test:coverage:json
142
- if: matrix.node-version == 22
142
+ if: matrix.node-version == <%= nodeLatestMajorVersion %>
143
143
  env:
144
144
  CI: true
145
145
 
146
146
  - name: Send results to codecov
147
147
  uses: codecov/codecov-action@v5
148
- if: matrix.node-version == 22 && github.actor != 'dependabot[bot]'
148
+ if: matrix.node-version == <%= nodeLatestMajorVersion %> && github.actor != 'dependabot[bot]'
149
149
  with:
150
150
  fail_ci_if_error: true
151
151
  token: ${{ secrets.CODECOV_TOKEN }}
@@ -162,7 +162,7 @@ jobs:
162
162
 
163
163
  strategy:
164
164
  matrix:
165
- node-version: [22]
165
+ node-version: [<%= nodeLatestMajorVersion %>]
166
166
 
167
167
  steps:
168
168
  - uses: actions/checkout@v4
@@ -196,7 +196,7 @@ jobs:
196
196
 
197
197
  - uses: actions/setup-node@v4
198
198
  with:
199
- node-version: 22
199
+ node-version: <%= nodeLatestMajorVersion %>
200
200
  check-latest: true
201
201
 
202
202
  - uses: GoogleCloudPlatform/release-please-action@v3
@@ -8,7 +8,7 @@ jobs:
8
8
 
9
9
  strategy:
10
10
  matrix:
11
- node-version: [<% if (!onlyLatestLTS) { -%>20.x, <% } -%>22.x]
11
+ node-version: [<% if (!onlyLatestLTS && nodeMaintenanceMajorVersion !== nodeLatestMajorVersion) { -%><%= nodeMaintenanceMajorVersion %>.x, <% } -%><%= nodeLatestMajorVersion %>.x]
12
12
 
13
13
  steps:
14
14
  - uses: actions/checkout@v4
@@ -44,39 +44,39 @@ jobs:
44
44
  <% if (checks) { -%>
45
45
  - name: Checks
46
46
  run: <%= packageManager %> run checks
47
- if: startsWith(matrix.node-version, '22.')
47
+ if: startsWith(matrix.node-version, '<%= nodeLatestMajorVersion %>.')
48
48
 
49
49
  <% } -%>
50
50
  <% if (build) { -%>
51
51
  - name: Build
52
52
  run: yarn run build
53
- if: startsWith(matrix.node-version, '22.')
53
+ if: startsWith(matrix.node-version, '<%= nodeLatestMajorVersion %>.')
54
54
 
55
55
  <% } -%>
56
56
  - name: Prettier
57
57
  run: <%= packageManager %> run lint:prettier
58
- if: startsWith(matrix.node-version, '22.')
58
+ if: startsWith(matrix.node-version, '<%= nodeLatestMajorVersion %>.')
59
59
  <% if (typescript) { -%>
60
60
 
61
61
  - name: Typescript
62
62
  run: yarn run tsc
63
- if: startsWith(matrix.node-version, '22.')
63
+ if: startsWith(matrix.node-version, '<%= nodeLatestMajorVersion %>.')
64
64
  <% } -%>
65
65
 
66
66
  - name: Eslint
67
67
  run: <%= packageManager %> run lint:eslint
68
- if: startsWith(matrix.node-version, '22.')
68
+ if: startsWith(matrix.node-version, '<%= nodeLatestMajorVersion %>.')
69
69
  <% if (true) { -%>
70
70
 
71
71
  - name: Check nothing was forgotten before commit
72
- if: startsWith(matrix.node-version, '22.')
72
+ if: startsWith(matrix.node-version, '<%= nodeLatestMajorVersion %>.')
73
73
  run: <%= packageManager === 'npm' ? 'npx' : 'yarn run' %> repository-check-dirty
74
74
  <% } -%>
75
75
  <% if (codecov) { -%>
76
76
 
77
77
  - name: Generate Test Coverage
78
78
  run: <%= packageManager %> run test:coverage:json
79
- if: startsWith(matrix.node-version, '22.')
79
+ if: startsWith(matrix.node-version, '<%= nodeLatestMajorVersion %>.')
80
80
  env:
81
81
  CI: true
82
82
 
@@ -85,7 +85,7 @@ jobs:
85
85
  with:
86
86
  fail_ci_if_error: true
87
87
  token: ${{ secrets.CODECOV_TOKEN }}
88
- if: startsWith(matrix.node-version, '22.') && github.actor != 'dependabot[bot]'
88
+ if: startsWith(matrix.node-version, '<%= nodeLatestMajorVersion %>.') && github.actor != 'dependabot[bot]'
89
89
  <% } else if (testing) { -%>
90
90
 
91
91
  - name: Test
@@ -101,7 +101,7 @@ jobs:
101
101
  <% if (isReleasePleaseEnabled) { -%>
102
102
 
103
103
  - uses: GoogleCloudPlatform/release-please-action@v3
104
- if: ${{ startsWith(matrix.node-version, '22.') && github.ref == 'refs/heads/main' }}
104
+ if: ${{ startsWith(matrix.node-version, '<%= nodeLatestMajorVersion %>.') && github.ref == 'refs/heads/main' }}
105
105
  id: release
106
106
  with:
107
107
  token: ${{ secrets.GH_TOKEN }}
@@ -111,7 +111,7 @@ jobs:
111
111
 
112
112
  # publish:
113
113
  - run: npm publish
114
- if: ${{ startsWith(matrix.node-version, '22.') && github.ref == 'refs/heads/main' && steps.release.outputs.release_created }}
114
+ if: ${{ startsWith(matrix.node-version, '<%= nodeLatestMajorVersion %>.') && github.ref == 'refs/heads/main' && steps.release.outputs.release_created }}
115
115
  env:
116
116
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
117
117
  <% } -%>
@@ -60,15 +60,15 @@ export default class CorePackageGenerator extends Generator {
60
60
  !pkg.engines.node ||
61
61
  !(
62
62
  pkg.engines.node.startsWith(">=22.") ||
63
- pkg.engines.node.startsWith(">=20.")
63
+ pkg.engines.node.startsWith(">=24.")
64
64
  )
65
65
  ) {
66
66
  // this might be overridden by babel generator
67
- pkg.engines.node = ">=20.11.0"; // .9.0 is the first lts node 20 version, 20.11.0 is the version with backported import.meta feature
67
+ pkg.engines.node = ">=22.18.0"; // 22.18.0 is the version with strip typescript out of experimental status
68
68
  }
69
69
 
70
70
  if (pkg.engines.node.startsWith(">=22.11.")) {
71
- pkg.engines.node = ">=22.14.0"; // 22.14 is the first version with findPackageJSON
71
+ pkg.engines.node = ">=22.18.0";
72
72
  }
73
73
 
74
74
  if (!this.options.isRoot) {
@@ -1,7 +1,7 @@
1
1
  import { rmSync } from "node:fs";
2
2
  import Generator from "yeoman-generator";
3
3
  import inMonorepo from "../../utils/inMonorepo.js";
4
- import { latestLTS } from "../../utils/node.js";
4
+ import { latestLTS } from "../../utils/nodeVersions.js";
5
5
  import * as packageUtils from "../../utils/package.js";
6
6
 
7
7
  export default class PobLibGenerator extends Generator {
@@ -330,6 +330,7 @@ export default class PobMonorepoGenerator extends Generator {
330
330
  packageNames: JSON.stringify(packageNames),
331
331
  packagePaths: JSON.stringify(packagePaths),
332
332
  testRunner: this.pobLernaConfig.testRunner,
333
+ onlyLatestLTS: this.options.onlyLatestLTS,
333
334
  });
334
335
 
335
336
  this.fs.writeJSON(this.destinationPath("package.json"), pkg);
@@ -1,4 +1,3 @@
1
- import { existsSync } from "node:fs";
2
1
  import Generator from "yeoman-generator";
3
2
  import * as packageUtils from "../../../utils/package.js";
4
3
  import { copyAndFormatTpl } from "../../../utils/writeAndFormat.js";
@@ -40,6 +39,12 @@ export default class MonorepoTypescriptGenerator extends Generator {
40
39
  required: false,
41
40
  default: false,
42
41
  });
42
+
43
+ this.option("onlyLatestLTS", {
44
+ type: Boolean,
45
+ required: false,
46
+ default: false,
47
+ });
43
48
  }
44
49
 
45
50
  writing() {
@@ -103,12 +108,12 @@ export default class MonorepoTypescriptGenerator extends Generator {
103
108
  const tsconfigCheckPath = this.destinationPath("tsconfig.check.json");
104
109
  const tsconfigBuildPath = this.destinationPath("tsconfig.build.json");
105
110
  const tsconfigTestPath = this.destinationPath("tsconfig.test.json");
111
+ this.fs.delete(tsconfigTestPath);
106
112
 
107
113
  if (!this.options.enable) {
108
114
  this.fs.delete(tsconfigPath);
109
115
  this.fs.delete(tsconfigCheckPath);
110
116
  this.fs.delete(tsconfigBuildPath);
111
- this.fs.delete(tsconfigTestPath);
112
117
  } else {
113
118
  const packagePaths = JSON.parse(this.options.packagePaths);
114
119
 
@@ -122,20 +127,6 @@ export default class MonorepoTypescriptGenerator extends Generator {
122
127
  },
123
128
  );
124
129
 
125
- if (this.options.testRunner === "node") {
126
- copyAndFormatTpl(
127
- this.fs,
128
- this.templatePath("tsconfig.json.ejs"),
129
- tsconfigTestPath,
130
- {
131
- packagePaths: packagePaths.filter((packagePath) =>
132
- existsSync(`${packagePath}/tsconfig.test.json`),
133
- ),
134
- tsConfigSuffix: "test",
135
- },
136
- );
137
- }
138
-
139
130
  this.fs.delete(tsconfigCheckPath);
140
131
  this.fs.delete(tsconfigBuildPath);
141
132
  // if (this.options.isAppProject) {
@@ -0,0 +1,2 @@
1
+ export const latestLTS = "22";
2
+ export const maintenanceLTS = "22";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pob",
3
- "version": "28.4.0",
3
+ "version": "29.0.0",
4
4
  "description": "Pile of bones, library generator with git/babel/typescript/typedoc/readme/jest",
5
5
  "keywords": [
6
6
  "skeleton"
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "type": "module",
20
20
  "engines": {
21
- "node": ">=22.14.0"
21
+ "node": ">=22.18.0"
22
22
  },
23
23
  "sideEffects": false,
24
24
  "bin": "./lib/pob.js",
@@ -46,8 +46,8 @@
46
46
  "@pob/eslint-config": "61.1.0",
47
47
  "@pob/eslint-config-typescript": "61.1.0",
48
48
  "@pob/eslint-config-typescript-react": "61.1.0",
49
- "@pob/sort-object": "9.0.2",
50
- "@pob/sort-pkg": "11.0.3",
49
+ "@pob/sort-object": "10.0.0",
50
+ "@pob/sort-pkg": "12.0.0",
51
51
  "@prettier/sync": "0.6.1",
52
52
  "@types/inquirer": "9.0.9",
53
53
  "@yarnpkg/cli": "4.9.2",
@@ -66,17 +66,17 @@
66
66
  "mem-fs-editor": "11.1.4",
67
67
  "minimist": "1.2.8",
68
68
  "parse-author": "2.0.0",
69
- "pob-dependencies": "19.3.0",
69
+ "pob-dependencies": "20.0.0",
70
70
  "prettier": "3.6.2",
71
71
  "semver": "7.7.2",
72
72
  "typescript": "5.8.3",
73
73
  "validate-npm-package-name": "^6.0.1",
74
- "yarn-workspace-utils": "8.10.0",
74
+ "yarn-workspace-utils": "9.0.0",
75
75
  "yeoman-environment": "4.4.3",
76
76
  "yeoman-generator": "7.5.1"
77
77
  },
78
78
  "devDependencies": {
79
- "@pob/root": "18.3.0",
79
+ "@pob/root": "19.0.0",
80
80
  "@types/node": "22.17.2"
81
81
  }
82
82
  }
@@ -1,10 +0,0 @@
1
- {
2
- "extends": [
3
- "./tsconfig.json",
4
- "@pob/root/tsconfigs/targets/node-<%= nodeVersion %>.json"
5
- ],
6
- "ts-node": {
7
- "esm": true,
8
- "transpileOnly": true
9
- }
10
- }
package/lib/utils/node.js DELETED
@@ -1,2 +0,0 @@
1
- export const latestLTS = "22";
2
- export const maintenanceLTS = "20";