@strapi/upgrade 0.0.0-experimental.e3e48deb89bd0a1b6cc69b698696566fa7854a95 → 0.0.0-experimental.e9122b401c96877b6707775c4f893660eab93ae3

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 (84) hide show
  1. package/LICENSE +19 -4
  2. package/dist/cli.js +1482 -5
  3. package/dist/cli.js.map +1 -1
  4. package/dist/index.js +366 -105
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +367 -106
  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 +6 -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 +3 -0
  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/format/formats.d.ts +6 -0
  24. package/dist/modules/format/formats.d.ts.map +1 -1
  25. package/dist/modules/project/constants.d.ts +6 -3
  26. package/dist/modules/project/constants.d.ts.map +1 -1
  27. package/dist/modules/project/index.d.ts +2 -0
  28. package/dist/modules/project/index.d.ts.map +1 -1
  29. package/dist/modules/project/project.d.ts +27 -5
  30. package/dist/modules/project/project.d.ts.map +1 -1
  31. package/dist/modules/project/types.d.ts +3 -10
  32. package/dist/modules/project/types.d.ts.map +1 -1
  33. package/dist/modules/project/utils.d.ts +6 -0
  34. package/dist/modules/project/utils.d.ts.map +1 -0
  35. package/dist/modules/report/report.d.ts.map +1 -1
  36. package/dist/modules/requirement/types.d.ts +2 -2
  37. package/dist/modules/requirement/types.d.ts.map +1 -1
  38. package/dist/modules/runner/json/transform.d.ts.map +1 -1
  39. package/dist/modules/upgrader/upgrader.d.ts +3 -3
  40. package/dist/modules/upgrader/upgrader.d.ts.map +1 -1
  41. package/dist/modules/version/range.d.ts +2 -0
  42. package/dist/modules/version/range.d.ts.map +1 -1
  43. package/dist/tasks/codemods/index.d.ts +2 -1
  44. package/dist/tasks/codemods/index.d.ts.map +1 -1
  45. package/dist/tasks/codemods/list-codemods.d.ts +3 -0
  46. package/dist/tasks/codemods/list-codemods.d.ts.map +1 -0
  47. package/dist/tasks/codemods/run-codemods.d.ts +3 -0
  48. package/dist/tasks/codemods/run-codemods.d.ts.map +1 -0
  49. package/dist/tasks/codemods/types.d.ts +9 -3
  50. package/dist/tasks/codemods/types.d.ts.map +1 -1
  51. package/dist/tasks/codemods/utils.d.ts +6 -0
  52. package/dist/tasks/codemods/utils.d.ts.map +1 -0
  53. package/dist/tasks/index.d.ts +1 -1
  54. package/dist/tasks/index.d.ts.map +1 -1
  55. package/dist/tasks/upgrade/upgrade.d.ts.map +1 -1
  56. package/package.json +9 -8
  57. package/resources/codemods/5.0.0/change-useAPIErrorHandler-import.code.ts +21 -0
  58. package/resources/codemods/5.0.0/comment-out-lifecycle-files.code.ts +63 -0
  59. package/resources/codemods/5.0.0/dependency-remove-strapi-plugin-i18n.json.ts +31 -0
  60. package/resources/codemods/5.0.0/dependency-upgrade-react-and-react-dom.json.ts +67 -0
  61. package/resources/codemods/5.0.0/dependency-upgrade-react-router-dom.json.ts +59 -0
  62. package/resources/codemods/5.0.0/dependency-upgrade-styled-components.json.ts +49 -0
  63. package/resources/codemods/5.0.0/entity-service-document-service.code.ts +437 -0
  64. package/resources/codemods/5.0.0/nocontent-migrate-to-emptystatelayout.code.ts +30 -0
  65. package/resources/codemods/5.0.0/s3-keys-wrapped-in-credentials.code.ts +1 -1
  66. package/resources/codemods/5.0.0/sqlite3-to-better-sqlite3.json.ts +5 -2
  67. package/resources/codemods/5.0.0/strapi-public-interface.code.ts +126 -0
  68. package/resources/codemods/5.0.0/use-uid-for-config-namespace.code.ts +1 -1
  69. package/resources/codemods/5.0.0/useRBAC-hook-import-change.code.ts +21 -0
  70. package/resources/codemods/5.0.0/utils-public-interface.code.ts +320 -0
  71. package/resources/examples/console.log-to-console.info.code.ts +1 -1
  72. package/resources/examples/disable-jsx-buttons.code.ts +42 -0
  73. package/resources/utils/change-import.ts +105 -0
  74. package/resources/utils/replace-jsx.ts +49 -0
  75. package/dist/_chunks/codemod-runner-B5OeSMTQ.js +0 -730
  76. package/dist/_chunks/codemod-runner-B5OeSMTQ.js.map +0 -1
  77. package/dist/_chunks/codemods-10ZKewQx.js +0 -108
  78. package/dist/_chunks/codemods-10ZKewQx.js.map +0 -1
  79. package/dist/_chunks/index-uxCwtuH1.js +0 -103
  80. package/dist/_chunks/index-uxCwtuH1.js.map +0 -1
  81. package/dist/_chunks/upgrade-A4T1OWs5.js +0 -357
  82. package/dist/_chunks/upgrade-A4T1OWs5.js.map +0 -1
  83. package/dist/tasks/codemods/codemods.d.ts +0 -3
  84. package/dist/tasks/codemods/codemods.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -263,13 +263,19 @@ const rangeFromVersions = (currentVersion, target) => {
263
263
  }
