@strapi/upgrade 0.0.0-experimental.fdacf4285d1cada9d94ab4dcd756c5362cba1b54 → 0.0.0-next.3c5400321681b66eb35ab84c11113a78c1d9386e

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.
package/dist/index.js CHANGED
@@ -1,139 +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
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
22
  const fastglob__default = /* @__PURE__ */ _interopDefault(fastglob);
23
- const CliTable3__default = /* @__PURE__ */ _interopDefault(CliTable3);
24
- class Requirement {
25
- isRequired;
26
- name;
27
- testCallback;
28
- children;
29
- constructor(name, testCallback, isRequired) {
30
- this.name = name;
31
- this.testCallback = testCallback;
32
- this.isRequired = isRequired ?? true;
33
- this.children = [];
34
- }
35
- setChildren(children) {
36
- this.children = children;
37
- return this;
38
- }
39
- addChild(child) {
40
- this.children.push(child);
41
- return this;
42
- }
43
- asOptional() {
44
- const newInstance = requirementFactory(this.name, this.testCallback, false);
45
- newInstance.setChildren(this.children);
46
- return newInstance;
47
- }
48
- asRequired() {
49
- const newInstance = requirementFactory(this.name, this.testCallback, true);
50
- newInstance.setChildren(this.children);
51
- return newInstance;
52
- }
53
- async test(context) {
54
- try {
55
- await this.testCallback?.(context);
56
- return ok();
57
- } catch (e) {
58
- if (e instanceof Error) {
59
- return errored(e);
60
- }
61
- if (typeof e === "string") {
62
- return errored(new Error(e));
63
- }
64
- return errored(new Error("Unknown error"));
65
- }
66
- }
67
- }
68
- const ok = () => ({ pass: true, error: null });
69
- const errored = (error) => ({ pass: false, error });
70
- const requirementFactory = (name, testCallback, isRequired) => new Requirement(name, testCallback, isRequired);
71
- const index$g = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
72
- __proto__: null,
73
- requirementFactory
74
- }, Symbol.toStringTag, { value: "Module" }));
75
- const REQUIRE_AVAILABLE_NEXT_MAJOR = requirementFactory(
76
- "REQUIRE_AVAILABLE_NEXT_MAJOR",
77
- (context) => {
78
- const { project, target } = context;
79
- const currentMajor = project.strapiVersion.major;
80
- const targetedMajor = target.major;
81
- if (targetedMajor === currentMajor) {
82
- throw new Error(`You're already on the latest major version (v${currentMajor})`);
83
- }
84
- }
85
- );
86
- const REQUIRE_LATEST_FOR_CURRENT_MAJOR = requirementFactory(
87
- "REQUIRE_LATEST_FOR_CURRENT_MAJOR",
88
- (context) => {
89
- const { project, target, npmVersionsMatches } = context;
90
- if (npmVersionsMatches.length !== 1) {
91
- const invalidVersions = npmVersionsMatches.slice(0, -1);
92
- const invalidVersionsAsSemVer = invalidVersions.map((v) => v.version);
93
- const nbInvalidVersions = npmVersionsMatches.length;
94
- const currentMajor = project.strapiVersion.major;
95
- throw new Error(
96
- `Doing a major upgrade requires to be on the latest v${currentMajor} version, but found ${nbInvalidVersions} versions between the current one and ${target}: ${invalidVersionsAsSemVer}`
97
- );
98
- }
99
- }
100
- );
101
- const REQUIRE_GIT_CLEAN_REPOSITORY = requirementFactory(
102
- "REQUIRE_GIT_CLEAN_REPOSITORY",
103
- async (context) => {
104
- const git = simpleGit__default.default({ baseDir: context.project.cwd });
105
- const status = await git.status();
106
- if (!status.isClean()) {
107
- throw new Error(
108
- "Repository is not clean. Please commit or stash any changes before upgrading"
109
- );
110
- }
111
- }
112
- );
113
- const REQUIRE_GIT_REPOSITORY = requirementFactory(
114
- "REQUIRE_GIT_REPOSITORY",
115
- async (context) => {
116
- const git = simpleGit__default.default({ baseDir: context.project.cwd });
117
- const isRepo = await git.checkIsRepo();
118
- if (!isRepo) {
119
- throw new Error("Not a git repository (or any of the parent directories)");
120
- }
121
- }
122
- ).addChild(REQUIRE_GIT_CLEAN_REPOSITORY.asOptional());
123
- const REQUIRE_GIT_INSTALLED = requirementFactory(
124
- "REQUIRE_GIT_INSTALLED",
125
- async (context) => {
126
- const git = simpleGit__default.default({ baseDir: context.project.cwd });
127
- try {
128
- await git.version();
129
- } catch {
130
- throw new Error("Git is not installed");
131
- }
132
- }
133
- ).addChild(REQUIRE_GIT_REPOSITORY.asOptional());
134
- const REQUIRE_GIT = requirementFactory("REQUIRE_GIT", null).addChild(
135
- REQUIRE_GIT_INSTALLED.asOptional()
136
- );
23
+ const simpleGit__default = /* @__PURE__ */ _interopDefault(simpleGit);
137
24
  class Timer {
138
25
  interval;
139
26
  constructor() {
@@ -164,51 +51,96 @@ const constants$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
164
51
  __proto__: null,
165
52
  ONE_SECOND_MS
166
53
  }, Symbol.toStringTag, { value: "Module" }));
