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

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 (86) hide show
  1. package/LICENSE +19 -4
  2. package/README.md +1 -1
  3. package/dist/cli.js +1578 -5
  4. package/dist/cli.js.map +1 -1
  5. package/dist/index.js +671 -412
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +667 -409
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/modules/codemod/codemod.d.ts +4 -2
  10. package/dist/modules/codemod/codemod.d.ts.map +1 -1
  11. package/dist/modules/codemod/types.d.ts +8 -1
  12. package/dist/modules/codemod/types.d.ts.map +1 -1
  13. package/dist/modules/codemod-repository/constants.d.ts.map +1 -1
  14. package/dist/modules/codemod-repository/repository.d.ts +5 -5
  15. package/dist/modules/codemod-repository/repository.d.ts.map +1 -1
  16. package/dist/modules/codemod-repository/types.d.ts +7 -3
  17. package/dist/modules/codemod-repository/types.d.ts.map +1 -1
  18. package/dist/modules/codemod-runner/codemod-runner.d.ts +6 -3
  19. package/dist/modules/codemod-runner/codemod-runner.d.ts.map +1 -1
  20. package/dist/modules/codemod-runner/index.d.ts +1 -0
  21. package/dist/modules/codemod-runner/index.d.ts.map +1 -1
  22. package/dist/modules/codemod-runner/types.d.ts +1 -0
  23. package/dist/modules/codemod-runner/types.d.ts.map +1 -1
  24. package/dist/modules/error/utils.d.ts +8 -0
  25. package/dist/modules/error/utils.d.ts.map +1 -1
  26. package/dist/modules/file-scanner/scanner.d.ts.map +1 -1
  27. package/dist/modules/format/formats.d.ts +6 -0
  28. package/dist/modules/format/formats.d.ts.map +1 -1
  29. package/dist/modules/project/constants.d.ts +6 -5
  30. package/dist/modules/project/constants.d.ts.map +1 -1
  31. package/dist/modules/project/project.d.ts +17 -3
  32. package/dist/modules/project/project.d.ts.map +1 -1
  33. package/dist/modules/project/types.d.ts +4 -0
  34. package/dist/modules/project/types.d.ts.map +1 -1
  35. package/dist/modules/project/utils.d.ts +1 -1
  36. package/dist/modules/project/utils.d.ts.map +1 -1
  37. package/dist/modules/report/report.d.ts.map +1 -1
  38. package/dist/modules/runner/json/transform.d.ts.map +1 -1
  39. package/dist/modules/upgrader/types.d.ts +6 -0
  40. package/dist/modules/upgrader/types.d.ts.map +1 -1
  41. package/dist/modules/upgrader/upgrader.d.ts +4 -0
  42. package/dist/modules/upgrader/upgrader.d.ts.map +1 -1
  43. package/dist/modules/version/range.d.ts +2 -0
  44. package/dist/modules/version/range.d.ts.map +1 -1
  45. package/dist/modules/version/types.d.ts +2 -1
  46. package/dist/modules/version/types.d.ts.map +1 -1
  47. package/dist/tasks/codemods/index.d.ts +2 -1
  48. package/dist/tasks/codemods/index.d.ts.map +1 -1
  49. package/dist/tasks/codemods/list-codemods.d.ts +3 -0
  50. package/dist/tasks/codemods/list-codemods.d.ts.map +1 -0
  51. package/dist/tasks/codemods/run-codemods.d.ts +3 -0
  52. package/dist/tasks/codemods/run-codemods.d.ts.map +1 -0
  53. package/dist/tasks/codemods/types.d.ts +9 -3
  54. package/dist/tasks/codemods/types.d.ts.map +1 -1
  55. package/dist/tasks/codemods/utils.d.ts +6 -0
  56. package/dist/tasks/codemods/utils.d.ts.map +1 -0
  57. package/dist/tasks/index.d.ts +1 -1
  58. package/dist/tasks/index.d.ts.map +1 -1
  59. package/dist/tasks/upgrade/prompts/index.d.ts +2 -0
  60. package/dist/tasks/upgrade/prompts/index.d.ts.map +1 -0
  61. package/dist/tasks/upgrade/prompts/latest.d.ts +9 -0
  62. package/dist/tasks/upgrade/prompts/latest.d.ts.map +1 -0
  63. package/dist/tasks/upgrade/requirements/major.d.ts.map +1 -1
  64. package/dist/tasks/upgrade/upgrade.d.ts.map +1 -1
  65. package/package.json +11 -11
  66. package/resources/codemods/5.0.0/comment-out-lifecycle-files.code.ts +63 -0
  67. package/resources/codemods/5.0.0/dependency-upgrade-react-and-react-dom.json.ts +67 -0
  68. package/resources/codemods/5.0.0/dependency-upgrade-styled-components.json.ts +49 -0
  69. package/resources/codemods/5.0.0/deprecate-helper-plugin.code.ts +192 -0
  70. package/resources/codemods/5.0.0/entity-service-document-service.code.ts +437 -0
  71. package/resources/codemods/5.0.0/sqlite3-to-better-sqlite3.json.ts +0 -1
  72. package/resources/codemods/5.0.0/strapi-public-interface.code.ts +126 -0
  73. package/resources/codemods/5.0.0/utils-public-interface.code.ts +320 -0
  74. package/resources/codemods/5.1.0/dependency-better-sqlite3.json.ts +48 -0
  75. package/resources/utils/change-import.ts +118 -0
  76. package/resources/utils/replace-jsx.ts +49 -0
  77. package/dist/_chunks/codemod-runner-mXNzVpHm.js +0 -798
  78. package/dist/_chunks/codemod-runner-mXNzVpHm.js.map +0 -1
  79. package/dist/_chunks/codemods-S4mNX9Qg.js +0 -105
  80. package/dist/_chunks/codemods-S4mNX9Qg.js.map +0 -1
  81. package/dist/_chunks/index-Pt-TU9MN.js +0 -103
  82. package/dist/_chunks/index-Pt-TU9MN.js.map +0 -1
  83. package/dist/_chunks/upgrade-aWNYibWB.js +0 -361
  84. package/dist/_chunks/upgrade-aWNYibWB.js.map +0 -1
  85. package/dist/tasks/codemods/codemods.d.ts +0 -3
  86. package/dist/tasks/codemods/codemods.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,138 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const path$1 = require("node:path");
4
- const simpleGit = require("simple-git");
4
+ const CliTable3 = require("cli-table3");
5
5
  const chalk = require("chalk");
6
+ const assert = require("node:assert");
6
7
  const semver = require("semver");
7
- const utils = require("@strapi/utils");
8
- const fp = require("lodash/fp");
9
8
  const fse = require("fs-extra");
10
- const assert = require("node:assert");
11
- const glob = require("glob");
9
+ const fastglob = require("fast-glob");
12
10
  const Runner = require("jscodeshift/src/Runner");
11
+ const fp = require("lodash/fp");
13
12
  const node = require("esbuild-register/dist/node");
14
- const CliTable3 = require("cli-table3");
13
+ const utils = require("@strapi/utils");
14
+ const simpleGit = require("simple-git");
15
15
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
16
16
  const path__default = /* @__PURE__ */ _interopDefault(path$1);
17
- const simpleGit__default = /* @__PURE__ */ _interopDefault(simpleGit);
17
+ const CliTable3__default = /* @__PURE__ */ _interopDefault(CliTable3);
18
18
  const chalk__default = /* @__PURE__ */ _interopDefault(chalk);
19
+ const assert__default = /* @__PURE__ */ _interopDefault(assert);
19
20
  const semver__default = /* @__PURE__ */ _interopDefault(semver);
20
21
  const fse__default = /* @__PURE__ */ _interopDefault(fse);
