@strapi/upgrade 5.0.0-beta.0 → 5.0.0-beta.10

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.
Files changed (74) hide show
  1. package/dist/cli.js +1452 -5
  2. package/dist/cli.js.map +1 -1
  3. package/dist/index.js +318 -89
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +319 -90
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/modules/codemod/codemod.d.ts +4 -2
  8. package/dist/modules/codemod/codemod.d.ts.map +1 -1
  9. package/dist/modules/codemod/types.d.ts +8 -1
  10. package/dist/modules/codemod/types.d.ts.map +1 -1
  11. package/dist/modules/codemod-repository/constants.d.ts.map +1 -1
  12. package/dist/modules/codemod-repository/repository.d.ts +6 -5
  13. package/dist/modules/codemod-repository/repository.d.ts.map +1 -1
  14. package/dist/modules/codemod-repository/types.d.ts +7 -3
  15. package/dist/modules/codemod-repository/types.d.ts.map +1 -1
  16. package/dist/modules/codemod-runner/codemod-runner.d.ts +3 -0
  17. package/dist/modules/codemod-runner/codemod-runner.d.ts.map +1 -1
  18. package/dist/modules/codemod-runner/index.d.ts +1 -0
  19. package/dist/modules/codemod-runner/index.d.ts.map +1 -1
  20. package/dist/modules/codemod-runner/types.d.ts +1 -0
  21. package/dist/modules/codemod-runner/types.d.ts.map +1 -1
  22. package/dist/modules/format/formats.d.ts +5 -0
  23. package/dist/modules/format/formats.d.ts.map +1 -1
  24. package/dist/modules/project/constants.d.ts +2 -0
  25. package/dist/modules/project/constants.d.ts.map +1 -1
  26. package/dist/modules/project/index.d.ts +2 -0
  27. package/dist/modules/project/index.d.ts.map +1 -1
  28. package/dist/modules/project/project.d.ts +12 -4
  29. package/dist/modules/project/project.d.ts.map +1 -1
  30. package/dist/modules/project/types.d.ts +1 -11
  31. package/dist/modules/project/types.d.ts.map +1 -1
  32. package/dist/modules/project/utils.d.ts +6 -0
  33. package/dist/modules/project/utils.d.ts.map +1 -0
  34. package/dist/modules/report/report.d.ts.map +1 -1
  35. package/dist/modules/requirement/types.d.ts +2 -2
  36. package/dist/modules/requirement/types.d.ts.map +1 -1
  37. package/dist/modules/upgrader/upgrader.d.ts +3 -3
  38. package/dist/modules/upgrader/upgrader.d.ts.map +1 -1
  39. package/dist/modules/version/range.d.ts +2 -0
  40. package/dist/modules/version/range.d.ts.map +1 -1
  41. package/dist/tasks/codemods/index.d.ts +2 -1
  42. package/dist/tasks/codemods/index.d.ts.map +1 -1
  43. package/dist/tasks/codemods/list-codemods.d.ts +3 -0
  44. package/dist/tasks/codemods/list-codemods.d.ts.map +1 -0
  45. package/dist/tasks/codemods/run-codemods.d.ts +3 -0
  46. package/dist/tasks/codemods/run-codemods.d.ts.map +1 -0
  47. package/dist/tasks/codemods/types.d.ts +9 -3
  48. package/dist/tasks/codemods/types.d.ts.map +1 -1
  49. package/dist/tasks/codemods/utils.d.ts +6 -0
  50. package/dist/tasks/codemods/utils.d.ts.map +1 -0
  51. package/dist/tasks/index.d.ts +1 -1
  52. package/dist/tasks/index.d.ts.map +1 -1
  53. package/dist/tasks/upgrade/upgrade.d.ts.map +1 -1
  54. package/package.json +9 -8
  55. package/resources/codemods/5.0.0/dependency-remove-strapi-plugin-i18n.json.ts +31 -0
  56. package/resources/codemods/5.0.0/dependency-upgrade-react-router-dom.json.ts +59 -0
  57. package/resources/codemods/5.0.0/entity-service-document-service.code.ts +437 -0
  58. package/resources/codemods/5.0.0/s3-keys-wrapped-in-credentials.code.ts +1 -1
  59. package/resources/codemods/5.0.0/sqlite3-to-better-sqlite3.json.ts +5 -2
  60. package/resources/codemods/5.0.0/strapi-public-interface.code.ts +126 -0
  61. package/resources/codemods/5.0.0/use-uid-for-config-namespace.code.ts +1 -1
  62. package/resources/codemods/5.0.0/utils-public-interface.code.ts +320 -0
  63. package/resources/examples/console.log-to-console.info.code.ts +1 -1
  64. package/resources/examples/disable-jsx-buttons.code.ts +42 -0
  65. package/dist/_chunks/codemod-runner-gNmBzH6-.js +0 -730
  66. package/dist/_chunks/codemod-runner-gNmBzH6-.js.map +0 -1
  67. package/dist/_chunks/codemods--eBNi-jZ.js +0 -105
  68. package/dist/_chunks/codemods--eBNi-jZ.js.map +0 -1
  69. package/dist/_chunks/index-oO2o1y7M.js +0 -103
  70. package/dist/_chunks/index-oO2o1y7M.js.map +0 -1
  71. package/dist/_chunks/upgrade-9ZLeKVLU.js +0 -354
  72. package/dist/_chunks/upgrade-9ZLeKVLU.js.map +0 -1
  73. package/dist/tasks/codemods/codemods.d.ts +0 -3
  74. package/dist/tasks/codemods/codemods.d.ts.map +0 -1
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import simpleGit from "simple-git";
3
3
  import chalk from "chalk";
