@strapi/upgrade 0.0.0-experimental.e60ec1829240dae21c1e1d29076681c322288813 → 0.0.0-experimental.eba25ec571b091c6bde1104eb6c753debdf15462

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 (43) hide show
  1. package/LICENSE +19 -4
  2. package/README.md +1 -1
  3. package/dist/cli.js +444 -318
  4. package/dist/cli.js.map +1 -1
  5. package/dist/index.js +467 -344
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +463 -341
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/modules/error/utils.d.ts +8 -0
  10. package/dist/modules/error/utils.d.ts.map +1 -1
  11. package/dist/modules/file-scanner/scanner.d.ts.map +1 -1
  12. package/dist/modules/format/formats.d.ts +2 -1
  13. package/dist/modules/format/formats.d.ts.map +1 -1
  14. package/dist/modules/project/constants.d.ts +6 -5
  15. package/dist/modules/project/constants.d.ts.map +1 -1
  16. package/dist/modules/project/project.d.ts +16 -2
  17. package/dist/modules/project/project.d.ts.map +1 -1
  18. package/dist/modules/project/types.d.ts +3 -0
  19. package/dist/modules/project/types.d.ts.map +1 -1
  20. package/dist/modules/runner/json/transform.d.ts.map +1 -1
  21. package/dist/modules/upgrader/types.d.ts +6 -0
  22. package/dist/modules/upgrader/types.d.ts.map +1 -1
  23. package/dist/modules/upgrader/upgrader.d.ts +4 -0
  24. package/dist/modules/upgrader/upgrader.d.ts.map +1 -1
  25. package/dist/modules/version/range.d.ts.map +1 -1
  26. package/dist/modules/version/types.d.ts +2 -1
  27. package/dist/modules/version/types.d.ts.map +1 -1
  28. package/dist/tasks/codemods/utils.d.ts.map +1 -1
  29. package/dist/tasks/upgrade/prompts/index.d.ts +2 -0
  30. package/dist/tasks/upgrade/prompts/index.d.ts.map +1 -0
  31. package/dist/tasks/upgrade/prompts/latest.d.ts +9 -0
  32. package/dist/tasks/upgrade/prompts/latest.d.ts.map +1 -0
  33. package/dist/tasks/upgrade/requirements/major.d.ts.map +1 -1
  34. package/dist/tasks/upgrade/upgrade.d.ts.map +1 -1
  35. package/package.json +7 -7
  36. package/resources/codemods/5.0.0/comment-out-lifecycle-files.code.ts +63 -0
  37. package/resources/codemods/5.0.0/dependency-upgrade-react-and-react-dom.json.ts +67 -0
  38. package/resources/codemods/5.0.0/dependency-upgrade-styled-components.json.ts +49 -0
  39. package/resources/codemods/5.0.0/deprecate-helper-plugin.code.ts +192 -0
  40. package/resources/codemods/5.0.0/sqlite3-to-better-sqlite3.json.ts +0 -1
  41. package/resources/codemods/5.1.0/dependency-better-sqlite3.json.ts +48 -0
  42. package/resources/utils/change-import.ts +118 -0
  43. package/resources/utils/replace-jsx.ts +49 -0
package/dist/index.mjs CHANGED
@@ -1,128 +1,15 @@
1
1
  import path$1 from "node:path";
2
- import simpleGit from "simple-git";
2
+ import CliTable3 from "cli-table3";
3
3
  import chalk from "chalk";
4
+ import assert from "node:assert";
4
5
  import semver from "semver";
5
- import { packageManager } from "@strapi/utils";
6
- import { cloneDeep, get, has, merge, set, omit, isEqual, groupBy, size } from "lodash/fp";
7
6
  import fse from "fs-extra";
8
- import assert from "node:assert";
9
- import { glob } from "glob";
7
+ import fastglob from "fast-glob";
10
8
  import { run } from "jscodeshift/src/Runner";
9
+ import { cloneDeep, get, has, merge, set, omit, isEqual, groupBy, size } from "lodash/fp";
11
10
  import { register } from "esbuild-register/dist/node";
12
- import CliTable3 from "cli-table3";
13
- class Requirement {
14
- isRequired;
15
- name;
16
- testCallback;
17
- children;
18
- constructor(name, testCallback, isRequired) {
19
- this.name = name;
20
- this.testCallback = testCallback;
21
- this.isRequired = isRequired ?? true;
22
- this.children = [];
23
- }
24
- setChildren(children) {
25
- this.children = children;
26
- return this;
27
- }
28
- addChild(child) {
29
- this.children.push(child);
30
- return this;
31
- }
32
- asOptional() {
33
- const newInstance = requirementFactory(this.name, this.testCallback, false);
34
- newInstance.setChildren(this.children);
35
- return newInstance;
36
- }
37
- asRequired() {
38
- const newInstance = requirementFactory(this.name, this.testCallback, true);
39
- newInstance.setChildren(this.children);
40
- return newInstance;
41
- }
42
- async test(context) {
43
- try {
44
- await this.testCallback?.(context);
45
- return ok();
46
- } catch (e) {
47
- if (e instanceof Error) {
48
- return errored(e);
49
- }
50
- if (typeof e === "string") {
51
- return errored(new Error(e));
52
- }
53
- return errored(new Error("Unknown error"));
54
- }
55
- }
56
- }
57
- const ok = () => ({ pass: true, error: null });
58
- const errored = (error) => ({ pass: false, error });
59
- const requirementFactory = (name, testCallback, isRequired) => new Requirement(name, testCallback, isRequired);
60
- const index$g = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
61
- __proto__: null,
62
- requirementFactory
63
- }, Symbol.toStringTag, { value: "Module" }));
64
- const REQUIRE_AVAILABLE_NEXT_MAJOR = requirementFactory(
65
- "REQUIRE_AVAILABLE_NEXT_MAJOR",
66
- (context) => {
67
- const { project, target } = context;
68
- const currentMajor = project.strapiVersion.major;
69
- const targetedMajor = target.major;
70
- if (targetedMajor === currentMajor) {
71
- throw new Error(`You're already on the latest major version (v${currentMajor})`);
72
- }
73
- }
74
- );
75
- const REQUIRE_LATEST_FOR_CURRENT_MAJOR = requirementFactory(
76
- "REQUIRE_LATEST_FOR_CURRENT_MAJOR",
77
- (context) => {
78
- const { project, target, npmVersionsMatches } = context;
79
- if (npmVersionsMatches.length !== 1) {
80
- const invalidVersions = npmVersionsMatches.slice(0, -1);
81
- const invalidVersionsAsSemVer = invalidVersions.map((v) => v.version);
82
- const nbInvalidVersions = npmVersionsMatches.length;
83
- const currentMajor = project.strapiVersion.major;
84
- throw new Error(
85
- `Doing a major upgrade requires to be on the latest v${currentMajor} version, but found ${nbInvalidVersions} versions between the current one and ${target}: ${invalidVersionsAsSemVer}`
86
- );
87
- }
88
- }
89
- );
90
- const REQUIRE_GIT_CLEAN_REPOSITORY = requirementFactory(
91
- "REQUIRE_GIT_CLEAN_REPOSITORY",
92
- async (context) => {
93
- const git = simpleGit({ baseDir: context.project.cwd });
94
- const status = await git.status();
95
- if (!status.isClean()) {
96
- throw new Error(
97
- "Repository is not clean. Please commit or stash any changes before upgrading"
98
- );
99
- }
100
- }
101
- );
102
- const REQUIRE_GIT_REPOSITORY = requirementFactory(
103
- "REQUIRE_GIT_REPOSITORY",
104
- async (context) => {
105
- const git = simpleGit({ baseDir: context.project.cwd });
106
- const isRepo = await git.checkIsRepo();
107
- if (!isRepo) {
108
- throw new Error("Not a git repository (or any of the parent directories)");
109
- }
110
- }
111
- ).addChild(REQUIRE_GIT_CLEAN_REPOSITORY.asOptional());
112
- const REQUIRE_GIT_INSTALLED = requirementFactory(
113
- "REQUIRE_GIT_INSTALLED",
114
- async (context) => {
115
- const git = simpleGit({ baseDir: context.project.cwd });
116
- try {
117
- await git.version();
118
- } catch {
119
- throw new Error("Git is not installed");
120
- }
121
- }
122
- ).addChild(REQUIRE_GIT_REPOSITORY.asOptional());
123
- const REQUIRE_GIT = requirementFactory("REQUIRE_GIT", null).addChild(
124
- REQUIRE_GIT_INSTALLED.asOptional()
125
- );
11
+ import { packageManager } from "@strapi/utils";
12
+ import simpleGit from "simple-git";
126
13
  class Timer {
127
14
  interval;
128
15
  constructor() {
@@ -153,55 +40,101 @@ const constants$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
153
40
  __proto__: null,
154
41
  ONE_SECOND_MS
155
42
  }, Symbol.toStringTag, { value: "Module" }));
