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

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 (96) 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 +739 -387
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +735 -384
  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 +6 -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 +3 -0
  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 -3
  30. package/dist/modules/project/constants.d.ts.map +1 -1
  31. package/dist/modules/project/index.d.ts +2 -0
  32. package/dist/modules/project/index.d.ts.map +1 -1
  33. package/dist/modules/project/project.d.ts +27 -5
  34. package/dist/modules/project/project.d.ts.map +1 -1
  35. package/dist/modules/project/types.d.ts +3 -10
  36. package/dist/modules/project/types.d.ts.map +1 -1
  37. package/dist/modules/project/utils.d.ts +6 -0
  38. package/dist/modules/project/utils.d.ts.map +1 -0
  39. package/dist/modules/report/report.d.ts.map +1 -1
  40. package/dist/modules/requirement/types.d.ts +2 -2
  41. package/dist/modules/requirement/types.d.ts.map +1 -1
  42. package/dist/modules/runner/json/transform.d.ts.map +1 -1
  43. package/dist/modules/upgrader/types.d.ts +6 -0
  44. package/dist/modules/upgrader/types.d.ts.map +1 -1
  45. package/dist/modules/upgrader/upgrader.d.ts +7 -3
  46. package/dist/modules/upgrader/upgrader.d.ts.map +1 -1
  47. package/dist/modules/version/range.d.ts +2 -0
  48. package/dist/modules/version/range.d.ts.map +1 -1
  49. package/dist/modules/version/types.d.ts +2 -1
  50. package/dist/modules/version/types.d.ts.map +1 -1
  51. package/dist/tasks/codemods/index.d.ts +2 -1
  52. package/dist/tasks/codemods/index.d.ts.map +1 -1
  53. package/dist/tasks/codemods/list-codemods.d.ts +3 -0
  54. package/dist/tasks/codemods/list-codemods.d.ts.map +1 -0
  55. package/dist/tasks/codemods/run-codemods.d.ts +3 -0
  56. package/dist/tasks/codemods/run-codemods.d.ts.map +1 -0
  57. package/dist/tasks/codemods/types.d.ts +9 -3
  58. package/dist/tasks/codemods/types.d.ts.map +1 -1
  59. package/dist/tasks/codemods/utils.d.ts +6 -0
  60. package/dist/tasks/codemods/utils.d.ts.map +1 -0
  61. package/dist/tasks/index.d.ts +1 -1
  62. package/dist/tasks/index.d.ts.map +1 -1
  63. package/dist/tasks/upgrade/prompts/index.d.ts +2 -0
  64. package/dist/tasks/upgrade/prompts/index.d.ts.map +1 -0
  65. package/dist/tasks/upgrade/prompts/latest.d.ts +9 -0
  66. package/dist/tasks/upgrade/prompts/latest.d.ts.map +1 -0
  67. package/dist/tasks/upgrade/requirements/major.d.ts.map +1 -1
  68. package/dist/tasks/upgrade/upgrade.d.ts.map +1 -1
  69. package/package.json +11 -10
  70. package/resources/codemods/5.0.0/comment-out-lifecycle-files.code.ts +63 -0
  71. package/resources/codemods/5.0.0/dependency-remove-strapi-plugin-i18n.json.ts +31 -0
  72. package/resources/codemods/5.0.0/dependency-upgrade-react-and-react-dom.json.ts +67 -0
  73. package/resources/codemods/5.0.0/dependency-upgrade-react-router-dom.json.ts +59 -0
  74. package/resources/codemods/5.0.0/dependency-upgrade-styled-components.json.ts +49 -0
  75. package/resources/codemods/5.0.0/deprecate-helper-plugin.code.ts +192 -0
  76. package/resources/codemods/5.0.0/entity-service-document-service.code.ts +437 -0
  77. package/resources/codemods/5.0.0/s3-keys-wrapped-in-credentials.code.ts +1 -1
  78. package/resources/codemods/5.0.0/sqlite3-to-better-sqlite3.json.ts +5 -3
  79. package/resources/codemods/5.0.0/strapi-public-interface.code.ts +126 -0
  80. package/resources/codemods/5.0.0/use-uid-for-config-namespace.code.ts +1 -1
  81. package/resources/codemods/5.0.0/utils-public-interface.code.ts +320 -0
  82. package/resources/codemods/5.1.0/dependency-better-sqlite3.json.ts +48 -0
  83. package/resources/examples/console.log-to-console.info.code.ts +1 -1
  84. package/resources/examples/disable-jsx-buttons.code.ts +42 -0
  85. package/resources/utils/change-import.ts +118 -0
  86. package/resources/utils/replace-jsx.ts +49 -0
  87. package/dist/_chunks/codemod-runner-B5OeSMTQ.js +0 -730
  88. package/dist/_chunks/codemod-runner-B5OeSMTQ.js.map +0 -1
  89. package/dist/_chunks/codemods-10ZKewQx.js +0 -108
  90. package/dist/_chunks/codemods-10ZKewQx.js.map +0 -1
  91. package/dist/_chunks/index-uxCwtuH1.js +0 -103
  92. package/dist/_chunks/index-uxCwtuH1.js.map +0 -1
  93. package/dist/_chunks/upgrade-A4T1OWs5.js +0 -357
  94. package/dist/_chunks/upgrade-A4T1OWs5.js.map +0 -1
  95. package/dist/tasks/codemods/codemods.d.ts +0 -3
  96. 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}...`);
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`;
210
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,17 +406,31 @@ 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_ALLOWED_EXTENSIONS = ["js", "ts", "json"];
380
- const PROJECT_DEFAULT_PATTERNS = ["package.json"];
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 = [
413
+ // Source files
414
+ "js",
415
+ "mjs",
416
+ "ts",
417
+ // React files
418
+ "jsx",
419
+ "tsx"
420
+ ];
421
+ const PROJECT_JSON_EXTENSIONS = ["json"];
422
+ const PROJECT_ALLOWED_EXTENSIONS = [...PROJECT_CODE_EXTENSIONS, ...PROJECT_JSON_EXTENSIONS];
381
423
  const SCOPED_STRAPI_PACKAGE_PREFIX = "@strapi/";