264
264
  throw new Error(`Invalid target set: ${target}`);
265
265
  };
266
+ const isValidStringifiedRange = (str) => semver__default.default.validRange(str) !== null;
267
+ const isRangeInstance = (range) => {
268
+ return range instanceof semver__default.default.Range;
269
+ };
266
270
  const index$e = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
267
271
  __proto__: null,
268
272
  Version: types,
269
273
  isLiteralSemVer,
274
+ isRangeInstance,
270
275
  isSemVerReleaseType,
271
276
  isSemverInstance,
272
277
  isValidSemVer,
278
+ isValidStringifiedRange,
273
279
  rangeFactory,
274
280
  rangeFromReleaseType,
275
281
  rangeFromVersions,
@@ -330,7 +336,11 @@ const transformJSON = async (codemodPath, paths, config) => {
330
336
  timeElapsed: "",
331
337
  stats: {}
332
338
  };
333
- const esbuildOptions = { extensions: [".js", ".mjs", ".ts"] };
339
+ const esbuildOptions = {
340
+ extensions: [".js", ".mjs", ".ts"],
341
+ hookIgnoreNodeModules: false,
342
+ hookMatcher: fp.isEqual(codemodPath)
343
+ };
334
344
  const { unregister } = node.register(esbuildOptions);
335
345
  const module2 = require(codemodPath);
336
346
  unregister();
@@ -375,17 +385,31 @@ const index$b = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
375
385
  jsonRunnerFactory
376
386
  }, Symbol.toStringTag, { value: "Module" }));
377
387
  const PROJECT_PACKAGE_JSON = "package.json";
378
- const PROJECT_DEFAULT_ALLOWED_ROOT_PATHS = ["src", "config", "public"];
379
- const PROJECT_DEFAULT_ALLOWED_EXTENSIONS = ["js", "ts", "json"];
380
- const PROJECT_DEFAULT_PATTERNS = ["package.json"];
388
+ const PROJECT_APP_ALLOWED_ROOT_PATHS = ["src", "config", "public"];
389
+ const PROJECT_PLUGIN_ALLOWED_ROOT_PATHS = ["admin", "server"];
390
+ const PROJECT_PLUGIN_ROOT_FILES = ["strapi-admin.js", "strapi-server.js"];
391
+ const PROJECT_CODE_EXTENSIONS = [
392
+ // Source files
393
+ "js",
394
+ "mjs",
395
+ "ts",
396
+ // React files
397
+ "jsx",
398
+ "tsx"
399
+ ];
400
+ const PROJECT_JSON_EXTENSIONS = ["json"];
401
+ const PROJECT_ALLOWED_EXTENSIONS = [...PROJECT_CODE_EXTENSIONS, ...PROJECT_JSON_EXTENSIONS];
381
402
  const SCOPED_STRAPI_PACKAGE_PREFIX = "@strapi/";
382
403
  const STRAPI_DEPENDENCY_NAME = `${SCOPED_STRAPI_PACKAGE_PREFIX}strapi`;
383
404
  const constants$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
384
405
  __proto__: null,
385
- PROJECT_DEFAULT_ALLOWED_EXTENSIONS,
386
- PROJECT_DEFAULT_ALLOWED_ROOT_PATHS,
387
- PROJECT_DEFAULT_PATTERNS,
406
+ PROJECT_ALLOWED_EXTENSIONS,
407
+ PROJECT_APP_ALLOWED_ROOT_PATHS,
408
+ PROJECT_CODE_EXTENSIONS,
409
+ PROJECT_JSON_EXTENSIONS,
388
410
  PROJECT_PACKAGE_JSON,
411
+ PROJECT_PLUGIN_ALLOWED_ROOT_PATHS,
412
+ PROJECT_PLUGIN_ROOT_FILES,
389
413
  SCOPED_STRAPI_PACKAGE_PREFIX,
390
414
  STRAPI_DEPENDENCY_NAME
391
415
  }, Symbol.toStringTag, { value: "Module" }));