156
- const index$f = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
43
+ const index$g = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
157
44
  __proto__: null,
158
45
  constants: constants$4,
159
46
  timerFactory
160
47
  }, Symbol.toStringTag, { value: "Module" }));
161
- class JSONTransformAPI {
162
- json;
163
- constructor(json) {
164
- this.json = cloneDeep(json);
165
- }
166
- get(path2, defaultValue) {
167
- if (!path2) {
168
- return this.root();
169
- }
170
- return cloneDeep(get(path2, this.json) ?? defaultValue);
171
- }
172
- has(path2) {
173
- return has(path2, this.json);
174
- }
175
- merge(other) {
176
- this.json = merge(other, this.json);
177
- return this;
178
- }
179
- root() {
180
- return cloneDeep(this.json);
181
- }
182
- set(path2, value) {
183
- this.json = set(path2, value, this.json);
184
- return this;
185
- }
186
- remove(path2) {
187
- this.json = omit(path2, this.json);
188
- return this;
189
- }
190
- }
191
- const createJSONTransformAPI = (object) => new JSONTransformAPI(object);
192
- const readJSON = async (path2) => {
193
- const buffer = await fse.readFile(path2);
194
- return JSON.parse(buffer.toString());
48
+ const path = (path2) => chalk.blue(path2);
49
+ const version = (version2) => {
50
+ return chalk.italic.yellow(`v${version2}`);
195
51
  };
196
- const saveJSON = async (path2, json) => {
197
- const jsonAsString = `${JSON.stringify(json, null, 2)}
198
- `;
199
- await fse.writeFile(path2, jsonAsString);
52
+ const codemodUID = (uid) => {
53
+ return chalk.bold.cyan(uid);
54
+ };
55
+ const projectDetails = (project) => {
56
+ return `Project: TYPE=${projectType(project.type)}; CWD=${path(project.cwd)}; PATHS=${project.paths.map(path)}`;
57
+ };
58
+ const projectType = (type) => chalk.cyan(type);
59
+ const versionRange = (range) => chalk.italic.yellow(range.raw);
60
+ const transform = (transformFilePath) => chalk.cyan(transformFilePath);
61
+ const highlight = (arg) => chalk.bold.underline(arg);
62
+ const upgradeStep = (text, step) => {
63
+ return chalk.bold(`(${step[0]}/${step[1]}) ${text}...`);
64
+ };
65
+ const reports = (reports2) => {
66
+ const rows = reports2.map(({ codemod, report }, i) => {
67
+ const fIndex = chalk.grey(i);
68
+ const fVersion = chalk.magenta(codemod.version);
69
+ const fKind = chalk.yellow(codemod.kind);
70
+ const fFormattedTransformPath = chalk.cyan(codemod.format());
71
+ const fTimeElapsed = i === 0 ? `${report.timeElapsed}s ${chalk.dim.italic("(cold start)")}` : `${report.timeElapsed}s`;
72
+ const fAffected = report.ok > 0 ? chalk.green(report.ok) : chalk.grey(0);
73
+ const fUnchanged = report.ok === 0 ? chalk.red(report.nochange) : chalk.grey(report.nochange);
74
+ return [fIndex, fVersion, fKind, fFormattedTransformPath, fAffected, fUnchanged, fTimeElapsed];
75
+ });
76
+ const table = new CliTable3({
77
+ style: { compact: true },
78
+ head: [
79
+ chalk.bold.grey("N°"),
80
+ chalk.bold.magenta("Version"),
81
+ chalk.bold.yellow("Kind"),
82
+ chalk.bold.cyan("Name"),
83
+ chalk.bold.green("Affected"),
84
+ chalk.bold.red("Unchanged"),
85
+ chalk.bold.blue("Duration")
86
+ ]
87
+ });
88
+ table.push(...rows);
89
+ return table.toString();
90
+ };
91
+ const codemodList = (codemods) => {
92
+ const rows = codemods.map((codemod, index2) => {
93
+ const fIndex = chalk.grey(index2);
94
+ const fVersion = chalk.magenta(codemod.version);
95
+ const fKind = chalk.yellow(codemod.kind);
96
+ const fName = chalk.blue(codemod.format());
97
+ const fUID = codemodUID(codemod.uid);
98
+ return [fIndex, fVersion, fKind, fName, fUID];
99
+ });
100
+ const table = new CliTable3({
101
+ style: { compact: true },
102
+ head: [
103
+ chalk.bold.grey("N°"),
104
+ chalk.bold.magenta("Version"),
105
+ chalk.bold.yellow("Kind"),
106
+ chalk.bold.blue("Name"),
107
+ chalk.bold.cyan("UID")
108
+ ]
109
+ });
110
+ table.push(...rows);
111
+ return table.toString();
200
112
  };
113
+ const durationMs = (elapsedMs) => {
114
+ const elapsedSeconds = (elapsedMs / ONE_SECOND_MS).toFixed(3);
115
+ return `${elapsedSeconds}s`;
116
+ };
117
+ const index$f = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
118
+ __proto__: null,
119
+ codemodList,
120
+ codemodUID,
121
+ durationMs,
122
+ highlight,
123
+ path,
124
+ projectDetails,
125
+ projectType,
126
+ reports,
127
+ transform,
128
+ upgradeStep,
129
+ version,
130
+ versionRange
131
+ }, Symbol.toStringTag, { value: "Module" }));
132
+ const NPM_REGISTRY_URL = "https://registry.npmjs.org";
201
133
  var ReleaseType = /* @__PURE__ */ ((ReleaseType2) => {
202
134
  ReleaseType2["Major"] = "major";
203
135
  ReleaseType2["Minor"] = "minor";
204
136
  ReleaseType2["Patch"] = "patch";
137
+ ReleaseType2["Latest"] = "latest";
205
138
  return ReleaseType2;
206
139
  })(ReleaseType || {});
