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

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