@strapi/upgrade 0.0.0-experimental.d362bf200f5f9359a4bbd4a549603de5ee1f04ca → 0.0.0-experimental.d65615a2b9130dd742d3c396674457d7971da928

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 (71) hide show
  1. package/LICENSE +19 -4
  2. package/dist/cli.js +1489 -5
  3. package/dist/cli.js.map +1 -1
  4. package/dist/index.js +305 -130
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +305 -131
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/modules/codemod/codemod.d.ts +4 -2
  9. package/dist/modules/codemod/codemod.d.ts.map +1 -1
  10. package/dist/modules/codemod/types.d.ts +8 -1
  11. package/dist/modules/codemod/types.d.ts.map +1 -1
  12. package/dist/modules/codemod-repository/constants.d.ts.map +1 -1
  13. package/dist/modules/codemod-repository/repository.d.ts +5 -5
  14. package/dist/modules/codemod-repository/repository.d.ts.map +1 -1
  15. package/dist/modules/codemod-repository/types.d.ts +7 -3
  16. package/dist/modules/codemod-repository/types.d.ts.map +1 -1
  17. package/dist/modules/codemod-runner/codemod-runner.d.ts +6 -3
  18. package/dist/modules/codemod-runner/codemod-runner.d.ts.map +1 -1
  19. package/dist/modules/codemod-runner/index.d.ts +1 -0
  20. package/dist/modules/codemod-runner/index.d.ts.map +1 -1
  21. package/dist/modules/codemod-runner/types.d.ts +1 -0
  22. package/dist/modules/codemod-runner/types.d.ts.map +1 -1
  23. package/dist/modules/file-scanner/scanner.d.ts.map +1 -1
  24. package/dist/modules/format/formats.d.ts +6 -0
  25. package/dist/modules/format/formats.d.ts.map +1 -1
  26. package/dist/modules/project/constants.d.ts +6 -5
  27. package/dist/modules/project/constants.d.ts.map +1 -1
  28. package/dist/modules/project/project.d.ts +17 -3
  29. package/dist/modules/project/project.d.ts.map +1 -1
  30. package/dist/modules/project/types.d.ts +4 -0
  31. package/dist/modules/project/types.d.ts.map +1 -1
  32. package/dist/modules/project/utils.d.ts +1 -1
  33. package/dist/modules/project/utils.d.ts.map +1 -1
  34. package/dist/modules/report/report.d.ts.map +1 -1
  35. package/dist/modules/runner/json/transform.d.ts.map +1 -1
  36. package/dist/modules/upgrader/upgrader.d.ts.map +1 -1
  37. package/dist/modules/version/range.d.ts +2 -0
  38. package/dist/modules/version/range.d.ts.map +1 -1
  39. package/dist/tasks/codemods/index.d.ts +2 -1
  40. package/dist/tasks/codemods/index.d.ts.map +1 -1
  41. package/dist/tasks/codemods/list-codemods.d.ts +3 -0
  42. package/dist/tasks/codemods/list-codemods.d.ts.map +1 -0
  43. package/dist/tasks/codemods/run-codemods.d.ts +3 -0
  44. package/dist/tasks/codemods/run-codemods.d.ts.map +1 -0
  45. package/dist/tasks/codemods/types.d.ts +9 -3
  46. package/dist/tasks/codemods/types.d.ts.map +1 -1
  47. package/dist/tasks/codemods/utils.d.ts +6 -0
  48. package/dist/tasks/codemods/utils.d.ts.map +1 -0
  49. package/dist/tasks/index.d.ts +1 -1
  50. package/dist/tasks/index.d.ts.map +1 -1
  51. package/dist/tasks/upgrade/upgrade.d.ts.map +1 -1
  52. package/package.json +10 -9
  53. package/resources/codemods/5.0.0/comment-out-lifecycle-files.code.ts +63 -0
  54. package/resources/codemods/5.0.0/dependency-upgrade-react-and-react-dom.json.ts +67 -0
  55. package/resources/codemods/5.0.0/dependency-upgrade-styled-components.json.ts +49 -0
  56. package/resources/codemods/5.0.0/deprecate-helper-plugin.code.ts +192 -0
  57. package/resources/codemods/5.0.0/entity-service-document-service.code.ts +437 -0
  58. package/resources/codemods/5.0.0/strapi-public-interface.code.ts +126 -0
  59. package/resources/codemods/5.0.0/utils-public-interface.code.ts +320 -0
  60. package/resources/utils/change-import.ts +118 -0
  61. package/resources/utils/replace-jsx.ts +49 -0
  62. package/dist/_chunks/codemod-runner-mXNzVpHm.js +0 -798
  63. package/dist/_chunks/codemod-runner-mXNzVpHm.js.map +0 -1
  64. package/dist/_chunks/codemods-S4mNX9Qg.js +0 -105
  65. package/dist/_chunks/codemods-S4mNX9Qg.js.map +0 -1
  66. package/dist/_chunks/index-Pt-TU9MN.js +0 -103
  67. package/dist/_chunks/index-Pt-TU9MN.js.map +0 -1
  68. package/dist/_chunks/upgrade-aWNYibWB.js +0 -361
  69. package/dist/_chunks/upgrade-aWNYibWB.js.map +0 -1
  70. package/dist/tasks/codemods/codemods.d.ts +0 -3
  71. package/dist/tasks/codemods/codemods.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ const utils = require("@strapi/utils");
8
8
  const fp = require("lodash/fp");