167
- const index$f = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
54
+ const index$g = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
168
55
  __proto__: null,
169
56
  constants: constants$4,
170
57
  timerFactory
171
58
  }, Symbol.toStringTag, { value: "Module" }));
172
- class JSONTransformAPI {
173
- json;
174
- constructor(json) {
175
- this.json = fp.cloneDeep(json);
176
- }
177
- get(path2, defaultValue) {
178
- if (!path2) {
179
- return this.root();
180
- }
181
- return fp.cloneDeep(fp.get(path2, this.json) ?? defaultValue);
182
- }
183
- has(path2) {
184
- return fp.has(path2, this.json);
185
- }
186
- merge(other) {
187
- this.json = fp.merge(other, this.json);
188
- return this;
189
- }
190
- root() {
191
- return fp.cloneDeep(this.json);
192
- }
193
- set(path2, value) {
194
- this.json = fp.set(path2, value, this.json);
195
- return this;
196
- }
197
- remove(path2) {
198
- this.json = fp.omit(path2, this.json);
199
- return this;
200
- }
201
- }
202
- const createJSONTransformAPI = (object) => new JSONTransformAPI(object);
203
- const readJSON = async (path2) => {
204
- const buffer = await fse__default.default.readFile(path2);
205
- 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}`);
206
62
  };
207
- const saveJSON = async (path2, json) => {
208
- const jsonAsString = `${JSON.stringify(json, null, 2)}
209
- `;
210
- 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();
211
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";
212
144
  var ReleaseType = /* @__PURE__ */ ((ReleaseType2) => {
213
145
  ReleaseType2["Major"] = "major";
214
146
  ReleaseType2["Minor"] = "minor";
@@ -239,16 +171,16 @@ const rangeFactory = (range) => {
239
171
  const rangeFromReleaseType = (current, identifier) => {
240
172
  switch (identifier) {
241
173
  case ReleaseType.Major: {
242
- const nextMajor = semver__default.default.inc(current, "major");
243
- return rangeFactory(`>${current.raw} <=${nextMajor}`);
244
- }
245
- case ReleaseType.Patch: {
246
- const minor = semver__default.default.inc(current, "minor");
247
- return rangeFactory(`>${current.raw} <${minor}`);
174
+ const nextMajor = semVerFactory(current.raw).inc("major");
175
+ return rangeFactory(`>${current.raw} <=${nextMajor.major}`);
248
176
  }
249
177
  case ReleaseType.Minor: {
250
- const major = semver__default.default.inc(current, "major");
251
- return rangeFactory(`>${current.raw} <${major}`);
178
+ const nextMajor = semVerFactory(current.raw).inc("major");
179
+ return rangeFactory(`>${current.raw} <${nextMajor.raw}`);
180
+ }
181
+ case ReleaseType.Patch: {
182
+ const nextMinor = semVerFactory(current.raw).inc("minor");
183
+ return rangeFactory(`>${current.raw} <${nextMinor.raw}`);
252
184
  }
253
185
  default: {
254
186
  throw new Error("Not implemented");
@@ -282,6 +214,48 @@ const index$e = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
282
214
  rangeFromVersions,
283
215
  semVerFactory
284
216
  }, Symbol.toStringTag, { value: "Module" }));
217
+ class Package {
218
+ name;
219
+ packageURL;
220
+ npmPackage;
221
+ constructor(name) {
222
+ this.name = name;
223
+ this.packageURL = `${NPM_REGISTRY_URL}/${name}`;
224
+ this.npmPackage = null;
225
+ }
226
+ get isLoaded() {
227
+ return this.npmPackage !== null;
228
+ }
229
+ assertPackageIsLoaded(npmPackage) {
230
+ assert__default.default(this.isLoaded, "The package is not loaded yet");
231
+ }
232
+ getVersionsDict() {
233
+ this.assertPackageIsLoaded(this.npmPackage);
234
+ return this.npmPackage.versions;
235
+ }
236
+ getVersionsAsList() {
237
+ this.assertPackageIsLoaded(this.npmPackage);
238
+ return Object.values(this.npmPackage.versions);
239
+ }
240
+ findVersionsInRange(range) {
241
+ const versions = this.getVersionsAsList();
242
+ return versions.filter((v) => range.test(v.version)).filter((v) => isLiteralSemVer(v.version)).sort((v1, v2) => semver__default.default.compare(v1.version, v2.version));
243
+ }
244
+ findVersion(version2) {
245
+ const versions = this.getVersionsAsList();
246
+ return versions.find((npmVersion) => semver__default.default.eq(npmVersion.version, version2));
247
+ }
248
+ async refresh() {
249
+ const response = await fetch(this.packageURL);
250
+ assert__default.default(response.ok, `Request failed for ${this.packageURL}`);
251
+ this.npmPackage = await response.json();
252
+ return this;
253
+ }
254
+ versionExists(version2) {
255
+ return this.findVersion(version2) !== void 0;
256
+ }
257
+ }
258
+ const npmPackageFactory = (name) => new Package(name);
285
259
  class FileScanner {
286
260
  cwd;
287
261
  constructor(cwd) {
@@ -314,20 +288,60 @@ class AbstractRunner {
314
288
  const runConfiguration = { ...this.configuration, ...configuration };
315
289
  return this.runner(codemod.path, this.paths, runConfiguration);
316
290
  }
317
- }
318
- class CodeRunner extends AbstractRunner {
319
- runner = Runner.run;
320
- valid(codemod) {
321
- return codemod.kind === "code";
291
+ }
292
+ class CodeRunner extends AbstractRunner {
293
+ runner = Runner.run;
294
+ valid(codemod) {
295
+ return codemod.kind === "code";
296
+ }
297
+ }
298
+ const codeRunnerFactory = (paths, configuration) => {
299
+ return new CodeRunner(paths, configuration);
300
+ };
301
+ const index$c = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
302
+ __proto__: null,
303
+ codeRunnerFactory
304
+ }, Symbol.toStringTag, { value: "Module" }));
305
+ class JSONTransformAPI {
306
+ json;
307
+ constructor(json) {
308
+ this.json = fp.cloneDeep(json);
309
+ }
310
+ get(path2, defaultValue) {
311
+ if (!path2) {
312
+ return this.root();
313
+ }
314
+ return fp.cloneDeep(fp.get(path2, this.json) ?? defaultValue);
315
+ }
316
+ has(path2) {
317
+ return fp.has(path2, this.json);
318
+ }
319
+ merge(other) {
320
+ this.json = fp.merge(other, this.json);
321
+ return this;
322
+ }
323
+ root() {
324
+ return fp.cloneDeep(this.json);
325
+ }
326
+ set(path2, value) {
327
+ this.json = fp.set(path2, value, this.json);
328
+ return this;
329
+ }
330
+ remove(path2) {
331
+ this.json = fp.omit(path2, this.json);
332
+ return this;
322
333
  }
323
334
  }
324
- const codeRunnerFactory = (paths, configuration) => {
325
- return new CodeRunner(paths, configuration);
335
+ const createJSONTransformAPI = (object) => new JSONTransformAPI(object);
336
+ const readJSON = async (path2) => {
337
+ const buffer = await fse__default.default.readFile(path2);
338
+ return JSON.parse(buffer.toString());
339
+ };
340
+ const saveJSON = async (path2, json) => {
341
+ const jsonAsString = `${JSON.stringify(json, null, 2)}
342
+ `;
343
+ await fse__default.default.writeFile(path2, jsonAsString);
326
344
  };
327
- const index$c = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
328
- __proto__: null,
329
- codeRunnerFactory
330
- }, Symbol.toStringTag, { value: "Module" }));
331
345
  const transformJSON = async (codemodPath, paths, config) => {
332
346
  const { dry } = config;
333
347
  const startTime = process.hrtime();
@@ -636,6 +650,13 @@ class UnexpectedError extends Error {
636
650
  super("Unexpected Error");
637
651
  }
638
652
  }
653
+ class NPMCandidateNotFoundError extends Error {
654
+ target;
655
+ constructor(target, message = `Couldn't find a valid NPM candidate for "${target}"`) {
656
+ super(message);
657
+ this.target = target;
658
+ }
659
+ }
639
660
  const unknownToError = (e) => {
640
661
  if (e instanceof Error) {
641
662
  return e;
@@ -647,93 +668,10 @@ const unknownToError = (e) => {
647
668
  };
648
669
  const index$9 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
649
670
  __proto__: null,
671
+ NPMCandidateNotFoundError,
650
672
  UnexpectedError,
651
673
  unknownToError
652
674
  }, Symbol.toStringTag, { value: "Module" }));
