pob 39.3.0 → 40.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.
@@ -1,5 +1,9 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import fs from "node:fs";
1
3
  import path from "node:path";
4
+ import { runTransform } from "@pob/codemods";
2
5
  import { format } from "oxfmt";
6
+ import semver from "semver";
3
7
  import Generator from "yeoman-generator";
4
8
  import { quoteArg } from "../../../utils/execUtils.js";
5
9
  import inMonorepo from "../../../utils/inMonorepo.js";
@@ -56,6 +60,23 @@ const isPreviousEslintConfigOutput = async (
56
60
  return false;
57
61
  };
58
62
 
63
+ // from this major, eslint no longer runs the rules checked by oxlint
64
+ const OXLINT_ONLY_ESLINT_CONFIG_MAJOR = 68;
65
+
66
+ /**
67
+ * Whether the project used a version of @pob/eslint-config running the rules
68
+ * checked by oxlint in eslint too.
69
+ *
70
+ * @param {string | undefined} range
71
+ */
72
+ const isBeforeOxlintOnlyEslintConfig = (range) => {
73
+ if (!range || !semver.validRange(range)) return false;
74
+ const minVersion = semver.minVersion(range);
75
+ return (
76
+ minVersion !== null && minVersion.major < OXLINT_ONLY_ESLINT_CONFIG_MAJOR
77
+ );
78
+ };
79
+
59
80
  // same as the base ignores of @pob/eslint-config: ignorePatterns are not
60
81
  // inherited through "extends"