9
9
  const fse = require("fs-extra");
10
10
  const assert = require("node:assert");
11
- const glob = require("glob");
11
+ const fastglob = require("fast-glob");
12
12
  const Runner = require("jscodeshift/src/Runner");
13
13
  const node = require("esbuild-register/dist/node");
14
14
  const CliTable3 = require("cli-table3");
@@ -19,6 +19,7 @@ const chalk__default = /* @__PURE__ */ _interopDefault(chalk);
19
19
  const semver__default = /* @__PURE__ */ _interopDefault(semver);
20
20
  const fse__default = /* @__PURE__ */ _interopDefault(fse);
21
21
  const assert__default = /* @__PURE__ */ _interopDefault(assert);
22
+ const fastglob__default = /* @__PURE__ */ _interopDefault(fastglob);
22
23
  const CliTable3__default = /* @__PURE__ */ _interopDefault(CliTable3);
23
24
  class Requirement {
24
25
  isRequired;
@@ -263,13 +264,19 @@ const rangeFromVersions = (currentVersion, target) => {
263
264
  }
264
265
  throw new Error(`Invalid target set: ${target}`);
265
266
  };
267
+ const isValidStringifiedRange = (str) => semver__default.default.validRange(str) !== null;
268
+ const isRangeInstance = (range) => {
269
+ return range instanceof semver__default.default.Range;
270
+ };
266
271
  const index$e = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
267
272
  __proto__: null,
268
273
  Version: types,
269
274
  isLiteralSemVer,
275
+ isRangeInstance,
270
276
  isSemVerReleaseType,
271
277
  isSemverInstance,
272
278
  isValidSemVer,
279
+ isValidStringifiedRange,
273
280
  rangeFactory,
274
281
  rangeFromReleaseType,
275
282
  rangeFromVersions,
@@ -281,7 +288,9 @@ class FileScanner {
281
288
  this.cwd = cwd;
282
289
  }
283
290
  scan(patterns) {
284
- const filenames = glob.glob.sync(patterns, { cwd: this.cwd });
291
+ const filenames = fastglob__default.default.sync(patterns, {
292
+ cwd: this.cwd
293
+ });
285
294
  return filenames.map((filename) => path__default.default.join(this.cwd, filename));
286
295
  }
287
296
  }
@@ -330,7 +339,11 @@ const transformJSON = async (codemodPath, paths, config) => {
330
339
  timeElapsed: "",
331
340
  stats: {}
332
341
  };
333
- const esbuildOptions = { extensions: [".js", ".mjs", ".ts"] };
342
+ const esbuildOptions = {
343
+ extensions: [".js", ".mjs", ".ts"],
344
+ hookIgnoreNodeModules: false,
345
+ hookMatcher: fp.isEqual(codemodPath)
346
+ };
334
347
  const { unregister } = node.register(esbuildOptions);
335
348
  const module2 = require(codemodPath);
336
349
  unregister();
@@ -375,8 +388,10 @@ const index$b = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
375
388
  jsonRunnerFactory
376
389
  }, Symbol.toStringTag, { value: "Module" }));
377
390
  const PROJECT_PACKAGE_JSON = "package.json";
378
- const PROJECT_DEFAULT_ALLOWED_ROOT_PATHS = ["src", "config", "public"];
379
- const PROJECT_DEFAULT_CODE_EXTENSIONS = [
391
+ const PROJECT_APP_ALLOWED_ROOT_PATHS = ["src", "config", "public"];
392
+ const PROJECT_PLUGIN_ALLOWED_ROOT_PATHS = ["admin", "server"];
393
+ const PROJECT_PLUGIN_ROOT_FILES = ["strapi-admin.js", "strapi-server.js"];
394
+ const PROJECT_CODE_EXTENSIONS = [
380
395
  // Source files
381
396
  "js",
382
397
  "mjs",
@@ -385,22 +400,19 @@ const PROJECT_DEFAULT_CODE_EXTENSIONS = [
385
400
  "jsx",
386
401
  "tsx"
387
402
  ];
388
- const PROJECT_DEFAULT_JSON_EXTENSIONS = ["json"];
389
- const PROJECT_DEFAULT_ALLOWED_EXTENSIONS = [
390
- ...PROJECT_DEFAULT_CODE_EXTENSIONS,
391
- ...PROJECT_DEFAULT_JSON_EXTENSIONS
392
- ];
393
- const PROJECT_DEFAULT_PATTERNS = ["package.json"];
403
+ const PROJECT_JSON_EXTENSIONS = ["json"];
404
+ const PROJECT_ALLOWED_EXTENSIONS = [...PROJECT_CODE_EXTENSIONS, ...PROJECT_JSON_EXTENSIONS];
394
405
  const SCOPED_STRAPI_PACKAGE_PREFIX = "@strapi/";
395
406
  const STRAPI_DEPENDENCY_NAME = `${SCOPED_STRAPI_PACKAGE_PREFIX}strapi`;
396
407
  const constants$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
397
408
  __proto__: null,
398
- PROJECT_DEFAULT_ALLOWED_EXTENSIONS,
399
- PROJECT_DEFAULT_ALLOWED_ROOT_PATHS,
400
- PROJECT_DEFAULT_CODE_EXTENSIONS,
401
- PROJECT_DEFAULT_JSON_EXTENSIONS,
402
- PROJECT_DEFAULT_PATTERNS,
409
+ PROJECT_ALLOWED_EXTENSIONS,
410
+ PROJECT_APP_ALLOWED_ROOT_PATHS,
411
+ PROJECT_CODE_EXTENSIONS,
412
+ PROJECT_JSON_EXTENSIONS,
403
413
  PROJECT_PACKAGE_JSON,
414
+ PROJECT_PLUGIN_ALLOWED_ROOT_PATHS,
415
+ PROJECT_PLUGIN_ROOT_FILES,
404
416
  SCOPED_STRAPI_PACKAGE_PREFIX,
405
417
  STRAPI_DEPENDENCY_NAME
406
418
  }, Symbol.toStringTag, { value: "Module" }));