21
- const assert__default = /* @__PURE__ */ _interopDefault(assert);
22
- const CliTable3__default = /* @__PURE__ */ _interopDefault(CliTable3);
23
- class Requirement {
24
- isRequired;
25
- name;
26
- testCallback;
27
- children;
28
- constructor(name, testCallback, isRequired) {
29
- this.name = name;
30
- this.testCallback = testCallback;
31
- this.isRequired = isRequired ?? true;
32
- this.children = [];
33
- }
34
- setChildren(children) {
35
- this.children = children;
36
- return this;
37
- }
38
- addChild(child) {
39
- this.children.push(child);
40
- return this;
41
- }
42
- asOptional() {
43
- const newInstance = requirementFactory(this.name, this.testCallback, false);
44
- newInstance.setChildren(this.children);
45
- return newInstance;
46
- }
47
- asRequired() {
48
- const newInstance = requirementFactory(this.name, this.testCallback, true);
49
- newInstance.setChildren(this.children);
50
- return newInstance;
51
- }
52
- async test(context) {
53
- try {
54
- await this.testCallback?.(context);
55
- return ok();
56
- } catch (e) {
57
- if (e instanceof Error) {
58
- return errored(e);
59
- }
60
- if (typeof e === "string") {
61
- return errored(new Error(e));
62
- }
63
- return errored(new Error("Unknown error"));
64
- }
65
- }
66
- }
67
- const ok = () => ({ pass: true, error: null });
68
- const errored = (error) => ({ pass: false, error });
69
- const requirementFactory = (name, testCallback, isRequired) => new Requirement(name, testCallback, isRequired);
70
- const index$g = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
71
- __proto__: null,
72
- requirementFactory
73
- }, Symbol.toStringTag, { value: "Module" }));
74
- const REQUIRE_AVAILABLE_NEXT_MAJOR = requirementFactory(
75
- "REQUIRE_AVAILABLE_NEXT_MAJOR",
76
- (context) => {
77
- const { project, target } = context;
78
- const currentMajor = project.strapiVersion.major;
79
- const targetedMajor = target.major;
80
- if (targetedMajor === currentMajor) {
81
- throw new Error(`You're already on the latest major version (v${currentMajor})`);
82
- }
83
- }
84
- );
85
- const REQUIRE_LATEST_FOR_CURRENT_MAJOR = requirementFactory(
86
- "REQUIRE_LATEST_FOR_CURRENT_MAJOR",
87
- (context) => {
88
- const { project, target, npmVersionsMatches } = context;
89
- if (npmVersionsMatches.length !== 1) {
90
- const invalidVersions = npmVersionsMatches.slice(0, -1);
91
- const invalidVersionsAsSemVer = invalidVersions.map((v) => v.version);
92
- const nbInvalidVersions = npmVersionsMatches.length;
93
- const currentMajor = project.strapiVersion.major;
94
- throw new Error(
95
- `Doing a major upgrade requires to be on the latest v${currentMajor} version, but found ${nbInvalidVersions} versions between the current one and ${target}: ${invalidVersionsAsSemVer}`
96
- );
97
- }
98
- }
99
- );
100
- const REQUIRE_GIT_CLEAN_REPOSITORY = requirementFactory(
101
- "REQUIRE_GIT_CLEAN_REPOSITORY",
102
- async (context) => {
103
- const git = simpleGit__default.default({ baseDir: context.project.cwd });
104
- const status = await git.status();
105
- if (!status.isClean()) {
106
- throw new Error(
107
- "Repository is not clean. Please commit or stash any changes before upgrading"
108
- );
109
- }
110
- }
111
- );
112
- const REQUIRE_GIT_REPOSITORY = requirementFactory(
113
- "REQUIRE_GIT_REPOSITORY",
114
- async (context) => {
115
- const git = simpleGit__default.default({ baseDir: context.project.cwd });
116
- const isRepo = await git.checkIsRepo();
117
- if (!isRepo) {
118
- throw new Error("Not a git repository (or any of the parent directories)");
119
- }
120
- }
121
- ).addChild(REQUIRE_GIT_CLEAN_REPOSITORY.asOptional());
122
- const REQUIRE_GIT_INSTALLED = requirementFactory(
123
- "REQUIRE_GIT_INSTALLED",
124
- async (context) => {
125
- const git = simpleGit__default.default({ baseDir: context.project.cwd });
126
- try {
127
- await git.version();
128
- } catch {
129
- throw new Error("Git is not installed");
130
- }
131
- }
132
- ).addChild(REQUIRE_GIT_REPOSITORY.asOptional());
133
- const REQUIRE_GIT = requirementFactory("REQUIRE_GIT", null).addChild(
134
- REQUIRE_GIT_INSTALLED.asOptional()
135
- );
22
+ const fastglob__default = /* @__PURE__ */ _interopDefault(fastglob);
23
+ const simpleGit__default = /* @__PURE__ */ _interopDefault(simpleGit);
136
24
  class Timer {
137
25
  interval;
138
26
  constructor() {
@@ -163,55 +51,101 @@ const constants$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
163
51
  __proto__: null,
164
52
  ONE_SECOND_MS
165
53
  }, Symbol.toStringTag, { value: "Module" }));
166
- const index$f = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
54
+ const index$g = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
167
55
  __proto__: null,
168
56
  constants: constants$4,
169
57
  timerFactory
170
58
  }, Symbol.toStringTag, { value: "Module" }));
171
- class JSONTransformAPI {
172
- json;
173
- constructor(json) {
174
- this.json = fp.cloneDeep(json);
175
- }
176
- get(path2, defaultValue) {
177
- if (!path2) {
178
- return this.root();
179
- }
180
- return fp.cloneDeep(fp.get(path2, this.json) ?? defaultValue);
181
- }
182
- has(path2) {
183
- return fp.has(path2, this.json);
184
- }
185
- merge(other) {
186
- this.json = fp.merge(other, this.json);
187
- return this;
188
- }
189
- root() {
190
- return fp.cloneDeep(this.json);
191
- }
192
- set(path2, value) {
193
- this.json = fp.set(path2, value, this.json);
194
- return this;
195
- }
196
- remove(path2) {
197
- this.json = fp.omit(path2, this.json);
198
- return this;
199
- }
200
- }
201
- const createJSONTransformAPI = (object) => new JSONTransformAPI(object);
202
- const readJSON = async (path2) => {
203
- const buffer = await fse__default.default.readFile(path2);
204
- return JSON.parse(buffer.toString());
59
+ const path = (path2) => chalk__default.default.blue(path2);
60
+ const version = (version2) => {
61
+ return chalk__default.default.italic.yellow(`v${version2}`);
205
62
  };
206
- const saveJSON = async (path2, json) => {
207
- const jsonAsString = `${JSON.stringify(json, null, 2)}
208
- `;
209
- await fse__default.default.writeFile(path2, jsonAsString);
63
+ const codemodUID = (uid) => {
64
+ return chalk__default.default.bold.cyan(uid);
65
+ };
66
+ const projectDetails = (project) => {
67
+ return `Project: TYPE=${projectType(project.type)}; CWD=${path(project.cwd)}; PATHS=${project.paths.map(path)}`;
68
+ };
69
+ const projectType = (type) => chalk__default.default.cyan(type);
70
+ const versionRange = (range) => chalk__default.default.italic.yellow(range.raw);
71
+ const transform = (transformFilePath) => chalk__default.default.cyan(transformFilePath);
72
+ const highlight = (arg) => chalk__default.default.bold.underline(arg);
73
+ const upgradeStep = (text, step) => {
74
+ return chalk__default.default.bold(`(${step[0]}/${step[1]}) ${text}...`);
210
75
  };
76
+ const reports = (reports2) => {
77
+ const rows = reports2.map(({ codemod, report }, i) => {
78
+ const fIndex = chalk__default.default.grey(i);
79
+ const fVersion = chalk__default.default.magenta(codemod.version);
80
+ const fKind = chalk__default.default.yellow(codemod.kind);
81
+ const fFormattedTransformPath = chalk__default.default.cyan(codemod.format());
82
+ const fTimeElapsed = i === 0 ? `${report.timeElapsed}s ${chalk__default.default.dim.italic("(cold start)")}` : `${report.timeElapsed}s`;
83
+ const fAffected = report.ok > 0 ? chalk__default.default.green(report.ok) : chalk__default.default.grey(0);
84
+ const fUnchanged = report.ok === 0 ? chalk__default.default.red(report.nochange) : chalk__default.default.grey(report.nochange);
85
+ return [fIndex, fVersion, fKind, fFormattedTransformPath, fAffected, fUnchanged, fTimeElapsed];
86
+ });
87
+ const table = new CliTable3__default.default({
88
+ style: { compact: true },
89
+ head: [
90
+ chalk__default.default.bold.grey("N°"),
91
+ chalk__default.default.bold.magenta("Version"),
92
+ chalk__default.default.bold.yellow("Kind"),
93
+ chalk__default.default.bold.cyan("Name"),
94
+ chalk__default.default.bold.green("Affected"),
95
+ chalk__default.default.bold.red("Unchanged"),
96
+ chalk__default.default.bold.blue("Duration")
97
+ ]
98
+ });
99
+ table.push(...rows);
100
+ return table.toString();
101
+ };
102
+ const codemodList = (codemods) => {
103
+ const rows = codemods.map((codemod, index2) => {
104
+ const fIndex = chalk__default.default.grey(index2);
105
+ const fVersion = chalk__default.default.magenta(codemod.version);
106
+ const fKind = chalk__default.default.yellow(codemod.kind);
107
+ const fName = chalk__default.default.blue(codemod.format());
108
+ const fUID = codemodUID(codemod.uid);
109
+ return [fIndex, fVersion, fKind, fName, fUID];
110
+ });
111
+ const table = new CliTable3__default.default({
112
+ style: { compact: true },
113
+ head: [
114
+ chalk__default.default.bold.grey("N°"),
115
+ chalk__default.default.bold.magenta("Version"),
116
+ chalk__default.default.bold.yellow("Kind"),
117
+ chalk__default.default.bold.blue("Name"),
118
+ chalk__default.default.bold.cyan("UID")
119
+ ]
120
+ });
121
+ table.push(...rows);
122
+ return table.toString();
123
+ };
124
+ const durationMs = (elapsedMs) => {
125
+ const elapsedSeconds = (elapsedMs / ONE_SECOND_MS).toFixed(3);
126
+ return `${elapsedSeconds}s`;
127
+ };
128
+ const index$f = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
129
+ __proto__: null,
130
+ codemodList,
131
+ codemodUID,
132
+ durationMs,
133
+ highlight,
134
+ path,
135
+ projectDetails,
136
+ projectType,
137
+ reports,
138
+ transform,
139
+ upgradeStep,
140
+ version,
141
+ versionRange
142
+ }, Symbol.toStringTag, { value: "Module" }));
143
+ const NPM_REGISTRY_URL = "https://registry.npmjs.org";
211
144
  var ReleaseType = /* @__PURE__ */ ((ReleaseType2) => {
212
145
  ReleaseType2["Major"] = "major";
213
146
  ReleaseType2["Minor"] = "minor";
214
147
  ReleaseType2["Patch"] = "patch";
148
+ ReleaseType2["Latest"] = "latest";
215
149
  return ReleaseType2;
216
150
  })(ReleaseType || {});