207
140
  const types = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
@@ -227,17 +160,20 @@ const rangeFactory = (range) => {
227
160
  };
228
161
  const rangeFromReleaseType = (current, identifier) => {
229
162
  switch (identifier) {
230
- case ReleaseType.Major: {
231
- const nextMajor = semver.inc(current, "major");
232
- return rangeFactory(`>${current.raw} <=${nextMajor}`);
163
+ case ReleaseType.Latest: {
164
+ return rangeFactory(`>${current.raw}`);
233
165
  }
234
- case ReleaseType.Patch: {
235
- const minor = semver.inc(current, "minor");
236
- return rangeFactory(`>${current.raw} <${minor}`);
166
+ case ReleaseType.Major: {
167
+ const nextMajor = semVerFactory(current.raw).inc("major");
168
+ return rangeFactory(`>${current.raw} <=${nextMajor.major}`);
237
169
  }
238
170
  case ReleaseType.Minor: {
239
- const major = semver.inc(current, "major");
240
- return rangeFactory(`>${current.raw} <${major}`);
171
+ const nextMajor = semVerFactory(current.raw).inc("major");
172
+ return rangeFactory(`>${current.raw} <${nextMajor.raw}`);
173
+ }
174
+ case ReleaseType.Patch: {
175
+ const nextMinor = semVerFactory(current.raw).inc("minor");
176
+ return rangeFactory(`>${current.raw} <${nextMinor.raw}`);
241
177
  }
242
178
  default: {
243
179
  throw new Error("Not implemented");
@@ -271,13 +207,57 @@ const index$e = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
271
207
  rangeFromVersions,
272
208
  semVerFactory
273
209
  }, Symbol.toStringTag, { value: "Module" }));
210
+ class Package {
211
+ name;
212
+ packageURL;
213
+ npmPackage;
214
+ constructor(name) {
215
+ this.name = name;
216
+ this.packageURL = `${NPM_REGISTRY_URL}/${name}`;
217
+ this.npmPackage = null;
218
+ }
219
+ get isLoaded() {
220
+ return this.npmPackage !== null;
221
+ }
222
+ assertPackageIsLoaded(npmPackage) {
223
+ assert(this.isLoaded, "The package is not loaded yet");
224
+ }
225
+ getVersionsDict() {
226
+ this.assertPackageIsLoaded(this.npmPackage);
227
+ return this.npmPackage.versions;
228
+ }
229
+ getVersionsAsList() {
230
+ this.assertPackageIsLoaded(this.npmPackage);
231
+ return Object.values(this.npmPackage.versions);
232
+ }
233
+ findVersionsInRange(range) {
234
+ const versions = this.getVersionsAsList();
235
+ return versions.filter((v) => range.test(v.version)).filter((v) => isLiteralSemVer(v.version)).sort((v1, v2) => semver.compare(v1.version, v2.version));
236
+ }
237
+ findVersion(version2) {
238
+ const versions = this.getVersionsAsList();
239
+ return versions.find((npmVersion) => semver.eq(npmVersion.version, version2));
240
+ }
241
+ async refresh() {
242
+ const response = await fetch(this.packageURL);
243
+ assert(response.ok, `Request failed for ${this.packageURL}`);
244
+ this.npmPackage = await response.json();
245
+ return this;
246
+ }
247
+ versionExists(version2) {
248
+ return this.findVersion(version2) !== void 0;
249
+ }
250
+ }
251
+ const npmPackageFactory = (name) => new Package(name);
274
252
  class FileScanner {
275
253
  cwd;
276
254
  constructor(cwd) {
277
255
  this.cwd = cwd;
278
256
  }
279
257
  scan(patterns) {
280
- const filenames = glob.sync(patterns, { cwd: this.cwd });
258
+ const filenames = fastglob.sync(patterns, {
259
+ cwd: this.cwd
260
+ });
281
261
  return filenames.map((filename) => path$1.join(this.cwd, filename));
282
262
  }
283
263
  }
@@ -315,6 +295,46 @@ const index$c = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
315
295
  __proto__: null,
316
296
  codeRunnerFactory
317
297
  }, Symbol.toStringTag, { value: "Module" }));
298
+ class JSONTransformAPI {
299
+ json;
300
+ constructor(json) {
301
+ this.json = cloneDeep(json);
302
+ }
303
+ get(path2, defaultValue) {
304
+ if (!path2) {
305
+ return this.root();
306
+ }
307
+ return cloneDeep(get(path2, this.json) ?? defaultValue);
308
+ }
309
+ has(path2) {
310
+ return has(path2, this.json);
311
+ }
312
+ merge(other) {
313
+ this.json = merge(other, this.json);
314
+ return this;
315
+ }
316
+ root() {
317
+ return cloneDeep(this.json);
318
+ }
319
+ set(path2, value) {
320
+ this.json = set(path2, value, this.json);
321
+ return this;
322
+ }
323
+ remove(path2) {
324
+ this.json = omit(path2, this.json);
325
+ return this;
326
+ }
327
+ }
328
+ const createJSONTransformAPI = (object) => new JSONTransformAPI(object);
329
+ const readJSON = async (path2) => {
330
+ const buffer = await fse.readFile(path2);
331
+ return JSON.parse(buffer.toString());
332
+ };
333
+ const saveJSON = async (path2, json) => {
334
+ const jsonAsString = `${JSON.stringify(json, null, 2)}
335
+ `;
336
+ await fse.writeFile(path2, jsonAsString);
337
+ };
318
338
  const transformJSON = async (codemodPath, paths, config) => {
319
339
  const { dry } = config;
320
340
  const startTime = process.hrtime();
@@ -326,7 +346,11 @@ const transformJSON = async (codemodPath, paths, config) => {
326
346
  timeElapsed: "",
327
347
  stats: {}
328
348
  };
329
- const esbuildOptions = { extensions: [".js", ".mjs", ".ts"] };
349
+ const esbuildOptions = {
350
+ extensions: [".js", ".mjs", ".ts"],
351
+ hookIgnoreNodeModules: false,
352
+ hookMatcher: isEqual(codemodPath)
353
+ };
330
354
  const { unregister } = register(esbuildOptions);
331
355
  const module = require(codemodPath);
332
356
  unregister();
@@ -371,8 +395,10 @@ const index$b = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
371
395
  jsonRunnerFactory
372
396
  }, Symbol.toStringTag, { value: "Module" }));