61
82
  const oxlintBaseIgnorePatterns = [
@@ -202,6 +223,12 @@ export default class CommonFormatLintGenerator extends Generator {
202
223
 
203
224
  async writing() {
204
225
  const pkg = this.fs.readJSON(this.destinationPath("package.json"));
226
+ // the codemods run once from the root, which lints the whole project
227
+ this.runOxlintOnlyCodemods =
228
+ (!inMonorepo || inMonorepo.root || this.options.monorepo) &&
229
+ isBeforeOxlintOnlyEslintConfig(
230
+ pkg.devDependencies?.["@pob/eslint-config"],
231
+ );
205
232
  const babelEnvs =
206
233
  (pkg.pob &&
207
234
  (pkg.pob.babelEnvs ||
@@ -379,17 +406,14 @@ export default class CommonFormatLintGenerator extends Generator {
379
406
  pkg.name !== "@pob/use-eslint-plugin"
380
407
  ) {
381
408
  packageUtils.addDevDependencies(pkg, ["@pob/eslint-config"]);
382
- packageUtils.addOrRemoveDevDependencies(
383
- pkg,
384
- shouldHavePluginsDependencies,
385
- ["eslint-plugin-import", "eslint-plugin-unicorn"],
386
- );
387
-
388
- packageUtils.addOrRemoveDevDependencies(
389
- pkg,
390
- shouldHavePluginsDependencies,
391
- ["eslint-plugin-node"],
392
- );
409
+ // eslint plugins are dependencies of @pob/eslint-config, loaded by
410
+ // the flat config
411
+ packageUtils.removeDevDependencies(pkg, [
412
+ "eslint-plugin-import",
413
+ "eslint-plugin-node",
414
+ "eslint-plugin-unicorn",
415
+ "@typescript-eslint/eslint-plugin",
416
+ ]);
393
417
 
394
418
  if ((inMonorepo && inMonorepo.root) || this.options.monorepo) {
395
419
  if (hasReact) {
@@ -405,16 +429,18 @@ export default class CommonFormatLintGenerator extends Generator {
405
429
  "@pob/eslint-config-typescript-react",
406
430
  ]);
407
431
  }
432
+ // import-x loads the TypeScript parser by name, from the project
433
+ // with yarn PnP
408
434
  packageUtils.addOrRemoveDevDependencies(
409
435
  pkg,
410
436
  this.options.typescript && shouldHavePluginsDependencies,
411
- ["@typescript-eslint/eslint-plugin", "@typescript-eslint/parser"],
437
+ ["@typescript-eslint/parser"],
412
438
  );
413
439
  } else {
414
440
  packageUtils.addOrRemoveDevDependencies(
415
441
  pkg,
416
442
  useTypescript && shouldHavePluginsDependencies,
417
- ["@typescript-eslint/eslint-plugin", "@typescript-eslint/parser"],
443
+ ["@typescript-eslint/parser"],
418
444
  );
419
445
 
420
446
  packageUtils.addOrRemoveDevDependencies(pkg, hasReact, [
@@ -426,8 +452,10 @@ export default class CommonFormatLintGenerator extends Generator {
426
452
 
427
453
  /**
428
454
  * @param {boolean} withReact
455
+ * @param {boolean} [legacy] the output of pob before @pob/eslint-config
456
+ * 68, to update it
429
457
  */
430
- const getEslintConfig = (withReact) => {
458
+ const getEslintConfig = (withReact, legacy = false) => {
431
459
  if (pkg.name === "@pob/eslint-config-monorepo") {
432
460
  return {
433
461
  imports: [
@@ -441,16 +469,19 @@ export default class CommonFormatLintGenerator extends Generator {
441
469
  }
442
470
 
443
471
  const flatCascade = (() => {
444
- if (!useTypescript) {
472
+ if (!useTypescript && legacy) {
445
473
  return [
446
474
  useNode
447
475
  ? `...pobConfig.configs.node${pkg.type === "commonjs" ? "Commonjs" : "Module"}`
448
476
  : `...pobConfig.configs.base${pkg.type === "commonjs" ? "Commonjs" : "Module"}`,
449
477
  ];
450
478
  }
451
- if (!withReact) {
479
+ // configs.node and configs.baseModule handle "type": "commonjs"
480
+ if (!useTypescript || !withReact) {
452
481
  return [
453
- useNode ? "...pobConfig.configs.node" : "...pobConfig.configs.base",
482
+ useNode
483
+ ? "...pobConfig.configs.node"
484
+ : `...pobConfig.configs.${legacy ? "base" : "baseModule"}`,
454
485
  ];
455
486
  }
456
487
 
@@ -458,7 +489,7 @@ export default class CommonFormatLintGenerator extends Generator {
458
489
  useNode
459
490
  ? "...pobTypescriptConfigReact.configs.node"
460
491
  : "...pobTypescriptConfigReact.configs.base",
461
- withReact && this.options.isApp && "...pobConfig.configs.app",
492
+ legacy && this.options.isApp && "...pobConfig.configs.app",
462
493
  pkg.dependencies?.["react-native-web"] &&
463
494
  '...pobTypescriptConfigReact.configs["react-native-web"]',
464
495
  ];
@@ -560,20 +591,25 @@ export default class CommonFormatLintGenerator extends Generator {
560
591
  (await isPreviousEslintConfigOutput(
561
592
  eslintConfigPath,
562
593
  existingEslintConfig,
563
- [
564
- getEslintConfig(!hasReact),
565
- // before the react configs were used when react is detected
566
- hasReact && {
567
- ...getEslintConfig(hasReact),
568
- flatCascade: getEslintConfig(hasReact).flatCascade.map(
569
- (config) =>
594
+ [false, true]
595
+ .flatMap((legacy) => [
596
+ getEslintConfig(!hasReact, legacy),
597
+ legacy && getEslintConfig(hasReact, legacy),
598
+ // before the react configs were used when react is detected
599
+ hasReact && {
600
+ ...getEslintConfig(hasReact, legacy),
601
+ flatCascade: getEslintConfig(
602
+ hasReact,
603
+ legacy,
604
+ ).flatCascade.map((config) =>
570
605
  config.replace(
571
606
  /^\.\.\.pobTypescriptConfigReact\.configs\.(node|base)$/,
572
607
  "...pobConfig.configs.$1",
573
608
  ),
574
- ),
575
- },
576
- ].filter(Boolean),
609
+ ),
610
+ },
611
+ ])
612
+ .filter(Boolean),
577
613
  )))
578
614
  ) {
579
615
  this.fs.write(eslintConfigPath, eslintConfigContent);
@@ -599,19 +635,20 @@ export default class CommonFormatLintGenerator extends Generator {
599
635
  ...oxlintConfig,
600
636
  extends: [
601
637
  pobOxlintConfigPath("base"),
602
- // ES module rules, not for configs.nodeCommonjs
638
+ // ES module rules, not for .js files of "type": "commonjs" packages
603
639
  (useTypescript || pkg.type !== "commonjs") &&
604
640
  pobOxlintConfigPath("module"),
605
- // like configs.node and configs.nodeModule (not configs.nodeCommonjs)
641
+ useNode && pobOxlintConfigPath("node"),
642
+ // .js files of "type": "commonjs" packages, after node.json
606
643
  useNode &&
607
- (useTypescript || pkg.type !== "commonjs") &&
608
- pobOxlintConfigPath("node"),
644
+ !useTypescript &&
645
+ pkg.type === "commonjs" &&
646
+ pobOxlintConfigPath("commonjs"),
609
647
  // type-aware rules: oxlint fails without tsgolint, even in a
610
648
  // project without TypeScript files
611
649
  useTypescript && pobOxlintConfigPath("typescript"),
612
- // overrides of typescript.json, like the matching eslint configs
613
- // (configs.app, configs.allowUnsafe, ...). Only app is known here:
614
- // the others are kept when added by hand.
650
+ // overrides of typescript.json. Only app is known here: the others
651
+ // are kept when added by hand.
615
652
  ...(useTypescript
616
653
  ? [
617
654
  "app",
@@ -626,8 +663,7 @@ export default class CommonFormatLintGenerator extends Generator {
626
663
  )
627
664
  .map(pobOxlintConfigPath)
628
665
  : []),
629
- // like the @pob/eslint-config-typescript-react configs, after
630
- // module.json and node.json: they override import/extensions.
666
+ // after module.json and node.json: they override import/extensions.
631
667
  // react-native.json is kept when added by hand.
632
668
  ...(hasReact
633
669
  ? ["react", "react-native", "react-native-web"]
@@ -688,4 +724,38 @@ export default class CommonFormatLintGenerator extends Generator {
688
724
 
689
725
  this.fs.writeJSON(this.destinationPath("package.json"), pkg);
690
726
  }
727
+
728
+ /**
729
+ * Updates eslint-disable comments and eslint.config.js for
730
+ * @pob/eslint-config running the rules checked by oxlint in oxlint only.
731
+ */
732
+ async end() {
733
+ if (!this.runOxlintOnlyCodemods) return;
734
+ const cwd = this.destinationPath();
735
+ const files = execFileSync(
736
+ "git",
737
+ ["ls-files", "--cached", "--others", "--exclude-standard"],
738
+ { cwd, encoding: "utf8" },
739
+ )
740
+ .split("\n")
741
+ .filter((file) => /\.[cm]?[jt]sx?$/.test(file) && !file.endsWith(".d.ts"))
742
+ .map((file) => path.join(cwd, file));
743
+ console.log(
744
+ "@pob/eslint-config 68: rewriting eslint-disable comments of rules checked by oxlint",
745
+ );
746
+ await runTransform("oxlint-directives", files, { silent: true });
747
+
748
+ const eslintConfigPath = ["eslint.config.js", "eslint.config.mjs"]
749
+ .map((fileName) => path.join(cwd, fileName))
750
+ .find((filePath) => fs.existsSync(filePath));
751
+ if (eslintConfigPath) {
752
+ await runTransform("oxlint-eslint-config", [eslintConfigPath]);
753
+ const { code } = await format(
754
+ eslintConfigPath,
755
+ fs.readFileSync(eslintConfigPath, "utf8"),
756
+ { printWidth: 80 },
757
+ );
758
+ fs.writeFileSync(eslintConfigPath, code);
759
+ }
760
+ }
691
761
  }
@@ -1,5 +1,3 @@
1
1
  "use strict";
2
2
 
3
3
  exports.pobEslintConfig = require("@pob/eslint-config/package.json");
4
-
5
- exports.pobEslintConfigTypescriptReact = require("@pob/eslint-config-typescript-react/package.json");
@@ -5,10 +5,7 @@ import sortPkg from "@pob/sort-pkg";
5
5
  import parseAuthor from "parse-author";
6
6
  import pobDependencies from "pob-dependencies";
7
7
  import semver from "semver";
8
- import {
9
- pobEslintConfig,
10
- pobEslintConfigTypescriptReact,
11
- } from "./dependenciesPackages.cjs";
8
+ import { pobEslintConfig } from "./dependenciesPackages.cjs";
12
9
  import pobPkg from "./packagejson.js";
13
10
 
14
11
  export { default as parseAuthor } from "parse-author";
@@ -63,24 +60,8 @@ const internalRemoveDependencies = (pkg, type, dependencyKeys) => {
63
60
  };
64
61
 
65
62
  const getVersionFromDependencyName = (dependency) => {
66
- if (
67
- [
68
- // 'eslint-import-resolver-node',
69
- "eslint-plugin-import",
70
- "eslint-plugin-node",
71
- "eslint-plugin-unicorn",
72
- ].includes(dependency)
73
- ) {
74
- return pobEslintConfig.dependencies[dependency];
75
- }
76
- if (["eslint-plugin-jsx-a11y", "eslint-plugin-react"].includes(dependency)) {
77
- return pobEslintConfigTypescriptReact.dependencies[dependency];
78
- }
79
- if (
80
- ["@typescript-eslint/eslint-plugin", "@typescript-eslint/parser"].includes(
81
- dependency,
82
- )
83
- ) {
63
+ // loaded by name by import-x, from the project with yarn PnP
64
+ if (dependency === "@typescript-eslint/parser") {
84
65
  return pobEslintConfig.dependencies[dependency];
85
66
  }
86
67
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pob",
3
- "version": "39.3.0",
3
+ "version": "40.0.0",
4
4
  "description": "Pile of bones, library generator with git/babel/typescript/typedoc/readme/vitest",
5
5
  "keywords": [
6
6
  "skeleton"
@@ -31,8 +31,9 @@
31
31
  "./package.json": "./package.json"
32
32
  },
33
33
  "dependencies": {
34
- "@pob/eslint-config": "67.14.0",
35
- "@pob/eslint-config-typescript-react": "67.14.0",
34
+ "@pob/codemods": "0.18.0",
35
+ "@pob/eslint-config": "68.0.0",
36
+ "@pob/eslint-config-typescript-react": "68.0.0",
36
37
  "@pob/sort-object": "11.2.0",
37
38
  "@pob/sort-pkg": "13.2.0",
38
39
  "@types/inquirer": "9.0.10",
@@ -50,7 +51,7 @@
50
51
  "minimist": "1.2.8",
51
52
  "oxfmt": "0.72.0",
52
53
  "parse-author": "2.0.0",
53
- "pob-dependencies": "27.14.0",
54
+ "pob-dependencies": "28.0.0",
54
55
  "semver": "7.8.5",
55
56
  "typescript": "npm:@typescript/typescript6@6.0.2",
56
57
  "validate-npm-package-name": "^8.0.0",
@@ -58,7 +59,7 @@
58
59
  "yeoman-generator": "10.0.1"
59
60
  },
60
61
  "devDependencies": {
61
- "@pob/root": "27.17.0",
62
+ "@pob/root": "28.0.0",
62
63
  "@types/node": "24.19.1",
63
64
  "@typescript/native": "npm:typescript@7.0.2",
64
65
  "@yeoman/types": "1.13.1"