217
151
  const types = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
@@ -237,17 +171,20 @@ const rangeFactory = (range) => {
237
171
  };
238
172
  const rangeFromReleaseType = (current, identifier) => {
239
173
  switch (identifier) {
240
- case ReleaseType.Major: {
241
- const nextMajor = semver__default.default.inc(current, "major");
242
- return rangeFactory(`>${current.raw} <=${nextMajor}`);
174
+ case ReleaseType.Latest: {
175
+ return rangeFactory(`>${current.raw}`);
243
176
  }
244
- case ReleaseType.Patch: {
245
- const minor = semver__default.default.inc(current, "minor");
246
- return rangeFactory(`>${current.raw} <${minor}`);
177
+ case ReleaseType.Major: {
178
+ const nextMajor = semVerFactory(current.raw).inc("major");
179
+ return rangeFactory(`>${current.raw} <=${nextMajor.major}`);
247
180
  }
248
181
  case ReleaseType.Minor: {
249
- const major = semver__default.default.inc(current, "major");
250
- return rangeFactory(`>${current.raw} <${major}`);
182
+ const nextMajor = semVerFactory(current.raw).inc("major");
183
+ return rangeFactory(`>${current.raw} <${nextMajor.raw}`);
184
+ }
185
+ case ReleaseType.Patch: {
186
+ const nextMinor = semVerFactory(current.raw).inc("minor");
187
+ return rangeFactory(`>${current.raw} <${nextMinor.raw}`);
251
188
  }
252
189
  default: {
253
190
  throw new Error("Not implemented");
@@ -263,25 +200,75 @@ const rangeFromVersions = (currentVersion, target) => {
263
200
  }
264
201
  throw new Error(`Invalid target set: ${target}`);
265
202
  };
203
+ const isValidStringifiedRange = (str) => semver__default.default.validRange(str) !== null;
204
+ const isRangeInstance = (range) => {
205
+ return range instanceof semver__default.default.Range;
206
+ };
266
207
  const index$e = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
267
208
  __proto__: null,
268
209
  Version: types,
269
210
  isLiteralSemVer,
211
+ isRangeInstance,
270
212
  isSemVerReleaseType,
271
213
  isSemverInstance,
272
214
  isValidSemVer,
215
+ isValidStringifiedRange,
273
216
  rangeFactory,
274
217
  rangeFromReleaseType,
275
218
  rangeFromVersions,
276
219
  semVerFactory
277
220
  }, Symbol.toStringTag, { value: "Module" }));
221
+ class Package {
222
+ name;
223
+ packageURL;
224
+ npmPackage;
225
+ constructor(name) {
226
+ this.name = name;
227
+ this.packageURL = `${NPM_REGISTRY_URL}/${name}`;
228
+ this.npmPackage = null;
229
+ }
230
+ get isLoaded() {
231
+ return this.npmPackage !== null;
232
+ }
233
+ assertPackageIsLoaded(npmPackage) {
234
+ assert__default.default(this.isLoaded, "The package is not loaded yet");
235
+ }
236
+ getVersionsDict() {
237
+ this.assertPackageIsLoaded(this.npmPackage);
238
+ return this.npmPackage.versions;
239
+ }
240
+ getVersionsAsList() {
241
+ this.assertPackageIsLoaded(this.npmPackage);
242
+ return Object.values(this.npmPackage.versions);
243
+ }
244
+ findVersionsInRange(range) {
245
+ const versions = this.getVersionsAsList();
246
+ return versions.filter((v) => range.test(v.version)).filter((v) => isLiteralSemVer(v.version)).sort((v1, v2) => semver__default.default.compare(v1.version, v2.version));
247
+ }
248
+ findVersion(version2) {
249
+ const versions = this.getVersionsAsList();
250
+ return versions.find((npmVersion) => semver__default.default.eq(npmVersion.version, version2));
251
+ }
252
+ async refresh() {
253
+ const response = await fetch(this.packageURL);
254
+ assert__default.default(response.ok, `Request failed for ${this.packageURL}`);
255
+ this.npmPackage = await response.json();
256
+ return this;
257
+ }
258
+ versionExists(version2) {
259
+ return this.findVersion(version2) !== void 0;
260
+ }
261
+ }
262
+ const npmPackageFactory = (name) => new Package(name);
278
263
  class FileScanner {
279
264
  cwd;
280
265
  constructor(cwd) {
281
266
  this.cwd = cwd;
282
267
  }
283
268
  scan(patterns) {
284
- const filenames = glob.glob.sync(patterns, { cwd: this.cwd });
269
+ const filenames = fastglob__default.default.sync(patterns, {
270
+ cwd: this.cwd
271
+ });
285
272
  return filenames.map((filename) => path__default.default.join(this.cwd, filename));
286
273
  }
287
274
  }
@@ -319,6 +306,46 @@ const index$c = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
319
306
  __proto__: null,
320
307
  codeRunnerFactory
321
308
  }, Symbol.toStringTag, { value: "Module" }));
309
+ class JSONTransformAPI {
310
+ json;
311
+ constructor(json) {
312
+ this.json = fp.cloneDeep(json);
313
+ }
314
+ get(path2, defaultValue) {
315
+ if (!path2) {
316
+ return this.root();
317
+ }
318
+ return fp.cloneDeep(fp.get(path2, this.json) ?? defaultValue);
319
+ }
320
+ has(path2) {
321
+ return fp.has(path2, this.json);
322
+ }
323
+ merge(other) {
324
+ this.json = fp.merge(other, this.json);
325
+ return this;
326
+ }
327
+ root() {
328
+ return fp.cloneDeep(this.json);
329
+ }
330
+ set(path2, value) {
331
+ this.json = fp.set(path2, value, this.json);
332
+ return this;
333
+ }
334
+ remove(path2) {
335
+ this.json = fp.omit(path2, this.json);
336
+ return this;
337
+ }
338
+ }
339
+ const createJSONTransformAPI = (object) => new JSONTransformAPI(object);
340
+ const readJSON = async (path2) => {
341
+ const buffer = await fse__default.default.readFile(path2);
342
+ return JSON.parse(buffer.toString());
343
+ };
344
+ const saveJSON = async (path2, json) => {
345
+ const jsonAsString = `${JSON.stringify(json, null, 2)}
346
+ `;
347
+ await fse__default.default.writeFile(path2, jsonAsString);
348
+ };
322
349
  const transformJSON = async (codemodPath, paths, config) => {
323
350
  const { dry } = config;
324
351
  const startTime = process.hrtime();
@@ -330,7 +357,11 @@ const transformJSON = async (codemodPath, paths, config) => {
330
357
  timeElapsed: "",
331
358
  stats: {}
332
359
  };
333
- const esbuildOptions = { extensions: [".js", ".mjs", ".ts"] };
360
+ const esbuildOptions = {
361
+ extensions: [".js", ".mjs", ".ts"],
362
+ hookIgnoreNodeModules: false,
363
+ hookMatcher: fp.isEqual(codemodPath)
364
+ };
334
365
  const { unregister } = node.register(esbuildOptions);
335
366
  const module2 = require(codemodPath);
336
367
  unregister();
@@ -375,8 +406,10 @@ const index$b = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
375
406
  jsonRunnerFactory
376
407
  }, Symbol.toStringTag, { value: "Module" }));
377
408
  const PROJECT_PACKAGE_JSON = "package.json";
378
- const PROJECT_DEFAULT_ALLOWED_ROOT_PATHS = ["src", "config", "public"];
379
- const PROJECT_DEFAULT_CODE_EXTENSIONS = [
409
+ const PROJECT_APP_ALLOWED_ROOT_PATHS = ["src", "config", "public"];
410
+ const PROJECT_PLUGIN_ALLOWED_ROOT_PATHS = ["admin", "server"];
411
+ const PROJECT_PLUGIN_ROOT_FILES = ["strapi-admin.js", "strapi-server.js"];
412
+ const PROJECT_CODE_EXTENSIONS = [
380
413
  // Source files
381
414
  "js",
382
415
  "mjs",
@@ -385,22 +418,19 @@ const PROJECT_DEFAULT_CODE_EXTENSIONS = [
385
418
  "jsx",
386
419
  "tsx"
387
420
  ];
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"];
421
+ const PROJECT_JSON_EXTENSIONS = ["json"];
422
+ const PROJECT_ALLOWED_EXTENSIONS = [...PROJECT_CODE_EXTENSIONS, ...PROJECT_JSON_EXTENSIONS];
394
423
  const SCOPED_STRAPI_PACKAGE_PREFIX = "@strapi/";
395
424
  const STRAPI_DEPENDENCY_NAME = `${SCOPED_STRAPI_PACKAGE_PREFIX}strapi`;
396
425
  const constants$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
397
426
  __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,
427
+ PROJECT_ALLOWED_EXTENSIONS,
428
+ PROJECT_APP_ALLOWED_ROOT_PATHS,
429
+ PROJECT_CODE_EXTENSIONS,
430
+ PROJECT_JSON_EXTENSIONS,
403
431
  PROJECT_PACKAGE_JSON,
432
+ PROJECT_PLUGIN_ALLOWED_ROOT_PATHS,
433
+ PROJECT_PLUGIN_ROOT_FILES,
404
434
  SCOPED_STRAPI_PACKAGE_PREFIX,
405
435
  STRAPI_DEPENDENCY_NAME
406
436
  }, Symbol.toStringTag, { value: "Module" }));