373
397
  const PROJECT_PACKAGE_JSON = "package.json";
374
- const PROJECT_DEFAULT_ALLOWED_ROOT_PATHS = ["src", "config", "public"];
375
- const PROJECT_DEFAULT_CODE_EXTENSIONS = [
398
+ const PROJECT_APP_ALLOWED_ROOT_PATHS = ["src", "config", "public"];
399
+ const PROJECT_PLUGIN_ALLOWED_ROOT_PATHS = ["admin", "server"];
400
+ const PROJECT_PLUGIN_ROOT_FILES = ["strapi-admin.js", "strapi-server.js"];
401
+ const PROJECT_CODE_EXTENSIONS = [
376
402
  // Source files
377
403
  "js",
378
404
  "mjs",
@@ -381,22 +407,19 @@ const PROJECT_DEFAULT_CODE_EXTENSIONS = [
381
407
  "jsx",
382
408
  "tsx"
383
409
  ];
384
- const PROJECT_DEFAULT_JSON_EXTENSIONS = ["json"];
385
- const PROJECT_DEFAULT_ALLOWED_EXTENSIONS = [
386
- ...PROJECT_DEFAULT_CODE_EXTENSIONS,
387
- ...PROJECT_DEFAULT_JSON_EXTENSIONS
388
- ];
389
- const PROJECT_DEFAULT_PATTERNS = ["package.json"];
410
+ const PROJECT_JSON_EXTENSIONS = ["json"];
411
+ const PROJECT_ALLOWED_EXTENSIONS = [...PROJECT_CODE_EXTENSIONS, ...PROJECT_JSON_EXTENSIONS];
390
412
  const SCOPED_STRAPI_PACKAGE_PREFIX = "@strapi/";
391
413
  const STRAPI_DEPENDENCY_NAME = `${SCOPED_STRAPI_PACKAGE_PREFIX}strapi`;
392
414
  const constants$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
393
415
  __proto__: null,
394
- PROJECT_DEFAULT_ALLOWED_EXTENSIONS,
395
- PROJECT_DEFAULT_ALLOWED_ROOT_PATHS,
396
- PROJECT_DEFAULT_CODE_EXTENSIONS,
397
- PROJECT_DEFAULT_JSON_EXTENSIONS,
398
- PROJECT_DEFAULT_PATTERNS,
416
+ PROJECT_ALLOWED_EXTENSIONS,
417
+ PROJECT_APP_ALLOWED_ROOT_PATHS,
418
+ PROJECT_CODE_EXTENSIONS,
419
+ PROJECT_JSON_EXTENSIONS,
399
420
  PROJECT_PACKAGE_JSON,
421
+ PROJECT_PLUGIN_ALLOWED_ROOT_PATHS,
422
+ PROJECT_PLUGIN_ROOT_FILES,
400
423
  SCOPED_STRAPI_PACKAGE_PREFIX,
401
424
  STRAPI_DEPENDENCY_NAME
402
425
  }, Symbol.toStringTag, { value: "Module" }));