653
- const path = (path2) => chalk__default.default.blue(path2);
654
- const version = (version2) => {
655
- return chalk__default.default.italic.yellow(`v${version2}`);
656
- };
657
- const codemodUID = (uid) => {
658
- return chalk__default.default.bold.cyan(uid);
659
- };
660
- const projectDetails = (project) => {
661
- return `Project: TYPE=${projectType(project.type)}; CWD=${path(project.cwd)}; PATHS=${project.paths.map(path)}`;
662
- };
663
- const projectType = (type) => chalk__default.default.cyan(type);
664
- const versionRange = (range) => chalk__default.default.italic.yellow(range.raw);
665
- const transform = (transformFilePath) => chalk__default.default.cyan(transformFilePath);
666
- const highlight = (arg) => chalk__default.default.bold.underline(arg);
667
- const upgradeStep = (text, step) => {
668
- return chalk__default.default.bold(`(${step[0]}/${step[1]}) ${text}...`);
669
- };
670
- const reports = (reports2) => {
671
- const rows = reports2.map(({ codemod, report }, i) => {
672
- const fIndex = chalk__default.default.grey(i);
673
- const fVersion = chalk__default.default.magenta(codemod.version);
674
- const fKind = chalk__default.default.yellow(codemod.kind);
675
- const fFormattedTransformPath = chalk__default.default.cyan(codemod.format());
676
- const fTimeElapsed = i === 0 ? `${report.timeElapsed}s ${chalk__default.default.dim.italic("(cold start)")}` : `${report.timeElapsed}s`;
677
- const fAffected = report.ok > 0 ? chalk__default.default.green(report.ok) : chalk__default.default.grey(0);
678
- const fUnchanged = report.ok === 0 ? chalk__default.default.red(report.nochange) : chalk__default.default.grey(report.nochange);
679
- return [fIndex, fVersion, fKind, fFormattedTransformPath, fAffected, fUnchanged, fTimeElapsed];
680
- });
681
- const table = new CliTable3__default.default({
682
- style: { compact: true },
683
- head: [
684
- chalk__default.default.bold.grey("N°"),
685
- chalk__default.default.bold.magenta("Version"),
686
- chalk__default.default.bold.yellow("Kind"),
687
- chalk__default.default.bold.cyan("Name"),
688
- chalk__default.default.bold.green("Affected"),
689
- chalk__default.default.bold.red("Unchanged"),
690
- chalk__default.default.bold.blue("Duration")
691
- ]
692
- });
693
- table.push(...rows);
694
- return table.toString();
695
- };
696
- const codemodList = (codemods) => {
697
- const rows = codemods.map((codemod, index2) => {
698
- const fIndex = chalk__default.default.grey(index2);
699
- const fVersion = chalk__default.default.magenta(codemod.version);
700
- const fKind = chalk__default.default.yellow(codemod.kind);
701
- const fName = chalk__default.default.blue(codemod.format());
702
- const fUID = codemodUID(codemod.uid);
703
- return [fIndex, fVersion, fKind, fName, fUID];
704
- });
705
- const table = new CliTable3__default.default({
706
- style: { compact: true },
707
- head: [
708
- chalk__default.default.bold.grey("N°"),
709
- chalk__default.default.bold.magenta("Version"),
710
- chalk__default.default.bold.yellow("Kind"),
711
- chalk__default.default.bold.blue("Name"),
712
- chalk__default.default.bold.cyan("UID")
713
- ]
714
- });
715
- table.push(...rows);
716
- return table.toString();
717
- };
718
- const durationMs = (elapsedMs) => {
719
- const elapsedSeconds = (elapsedMs / ONE_SECOND_MS).toFixed(3);
720
- return `${elapsedSeconds}s`;
721
- };
722
- const index$8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
723
- __proto__: null,
724
- codemodList,
725
- codemodUID,
726
- durationMs,
727
- highlight,
728
- path,
729
- projectDetails,
730
- projectType,
731
- reports,
732
- transform,
733
- upgradeStep,
734
- version,
735
- versionRange
736
- }, Symbol.toStringTag, { value: "Module" }));
737
675
  const CODEMOD_CODE_SUFFIX = "code";
