@strapi/upgrade 0.0.0-experimental.f6c00790e260ea5a9b6b86abac5fea02b05d569c → 0.0.0-experimental.f736daceb203b33d9ed36a1149e21f2814d1935c

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 (33) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +391 -295
  3. package/dist/cli.js.map +1 -1
  4. package/dist/index.js +401 -310
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +397 -307
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/modules/error/utils.d.ts +8 -0
  9. package/dist/modules/error/utils.d.ts.map +1 -1
  10. package/dist/modules/file-scanner/scanner.d.ts.map +1 -1
  11. package/dist/modules/project/project.d.ts.map +1 -1
  12. package/dist/modules/upgrader/types.d.ts +6 -0
  13. package/dist/modules/upgrader/types.d.ts.map +1 -1
  14. package/dist/modules/upgrader/upgrader.d.ts +4 -0
  15. package/dist/modules/upgrader/upgrader.d.ts.map +1 -1
  16. package/dist/modules/version/range.d.ts.map +1 -1
  17. package/dist/modules/version/types.d.ts +2 -1
  18. package/dist/modules/version/types.d.ts.map +1 -1
  19. package/dist/tasks/codemods/utils.d.ts.map +1 -1
  20. package/dist/tasks/upgrade/prompts/index.d.ts +2 -0
  21. package/dist/tasks/upgrade/prompts/index.d.ts.map +1 -0
  22. package/dist/tasks/upgrade/prompts/latest.d.ts +9 -0
  23. package/dist/tasks/upgrade/prompts/latest.d.ts.map +1 -0
  24. package/dist/tasks/upgrade/requirements/major.d.ts.map +1 -1
  25. package/dist/tasks/upgrade/upgrade.d.ts.map +1 -1
  26. package/package.json +10 -11
  27. package/resources/codemods/5.0.0/deprecate-helper-plugin.code.ts +192 -0
  28. package/resources/codemods/5.0.0/sqlite3-to-better-sqlite3.json.ts +0 -1
  29. package/resources/codemods/5.1.0/dependency-better-sqlite3.json.ts +48 -0
  30. package/resources/utils/change-import.ts +23 -10
  31. package/resources/codemods/5.0.0/change-useAPIErrorHandler-import.code.ts +0 -21
  32. package/resources/codemods/5.0.0/nocontent-migrate-to-emptystatelayout.code.ts +0 -30
  33. package/resources/codemods/5.0.0/useRBAC-hook-import-change.code.ts +0 -21
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();
210
101
  };
102
+ const codemodList = (codemods) => {
103
+ const rows = codemods.map((codemod, index2) => {
104
+ const fIndex = chalk__default.default.grey(index2);
105
+ const fVersion = chalk__default.default.magenta(codemod.version);
106
+ const fKind = chalk__default.default.yellow(codemod.kind);
107
+ const fName = chalk__default.default.blue(codemod.format());
108
+ const fUID = codemodUID(codemod.uid);
109
+ return [fIndex, fVersion, fKind, fName, fUID];
110
+ });
111
+ const table = new CliTable3__default.default({
112
+ style: { compact: true },
113
+ head: [
114
+ chalk__default.default.bold.grey("N°"),
115
+ chalk__default.default.bold.magenta("Version"),
116
+ chalk__default.default.bold.yellow("Kind"),
117
+ chalk__default.default.bold.blue("Name"),
118
+ chalk__default.default.bold.cyan("UID")
119
+ ]
120
+ });
121
+ table.push(...rows);
122
+ return table.toString();
123
+ };
124
+ const durationMs = (elapsedMs) => {
125
+ const elapsedSeconds = (elapsedMs / ONE_SECOND_MS).toFixed(3);
126
+ return `${elapsedSeconds}s`;
127
+ };
128
+ const index$f = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
129
+ __proto__: null,
130
+ codemodList,
131
+ codemodUID,
132
+ durationMs,
133
+ highlight,
134
+ path,
135
+ projectDetails,
136
+ projectType,
137
+ reports,
138
+ transform,
139
+ upgradeStep,
140
+ version,
141
+ versionRange
142
+ }, Symbol.toStringTag, { value: "Module" }));
143
+ const NPM_REGISTRY_URL = "https://registry.npmjs.org";
211
144
  var ReleaseType = /* @__PURE__ */ ((ReleaseType2) => {
212
145
  ReleaseType2["Major"] = "major";
213
146
  ReleaseType2["Minor"] = "minor";
214
147
  ReleaseType2["Patch"] = "patch";
148
+ ReleaseType2["Latest"] = "latest";
215
149
  return ReleaseType2;
216
150
  })(ReleaseType || {});