@@ -406,11 +429,13 @@ class Project {
406
429
  files;
407
430
  packageJSONPath;
408
431
  packageJSON;
409
- constructor(cwd) {
432
+ paths;
433
+ constructor(cwd, config) {
410
434
  if (!fse.pathExistsSync(cwd)) {
411
435
  throw new Error(`ENOENT: no such file or directory, access '${cwd}'`);
412
436
  }
413
437
  this.cwd = cwd;
438
+ this.paths = config.paths;
414
439
  this.refresh();
415
440
  }
416
441
  getFilesByExtensions(extensions) {
@@ -438,12 +463,8 @@ class Project {
438
463
  return reports2;
439
464
  }
440
465
  createProjectCodemodsRunners(dry = false) {
441
- const jsonExtensions = PROJECT_DEFAULT_JSON_EXTENSIONS.map(
442
- (ext) => `.${ext}`
443
- );
444
- const codeExtensions = PROJECT_DEFAULT_CODE_EXTENSIONS.map(
445
- (ext) => `.${ext}`
446
- );
466
+ const jsonExtensions = PROJECT_JSON_EXTENSIONS.map((ext) => `.${ext}`);
467
+ const codeExtensions = PROJECT_CODE_EXTENSIONS.map((ext) => `.${ext}`);
447
468
  const jsonFiles = this.getFilesByExtensions(jsonExtensions);
448
469
  const codeFiles = this.getFilesByExtensions(codeExtensions);
449
470
  const codeRunner = codeRunnerFactory(codeFiles, {
@@ -451,7 +472,7 @@ class Project {
451
472
  parser: "ts",
452
473
  runInBand: true,
453
474
  babel: true,
454
- extensions: PROJECT_DEFAULT_CODE_EXTENSIONS.join(","),
475
+ extensions: PROJECT_CODE_EXTENSIONS.join(","),
455
476
  // Don't output any log coming from the runner
456
477
  print: false,
457
478
  silent: true,
@@ -472,23 +493,32 @@ class Project {
472
493
  this.packageJSON = JSON.parse(packageJSONBuffer.toString());
473
494
  }
474
495
  refreshProjectFiles() {
475
- const allowedRootPaths = formatGlobCollectionPattern(
476
- PROJECT_DEFAULT_ALLOWED_ROOT_PATHS
477
- );
478
- const allowedExtensions = formatGlobCollectionPattern(
479
- PROJECT_DEFAULT_ALLOWED_EXTENSIONS
480
- );
481
- const projectFilesPattern = `./${allowedRootPaths}/**/*.${allowedExtensions}`;
482
- const patterns = [projectFilesPattern, ...PROJECT_DEFAULT_PATTERNS];
483
496
  const scanner = fileScannerFactory(this.cwd);
484
- this.files = scanner.scan(patterns);
497
+ this.files = scanner.scan(this.paths);
485
498
  }
486
499
  }
487
500
  class AppProject extends Project {
488
501
  strapiVersion;
489
502
  type = "application";
503
+ /**
504
+ * Returns an array of allowed file paths for a Strapi application
505
+ *
506
+ * The resulting paths include app default files and the root package.json file.
507
+ */
508
+ static get paths() {
509
+ const allowedRootPaths = formatGlobCollectionPattern(PROJECT_APP_ALLOWED_ROOT_PATHS);
510
+ const allowedExtensions = formatGlobCollectionPattern(PROJECT_ALLOWED_EXTENSIONS);
511
+ return [
512
+ // App default files
513
+ `./${allowedRootPaths}/**/*.${allowedExtensions}`,
514
+ `!./**/node_modules/**/*`,
515
+ `!./**/dist/**/*`,
516
+ // Root package.json file
517
+ PROJECT_PACKAGE_JSON
518
+ ];
519
+ }
490
520
  constructor(cwd) {
491
- super(cwd);
521
+ super(cwd, { paths: AppProject.paths });
492
522
  this.refreshStrapiVersion();
493
523
  }
494
524
  refresh() {
@@ -543,6 +573,30 @@ const formatGlobCollectionPattern = (collection) => {
543
573
  };
544
574
  class PluginProject extends Project {
545
575
  type = "plugin";
576
+ /**
577
+ * Returns an array of allowed file paths for a Strapi plugin
578
+ *
579
+ * The resulting paths include plugin default files, the root package.json file, and plugin-specific files.
580
+ */
581
+ static get paths() {
582
+ const allowedRootPaths = formatGlobCollectionPattern(
583
+ PROJECT_PLUGIN_ALLOWED_ROOT_PATHS
584
+ );
585
+ const allowedExtensions = formatGlobCollectionPattern(PROJECT_ALLOWED_EXTENSIONS);
586
+ return [
587
+ // Plugin default files
588
+ `./${allowedRootPaths}/**/*.${allowedExtensions}`,
589
+ `!./**/node_modules/**/*`,
590
+ `!./**/dist/**/*`,
591
+ // Root package.json file
592
+ PROJECT_PACKAGE_JSON,
593
+ // Plugin root files
594
+ ...PROJECT_PLUGIN_ROOT_FILES
595
+ ];
596
+ }
597
+ constructor(cwd) {
598
+ super(cwd, { paths: PluginProject.paths });
599
+ }
546
600
  }
547
601
  const isPlugin = (cwd) => {
548
602
  const packageJSONPath = path$1.join(cwd, PROJECT_PACKAGE_JSON);
@@ -557,10 +611,7 @@ const isPlugin = (cwd) => {
557
611
  };
558
612
  const projectFactory = (cwd) => {
559
613
  fse.accessSync(cwd);
560
- if (isPlugin(cwd)) {
561
- return new PluginProject(cwd);
562
- }
563
- return new AppProject(cwd);
614
+ return isPlugin(cwd) ? new PluginProject(cwd) : new AppProject(cwd);
564
615
  };
565
616
  const isPluginProject = (project) => {
566
617
  return project instanceof PluginProject;
@@ -592,6 +643,18 @@ class UnexpectedError extends Error {
592
643
  super("Unexpected Error");
593
644
  }
594
645
  }
646
+ class NPMCandidateNotFoundError extends Error {
647
+ target;
648
+ constructor(target, message = `Couldn't find a valid NPM candidate for "${target}"`) {
649
+ super(message);
650
+ this.target = target;
651
+ }
652
+ }
653
+ class AbortedError extends Error {
654
+ constructor(message = "Upgrade aborted") {
655
+ super(message);
656
+ }
657
+ }
595
658
  const unknownToError = (e) => {
596
659
  if (e instanceof Error) {
597
660
  return e;
@@ -603,89 +666,11 @@ const unknownToError = (e) => {
603
666
  };
604
667
  const index$9 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
605
668
  __proto__: null,
669
+ AbortedError,
670
+ NPMCandidateNotFoundError,
606
671
  UnexpectedError,
607
672
  unknownToError
608
673
  }, Symbol.toStringTag, { value: "Module" }));
609
- const path = (path2) => chalk.blue(path2);
610
- const version = (version2) => {
611
- return chalk.italic.yellow(`v${version2}`);
612
- };
613
- const codemodUID = (uid) => {
614
- return chalk.bold.cyan(uid);
615
- };
616
- const projectType = (type) => chalk.cyan(type);
617
- const versionRange = (range) => chalk.italic.yellow(range.raw);
618
- const transform = (transformFilePath) => chalk.cyan(transformFilePath);
619
- const highlight = (arg) => chalk.bold.underline(arg);
620
- const upgradeStep = (text, step) => {
621
- return chalk.bold(`(${step[0]}/${step[1]}) ${text}...`);
622
- };
623
- const reports = (reports2) => {
624
- const rows = reports2.map(({ codemod, report }, i) => {
625
- const fIndex = chalk.grey(i);
626
- const fVersion = chalk.magenta(codemod.version);
627
- const fKind = chalk.yellow(codemod.kind);
628
- const fFormattedTransformPath = chalk.cyan(codemod.format());
629
- const fTimeElapsed = i === 0 ? `${report.timeElapsed}s ${chalk.dim.italic("(cold start)")}` : `${report.timeElapsed}s`;
630
- const fAffected = report.ok > 0 ? chalk.green(report.ok) : chalk.grey(0);
631
- const fUnchanged = report.ok === 0 ? chalk.red(report.nochange) : chalk.grey(report.nochange);
632
- return [fIndex, fVersion, fKind, fFormattedTransformPath, fAffected, fUnchanged, fTimeElapsed];
633
- });
634
- const table = new CliTable3({
635
- style: { compact: true },
636
- head: [
637
- chalk.bold.grey("N°"),
638
- chalk.bold.magenta("Version"),
639
- chalk.bold.yellow("Kind"),
640
- chalk.bold.cyan("Name"),
641
- chalk.bold.green("Affected"),
642
- chalk.bold.red("Unchanged"),
643
- chalk.bold.blue("Duration")
644
- ]
645
- });
646
- table.push(...rows);
647
- return table.toString();
648
- };
649
- const codemodList = (codemods) => {
650
- const rows = codemods.map((codemod, index2) => {
651
- const fIndex = chalk.grey(index2);
652
- const fVersion = chalk.magenta(codemod.version);
653
- const fKind = chalk.yellow(codemod.kind);
654
- const fName = chalk.blue(codemod.format());
655
- const fUID = codemodUID(codemod.uid);
656
- return [fIndex, fVersion, fKind, fName, fUID];
657
- });
658
- const table = new CliTable3({
659
- style: { compact: true },
660
- head: [
661
- chalk.bold.grey("N°"),
662
- chalk.bold.magenta("Version"),
663
- chalk.bold.yellow("Kind"),
664
- chalk.bold.blue("Name"),
665
- chalk.bold.cyan("UID")
666
- ]
667
- });
668
- table.push(...rows);
669
- return table.toString();
670
- };
671
- const durationMs = (elapsedMs) => {
672
- const elapsedSeconds = (elapsedMs / ONE_SECOND_MS).toFixed(3);
673
- return `${elapsedSeconds}s`;
674
- };
675
- const index$8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
676
- __proto__: null,
677
- codemodList,
678
- codemodUID,
679
- durationMs,
680
- highlight,
681
- path,
682
- projectType,
683
- reports,
684
- transform,
685
- upgradeStep,
686
- version,
687
- versionRange
688
- }, Symbol.toStringTag, { value: "Module" }));
689
674
  const CODEMOD_CODE_SUFFIX = "code";
690
675
  const CODEMOD_JSON_SUFFIX = "json";
691
676
  const CODEMOD_ALLOWED_SUFFIXES = [CODEMOD_CODE_SUFFIX, CODEMOD_JSON_SUFFIX];
@@ -738,7 +723,7 @@ class Codemod {
738
723
  }
739
724
  }
740
725
  const codemodFactory = (options) => new Codemod(options);
741
- const index$7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
726
+ const index$8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
742
727
  __proto__: null,
743
728
  codemodFactory,
744
729
  constants: constants$2
@@ -850,7 +835,7 @@ const parseCodemodKindFromFilename = (filename) => {
850
835
  const codemodRepositoryFactory = (cwd = INTERNAL_CODEMODS_DIRECTORY) => {
851
836
  return new CodemodRepository(cwd);
852
837
  };
853
- const index$6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
838
+ const index$7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
854
839
  __proto__: null,
855
840
  codemodRepositoryFactory,
856
841
  constants: constants$1
@@ -963,6 +948,15 @@ class Upgrader {
963
948
  this.logger = null;
964
949
  this.confirmationCallback = null;
965
950
  }
951
+ getNPMPackage() {
952
+ return this.npmPackage;
953
+ }
954
+ getProject() {
955
+ return this.project;
956
+ }
957
+ getTarget() {
958
+ return semVerFactory(this.target.raw);
959
+ }
966
960
  setRequirements(requirements) {
967
961
  this.requirements = requirements;
968
962
  return this;
@@ -1044,6 +1038,12 @@ class Upgrader {
1044
1038
  }
1045
1039
  return successReport();
1046
1040
  }
1041
+ async confirm(message) {
1042
+ if (typeof this.confirmationCallback !== "function") {
1043
+ return true;
1044
+ }
1045
+ return this.confirmationCallback(message);
1046
+ }
1047
1047
  async checkRequirements(requirements, context) {
1048
1048
  for (const requirement of requirements) {
1049
1049
  const { pass, error } = await requirement.test(context);
@@ -1115,7 +1115,7 @@ class Upgrader {
1115
1115
  const packageManagerName = await packageManager.getPreferred(projectPath);
1116
1116
  this.logger?.debug?.(`Using ${highlight(packageManagerName)} as package manager`);
1117
1117
  if (this.isDry) {
1118
- this.logger?.debug?.(`Skipping dependencies installation (${chalk.italic("dry mode")}`);
1118
+ this.logger?.debug?.(`Skipping dependencies installation (${chalk.italic("dry mode")})`);
1119
1119
  return;
1120
1120
  }
1121
1121
  await packageManager.installDependencies(projectPath, packageManagerName, {
@@ -1134,23 +1134,28 @@ class Upgrader {
1134
1134
  }
1135
1135
  const resolveNPMTarget = (project, target, npmPackage) => {
1136
1136
  if (isSemverInstance(target)) {
1137
- return npmPackage.findVersion(target);
1137
+ const version2 = npmPackage.findVersion(target);
1138
+ if (!version2) {
1139
+ throw new NPMCandidateNotFoundError(target);
1140
+ }
1141
+ return version2;
1138
1142
  }
1139
1143
  if (isSemVerReleaseType(target)) {
1140
1144
  const range = rangeFromVersions(project.strapiVersion, target);
1141
1145
  const npmVersionsMatches = npmPackage.findVersionsInRange(range);
1142
- return npmVersionsMatches.at(-1);
1146
+ const version2 = npmVersionsMatches.at(-1);
1147
+ if (!version2) {
1148
+ throw new NPMCandidateNotFoundError(range, `The project is already up-to-date (${target})`);
1149
+ }
1150
+ return version2;
1143
1151
  }
1144
- return void 0;
1152
+ throw new NPMCandidateNotFoundError(target);
1145
1153
  };
1146
1154
  const upgraderFactory = (project, target, npmPackage) => {
1147
- const targetedNPMVersion = resolveNPMTarget(project, target, npmPackage);
1148
- if (!targetedNPMVersion) {
1149
- throw new Error(`Couldn't find a matching version in the NPM registry for "${target}"`);
1150
- }
1151
- const semverTarget = semVerFactory(targetedNPMVersion.version);
1155
+ const npmTarget = resolveNPMTarget(project, target, npmPackage);
1156
+ const semverTarget = semVerFactory(npmTarget.version);
1152
1157
  if (semver.eq(semverTarget, project.strapiVersion)) {
1153
- throw new Error(`The project is already on ${version(semverTarget)}`);
1158
+ throw new Error(`The project is already using v${semverTarget}`);
1154
1159
  }
1155
1160
  return new Upgrader(project, semverTarget, npmPackage);
1156
1161
  };
@@ -1161,80 +1166,195 @@ const constants = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
1161
1166
  __proto__: null,
1162
1167
  STRAPI_PACKAGE_NAME
1163
1168
  }, Symbol.toStringTag, { value: "Module" }));
1164
- const index$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1169
+ const index$6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1165
1170
  __proto__: null,
1166
1171
  constants,
1167
1172
  upgraderFactory
1168
1173
  }, Symbol.toStringTag, { value: "Module" }));
1169
- const NPM_REGISTRY_URL = "https://registry.npmjs.org";
1170
- class Package {
1174
+ class Requirement {
1175
+ isRequired;
1171
1176
  name;
1172
- packageURL;
1173
- npmPackage;
1174
- constructor(name) {
1177
+ testCallback;
1178
+ children;
1179
+ constructor(name, testCallback, isRequired) {
1175
1180
  this.name = name;
1176
- this.packageURL = `${NPM_REGISTRY_URL}/${name}`;
1177
- this.npmPackage = null;
1181
+ this.testCallback = testCallback;
1182
+ this.isRequired = isRequired ?? true;
1183
+ this.children = [];
1178
1184
  }
1179
- get isLoaded() {
1180
- return this.npmPackage !== null;
1185
+ setChildren(children) {
1186
+ this.children = children;
1187
+ return this;
1181
1188
  }
1182
- assertPackageIsLoaded(npmPackage) {
1183
- assert(this.isLoaded, "The package is not loaded yet");
1189
+ addChild(child) {
1190
+ this.children.push(child);
1191
+ return this;
1184
1192
  }
1185
- getVersionsDict() {
1186
- this.assertPackageIsLoaded(this.npmPackage);
1187
- return this.npmPackage.versions;
1193
+ asOptional() {
1194
+ const newInstance = requirementFactory(this.name, this.testCallback, false);
1195
+ newInstance.setChildren(this.children);
1196
+ return newInstance;
1188
1197
  }
1189
- getVersionsAsList() {
1190
- this.assertPackageIsLoaded(this.npmPackage);
1191
- return Object.values(this.npmPackage.versions);
1198
+ asRequired() {
1199
+ const newInstance = requirementFactory(this.name, this.testCallback, true);
1200
+ newInstance.setChildren(this.children);
1201
+ return newInstance;
1192
1202
  }
1193
- findVersionsInRange(range) {
1194
- const versions = this.getVersionsAsList();
1195
- return versions.filter((v) => range.test(v.version)).filter((v) => isLiteralSemVer(v.version)).sort((v1, v2) => semver.compare(v1.version, v2.version));
1203
+ async test(context) {
1204
+ try {
1205
+ await this.testCallback?.(context);
1206
+ return ok();
1207
+ } catch (e) {
1208
+ if (e instanceof Error) {
1209
+ return errored(e);
1210
+ }
1211
+ if (typeof e === "string") {
1212
+ return errored(new Error(e));
1213
+ }
1214
+ return errored(new Error("Unknown error"));
1215
+ }
1196
1216
  }
1197
- findVersion(version2) {
1198
- const versions = this.getVersionsAsList();
1199
- return versions.find((npmVersion) => semver.eq(npmVersion.version, version2));
1217
+ }
1218
+ const ok = () => ({ pass: true, error: null });
1219
+ const errored = (error) => ({ pass: false, error });
1220
+ const requirementFactory = (name, testCallback, isRequired) => new Requirement(name, testCallback, isRequired);
1221
+ const index$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1222
+ __proto__: null,
1223
+ requirementFactory
1224
+ }, Symbol.toStringTag, { value: "Module" }));
1225
+ const REQUIRE_AVAILABLE_NEXT_MAJOR = requirementFactory(
1226
+ "REQUIRE_AVAILABLE_NEXT_MAJOR",
1227
+ (context) => {
1228
+ const { project, target } = context;
1229
+ const currentMajor = project.strapiVersion.major;
1230
+ const targetedMajor = target.major;
1231
+ if (targetedMajor === currentMajor) {
1232
+ throw new Error(`You're already on the latest major version (v${currentMajor})`);
1233
+ }
1200
1234
  }
1201
- async refresh() {
1202
- const response = await fetch(this.packageURL);
1203
- assert(response.ok, `Request failed for ${this.packageURL}`);
1204
- this.npmPackage = await response.json();
1205
- return this;
1235
+ );
1236
+ const REQUIRE_LATEST_FOR_CURRENT_MAJOR = requirementFactory(
1237
+ "REQUIRE_LATEST_FOR_CURRENT_MAJOR",
1238
+ (context) => {
1239
+ const { project, target, npmVersionsMatches } = context;
1240
+ const { major: currentMajor } = project.strapiVersion;
1241
+ const invalidMatches = npmVersionsMatches.filter(
1242
+ (match) => semVerFactory(match.version).major === currentMajor
1243
+ );
1244
+ if (invalidMatches.length > 0) {
1245
+ const invalidVersions = invalidMatches.map((match) => match.version);
1246
+ const invalidVersionsCount = invalidVersions.length;
1247
+ throw new Error(
1248
+ `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.`
1249
+ );
1250
+ }
1206
1251
  }
1207
- versionExists(version2) {
1208
- return this.findVersion(version2) !== void 0;
1252
+ );
1253
+ const REQUIRE_GIT_CLEAN_REPOSITORY = requirementFactory(
1254
+ "REQUIRE_GIT_CLEAN_REPOSITORY",
1255
+ async (context) => {
1256
+ const git = simpleGit({ baseDir: context.project.cwd });
1257
+ const status = await git.status();
1258
+ if (!status.isClean()) {
1259
+ throw new Error(
1260
+ "Repository is not clean. Please commit or stash any changes before upgrading"
1261
+ );
1262
+ }
1209
1263
  }
1210
- }
1211
- const npmPackageFactory = (name) => new Package(name);
1264
+ );
1265
+ const REQUIRE_GIT_REPOSITORY = requirementFactory(
1266
+ "REQUIRE_GIT_REPOSITORY",
1267
+ async (context) => {
1268
+ const git = simpleGit({ baseDir: context.project.cwd });
1269
+ const isRepo = await git.checkIsRepo();
1270
+ if (!isRepo) {
1271
+ throw new Error("Not a git repository (or any of the parent directories)");
1272
+ }
1273
+ }
1274
+ ).addChild(REQUIRE_GIT_CLEAN_REPOSITORY.asOptional());
1275
+ const REQUIRE_GIT_INSTALLED = requirementFactory(
1276
+ "REQUIRE_GIT_INSTALLED",
1277
+ async (context) => {
1278
+ const git = simpleGit({ baseDir: context.project.cwd });
1279
+ try {
1280
+ await git.version();
1281
+ } catch {
1282
+ throw new Error("Git is not installed");
1283
+ }
1284
+ }
1285
+ ).addChild(REQUIRE_GIT_REPOSITORY.asOptional());
1286
+ const REQUIRE_GIT = requirementFactory("REQUIRE_GIT", null).addChild(
1287
+ REQUIRE_GIT_INSTALLED.asOptional()
1288
+ );
1289
+ const latest = async (upgrader, options) => {
1290
+ if (options.target !== ReleaseType.Latest) {
1291
+ return;
1292
+ }
1293
+ const npmPackage = upgrader.getNPMPackage();
1294
+ const target = upgrader.getTarget();
1295
+ const project = upgrader.getProject();
1296
+ const { strapiVersion: current } = project;
1297
+ const fTargetMajor = highlight(`v${target.major}`);
1298
+ const fCurrentMajor = highlight(`v${current.major}`);
1299
+ const fTarget = version(target);
1300
+ const fCurrent = version(current);
1301
+ const isMajorUpgrade = target.major > current.major;
1302
+ if (isMajorUpgrade) {
1303
+ options.logger.warn(
1304
+ `Detected a major upgrade for the "${highlight(ReleaseType.Latest)}" tag: ${fCurrent} > ${fTarget}`
1305
+ );
1306
+ const newerPackageRelease = npmPackage.findVersionsInRange(rangeFactory(`>${current.raw} <${target.major}`)).at(-1);
1307
+ if (newerPackageRelease) {
1308
+ const fLatest = version(semVerFactory(newerPackageRelease.version));
1309
+ options.logger.warn(
1310
+ `It's recommended to first upgrade to the latest version of ${fCurrentMajor} (${fLatest}) before upgrading to ${fTargetMajor}.`
1311
+ );
1312
+ }
1313
+ const proceedAnyway = await upgrader.confirm(`I know what I'm doing. Proceed anyway!`);
1314
+ if (!proceedAnyway) {
1315
+ throw new AbortedError();
1316
+ }
1317
+ }
1318
+ };
1212
1319
  const upgrade = async (options) => {
1213
1320
  const timer = timerFactory();
1214
1321
  const { logger, codemodsTarget } = options;
1215
1322
  const cwd = path$1.resolve(options.cwd ?? process.cwd());
1216
1323
  const project = projectFactory(cwd);
1324
+ logger.debug(projectDetails(project));
1217
1325
  if (!isApplicationProject(project)) {
1218
1326
  throw new Error(
1219
1327
  `The "${options.target}" upgrade can only be run on a Strapi project; for plugins, please use "codemods".`
1220
1328
  );
1221
1329
  }
1330
+ logger.debug(
1331
+ `Application: VERSION=${version(project.packageJSON.version)}; STRAPI_VERSION=${version(project.strapiVersion)}`
1332
+ );
1222
1333
  const npmPackage = npmPackageFactory(STRAPI_PACKAGE_NAME);
1223
1334
  await npmPackage.refresh();
1224
1335
  const upgrader = upgraderFactory(project, options.target, npmPackage).dry(options.dry ?? false).onConfirm(options.confirm ?? null).setLogger(logger);
1225
1336
  if (codemodsTarget !== void 0) {
1226
1337
  upgrader.overrideCodemodsTarget(codemodsTarget);
1227
1338
  }
1228
- if (options.target === ReleaseType.Major) {
1229
- upgrader.addRequirement(REQUIRE_AVAILABLE_NEXT_MAJOR).addRequirement(REQUIRE_LATEST_FOR_CURRENT_MAJOR);
1230
- }
1231
- upgrader.addRequirement(REQUIRE_GIT.asOptional());
1339
+ await runUpgradePrompts(upgrader, options);
1340
+ addUpgradeRequirements(upgrader, options);
1232
1341
  const upgradeReport = await upgrader.upgrade();
1233
1342
  if (!upgradeReport.success) {
1234
1343
  throw upgradeReport.error;
1235
1344
  }
1236
1345
  timer.stop();
1237
- logger.info(`Completed in ${durationMs(timer.elapsedMs)}`);
1346
+ logger.info(`Completed in ${durationMs(timer.elapsedMs)}ms`);
1347
+ };
1348
+ const runUpgradePrompts = async (upgrader, options) => {
1349
+ if (options.target === ReleaseType.Latest) {
1350
+ await latest(upgrader, options);
1351
+ }
1352
+ };
1353
+ const addUpgradeRequirements = (upgrader, options) => {
1354
+ if (options.target === ReleaseType.Major) {
1355
+ upgrader.addRequirement(REQUIRE_AVAILABLE_NEXT_MAJOR).addRequirement(REQUIRE_LATEST_FOR_CURRENT_MAJOR);
1356
+ }
1357
+ upgrader.addRequirement(REQUIRE_GIT.asOptional());
1238
1358
  };
1239
1359
  const resolvePath = (cwd) => path$1.resolve(cwd ?? process.cwd());
1240
1360
  const getRangeFromTarget = (currentVersion, target) => {
@@ -1243,6 +1363,8 @@ const getRangeFromTarget = (currentVersion, target) => {
1243
1363
  }
1244
1364
  const { major, minor, patch } = currentVersion;
1245
1365
  switch (target) {
1366
+ case ReleaseType.Latest:
1367
+ throw new Error("Can't use <latest> to create a codemods range: not implemented");
1246
1368
  case ReleaseType.Major:
1247
1369
  return rangeFactory(`${major}`);
1248
1370
  case ReleaseType.Minor:
@@ -1268,7 +1390,7 @@ const runCodemods = async (options) => {
1268
1390
  const cwd = resolvePath(options.cwd);
1269
1391
  const project = projectFactory(cwd);
1270
1392
  const range = findRangeFromTarget(project, options.target);
1271
- logger.debug(`Project: ${projectType(project.type)} found in ${path(cwd)}`);
1393
+ logger.debug(projectDetails(project));
1272
1394
  logger.debug(`Range: set to ${versionRange(range)}`);
1273
1395
  const codemodRunner = codemodRunnerFactory(project, range).dry(options.dry ?? false).onSelectCodemods(options.selectCodemods ?? null).setLogger(logger);
1274
1396
  let report;
@@ -1289,7 +1411,7 @@ const listCodemods = async (options) => {
1289
1411
  const cwd = resolvePath(options.cwd);
1290
1412
  const project = projectFactory(cwd);
1291
1413
  const range = findRangeFromTarget(project, target);
1292
- logger.debug(`Project: ${projectType(project.type)} found in ${path(cwd)}`);
1414
+ logger.debug(projectDetails(project));
1293
1415
  logger.debug(`Range: set to ${versionRange(range)}`);
1294
1416
  const repo = codemodRepositoryFactory();
1295
1417
  repo.refresh();
@@ -1400,18 +1522,18 @@ const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
1400
1522
  }, Symbol.toStringTag, { value: "Module" }));
1401
1523
  const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1402
1524
  __proto__: null,
1403
- codemod: index$7,
1404
- codemodRepository: index$6,
1525
+ codemod: index$8,
1526
+ codemodRepository: index$7,
1405
1527
  error: index$9,
1406
- f: index$8,
1528
+ f: index$f,
1407
1529
  fileScanner: index$d,
1408
1530
  logger: index$3,
1409
1531
  project: index$a,
1410
1532
  report: index$2,
1411
- requirement: index$g,
1533
+ requirement: index$5,
1412
1534
  runner: index$1,
1413
- timer: index$f,
1414
- upgrader: index$5,
1535
+ timer: index$g,
1536
+ upgrader: index$6,
1415
1537
  version: index$e
1416
1538
  }, Symbol.toStringTag, { value: "Module" }));
1417
1539
  export {