4
4
  import semver from "semver";
5
5
  import { packageManager } from "@strapi/utils";
6
- import { cloneDeep, get, has, merge, set, omit, isEqual } from "lodash/fp";
6
+ import { cloneDeep, get, has, merge, set, omit, isEqual, groupBy, size } from "lodash/fp";
7
7
  import fse from "fs-extra";
8
8
  import assert from "node:assert";
9
9
  import { glob } from "glob";
@@ -253,13 +253,19 @@ const rangeFromVersions = (currentVersion, target) => {
253
253
  }
254
254
  throw new Error(`Invalid target set: ${target}`);
255
255
  };
256
+ const isValidStringifiedRange = (str) => semver.validRange(str) !== null;
257
+ const isRangeInstance = (range) => {
258
+ return range instanceof semver.Range;
259
+ };
256
260
  const index$e = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
257
261
  __proto__: null,
258
262
  Version: types,
259
263
  isLiteralSemVer,
264
+ isRangeInstance,
260
265
  isSemVerReleaseType,
261
266
  isSemverInstance,
262
267
  isValidSemVer,
268
+ isValidStringifiedRange,
263
269
  rangeFactory,
264
270
  rangeFromReleaseType,
265
271
  rangeFromVersions,
@@ -366,7 +372,20 @@ const index$b = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
366
372
  }, Symbol.toStringTag, { value: "Module" }));
367
373
  const PROJECT_PACKAGE_JSON = "package.json";
368
374
  const PROJECT_DEFAULT_ALLOWED_ROOT_PATHS = ["src", "config", "public"];
369
- const PROJECT_DEFAULT_ALLOWED_EXTENSIONS = ["js", "ts", "json"];
375
+ const PROJECT_DEFAULT_CODE_EXTENSIONS = [
376
+ // Source files
377
+ "js",
378
+ "mjs",
379
+ "ts",
380
+ // React files
381
+ "jsx",
382
+ "tsx"
383
+ ];
384
+ const PROJECT_DEFAULT_JSON_EXTENSIONS = ["json"];
385
+ const PROJECT_DEFAULT_ALLOWED_EXTENSIONS = [
386
+ ...PROJECT_DEFAULT_CODE_EXTENSIONS,
387
+ ...PROJECT_DEFAULT_JSON_EXTENSIONS
388
+ ];
370
389
  const PROJECT_DEFAULT_PATTERNS = ["package.json"];
371
390
  const SCOPED_STRAPI_PACKAGE_PREFIX = "@strapi/";
372
391
  const STRAPI_DEPENDENCY_NAME = `${SCOPED_STRAPI_PACKAGE_PREFIX}strapi`;
@@ -374,6 +393,8 @@ const constants$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
374
393
  __proto__: null,
375
394
  PROJECT_DEFAULT_ALLOWED_EXTENSIONS,
376
395
  PROJECT_DEFAULT_ALLOWED_ROOT_PATHS,
396
+ PROJECT_DEFAULT_CODE_EXTENSIONS,
397
+ PROJECT_DEFAULT_JSON_EXTENSIONS,
377
398
  PROJECT_DEFAULT_PATTERNS,
378
399
  PROJECT_PACKAGE_JSON,
379
400
  SCOPED_STRAPI_PACKAGE_PREFIX,