217
151
  const types = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
@@ -237,17 +171,20 @@ const rangeFactory = (range) => {
237
171
  };
238
172
  const rangeFromReleaseType = (current, identifier) => {
239
173
  switch (identifier) {
240
- case ReleaseType.Major: {
241
- const nextMajor = semver__default.default.inc(current, "major");
242
- return rangeFactory(`>${current.raw} <=${nextMajor}`);
174
+ case ReleaseType.Latest: {
175
+ return rangeFactory(`>${current.raw}`);
243
176
  }
244
- case ReleaseType.Patch: {
245
- const minor = semver__default.default.inc(current, "minor");
246
- return rangeFactory(`>${current.raw} <${minor}`);
177
+ case ReleaseType.Major: {
178
+ const nextMajor = semVerFactory(current.raw).inc("major");
179
+ return rangeFactory(`>${current.raw} <=${nextMajor.major}`);
247
180
  }
248
181
  case ReleaseType.Minor: {
249
- const major = semver__default.default.inc(current, "major");
250
- return rangeFactory(`>${current.raw} <${major}`);
182
+ const nextMajor = semVerFactory(current.raw).inc("major");
183
+ return rangeFactory(`>${current.raw} <${nextMajor.raw}`);
184
+ }
185
+ case ReleaseType.Patch: {
186
+ const nextMinor = semVerFactory(current.raw).inc("minor");
187
+ return rangeFactory(`>${current.raw} <${nextMinor.raw}`);
251
188
  }
252
189
  default: {
253
190
  throw new Error("Not implemented");
@@ -281,13 +218,57 @@ const index$e = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
281
218
  rangeFromVersions,
282
219
  semVerFactory
283
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);
284
263
  class FileScanner {
285
264
  cwd;
286
265
  constructor(cwd) {
287
266
  this.cwd = cwd;
288
267
  }
289
268
  scan(patterns) {
290
- const filenames = glob.glob.sync(patterns, { cwd: this.cwd });
269
+ const filenames = fastglob__default.default.sync(patterns, {
270
+ cwd: this.cwd
271
+ });
291
272
  return filenames.map((filename) => path__default.default.join(this.cwd, filename));
292
273
  }
293
274
  }
@@ -325,6 +306,46 @@ const index$c = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
325
306
  __proto__: null,
326
307
  codeRunnerFactory
327
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
+ };
328
349
  const transformJSON = async (codemodPath, paths, config) => {
329
350
  const { dry } = config;
330
351
  const startTime = process.hrtime();
@@ -501,6 +522,8 @@ class AppProject extends Project {
501
522
  return [
502
523
  // App default files
503
524
  `./${allowedRootPaths}/**/*.${allowedExtensions}`,
525
+ `!./**/node_modules/**/*`,
526
+ `!./**/dist/**/*`,
504
527
  // Root package.json file
505
528
  PROJECT_PACKAGE_JSON
506
529
  ];
@@ -574,6 +597,8 @@ class PluginProject extends Project {
574
597
  return [
575
598
  // Plugin default files
576
599
  `./${allowedRootPaths}/**/*.${allowedExtensions}`,
600
+ `!./**/node_modules/**/*`,
601
+ `!./**/dist/**/*`,
577
602
  // Root package.json file
578
603
  PROJECT_PACKAGE_JSON,
579
604
  // Plugin root files
@@ -629,6 +654,18 @@ class UnexpectedError extends Error {
629
654
  super("Unexpected Error");
630
655
  }
631
656
  }
657
+ class NPMCandidateNotFoundError extends Error {
658
+ target;
659
+ constructor(target, message = `Couldn't find a valid NPM candidate for "${target}"`) {
660
+ super(message);
661
+ this.target = target;
662
+ }
663
+ }
664
+ class AbortedError extends Error {
665
+ constructor(message = "Upgrade aborted") {
666
+ super(message);
667
+ }
668
+ }
632
669
  const unknownToError = (e) => {
633
670
  if (e instanceof Error) {
634
671
  return e;
@@ -640,93 +677,11 @@ const unknownToError = (e) => {
640
677
  };
641
678
  const index$9 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
642
679
  __proto__: null,
680
+ AbortedError,
681
+ NPMCandidateNotFoundError,
643
682
  UnexpectedError,
644
683
  unknownToError
645
684
  }, Symbol.toStringTag, { value: "Module" }));
646
- const path = (path2) => chalk__default.default.blue(path2);
647
- const version = (version2) => {
648
- return chalk__default.default.italic.yellow(`v${version2}`);
649
- };
650
- const codemodUID = (uid) => {
651
- return chalk__default.default.bold.cyan(uid);
652
- };
653
- const projectDetails = (project) => {
654
- return `Project: TYPE=${projectType(project.type)}; CWD=${path(project.cwd)}; PATHS=${project.paths.map(path)}`;
655
- };
656
- const projectType = (type) => chalk__default.default.cyan(type);
657
- const versionRange = (range) => chalk__default.default.italic.yellow(range.raw);
658
- const transform = (transformFilePath) => chalk__default.default.cyan(transformFilePath);
659
- const highlight = (arg) => chalk__default.default.bold.underline(arg);
660
- const upgradeStep = (text, step) => {
661
- return chalk__default.default.bold(`(${step[0]}/${step[1]}) ${text}...`);
662
- };
663
- const reports = (reports2) => {
664
- const rows = reports2.map(({ codemod, report }, i) => {
665
- const fIndex = chalk__default.default.grey(i);
666
- const fVersion = chalk__default.default.magenta(codemod.version);
667
- const fKind = chalk__default.default.yellow(codemod.kind);
668
- const fFormattedTransformPath = chalk__default.default.cyan(codemod.format());
669
- const fTimeElapsed = i === 0 ? `${report.timeElapsed}s ${chalk__default.default.dim.italic("(cold start)")}` : `${report.timeElapsed}s`;
670
- const fAffected = report.ok > 0 ? chalk__default.default.green(report.ok) : chalk__default.default.grey(0);
671
- const fUnchanged = report.ok === 0 ? chalk__default.default.red(report.nochange) : chalk__default.default.grey(report.nochange);
672
- return [fIndex, fVersion, fKind, fFormattedTransformPath, fAffected, fUnchanged, fTimeElapsed];
673
- });
674
- const table = new CliTable3__default.default({
675
- style: { compact: true },
676
- head: [
677
- chalk__default.default.bold.grey("N°"),
678
- chalk__default.default.bold.magenta("Version"),
679
- chalk__default.default.bold.yellow("Kind"),
680
- chalk__default.default.bold.cyan("Name"),
681
- chalk__default.default.bold.green("Affected"),
682
- chalk__default.default.bold.red("Unchanged"),
683
- chalk__default.default.bold.blue("Duration")
684
- ]
685
- });
686
- table.push(...rows);
687
- return table.toString();
688
- };
689
- const codemodList = (codemods) => {
690
- const rows = codemods.map((codemod, index2) => {
691
- const fIndex = chalk__default.default.grey(index2);
692
- const fVersion = chalk__default.default.magenta(codemod.version);
693
- const fKind = chalk__default.default.yellow(codemod.kind);
694
- const fName = chalk__default.default.blue(codemod.format());
695
- const fUID = codemodUID(codemod.uid);
696
- return [fIndex, fVersion, fKind, fName, fUID];
697
- });
698
- const table = new CliTable3__default.default({
699
- style: { compact: true },
700
- head: [
701
- chalk__default.default.bold.grey("N°"),
702
- chalk__default.default.bold.magenta("Version"),
703
- chalk__default.default.bold.yellow("Kind"),
704
- chalk__default.default.bold.blue("Name"),
705
- chalk__default.default.bold.cyan("UID")
706
- ]
707
- });
708
- table.push(...rows);
709
- return table.toString();
710
- };
711
- const durationMs = (elapsedMs) => {
712
- const elapsedSeconds = (elapsedMs / ONE_SECOND_MS).toFixed(3);
713
- return `${elapsedSeconds}s`;
714
- };
715
- const index$8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
716
- __proto__: null,
717
- codemodList,
718
- codemodUID,
719
- durationMs,
720
- highlight,
721
- path,
722
- projectDetails,
723
- projectType,
724
- reports,
725
- transform,
726
- upgradeStep,
727
- version,
728
- versionRange
729
- }, Symbol.toStringTag, { value: "Module" }));
730
685
  const CODEMOD_CODE_SUFFIX = "code";
731
686
  const CODEMOD_JSON_SUFFIX = "json";
732
687
  const CODEMOD_ALLOWED_SUFFIXES = [CODEMOD_CODE_SUFFIX, CODEMOD_JSON_SUFFIX];
@@ -779,7 +734,7 @@ class Codemod {
779
734
  }
780
735
  }
781
736
  const codemodFactory = (options) => new Codemod(options);
782
- const index$7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
737
+ const index$8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
783
738
  __proto__: null,
784
739
  codemodFactory,
785
740
  constants: constants$2
@@ -891,7 +846,7 @@ const parseCodemodKindFromFilename = (filename) => {
891
846
  const codemodRepositoryFactory = (cwd = INTERNAL_CODEMODS_DIRECTORY) => {
892
847
  return new CodemodRepository(cwd);
893
848
  };
894
- const index$6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
849
+ const index$7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
895
850
  __proto__: null,
896
851
  codemodRepositoryFactory,
897
852
  constants: constants$1
@@ -1004,6 +959,15 @@ class Upgrader {
1004
959
  this.logger = null;
1005
960
  this.confirmationCallback = null;
1006
961
  }
962
+ getNPMPackage() {
963
+ return this.npmPackage;
964
+ }
965
+ getProject() {
966
+ return this.project;
967
+ }
968
+ getTarget() {
969
+ return semVerFactory(this.target.raw);
970
+ }
1007
971
  setRequirements(requirements) {
1008
972
  this.requirements = requirements;
1009
973
  return this;
@@ -1085,6 +1049,12 @@ class Upgrader {
1085
1049
  }
1086
1050
  return successReport();
1087
1051
  }
1052
+ async confirm(message) {
1053
+ if (typeof this.confirmationCallback !== "function") {
1054
+ return true;
1055
+ }
1056
+ return this.confirmationCallback(message);
1057
+ }
1088
1058
  async checkRequirements(requirements, context) {
1089
1059
  for (const requirement of requirements) {
1090
1060
  const { pass, error } = await requirement.test(context);
@@ -1156,7 +1126,7 @@ class Upgrader {
1156
1126
  const packageManagerName = await utils.packageManager.getPreferred(projectPath);
1157
1127
  this.logger?.debug?.(`Using ${highlight(packageManagerName)} as package manager`);
1158
1128
  if (this.isDry) {
1159
- 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")})`);
1160
1130
  return;
1161
1131
  }
1162
1132
  await utils.packageManager.installDependencies(projectPath, packageManagerName, {
@@ -1175,23 +1145,28 @@ class Upgrader {
1175
1145
  }
1176
1146
  const resolveNPMTarget = (project, target, npmPackage) => {
1177
1147
  if (isSemverInstance(target)) {
1178
- return npmPackage.findVersion(target);
1148
+ const version2 = npmPackage.findVersion(target);
1149
+ if (!version2) {
1150
+ throw new NPMCandidateNotFoundError(target);
1151
+ }
1152
+ return version2;
1179
1153
  }
1180
1154
  if (isSemVerReleaseType(target)) {
1181
1155
  const range = rangeFromVersions(project.strapiVersion, target);
1182
1156
  const npmVersionsMatches = npmPackage.findVersionsInRange(range);
1183
- 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;
1184
1162
  }
1185
- return void 0;
1163
+ throw new NPMCandidateNotFoundError(target);
1186
1164
  };
1187
1165
  const upgraderFactory = (project, target, npmPackage) => {
1188
- const targetedNPMVersion = resolveNPMTarget(project, target, npmPackage);
1189
- if (!targetedNPMVersion) {
1190
- throw new Error(`Couldn't find a matching version in the NPM registry for "${target}"`);
1191
- }
1192
- const semverTarget = semVerFactory(targetedNPMVersion.version);
1166
+ const npmTarget = resolveNPMTarget(project, target, npmPackage);
1167
+ const semverTarget = semVerFactory(npmTarget.version);
1193
1168
  if (semver__default.default.eq(semverTarget, project.strapiVersion)) {
1194
- throw new Error(`The project is already on ${version(semverTarget)}`);
1169
+ throw new Error(`The project is already using v${semverTarget}`);
1195
1170
  }
1196
1171
  return new Upgrader(project, semverTarget, npmPackage);
1197
1172
  };
@@ -1202,54 +1177,156 @@ const constants = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
1202
1177
  __proto__: null,
1203
1178
  STRAPI_PACKAGE_NAME
1204
1179
  }, Symbol.toStringTag, { value: "Module" }));
1205
- const index$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1180
+ const index$6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1206
1181
  __proto__: null,
1207
1182
  constants,
1208
1183
  upgraderFactory
1209
1184
  }, Symbol.toStringTag, { value: "Module" }));
1210
- const NPM_REGISTRY_URL = "https://registry.npmjs.org";
1211
- class Package {
1185
+ class Requirement {
1186
+ isRequired;
1212
1187
  name;
1213
- packageURL;
1214
- npmPackage;
1215
- constructor(name) {
1188
+ testCallback;
1189
+ children;
1190
+ constructor(name, testCallback, isRequired) {
1216
1191
  this.name = name;
1217
- this.packageURL = `${NPM_REGISTRY_URL}/${name}`;
1218
- this.npmPackage = null;
1192
+ this.testCallback = testCallback;
1193
+ this.isRequired = isRequired ?? true;
1194
+ this.children = [];
1219
1195
  }
1220
- get isLoaded() {
1221
- return this.npmPackage !== null;
1196
+ setChildren(children) {
1197
+ this.children = children;
1198
+ return this;
1222
1199
  }
1223
- assertPackageIsLoaded(npmPackage) {
1224
- assert__default.default(this.isLoaded, "The package is not loaded yet");
1200
+ addChild(child) {
1201
+ this.children.push(child);
1202
+ return this;
1225
1203
  }
1226
- getVersionsDict() {
1227
- this.assertPackageIsLoaded(this.npmPackage);
1228
- return this.npmPackage.versions;
1204
+ asOptional() {
1205
+ const newInstance = requirementFactory(this.name, this.testCallback, false);
1206
+ newInstance.setChildren(this.children);
1207
+ return newInstance;
1229
1208
  }
1230
- getVersionsAsList() {
1231
- this.assertPackageIsLoaded(this.npmPackage);
1232
- 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;
1233
1213
  }
1234
- findVersionsInRange(range) {
1235
- const versions = this.getVersionsAsList();
1236
- 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
+ }
1237
1227
  }
1238
- findVersion(version2) {
1239
- const versions = this.getVersionsAsList();
1240
- 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
+ }
1241
1245
  }
1242
- async refresh() {
1243
- const response = await fetch(this.packageURL);
1244
- assert__default.default(response.ok, `Request failed for ${this.packageURL}`);
1245
- this.npmPackage = await response.json();
1246
- 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
+ }
1247
1262
  }
1248
- versionExists(version2) {
1249
- 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
+ }
1250
1274
  }
1251
- }
1252
- 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
+ };
1253
1330
  const upgrade = async (options) => {
1254
1331
  const timer = timerFactory();
1255
1332
  const { logger, codemodsTarget } = options;
@@ -1261,22 +1338,34 @@ const upgrade = async (options) => {
1261
1338
  `The "${options.target}" upgrade can only be run on a Strapi project; for plugins, please use "codemods".`
1262
1339
  );
1263
1340
  }
1341
+ logger.debug(
1342
+ `Application: VERSION=${version(project.packageJSON.version)}; STRAPI_VERSION=${version(project.strapiVersion)}`
1343
+ );
1264
1344
  const npmPackage = npmPackageFactory(STRAPI_PACKAGE_NAME);
1265
1345
  await npmPackage.refresh();
1266
1346
  const upgrader = upgraderFactory(project, options.target, npmPackage).dry(options.dry ?? false).onConfirm(options.confirm ?? null).setLogger(logger);
1267
1347
  if (codemodsTarget !== void 0) {
1268
1348
  upgrader.overrideCodemodsTarget(codemodsTarget);
1269
1349
  }
1270
- if (options.target === ReleaseType.Major) {
1271
- upgrader.addRequirement(REQUIRE_AVAILABLE_NEXT_MAJOR).addRequirement(REQUIRE_LATEST_FOR_CURRENT_MAJOR);
1272
- }
1273
- upgrader.addRequirement(REQUIRE_GIT.asOptional());
1350
+ await runUpgradePrompts(upgrader, options);
1351
+ addUpgradeRequirements(upgrader, options);
1274
1352
  const upgradeReport = await upgrader.upgrade();
1275
1353
  if (!upgradeReport.success) {
1276
1354
  throw upgradeReport.error;
1277
1355
  }
1278
1356
  timer.stop();
1279
- logger.info(`Completed in ${durationMs(timer.elapsedMs)}`);
1357
+ logger.info(`Completed in ${durationMs(timer.elapsedMs)}ms`);
1358
+ };
1359
+ const runUpgradePrompts = async (upgrader, options) => {
1360
+ if (options.target === ReleaseType.Latest) {
1361
+ await latest(upgrader, options);
1362
+ }
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());
1280
1369
  };