@@ -410,11 +422,13 @@ class Project {
410
422
  files;
411
423
  packageJSONPath;
412
424
  packageJSON;
413
- constructor(cwd) {
425
+ paths;
426
+ constructor(cwd, config) {
414
427
  if (!fse__default.default.pathExistsSync(cwd)) {
415
428
  throw new Error(`ENOENT: no such file or directory, access '${cwd}'`);
416
429
  }
417
430
  this.cwd = cwd;
431
+ this.paths = config.paths;
418
432
  this.refresh();
419
433
  }
420
434
  getFilesByExtensions(extensions) {
@@ -428,10 +442,10 @@ class Project {
428
442
  this.refreshProjectFiles();
429
443
  return this;
430
444
  }
431
- async runCodemods(codemods2, options) {
445
+ async runCodemods(codemods, options) {
432
446
  const runners = this.createProjectCodemodsRunners(options.dry);
433
447
  const reports2 = [];
434
- for (const codemod of codemods2) {
448
+ for (const codemod of codemods) {
435
449
  for (const runner of runners) {
436
450
  if (runner.valid(codemod)) {
437
451
  const report = await runner.run(codemod);
@@ -442,12 +456,8 @@ class Project {
442
456
  return reports2;
443
457
  }
444
458
  createProjectCodemodsRunners(dry = false) {
445
- const jsonExtensions = PROJECT_DEFAULT_JSON_EXTENSIONS.map(
446
- (ext) => `.${ext}`
447
- );
448
- const codeExtensions = PROJECT_DEFAULT_CODE_EXTENSIONS.map(
449
- (ext) => `.${ext}`
450
- );
459
+ const jsonExtensions = PROJECT_JSON_EXTENSIONS.map((ext) => `.${ext}`);
460
+ const codeExtensions = PROJECT_CODE_EXTENSIONS.map((ext) => `.${ext}`);
451
461
  const jsonFiles = this.getFilesByExtensions(jsonExtensions);
452
462
  const codeFiles = this.getFilesByExtensions(codeExtensions);
453
463
  const codeRunner = codeRunnerFactory(codeFiles, {
@@ -455,7 +465,7 @@ class Project {
455
465
  parser: "ts",
456
466
  runInBand: true,
457
467
  babel: true,
458
- extensions: PROJECT_DEFAULT_CODE_EXTENSIONS.join(","),
468
+ extensions: PROJECT_CODE_EXTENSIONS.join(","),
459
469
  // Don't output any log coming from the runner
460
470
  print: false,
461
471
  silent: true,
@@ -476,23 +486,32 @@ class Project {
476
486
  this.packageJSON = JSON.parse(packageJSONBuffer.toString());
477
487
  }
478
488
  refreshProjectFiles() {
479
- const allowedRootPaths = formatGlobCollectionPattern(
480
- PROJECT_DEFAULT_ALLOWED_ROOT_PATHS
481
- );
482
- const allowedExtensions = formatGlobCollectionPattern(
483
- PROJECT_DEFAULT_ALLOWED_EXTENSIONS
484
- );
485
- const projectFilesPattern = `./${allowedRootPaths}/**/*.${allowedExtensions}`;
486
- const patterns = [projectFilesPattern, ...PROJECT_DEFAULT_PATTERNS];
487
489
  const scanner = fileScannerFactory(this.cwd);
488
- this.files = scanner.scan(patterns);
490
+ this.files = scanner.scan(this.paths);
489
491
  }
490
492
  }
491
493
  class AppProject extends Project {
492
494
  strapiVersion;
493
- type = "app";
495
+ type = "application";
496
+ /**
497
+ * Returns an array of allowed file paths for a Strapi application
498
+ *
499
+ * The resulting paths include app default files and the root package.json file.
500
+ */
501
+ static get paths() {
502
+ const allowedRootPaths = formatGlobCollectionPattern(PROJECT_APP_ALLOWED_ROOT_PATHS);
503
+ const allowedExtensions = formatGlobCollectionPattern(PROJECT_ALLOWED_EXTENSIONS);
504
+ return [
505
+ // App default files
506
+ `./${allowedRootPaths}/**/*.${allowedExtensions}`,
507
+ `!./**/node_modules/**/*`,
508
+ `!./**/dist/**/*`,
509
+ // Root package.json file
510
+ PROJECT_PACKAGE_JSON
511
+ ];
512
+ }
494
513
  constructor(cwd) {
495
- super(cwd);
514
+ super(cwd, { paths: AppProject.paths });
496
515
  this.refreshStrapiVersion();
497
516
  }
498
517
  refresh() {
@@ -547,6 +566,30 @@ const formatGlobCollectionPattern = (collection) => {
547
566
  };
548
567
  class PluginProject extends Project {
549
568
  type = "plugin";
569
+ /**
570
+ * Returns an array of allowed file paths for a Strapi plugin
571
+ *
572
+ * The resulting paths include plugin default files, the root package.json file, and plugin-specific files.
573
+ */
574
+ static get paths() {
575
+ const allowedRootPaths = formatGlobCollectionPattern(
576
+ PROJECT_PLUGIN_ALLOWED_ROOT_PATHS
577
+ );
578
+ const allowedExtensions = formatGlobCollectionPattern(PROJECT_ALLOWED_EXTENSIONS);
579
+ return [
580
+ // Plugin default files
581
+ `./${allowedRootPaths}/**/*.${allowedExtensions}`,
582
+ `!./**/node_modules/**/*`,
583
+ `!./**/dist/**/*`,
584
+ // Root package.json file
585
+ PROJECT_PACKAGE_JSON,
586
+ // Plugin root files
587
+ ...PROJECT_PLUGIN_ROOT_FILES
588
+ ];
589
+ }
590
+ constructor(cwd) {
591
+ super(cwd, { paths: PluginProject.paths });
592
+ }
550
593
  }
551
594
  const isPlugin = (cwd) => {
552
595
  const packageJSONPath = path__default.default.join(cwd, PROJECT_PACKAGE_JSON);
@@ -561,10 +604,7 @@ const isPlugin = (cwd) => {
561
604
  };
562
605
  const projectFactory = (cwd) => {
563
606
  fse__default.default.accessSync(cwd);
564
- if (isPlugin(cwd)) {
565
- return new PluginProject(cwd);
566
- }
567
- return new AppProject(cwd);
607
+ return isPlugin(cwd) ? new PluginProject(cwd) : new AppProject(cwd);
568
608
  };
569
609
  const isPluginProject = (project) => {
570
610
  return project instanceof PluginProject;
@@ -574,12 +614,12 @@ function assertPluginProject(project) {
574
614
  throw new Error("Project is not a plugin");
575
615
  }
576
616
  }
577
- const isAppProject = (project) => {
617
+ const isApplicationProject = (project) => {
578
618
  return project instanceof AppProject;
579
619
  };
580
620
  function assertAppProject(project) {
581
- if (!isAppProject(project)) {
582
- throw new Error("Project is not an app");
621
+ if (!isApplicationProject(project)) {
622
+ throw new Error("Project is not an application");
583
623
  }
584
624
  }
585
625
  const index$a = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
@@ -587,7 +627,7 @@ const index$a = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
587
627
  assertAppProject,
588
628
  assertPluginProject,
589
629
  constants: constants$3,
590
- isAppProject,
630
+ isApplicationProject,
591
631
  isPluginProject,
592
632
  projectFactory
593
633
  }, Symbol.toStringTag, { value: "Module" }));
@@ -614,7 +654,14 @@ const path = (path2) => chalk__default.default.blue(path2);
614
654
  const version = (version2) => {
615
655
  return chalk__default.default.italic.yellow(`v${version2}`);
616
656
  };
617
- const versionRange = (range) => chalk__default.default.italic.yellow(range);
657
+ const codemodUID = (uid) => {
658
+ return chalk__default.default.bold.cyan(uid);
659
+ };
660
+ const projectDetails = (project) => {
661
+ return `Project: TYPE=${projectType(project.type)}; CWD=${path(project.cwd)}; PATHS=${project.paths.map(path)}`;
662
+ };
663
+ const projectType = (type) => chalk__default.default.cyan(type);
664
+ const versionRange = (range) => chalk__default.default.italic.yellow(range.raw);
618
665
  const transform = (transformFilePath) => chalk__default.default.cyan(transformFilePath);
619
666
  const highlight = (arg) => chalk__default.default.bold.underline(arg);
620
667
  const upgradeStep = (text, step) => {
@@ -646,15 +693,41 @@ const reports = (reports2) => {
646
693
  table.push(...rows);
647
694
  return table.toString();
648
695
  };
696
+ const codemodList = (codemods) => {
697
+ const rows = codemods.map((codemod, index2) => {
698
+ const fIndex = chalk__default.default.grey(index2);
699
+ const fVersion = chalk__default.default.magenta(codemod.version);
700
+ const fKind = chalk__default.default.yellow(codemod.kind);
701
+ const fName = chalk__default.default.blue(codemod.format());
702
+ const fUID = codemodUID(codemod.uid);
703
+ return [fIndex, fVersion, fKind, fName, fUID];
704
+ });
705
+ const table = new CliTable3__default.default({
706
+ style: { compact: true },
707
+ head: [
708
+ chalk__default.default.bold.grey("N°"),
709
+ chalk__default.default.bold.magenta("Version"),
710
+ chalk__default.default.bold.yellow("Kind"),
711
+ chalk__default.default.bold.blue("Name"),
712
+ chalk__default.default.bold.cyan("UID")
713
+ ]
714
+ });
715
+ table.push(...rows);
716
+ return table.toString();
717
+ };
649
718
  const durationMs = (elapsedMs) => {
650
719
  const elapsedSeconds = (elapsedMs / ONE_SECOND_MS).toFixed(3);
651
720
  return `${elapsedSeconds}s`;
652
721
  };
653
722
  const index$8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
654
723
  __proto__: null,
724
+ codemodList,
725
+ codemodUID,
655
726
  durationMs,
656
727
  highlight,
657
728
  path,
729
+ projectDetails,
730
+ projectType,
658
731
  reports,
659
732
  transform,
660
733
  upgradeStep,
@@ -677,6 +750,7 @@ const constants$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
677
750
  CODEMOD_JSON_SUFFIX
678
751
  }, Symbol.toStringTag, { value: "Module" }));
679
752
  class Codemod {
753
+ uid;
680
754
  kind;
681
755
  version;
682
756
  baseDirectory;
@@ -688,9 +762,27 @@ class Codemod {
688
762
  this.baseDirectory = options.baseDirectory;
689
763
  this.filename = options.filename;
690
764
  this.path = path__default.default.join(this.baseDirectory, this.version.raw, this.filename);
691
- }
692
- format() {
693
- return this.filename.replace(`.${CODEMOD_CODE_SUFFIX}.${CODEMOD_EXTENSION}`, "").replace(`.${CODEMOD_JSON_SUFFIX}.${CODEMOD_EXTENSION}`, "").replaceAll("-", " ");
765
+ this.uid = this.createUID();
766
+ }
767
+ createUID() {
768
+ const name = this.format({ stripExtension: true, stripKind: true, stripHyphens: false });
769
+ const kind = this.kind;
770
+ const version2 = this.version.raw;
771
+ return `${version2}-${name}-${kind}`;
772
+ }
773
+ format(options) {
774
+ const { stripExtension = true, stripKind = true, stripHyphens = true } = options ?? {};
775
+ let formatted = this.filename;
776
+ if (stripExtension) {
777
+ formatted = formatted.replace(new RegExp(`\\.${CODEMOD_EXTENSION}$`, "i"), "");
778
+ }
779
+ if (stripKind) {
780
+ formatted = formatted.replace(`.${CODEMOD_CODE_SUFFIX}`, "").replace(`.${CODEMOD_JSON_SUFFIX}`, "");
781
+ }
782
+ if (stripHyphens) {
783
+ formatted = formatted.replaceAll("-", " ");
784
+ }
785
+ return formatted;
694
786
  }
695
787
  }
696
788
  const codemodFactory = (options) => new Codemod(options);
@@ -699,6 +791,20 @@ const index$7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
699
791
  codemodFactory,
700
792
  constants: constants$2
701
793
  }, Symbol.toStringTag, { value: "Module" }));
794
+ const INTERNAL_CODEMODS_DIRECTORY = path__default.default.join(
795
+ __dirname,
796
+ // upgrade/dist
797
+ "..",
798
+ // upgrade
799
+ "resources",
800
+ // upgrade/resources
801
+ "codemods"
802
+ // upgrade/resources/codemods
803
+ );
804
+ const constants$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
805
+ __proto__: null,
806
+ INTERNAL_CODEMODS_DIRECTORY
807
+ }, Symbol.toStringTag, { value: "Module" }));
702
808
  class CodemodRepository {
703
809
  groups;
704
810
  versions;
@@ -717,29 +823,47 @@ class CodemodRepository {
717
823
  count(version2) {
718
824
  return this.findByVersion(version2).length;
719
825
  }
720
- countRange(range) {
721
- return this.findByRange(range).length;
722
- }
723
- exists(version2) {
826
+ versionExists(version2) {
724
827
  return version2.raw in this.groups;
725
828
  }
726
- findByRange(range) {
829
+ has(uid) {
830
+ const result = this.find({ uids: [uid] });
831
+ if (result.length !== 1) {
832
+ return false;
833
+ }
834
+ const { codemods } = result[0];
835
+ return codemods.length === 1 && codemods[0].uid === uid;
836
+ }
837
+ find(q) {
727
838
  const entries = Object.entries(this.groups);
728
- return entries.filter(([version2]) => range.test(version2)).map(([version2, codemods2]) => ({
839
+ return entries.filter(maybeFilterByRange).map(([version2, codemods]) => ({
729
840
  version: semVerFactory(version2),
730
- codemods: codemods2
731
- }));
841
+ // Filter by UID if provided in the query
842
+ codemods: codemods.filter(maybeFilterByUIDs)
843
+ })).filter(({ codemods }) => codemods.length > 0);
844
+ function maybeFilterByRange([version2]) {
845
+ if (!isRangeInstance(q.range)) {
846
+ return true;
847
+ }
848
+ return q.range.test(version2);
849
+ }
850
+ function maybeFilterByUIDs(codemod) {
851
+ if (q.uids === void 0) {
852
+ return true;
853
+ }
854
+ return q.uids.includes(codemod.uid);
855
+ }
732
856
  }
733
857
  findByVersion(version2) {
734
858
  const literalVersion = version2.raw;
735
- const codemods2 = this.groups[literalVersion];
736
- return codemods2 ?? [];
859
+ const codemods = this.groups[literalVersion];
860
+ return codemods ?? [];
737
861
  }
738
862
  findAll() {
739
863
  const entries = Object.entries(this.groups);
740
- return entries.map(([version2, codemods2]) => ({
864
+ return entries.map(([version2, codemods]) => ({
741
865
  version: semVerFactory(version2),
742
- codemods: codemods2
866
+ codemods
743
867
  }));
744
868
  }
745
869
  refreshAvailableVersions() {
@@ -771,18 +895,9 @@ const parseCodemodKindFromFilename = (filename) => {
771
895
  assert__default.default(CODEMOD_ALLOWED_SUFFIXES.includes(kind));
772
896
  return kind;
773
897
  };
774
- const codemodRepositoryFactory = (cwd) => new CodemodRepository(cwd);
775
- const INTERNAL_CODEMODS_DIRECTORY = path__default.default.join(
776
- __dirname,
777
- "..",
778
- "..",
779
- "resources",
780
- "codemods"
781
- );
782
- const constants$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
783
- __proto__: null,
784
- INTERNAL_CODEMODS_DIRECTORY
785
- }, Symbol.toStringTag, { value: "Module" }));
898
+ const codemodRepositoryFactory = (cwd = INTERNAL_CODEMODS_DIRECTORY) => {
899
+ return new CodemodRepository(cwd);
900
+ };
786
901
  const index$6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
787
902
  __proto__: null,
788
903
  codemodRepositoryFactory,
@@ -817,40 +932,59 @@ class CodemodRunner {
817
932
  this.isDry = enabled;
818
933
  return this;
819
934
  }
820
- async run(codemodsDirectory) {
935
+ createRepository(codemodsDirectory) {
821
936
  const repository = codemodRepositoryFactory(
822
937
  codemodsDirectory ?? INTERNAL_CODEMODS_DIRECTORY
823
938
  );
824
939
  repository.refresh();
825
- const allVersionedCodemods = this.range ? repository.findByRange(this.range) : repository.findAll();
826
- const versionedCodemods = this.selectCodemodsCallback ? await this.selectCodemodsCallback(allVersionedCodemods) : allVersionedCodemods;
827
- const hasCodemodsToRun = versionedCodemods.length > 0;
828
- if (!hasCodemodsToRun) {
829
- if (this.range) {
830
- this.logger?.debug(`Found no codemods to run for ${versionRange(this.range)}`);
831
- } else {
832
- this.logger?.debug(`Found no codemods to run`);
833
- }
834
- return successReport$1();
835
- }
836
- if (this.range) {
837
- this.logger?.debug(
838
- `Found codemods for ${highlight(versionedCodemods.length)} version(s) using ${this.range}`
940
+ return repository;
941
+ }
942
+ async safeRunAndReport(codemods) {
943
+ if (this.isDry) {
944
+ this.logger?.warn?.(
945
+ "Running the codemods in dry mode. No files will be modified during the process."
839
946
  );
840
- } else {
841
- this.logger?.debug(`Found codemods for ${highlight(versionedCodemods.length)} version(s)`);
842
947
  }
843
- versionedCodemods.forEach(
844
- ({ version: version$1, codemods: codemods22 }) => this.logger?.debug(`- ${version(version$1)} (${codemods22.length})`)
845
- );
846
- const codemods2 = versionedCodemods.map(({ codemods: codemods22 }) => codemods22).flat();
847
948
  try {
848
- const reports$1 = await this.project.runCodemods(codemods2, { dry: this.isDry });
849
- this.logger?.raw(reports(reports$1));
949
+ const reports$1 = await this.project.runCodemods(codemods, { dry: this.isDry });
950
+ this.logger?.raw?.(reports(reports$1));
951
+ if (!this.isDry) {
952
+ const nbAffectedTotal = reports$1.flatMap((report) => report.report.ok).reduce((acc, nb) => acc + nb, 0);
953
+ this.logger?.debug?.(
954
+ `Successfully ran ${highlight(codemods.length)} codemod(s), ${highlight(nbAffectedTotal)} change(s) have been detected`
955
+ );
956
+ }
957
+ return successReport$1();
850
958
  } catch (e) {
851
959
  return erroredReport$1(unknownToError(e));
852
960
  }
853
- return successReport$1();
961
+ }
962
+ async runByUID(uid, codemodsDirectory) {
963
+ const repository = this.createRepository(codemodsDirectory);
964
+ if (!repository.has(uid)) {
965
+ throw new Error(`Unknown codemod UID provided: ${uid}`);
966
+ }
967
+ const codemods = repository.find({ uids: [uid] }).flatMap(({ codemods: codemods2 }) => codemods2);
968
+ return this.safeRunAndReport(codemods);
969
+ }
970
+ async run(codemodsDirectory) {
971
+ const repository = this.createRepository(codemodsDirectory);
972
+ const codemodsInRange = repository.find({ range: this.range });
973
+ const selectedCodemods = this.selectCodemodsCallback ? await this.selectCodemodsCallback(codemodsInRange) : codemodsInRange;
974
+ if (selectedCodemods.length === 0) {
975
+ this.logger?.debug?.(`Found no codemods to run for ${versionRange(this.range)}`);
976
+ return successReport$1();
977
+ }
978
+ const codemods = selectedCodemods.flatMap(({ codemods: codemods2 }) => codemods2);
979
+ const codemodsByVersion = fp.groupBy("version", codemods);
980
+ const fRange = versionRange(this.range);
981
+ this.logger?.debug?.(
982
+ `Found ${highlight(codemods.length)} codemods for ${highlight(fp.size(codemodsByVersion))} version(s) using ${fRange}`
983
+ );
984
+ for (const [version$1, codemods2] of Object.entries(codemodsByVersion)) {
985
+ this.logger?.debug?.(`- ${version(semVerFactory(version$1))} (${codemods2.length})`);
986
+ }
987
+ return this.safeRunAndReport(codemods);
854
988
  }
855
989
  }
856
990
  const codemodRunnerFactory = (project, range) => {
@@ -889,7 +1023,7 @@ class Upgrader {
889
1023
  this.codemodsTarget = semVerFactory(
890
1024
  `${this.target.major}.${this.target.minor}.${this.target.patch}`
891
1025
  );
892
- this.logger?.debug(
1026
+ this.logger?.debug?.(
893
1027
  `The codemods target has been synced with the upgrade target. The codemod runner will now look for ${version(
894
1028
  this.codemodsTarget
895
1029
  )}`
@@ -898,7 +1032,7 @@ class Upgrader {
898
1032
  }
899
1033
  overrideCodemodsTarget(target) {
900
1034
  this.codemodsTarget = target;
901
- this.logger?.debug(
1035
+ this.logger?.debug?.(
902
1036
  `Overriding the codemods target. The codemod runner will now look for ${version(target)}`
903
1037
  );
904
1038
  return this;
@@ -918,40 +1052,40 @@ class Upgrader {
918
1052
  addRequirement(requirement) {
919
1053
  this.requirements.push(requirement);
920
1054
  const fRequired = requirement.isRequired ? "(required)" : "(optional)";
921
- this.logger?.debug(
1055
+ this.logger?.debug?.(
922
1056
  `Added a new requirement to the upgrade: ${highlight(requirement.name)} ${fRequired}`
923
1057
  );
924
1058
  return this;
925
1059
  }
926
1060
  async upgrade() {
927
- this.logger?.info(
1061
+ this.logger?.info?.(
928
1062
  `Upgrading from ${version(this.project.strapiVersion)} to ${version(this.target)}`
929
1063
  );
930
1064
  if (this.isDry) {
931
- this.logger?.warn(
1065
+ this.logger?.warn?.(
932
1066
  "Running the upgrade in dry mode. No files will be modified during the process."
933
1067
  );
934
1068
  }
935
1069
  const range = rangeFromVersions(this.project.strapiVersion, this.target);
936
1070
  const codemodsRange = rangeFromVersions(this.project.strapiVersion, this.codemodsTarget);
937
1071
  const npmVersionsMatches = this.npmPackage?.findVersionsInRange(range) ?? [];
938
- this.logger?.debug(
1072
+ this.logger?.debug?.(
939
1073
  `Found ${highlight(npmVersionsMatches.length)} versions satisfying ${versionRange(range)}`
940
1074
  );
941
1075
  try {
942
- this.logger?.info(upgradeStep("Checking requirement", [1, 4]));
1076
+ this.logger?.info?.(upgradeStep("Checking requirement", [1, 4]));
943
1077
  await this.checkRequirements(this.requirements, {
944
1078
  npmVersionsMatches,
945
1079
  project: this.project,
946
1080
  target: this.target
947
1081
  });
948
- this.logger?.info(upgradeStep("Applying the latest code modifications", [2, 4]));
1082
+ this.logger?.info?.(upgradeStep("Applying the latest code modifications", [2, 4]));
949
1083
  await this.runCodemods(codemodsRange);
950
- this.logger?.debug("Refreshing project information...");
1084
+ this.logger?.debug?.("Refreshing project information...");
951
1085
  this.project.refresh();
952
- this.logger?.info(upgradeStep("Upgrading Strapi dependencies", [3, 4]));
1086
+ this.logger?.info?.(upgradeStep("Upgrading Strapi dependencies", [3, 4]));
953
1087
  await this.updateDependencies();
954
- this.logger?.info(upgradeStep("Installing dependencies", [4, 4]));
1088
+ this.logger?.info?.(upgradeStep("Installing dependencies", [4, 4]));
955
1089
  await this.installDependencies();
956
1090
  } catch (e) {
957
1091
  return erroredReport(unknownToError(e));
@@ -984,7 +1118,7 @@ class Upgrader {
984
1118
  if (requirement.isRequired) {
985
1119
  throw error;
986
1120
  }
987
- this.logger?.warn(warningMessage);
1121
+ this.logger?.warn?.(warningMessage);
988
1122
  const response = await this.confirmationCallback?.(confirmationMessage);
989
1123
  if (!response) {
990
1124
  throw error;
@@ -995,9 +1129,11 @@ class Upgrader {
995
1129
  const json = createJSONTransformAPI(packageJSON);
996
1130
  const dependencies = json.get("dependencies", {});
997
1131
  const strapiDependencies = this.getScopedStrapiDependencies(dependencies);
998
- this.logger?.debug(`Found ${highlight(strapiDependencies.length)} dependency(ies) to update`);
1132
+ this.logger?.debug?.(
1133
+ `Found ${highlight(strapiDependencies.length)} dependency(ies) to update`
1134
+ );
999
1135
  strapiDependencies.forEach(
1000
- (dependency) => this.logger?.debug(`- ${dependency[0]} (${dependency[1]} -> ${this.target})`)
1136
+ (dependency) => this.logger?.debug?.(`- ${dependency[0]} (${dependency[1]} -> ${this.target})`)
1001
1137
  );
1002
1138
  if (strapiDependencies.length === 0) {
1003
1139
  return;
@@ -1005,7 +1141,7 @@ class Upgrader {
1005
1141
  strapiDependencies.forEach(([name]) => json.set(`dependencies.${name}`, this.target.raw));
1006
1142
  const updatedPackageJSON = json.root();
1007
1143
  if (this.isDry) {
1008
- this.logger?.debug(`Skipping dependencies update (${chalk__default.default.italic("dry mode")})`);
1144
+ this.logger?.debug?.(`Skipping dependencies update (${chalk__default.default.italic("dry mode")})`);
1009
1145
  return;
1010
1146
  }
1011
1147
  await saveJSON(packageJSONPath, updatedPackageJSON);
@@ -1025,9 +1161,9 @@ class Upgrader {
1025
1161
  async installDependencies() {
1026
1162
  const projectPath = this.project.cwd;
1027
1163
  const packageManagerName = await utils.packageManager.getPreferred(projectPath);
1028
- this.logger?.debug(`Using ${highlight(packageManagerName)} as package manager`);
1164
+ this.logger?.debug?.(`Using ${highlight(packageManagerName)} as package manager`);
1029
1165
  if (this.isDry) {
1030
- this.logger?.debug(`Skipping dependencies installation (${chalk__default.default.italic("dry mode")}`);
1166
+ this.logger?.debug?.(`Skipping dependencies installation (${chalk__default.default.italic("dry mode")}`);
1031
1167
  return;
1032
1168
  }
1033
1169
  await utils.packageManager.installDependencies(projectPath, packageManagerName, {
@@ -1126,7 +1262,8 @@ const upgrade = async (options) => {
1126
1262
  const { logger, codemodsTarget } = options;
1127
1263
  const cwd = path__default.default.resolve(options.cwd ?? process.cwd());
1128
1264
  const project = projectFactory(cwd);
1129
- if (!isAppProject(project)) {
1265
+ logger.debug(projectDetails(project));
1266
+ if (!isApplicationProject(project)) {
1130
1267
  throw new Error(
1131
1268
  `The "${options.target}" upgrade can only be run on a Strapi project; for plugins, please use "codemods".`
1132
1269
  );
@@ -1148,20 +1285,7 @@ const upgrade = async (options) => {
1148
1285
  timer.stop();
1149
1286
  logger.info(`Completed in ${durationMs(timer.elapsedMs)}`);
1150
1287
  };
1151
- const codemods = async (options) => {
1152
- const timer = timerFactory();
1153
- const { logger } = options;
1154
- const cwd = path__default.default.resolve(options.cwd ?? process.cwd());
1155
- const project = projectFactory(cwd);
1156
- const range = isAppProject(project) ? getRangeFromTarget(project.strapiVersion, options.target) : void 0;
1157
- const codemodRunner = codemodRunnerFactory(project, range).dry(options.dry ?? false).onSelectCodemods(options.selectCodemods ?? null).setLogger(logger);
1158
- const executionReport = await codemodRunner.run();
1159
- if (!executionReport.success) {
1160
- throw executionReport.error;
1161
- }
1162
- timer.stop();
1163
- logger.info(`Completed in ${timer.elapsedMs}`);
1164
- };
1288
+ const resolvePath = (cwd) => path__default.default.resolve(cwd ?? process.cwd());
1165
1289
  const getRangeFromTarget = (currentVersion, target) => {
1166
1290
  if (isSemverInstance(target)) {
1167
1291
  return rangeFactory(target);
@@ -1178,9 +1302,60 @@ const getRangeFromTarget = (currentVersion, target) => {
1178
1302
  throw new Error(`Invalid target set: ${target}`);
1179
1303
  }
1180
1304
  };
1305
+ const findRangeFromTarget = (project, target) => {
1306
+ if (isRangeInstance(target)) {
1307
+ return target;
1308
+ }
1309
+ if (isApplicationProject(project)) {
1310
+ return getRangeFromTarget(project.strapiVersion, target);
1311
+ }
1312
+ return rangeFactory("*");
1313
+ };
1314
+ const runCodemods = async (options) => {
1315
+ const timer = timerFactory();
1316
+ const { logger, uid } = options;
1317
+ const cwd = resolvePath(options.cwd);
1318
+ const project = projectFactory(cwd);
1319
+ const range = findRangeFromTarget(project, options.target);
1320
+ logger.debug(projectDetails(project));
1321
+ logger.debug(`Range: set to ${versionRange(range)}`);
1322
+ const codemodRunner = codemodRunnerFactory(project, range).dry(options.dry ?? false).onSelectCodemods(options.selectCodemods ?? null).setLogger(logger);
1323
+ let report;
1324
+ if (uid !== void 0) {
1325
+ logger.debug(`Running a single codemod: ${codemodUID(uid)}`);
1326
+ report = await codemodRunner.runByUID(uid);
1327
+ } else {
1328
+ report = await codemodRunner.run();
1329
+ }
1330
+ if (!report.success) {
1331
+ throw report.error;
1332
+ }
1333
+ timer.stop();
1334
+ logger.info(`Completed in ${timer.elapsedMs}`);
1335
+ };
1336
+ const listCodemods = async (options) => {
1337
+ const { logger, target } = options;
1338
+ const cwd = resolvePath(options.cwd);
1339
+ const project = projectFactory(cwd);
1340
+ const range = findRangeFromTarget(project, target);
1341
+ logger.debug(projectDetails(project));
1342
+ logger.debug(`Range: set to ${versionRange(range)}`);
1343
+ const repo = codemodRepositoryFactory();
1344
+ repo.refresh();
1345
+ const groups = repo.find({ range });
1346
+ const codemods = groups.flatMap((collection) => collection.codemods);
1347
+ logger.debug(`Found ${highlight(codemods.length)} codemods`);
1348
+ if (codemods.length === 0) {
1349
+ logger.info(`Found no codemods matching ${versionRange(range)}`);
1350
+ return;
1351
+ }
1352
+ const fCodemods = codemodList(codemods);
1353
+ logger.raw(fCodemods);
1354
+ };
1181
1355
  const index$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1182
1356
  __proto__: null,
1183
- codemods,
1357
+ listCodemods,
1358
+ runCodemods,
1184
1359
  upgrade
1185
1360
  }, Symbol.toStringTag, { value: "Module" }));
1186
1361
  class Logger {