382
424
  const STRAPI_DEPENDENCY_NAME = `${SCOPED_STRAPI_PACKAGE_PREFIX}strapi`;
383
425
  const constants$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
384
426
  __proto__: null,
385
- PROJECT_DEFAULT_ALLOWED_EXTENSIONS,
386
- PROJECT_DEFAULT_ALLOWED_ROOT_PATHS,
387
- PROJECT_DEFAULT_PATTERNS,
427
+ PROJECT_ALLOWED_EXTENSIONS,
428
+ PROJECT_APP_ALLOWED_ROOT_PATHS,
429
+ PROJECT_CODE_EXTENSIONS,
430
+ PROJECT_JSON_EXTENSIONS,
388
431
  PROJECT_PACKAGE_JSON,
432
+ PROJECT_PLUGIN_ALLOWED_ROOT_PATHS,
433
+ PROJECT_PLUGIN_ROOT_FILES,
389
434
  SCOPED_STRAPI_PACKAGE_PREFIX,
390
435
  STRAPI_DEPENDENCY_NAME
391
436
  }, Symbol.toStringTag, { value: "Module" }));
@@ -395,12 +440,13 @@ class Project {
395
440
  files;
396
441
  packageJSONPath;
397
442
  packageJSON;
398
- strapiVersion;
399
- constructor(cwd) {
443
+ paths;
444
+ constructor(cwd, config) {
400
445
  if (!fse__default.default.pathExistsSync(cwd)) {
401
446
  throw new Error(`ENOENT: no such file or directory, access '${cwd}'`);
402
447
  }
403
448
  this.cwd = cwd;
449
+ this.paths = config.paths;
404
450
  this.refresh();
405
451
  }
406
452
  getFilesByExtensions(extensions) {
@@ -411,14 +457,13 @@ class Project {
411
457
  }
412
458
  refresh() {
413
459
  this.refreshPackageJSON();
414
- this.refreshStrapiVersion();
415
460
  this.refreshProjectFiles();
416
461
  return this;
417
462
  }
418
- async runCodemods(codemods2, options) {
463
+ async runCodemods(codemods, options) {
419
464
  const runners = this.createProjectCodemodsRunners(options.dry);
420
465
  const reports2 = [];
421
- for (const codemod of codemods2) {
466
+ for (const codemod of codemods) {
422
467
  for (const runner of runners) {
423
468
  if (runner.valid(codemod)) {
424
469
  const report = await runner.run(codemod);
@@ -429,17 +474,20 @@ class Project {
429
474
  return reports2;
430
475
  }
431
476
  createProjectCodemodsRunners(dry = false) {
432
- const jsonFiles = this.getFilesByExtensions([".json"]);
433
- const codeFiles = this.getFilesByExtensions([".js", ".ts", ".mjs"]);
477
+ const jsonExtensions = PROJECT_JSON_EXTENSIONS.map((ext) => `.${ext}`);
478
+ const codeExtensions = PROJECT_CODE_EXTENSIONS.map((ext) => `.${ext}`);
479
+ const jsonFiles = this.getFilesByExtensions(jsonExtensions);
480
+ const codeFiles = this.getFilesByExtensions(codeExtensions);
434
481
  const codeRunner = codeRunnerFactory(codeFiles, {
435
482
  dry,
436
- print: false,
437
- silent: true,
438
- extensions: "js,ts",
483
+ parser: "ts",
439
484
  runInBand: true,
440
- verbose: 0,
441
485
  babel: true,
442
- parser: "ts"
486
+ extensions: PROJECT_CODE_EXTENSIONS.join(","),
487
+ // Don't output any log coming from the runner
488
+ print: false,
489
+ silent: true,
490
+ verbose: 0
443
491
  });
444
492
  const jsonRunner = jsonRunnerFactory(jsonFiles, { dry, cwd: this.cwd });
445
493
  return [codeRunner, jsonRunner];
@@ -456,16 +504,38 @@ class Project {
456
504
  this.packageJSON = JSON.parse(packageJSONBuffer.toString());
457
505
  }
458
506
  refreshProjectFiles() {
459
- const allowedRootPaths = formatGlobCollectionPattern(
460
- PROJECT_DEFAULT_ALLOWED_ROOT_PATHS
461
- );
462
- const allowedExtensions = formatGlobCollectionPattern(
463
- PROJECT_DEFAULT_ALLOWED_EXTENSIONS
464
- );
465
- const projectFilesPattern = `./${allowedRootPaths}/**/*.${allowedExtensions}`;
466
- const patterns = [projectFilesPattern, ...PROJECT_DEFAULT_PATTERNS];
467
507
  const scanner = fileScannerFactory(this.cwd);
468
- this.files = scanner.scan(patterns);
508
+ this.files = scanner.scan(this.paths);
509
+ }
510
+ }
511
+ class AppProject extends Project {
512
+ strapiVersion;
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
+ }
531
+ constructor(cwd) {
532
+ super(cwd, { paths: AppProject.paths });
533
+ this.refreshStrapiVersion();
534
+ }
535
+ refresh() {
536
+ super.refresh();
537
+ this.refreshStrapiVersion();
538
+ return this;
469
539
  }
470
540
  refreshStrapiVersion() {
471
541
  this.strapiVersion = // First try to get the strapi version from the package.json dependencies
@@ -512,10 +582,71 @@ const formatGlobCollectionPattern = (collection) => {
512
582
  );
513
583
  return collection.length === 1 ? collection[0] : `{${collection}}`;
514
584
  };
515
- const projectFactory = (cwd) => new Project(cwd);
585
+ class PluginProject extends Project {
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
+ }
611
+ }
612
+ const isPlugin = (cwd) => {
613
+ const packageJSONPath = path__default.default.join(cwd, PROJECT_PACKAGE_JSON);
614
+ try {
615
+ fse__default.default.accessSync(packageJSONPath);
616
+ } catch {
617
+ throw new Error(`Could not find a ${PROJECT_PACKAGE_JSON} file in ${cwd}`);
618
+ }
619
+ const packageJSONBuffer = fse__default.default.readFileSync(packageJSONPath);
620
+ const packageJSON = JSON.parse(packageJSONBuffer.toString());
621
+ return packageJSON?.strapi?.kind === "plugin";
622
+ };
623
+ const projectFactory = (cwd) => {
624
+ fse__default.default.accessSync(cwd);
625
+ return isPlugin(cwd) ? new PluginProject(cwd) : new AppProject(cwd);
626
+ };
627
+ const isPluginProject = (project) => {
628
+ return project instanceof PluginProject;
629
+ };
630
+ function assertPluginProject(project) {
631
+ if (!isPluginProject(project)) {
632
+ throw new Error("Project is not a plugin");
633
+ }
634
+ }
635
+ const isApplicationProject = (project) => {
636
+ return project instanceof AppProject;
637
+ };
638
+ function assertAppProject(project) {
639
+ if (!isApplicationProject(project)) {
640
+ throw new Error("Project is not an application");
641
+ }
642
+ }
516
643
  const index$a = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
517
644
  __proto__: null,
645
+ assertAppProject,
646
+ assertPluginProject,
518
647
  constants: constants$3,
648
+ isApplicationProject,
649
+ isPluginProject,
519
650
  projectFactory
520
651
  }, Symbol.toStringTag, { value: "Module" }));
521
652
  class UnexpectedError extends Error {
@@ -523,70 +654,33 @@ class UnexpectedError extends Error {
523
654
  super("Unexpected Error");
524
655
  }
525
656
  }
526
- const unknownToError = (e) => {
527
- if (e instanceof Error) {
528
- return e;
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;
529
662
  }
530
- if (typeof e === "string") {
531
- return new Error(e);
663
+ }
664
+ class AbortedError extends Error {
665
+ constructor(message = "Upgrade aborted") {
666
+ super(message);
532
667
  }
533
- return new UnexpectedError();
534
- };
535
- const index$9 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
536
- __proto__: null,
537
- UnexpectedError,
538
- unknownToError
539
- }, Symbol.toStringTag, { value: "Module" }));
540
- const path = (path2) => chalk__default.default.blue(path2);
541
- const version = (version2) => {
542
- return chalk__default.default.italic.yellow(`v${version2}`);
543
- };
544
- const versionRange = (range) => chalk__default.default.italic.yellow(range);
545
- const transform = (transformFilePath) => chalk__default.default.cyan(transformFilePath);
546
- const highlight = (arg) => chalk__default.default.bold.underline(arg);
547
- const upgradeStep = (text, step) => {
548
- return chalk__default.default.bold(`(${step[0]}/${step[1]}) ${text}...`);
549
- };
550
- const reports = (reports2) => {
551
- const rows = reports2.map(({ codemod, report }, i) => {
552
- const fIndex = chalk__default.default.grey(i);
553
- const fVersion = chalk__default.default.magenta(codemod.version);
554
- const fKind = chalk__default.default.yellow(codemod.kind);
555
- const fFormattedTransformPath = chalk__default.default.cyan(codemod.format());
556
- const fTimeElapsed = i === 0 ? `${report.timeElapsed}s ${chalk__default.default.dim.italic("(cold start)")}` : `${report.timeElapsed}s`;
557
- const fAffected = report.ok > 0 ? chalk__default.default.green(report.ok) : chalk__default.default.grey(0);
558
- const fUnchanged = report.ok === 0 ? chalk__default.default.red(report.nochange) : chalk__default.default.grey(report.nochange);
559
- return [fIndex, fVersion, fKind, fFormattedTransformPath, fAffected, fUnchanged, fTimeElapsed];
560
- });
561
- const table = new CliTable3__default.default({
562
- style: { compact: true },
563
- head: [
564
- chalk__default.default.bold.grey("N°"),
565
- chalk__default.default.bold.magenta("Version"),
566
- chalk__default.default.bold.yellow("Kind"),
567
- chalk__default.default.bold.cyan("Name"),
568
- chalk__default.default.bold.green("Affected"),
569
- chalk__default.default.bold.red("Unchanged"),
570
- chalk__default.default.bold.blue("Duration")
571
- ]
572
- });
573
- table.push(...rows);
574
- return table.toString();
575
- };
576
- const durationMs = (elapsedMs) => {
577
- const elapsedSeconds = (elapsedMs / ONE_SECOND_MS).toFixed(3);
578
- return `${elapsedSeconds}s`;
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();
579
677
  };
580
- const index$8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
678
+ const index$9 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
581
679
  __proto__: null,
582
- durationMs,
583
- highlight,
584
- path,
585
- reports,
586
- transform,
587
- upgradeStep,
588
- version,
589
- versionRange
680
+ AbortedError,
681
+ NPMCandidateNotFoundError,
682
+ UnexpectedError,
683
+ unknownToError
590
684
  }, Symbol.toStringTag, { value: "Module" }));