1281
1370
  const resolvePath = (cwd) => path__default.default.resolve(cwd ?? process.cwd());
1282
1371
  const getRangeFromTarget = (currentVersion, target) => {
@@ -1285,6 +1374,8 @@ const getRangeFromTarget = (currentVersion, target) => {
1285
1374
  }
1286
1375
  const { major, minor, patch } = currentVersion;
1287
1376
  switch (target) {
1377
+ case ReleaseType.Latest:
1378
+ throw new Error("Can't use <latest> to create a codemods range: not implemented");
1288
1379
  case ReleaseType.Major:
1289
1380
  return rangeFactory(`${major}`);
1290
1381
  case ReleaseType.Minor:
@@ -1442,18 +1533,18 @@ const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
1442
1533
  }, Symbol.toStringTag, { value: "Module" }));
1443
1534
  const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1444
1535
  __proto__: null,
1445
- codemod: index$7,
1446
- codemodRepository: index$6,
1536
+ codemod: index$8,
1537
+ codemodRepository: index$7,
1447
1538
  error: index$9,
1448
- f: index$8,
1539
+ f: index$f,
1449
1540
  fileScanner: index$d,
1450
1541
  logger: index$3,
1451
1542
  project: index$a,
1452
1543
  report: index$2,
1453
- requirement: index$g,
1544
+ requirement: index$5,
1454
1545
  runner: index$1,
1455
- timer: index$f,
1456
- upgrader: index$5,
1546
+ timer: index$g,
1547
+ upgrader: index$6,
1457
1548
  version: index$e
1458
1549
  }, Symbol.toStringTag, { value: "Module" }));
1459
1550
  exports.modules = index;