@@ -385,7 +406,6 @@ class Project {
385
406
  files;
386
407
  packageJSONPath;
387
408
  packageJSON;
388
- strapiVersion;
389
409
  constructor(cwd) {
390
410
  if (!fse.pathExistsSync(cwd)) {
391
411
  throw new Error(`ENOENT: no such file or directory, access '${cwd}'`);
@@ -401,14 +421,13 @@ class Project {
401
421
  }
402
422
  refresh() {
403
423
  this.refreshPackageJSON();
404
- this.refreshStrapiVersion();
405
424
  this.refreshProjectFiles();
406
425
  return this;
407
426
  }
408
- async runCodemods(codemods2, options) {
427
+ async runCodemods(codemods, options) {
409
428
  const runners = this.createProjectCodemodsRunners(options.dry);
410
429
  const reports2 = [];
411
- for (const codemod of codemods2) {
430
+ for (const codemod of codemods) {
412
431
  for (const runner of runners) {
413
432
  if (runner.valid(codemod)) {
414
433
  const report = await runner.run(codemod);
@@ -419,17 +438,24 @@ class Project {
419
438
  return reports2;
420
439
  }
421
440
  createProjectCodemodsRunners(dry = false) {
422
- const jsonFiles = this.getFilesByExtensions([".json"]);
423
- const codeFiles = this.getFilesByExtensions([".js", ".ts", ".mjs"]);
441
+ const jsonExtensions = PROJECT_DEFAULT_JSON_EXTENSIONS.map(
442
+ (ext) => `.${ext}`
443
+ );
444
+ const codeExtensions = PROJECT_DEFAULT_CODE_EXTENSIONS.map(
445
+ (ext) => `.${ext}`
446
+ );
447
+ const jsonFiles = this.getFilesByExtensions(jsonExtensions);
448
+ const codeFiles = this.getFilesByExtensions(codeExtensions);
424
449
  const codeRunner = codeRunnerFactory(codeFiles, {
425
450
  dry,
426
- print: false,
427
- silent: true,
428
- extensions: "js,ts",
451
+ parser: "ts",
429
452
  runInBand: true,
430
- verbose: 0,
431
453
  babel: true,
432
- parser: "ts"
454
+ extensions: PROJECT_DEFAULT_CODE_EXTENSIONS.join(","),
455
+ // Don't output any log coming from the runner
456
+ print: false,
457
+ silent: true,
458
+ verbose: 0
433
459
  });
434
460
  const jsonRunner = jsonRunnerFactory(jsonFiles, { dry, cwd: this.cwd });
435
461
  return [codeRunner, jsonRunner];
@@ -457,6 +483,19 @@ class Project {
457
483
  const scanner = fileScannerFactory(this.cwd);
458
484
  this.files = scanner.scan(patterns);
459
485
  }
486
+ }
487
+ class AppProject extends Project {
488
+ strapiVersion;
489
+ type = "application";
490
+ constructor(cwd) {
491
+ super(cwd);
492
+ this.refreshStrapiVersion();
493
+ }
494
+ refresh() {
495
+ super.refresh();
496
+ this.refreshStrapiVersion();
497
+ return this;
498
+ }
460
499
  refreshStrapiVersion() {
461
500
  this.strapiVersion = // First try to get the strapi version from the package.json dependencies
462
501
  this.findStrapiVersionFromProjectPackageJSON() ?? // If the version found is not a valid SemVer, get the Strapi version from the installed package
@@ -502,10 +541,50 @@ const formatGlobCollectionPattern = (collection) => {
502
541
  );
503
542
  return collection.length === 1 ? collection[0] : `{${collection}}`;
504
543
  };
505
- const projectFactory = (cwd) => new Project(cwd);
544
+ class PluginProject extends Project {
545
+ type = "plugin";
546
+ }
547
+ const isPlugin = (cwd) => {
548
+ const packageJSONPath = path$1.join(cwd, PROJECT_PACKAGE_JSON);
549
+ try {
550
+ fse.accessSync(packageJSONPath);
551
+ } catch {
552
+ throw new Error(`Could not find a ${PROJECT_PACKAGE_JSON} file in ${cwd}`);
553
+ }
554
+ const packageJSONBuffer = fse.readFileSync(packageJSONPath);
555
+ const packageJSON = JSON.parse(packageJSONBuffer.toString());
556
+ return packageJSON?.strapi?.kind === "plugin";
557
+ };
558
+ const projectFactory = (cwd) => {
559
+ fse.accessSync(cwd);
560
+ if (isPlugin(cwd)) {
561
+ return new PluginProject(cwd);
562
+ }
563
+ return new AppProject(cwd);
564
+ };
565
+ const isPluginProject = (project) => {
566
+ return project instanceof PluginProject;
567
+ };
568
+ function assertPluginProject(project) {
569
+ if (!isPluginProject(project)) {
570
+ throw new Error("Project is not a plugin");
571
+ }
572
+ }
573
+ const isApplicationProject = (project) => {
574
+ return project instanceof AppProject;
575
+ };
576
+ function assertAppProject(project) {
577
+ if (!isApplicationProject(project)) {
578
+ throw new Error("Project is not an application");
579
+ }
580
+ }
506
581
  const index$a = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
507
582
  __proto__: null,
583
+ assertAppProject,
584
+ assertPluginProject,
508
585
  constants: constants$3,
586
+ isApplicationProject,
587
+ isPluginProject,
509
588
  projectFactory
510
589
  }, Symbol.toStringTag, { value: "Module" }));
511
590
  class UnexpectedError extends Error {
@@ -531,7 +610,11 @@ const path = (path2) => chalk.blue(path2);
531
610
  const version = (version2) => {
532
611
  return chalk.italic.yellow(`v${version2}`);
533
612
  };
534
- const versionRange = (range) => chalk.italic.yellow(range);
613
+ const codemodUID = (uid) => {
614
+ return chalk.bold.cyan(uid);
615
+ };
616
+ const projectType = (type) => chalk.cyan(type);
617
+ const versionRange = (range) => chalk.italic.yellow(range.raw);
535
618
  const transform = (transformFilePath) => chalk.cyan(transformFilePath);
536
619
  const highlight = (arg) => chalk.bold.underline(arg);
537
620
  const upgradeStep = (text, step) => {
@@ -563,15 +646,40 @@ const reports = (reports2) => {
563
646
  table.push(...rows);
564
647
  return table.toString();
565
648
  };
649
+ const codemodList = (codemods) => {
650
+ const rows = codemods.map((codemod, index2) => {
651
+ const fIndex = chalk.grey(index2);
652
+ const fVersion = chalk.magenta(codemod.version);
653
+ const fKind = chalk.yellow(codemod.kind);
654
+ const fName = chalk.blue(codemod.format());
655
+ const fUID = codemodUID(codemod.uid);
656
+ return [fIndex, fVersion, fKind, fName, fUID];
657
+ });
658
+ const table = new CliTable3({
659
+ style: { compact: true },
660
+ head: [
661
+ chalk.bold.grey("N°"),
662
+ chalk.bold.magenta("Version"),
663
+ chalk.bold.yellow("Kind"),
664
+ chalk.bold.blue("Name"),
665
+ chalk.bold.cyan("UID")
666
+ ]
667
+ });
668
+ table.push(...rows);
669
+ return table.toString();
670
+ };
566
671
  const durationMs = (elapsedMs) => {
567
672
  const elapsedSeconds = (elapsedMs / ONE_SECOND_MS).toFixed(3);
568
673
  return `${elapsedSeconds}s`;
569
674
  };
570
675
  const index$8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
571
676
  __proto__: null,
677
+ codemodList,
678
+ codemodUID,
572
679
  durationMs,
573
680
  highlight,
574
681
  path,
682
+ projectType,
575
683
  reports,
576
684
  transform,
577
685
  upgradeStep,
@@ -594,6 +702,7 @@ const constants$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
594
702
  CODEMOD_JSON_SUFFIX
595
703
  }, Symbol.toStringTag, { value: "Module" }));
596
704
  class Codemod {
705
+ uid;
597
706
  kind;
598
707
  version;
599
708
  baseDirectory;
@@ -605,9 +714,27 @@ class Codemod {
605
714
  this.baseDirectory = options.baseDirectory;
606
715
  this.filename = options.filename;
607
716
  this.path = path$1.join(this.baseDirectory, this.version.raw, this.filename);
608
- }
609
- format() {
610
- return this.filename.replace(`.${CODEMOD_CODE_SUFFIX}.${CODEMOD_EXTENSION}`, "").replace(`.${CODEMOD_JSON_SUFFIX}.${CODEMOD_EXTENSION}`, "").replaceAll("-", " ");
717
+ this.uid = this.createUID();
718
+ }
719
+ createUID() {
720
+ const name = this.format({ stripExtension: true, stripKind: true, stripHyphens: false });
721
+ const kind = this.kind;
722
+ const version2 = this.version.raw;
723
+ return `${version2}-${name}-${kind}`;
724
+ }
725
+ format(options) {
726
+ const { stripExtension = true, stripKind = true, stripHyphens = true } = options ?? {};
727
+ let formatted = this.filename;
728
+ if (stripExtension) {
729
+ formatted = formatted.replace(new RegExp(`\\.${CODEMOD_EXTENSION}$`, "i"), "");
730
+ }
731
+ if (stripKind) {
732
+ formatted = formatted.replace(`.${CODEMOD_CODE_SUFFIX}`, "").replace(`.${CODEMOD_JSON_SUFFIX}`, "");
733
+ }
734
+ if (stripHyphens) {
735
+ formatted = formatted.replaceAll("-", " ");
736
+ }
737
+ return formatted;
611
738
  }
612
739
  }
613
740
  const codemodFactory = (options) => new Codemod(options);
@@ -616,6 +743,20 @@ const index$7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
616
743
  codemodFactory,
617
744
  constants: constants$2
618
745
  }, Symbol.toStringTag, { value: "Module" }));
746
+ const INTERNAL_CODEMODS_DIRECTORY = path$1.join(
747
+ __dirname,
748
+ // upgrade/dist
749
+ "..",
750
+ // upgrade
751
+ "resources",
752
+ // upgrade/resources
753
+ "codemods"
754
+ // upgrade/resources/codemods
755
+ );
756
+ const constants$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
757
+ __proto__: null,
758
+ INTERNAL_CODEMODS_DIRECTORY
759
+ }, Symbol.toStringTag, { value: "Module" }));
619
760
  class CodemodRepository {
620
761
  groups;
621
762
  versions;
@@ -634,23 +775,48 @@ class CodemodRepository {
634
775
  count(version2) {
635
776
  return this.findByVersion(version2).length;
636
777
  }
637
- countRange(range) {
638
- return this.findByRange(range).length;
639
- }
640
- exists(version2) {
778
+ versionExists(version2) {
641
779
  return version2.raw in this.groups;
642
780
  }
643
- findByRange(range) {
781
+ has(uid) {
782
+ const result = this.find({ uids: [uid] });
783
+ if (result.length !== 1) {
784
+ return false;
785
+ }
786
+ const { codemods } = result[0];
787
+ return codemods.length === 1 && codemods[0].uid === uid;
788
+ }
789
+ find(q) {
644
790
  const entries = Object.entries(this.groups);
645
- return entries.filter(([version2]) => range.test(version2)).map(([version2, codemods2]) => ({
791
+ return entries.filter(maybeFilterByRange).map(([version2, codemods]) => ({
646
792
  version: semVerFactory(version2),
647
- codemods: codemods2
648
- }));
793
+ // Filter by UID if provided in the query
794
+ codemods: codemods.filter(maybeFilterByUIDs)
795
+ })).filter(({ codemods }) => codemods.length > 0);
796
+ function maybeFilterByRange([version2]) {
797
+ if (!isRangeInstance(q.range)) {
798
+ return true;
799
+ }
800
+ return q.range.test(version2);
801
+ }
802
+ function maybeFilterByUIDs(codemod) {
803
+ if (q.uids === void 0) {
804
+ return true;
805
+ }
806
+ return q.uids.includes(codemod.uid);
807
+ }
649
808
  }
650
809
  findByVersion(version2) {
651
810
  const literalVersion = version2.raw;
652
- const codemods2 = this.groups[literalVersion];
653
- return codemods2 ?? [];
811
+ const codemods = this.groups[literalVersion];
812
+ return codemods ?? [];
813
+ }
814
+ findAll() {
815
+ const entries = Object.entries(this.groups);
816
+ return entries.map(([version2, codemods]) => ({
817
+ version: semVerFactory(version2),
818
+ codemods
819
+ }));
654
820
  }
655
821
  refreshAvailableVersions() {
656
822
  this.versions = fse.readdirSync(this.cwd).filter((filename) => fse.statSync(path$1.join(this.cwd, filename)).isDirectory()).filter((filename) => semver.valid(filename) !== null).map((version2) => semVerFactory(version2)).sort(semver.compare);
@@ -681,18 +847,9 @@ const parseCodemodKindFromFilename = (filename) => {
681
847
  assert(CODEMOD_ALLOWED_SUFFIXES.includes(kind));
682
848
  return kind;
683
849
  };
684
- const codemodRepositoryFactory = (cwd) => new CodemodRepository(cwd);
685
- const INTERNAL_CODEMODS_DIRECTORY = path$1.join(
686
- __dirname,
687
- "..",
688
- "..",
689
- "resources",
690
- "codemods"
691
- );
692
- const constants$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
693
- __proto__: null,
694
- INTERNAL_CODEMODS_DIRECTORY
695
- }, Symbol.toStringTag, { value: "Module" }));
850
+ const codemodRepositoryFactory = (cwd = INTERNAL_CODEMODS_DIRECTORY) => {
851
+ return new CodemodRepository(cwd);
852
+ };
696
853
  const index$6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
697
854
  __proto__: null,
698
855
  codemodRepositoryFactory,
@@ -727,34 +884,59 @@ class CodemodRunner {
727
884
  this.isDry = enabled;
728
885
  return this;
729
886
  }
730
- async run(codemodsDirectory) {
887
+ createRepository(codemodsDirectory) {
731
888
  const repository = codemodRepositoryFactory(
732
889
  codemodsDirectory ?? INTERNAL_CODEMODS_DIRECTORY
733
890
  );
734
891
  repository.refresh();
735
- const allVersionedCodemods = repository.findByRange(this.range);
736
- const versionedCodemods = this.selectCodemodsCallback ? await this.selectCodemodsCallback(allVersionedCodemods) : allVersionedCodemods;
737
- const hasCodemodsToRun = versionedCodemods.length > 0;
738
- if (!hasCodemodsToRun) {
739
- this.logger?.debug(`Found no codemods to run for ${versionRange(this.range)}`);
740
- return successReport$1();
892
+ return repository;
893
+ }
894
+ async safeRunAndReport(codemods) {
895
+ if (this.isDry) {
896
+ this.logger?.warn?.(
897
+ "Running the codemods in dry mode. No files will be modified during the process."
898
+ );
741
899
  }
742
- this.logger?.debug(
743
- `Found codemods for ${highlight(
744
- versionedCodemods.length
745
- )} version(s) using ${versionRange(this.range)}`
746
- );
747
- versionedCodemods.forEach(
748
- ({ version: version$1, codemods: codemods22 }) => this.logger?.debug(`- ${version(version$1)} (${codemods22.length})`)
749
- );
750
- const codemods2 = versionedCodemods.map(({ codemods: codemods22 }) => codemods22).flat();
751
900
  try {
752
- const reports$1 = await this.project.runCodemods(codemods2, { dry: this.isDry });
753
- this.logger?.raw(reports(reports$1));
901
+ const reports$1 = await this.project.runCodemods(codemods, { dry: this.isDry });
902
+ this.logger?.raw?.(reports(reports$1));
903
+ if (!this.isDry) {
904
+ const nbAffectedTotal = reports$1.flatMap((report) => report.report.ok).reduce((acc, nb) => acc + nb, 0);
905
+ this.logger?.debug?.(
906
+ `Successfully ran ${highlight(codemods.length)} codemod(s), ${highlight(nbAffectedTotal)} change(s) have been detected`
907
+ );
908
+ }
909
+ return successReport$1();
754
910
  } catch (e) {
755
911
  return erroredReport$1(unknownToError(e));
756
912
  }
757
- return successReport$1();
913
+ }
914
+ async runByUID(uid, codemodsDirectory) {
915
+ const repository = this.createRepository(codemodsDirectory);
916
+ if (!repository.has(uid)) {
917
+ throw new Error(`Unknown codemod UID provided: ${uid}`);
918
+ }
919
+ const codemods = repository.find({ uids: [uid] }).flatMap(({ codemods: codemods2 }) => codemods2);
920
+ return this.safeRunAndReport(codemods);
921
+ }
922
+ async run(codemodsDirectory) {
923
+ const repository = this.createRepository(codemodsDirectory);
924
+ const codemodsInRange = repository.find({ range: this.range });
925
+ const selectedCodemods = this.selectCodemodsCallback ? await this.selectCodemodsCallback(codemodsInRange) : codemodsInRange;
926
+ if (selectedCodemods.length === 0) {
927
+ this.logger?.debug?.(`Found no codemods to run for ${versionRange(this.range)}`);
928
+ return successReport$1();
929
+ }
930
+ const codemods = selectedCodemods.flatMap(({ codemods: codemods2 }) => codemods2);
931
+ const codemodsByVersion = groupBy("version", codemods);
932
+ const fRange = versionRange(this.range);
933
+ this.logger?.debug?.(
934
+ `Found ${highlight(codemods.length)} codemods for ${highlight(size(codemodsByVersion))} version(s) using ${fRange}`
935
+ );
936
+ for (const [version$1, codemods2] of Object.entries(codemodsByVersion)) {
937
+ this.logger?.debug?.(`- ${version(semVerFactory(version$1))} (${codemods2.length})`);
938
+ }
939
+ return this.safeRunAndReport(codemods);
758
940
  }
759
941
  }
760
942
  const codemodRunnerFactory = (project, range) => {
@@ -793,7 +975,7 @@ class Upgrader {
793
975
  this.codemodsTarget = semVerFactory(
794
976
  `${this.target.major}.${this.target.minor}.${this.target.patch}`
795
977
  );
796
- this.logger?.debug(
978
+ this.logger?.debug?.(
797
979
  `The codemods target has been synced with the upgrade target. The codemod runner will now look for ${version(
798
980
  this.codemodsTarget
799
981
  )}`
@@ -802,7 +984,7 @@ class Upgrader {
802
984
  }
803
985
  overrideCodemodsTarget(target) {
804
986
  this.codemodsTarget = target;
805
- this.logger?.debug(
987
+ this.logger?.debug?.(
806
988
  `Overriding the codemods target. The codemod runner will now look for ${version(target)}`
807
989
  );
808
990
  return this;
@@ -822,38 +1004,40 @@ class Upgrader {
822
1004
  addRequirement(requirement) {
823
1005
  this.requirements.push(requirement);
824
1006
  const fRequired = requirement.isRequired ? "(required)" : "(optional)";
825
- this.logger?.debug(
1007
+ this.logger?.debug?.(
826
1008
  `Added a new requirement to the upgrade: ${highlight(requirement.name)} ${fRequired}`
827
1009
  );
828
1010
  return this;
829
1011
  }
830
1012
  async upgrade() {
831
- this.logger?.info(
1013
+ this.logger?.info?.(
832
1014
  `Upgrading from ${version(this.project.strapiVersion)} to ${version(this.target)}`
833
1015
  );
834
1016
  if (this.isDry) {
835
- this.logger?.warn(
1017
+ this.logger?.warn?.(
836
1018
  "Running the upgrade in dry mode. No files will be modified during the process."
837
1019
  );
838
1020
  }
839
1021
  const range = rangeFromVersions(this.project.strapiVersion, this.target);
840
1022
  const codemodsRange = rangeFromVersions(this.project.strapiVersion, this.codemodsTarget);
841
1023
  const npmVersionsMatches = this.npmPackage?.findVersionsInRange(range) ?? [];
842
- this.logger?.debug(
1024
+ this.logger?.debug?.(
843
1025
  `Found ${highlight(npmVersionsMatches.length)} versions satisfying ${versionRange(range)}`
844
1026
  );
845
1027
  try {
846
- this.logger?.info(upgradeStep("Checking requirement", [1, 4]));
1028
+ this.logger?.info?.(upgradeStep("Checking requirement", [1, 4]));
847
1029
  await this.checkRequirements(this.requirements, {
848
1030
  npmVersionsMatches,
849
1031
  project: this.project,
850
1032
  target: this.target
851
1033
  });
852
- this.logger?.info(upgradeStep("Applying the latest code modifications", [2, 4]));
1034
+ this.logger?.info?.(upgradeStep("Applying the latest code modifications", [2, 4]));
853
1035
  await this.runCodemods(codemodsRange);
854
- this.logger?.info(upgradeStep("Upgrading Strapi dependencies", [3, 4]));
1036
+ this.logger?.debug?.("Refreshing project information...");
1037
+ this.project.refresh();
1038
+ this.logger?.info?.(upgradeStep("Upgrading Strapi dependencies", [3, 4]));
855
1039
  await this.updateDependencies();
856
- this.logger?.info(upgradeStep("Installing dependencies", [4, 4]));
1040
+ this.logger?.info?.(upgradeStep("Installing dependencies", [4, 4]));
857
1041
  await this.installDependencies();
858
1042
  } catch (e) {
859
1043
  return erroredReport(unknownToError(e));
@@ -886,7 +1070,7 @@ class Upgrader {
886
1070
  if (requirement.isRequired) {
887
1071
  throw error;
888
1072
  }
889
- this.logger?.warn(warningMessage);
1073
+ this.logger?.warn?.(warningMessage);
890
1074
  const response = await this.confirmationCallback?.(confirmationMessage);
891
1075
  if (!response) {
892
1076
  throw error;
@@ -897,9 +1081,11 @@ class Upgrader {
897
1081
  const json = createJSONTransformAPI(packageJSON);
898
1082
  const dependencies = json.get("dependencies", {});
899
1083
  const strapiDependencies = this.getScopedStrapiDependencies(dependencies);
900
- this.logger?.debug(`Found ${highlight(strapiDependencies.length)} dependency(ies) to update`);
1084
+ this.logger?.debug?.(
1085
+ `Found ${highlight(strapiDependencies.length)} dependency(ies) to update`
1086
+ );
901
1087
  strapiDependencies.forEach(
902
- (dependency) => this.logger?.debug(`- ${dependency[0]} (${dependency[1]} -> ${this.target})`)
1088
+ (dependency) => this.logger?.debug?.(`- ${dependency[0]} (${dependency[1]} -> ${this.target})`)
903
1089
  );
904
1090
  if (strapiDependencies.length === 0) {
905
1091
  return;
@@ -907,7 +1093,7 @@ class Upgrader {
907
1093
  strapiDependencies.forEach(([name]) => json.set(`dependencies.${name}`, this.target.raw));
908
1094
  const updatedPackageJSON = json.root();
909
1095
  if (this.isDry) {
910
- this.logger?.debug(`Skipping dependencies update (${chalk.italic("dry mode")})`);
1096
+ this.logger?.debug?.(`Skipping dependencies update (${chalk.italic("dry mode")})`);
911
1097
  return;
912
1098
  }
913
1099
  await saveJSON(packageJSONPath, updatedPackageJSON);
@@ -927,9 +1113,9 @@ class Upgrader {
927
1113
  async installDependencies() {
928
1114
  const projectPath = this.project.cwd;
929
1115
  const packageManagerName = await packageManager.getPreferred(projectPath);
930
- this.logger?.debug(`Using ${highlight(packageManagerName)} as package manager`);
1116
+ this.logger?.debug?.(`Using ${highlight(packageManagerName)} as package manager`);
931
1117
  if (this.isDry) {
932
- this.logger?.debug(`Skipping dependencies installation (${chalk.italic("dry mode")}`);
1118
+ this.logger?.debug?.(`Skipping dependencies installation (${chalk.italic("dry mode")}`);
933
1119
  return;
934
1120
  }
935
1121
  await packageManager.installDependencies(projectPath, packageManagerName, {
@@ -1028,6 +1214,11 @@ const upgrade = async (options) => {
1028
1214
  const { logger, codemodsTarget } = options;
1029
1215
  const cwd = path$1.resolve(options.cwd ?? process.cwd());
1030
1216
  const project = projectFactory(cwd);
1217
+ if (!isApplicationProject(project)) {
1218
+ throw new Error(
1219
+ `The "${options.target}" upgrade can only be run on a Strapi project; for plugins, please use "codemods".`
1220
+ );
1221
+ }
1031
1222
  const npmPackage = npmPackageFactory(STRAPI_PACKAGE_NAME);
1032
1223
  await npmPackage.refresh();
1033
1224
  const upgrader = upgraderFactory(project, options.target, npmPackage).dry(options.dry ?? false).onConfirm(options.confirm ?? null).setLogger(logger);
@@ -1045,20 +1236,7 @@ const upgrade = async (options) => {
1045
1236
  timer.stop();
1046
1237
  logger.info(`Completed in ${durationMs(timer.elapsedMs)}`);
1047
1238
  };
1048
- const codemods = async (options) => {
1049
- const timer = timerFactory();
1050
- const { logger } = options;
1051
- const cwd = path$1.resolve(options.cwd ?? process.cwd());
1052
- const project = projectFactory(cwd);
1053
- const range = getRangeFromTarget(project.strapiVersion, options.target);
1054
- const codemodRunner = codemodRunnerFactory(project, range).dry(options.dry ?? false).onSelectCodemods(options.selectCodemods ?? null).setLogger(logger);
1055
- const executionReport = await codemodRunner.run();
1056
- if (!executionReport.success) {
1057
- throw executionReport.error;
1058
- }
1059
- timer.stop();
1060
- logger.info(`Completed in ${timer.elapsedMs}`);
1061
- };
1239
+ const resolvePath = (cwd) => path$1.resolve(cwd ?? process.cwd());
1062
1240
  const getRangeFromTarget = (currentVersion, target) => {
1063
1241
  if (isSemverInstance(target)) {
1064
1242
  return rangeFactory(target);
@@ -1075,9 +1253,60 @@ const getRangeFromTarget = (currentVersion, target) => {
1075
1253
  throw new Error(`Invalid target set: ${target}`);
1076
1254
  }
1077
1255
  };
1256
+ const findRangeFromTarget = (project, target) => {
1257
+ if (isRangeInstance(target)) {
1258
+ return target;
1259
+ }
1260
+ if (isApplicationProject(project)) {
1261
+ return getRangeFromTarget(project.strapiVersion, target);
1262
+ }
1263
+ return rangeFactory("*");
1264
+ };
1265
+ const runCodemods = async (options) => {
1266
+ const timer = timerFactory();
1267
+ const { logger, uid } = options;
1268
+ const cwd = resolvePath(options.cwd);
1269
+ const project = projectFactory(cwd);
1270
+ const range = findRangeFromTarget(project, options.target);
1271
+ logger.debug(`Project: ${projectType(project.type)} found in ${path(cwd)}`);
1272
+ logger.debug(`Range: set to ${versionRange(range)}`);
1273
+ const codemodRunner = codemodRunnerFactory(project, range).dry(options.dry ?? false).onSelectCodemods(options.selectCodemods ?? null).setLogger(logger);
1274
+ let report;
1275
+ if (uid !== void 0) {
1276
+ logger.debug(`Running a single codemod: ${codemodUID(uid)}`);
1277
+ report = await codemodRunner.runByUID(uid);
1278
+ } else {
1279
+ report = await codemodRunner.run();
1280
+ }
1281
+ if (!report.success) {
1282
+ throw report.error;
1283
+ }
1284
+ timer.stop();
1285
+ logger.info(`Completed in ${timer.elapsedMs}`);
1286
+ };
1287
+ const listCodemods = async (options) => {
1288
+ const { logger, target } = options;
1289
+ const cwd = resolvePath(options.cwd);
1290
+ const project = projectFactory(cwd);
1291
+ const range = findRangeFromTarget(project, target);
1292
+ logger.debug(`Project: ${projectType(project.type)} found in ${path(cwd)}`);
1293
+ logger.debug(`Range: set to ${versionRange(range)}`);
1294
+ const repo = codemodRepositoryFactory();
1295
+ repo.refresh();
1296
+ const groups = repo.find({ range });
1297
+ const codemods = groups.flatMap((collection) => collection.codemods);
1298
+ logger.debug(`Found ${highlight(codemods.length)} codemods`);
1299
+ if (codemods.length === 0) {
1300
+ logger.info(`Found no codemods matching ${versionRange(range)}`);
1301
+ return;
1302
+ }
1303
+ const fCodemods = codemodList(codemods);
1304
+ logger.raw(fCodemods);
1305
+ };
1078
1306
  const index$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1079
1307
  __proto__: null,
1080
- codemods,
1308
+ listCodemods,
1309
+ runCodemods,
1081
1310
  upgrade
1082
1311
  }, Symbol.toStringTag, { value: "Module" }));
1083
1312
  class Logger {