@@ -395,12 +419,13 @@ class Project {
395
419
  files;
396
420
  packageJSONPath;
397
421
  packageJSON;
398
- strapiVersion;
399
- constructor(cwd) {
422
+ paths;
423
+ constructor(cwd, config) {
400
424
  if (!fse__default.default.pathExistsSync(cwd)) {
401
425
  throw new Error(`ENOENT: no such file or directory, access '${cwd}'`);
402
426
  }
403
427
  this.cwd = cwd;
428
+ this.paths = config.paths;
404
429
  this.refresh();
405
430
  }
406
431
  getFilesByExtensions(extensions) {
@@ -411,14 +436,13 @@ class Project {
411
436
  }
412
437
  refresh() {
413
438
  this.refreshPackageJSON();
414
- this.refreshStrapiVersion();
415
439
  this.refreshProjectFiles();
416
440
  return this;
417
441
  }
418
- async runCodemods(codemods2, options) {
442
+ async runCodemods(codemods, options) {
419
443
  const runners = this.createProjectCodemodsRunners(options.dry);
420
444
  const reports2 = [];
421
- for (const codemod of codemods2) {
445
+ for (const codemod of codemods) {
422
446
  for (const runner of runners) {
423
447
  if (runner.valid(codemod)) {
424
448
  const report = await runner.run(codemod);
@@ -429,17 +453,20 @@ class Project {
429
453
  return reports2;
430
454
  }
431
455
  createProjectCodemodsRunners(dry = false) {
432
- const jsonFiles = this.getFilesByExtensions([".json"]);
433
- const codeFiles = this.getFilesByExtensions([".js", ".ts", ".mjs"]);
456
+ const jsonExtensions = PROJECT_JSON_EXTENSIONS.map((ext) => `.${ext}`);
457
+ const codeExtensions = PROJECT_CODE_EXTENSIONS.map((ext) => `.${ext}`);
458
+ const jsonFiles = this.getFilesByExtensions(jsonExtensions);
459
+ const codeFiles = this.getFilesByExtensions(codeExtensions);
434
460
  const codeRunner = codeRunnerFactory(codeFiles, {
435
461
  dry,
436
- print: false,
437
- silent: true,
438
- extensions: "js,ts",
462
+ parser: "ts",
439
463
  runInBand: true,
440
- verbose: 0,
441
464
  babel: true,
442
- parser: "ts"
465
+ extensions: PROJECT_CODE_EXTENSIONS.join(","),
466
+ // Don't output any log coming from the runner
467
+ print: false,
468
+ silent: true,
469
+ verbose: 0
443
470
  });
444
471
  const jsonRunner = jsonRunnerFactory(jsonFiles, { dry, cwd: this.cwd });
445
472
  return [codeRunner, jsonRunner];
@@ -456,16 +483,36 @@ class Project {
456
483
  this.packageJSON = JSON.parse(packageJSONBuffer.toString());
457
484
  }
458
485
  refreshProjectFiles() {
459
- const allowedRootPaths = formatGlobCollectionPattern(
460
- PROJECT_DEFAULT_ALLOWED_ROOT_PATHS
461
- );
462
- const allowedExtensions = formatGlobCollectionPattern(
463
- PROJECT_DEFAULT_ALLOWED_EXTENSIONS
464
- );
465
- const projectFilesPattern = `./${allowedRootPaths}/**/*.${allowedExtensions}`;
466
- const patterns = [projectFilesPattern, ...PROJECT_DEFAULT_PATTERNS];
467
486
  const scanner = fileScannerFactory(this.cwd);
468
- this.files = scanner.scan(patterns);
487
+ this.files = scanner.scan(this.paths);
488
+ }
489
+ }
490
+ class AppProject extends Project {
491
+ strapiVersion;
492
+ type = "application";
493
+ /**
494
+ * Returns an array of allowed file paths for a Strapi application
495
+ *
496
+ * The resulting paths include app default files and the root package.json file.
497
+ */
498
+ static get paths() {
499
+ const allowedRootPaths = formatGlobCollectionPattern(PROJECT_APP_ALLOWED_ROOT_PATHS);
500
+ const allowedExtensions = formatGlobCollectionPattern(PROJECT_ALLOWED_EXTENSIONS);
501
+ return [
502
+ // App default files
503
+ `./${allowedRootPaths}/**/*.${allowedExtensions}`,
504
+ // Root package.json file
505
+ PROJECT_PACKAGE_JSON
506
+ ];
507
+ }
508
+ constructor(cwd) {
509
+ super(cwd, { paths: AppProject.paths });
510
+ this.refreshStrapiVersion();
511
+ }
512
+ refresh() {
513
+ super.refresh();
514
+ this.refreshStrapiVersion();
515
+ return this;
469
516
  }
470
517
  refreshStrapiVersion() {
471
518
  this.strapiVersion = // First try to get the strapi version from the package.json dependencies
@@ -512,10 +559,69 @@ const formatGlobCollectionPattern = (collection) => {
512
559
  );
513
560
  return collection.length === 1 ? collection[0] : `{${collection}}`;
514
561
  };
515
- const projectFactory = (cwd) => new Project(cwd);
562
+ class PluginProject extends Project {
563
+ type = "plugin";
564
+ /**
565
+ * Returns an array of allowed file paths for a Strapi plugin
566
+ *
567
+ * The resulting paths include plugin default files, the root package.json file, and plugin-specific files.
568
+ */
569
+ static get paths() {
570
+ const allowedRootPaths = formatGlobCollectionPattern(
571
+ PROJECT_PLUGIN_ALLOWED_ROOT_PATHS
572
+ );
573
+ const allowedExtensions = formatGlobCollectionPattern(PROJECT_ALLOWED_EXTENSIONS);
574
+ return [
575
+ // Plugin default files
576
+ `./${allowedRootPaths}/**/*.${allowedExtensions}`,
577
+ // Root package.json file
578
+ PROJECT_PACKAGE_JSON,
579
+ // Plugin root files
580
+ ...PROJECT_PLUGIN_ROOT_FILES
581
+ ];
582
+ }
583
+ constructor(cwd) {
584
+ super(cwd, { paths: PluginProject.paths });
585
+ }
586
+ }
587
+ const isPlugin = (cwd) => {
588
+ const packageJSONPath = path__default.default.join(cwd, PROJECT_PACKAGE_JSON);
589
+ try {
590
+ fse__default.default.accessSync(packageJSONPath);
591
+ } catch {
592
+ throw new Error(`Could not find a ${PROJECT_PACKAGE_JSON} file in ${cwd}`);
593
+ }
594
+ const packageJSONBuffer = fse__default.default.readFileSync(packageJSONPath);
595
+ const packageJSON = JSON.parse(packageJSONBuffer.toString());
596
+ return packageJSON?.strapi?.kind === "plugin";
597
+ };
598
+ const projectFactory = (cwd) => {
599
+ fse__default.default.accessSync(cwd);
600
+ return isPlugin(cwd) ? new PluginProject(cwd) : new AppProject(cwd);
601
+ };
602
+ const isPluginProject = (project) => {
603
+ return project instanceof PluginProject;
604
+ };
605
+ function assertPluginProject(project) {
606
+ if (!isPluginProject(project)) {
607
+ throw new Error("Project is not a plugin");
608
+ }
609
+ }
610
+ const isApplicationProject = (project) => {
611
+ return project instanceof AppProject;
612
+ };
613
+ function assertAppProject(project) {
614
+ if (!isApplicationProject(project)) {
615
+ throw new Error("Project is not an application");
616
+ }
617
+ }
516
618
  const index$a = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
517
619
  __proto__: null,
620
+ assertAppProject,
621
+ assertPluginProject,
518
622
  constants: constants$3,
623
+ isApplicationProject,
624
+ isPluginProject,
519
625
  projectFactory
520
626
  }, Symbol.toStringTag, { value: "Module" }));
521
627
  class UnexpectedError extends Error {
@@ -541,7 +647,14 @@ const path = (path2) => chalk__default.default.blue(path2);
541
647
  const version = (version2) => {
542
648
  return chalk__default.default.italic.yellow(`v${version2}`);
543
649
  };
544
- const versionRange = (range) => chalk__default.default.italic.yellow(range);
650
+ const codemodUID = (uid) => {
651
+ return chalk__default.default.bold.cyan(uid);
652
+ };
653
+ const projectDetails = (project) => {
654
+ return `Project: TYPE=${projectType(project.type)}; CWD=${path(project.cwd)}; PATHS=${project.paths.map(path)}`;
655
+ };
656
+ const projectType = (type) => chalk__default.default.cyan(type);
657
+ const versionRange = (range) => chalk__default.default.italic.yellow(range.raw);
545
658
  const transform = (transformFilePath) => chalk__default.default.cyan(transformFilePath);
546
659
  const highlight = (arg) => chalk__default.default.bold.underline(arg);
547
660
  const upgradeStep = (text, step) => {
@@ -573,15 +686,41 @@ const reports = (reports2) => {
573
686
  table.push(...rows);
574
687
  return table.toString();
575
688
  };
689
+ const codemodList = (codemods) => {
690
+ const rows = codemods.map((codemod, index2) => {
691
+ const fIndex = chalk__default.default.grey(index2);
692
+ const fVersion = chalk__default.default.magenta(codemod.version);
693
+ const fKind = chalk__default.default.yellow(codemod.kind);
694
+ const fName = chalk__default.default.blue(codemod.format());
695
+ const fUID = codemodUID(codemod.uid);
696
+ return [fIndex, fVersion, fKind, fName, fUID];
697
+ });
698
+ const table = new CliTable3__default.default({
699
+ style: { compact: true },
700
+ head: [
701
+ chalk__default.default.bold.grey("N°"),
702
+ chalk__default.default.bold.magenta("Version"),
703
+ chalk__default.default.bold.yellow("Kind"),
704
+ chalk__default.default.bold.blue("Name"),
705
+ chalk__default.default.bold.cyan("UID")
706
+ ]
707
+ });
708
+ table.push(...rows);
709
+ return table.toString();
710
+ };
576
711
  const durationMs = (elapsedMs) => {
577
712
  const elapsedSeconds = (elapsedMs / ONE_SECOND_MS).toFixed(3);
578
713
  return `${elapsedSeconds}s`;
579
714
  };
580
715
  const index$8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
581
716
  __proto__: null,
717
+ codemodList,
718
+ codemodUID,
582
719
  durationMs,
583
720
  highlight,
584
721
  path,
722
+ projectDetails,
723
+ projectType,
585
724
  reports,
586
725
  transform,
587
726
  upgradeStep,
@@ -604,6 +743,7 @@ const constants$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
604
743
  CODEMOD_JSON_SUFFIX
605
744
  }, Symbol.toStringTag, { value: "Module" }));
606
745
  class Codemod {
746
+ uid;
607
747
  kind;
608
748
  version;
609
749
  baseDirectory;
@@ -615,9 +755,27 @@ class Codemod {
615
755
  this.baseDirectory = options.baseDirectory;
616
756
  this.filename = options.filename;
617
757
  this.path = path__default.default.join(this.baseDirectory, this.version.raw, this.filename);
618
- }
619
- format() {
620
- return this.filename.replace(`.${CODEMOD_CODE_SUFFIX}.${CODEMOD_EXTENSION}`, "").replace(`.${CODEMOD_JSON_SUFFIX}.${CODEMOD_EXTENSION}`, "").replaceAll("-", " ");
758
+ this.uid = this.createUID();
759
+ }
760
+ createUID() {
761
+ const name = this.format({ stripExtension: true, stripKind: true, stripHyphens: false });
762
+ const kind = this.kind;
763
+ const version2 = this.version.raw;
764
+ return `${version2}-${name}-${kind}`;
765
+ }
766
+ format(options) {
767
+ const { stripExtension = true, stripKind = true, stripHyphens = true } = options ?? {};
768
+ let formatted = this.filename;
769
+ if (stripExtension) {
770
+ formatted = formatted.replace(new RegExp(`\\.${CODEMOD_EXTENSION}$`, "i"), "");
771
+ }
772
+ if (stripKind) {
773
+ formatted = formatted.replace(`.${CODEMOD_CODE_SUFFIX}`, "").replace(`.${CODEMOD_JSON_SUFFIX}`, "");
774
+ }
775
+ if (stripHyphens) {
776
+ formatted = formatted.replaceAll("-", " ");
777
+ }
778
+ return formatted;
621
779
  }
622
780
  }
623
781
  const codemodFactory = (options) => new Codemod(options);
@@ -626,6 +784,20 @@ const index$7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
626
784
  codemodFactory,
627
785
  constants: constants$2
628
786
  }, Symbol.toStringTag, { value: "Module" }));
787
+ const INTERNAL_CODEMODS_DIRECTORY = path__default.default.join(
788
+ __dirname,
789
+ // upgrade/dist
790
+ "..",
791
+ // upgrade
792
+ "resources",
793
+ // upgrade/resources
794
+ "codemods"
795
+ // upgrade/resources/codemods
796
+ );
797
+ const constants$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
798
+ __proto__: null,
799
+ INTERNAL_CODEMODS_DIRECTORY
800
+ }, Symbol.toStringTag, { value: "Module" }));
629
801
  class CodemodRepository {
630
802
  groups;
631
803
  versions;
@@ -644,23 +816,48 @@ class CodemodRepository {
644
816
  count(version2) {
645
817
  return this.findByVersion(version2).length;
646
818
  }
647
- countRange(range) {
648
- return this.findByRange(range).length;
649
- }
650
- exists(version2) {
819
+ versionExists(version2) {
651
820
  return version2.raw in this.groups;
652
821
  }
653
- findByRange(range) {
822
+ has(uid) {
823
+ const result = this.find({ uids: [uid] });
824
+ if (result.length !== 1) {
825
+ return false;
826
+ }
827
+ const { codemods } = result[0];
828
+ return codemods.length === 1 && codemods[0].uid === uid;
829
+ }
830
+ find(q) {
654
831
  const entries = Object.entries(this.groups);
655
- return entries.filter(([version2]) => range.test(version2)).map(([version2, codemods2]) => ({
832
+ return entries.filter(maybeFilterByRange).map(([version2, codemods]) => ({
656
833
  version: semVerFactory(version2),
657
- codemods: codemods2
658
- }));
834
+ // Filter by UID if provided in the query
835
+ codemods: codemods.filter(maybeFilterByUIDs)
836
+ })).filter(({ codemods }) => codemods.length > 0);
837
+ function maybeFilterByRange([version2]) {
838
+ if (!isRangeInstance(q.range)) {
839
+ return true;
840
+ }
841
+ return q.range.test(version2);
842
+ }
843
+ function maybeFilterByUIDs(codemod) {
844
+ if (q.uids === void 0) {
845
+ return true;
846
+ }
847
+ return q.uids.includes(codemod.uid);
848
+ }
659
849
  }
660
850
  findByVersion(version2) {
661
851
  const literalVersion = version2.raw;
662
- const codemods2 = this.groups[literalVersion];
663
- return codemods2 ?? [];
852
+ const codemods = this.groups[literalVersion];
853
+ return codemods ?? [];
854
+ }
855
+ findAll() {
856
+ const entries = Object.entries(this.groups);
857
+ return entries.map(([version2, codemods]) => ({
858
+ version: semVerFactory(version2),
859
+ codemods
860
+ }));
664
861
  }
665
862
  refreshAvailableVersions() {
666
863
  this.versions = fse__default.default.readdirSync(this.cwd).filter((filename) => fse__default.default.statSync(path__default.default.join(this.cwd, filename)).isDirectory()).filter((filename) => semver__default.default.valid(filename) !== null).map((version2) => semVerFactory(version2)).sort(semver__default.default.compare);
@@ -691,18 +888,9 @@ const parseCodemodKindFromFilename = (filename) => {
691
888
  assert__default.default(CODEMOD_ALLOWED_SUFFIXES.includes(kind));
692
889
  return kind;
693
890
  };
694
- const codemodRepositoryFactory = (cwd) => new CodemodRepository(cwd);
695
- const INTERNAL_CODEMODS_DIRECTORY = path__default.default.join(
696
- __dirname,
697
- "..",
698
- "..",
699
- "resources",
700
- "codemods"
701
- );
702
- const constants$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
703
- __proto__: null,
704
- INTERNAL_CODEMODS_DIRECTORY
705
- }, Symbol.toStringTag, { value: "Module" }));
891
+ const codemodRepositoryFactory = (cwd = INTERNAL_CODEMODS_DIRECTORY) => {
892
+ return new CodemodRepository(cwd);
893
+ };
706
894
  const index$6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
707
895
  __proto__: null,
708
896
  codemodRepositoryFactory,
@@ -737,34 +925,59 @@ class CodemodRunner {
737
925
  this.isDry = enabled;
738
926
  return this;
739
927
  }
740
- async run(codemodsDirectory) {
928
+ createRepository(codemodsDirectory) {
741
929
  const repository = codemodRepositoryFactory(
742
930
  codemodsDirectory ?? INTERNAL_CODEMODS_DIRECTORY
743
931
  );
744
932
  repository.refresh();
745
- const allVersionedCodemods = repository.findByRange(this.range);
746
- const versionedCodemods = this.selectCodemodsCallback ? await this.selectCodemodsCallback(allVersionedCodemods) : allVersionedCodemods;
747
- const hasCodemodsToRun = versionedCodemods.length > 0;
748
- if (!hasCodemodsToRun) {
749
- this.logger?.debug(`Found no codemods to run for ${versionRange(this.range)}`);
750
- return successReport$1();
933
+ return repository;
934
+ }
935
+ async safeRunAndReport(codemods) {
936
+ if (this.isDry) {
937
+ this.logger?.warn?.(
938
+ "Running the codemods in dry mode. No files will be modified during the process."
939
+ );
751
940
  }
752
- this.logger?.debug(
753
- `Found codemods for ${highlight(
754
- versionedCodemods.length
755
- )} version(s) using ${versionRange(this.range)}`
756
- );
757
- versionedCodemods.forEach(
758
- ({ version: version$1, codemods: codemods22 }) => this.logger?.debug(`- ${version(version$1)} (${codemods22.length})`)
759
- );
760
- const codemods2 = versionedCodemods.map(({ codemods: codemods22 }) => codemods22).flat();
761
941
  try {
762
- const reports$1 = await this.project.runCodemods(codemods2, { dry: this.isDry });
763
- this.logger?.raw(reports(reports$1));
942
+ const reports$1 = await this.project.runCodemods(codemods, { dry: this.isDry });
943
+ this.logger?.raw?.(reports(reports$1));
944
+ if (!this.isDry) {
945
+ const nbAffectedTotal = reports$1.flatMap((report) => report.report.ok).reduce((acc, nb) => acc + nb, 0);
946
+ this.logger?.debug?.(
947
+ `Successfully ran ${highlight(codemods.length)} codemod(s), ${highlight(nbAffectedTotal)} change(s) have been detected`
948
+ );
949
+ }
950
+ return successReport$1();
764
951
  } catch (e) {
765
952
  return erroredReport$1(unknownToError(e));
766
953
  }
767
- return successReport$1();
954
+ }
955
+ async runByUID(uid, codemodsDirectory) {
956
+ const repository = this.createRepository(codemodsDirectory);
957
+ if (!repository.has(uid)) {
958
+ throw new Error(`Unknown codemod UID provided: ${uid}`);
959
+ }
960
+ const codemods = repository.find({ uids: [uid] }).flatMap(({ codemods: codemods2 }) => codemods2);
961
+ return this.safeRunAndReport(codemods);
962
+ }
963
+ async run(codemodsDirectory) {
964
+ const repository = this.createRepository(codemodsDirectory);
965
+ const codemodsInRange = repository.find({ range: this.range });
966
+ const selectedCodemods = this.selectCodemodsCallback ? await this.selectCodemodsCallback(codemodsInRange) : codemodsInRange;
967
+ if (selectedCodemods.length === 0) {
968
+ this.logger?.debug?.(`Found no codemods to run for ${versionRange(this.range)}`);
969
+ return successReport$1();
970
+ }
971
+ const codemods = selectedCodemods.flatMap(({ codemods: codemods2 }) => codemods2);
972
+ const codemodsByVersion = fp.groupBy("version", codemods);
973
+ const fRange = versionRange(this.range);
974
+ this.logger?.debug?.(
975
+ `Found ${highlight(codemods.length)} codemods for ${highlight(fp.size(codemodsByVersion))} version(s) using ${fRange}`
976
+ );
977
+ for (const [version$1, codemods2] of Object.entries(codemodsByVersion)) {
978
+ this.logger?.debug?.(`- ${version(semVerFactory(version$1))} (${codemods2.length})`);
979
+ }
980
+ return this.safeRunAndReport(codemods);
768
981
  }
769
982
  }
770
983
  const codemodRunnerFactory = (project, range) => {
@@ -803,7 +1016,7 @@ class Upgrader {
803
1016
  this.codemodsTarget = semVerFactory(
804
1017
  `${this.target.major}.${this.target.minor}.${this.target.patch}`
805
1018
  );
806
- this.logger?.debug(
1019
+ this.logger?.debug?.(
807
1020
  `The codemods target has been synced with the upgrade target. The codemod runner will now look for ${version(
808
1021
  this.codemodsTarget
809
1022
  )}`
@@ -812,7 +1025,7 @@ class Upgrader {
812
1025
  }
813
1026
  overrideCodemodsTarget(target) {
814
1027
  this.codemodsTarget = target;
815
- this.logger?.debug(
1028
+ this.logger?.debug?.(
816
1029
  `Overriding the codemods target. The codemod runner will now look for ${version(target)}`
817
1030
  );
818
1031
  return this;
@@ -832,38 +1045,40 @@ class Upgrader {
832
1045
  addRequirement(requirement) {
833
1046
  this.requirements.push(requirement);
834
1047
  const fRequired = requirement.isRequired ? "(required)" : "(optional)";
835
- this.logger?.debug(
1048
+ this.logger?.debug?.(
836
1049
  `Added a new requirement to the upgrade: ${highlight(requirement.name)} ${fRequired}`
837
1050
  );
838
1051
  return this;
839
1052
  }
840
1053
  async upgrade() {
841
- this.logger?.info(
1054
+ this.logger?.info?.(
842
1055
  `Upgrading from ${version(this.project.strapiVersion)} to ${version(this.target)}`
843
1056
  );
844
1057
  if (this.isDry) {
845
- this.logger?.warn(
1058
+ this.logger?.warn?.(
846
1059
  "Running the upgrade in dry mode. No files will be modified during the process."
847
1060
  );
848
1061
  }
849
1062
  const range = rangeFromVersions(this.project.strapiVersion, this.target);
850
1063
  const codemodsRange = rangeFromVersions(this.project.strapiVersion, this.codemodsTarget);
851
1064
  const npmVersionsMatches = this.npmPackage?.findVersionsInRange(range) ?? [];
852
- this.logger?.debug(
1065
+ this.logger?.debug?.(
853
1066
  `Found ${highlight(npmVersionsMatches.length)} versions satisfying ${versionRange(range)}`
854
1067
  );
855
1068
  try {
856
- this.logger?.info(upgradeStep("Checking requirement", [1, 4]));
1069
+ this.logger?.info?.(upgradeStep("Checking requirement", [1, 4]));
857
1070
  await this.checkRequirements(this.requirements, {
858
1071
  npmVersionsMatches,
859
1072
  project: this.project,
860
1073
  target: this.target
861
1074
  });
862
- this.logger?.info(upgradeStep("Applying the latest code modifications", [2, 4]));
1075
+ this.logger?.info?.(upgradeStep("Applying the latest code modifications", [2, 4]));
863
1076
  await this.runCodemods(codemodsRange);
864
- this.logger?.info(upgradeStep("Upgrading Strapi dependencies", [3, 4]));
1077
+ this.logger?.debug?.("Refreshing project information...");
1078
+ this.project.refresh();
1079
+ this.logger?.info?.(upgradeStep("Upgrading Strapi dependencies", [3, 4]));
865
1080
  await this.updateDependencies();
866
- this.logger?.info(upgradeStep("Installing dependencies", [4, 4]));
1081
+ this.logger?.info?.(upgradeStep("Installing dependencies", [4, 4]));
867
1082
  await this.installDependencies();
868
1083
  } catch (e) {
869
1084
  return erroredReport(unknownToError(e));
@@ -896,7 +1111,7 @@ class Upgrader {
896
1111
  if (requirement.isRequired) {
897
1112
  throw error;
898
1113
  }
899
- this.logger?.warn(warningMessage);
1114
+ this.logger?.warn?.(warningMessage);
900
1115
  const response = await this.confirmationCallback?.(confirmationMessage);
901
1116
  if (!response) {
902
1117
  throw error;
@@ -907,9 +1122,11 @@ class Upgrader {
907
1122
  const json = createJSONTransformAPI(packageJSON);
908
1123
  const dependencies = json.get("dependencies", {});
909
1124
  const strapiDependencies = this.getScopedStrapiDependencies(dependencies);
910
- this.logger?.debug(`Found ${highlight(strapiDependencies.length)} dependency(ies) to update`);
1125
+ this.logger?.debug?.(
1126
+ `Found ${highlight(strapiDependencies.length)} dependency(ies) to update`
1127
+ );
911
1128
  strapiDependencies.forEach(
912
- (dependency) => this.logger?.debug(`- ${dependency[0]} (${dependency[1]} -> ${this.target})`)
1129
+ (dependency) => this.logger?.debug?.(`- ${dependency[0]} (${dependency[1]} -> ${this.target})`)
913
1130
  );
914
1131
  if (strapiDependencies.length === 0) {
915
1132
  return;
@@ -917,7 +1134,7 @@ class Upgrader {
917
1134
  strapiDependencies.forEach(([name]) => json.set(`dependencies.${name}`, this.target.raw));
918
1135
  const updatedPackageJSON = json.root();
919
1136
  if (this.isDry) {
920
- this.logger?.debug(`Skipping dependencies update (${chalk__default.default.italic("dry mode")})`);
1137
+ this.logger?.debug?.(`Skipping dependencies update (${chalk__default.default.italic("dry mode")})`);
921
1138
  return;
922
1139
  }
923
1140
  await saveJSON(packageJSONPath, updatedPackageJSON);
@@ -937,9 +1154,9 @@ class Upgrader {
937
1154
  async installDependencies() {
938
1155
  const projectPath = this.project.cwd;
939
1156
  const packageManagerName = await utils.packageManager.getPreferred(projectPath);
940
- this.logger?.debug(`Using ${highlight(packageManagerName)} as package manager`);
1157
+ this.logger?.debug?.(`Using ${highlight(packageManagerName)} as package manager`);
941
1158
  if (this.isDry) {
942
- this.logger?.debug(`Skipping dependencies installation (${chalk__default.default.italic("dry mode")}`);
1159
+ this.logger?.debug?.(`Skipping dependencies installation (${chalk__default.default.italic("dry mode")}`);
943
1160
  return;
944
1161
  }
945
1162
  await utils.packageManager.installDependencies(projectPath, packageManagerName, {
@@ -1038,6 +1255,12 @@ const upgrade = async (options) => {
1038
1255
  const { logger, codemodsTarget } = options;
1039
1256
  const cwd = path__default.default.resolve(options.cwd ?? process.cwd());
1040
1257
  const project = projectFactory(cwd);
1258
+ logger.debug(projectDetails(project));
1259
+ if (!isApplicationProject(project)) {
1260
+ throw new Error(
1261
+ `The "${options.target}" upgrade can only be run on a Strapi project; for plugins, please use "codemods".`
1262
+ );
1263
+ }
1041
1264
  const npmPackage = npmPackageFactory(STRAPI_PACKAGE_NAME);
1042
1265
  await npmPackage.refresh();
1043
1266
  const upgrader = upgraderFactory(project, options.target, npmPackage).dry(options.dry ?? false).onConfirm(options.confirm ?? null).setLogger(logger);
@@ -1055,20 +1278,7 @@ const upgrade = async (options) => {
1055
1278
  timer.stop();
1056
1279
  logger.info(`Completed in ${durationMs(timer.elapsedMs)}`);
1057
1280
  };
1058
- const codemods = async (options) => {
1059
- const timer = timerFactory();
1060
- const { logger } = options;
1061
- const cwd = path__default.default.resolve(options.cwd ?? process.cwd());
1062
- const project = projectFactory(cwd);
1063
- const range = getRangeFromTarget(project.strapiVersion, options.target);
1064
- const codemodRunner = codemodRunnerFactory(project, range).dry(options.dry ?? false).onSelectCodemods(options.selectCodemods ?? null).setLogger(logger);
1065
- const executionReport = await codemodRunner.run();
1066
- if (!executionReport.success) {
1067
- throw executionReport.error;
1068
- }
1069
- timer.stop();
1070
- logger.info(`Completed in ${timer.elapsedMs}`);
1071
- };
1281
+ const resolvePath = (cwd) => path__default.default.resolve(cwd ?? process.cwd());
1072
1282
  const getRangeFromTarget = (currentVersion, target) => {
1073
1283
  if (isSemverInstance(target)) {
1074
1284
  return rangeFactory(target);
@@ -1085,9 +1295,60 @@ const getRangeFromTarget = (currentVersion, target) => {
1085
1295
  throw new Error(`Invalid target set: ${target}`);
1086
1296
  }
1087
1297
  };
1298
+ const findRangeFromTarget = (project, target) => {
1299
+ if (isRangeInstance(target)) {
1300
+ return target;
1301
+ }
1302
+ if (isApplicationProject(project)) {
1303
+ return getRangeFromTarget(project.strapiVersion, target);
1304
+ }
1305
+ return rangeFactory("*");
1306
+ };
1307
+ const runCodemods = async (options) => {
1308
+ const timer = timerFactory();
1309
+ const { logger, uid } = options;
1310
+ const cwd = resolvePath(options.cwd);
1311
+ const project = projectFactory(cwd);
1312
+ const range = findRangeFromTarget(project, options.target);
1313
+ logger.debug(projectDetails(project));
1314
+ logger.debug(`Range: set to ${versionRange(range)}`);
1315
+ const codemodRunner = codemodRunnerFactory(project, range).dry(options.dry ?? false).onSelectCodemods(options.selectCodemods ?? null).setLogger(logger);
1316
+ let report;
1317
+ if (uid !== void 0) {
1318
+ logger.debug(`Running a single codemod: ${codemodUID(uid)}`);
1319
+ report = await codemodRunner.runByUID(uid);
1320
+ } else {
1321
+ report = await codemodRunner.run();
1322
+ }
1323
+ if (!report.success) {
1324
+ throw report.error;
1325
+ }
1326
+ timer.stop();
1327
+ logger.info(`Completed in ${timer.elapsedMs}`);
1328
+ };
1329
+ const listCodemods = async (options) => {
1330
+ const { logger, target } = options;
1331
+ const cwd = resolvePath(options.cwd);
1332
+ const project = projectFactory(cwd);
1333
+ const range = findRangeFromTarget(project, target);
1334
+ logger.debug(projectDetails(project));
1335
+ logger.debug(`Range: set to ${versionRange(range)}`);
1336
+ const repo = codemodRepositoryFactory();
1337
+ repo.refresh();
1338
+ const groups = repo.find({ range });
1339
+ const codemods = groups.flatMap((collection) => collection.codemods);
1340
+ logger.debug(`Found ${highlight(codemods.length)} codemods`);
1341
+ if (codemods.length === 0) {
1342
+ logger.info(`Found no codemods matching ${versionRange(range)}`);
1343
+ return;
1344
+ }
1345
+ const fCodemods = codemodList(codemods);
1346
+ logger.raw(fCodemods);
1347
+ };
1088
1348
  const index$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1089
1349
  __proto__: null,
1090
- codemods,
1350
+ listCodemods,
1351
+ runCodemods,
1091
1352
  upgrade
1092
1353
  }, Symbol.toStringTag, { value: "Module" }));
1093
1354
  class Logger {