591
685
  const CODEMOD_CODE_SUFFIX = "code";
592
686
  const CODEMOD_JSON_SUFFIX = "json";
@@ -604,6 +698,7 @@ const constants$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
604
698
  CODEMOD_JSON_SUFFIX
605
699
  }, Symbol.toStringTag, { value: "Module" }));
606
700
  class Codemod {
701
+ uid;
607
702
  kind;
608
703
  version;
609
704
  baseDirectory;
@@ -615,17 +710,49 @@ class Codemod {
615
710
  this.baseDirectory = options.baseDirectory;
616
711
  this.filename = options.filename;
617
712
  this.path = path__default.default.join(this.baseDirectory, this.version.raw, this.filename);
618
- }
619
- format() {
620
- return this.filename.replace(`.${CODEMOD_CODE_SUFFIX}.${CODEMOD_EXTENSION}`, "").replace(`.${CODEMOD_JSON_SUFFIX}.${CODEMOD_EXTENSION}`, "").replaceAll("-", " ");
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;
621
734
  }
622
735
  }
623
736
  const codemodFactory = (options) => new Codemod(options);
624
- const index$7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
737
+ const index$8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
625
738
  __proto__: null,
626
739
  codemodFactory,
627
740
  constants: constants$2
628
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" }));
629
756
  class CodemodRepository {
630
757
  groups;
631
758
  versions;
@@ -644,23 +771,48 @@ class CodemodRepository {
644
771
  count(version2) {
645
772
  return this.findByVersion(version2).length;
646
773
  }
647
- countRange(range) {
648
- return this.findByRange(range).length;
649
- }
650
- exists(version2) {
774
+ versionExists(version2) {
651
775
  return version2.raw in this.groups;
652
776
  }
653
- 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) {
654
786
  const entries = Object.entries(this.groups);
655
- return entries.filter(([version2]) => range.test(version2)).map(([version2, codemods2]) => ({
787
+ return entries.filter(maybeFilterByRange).map(([version2, codemods]) => ({
656
788
  version: semVerFactory(version2),
657
- codemods: codemods2
658
- }));
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
+ }
659
804
  }
660
805
  findByVersion(version2) {
661
806
  const literalVersion = version2.raw;
662
- const codemods2 = this.groups[literalVersion];
663
- return codemods2 ?? [];
807
+ const codemods = this.groups[literalVersion];
808
+ return codemods ?? [];
809
+ }
810
+ findAll() {
811
+ const entries = Object.entries(this.groups);
812
+ return entries.map(([version2, codemods]) => ({
813
+ version: semVerFactory(version2),
814
+ codemods
815
+ }));
664
816
  }
665
817
  refreshAvailableVersions() {
666
818
  this.versions = fse__default.default.readdirSync(this.cwd).filter((filename) => fse__default.default.statSync(path__default.default.join(this.cwd, filename)).isDirectory()).filter((filename) => semver__default.default.valid(filename) !== null).map((version2) => semVerFactory(version2)).sort(semver__default.default.compare);
@@ -691,19 +843,10 @@ const parseCodemodKindFromFilename = (filename) => {
691
843
  assert__default.default(CODEMOD_ALLOWED_SUFFIXES.includes(kind));
692
844
  return kind;
693
845
  };
694
- const codemodRepositoryFactory = (cwd) => new CodemodRepository(cwd);
695
- const INTERNAL_CODEMODS_DIRECTORY = path__default.default.join(
696
- __dirname,
697
- "..",
698
- "..",
699
- "resources",
700
- "codemods"
701
- );
702
- const constants$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
703
- __proto__: null,
704
- INTERNAL_CODEMODS_DIRECTORY
705
- }, Symbol.toStringTag, { value: "Module" }));
706
- 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({
707
850
  __proto__: null,
708
851
  codemodRepositoryFactory,
709
852
  constants: constants$1
@@ -737,34 +880,59 @@ class CodemodRunner {
737
880
  this.isDry = enabled;
738
881
  return this;
739
882
  }
740
- async run(codemodsDirectory) {
883
+ createRepository(codemodsDirectory) {
741
884
  const repository = codemodRepositoryFactory(
742
885
  codemodsDirectory ?? INTERNAL_CODEMODS_DIRECTORY
743
886
  );
744
887
  repository.refresh();
745
- const allVersionedCodemods = repository.findByRange(this.range);
746
- const versionedCodemods = this.selectCodemodsCallback ? await this.selectCodemodsCallback(allVersionedCodemods) : allVersionedCodemods;
747
- const hasCodemodsToRun = versionedCodemods.length > 0;
748
- if (!hasCodemodsToRun) {
749
- this.logger?.debug(`Found no codemods to run for ${versionRange(this.range)}`);
750
- return successReport$1();
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."
894
+ );
751
895
  }
752
- this.logger?.debug(
753
- `Found codemods for ${highlight(
754
- versionedCodemods.length
755
- )} version(s) using ${versionRange(this.range)}`
756
- );
757
- versionedCodemods.forEach(
758
- ({ version: version$1, codemods: codemods22 }) => this.logger?.debug(`- ${version(version$1)} (${codemods22.length})`)
759
- );
760
- const codemods2 = versionedCodemods.map(({ codemods: codemods22 }) => codemods22).flat();
761
896
  try {
762
- const reports$1 = await this.project.runCodemods(codemods2, { dry: this.isDry });
763
- 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();
764
906
  } catch (e) {
765
907
  return erroredReport$1(unknownToError(e));
766
908
  }
767
- 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);
768
936
  }
769
937
  }
770
938
  const codemodRunnerFactory = (project, range) => {
@@ -791,6 +959,15 @@ class Upgrader {
791
959
  this.logger = null;
792
960
  this.confirmationCallback = null;
793
961
  }
962
+ getNPMPackage() {
963
+ return this.npmPackage;
964
+ }
965
+ getProject() {
966
+ return this.project;
967
+ }
968
+ getTarget() {
969
+ return semVerFactory(this.target.raw);
970
+ }
794
971
  setRequirements(requirements) {
795
972
  this.requirements = requirements;
796
973
  return this;
@@ -803,7 +980,7 @@ class Upgrader {
803
980
  this.codemodsTarget = semVerFactory(
804
981
  `${this.target.major}.${this.target.minor}.${this.target.patch}`
805
982
  );
806
- this.logger?.debug(
983
+ this.logger?.debug?.(
807
984
  `The codemods target has been synced with the upgrade target. The codemod runner will now look for ${version(
808
985
  this.codemodsTarget
809
986
  )}`
@@ -812,7 +989,7 @@ class Upgrader {
812
989
  }
813
990
  overrideCodemodsTarget(target) {
814
991
  this.codemodsTarget = target;
815
- this.logger?.debug(
992
+ this.logger?.debug?.(
816
993
  `Overriding the codemods target. The codemod runner will now look for ${version(target)}`
817
994
  );
818
995
  return this;
@@ -832,44 +1009,52 @@ class Upgrader {
832
1009
  addRequirement(requirement) {
833
1010
  this.requirements.push(requirement);
834
1011
  const fRequired = requirement.isRequired ? "(required)" : "(optional)";
835
- this.logger?.debug(
1012
+ this.logger?.debug?.(
836
1013
  `Added a new requirement to the upgrade: ${highlight(requirement.name)} ${fRequired}`
837
1014
  );
838
1015
  return this;
839
1016
  }
840
1017
  async upgrade() {
841
- this.logger?.info(
1018
+ this.logger?.info?.(
842
1019
  `Upgrading from ${version(this.project.strapiVersion)} to ${version(this.target)}`
843
1020
  );
844
1021
  if (this.isDry) {
845
- this.logger?.warn(
1022
+ this.logger?.warn?.(
846
1023
  "Running the upgrade in dry mode. No files will be modified during the process."
847
1024
  );
848
1025
  }
849
1026
  const range = rangeFromVersions(this.project.strapiVersion, this.target);
850
1027
  const codemodsRange = rangeFromVersions(this.project.strapiVersion, this.codemodsTarget);
851
1028
  const npmVersionsMatches = this.npmPackage?.findVersionsInRange(range) ?? [];
852
- this.logger?.debug(
1029
+ this.logger?.debug?.(
853
1030
  `Found ${highlight(npmVersionsMatches.length)} versions satisfying ${versionRange(range)}`
854
1031
  );
855
1032
  try {
856
- this.logger?.info(upgradeStep("Checking requirement", [1, 4]));
1033
+ this.logger?.info?.(upgradeStep("Checking requirement", [1, 4]));
857
1034
  await this.checkRequirements(this.requirements, {
858
1035
  npmVersionsMatches,
859
1036
  project: this.project,
860
1037
  target: this.target
861
1038
  });
862
- this.logger?.info(upgradeStep("Applying the latest code modifications", [2, 4]));
1039
+ this.logger?.info?.(upgradeStep("Applying the latest code modifications", [2, 4]));
863
1040
  await this.runCodemods(codemodsRange);
864
- this.logger?.info(upgradeStep("Upgrading Strapi dependencies", [3, 4]));
1041
+ this.logger?.debug?.("Refreshing project information...");
1042
+ this.project.refresh();
1043
+ this.logger?.info?.(upgradeStep("Upgrading Strapi dependencies", [3, 4]));
865
1044
  await this.updateDependencies();
866
- this.logger?.info(upgradeStep("Installing dependencies", [4, 4]));
1045
+ this.logger?.info?.(upgradeStep("Installing dependencies", [4, 4]));
867
1046
  await this.installDependencies();
868
1047
  } catch (e) {
869
1048
  return erroredReport(unknownToError(e));
870
1049
  }
871
1050
  return successReport();
872
1051
  }
1052
+ async confirm(message) {
1053
+ if (typeof this.confirmationCallback !== "function") {
1054
+ return true;
1055
+ }
1056
+ return this.confirmationCallback(message);
1057
+ }
873
1058
  async checkRequirements(requirements, context) {
874
1059
  for (const requirement of requirements) {
875
1060
  const { pass, error } = await requirement.test(context);
@@ -896,7 +1081,7 @@ class Upgrader {
896
1081
  if (requirement.isRequired) {
897
1082
  throw error;
898
1083
  }
899
- this.logger?.warn(warningMessage);
1084
+ this.logger?.warn?.(warningMessage);
900
1085
  const response = await this.confirmationCallback?.(confirmationMessage);
901
1086
  if (!response) {
902
1087
  throw error;
@@ -907,9 +1092,11 @@ class Upgrader {
907
1092
  const json = createJSONTransformAPI(packageJSON);
908
1093
  const dependencies = json.get("dependencies", {});
909
1094
  const strapiDependencies = this.getScopedStrapiDependencies(dependencies);
910
- 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
+ );
911
1098
  strapiDependencies.forEach(
912
- (dependency) => this.logger?.debug(`- ${dependency[0]} (${dependency[1]} -> ${this.target})`)
1099
+ (dependency) => this.logger?.debug?.(`- ${dependency[0]} (${dependency[1]} -> ${this.target})`)
913
1100
  );
914
1101
  if (strapiDependencies.length === 0) {
915
1102
  return;
@@ -917,7 +1104,7 @@ class Upgrader {
917
1104
  strapiDependencies.forEach(([name]) => json.set(`dependencies.${name}`, this.target.raw));
918
1105
  const updatedPackageJSON = json.root();
919
1106
  if (this.isDry) {
920
- 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")})`);
921
1108
  return;
922
1109
  }
923
1110
  await saveJSON(packageJSONPath, updatedPackageJSON);
@@ -937,9 +1124,9 @@ class Upgrader {
937
1124
  async installDependencies() {
938
1125
  const projectPath = this.project.cwd;
939
1126
  const packageManagerName = await utils.packageManager.getPreferred(projectPath);
940
- this.logger?.debug(`Using ${highlight(packageManagerName)} as package manager`);
1127
+ this.logger?.debug?.(`Using ${highlight(packageManagerName)} as package manager`);
941
1128
  if (this.isDry) {
942
- 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")})`);
943
1130
  return;
944
1131
  }
945
1132
  await utils.packageManager.installDependencies(projectPath, packageManagerName, {
@@ -958,23 +1145,28 @@ class Upgrader {
958
1145
  }
959
1146
  const resolveNPMTarget = (project, target, npmPackage) => {
960
1147
  if (isSemverInstance(target)) {
961
- return npmPackage.findVersion(target);
1148
+ const version2 = npmPackage.findVersion(target);
1149
+ if (!version2) {
1150
+ throw new NPMCandidateNotFoundError(target);
1151
+ }
1152
+ return version2;
962
1153
  }
963
1154
  if (isSemVerReleaseType(target)) {
964
1155
  const range = rangeFromVersions(project.strapiVersion, target);
965
1156
  const npmVersionsMatches = npmPackage.findVersionsInRange(range);
966
- 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;
967
1162
  }
968
- return void 0;
1163
+ throw new NPMCandidateNotFoundError(target);
969
1164
  };
970
1165
  const upgraderFactory = (project, target, npmPackage) => {
971
- const targetedNPMVersion = resolveNPMTarget(project, target, npmPackage);
972
- if (!targetedNPMVersion) {
973
- throw new Error(`Couldn't find a matching version in the NPM registry for "${target}"`);
974
- }
975
- const semverTarget = semVerFactory(targetedNPMVersion.version);
1166
+ const npmTarget = resolveNPMTarget(project, target, npmPackage);
1167
+ const semverTarget = semVerFactory(npmTarget.version);
976
1168
  if (semver__default.default.eq(semverTarget, project.strapiVersion)) {
977
- throw new Error(`The project is already on ${version(semverTarget)}`);
1169
+ throw new Error(`The project is already using v${semverTarget}`);
978
1170
  }
979
1171
  return new Upgrader(project, semverTarget, npmPackage);
980
1172
  };
@@ -985,96 +1177,205 @@ const constants = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
985
1177
  __proto__: null,
986
1178
  STRAPI_PACKAGE_NAME
987
1179
  }, Symbol.toStringTag, { value: "Module" }));
988
- const index$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1180
+ const index$6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
989
1181
  __proto__: null,
990
1182
  constants,
991
1183
  upgraderFactory
992
1184
  }, Symbol.toStringTag, { value: "Module" }));
993
- const NPM_REGISTRY_URL = "https://registry.npmjs.org";
994
- class Package {
1185
+ class Requirement {
1186
+ isRequired;
995
1187
  name;
996
- packageURL;
997
- npmPackage;
998
- constructor(name) {
1188
+ testCallback;
1189
+ children;
1190
+ constructor(name, testCallback, isRequired) {
999
1191
  this.name = name;
1000
- this.packageURL = `${NPM_REGISTRY_URL}/${name}`;
1001
- this.npmPackage = null;
1192
+ this.testCallback = testCallback;
1193
+ this.isRequired = isRequired ?? true;
1194
+ this.children = [];
1002
1195
  }
1003
- get isLoaded() {
1004
- return this.npmPackage !== null;
1196
+ setChildren(children) {
1197
+ this.children = children;
1198
+ return this;
1005
1199
  }
1006
- assertPackageIsLoaded(npmPackage) {
1007
- assert__default.default(this.isLoaded, "The package is not loaded yet");
1200
+ addChild(child) {
1201
+ this.children.push(child);
1202
+ return this;
1008
1203
  }
1009
- getVersionsDict() {
1010
- this.assertPackageIsLoaded(this.npmPackage);
1011
- return this.npmPackage.versions;
1204
+ asOptional() {
1205
+ const newInstance = requirementFactory(this.name, this.testCallback, false);
1206
+ newInstance.setChildren(this.children);
1207
+ return newInstance;
1012
1208
  }
1013
- getVersionsAsList() {
1014
- this.assertPackageIsLoaded(this.npmPackage);
1015
- 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;
1016
1213
  }
1017
- findVersionsInRange(range) {
1018
- const versions = this.getVersionsAsList();
1019
- 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
+ }
1020
1227
  }
1021
- findVersion(version2) {
1022
- const versions = this.getVersionsAsList();
1023
- 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
+ }
1024
1245
  }
1025
- async refresh() {
1026
- const response = await fetch(this.packageURL);
1027
- assert__default.default(response.ok, `Request failed for ${this.packageURL}`);
1028
- this.npmPackage = await response.json();
1029
- 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
+ }
1030
1262
  }
1031
- versionExists(version2) {
1032
- 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
+ }
1033
1274
  }
1034
- }
1035
- 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
+ };
1036
1330
  const upgrade = async (options) => {
1037
1331
  const timer = timerFactory();
1038
1332
  const { logger, codemodsTarget } = options;
1039
1333
  const cwd = path__default.default.resolve(options.cwd ?? process.cwd());
1040
1334
  const project = projectFactory(cwd);
1335
+ logger.debug(projectDetails(project));
1336
+ if (!isApplicationProject(project)) {
1337
+ throw new Error(
1338
+ `The "${options.target}" upgrade can only be run on a Strapi project; for plugins, please use "codemods".`
1339
+ );
1340
+ }
1341
+ logger.debug(
1342
+ `Application: VERSION=${version(project.packageJSON.version)}; STRAPI_VERSION=${version(project.strapiVersion)}`
1343
+ );
1041
1344
  const npmPackage = npmPackageFactory(STRAPI_PACKAGE_NAME);
1042
1345
  await npmPackage.refresh();
1043
1346
  const upgrader = upgraderFactory(project, options.target, npmPackage).dry(options.dry ?? false).onConfirm(options.confirm ?? null).setLogger(logger);
1044
1347
  if (codemodsTarget !== void 0) {
1045
1348
  upgrader.overrideCodemodsTarget(codemodsTarget);
1046
1349
  }
1047
- if (options.target === ReleaseType.Major) {
1048
- upgrader.addRequirement(REQUIRE_AVAILABLE_NEXT_MAJOR).addRequirement(REQUIRE_LATEST_FOR_CURRENT_MAJOR);
1049
- }
1050
- upgrader.addRequirement(REQUIRE_GIT.asOptional());
1350
+ await runUpgradePrompts(upgrader, options);
1351
+ addUpgradeRequirements(upgrader, options);
1051
1352
  const upgradeReport = await upgrader.upgrade();
1052
1353
  if (!upgradeReport.success) {
1053
1354
  throw upgradeReport.error;
1054
1355
  }
1055
1356
  timer.stop();
1056
- logger.info(`Completed in ${durationMs(timer.elapsedMs)}`);
1357
+ logger.info(`Completed in ${durationMs(timer.elapsedMs)}ms`);
1057
1358
  };
1058
- const codemods = async (options) => {
1059
- const timer = timerFactory();
1060
- const { logger } = options;
1061
- const cwd = path__default.default.resolve(options.cwd ?? process.cwd());
1062
- const project = projectFactory(cwd);
1063
- const range = getRangeFromTarget(project.strapiVersion, options.target);
1064
- const codemodRunner = codemodRunnerFactory(project, range).dry(options.dry ?? false).onSelectCodemods(options.selectCodemods ?? null).setLogger(logger);
1065
- const executionReport = await codemodRunner.run();
1066
- if (!executionReport.success) {
1067
- throw executionReport.error;
1359
+ const runUpgradePrompts = async (upgrader, options) => {
1360
+ if (options.target === ReleaseType.Latest) {
1361
+ await latest(upgrader, options);
1068
1362
  }
1069
- timer.stop();
1070
- logger.info(`Completed in ${timer.elapsedMs}`);
1071
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());
1072
1371
  const getRangeFromTarget = (currentVersion, target) => {
1073
1372
  if (isSemverInstance(target)) {
1074
1373
  return rangeFactory(target);
1075
1374
  }
1076
1375
  const { major, minor, patch } = currentVersion;
1077
1376
  switch (target) {
1377
+ case ReleaseType.Latest:
1378
+ throw new Error("Can't use <latest> to create a codemods range: not implemented");
1078
1379
  case ReleaseType.Major:
1079
1380
  return rangeFactory(`${major}`);
1080
1381
  case ReleaseType.Minor:
@@ -1085,9 +1386,60 @@ const getRangeFromTarget = (currentVersion, target) => {
1085
1386
  throw new Error(`Invalid target set: ${target}`);
1086
1387
  }
1087
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
+ };
1088
1439
  const index$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1089
1440
  __proto__: null,
1090
- codemods,
1441
+ listCodemods,
1442
+ runCodemods,
1091
1443
  upgrade
1092
1444
  }, Symbol.toStringTag, { value: "Module" }));
1093
1445
  class Logger {
@@ -1181,18 +1533,18 @@ const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
1181
1533
  }, Symbol.toStringTag, { value: "Module" }));
1182
1534
  const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1183
1535
  __proto__: null,
1184
- codemod: index$7,
1185
- codemodRepository: index$6,
1536
+ codemod: index$8,
1537
+ codemodRepository: index$7,
1186
1538
  error: index$9,
1187
- f: index$8,
1539
+ f: index$f,
1188
1540
  fileScanner: index$d,
1189
1541
  logger: index$3,
1190
1542
  project: index$a,
1191
1543
  report: index$2,
1192
- requirement: index$g,
1544
+ requirement: index$5,
1193
1545
  runner: index$1,
1194
- timer: index$f,
1195
- upgrader: index$5,
1546
+ timer: index$g,
1547
+ upgrader: index$6,
1196
1548
  version: index$e
1197
1549
  }, Symbol.toStringTag, { value: "Module" }));
1198
1550
  exports.modules = index;