@@ -410,11 +440,13 @@ class Project {
410
440
  files;
411
441
  packageJSONPath;
412
442
  packageJSON;
413
- constructor(cwd) {
443
+ paths;
444
+ constructor(cwd, config) {
414
445
  if (!fse__default.default.pathExistsSync(cwd)) {
415
446
  throw new Error(`ENOENT: no such file or directory, access '${cwd}'`);
416
447
  }
417
448
  this.cwd = cwd;
449
+ this.paths = config.paths;
418
450
  this.refresh();
419
451
  }
420
452
  getFilesByExtensions(extensions) {
@@ -428,10 +460,10 @@ class Project {
428
460
  this.refreshProjectFiles();
429
461
  return this;
430
462
  }
431
- async runCodemods(codemods2, options) {
463
+ async runCodemods(codemods, options) {
432
464
  const runners = this.createProjectCodemodsRunners(options.dry);
433
465
  const reports2 = [];
434
- for (const codemod of codemods2) {
466
+ for (const codemod of codemods) {
435
467
  for (const runner of runners) {
436
468
  if (runner.valid(codemod)) {
437
469
  const report = await runner.run(codemod);
@@ -442,12 +474,8 @@ class Project {
442
474
  return reports2;
443
475
  }
444
476
  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
- );
477
+ const jsonExtensions = PROJECT_JSON_EXTENSIONS.map((ext) => `.${ext}`);
478
+ const codeExtensions = PROJECT_CODE_EXTENSIONS.map((ext) => `.${ext}`);
451
479
  const jsonFiles = this.getFilesByExtensions(jsonExtensions);
452
480
  const codeFiles = this.getFilesByExtensions(codeExtensions);
453
481
  const codeRunner = codeRunnerFactory(codeFiles, {
@@ -455,7 +483,7 @@ class Project {
455
483
  parser: "ts",
456
484
  runInBand: true,
457
485
  babel: true,
458
- extensions: PROJECT_DEFAULT_CODE_EXTENSIONS.join(","),
486
+ extensions: PROJECT_CODE_EXTENSIONS.join(","),
459
487
  // Don't output any log coming from the runner
460
488
  print: false,
461
489
  silent: true,
@@ -476,23 +504,32 @@ class Project {
476
504
  this.packageJSON = JSON.parse(packageJSONBuffer.toString());
477
505
  }
478
506
  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
507
  const scanner = fileScannerFactory(this.cwd);
488
- this.files = scanner.scan(patterns);
508
+ this.files = scanner.scan(this.paths);
489
509
  }
490
510
  }
491
511
  class AppProject extends Project {
492
512
  strapiVersion;
493
- type = "app";
513
+ type = "application";
514
+ /**
515
+ * Returns an array of allowed file paths for a Strapi application
516
+ *
517
+ * The resulting paths include app default files and the root package.json file.
518
+ */
519
+ static get paths() {
520
+ const allowedRootPaths = formatGlobCollectionPattern(PROJECT_APP_ALLOWED_ROOT_PATHS);
521
+ const allowedExtensions = formatGlobCollectionPattern(PROJECT_ALLOWED_EXTENSIONS);
522
+ return [
523
+ // App default files
524
+ `./${allowedRootPaths}/**/*.${allowedExtensions}`,
525
+ `!./**/node_modules/**/*`,
526
+ `!./**/dist/**/*`,
527
+ // Root package.json file
528
+ PROJECT_PACKAGE_JSON
529
+ ];
530
+ }
494
531
  constructor(cwd) {
495
- super(cwd);
532
+ super(cwd, { paths: AppProject.paths });
496
533
  this.refreshStrapiVersion();
497
534
  }
498
535
  refresh() {
@@ -547,6 +584,30 @@ const formatGlobCollectionPattern = (collection) => {
547
584
  };
548
585
  class PluginProject extends Project {
549
586
  type = "plugin";
587
+ /**
588
+ * Returns an array of allowed file paths for a Strapi plugin
589
+ *
590
+ * The resulting paths include plugin default files, the root package.json file, and plugin-specific files.
591
+ */
592
+ static get paths() {
593
+ const allowedRootPaths = formatGlobCollectionPattern(
594
+ PROJECT_PLUGIN_ALLOWED_ROOT_PATHS
595
+ );
596
+ const allowedExtensions = formatGlobCollectionPattern(PROJECT_ALLOWED_EXTENSIONS);
597
+ return [
598
+ // Plugin default files
599
+ `./${allowedRootPaths}/**/*.${allowedExtensions}`,
600
+ `!./**/node_modules/**/*`,
601
+ `!./**/dist/**/*`,
602
+ // Root package.json file
603
+ PROJECT_PACKAGE_JSON,
604
+ // Plugin root files
605
+ ...PROJECT_PLUGIN_ROOT_FILES
606
+ ];
607
+ }
608
+ constructor(cwd) {
609
+ super(cwd, { paths: PluginProject.paths });
610
+ }
550
611
  }
551
612
  const isPlugin = (cwd) => {
552
613
  const packageJSONPath = path__default.default.join(cwd, PROJECT_PACKAGE_JSON);
@@ -561,10 +622,7 @@ const isPlugin = (cwd) => {
561
622
  };
562
623
  const projectFactory = (cwd) => {
563
624
  fse__default.default.accessSync(cwd);
564
- if (isPlugin(cwd)) {
565
- return new PluginProject(cwd);
566
- }
567
- return new AppProject(cwd);
625
+ return isPlugin(cwd) ? new PluginProject(cwd) : new AppProject(cwd);
568
626
  };
569
627
  const isPluginProject = (project) => {
570
628
  return project instanceof PluginProject;
@@ -574,12 +632,12 @@ function assertPluginProject(project) {
574
632
  throw new Error("Project is not a plugin");
575
633
  }
576
634
  }
577
- const isAppProject = (project) => {
635
+ const isApplicationProject = (project) => {
578
636
  return project instanceof AppProject;
579
637
  };
580
638
  function assertAppProject(project) {
581
- if (!isAppProject(project)) {
582
- throw new Error("Project is not an app");
639
+ if (!isApplicationProject(project)) {
640
+ throw new Error("Project is not an application");
583
641
  }
584
642
  }
585
643
  const index$a = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
@@ -587,7 +645,7 @@ const index$a = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
587
645
  assertAppProject,
588
646
  assertPluginProject,
589
647
  constants: constants$3,
590
- isAppProject,
648
+ isApplicationProject,
591
649
  isPluginProject,
592
650
  projectFactory
593
651
  }, Symbol.toStringTag, { value: "Module" }));
@@ -596,70 +654,33 @@ class UnexpectedError extends Error {
596
654
  super("Unexpected Error");
597
655
  }
598
656
  }
599
- const unknownToError = (e) => {
600
- if (e instanceof Error) {
601
- return e;
602
- }
603
- if (typeof e === "string") {
604
- return new Error(e);
605
- }
606
- return new UnexpectedError();
607
- };
608
- const index$9 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
609
- __proto__: null,
610
- UnexpectedError,
611
- unknownToError
612
- }, Symbol.toStringTag, { value: "Module" }));
613
- const path = (path2) => chalk__default.default.blue(path2);
614
- const version = (version2) => {
615
- return chalk__default.default.italic.yellow(`v${version2}`);
616
- };
617
- const versionRange = (range) => chalk__default.default.italic.yellow(range);
618
- const transform = (transformFilePath) => chalk__default.default.cyan(transformFilePath);
619
- const highlight = (arg) => chalk__default.default.bold.underline(arg);
620
- const upgradeStep = (text, step) => {
621
- return chalk__default.default.bold(`(${step[0]}/${step[1]}) ${text}...`);
622
- };
623
- const reports = (reports2) => {
624
- const rows = reports2.map(({ codemod, report }, i) => {
625
- const fIndex = chalk__default.default.grey(i);
626
- const fVersion = chalk__default.default.magenta(codemod.version);
627
- const fKind = chalk__default.default.yellow(codemod.kind);
628
- const fFormattedTransformPath = chalk__default.default.cyan(codemod.format());
629
- const fTimeElapsed = i === 0 ? `${report.timeElapsed}s ${chalk__default.default.dim.italic("(cold start)")}` : `${report.timeElapsed}s`;
630
- const fAffected = report.ok > 0 ? chalk__default.default.green(report.ok) : chalk__default.default.grey(0);
631
- const fUnchanged = report.ok === 0 ? chalk__default.default.red(report.nochange) : chalk__default.default.grey(report.nochange);
632
- return [fIndex, fVersion, fKind, fFormattedTransformPath, fAffected, fUnchanged, fTimeElapsed];
633
- });
634
- const table = new CliTable3__default.default({
635
- style: { compact: true },
636
- head: [
637
- chalk__default.default.bold.grey("N°"),
638
- chalk__default.default.bold.magenta("Version"),
639
- chalk__default.default.bold.yellow("Kind"),
640
- chalk__default.default.bold.cyan("Name"),
641
- chalk__default.default.bold.green("Affected"),
642
- chalk__default.default.bold.red("Unchanged"),
643
- chalk__default.default.bold.blue("Duration")
644
- ]
645
- });
646
- table.push(...rows);
647
- return table.toString();
648
- };
649
- const durationMs = (elapsedMs) => {
650
- const elapsedSeconds = (elapsedMs / ONE_SECOND_MS).toFixed(3);
651
- return `${elapsedSeconds}s`;
657
+ class NPMCandidateNotFoundError extends Error {
658
+ target;
659
+ constructor(target, message = `Couldn't find a valid NPM candidate for "${target}"`) {
660
+ super(message);
661
+ this.target = target;
662
+ }
663
+ }
664
+ class AbortedError extends Error {
665
+ constructor(message = "Upgrade aborted") {
666
+ super(message);
667
+ }
668
+ }
669
+ const unknownToError = (e) => {
670
+ if (e instanceof Error) {
671
+ return e;
672
+ }
673
+ if (typeof e === "string") {
674
+ return new Error(e);
675
+ }
676
+ return new UnexpectedError();
652
677
  };
653
- const index$8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
678
+ const index$9 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
654
679
  __proto__: null,
655
- durationMs,
656
- highlight,
657
- path,
658
- reports,
659
- transform,
660
- upgradeStep,
661
- version,
662
- versionRange
680
+ AbortedError,
681
+ NPMCandidateNotFoundError,
682
+ UnexpectedError,
683
+ unknownToError
663
684
  }, Symbol.toStringTag, { value: "Module" }));
664
685
  const CODEMOD_CODE_SUFFIX = "code";
665
686
  const CODEMOD_JSON_SUFFIX = "json";
@@ -677,6 +698,7 @@ const constants$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
677
698
  CODEMOD_JSON_SUFFIX
678
699
  }, Symbol.toStringTag, { value: "Module" }));
679
700
  class Codemod {
701
+ uid;
680
702
  kind;
681
703
  version;
682
704
  baseDirectory;
@@ -688,17 +710,49 @@ class Codemod {
688
710
  this.baseDirectory = options.baseDirectory;
689
711
  this.filename = options.filename;
690
712
  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("-", " ");
713
+ this.uid = this.createUID();
714
+ }
715
+ createUID() {
716
+ const name = this.format({ stripExtension: true, stripKind: true, stripHyphens: false });
717
+ const kind = this.kind;
718
+ const version2 = this.version.raw;
719
+ return `${version2}-${name}-${kind}`;
720
+ }
721
+ format(options) {
722
+ const { stripExtension = true, stripKind = true, stripHyphens = true } = options ?? {};
723
+ let formatted = this.filename;
724
+ if (stripExtension) {
725
+ formatted = formatted.replace(new RegExp(`\\.${CODEMOD_EXTENSION}$`, "i"), "");
726
+ }
727
+ if (stripKind) {
728
+ formatted = formatted.replace(`.${CODEMOD_CODE_SUFFIX}`, "").replace(`.${CODEMOD_JSON_SUFFIX}`, "");
729
+ }
730
+ if (stripHyphens) {
731
+ formatted = formatted.replaceAll("-", " ");
732
+ }
733
+ return formatted;
694
734
  }
695
735
  }
696
736
  const codemodFactory = (options) => new Codemod(options);
697
- const index$7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
737
+ const index$8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
698
738
  __proto__: null,
699
739
  codemodFactory,
700
740
  constants: constants$2
701
741
  }, Symbol.toStringTag, { value: "Module" }));