738
676
  const CODEMOD_JSON_SUFFIX = "json";
739
677
  const CODEMOD_ALLOWED_SUFFIXES = [CODEMOD_CODE_SUFFIX, CODEMOD_JSON_SUFFIX];
@@ -786,7 +724,7 @@ class Codemod {
786
724
  }
787
725
  }
788
726
  const codemodFactory = (options) => new Codemod(options);
789
- const index$7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
727
+ const index$8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
790
728
  __proto__: null,
791
729
  codemodFactory,
792
730
  constants: constants$2
@@ -898,7 +836,7 @@ const parseCodemodKindFromFilename = (filename) => {
898
836
  const codemodRepositoryFactory = (cwd = INTERNAL_CODEMODS_DIRECTORY) => {
899
837
  return new CodemodRepository(cwd);
900
838
  };
901
- const index$6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
839
+ const index$7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
902
840
  __proto__: null,
903
841
  codemodRepositoryFactory,
904
842
  constants: constants$1
@@ -1163,7 +1101,7 @@ class Upgrader {
1163
1101
  const packageManagerName = await utils.packageManager.getPreferred(projectPath);
1164
1102
  this.logger?.debug?.(`Using ${highlight(packageManagerName)} as package manager`);
1165
1103
  if (this.isDry) {
1166
- this.logger?.debug?.(`Skipping dependencies installation (${chalk__default.default.italic("dry mode")}`);
1104
+ this.logger?.debug?.(`Skipping dependencies installation (${chalk__default.default.italic("dry mode")})`);
1167
1105
  return;
1168
1106
  }
1169
1107
  await utils.packageManager.installDependencies(projectPath, packageManagerName, {
@@ -1182,23 +1120,28 @@ class Upgrader {
1182
1120
  }
1183
1121
  const resolveNPMTarget = (project, target, npmPackage) => {
1184
1122
  if (isSemverInstance(target)) {
1185
- return npmPackage.findVersion(target);
1123
+ const version2 = npmPackage.findVersion(target);
1124
+ if (!version2) {
1125
+ throw new NPMCandidateNotFoundError(target);
1126
+ }
1127
+ return version2;
1186
1128
  }
1187
1129
  if (isSemVerReleaseType(target)) {
1188
1130
  const range = rangeFromVersions(project.strapiVersion, target);
1189
1131
  const npmVersionsMatches = npmPackage.findVersionsInRange(range);
1190
- return npmVersionsMatches.at(-1);
1132
+ const version2 = npmVersionsMatches.at(-1);
1133
+ if (!version2) {
1134
+ throw new NPMCandidateNotFoundError(range, `The project is already up-to-date (${target})`);
1135
+ }
1136
+ return version2;
1191
1137
  }
1192
- return void 0;
1138
+ throw new NPMCandidateNotFoundError(target);
1193
1139
  };
1194
1140
  const upgraderFactory = (project, target, npmPackage) => {
1195
- const targetedNPMVersion = resolveNPMTarget(project, target, npmPackage);
1196
- if (!targetedNPMVersion) {
1197
- throw new Error(`Couldn't find a matching version in the NPM registry for "${target}"`);
1198
- }
1199
- const semverTarget = semVerFactory(targetedNPMVersion.version);
1141
+ const npmTarget = resolveNPMTarget(project, target, npmPackage);
1142
+ const semverTarget = semVerFactory(npmTarget.version);
1200
1143
  if (semver__default.default.eq(semverTarget, project.strapiVersion)) {
1201
- throw new Error(`The project is already on ${version(semverTarget)}`);
1144
+ throw new Error(`The project is already using v${semverTarget}`);
1202
1145
  }
1203
1146
  return new Upgrader(project, semverTarget, npmPackage);
1204
1147
  };
@@ -1209,54 +1152,126 @@ const constants = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
1209
1152
  __proto__: null,
1210
1153
  STRAPI_PACKAGE_NAME
1211
1154
  }, Symbol.toStringTag, { value: "Module" }));
1212
- const index$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1155
+ const index$6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1213
1156
  __proto__: null,
1214
1157
  constants,
1215
1158
  upgraderFactory
1216
1159
  }, Symbol.toStringTag, { value: "Module" }));