742
+ const INTERNAL_CODEMODS_DIRECTORY = path__default.default.join(
743
+ __dirname,
744
+ // upgrade/dist
745
+ "..",
746
+ // upgrade
747
+ "resources",
748
+ // upgrade/resources
749
+ "codemods"
750
+ // upgrade/resources/codemods
751
+ );
752
+ const constants$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
753
+ __proto__: null,
754
+ INTERNAL_CODEMODS_DIRECTORY
755
+ }, Symbol.toStringTag, { value: "Module" }));
702
756
  class CodemodRepository {
703
757
  groups;
704
758
  versions;
@@ -717,29 +771,47 @@ class CodemodRepository {
717
771
  count(version2) {
718
772
  return this.findByVersion(version2).length;
719
773
  }
720
- countRange(range) {
721
- return this.findByRange(range).length;
722
- }
723
- exists(version2) {
774
+ versionExists(version2) {
724
775
  return version2.raw in this.groups;
725
776
  }
726
- findByRange(range) {
777
+ has(uid) {
778
+ const result = this.find({ uids: [uid] });
779
+ if (result.length !== 1) {
780
+ return false;
781
+ }
782
+ const { codemods } = result[0];
783
+ return codemods.length === 1 && codemods[0].uid === uid;
784
+ }
785
+ find(q) {
727
786
  const entries = Object.entries(this.groups);
728
- return entries.filter(([version2]) => range.test(version2)).map(([version2, codemods2]) => ({
787
+ return entries.filter(maybeFilterByRange).map(([version2, codemods]) => ({
729
788
  version: semVerFactory(version2),
730
- codemods: codemods2
731
- }));
789
+ // Filter by UID if provided in the query
790
+ codemods: codemods.filter(maybeFilterByUIDs)
791
+ })).filter(({ codemods }) => codemods.length > 0);
792
+ function maybeFilterByRange([version2]) {
793
+ if (!isRangeInstance(q.range)) {
794
+ return true;
795
+ }
796
+ return q.range.test(version2);
797
+ }
798
+ function maybeFilterByUIDs(codemod) {
799
+ if (q.uids === void 0) {
800
+ return true;
801
+ }
802
+ return q.uids.includes(codemod.uid);
803
+ }
732
804
  }
733
805
  findByVersion(version2) {
734
806
  const literalVersion = version2.raw;
735
- const codemods2 = this.groups[literalVersion];
736
- return codemods2 ?? [];
807
+ const codemods = this.groups[literalVersion];
808
+ return codemods ?? [];
737
809
  }
738
810
  findAll() {
739
811
  const entries = Object.entries(this.groups);
740
- return entries.map(([version2, codemods2]) => ({
812
+ return entries.map(([version2, codemods]) => ({
741
813
  version: semVerFactory(version2),
742
- codemods: codemods2
814
+ codemods
743
815
  }));
744
816
  }
745
817
  refreshAvailableVersions() {
@@ -771,19 +843,10 @@ const parseCodemodKindFromFilename = (filename) => {
771
843
  assert__default.default(CODEMOD_ALLOWED_SUFFIXES.includes(kind));
772
844
  return kind;
773
845
  };
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" }));
786
- const index$6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
846
+ const codemodRepositoryFactory = (cwd = INTERNAL_CODEMODS_DIRECTORY) => {
847
+ return new CodemodRepository(cwd);
848
+ };
849
+ const index$7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
787
850
  __proto__: null,
788
851
  codemodRepositoryFactory,
789
852
  constants: constants$1
@@ -817,40 +880,59 @@ class CodemodRunner {
817
880
  this.isDry = enabled;
818
881
  return this;
819
882
  }
820
- async run(codemodsDirectory) {
883
+ createRepository(codemodsDirectory) {
821
884
  const repository = codemodRepositoryFactory(
822
885
  codemodsDirectory ?? INTERNAL_CODEMODS_DIRECTORY
823
886
  );
824
887
  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}`
888
+ return repository;
889
+ }
890
+ async safeRunAndReport(codemods) {
891
+ if (this.isDry) {
892
+ this.logger?.warn?.(
893
+ "Running the codemods in dry mode. No files will be modified during the process."
839
894
  );
840
- } else {
841
- this.logger?.debug(`Found codemods for ${highlight(versionedCodemods.length)} version(s)`);
842
895
  }
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
896
  try {
848
- const reports$1 = await this.project.runCodemods(codemods2, { dry: this.isDry });
849
- this.logger?.raw(reports(reports$1));
897
+ const reports$1 = await this.project.runCodemods(codemods, { dry: this.isDry });
898
+ this.logger?.raw?.(reports(reports$1));
899
+ if (!this.isDry) {
900
+ const nbAffectedTotal = reports$1.flatMap((report) => report.report.ok).reduce((acc, nb) => acc + nb, 0);
901
+ this.logger?.debug?.(
902
+ `Successfully ran ${highlight(codemods.length)} codemod(s), ${highlight(nbAffectedTotal)} change(s) have been detected`
903
+ );
904
+ }
905
+ return successReport$1();
850
906
  } catch (e) {
851
907
  return erroredReport$1(unknownToError(e));
852
908
  }
853
- return successReport$1();
909
+ }
910
+ async runByUID(uid, codemodsDirectory) {
911
+ const repository = this.createRepository(codemodsDirectory);
912
+ if (!repository.has(uid)) {
913
+ throw new Error(`Unknown codemod UID provided: ${uid}`);
914
+ }
915
+ const codemods = repository.find({ uids: [uid] }).flatMap(({ codemods: codemods2 }) => codemods2);
916
+ return this.safeRunAndReport(codemods);
917
+ }
918
+ async run(codemodsDirectory) {
919
+ const repository = this.createRepository(codemodsDirectory);
920
+ const codemodsInRange = repository.find({ range: this.range });
921
+ const selectedCodemods = this.selectCodemodsCallback ? await this.selectCodemodsCallback(codemodsInRange) : codemodsInRange;
922
+ if (selectedCodemods.length === 0) {
923
+ this.logger?.debug?.(`Found no codemods to run for ${versionRange(this.range)}`);
924
+ return successReport$1();
925
+ }
926
+ const codemods = selectedCodemods.flatMap(({ codemods: codemods2 }) => codemods2);
927
+ const codemodsByVersion = fp.groupBy("version", codemods);
928
+ const fRange = versionRange(this.range);
929
+ this.logger?.debug?.(
930
+ `Found ${highlight(codemods.length)} codemods for ${highlight(fp.size(codemodsByVersion))} version(s) using ${fRange}`
931
+ );
932
+ for (const [version$1, codemods2] of Object.entries(codemodsByVersion)) {
933
+ this.logger?.debug?.(`- ${version(semVerFactory(version$1))} (${codemods2.length})`);
934
+ }
935
+ return this.safeRunAndReport(codemods);
854
936
  }
855
937
  }
856
938
  const codemodRunnerFactory = (project, range) => {
@@ -877,6 +959,15 @@ class Upgrader {
877
959
  this.logger = null;
878
960
  this.confirmationCallback = null;
879
961
  }
962
+ getNPMPackage() {
963
+ return this.npmPackage;
964
+ }
965
+ getProject() {
966
+ return this.project;
967
+ }
968
+ getTarget() {
969
+ return semVerFactory(this.target.raw);
970
+ }
880
971
  setRequirements(requirements) {
881
972
  this.requirements = requirements;
882
973
  return this;
@@ -889,7 +980,7 @@ class Upgrader {
889
980
  this.codemodsTarget = semVerFactory(
890
981
  `${this.target.major}.${this.target.minor}.${this.target.patch}`
891
982
  );
892
- this.logger?.debug(
983
+ this.logger?.debug?.(
893
984
  `The codemods target has been synced with the upgrade target. The codemod runner will now look for ${version(
894
985
  this.codemodsTarget
895
986
  )}`
@@ -898,7 +989,7 @@ class Upgrader {
898
989
  }
899
990
  overrideCodemodsTarget(target) {
900
991
  this.codemodsTarget = target;
901
- this.logger?.debug(
992
+ this.logger?.debug?.(
902
993
  `Overriding the codemods target. The codemod runner will now look for ${version(target)}`
903
994
  );
904
995
  return this;
@@ -918,46 +1009,52 @@ class Upgrader {
918
1009
  addRequirement(requirement) {
919
1010
  this.requirements.push(requirement);
920
1011
  const fRequired = requirement.isRequired ? "(required)" : "(optional)";
921
- this.logger?.debug(
1012
+ this.logger?.debug?.(
922
1013
  `Added a new requirement to the upgrade: ${highlight(requirement.name)} ${fRequired}`
923
1014
  );
924
1015
  return this;
925
1016
  }
926
1017
  async upgrade() {
927
- this.logger?.info(
1018
+ this.logger?.info?.(
928
1019
  `Upgrading from ${version(this.project.strapiVersion)} to ${version(this.target)}`
929
1020
  );
930
1021
  if (this.isDry) {
931
- this.logger?.warn(
1022
+ this.logger?.warn?.(
932
1023
  "Running the upgrade in dry mode. No files will be modified during the process."
933
1024
  );
934
1025
  }
935
1026
  const range = rangeFromVersions(this.project.strapiVersion, this.target);
936
1027
  const codemodsRange = rangeFromVersions(this.project.strapiVersion, this.codemodsTarget);
937
1028
  const npmVersionsMatches = this.npmPackage?.findVersionsInRange(range) ?? [];
938
- this.logger?.debug(
1029
+ this.logger?.debug?.(
939
1030
  `Found ${highlight(npmVersionsMatches.length)} versions satisfying ${versionRange(range)}`
940
1031
  );
941
1032
  try {
942
- this.logger?.info(upgradeStep("Checking requirement", [1, 4]));
1033
+ this.logger?.info?.(upgradeStep("Checking requirement", [1, 4]));
943
1034
  await this.checkRequirements(this.requirements, {
944
1035
  npmVersionsMatches,
945
1036
  project: this.project,
946
1037
  target: this.target
947
1038
  });
948
- this.logger?.info(upgradeStep("Applying the latest code modifications", [2, 4]));
1039
+ this.logger?.info?.(upgradeStep("Applying the latest code modifications", [2, 4]));
949
1040
  await this.runCodemods(codemodsRange);
950
- this.logger?.debug("Refreshing project information...");
1041
+ this.logger?.debug?.("Refreshing project information...");
951
1042
  this.project.refresh();
952
- this.logger?.info(upgradeStep("Upgrading Strapi dependencies", [3, 4]));
1043
+ this.logger?.info?.(upgradeStep("Upgrading Strapi dependencies", [3, 4]));
953
1044
  await this.updateDependencies();
954
- this.logger?.info(upgradeStep("Installing dependencies", [4, 4]));
1045
+ this.logger?.info?.(upgradeStep("Installing dependencies", [4, 4]));
955
1046
  await this.installDependencies();
956
1047
  } catch (e) {
957
1048
  return erroredReport(unknownToError(e));
958
1049
  }
959
1050
  return successReport();
960
1051
  }
1052
+ async confirm(message) {
1053
+ if (typeof this.confirmationCallback !== "function") {
1054
+ return true;
1055
+ }
1056
+ return this.confirmationCallback(message);
1057
+ }
961
1058
  async checkRequirements(requirements, context) {
962
1059
  for (const requirement of requirements) {
963
1060
  const { pass, error } = await requirement.test(context);
@@ -984,7 +1081,7 @@ class Upgrader {
984
1081
  if (requirement.isRequired) {
985
1082
  throw error;
986
1083
  }
987
- this.logger?.warn(warningMessage);
1084
+ this.logger?.warn?.(warningMessage);
988
1085
  const response = await this.confirmationCallback?.(confirmationMessage);
989
1086
  if (!response) {
990
1087
  throw error;
@@ -995,9 +1092,11 @@ class Upgrader {
995
1092
  const json = createJSONTransformAPI(packageJSON);
996
1093
  const dependencies = json.get("dependencies", {});
997
1094
  const strapiDependencies = this.getScopedStrapiDependencies(dependencies);
998
- this.logger?.debug(`Found ${highlight(strapiDependencies.length)} dependency(ies) to update`);
1095
+ this.logger?.debug?.(
1096
+ `Found ${highlight(strapiDependencies.length)} dependency(ies) to update`
1097
+ );
999
1098
  strapiDependencies.forEach(
1000
- (dependency) => this.logger?.debug(`- ${dependency[0]} (${dependency[1]} -> ${this.target})`)
1099
+ (dependency) => this.logger?.debug?.(`- ${dependency[0]} (${dependency[1]} -> ${this.target})`)
1001
1100
  );
1002
1101
  if (strapiDependencies.length === 0) {
1003
1102
  return;
@@ -1005,7 +1104,7 @@ class Upgrader {
1005
1104
  strapiDependencies.forEach(([name]) => json.set(`dependencies.${name}`, this.target.raw));
1006
1105
  const updatedPackageJSON = json.root();
1007
1106
  if (this.isDry) {
1008
- this.logger?.debug(`Skipping dependencies update (${chalk__default.default.italic("dry mode")})`);
1107
+ this.logger?.debug?.(`Skipping dependencies update (${chalk__default.default.italic("dry mode")})`);
1009
1108
  return;
1010
1109
  }
1011
1110
  await saveJSON(packageJSONPath, updatedPackageJSON);
@@ -1025,9 +1124,9 @@ class Upgrader {
1025
1124
  async installDependencies() {
1026
1125
  const projectPath = this.project.cwd;
1027
1126
  const packageManagerName = await utils.packageManager.getPreferred(projectPath);
1028
- this.logger?.debug(`Using ${highlight(packageManagerName)} as package manager`);
1127
+ this.logger?.debug?.(`Using ${highlight(packageManagerName)} as package manager`);
1029
1128
  if (this.isDry) {
1030
- this.logger?.debug(`Skipping dependencies installation (${chalk__default.default.italic("dry mode")}`);
1129
+ this.logger?.debug?.(`Skipping dependencies installation (${chalk__default.default.italic("dry mode")})`);
1031
1130
  return;
1032
1131
  }
1033
1132
  await utils.packageManager.installDependencies(projectPath, packageManagerName, {
@@ -1046,23 +1145,28 @@ class Upgrader {
1046
1145
  }
1047
1146
  const resolveNPMTarget = (project, target, npmPackage) => {
1048
1147
  if (isSemverInstance(target)) {
1049
- return npmPackage.findVersion(target);
1148
+ const version2 = npmPackage.findVersion(target);
1149
+ if (!version2) {
1150
+ throw new NPMCandidateNotFoundError(target);
1151
+ }
1152
+ return version2;
1050
1153
  }
1051
1154
  if (isSemVerReleaseType(target)) {
1052
1155
  const range = rangeFromVersions(project.strapiVersion, target);
1053
1156
  const npmVersionsMatches = npmPackage.findVersionsInRange(range);
1054
- return npmVersionsMatches.at(-1);
1157
+ const version2 = npmVersionsMatches.at(-1);
1158
+ if (!version2) {
1159
+ throw new NPMCandidateNotFoundError(range, `The project is already up-to-date (${target})`);
1160
+ }
1161
+ return version2;
1055
1162
  }
1056
- return void 0;
1163
+ throw new NPMCandidateNotFoundError(target);
1057
1164
  };
1058
1165
  const upgraderFactory = (project, target, npmPackage) => {
1059
- const targetedNPMVersion = resolveNPMTarget(project, target, npmPackage);
1060
- if (!targetedNPMVersion) {
1061
- throw new Error(`Couldn't find a matching version in the NPM registry for "${target}"`);
1062
- }
1063
- const semverTarget = semVerFactory(targetedNPMVersion.version);
1166
+ const npmTarget = resolveNPMTarget(project, target, npmPackage);
1167
+ const semverTarget = semVerFactory(npmTarget.version);
1064
1168
  if (semver__default.default.eq(semverTarget, project.strapiVersion)) {
1065
- throw new Error(`The project is already on ${version(semverTarget)}`);
1169
+ throw new Error(`The project is already using v${semverTarget}`);
1066
1170
  }
1067
1171
  return new Upgrader(project, semverTarget, npmPackage);
1068
1172
  };
@@ -1073,101 +1177,205 @@ const constants = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
1073
1177
  __proto__: null,
1074
1178
  STRAPI_PACKAGE_NAME
1075
1179
  }, Symbol.toStringTag, { value: "Module" }));
1076
- const index$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1180
+ const index$6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1077
1181
  __proto__: null,
1078
1182
  constants,
1079
1183
  upgraderFactory
1080
1184
  }, Symbol.toStringTag, { value: "Module" }));
1081
- const NPM_REGISTRY_URL = "https://registry.npmjs.org";
1082
- class Package {
1185
+ class Requirement {
1186
+ isRequired;
1083
1187
  name;
1084
- packageURL;
1085
- npmPackage;
1086
- constructor(name) {
1188
+ testCallback;
1189
+ children;
1190
+ constructor(name, testCallback, isRequired) {
1087
1191
  this.name = name;
1088
- this.packageURL = `${NPM_REGISTRY_URL}/${name}`;
1089
- this.npmPackage = null;
1192
+ this.testCallback = testCallback;
1193
+ this.isRequired = isRequired ?? true;
1194
+ this.children = [];
1090
1195
  }
1091
- get isLoaded() {
1092
- return this.npmPackage !== null;
1196
+ setChildren(children) {
1197
+ this.children = children;
1198
+ return this;
1093
1199
  }
1094
- assertPackageIsLoaded(npmPackage) {
1095
- assert__default.default(this.isLoaded, "The package is not loaded yet");
1200
+ addChild(child) {
1201
+ this.children.push(child);
1202
+ return this;
1096
1203
  }
1097
- getVersionsDict() {
1098
- this.assertPackageIsLoaded(this.npmPackage);
1099
- return this.npmPackage.versions;
1204
+ asOptional() {
1205
+ const newInstance = requirementFactory(this.name, this.testCallback, false);
1206
+ newInstance.setChildren(this.children);
1207
+ return newInstance;
1100
1208
  }
1101
- getVersionsAsList() {
1102
- this.assertPackageIsLoaded(this.npmPackage);
1103
- return Object.values(this.npmPackage.versions);
1209
+ asRequired() {
1210
+ const newInstance = requirementFactory(this.name, this.testCallback, true);
1211
+ newInstance.setChildren(this.children);
1212
+ return newInstance;
1104
1213
  }
1105
- findVersionsInRange(range) {
1106
- const versions = this.getVersionsAsList();
1107
- return versions.filter((v) => range.test(v.version)).filter((v) => isLiteralSemVer(v.version)).sort((v1, v2) => semver__default.default.compare(v1.version, v2.version));
1214
+ async test(context) {
1215
+ try {
1216
+ await this.testCallback?.(context);
1217
+ return ok();
1218
+ } catch (e) {
1219
+ if (e instanceof Error) {
1220
+ return errored(e);
1221
+ }
1222
+ if (typeof e === "string") {
1223
+ return errored(new Error(e));
1224
+ }
1225
+ return errored(new Error("Unknown error"));
1226
+ }
1108
1227
  }
1109
- findVersion(version2) {
1110
- const versions = this.getVersionsAsList();
1111
- return versions.find((npmVersion) => semver__default.default.eq(npmVersion.version, version2));
1228
+ }
1229
+ const ok = () => ({ pass: true, error: null });
1230
+ const errored = (error) => ({ pass: false, error });
1231
+ const requirementFactory = (name, testCallback, isRequired) => new Requirement(name, testCallback, isRequired);
1232
+ const index$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1233
+ __proto__: null,
1234
+ requirementFactory
1235
+ }, Symbol.toStringTag, { value: "Module" }));
1236
+ const REQUIRE_AVAILABLE_NEXT_MAJOR = requirementFactory(
1237
+ "REQUIRE_AVAILABLE_NEXT_MAJOR",
1238
+ (context) => {
1239
+ const { project, target } = context;
1240
+ const currentMajor = project.strapiVersion.major;
1241
+ const targetedMajor = target.major;
1242
+ if (targetedMajor === currentMajor) {
1243
+ throw new Error(`You're already on the latest major version (v${currentMajor})`);
1244
+ }
1112
1245
  }
1113
- async refresh() {
1114
- const response = await fetch(this.packageURL);
1115
- assert__default.default(response.ok, `Request failed for ${this.packageURL}`);
1116
- this.npmPackage = await response.json();
1117
- return this;
1246
+ );
1247
+ const REQUIRE_LATEST_FOR_CURRENT_MAJOR = requirementFactory(
1248
+ "REQUIRE_LATEST_FOR_CURRENT_MAJOR",
1249
+ (context) => {
1250
+ const { project, target, npmVersionsMatches } = context;
1251
+ const { major: currentMajor } = project.strapiVersion;
1252
+ const invalidMatches = npmVersionsMatches.filter(
1253
+ (match) => semVerFactory(match.version).major === currentMajor
1254
+ );
1255
+ if (invalidMatches.length > 0) {
1256
+ const invalidVersions = invalidMatches.map((match) => match.version);
1257
+ const invalidVersionsCount = invalidVersions.length;
1258
+ throw new Error(
1259
+ `Doing a major upgrade requires to be on the latest v${currentMajor} version, but found ${invalidVersionsCount} versions between the current one and ${target}. Please upgrade to ${invalidVersions.at(-1)} and try again.`
1260
+ );
1261
+ }
1118
1262
  }
1119
- versionExists(version2) {
1120
- return this.findVersion(version2) !== void 0;
1263
+ );
1264
+ const REQUIRE_GIT_CLEAN_REPOSITORY = requirementFactory(
1265
+ "REQUIRE_GIT_CLEAN_REPOSITORY",
1266
+ async (context) => {
1267
+ const git = simpleGit__default.default({ baseDir: context.project.cwd });
1268
+ const status = await git.status();
1269
+ if (!status.isClean()) {
1270
+ throw new Error(
1271
+ "Repository is not clean. Please commit or stash any changes before upgrading"
1272
+ );
1273
+ }
1121
1274
  }
1122
- }
1123
- const npmPackageFactory = (name) => new Package(name);
1275
+ );
1276
+ const REQUIRE_GIT_REPOSITORY = requirementFactory(
1277
+ "REQUIRE_GIT_REPOSITORY",
1278
+ async (context) => {
1279
+ const git = simpleGit__default.default({ baseDir: context.project.cwd });
1280
+ const isRepo = await git.checkIsRepo();
1281
+ if (!isRepo) {
1282
+ throw new Error("Not a git repository (or any of the parent directories)");
1283
+ }
1284
+ }
1285
+ ).addChild(REQUIRE_GIT_CLEAN_REPOSITORY.asOptional());
1286
+ const REQUIRE_GIT_INSTALLED = requirementFactory(
1287
+ "REQUIRE_GIT_INSTALLED",
1288
+ async (context) => {
1289
+ const git = simpleGit__default.default({ baseDir: context.project.cwd });
1290
+ try {
1291
+ await git.version();
1292
+ } catch {
1293
+ throw new Error("Git is not installed");
1294
+ }
1295
+ }
1296
+ ).addChild(REQUIRE_GIT_REPOSITORY.asOptional());
1297
+ const REQUIRE_GIT = requirementFactory("REQUIRE_GIT", null).addChild(
1298
+ REQUIRE_GIT_INSTALLED.asOptional()
1299
+ );
1300
+ const latest = async (upgrader, options) => {
1301
+ if (options.target !== ReleaseType.Latest) {
1302
+ return;
1303
+ }
1304
+ const npmPackage = upgrader.getNPMPackage();
1305
+ const target = upgrader.getTarget();
1306
+ const project = upgrader.getProject();
1307
+ const { strapiVersion: current } = project;
1308
+ const fTargetMajor = highlight(`v${target.major}`);
1309
+ const fCurrentMajor = highlight(`v${current.major}`);
1310
+ const fTarget = version(target);
1311
+ const fCurrent = version(current);
1312
+ const isMajorUpgrade = target.major > current.major;
1313
+ if (isMajorUpgrade) {
1314
+ options.logger.warn(
1315
+ `Detected a major upgrade for the "${highlight(ReleaseType.Latest)}" tag: ${fCurrent} > ${fTarget}`
1316
+ );
1317
+ const newerPackageRelease = npmPackage.findVersionsInRange(rangeFactory(`>${current.raw} <${target.major}`)).at(-1);
1318
+ if (newerPackageRelease) {
1319
+ const fLatest = version(semVerFactory(newerPackageRelease.version));
1320
+ options.logger.warn(
1321
+ `It's recommended to first upgrade to the latest version of ${fCurrentMajor} (${fLatest}) before upgrading to ${fTargetMajor}.`
1322
+ );
1323
+ }
1324
+ const proceedAnyway = await upgrader.confirm(`I know what I'm doing. Proceed anyway!`);
1325
+ if (!proceedAnyway) {
1326
+ throw new AbortedError();
1327
+ }
1328
+ }
1329
+ };
1124
1330
  const upgrade = async (options) => {
1125
1331
  const timer = timerFactory();
1126
1332
  const { logger, codemodsTarget } = options;
1127
1333
  const cwd = path__default.default.resolve(options.cwd ?? process.cwd());
1128
1334
  const project = projectFactory(cwd);
1129
- if (!isAppProject(project)) {
1335
+ logger.debug(projectDetails(project));
1336
+ if (!isApplicationProject(project)) {
1130
1337
  throw new Error(
1131
1338
  `The "${options.target}" upgrade can only be run on a Strapi project; for plugins, please use "codemods".`
1132
1339
  );
1133
1340
  }
1341
+ logger.debug(
1342
+ `Application: VERSION=${version(project.packageJSON.version)}; STRAPI_VERSION=${version(project.strapiVersion)}`
1343
+ );
1134
1344
  const npmPackage = npmPackageFactory(STRAPI_PACKAGE_NAME);
1135
1345
  await npmPackage.refresh();
1136
1346
  const upgrader = upgraderFactory(project, options.target, npmPackage).dry(options.dry ?? false).onConfirm(options.confirm ?? null).setLogger(logger);
1137
1347
  if (codemodsTarget !== void 0) {
1138
1348
  upgrader.overrideCodemodsTarget(codemodsTarget);
1139
1349
  }
1140
- if (options.target === ReleaseType.Major) {
1141
- upgrader.addRequirement(REQUIRE_AVAILABLE_NEXT_MAJOR).addRequirement(REQUIRE_LATEST_FOR_CURRENT_MAJOR);
1142
- }
1143
- upgrader.addRequirement(REQUIRE_GIT.asOptional());
1350
+ await runUpgradePrompts(upgrader, options);
1351
+ addUpgradeRequirements(upgrader, options);
1144
1352
  const upgradeReport = await upgrader.upgrade();
1145
1353
  if (!upgradeReport.success) {
1146
1354
  throw upgradeReport.error;
1147
1355
  }
1148
1356
  timer.stop();
1149
- logger.info(`Completed in ${durationMs(timer.elapsedMs)}`);
1357
+ logger.info(`Completed in ${durationMs(timer.elapsedMs)}ms`);
1150
1358
  };
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;
1359
+ const runUpgradePrompts = async (upgrader, options) => {
1360
+ if (options.target === ReleaseType.Latest) {
1361
+ await latest(upgrader, options);
1161
1362
  }
1162
- timer.stop();
1163
- logger.info(`Completed in ${timer.elapsedMs}`);
1164
1363
  };
1364
+ const addUpgradeRequirements = (upgrader, options) => {
1365
+ if (options.target === ReleaseType.Major) {
1366
+ upgrader.addRequirement(REQUIRE_AVAILABLE_NEXT_MAJOR).addRequirement(REQUIRE_LATEST_FOR_CURRENT_MAJOR);
1367
+ }
1368
+ upgrader.addRequirement(REQUIRE_GIT.asOptional());
1369
+ };
1370
+ const resolvePath = (cwd) => path__default.default.resolve(cwd ?? process.cwd());
1165
1371
  const getRangeFromTarget = (currentVersion, target) => {
1166
1372
  if (isSemverInstance(target)) {
1167
1373
  return rangeFactory(target);
1168
1374
  }
1169
1375
  const { major, minor, patch } = currentVersion;
1170
1376
  switch (target) {
1377
+ case ReleaseType.Latest:
1378
+ throw new Error("Can't use <latest> to create a codemods range: not implemented");
1171
1379
  case ReleaseType.Major:
1172
1380
  return rangeFactory(`${major}`);
1173
1381
  case ReleaseType.Minor:
@@ -1178,9 +1386,60 @@ const getRangeFromTarget = (currentVersion, target) => {
1178
1386
  throw new Error(`Invalid target set: ${target}`);
1179
1387
  }
1180
1388
  };
1389
+ const findRangeFromTarget = (project, target) => {
1390
+ if (isRangeInstance(target)) {
1391
+ return target;
1392
+ }
1393
+ if (isApplicationProject(project)) {
1394
+ return getRangeFromTarget(project.strapiVersion, target);
1395
+ }
1396
+ return rangeFactory("*");
1397
+ };
1398
+ const runCodemods = async (options) => {
1399
+ const timer = timerFactory();
1400
+ const { logger, uid } = options;
1401
+ const cwd = resolvePath(options.cwd);
1402
+ const project = projectFactory(cwd);
1403
+ const range = findRangeFromTarget(project, options.target);
1404
+ logger.debug(projectDetails(project));
1405
+ logger.debug(`Range: set to ${versionRange(range)}`);
1406
+ const codemodRunner = codemodRunnerFactory(project, range).dry(options.dry ?? false).onSelectCodemods(options.selectCodemods ?? null).setLogger(logger);
1407
+ let report;
1408
+ if (uid !== void 0) {
1409
+ logger.debug(`Running a single codemod: ${codemodUID(uid)}`);
1410
+ report = await codemodRunner.runByUID(uid);
1411
+ } else {
1412
+ report = await codemodRunner.run();
1413
+ }
1414
+ if (!report.success) {
1415
+ throw report.error;
1416
+ }
1417
+ timer.stop();
1418
+ logger.info(`Completed in ${timer.elapsedMs}`);
1419
+ };
1420
+ const listCodemods = async (options) => {
1421
+ const { logger, target } = options;
1422
+ const cwd = resolvePath(options.cwd);
1423
+ const project = projectFactory(cwd);
1424
+ const range = findRangeFromTarget(project, target);
1425
+ logger.debug(projectDetails(project));
1426
+ logger.debug(`Range: set to ${versionRange(range)}`);
1427
+ const repo = codemodRepositoryFactory();
1428
+ repo.refresh();
1429
+ const groups = repo.find({ range });
1430
+ const codemods = groups.flatMap((collection) => collection.codemods);
1431
+ logger.debug(`Found ${highlight(codemods.length)} codemods`);
1432
+ if (codemods.length === 0) {
1433
+ logger.info(`Found no codemods matching ${versionRange(range)}`);
1434
+ return;
1435
+ }
1436
+ const fCodemods = codemodList(codemods);
1437
+ logger.raw(fCodemods);
1438
+ };
1181
1439
  const index$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1182
1440
  __proto__: null,
1183
- codemods,
1441
+ listCodemods,
1442
+ runCodemods,
1184
1443
  upgrade
1185
1444
  }, Symbol.toStringTag, { value: "Module" }));
1186
1445
  class Logger {
@@ -1274,18 +1533,18 @@ const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
1274
1533
  }, Symbol.toStringTag, { value: "Module" }));
1275
1534
  const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1276
1535
  __proto__: null,
1277
- codemod: index$7,
1278
- codemodRepository: index$6,
1536
+ codemod: index$8,
1537
+ codemodRepository: index$7,
1279
1538
  error: index$9,
1280
- f: index$8,
1539
+ f: index$f,
1281
1540
  fileScanner: index$d,
1282
1541
  logger: index$3,
1283
1542
  project: index$a,
1284
1543
  report: index$2,
1285
- requirement: index$g,
1544
+ requirement: index$5,
1286
1545
  runner: index$1,
1287
- timer: index$f,
1288
- upgrader: index$5,
1546
+ timer: index$g,
1547
+ upgrader: index$6,
1289
1548
  version: index$e
1290
1549
  }, Symbol.toStringTag, { value: "Module" }));
1291
1550
  exports.modules = index;