1217
- const NPM_REGISTRY_URL = "https://registry.npmjs.org";
1218
- class Package {
1160
+ class Requirement {
1161
+ isRequired;
1219
1162
  name;
1220
- packageURL;
1221
- npmPackage;
1222
- constructor(name) {
1163
+ testCallback;
1164
+ children;
1165
+ constructor(name, testCallback, isRequired) {
1223
1166
  this.name = name;
1224
- this.packageURL = `${NPM_REGISTRY_URL}/${name}`;
1225
- this.npmPackage = null;
1167
+ this.testCallback = testCallback;
1168
+ this.isRequired = isRequired ?? true;
1169
+ this.children = [];
1226
1170
  }
1227
- get isLoaded() {
1228
- return this.npmPackage !== null;
1171
+ setChildren(children) {
1172
+ this.children = children;
1173
+ return this;
1229
1174
  }
1230
- assertPackageIsLoaded(npmPackage) {
1231
- assert__default.default(this.isLoaded, "The package is not loaded yet");
1175
+ addChild(child) {
1176
+ this.children.push(child);
1177
+ return this;
1232
1178
  }
1233
- getVersionsDict() {
1234
- this.assertPackageIsLoaded(this.npmPackage);
1235
- return this.npmPackage.versions;
1179
+ asOptional() {
1180
+ const newInstance = requirementFactory(this.name, this.testCallback, false);
1181
+ newInstance.setChildren(this.children);
1182
+ return newInstance;
1236
1183
  }
1237
- getVersionsAsList() {
1238
- this.assertPackageIsLoaded(this.npmPackage);
1239
- return Object.values(this.npmPackage.versions);
1184
+ asRequired() {
1185
+ const newInstance = requirementFactory(this.name, this.testCallback, true);
1186
+ newInstance.setChildren(this.children);
1187
+ return newInstance;
1240
1188
  }
1241
- findVersionsInRange(range) {
1242
- const versions = this.getVersionsAsList();
1243
- return versions.filter((v) => range.test(v.version)).filter((v) => isLiteralSemVer(v.version)).sort((v1, v2) => semver__default.default.compare(v1.version, v2.version));
1189
+ async test(context) {
1190
+ try {
1191
+ await this.testCallback?.(context);
1192
+ return ok();
1193
+ } catch (e) {
1194
+ if (e instanceof Error) {
1195
+ return errored(e);
1196
+ }
1197
+ if (typeof e === "string") {
1198
+ return errored(new Error(e));
1199
+ }
1200
+ return errored(new Error("Unknown error"));
1201
+ }
1244
1202
  }
1245
- findVersion(version2) {
1246
- const versions = this.getVersionsAsList();
1247
- return versions.find((npmVersion) => semver__default.default.eq(npmVersion.version, version2));
1203
+ }
1204
+ const ok = () => ({ pass: true, error: null });
1205
+ const errored = (error) => ({ pass: false, error });
1206
+ const requirementFactory = (name, testCallback, isRequired) => new Requirement(name, testCallback, isRequired);
1207
+ const index$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1208
+ __proto__: null,
1209
+ requirementFactory
1210
+ }, Symbol.toStringTag, { value: "Module" }));
1211
+ const REQUIRE_AVAILABLE_NEXT_MAJOR = requirementFactory(
1212
+ "REQUIRE_AVAILABLE_NEXT_MAJOR",
1213
+ (context) => {
1214
+ const { project, target } = context;
1215
+ const currentMajor = project.strapiVersion.major;
1216
+ const targetedMajor = target.major;
1217
+ if (targetedMajor === currentMajor) {
1218
+ throw new Error(`You're already on the latest major version (v${currentMajor})`);
1219
+ }
1248
1220
  }
1249
- async refresh() {
1250
- const response = await fetch(this.packageURL);
1251
- assert__default.default(response.ok, `Request failed for ${this.packageURL}`);
1252
- this.npmPackage = await response.json();
1253
- return this;
1221
+ );
1222
+ const REQUIRE_LATEST_FOR_CURRENT_MAJOR = requirementFactory(
1223
+ "REQUIRE_LATEST_FOR_CURRENT_MAJOR",
1224
+ (context) => {
1225
+ const { project, target, npmVersionsMatches } = context;
1226
+ const { major: currentMajor } = project.strapiVersion;
1227
+ const invalidMatches = npmVersionsMatches.filter(
1228
+ (match) => semVerFactory(match.version).major === currentMajor
1229
+ );
1230
+ if (invalidMatches.length > 0) {
1231
+ const invalidVersions = invalidMatches.map((match) => match.version);
1232
+ const invalidVersionsCount = invalidVersions.length;
1233
+ throw new Error(
1234
+ `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.`
1235
+ );
1236
+ }
1254
1237
  }
1255
- versionExists(version2) {
1256
- return this.findVersion(version2) !== void 0;
1238
+ );
1239
+ const REQUIRE_GIT_CLEAN_REPOSITORY = requirementFactory(
1240
+ "REQUIRE_GIT_CLEAN_REPOSITORY",
1241
+ async (context) => {
1242
+ const git = simpleGit__default.default({ baseDir: context.project.cwd });
1243
+ const status = await git.status();
1244
+ if (!status.isClean()) {
1245
+ throw new Error(
1246
+ "Repository is not clean. Please commit or stash any changes before upgrading"
1247
+ );
1248
+ }
1257
1249
  }
1258
- }
1259
- const npmPackageFactory = (name) => new Package(name);
1250
+ );
1251
+ const REQUIRE_GIT_REPOSITORY = requirementFactory(
1252
+ "REQUIRE_GIT_REPOSITORY",
1253
+ async (context) => {
1254
+ const git = simpleGit__default.default({ baseDir: context.project.cwd });
1255
+ const isRepo = await git.checkIsRepo();
1256
+ if (!isRepo) {
1257
+ throw new Error("Not a git repository (or any of the parent directories)");
1258
+ }
1259
+ }
1260
+ ).addChild(REQUIRE_GIT_CLEAN_REPOSITORY.asOptional());
1261
+ const REQUIRE_GIT_INSTALLED = requirementFactory(
1262
+ "REQUIRE_GIT_INSTALLED",
1263
+ async (context) => {
1264
+ const git = simpleGit__default.default({ baseDir: context.project.cwd });
1265
+ try {
1266
+ await git.version();
1267
+ } catch {
1268
+ throw new Error("Git is not installed");
1269
+ }
1270
+ }
1271
+ ).addChild(REQUIRE_GIT_REPOSITORY.asOptional());
1272
+ const REQUIRE_GIT = requirementFactory("REQUIRE_GIT", null).addChild(
1273
+ REQUIRE_GIT_INSTALLED.asOptional()
1274
+ );
1260
1275
  const upgrade = async (options) => {
1261
1276
  const timer = timerFactory();
1262
1277
  const { logger, codemodsTarget } = options;
@@ -1268,6 +1283,9 @@ const upgrade = async (options) => {
1268
1283
  `The "${options.target}" upgrade can only be run on a Strapi project; for plugins, please use "codemods".`
1269
1284
  );
1270
1285
  }
1286
+ logger.debug(
1287
+ `Application: VERSION=${version(project.packageJSON.version)}; STRAPI_VERSION=${version(project.strapiVersion)}`
1288
+ );
1271
1289
  const npmPackage = npmPackageFactory(STRAPI_PACKAGE_NAME);
1272
1290
  await npmPackage.refresh();
1273
1291
  const upgrader = upgraderFactory(project, options.target, npmPackage).dry(options.dry ?? false).onConfirm(options.confirm ?? null).setLogger(logger);
@@ -1283,7 +1301,7 @@ const upgrade = async (options) => {
1283
1301
  throw upgradeReport.error;
1284
1302
  }
1285
1303
  timer.stop();
1286
- logger.info(`Completed in ${durationMs(timer.elapsedMs)}`);
1304
+ logger.info(`Completed in ${durationMs(timer.elapsedMs)}ms`);
1287
1305
  };
1288
1306
  const resolvePath = (cwd) => path__default.default.resolve(cwd ?? process.cwd());
1289
1307
  const getRangeFromTarget = (currentVersion, target) => {
@@ -1449,18 +1467,18 @@ const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
1449
1467
  }, Symbol.toStringTag, { value: "Module" }));
1450
1468
  const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1451
1469
  __proto__: null,
1452
- codemod: index$7,
1453
- codemodRepository: index$6,
1470
+ codemod: index$8,
1471
+ codemodRepository: index$7,
1454
1472
  error: index$9,
1455
- f: index$8,
1473
+ f: index$f,
1456
1474
  fileScanner: index$d,
1457
1475
  logger: index$3,
1458
1476
  project: index$a,
1459
1477
  report: index$2,
1460
- requirement: index$g,
1478
+ requirement: index$5,
1461
1479
  runner: index$1,
1462
- timer: index$f,
1463
- upgrader: index$5,
1480
+ timer: index$g,
1481
+ upgrader: index$6,
1464
1482
  version: index$e
1465
1483
  }, Symbol.toStringTag, { value: "Module" }));
1466
1484
  exports.modules = index;