@strapi/upgrade 5.8.1 → 5.10.0
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/chunks/logger-B44ixHKE.js +1649 -0
- package/dist/chunks/logger-B44ixHKE.js.map +1 -0
- package/dist/chunks/logger-DLKyDz9F.mjs +1599 -0
- package/dist/chunks/logger-DLKyDz9F.mjs.map +1 -0
- package/dist/cli.js +191 -1558
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +211 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.js +127 -1518
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +125 -1510
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -7
package/dist/cli.js
CHANGED
|
@@ -1,1556 +1,188 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const assert__default = /* @__PURE__ */ _interopDefault(assert);
|
|
25
|
-
const fse__default = /* @__PURE__ */ _interopDefault(fse);
|
|
26
|
-
const fastglob__default = /* @__PURE__ */ _interopDefault(fastglob);
|
|
27
|
-
const simpleGit__default = /* @__PURE__ */ _interopDefault(simpleGit);
|
|
28
|
-
class Logger {
|
|
29
|
-
isDebug;
|
|
30
|
-
isSilent;
|
|
31
|
-
nbErrorsCalls;
|
|
32
|
-
nbWarningsCalls;
|
|
33
|
-
constructor(options = {}) {
|
|
34
|
-
this.isDebug = options.debug ?? false;
|
|
35
|
-
this.isSilent = options.silent ?? false;
|
|
36
|
-
this.nbErrorsCalls = 0;
|
|
37
|
-
this.nbWarningsCalls = 0;
|
|
38
|
-
}
|
|
39
|
-
get isNotSilent() {
|
|
40
|
-
return !this.isSilent;
|
|
41
|
-
}
|
|
42
|
-
get errors() {
|
|
43
|
-
return this.nbErrorsCalls;
|
|
44
|
-
}
|
|
45
|
-
get warnings() {
|
|
46
|
-
return this.nbWarningsCalls;
|
|
47
|
-
}
|
|
48
|
-
get stdout() {
|
|
49
|
-
return this.isSilent ? void 0 : process.stdout;
|
|
50
|
-
}
|
|
51
|
-
get stderr() {
|
|
52
|
-
return this.isSilent ? void 0 : process.stderr;
|
|
53
|
-
}
|
|
54
|
-
setDebug(debug) {
|
|
55
|
-
this.isDebug = debug;
|
|
56
|
-
return this;
|
|
57
|
-
}
|
|
58
|
-
setSilent(silent) {
|
|
59
|
-
this.isSilent = silent;
|
|
60
|
-
return this;
|
|
61
|
-
}
|
|
62
|
-
debug(...args) {
|
|
63
|
-
const isDebugEnabled = this.isNotSilent && this.isDebug;
|
|
64
|
-
if (isDebugEnabled) {
|
|
65
|
-
console.log(chalk__default.default.cyan(`[DEBUG] [${nowAsISO()}]`), ...args);
|
|
66
|
-
}
|
|
67
|
-
return this;
|
|
68
|
-
}
|
|
69
|
-
error(...args) {
|
|
70
|
-
this.nbErrorsCalls += 1;
|
|
71
|
-
if (this.isNotSilent) {
|
|
72
|
-
console.error(chalk__default.default.red(`[ERROR] [${nowAsISO()}]`), ...args);
|
|
73
|
-
}
|
|
74
|
-
return this;
|
|
75
|
-
}
|
|
76
|
-
info(...args) {
|
|
77
|
-
if (this.isNotSilent) {
|
|
78
|
-
console.info(chalk__default.default.blue(`[INFO] [${(/* @__PURE__ */ new Date()).toISOString()}]`), ...args);
|
|
79
|
-
}
|
|
80
|
-
return this;
|
|
81
|
-
}
|
|
82
|
-
raw(...args) {
|
|
83
|
-
if (this.isNotSilent) {
|
|
84
|
-
console.log(...args);
|
|
85
|
-
}
|
|
86
|
-
return this;
|
|
87
|
-
}
|
|
88
|
-
warn(...args) {
|
|
89
|
-
this.nbWarningsCalls += 1;
|
|
90
|
-
if (this.isNotSilent) {
|
|
91
|
-
console.warn(chalk__default.default.yellow(`[WARN] [${(/* @__PURE__ */ new Date()).toISOString()}]`), ...args);
|
|
92
|
-
}
|
|
93
|
-
return this;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
const nowAsISO = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
97
|
-
const loggerFactory = (options = {}) => new Logger(options);
|
|
98
|
-
var ReleaseType = /* @__PURE__ */ ((ReleaseType2) => {
|
|
99
|
-
ReleaseType2["Major"] = "major";
|
|
100
|
-
ReleaseType2["Minor"] = "minor";
|
|
101
|
-
ReleaseType2["Patch"] = "patch";
|
|
102
|
-
ReleaseType2["Latest"] = "latest";
|
|
103
|
-
return ReleaseType2;
|
|
104
|
-
})(ReleaseType || {});
|
|
105
|
-
const semVerFactory = (version2) => {
|
|
106
|
-
return new semver__default.default.SemVer(version2);
|
|
107
|
-
};
|
|
108
|
-
const isLiteralSemVer = (str) => {
|
|
109
|
-
const tokens = str.split(".");
|
|
110
|
-
return tokens.length === 3 && tokens.every((token) => !Number.isNaN(+token) && Number.isInteger(+token));
|
|
111
|
-
};
|
|
112
|
-
const isValidSemVer = (str) => semver__default.default.valid(str) !== null;
|
|
113
|
-
const isSemverInstance = (value) => {
|
|
114
|
-
return value instanceof semver__default.default.SemVer;
|
|
115
|
-
};
|
|
116
|
-
const isSemVerReleaseType = (str) => {
|
|
117
|
-
return Object.values(ReleaseType).includes(str);
|
|
118
|
-
};
|
|
119
|
-
const rangeFactory = (range) => {
|
|
120
|
-
return new semver__default.default.Range(range);
|
|
121
|
-
};
|
|
122
|
-
const rangeFromReleaseType = (current, identifier) => {
|
|
123
|
-
switch (identifier) {
|
|
124
|
-
case ReleaseType.Latest: {
|
|
125
|
-
return rangeFactory(`>${current.raw}`);
|
|
126
|
-
}
|
|
127
|
-
case ReleaseType.Major: {
|
|
128
|
-
const nextMajor = semVerFactory(current.raw).inc("major");
|
|
129
|
-
return rangeFactory(`>${current.raw} <=${nextMajor.major}`);
|
|
130
|
-
}
|
|
131
|
-
case ReleaseType.Minor: {
|
|
132
|
-
const nextMajor = semVerFactory(current.raw).inc("major");
|
|
133
|
-
return rangeFactory(`>${current.raw} <${nextMajor.raw}`);
|
|
134
|
-
}
|
|
135
|
-
case ReleaseType.Patch: {
|
|
136
|
-
const nextMinor = semVerFactory(current.raw).inc("minor");
|
|
137
|
-
return rangeFactory(`>${current.raw} <${nextMinor.raw}`);
|
|
138
|
-
}
|
|
139
|
-
default: {
|
|
140
|
-
throw new Error("Not implemented");
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
const rangeFromVersions = (currentVersion, target) => {
|
|
145
|
-
if (isSemverInstance(target)) {
|
|
146
|
-
return rangeFactory(`>${currentVersion.raw} <=${target.raw}`);
|
|
147
|
-
}
|
|
148
|
-
if (isSemVerReleaseType(target)) {
|
|
149
|
-
return rangeFromReleaseType(currentVersion, target);
|
|
150
|
-
}
|
|
151
|
-
throw new Error(`Invalid target set: ${target}`);
|
|
152
|
-
};
|
|
153
|
-
const isValidStringifiedRange = (str) => semver__default.default.validRange(str) !== null;
|
|
154
|
-
const isRangeInstance = (range) => {
|
|
155
|
-
return range instanceof semver__default.default.Range;
|
|
156
|
-
};
|
|
157
|
-
class UnexpectedError extends Error {
|
|
158
|
-
constructor() {
|
|
159
|
-
super("Unexpected Error");
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
class NPMCandidateNotFoundError extends Error {
|
|
163
|
-
target;
|
|
164
|
-
constructor(target, message = `Couldn't find a valid NPM candidate for "${target}"`) {
|
|
165
|
-
super(message);
|
|
166
|
-
this.target = target;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
class AbortedError extends Error {
|
|
170
|
-
constructor(message = "Upgrade aborted") {
|
|
171
|
-
super(message);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
const unknownToError = (e) => {
|
|
175
|
-
if (e instanceof Error) {
|
|
176
|
-
return e;
|
|
177
|
-
}
|
|
178
|
-
if (typeof e === "string") {
|
|
179
|
-
return new Error(e);
|
|
180
|
-
}
|
|
181
|
-
return new UnexpectedError();
|
|
182
|
-
};
|
|
183
|
-
const handleError = (err, isSilent) => {
|
|
184
|
-
if (err instanceof AbortedError) {
|
|
185
|
-
process.exit(0);
|
|
186
|
-
}
|
|
187
|
-
if (!isSilent) {
|
|
188
|
-
console.error(
|
|
189
|
-
chalk__default.default.red(`[ERROR] [${(/* @__PURE__ */ new Date()).toISOString()}]`),
|
|
190
|
-
err instanceof Error ? err.message : err
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
process.exit(1);
|
|
194
|
-
};
|
|
195
|
-
class Timer {
|
|
196
|
-
interval;
|
|
197
|
-
constructor() {
|
|
198
|
-
this.reset();
|
|
199
|
-
}
|
|
200
|
-
get elapsedMs() {
|
|
201
|
-
const { start, end } = this.interval;
|
|
202
|
-
return end ? end - start : Date.now() - start;
|
|
203
|
-
}
|
|
204
|
-
get end() {
|
|
205
|
-
return this.interval.end;
|
|
206
|
-
}
|
|
207
|
-
get start() {
|
|
208
|
-
return this.interval.start;
|
|
209
|
-
}
|
|
210
|
-
stop() {
|
|
211
|
-
this.interval.end = Date.now();
|
|
212
|
-
return this.elapsedMs;
|
|
213
|
-
}
|
|
214
|
-
reset() {
|
|
215
|
-
this.interval = { start: Date.now(), end: null };
|
|
216
|
-
return this;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
const timerFactory = () => new Timer();
|
|
220
|
-
const ONE_SECOND_MS = 1e3;
|
|
221
|
-
const path = (path2) => chalk__default.default.blue(path2);
|
|
222
|
-
const version$1 = (version2) => {
|
|
223
|
-
return chalk__default.default.italic.yellow(`v${version2}`);
|
|
224
|
-
};
|
|
225
|
-
const codemodUID = (uid) => {
|
|
226
|
-
return chalk__default.default.bold.cyan(uid);
|
|
227
|
-
};
|
|
228
|
-
const projectDetails = (project) => {
|
|
229
|
-
return `Project: TYPE=${projectType(project.type)}; CWD=${path(project.cwd)}; PATHS=${project.paths.map(path)}`;
|
|
230
|
-
};
|
|
231
|
-
const projectType = (type) => chalk__default.default.cyan(type);
|
|
232
|
-
const versionRange = (range) => chalk__default.default.italic.yellow(range.raw);
|
|
233
|
-
const highlight = (arg) => chalk__default.default.bold.underline(arg);
|
|
234
|
-
const upgradeStep = (text, step) => {
|
|
235
|
-
return chalk__default.default.bold(`(${step[0]}/${step[1]}) ${text}...`);
|
|
236
|
-
};
|
|
237
|
-
const reports = (reports2) => {
|
|
238
|
-
const rows = reports2.map(({ codemod, report }, i) => {
|
|
239
|
-
const fIndex = chalk__default.default.grey(i);
|
|
240
|
-
const fVersion = chalk__default.default.magenta(codemod.version);
|
|
241
|
-
const fKind = chalk__default.default.yellow(codemod.kind);
|
|
242
|
-
const fFormattedTransformPath = chalk__default.default.cyan(codemod.format());
|
|
243
|
-
const fTimeElapsed = i === 0 ? `${report.timeElapsed}s ${chalk__default.default.dim.italic("(cold start)")}` : `${report.timeElapsed}s`;
|
|
244
|
-
const fAffected = report.ok > 0 ? chalk__default.default.green(report.ok) : chalk__default.default.grey(0);
|
|
245
|
-
const fUnchanged = report.ok === 0 ? chalk__default.default.red(report.nochange) : chalk__default.default.grey(report.nochange);
|
|
246
|
-
return [fIndex, fVersion, fKind, fFormattedTransformPath, fAffected, fUnchanged, fTimeElapsed];
|
|
247
|
-
});
|
|
248
|
-
const table = new CliTable3__default.default({
|
|
249
|
-
style: { compact: true },
|
|
250
|
-
head: [
|
|
251
|
-
chalk__default.default.bold.grey("N°"),
|
|
252
|
-
chalk__default.default.bold.magenta("Version"),
|
|
253
|
-
chalk__default.default.bold.yellow("Kind"),
|
|
254
|
-
chalk__default.default.bold.cyan("Name"),
|
|
255
|
-
chalk__default.default.bold.green("Affected"),
|
|
256
|
-
chalk__default.default.bold.red("Unchanged"),
|
|
257
|
-
chalk__default.default.bold.blue("Duration")
|
|
258
|
-
]
|
|
259
|
-
});
|
|
260
|
-
table.push(...rows);
|
|
261
|
-
return table.toString();
|
|
262
|
-
};
|
|
263
|
-
const codemodList = (codemods) => {
|
|
264
|
-
const rows = codemods.map((codemod, index) => {
|
|
265
|
-
const fIndex = chalk__default.default.grey(index);
|
|
266
|
-
const fVersion = chalk__default.default.magenta(codemod.version);
|
|
267
|
-
const fKind = chalk__default.default.yellow(codemod.kind);
|
|
268
|
-
const fName = chalk__default.default.blue(codemod.format());
|
|
269
|
-
const fUID = codemodUID(codemod.uid);
|
|
270
|
-
return [fIndex, fVersion, fKind, fName, fUID];
|
|
271
|
-
});
|
|
272
|
-
const table = new CliTable3__default.default({
|
|
273
|
-
style: { compact: true },
|
|
274
|
-
head: [
|
|
275
|
-
chalk__default.default.bold.grey("N°"),
|
|
276
|
-
chalk__default.default.bold.magenta("Version"),
|
|
277
|
-
chalk__default.default.bold.yellow("Kind"),
|
|
278
|
-
chalk__default.default.bold.blue("Name"),
|
|
279
|
-
chalk__default.default.bold.cyan("UID")
|
|
280
|
-
]
|
|
281
|
-
});
|
|
282
|
-
table.push(...rows);
|
|
283
|
-
return table.toString();
|
|
284
|
-
};
|
|
285
|
-
const durationMs = (elapsedMs) => {
|
|
286
|
-
const elapsedSeconds = (elapsedMs / ONE_SECOND_MS).toFixed(3);
|
|
287
|
-
return `${elapsedSeconds}s`;
|
|
288
|
-
};
|
|
289
|
-
const NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
290
|
-
class Package {
|
|
291
|
-
name;
|
|
292
|
-
packageURL;
|
|
293
|
-
npmPackage;
|
|
294
|
-
constructor(name) {
|
|
295
|
-
this.name = name;
|
|
296
|
-
this.packageURL = `${NPM_REGISTRY_URL}/${name}`;
|
|
297
|
-
this.npmPackage = null;
|
|
298
|
-
}
|
|
299
|
-
get isLoaded() {
|
|
300
|
-
return this.npmPackage !== null;
|
|
301
|
-
}
|
|
302
|
-
assertPackageIsLoaded(npmPackage) {
|
|
303
|
-
assert__default.default(this.isLoaded, "The package is not loaded yet");
|
|
304
|
-
}
|
|
305
|
-
getVersionsDict() {
|
|
306
|
-
this.assertPackageIsLoaded(this.npmPackage);
|
|
307
|
-
return this.npmPackage.versions;
|
|
308
|
-
}
|
|
309
|
-
getVersionsAsList() {
|
|
310
|
-
this.assertPackageIsLoaded(this.npmPackage);
|
|
311
|
-
return Object.values(this.npmPackage.versions);
|
|
312
|
-
}
|
|
313
|
-
findVersionsInRange(range) {
|
|
314
|
-
const versions = this.getVersionsAsList();
|
|
315
|
-
return versions.filter((v) => range.test(v.version)).filter((v) => isLiteralSemVer(v.version)).sort((v1, v2) => semver__default.default.compare(v1.version, v2.version));
|
|
316
|
-
}
|
|
317
|
-
findVersion(version2) {
|
|
318
|
-
const versions = this.getVersionsAsList();
|
|
319
|
-
return versions.find((npmVersion) => semver__default.default.eq(npmVersion.version, version2));
|
|
320
|
-
}
|
|
321
|
-
async refresh() {
|
|
322
|
-
const response = await fetch(this.packageURL);
|
|
323
|
-
assert__default.default(response.ok, `Request failed for ${this.packageURL}`);
|
|
324
|
-
this.npmPackage = await response.json();
|
|
325
|
-
return this;
|
|
326
|
-
}
|
|
327
|
-
versionExists(version2) {
|
|
328
|
-
return this.findVersion(version2) !== void 0;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
const npmPackageFactory = (name) => new Package(name);
|
|
332
|
-
class FileScanner {
|
|
333
|
-
cwd;
|
|
334
|
-
constructor(cwd) {
|
|
335
|
-
this.cwd = cwd;
|
|
336
|
-
}
|
|
337
|
-
scan(patterns) {
|
|
338
|
-
const filenames = fastglob__default.default.sync(patterns, {
|
|
339
|
-
cwd: this.cwd
|
|
340
|
-
});
|
|
341
|
-
return filenames.map((filename) => path__default.default.join(this.cwd, filename));
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
const fileScannerFactory = (cwd) => new FileScanner(cwd);
|
|
345
|
-
class AbstractRunner {
|
|
346
|
-
paths;
|
|
347
|
-
configuration;
|
|
348
|
-
constructor(paths, configuration) {
|
|
349
|
-
this.paths = paths;
|
|
350
|
-
this.configuration = configuration;
|
|
351
|
-
}
|
|
352
|
-
async run(codemod, configuration) {
|
|
353
|
-
const isValidCodemod = this.valid(codemod);
|
|
354
|
-
if (!isValidCodemod) {
|
|
355
|
-
throw new Error(`Invalid codemod provided to the runner: ${codemod.filename}`);
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var os = require('os');
|
|
4
|
+
var chalk = require('chalk');
|
|
5
|
+
var commander = require('commander');
|
|
6
|
+
var prompts = require('prompts');
|
|
7
|
+
var logger = require('./chunks/logger-B44ixHKE.js');
|
|
8
|
+
require('semver');
|
|
9
|
+
require('cli-table3');
|
|
10
|
+
require('node:path');
|
|
11
|
+
require('node:assert');
|
|
12
|
+
require('fs-extra');
|
|
13
|
+
require('fast-glob');
|
|
14
|
+
require('jscodeshift/src/Runner');
|
|
15
|
+
require('lodash/fp');
|
|
16
|
+
require('esbuild-register/dist/node');
|
|
17
|
+
require('@strapi/utils');
|
|
18
|
+
require('simple-git');
|
|
19
|
+
|
|
20
|
+
const handleError = (err, isSilent)=>{
|
|
21
|
+
// If the upgrade process has been aborted, exit silently
|
|
22
|
+
if (err instanceof logger.AbortedError) {
|
|
23
|
+
process.exit(0);
|
|
356
24
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
class CodeRunner extends AbstractRunner {
|
|
362
|
-
runner = Runner.run;
|
|
363
|
-
valid(codemod) {
|
|
364
|
-
return codemod.kind === "code";
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
const codeRunnerFactory = (paths, configuration) => {
|
|
368
|
-
return new CodeRunner(paths, configuration);
|
|
369
|
-
};
|
|
370
|
-
class JSONTransformAPI {
|
|
371
|
-
json;
|
|
372
|
-
constructor(json) {
|
|
373
|
-
this.json = fp.cloneDeep(json);
|
|
374
|
-
}
|
|
375
|
-
get(path2, defaultValue) {
|
|
376
|
-
if (!path2) {
|
|
377
|
-
return this.root();
|
|
25
|
+
if (!isSilent) {
|
|
26
|
+
console.error(chalk.red(`[ERROR]\t[${new Date().toISOString()}]`), err instanceof Error ? err.message : err);
|
|
378
27
|
}
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
has(path2) {
|
|
382
|
-
return fp.has(path2, this.json);
|
|
383
|
-
}
|
|
384
|
-
merge(other) {
|
|
385
|
-
this.json = fp.merge(other, this.json);
|
|
386
|
-
return this;
|
|
387
|
-
}
|
|
388
|
-
root() {
|
|
389
|
-
return fp.cloneDeep(this.json);
|
|
390
|
-
}
|
|
391
|
-
set(path2, value) {
|
|
392
|
-
this.json = fp.set(path2, value, this.json);
|
|
393
|
-
return this;
|
|
394
|
-
}
|
|
395
|
-
remove(path2) {
|
|
396
|
-
this.json = fp.omit(path2, this.json);
|
|
397
|
-
return this;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
const createJSONTransformAPI = (object) => new JSONTransformAPI(object);
|
|
401
|
-
const readJSON = async (path2) => {
|
|
402
|
-
const buffer = await fse__default.default.readFile(path2);
|
|
403
|
-
return JSON.parse(buffer.toString());
|
|
28
|
+
process.exit(1);
|
|
404
29
|
};
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
stats: {}
|
|
420
|
-
};
|
|
421
|
-
const esbuildOptions = {
|
|
422
|
-
extensions: [".js", ".mjs", ".ts"],
|
|
423
|
-
hookIgnoreNodeModules: false,
|
|
424
|
-
hookMatcher: fp.isEqual(codemodPath)
|
|
425
|
-
};
|
|
426
|
-
const { unregister } = node.register(esbuildOptions);
|
|
427
|
-
const module = require(codemodPath);
|
|
428
|
-
unregister();
|
|
429
|
-
const codemod = typeof module.default === "function" ? module.default : module;
|
|
430
|
-
assert__default.default(typeof codemod === "function", `Codemod must be a function. Found ${typeof codemod}`);
|
|
431
|
-
for (const path2 of paths) {
|
|
30
|
+
|
|
31
|
+
const projectPathOption = new commander.Option('-p, --project-path <project-path>', 'Root path to the Strapi application or plugin');
|
|
32
|
+
const dryOption = new commander.Option('-n, --dry', 'Simulate the upgrade without updating any files').default(false);
|
|
33
|
+
const debugOption = new commander.Option('-d, --debug', 'Get more logs in debug mode').default(false);
|
|
34
|
+
const silentOption = new commander.Option('-s, --silent', "Don't log anything").default(false);
|
|
35
|
+
const autoConfirmOption = new commander.Option('-y, --yes', 'Automatically answer "yes" to any prompts that the CLI might print on the command line.').default(false);
|
|
36
|
+
const rangeOption = new commander.Option('-r, --range <range>', 'Use a custom semver range for the codemods execution.').argParser((range)=>{
|
|
37
|
+
if (!logger.isValidStringifiedRange(range)) {
|
|
38
|
+
throw new commander.InvalidArgumentError('Expected a valid semver range');
|
|
39
|
+
}
|
|
40
|
+
return logger.rangeFactory(range);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const upgrade = async (options)=>{
|
|
432
44
|
try {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
45
|
+
const { silent, debug, yes } = options;
|
|
46
|
+
const logger$1 = logger.loggerFactory({
|
|
47
|
+
silent,
|
|
48
|
+
debug
|
|
49
|
+
});
|
|
50
|
+
logger$1.warn("Please make sure you've created a backup of your codebase and files before upgrading");
|
|
51
|
+
const confirm = async (message)=>{
|
|
52
|
+
if (yes) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
const { confirm } = await prompts({
|
|
56
|
+
name: 'confirm',
|
|
57
|
+
type: 'confirm',
|
|
58
|
+
message
|
|
59
|
+
});
|
|
60
|
+
// If confirm is undefined (Ctrl + C), default to false
|
|
61
|
+
return confirm ?? false;
|
|
62
|
+
};
|
|
63
|
+
await logger.upgrade({
|
|
64
|
+
logger: logger$1,
|
|
65
|
+
confirm,
|
|
66
|
+
dry: options.dry,
|
|
67
|
+
cwd: options.projectPath,
|
|
68
|
+
target: options.target,
|
|
69
|
+
codemodsTarget: options.codemodsTarget
|
|
70
|
+
});
|
|
71
|
+
} catch (err) {
|
|
72
|
+
handleError(err, options.silent);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Registers upgrade related codemods.
|
|
77
|
+
*/ const register$1 = (program)=>{
|
|
78
|
+
const addReleaseUpgradeCommand = (releaseType, description)=>{
|
|
79
|
+
program.command(releaseType).description(description).addOption(projectPathOption).addOption(dryOption).addOption(debugOption).addOption(silentOption).addOption(autoConfirmOption).action(async (options)=>{
|
|
80
|
+
return upgrade({
|
|
81
|
+
...options,
|
|
82
|
+
target: releaseType
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
// upgrade latest
|
|
87
|
+
addReleaseUpgradeCommand(logger.ReleaseType.Latest, 'Upgrade to the latest available version of Strapi');
|
|
88
|
+
// upgrade major
|
|
89
|
+
addReleaseUpgradeCommand(logger.ReleaseType.Major, 'Upgrade to the next available major version of Strapi');
|
|
90
|
+
// upgrade minor
|
|
91
|
+
addReleaseUpgradeCommand(logger.ReleaseType.Minor, 'Upgrade to the latest minor and patch version of Strapi for the current major');
|
|
92
|
+
// upgrade patch
|
|
93
|
+
addReleaseUpgradeCommand(logger.ReleaseType.Patch, 'Upgrade to latest patch version of Strapi for the current major and minor');
|
|
94
|
+
// upgrade to <target>
|
|
95
|
+
program.command('to <target>', {
|
|
96
|
+
hidden: true
|
|
97
|
+
}).description('Upgrade to the specified version of Strapi').addOption(projectPathOption).addOption(dryOption).addOption(debugOption).addOption(silentOption).addOption(autoConfirmOption).addOption(new commander.Option('-c, --codemods-target <codemodsTarget>', 'Use a custom target for the codemods execution. Useful when targeting pre-releases').argParser((codemodsTarget)=>{
|
|
98
|
+
if (!logger.isLiteralSemVer(codemodsTarget)) {
|
|
99
|
+
throw new commander.InvalidArgumentError(`Expected a version with the following format: "<number>.<number>.<number>"`);
|
|
443
100
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
report.error += 1;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
const endTime = process.hrtime(startTime);
|
|
453
|
-
report.timeElapsed = (endTime[0] + endTime[1] / 1e9).toFixed(3);
|
|
454
|
-
return report;
|
|
455
|
-
};
|
|
456
|
-
class JSONRunner extends AbstractRunner {
|
|
457
|
-
runner = transformJSON;
|
|
458
|
-
valid(codemod) {
|
|
459
|
-
return codemod.kind === "json";
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
const jsonRunnerFactory = (paths, configuration) => {
|
|
463
|
-
return new JSONRunner(paths, configuration);
|
|
464
|
-
};
|
|
465
|
-
const PROJECT_PACKAGE_JSON = "package.json";
|
|
466
|
-
const PROJECT_APP_ALLOWED_ROOT_PATHS = ["src", "config", "public"];
|
|
467
|
-
const PROJECT_PLUGIN_ALLOWED_ROOT_PATHS = ["admin", "server"];
|
|
468
|
-
const PROJECT_PLUGIN_ROOT_FILES = ["strapi-admin.js", "strapi-server.js"];
|
|
469
|
-
const PROJECT_CODE_EXTENSIONS = [
|
|
470
|
-
// Source files
|
|
471
|
-
"js",
|
|
472
|
-
"mjs",
|
|
473
|
-
"ts",
|
|
474
|
-
// React files
|
|
475
|
-
"jsx",
|
|
476
|
-
"tsx"
|
|
477
|
-
];
|
|
478
|
-
const PROJECT_JSON_EXTENSIONS = ["json"];
|
|
479
|
-
const PROJECT_ALLOWED_EXTENSIONS = [...PROJECT_CODE_EXTENSIONS, ...PROJECT_JSON_EXTENSIONS];
|
|
480
|
-
const SCOPED_STRAPI_PACKAGE_PREFIX = "@strapi/";
|
|
481
|
-
const STRAPI_DEPENDENCY_NAME = `${SCOPED_STRAPI_PACKAGE_PREFIX}strapi`;
|
|
482
|
-
class Project {
|
|
483
|
-
cwd;
|
|
484
|
-
// The following properties are assigned during the .refresh() call in the constructor.
|
|
485
|
-
files;
|
|
486
|
-
packageJSONPath;
|
|
487
|
-
packageJSON;
|
|
488
|
-
paths;
|
|
489
|
-
constructor(cwd, config) {
|
|
490
|
-
if (!fse__default.default.pathExistsSync(cwd)) {
|
|
491
|
-
throw new Error(`ENOENT: no such file or directory, access '${cwd}'`);
|
|
492
|
-
}
|
|
493
|
-
this.cwd = cwd;
|
|
494
|
-
this.paths = config.paths;
|
|
495
|
-
this.refresh();
|
|
496
|
-
}
|
|
497
|
-
getFilesByExtensions(extensions) {
|
|
498
|
-
return this.files.filter((filePath) => {
|
|
499
|
-
const fileExtension = path__default.default.extname(filePath);
|
|
500
|
-
return extensions.includes(fileExtension);
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
refresh() {
|
|
504
|
-
this.refreshPackageJSON();
|
|
505
|
-
this.refreshProjectFiles();
|
|
506
|
-
return this;
|
|
507
|
-
}
|
|
508
|
-
async runCodemods(codemods, options) {
|
|
509
|
-
const runners = this.createProjectCodemodsRunners(options.dry);
|
|
510
|
-
const reports2 = [];
|
|
511
|
-
for (const codemod of codemods) {
|
|
512
|
-
for (const runner of runners) {
|
|
513
|
-
if (runner.valid(codemod)) {
|
|
514
|
-
const report = await runner.run(codemod);
|
|
515
|
-
reports2.push({ codemod, report });
|
|
101
|
+
return logger.semVerFactory(codemodsTarget);
|
|
102
|
+
})).action(async (target, options)=>{
|
|
103
|
+
if (!logger.isValidSemVer(target)) {
|
|
104
|
+
console.error(`Invalid target supplied, expected a valid semver but got "${target}"`);
|
|
105
|
+
process.exit(1);
|
|
516
106
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
createProjectCodemodsRunners(dry = false) {
|
|
522
|
-
const jsonExtensions = PROJECT_JSON_EXTENSIONS.map((ext) => `.${ext}`);
|
|
523
|
-
const codeExtensions = PROJECT_CODE_EXTENSIONS.map((ext) => `.${ext}`);
|
|
524
|
-
const jsonFiles = this.getFilesByExtensions(jsonExtensions);
|
|
525
|
-
const codeFiles = this.getFilesByExtensions(codeExtensions);
|
|
526
|
-
const codeRunner = codeRunnerFactory(codeFiles, {
|
|
527
|
-
dry,
|
|
528
|
-
parser: "ts",
|
|
529
|
-
runInBand: true,
|
|
530
|
-
babel: true,
|
|
531
|
-
extensions: PROJECT_CODE_EXTENSIONS.join(","),
|
|
532
|
-
// Don't output any log coming from the runner
|
|
533
|
-
print: false,
|
|
534
|
-
silent: true,
|
|
535
|
-
verbose: 0
|
|
107
|
+
return upgrade({
|
|
108
|
+
...options,
|
|
109
|
+
target: logger.semVerFactory(target)
|
|
110
|
+
});
|
|
536
111
|
});
|
|
537
|
-
const jsonRunner = jsonRunnerFactory(jsonFiles, { dry, cwd: this.cwd });
|
|
538
|
-
return [codeRunner, jsonRunner];
|
|
539
|
-
}
|
|
540
|
-
refreshPackageJSON() {
|
|
541
|
-
const packageJSONPath = path__default.default.join(this.cwd, PROJECT_PACKAGE_JSON);
|
|
542
|
-
try {
|
|
543
|
-
fse__default.default.accessSync(packageJSONPath);
|
|
544
|
-
} catch {
|
|
545
|
-
throw new Error(`Could not find a ${PROJECT_PACKAGE_JSON} file in ${this.cwd}`);
|
|
546
|
-
}
|
|
547
|
-
const packageJSONBuffer = fse__default.default.readFileSync(packageJSONPath);
|
|
548
|
-
this.packageJSONPath = packageJSONPath;
|
|
549
|
-
this.packageJSON = JSON.parse(packageJSONBuffer.toString());
|
|
550
|
-
}
|
|
551
|
-
refreshProjectFiles() {
|
|
552
|
-
const scanner = fileScannerFactory(this.cwd);
|
|
553
|
-
this.files = scanner.scan(this.paths);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
class AppProject extends Project {
|
|
557
|
-
strapiVersion;
|
|
558
|
-
type = "application";
|
|
559
|
-
/**
|
|
560
|
-
* Returns an array of allowed file paths for a Strapi application
|
|
561
|
-
*
|
|
562
|
-
* The resulting paths include app default files and the root package.json file.
|
|
563
|
-
*/
|
|
564
|
-
static get paths() {
|
|
565
|
-
const allowedRootPaths = formatGlobCollectionPattern(PROJECT_APP_ALLOWED_ROOT_PATHS);
|
|
566
|
-
const allowedExtensions = formatGlobCollectionPattern(PROJECT_ALLOWED_EXTENSIONS);
|
|
567
|
-
return [
|
|
568
|
-
// App default files
|
|
569
|
-
`./${allowedRootPaths}/**/*.${allowedExtensions}`,
|
|
570
|
-
`!./**/node_modules/**/*`,
|
|
571
|
-
`!./**/dist/**/*`,
|
|
572
|
-
// Root package.json file
|
|
573
|
-
PROJECT_PACKAGE_JSON
|
|
574
|
-
];
|
|
575
|
-
}
|
|
576
|
-
constructor(cwd) {
|
|
577
|
-
super(cwd, { paths: AppProject.paths });
|
|
578
|
-
this.refreshStrapiVersion();
|
|
579
|
-
}
|
|
580
|
-
refresh() {
|
|
581
|
-
super.refresh();
|
|
582
|
-
this.refreshStrapiVersion();
|
|
583
|
-
return this;
|
|
584
|
-
}
|
|
585
|
-
refreshStrapiVersion() {
|
|
586
|
-
this.strapiVersion = // First try to get the strapi version from the package.json dependencies
|
|
587
|
-
this.findStrapiVersionFromProjectPackageJSON() ?? // If the version found is not a valid SemVer, get the Strapi version from the installed package
|
|
588
|
-
this.findLocallyInstalledStrapiVersion();
|
|
589
|
-
}
|
|
590
|
-
findStrapiVersionFromProjectPackageJSON() {
|
|
591
|
-
const projectName = this.packageJSON.name;
|
|
592
|
-
const version2 = this.packageJSON.dependencies?.[STRAPI_DEPENDENCY_NAME];
|
|
593
|
-
if (version2 === void 0) {
|
|
594
|
-
throw new Error(
|
|
595
|
-
`No version of ${STRAPI_DEPENDENCY_NAME} was found in ${projectName}. Are you in a valid Strapi project?`
|
|
596
|
-
);
|
|
597
|
-
}
|
|
598
|
-
const isValidSemVer2 = isLiteralSemVer(version2) && semver__default.default.valid(version2) === version2;
|
|
599
|
-
return isValidSemVer2 ? semVerFactory(version2) : void 0;
|
|
600
|
-
}
|
|
601
|
-
findLocallyInstalledStrapiVersion() {
|
|
602
|
-
const packageSearchText = `${STRAPI_DEPENDENCY_NAME}/package.json`;
|
|
603
|
-
let strapiPackageJSONPath;
|
|
604
|
-
let strapiPackageJSON;
|
|
605
|
-
try {
|
|
606
|
-
strapiPackageJSONPath = require.resolve(packageSearchText, { paths: [this.cwd] });
|
|
607
|
-
strapiPackageJSON = require(strapiPackageJSONPath);
|
|
608
|
-
assert__default.default(typeof strapiPackageJSON === "object");
|
|
609
|
-
} catch {
|
|
610
|
-
throw new Error(
|
|
611
|
-
`Cannot resolve module "${STRAPI_DEPENDENCY_NAME}" from paths [${this.cwd}]`
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
const strapiVersion = strapiPackageJSON.version;
|
|
615
|
-
if (!isValidSemVer(strapiVersion)) {
|
|
616
|
-
throw new Error(
|
|
617
|
-
`Invalid ${STRAPI_DEPENDENCY_NAME} version found in ${strapiPackageJSONPath} (${strapiVersion})`
|
|
618
|
-
);
|
|
619
|
-
}
|
|
620
|
-
return semVerFactory(strapiVersion);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
const formatGlobCollectionPattern = (collection) => {
|
|
624
|
-
assert__default.default(
|
|
625
|
-
collection.length > 0,
|
|
626
|
-
"Invalid pattern provided, the given collection needs at least 1 element"
|
|
627
|
-
);
|
|
628
|
-
return collection.length === 1 ? collection[0] : `{${collection}}`;
|
|
629
|
-
};
|
|
630
|
-
class PluginProject extends Project {
|
|
631
|
-
type = "plugin";
|
|
632
|
-
/**
|
|
633
|
-
* Returns an array of allowed file paths for a Strapi plugin
|
|
634
|
-
*
|
|
635
|
-
* The resulting paths include plugin default files, the root package.json file, and plugin-specific files.
|
|
636
|
-
*/
|
|
637
|
-
static get paths() {
|
|
638
|
-
const allowedRootPaths = formatGlobCollectionPattern(
|
|
639
|
-
PROJECT_PLUGIN_ALLOWED_ROOT_PATHS
|
|
640
|
-
);
|
|
641
|
-
const allowedExtensions = formatGlobCollectionPattern(PROJECT_ALLOWED_EXTENSIONS);
|
|
642
|
-
return [
|
|
643
|
-
// Plugin default files
|
|
644
|
-
`./${allowedRootPaths}/**/*.${allowedExtensions}`,
|
|
645
|
-
`!./**/node_modules/**/*`,
|
|
646
|
-
`!./**/dist/**/*`,
|
|
647
|
-
// Root package.json file
|
|
648
|
-
PROJECT_PACKAGE_JSON,
|
|
649
|
-
// Plugin root files
|
|
650
|
-
...PROJECT_PLUGIN_ROOT_FILES
|
|
651
|
-
];
|
|
652
|
-
}
|
|
653
|
-
constructor(cwd) {
|
|
654
|
-
super(cwd, { paths: PluginProject.paths });
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
const isPlugin = (cwd) => {
|
|
658
|
-
const packageJSONPath = path__default.default.join(cwd, PROJECT_PACKAGE_JSON);
|
|
659
|
-
try {
|
|
660
|
-
fse__default.default.accessSync(packageJSONPath);
|
|
661
|
-
} catch {
|
|
662
|
-
throw new Error(`Could not find a ${PROJECT_PACKAGE_JSON} file in ${cwd}`);
|
|
663
|
-
}
|
|
664
|
-
const packageJSONBuffer = fse__default.default.readFileSync(packageJSONPath);
|
|
665
|
-
const packageJSON = JSON.parse(packageJSONBuffer.toString());
|
|
666
|
-
return packageJSON?.strapi?.kind === "plugin";
|
|
667
|
-
};
|
|
668
|
-
const projectFactory = (cwd) => {
|
|
669
|
-
fse__default.default.accessSync(cwd);
|
|
670
|
-
return isPlugin(cwd) ? new PluginProject(cwd) : new AppProject(cwd);
|
|
671
|
-
};
|
|
672
|
-
const isApplicationProject = (project) => {
|
|
673
|
-
return project instanceof AppProject;
|
|
674
|
-
};
|
|
675
|
-
const CODEMOD_CODE_SUFFIX = "code";
|
|
676
|
-
const CODEMOD_JSON_SUFFIX = "json";
|
|
677
|
-
const CODEMOD_ALLOWED_SUFFIXES = [CODEMOD_CODE_SUFFIX, CODEMOD_JSON_SUFFIX];
|
|
678
|
-
const CODEMOD_EXTENSION = "ts";
|
|
679
|
-
const CODEMOD_FILE_REGEXP = new RegExp(
|
|
680
|
-
`^.+[.](${CODEMOD_ALLOWED_SUFFIXES.join("|")})[.]${CODEMOD_EXTENSION}$`
|
|
681
|
-
);
|
|
682
|
-
class Codemod {
|
|
683
|
-
uid;
|
|
684
|
-
kind;
|
|
685
|
-
version;
|
|
686
|
-
baseDirectory;
|
|
687
|
-
filename;
|
|
688
|
-
path;
|
|
689
|
-
constructor(options) {
|
|
690
|
-
this.kind = options.kind;
|
|
691
|
-
this.version = options.version;
|
|
692
|
-
this.baseDirectory = options.baseDirectory;
|
|
693
|
-
this.filename = options.filename;
|
|
694
|
-
this.path = path__default.default.join(this.baseDirectory, this.version.raw, this.filename);
|
|
695
|
-
this.uid = this.createUID();
|
|
696
|
-
}
|
|
697
|
-
createUID() {
|
|
698
|
-
const name = this.format({ stripExtension: true, stripKind: true, stripHyphens: false });
|
|
699
|
-
const kind = this.kind;
|
|
700
|
-
const version2 = this.version.raw;
|
|
701
|
-
return `${version2}-${name}-${kind}`;
|
|
702
|
-
}
|
|
703
|
-
format(options) {
|
|
704
|
-
const { stripExtension = true, stripKind = true, stripHyphens = true } = options ?? {};
|
|
705
|
-
let formatted = this.filename;
|
|
706
|
-
if (stripExtension) {
|
|
707
|
-
formatted = formatted.replace(new RegExp(`\\.${CODEMOD_EXTENSION}$`, "i"), "");
|
|
708
|
-
}
|
|
709
|
-
if (stripKind) {
|
|
710
|
-
formatted = formatted.replace(`.${CODEMOD_CODE_SUFFIX}`, "").replace(`.${CODEMOD_JSON_SUFFIX}`, "");
|
|
711
|
-
}
|
|
712
|
-
if (stripHyphens) {
|
|
713
|
-
formatted = formatted.replaceAll("-", " ");
|
|
714
|
-
}
|
|
715
|
-
return formatted;
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
const codemodFactory = (options) => new Codemod(options);
|
|
719
|
-
const INTERNAL_CODEMODS_DIRECTORY = path__default.default.join(
|
|
720
|
-
__dirname,
|
|
721
|
-
// upgrade/dist
|
|
722
|
-
"..",
|
|
723
|
-
// upgrade
|
|
724
|
-
"resources",
|
|
725
|
-
// upgrade/resources
|
|
726
|
-
"codemods"
|
|
727
|
-
// upgrade/resources/codemods
|
|
728
|
-
);
|
|
729
|
-
class CodemodRepository {
|
|
730
|
-
groups;
|
|
731
|
-
versions;
|
|
732
|
-
cwd;
|
|
733
|
-
constructor(cwd) {
|
|
734
|
-
assert__default.default(fse__default.default.existsSync(cwd), `Invalid codemods directory provided "${cwd}"`);
|
|
735
|
-
this.cwd = cwd;
|
|
736
|
-
this.groups = {};
|
|
737
|
-
this.versions = [];
|
|
738
|
-
}
|
|
739
|
-
refresh() {
|
|
740
|
-
this.refreshAvailableVersions();
|
|
741
|
-
this.refreshAvailableFiles();
|
|
742
|
-
return this;
|
|
743
|
-
}
|
|
744
|
-
count(version2) {
|
|
745
|
-
return this.findByVersion(version2).length;
|
|
746
|
-
}
|
|
747
|
-
versionExists(version2) {
|
|
748
|
-
return version2.raw in this.groups;
|
|
749
|
-
}
|
|
750
|
-
has(uid) {
|
|
751
|
-
const result = this.find({ uids: [uid] });
|
|
752
|
-
if (result.length !== 1) {
|
|
753
|
-
return false;
|
|
754
|
-
}
|
|
755
|
-
const { codemods } = result[0];
|
|
756
|
-
return codemods.length === 1 && codemods[0].uid === uid;
|
|
757
|
-
}
|
|
758
|
-
find(q) {
|
|
759
|
-
const entries = Object.entries(this.groups);
|
|
760
|
-
return entries.filter(maybeFilterByRange).map(([version2, codemods]) => ({
|
|
761
|
-
version: semVerFactory(version2),
|
|
762
|
-
// Filter by UID if provided in the query
|
|
763
|
-
codemods: codemods.filter(maybeFilterByUIDs)
|
|
764
|
-
})).filter(({ codemods }) => codemods.length > 0);
|
|
765
|
-
function maybeFilterByRange([version2]) {
|
|
766
|
-
if (!isRangeInstance(q.range)) {
|
|
767
|
-
return true;
|
|
768
|
-
}
|
|
769
|
-
return q.range.test(version2);
|
|
770
|
-
}
|
|
771
|
-
function maybeFilterByUIDs(codemod) {
|
|
772
|
-
if (q.uids === void 0) {
|
|
773
|
-
return true;
|
|
774
|
-
}
|
|
775
|
-
return q.uids.includes(codemod.uid);
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
findByVersion(version2) {
|
|
779
|
-
const literalVersion = version2.raw;
|
|
780
|
-
const codemods = this.groups[literalVersion];
|
|
781
|
-
return codemods ?? [];
|
|
782
|
-
}
|
|
783
|
-
findAll() {
|
|
784
|
-
const entries = Object.entries(this.groups);
|
|
785
|
-
return entries.map(([version2, codemods]) => ({
|
|
786
|
-
version: semVerFactory(version2),
|
|
787
|
-
codemods
|
|
788
|
-
}));
|
|
789
|
-
}
|
|
790
|
-
refreshAvailableVersions() {
|
|
791
|
-
this.versions = fse__default.default.readdirSync(this.cwd).filter((filename) => fse__default.default.statSync(path__default.default.join(this.cwd, filename)).isDirectory()).filter((filename) => semver__default.default.valid(filename) !== null).map((version2) => semVerFactory(version2)).sort(semver__default.default.compare);
|
|
792
|
-
return this;
|
|
793
|
-
}
|
|
794
|
-
refreshAvailableFiles() {
|
|
795
|
-
this.groups = {};
|
|
796
|
-
for (const version2 of this.versions) {
|
|
797
|
-
this.refreshAvailableFilesForVersion(version2);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
refreshAvailableFilesForVersion(version2) {
|
|
801
|
-
const literalVersion = version2.raw;
|
|
802
|
-
const versionDirectory = path__default.default.join(this.cwd, literalVersion);
|
|
803
|
-
if (!fse__default.default.existsSync(versionDirectory)) {
|
|
804
|
-
return;
|
|
805
|
-
}
|
|
806
|
-
this.groups[literalVersion] = fse__default.default.readdirSync(versionDirectory).filter((filename) => fse__default.default.statSync(path__default.default.join(versionDirectory, filename)).isFile()).filter((filename) => CODEMOD_FILE_REGEXP.test(filename)).map((filename) => {
|
|
807
|
-
const kind = parseCodemodKindFromFilename(filename);
|
|
808
|
-
const baseDirectory = this.cwd;
|
|
809
|
-
return codemodFactory({ kind, baseDirectory, version: version2, filename });
|
|
810
|
-
});
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
const parseCodemodKindFromFilename = (filename) => {
|
|
814
|
-
const kind = filename.split(".").at(-2);
|
|
815
|
-
assert__default.default(kind !== void 0);
|
|
816
|
-
assert__default.default(CODEMOD_ALLOWED_SUFFIXES.includes(kind));
|
|
817
|
-
return kind;
|
|
818
|
-
};
|
|
819
|
-
const codemodRepositoryFactory = (cwd = INTERNAL_CODEMODS_DIRECTORY) => {
|
|
820
|
-
return new CodemodRepository(cwd);
|
|
821
112
|
};
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
this.project = project;
|
|
830
|
-
this.range = range;
|
|
831
|
-
this.isDry = false;
|
|
832
|
-
this.logger = null;
|
|
833
|
-
this.selectCodemodsCallback = null;
|
|
834
|
-
}
|
|
835
|
-
setRange(range) {
|
|
836
|
-
this.range = range;
|
|
837
|
-
return this;
|
|
838
|
-
}
|
|
839
|
-
setLogger(logger) {
|
|
840
|
-
this.logger = logger;
|
|
841
|
-
return this;
|
|
842
|
-
}
|
|
843
|
-
onSelectCodemods(callback) {
|
|
844
|
-
this.selectCodemodsCallback = callback;
|
|
845
|
-
return this;
|
|
846
|
-
}
|
|
847
|
-
dry(enabled = true) {
|
|
848
|
-
this.isDry = enabled;
|
|
849
|
-
return this;
|
|
850
|
-
}
|
|
851
|
-
createRepository(codemodsDirectory) {
|
|
852
|
-
const repository = codemodRepositoryFactory(
|
|
853
|
-
codemodsDirectory ?? INTERNAL_CODEMODS_DIRECTORY
|
|
854
|
-
);
|
|
855
|
-
repository.refresh();
|
|
856
|
-
return repository;
|
|
857
|
-
}
|
|
858
|
-
async safeRunAndReport(codemods) {
|
|
859
|
-
if (this.isDry) {
|
|
860
|
-
this.logger?.warn?.(
|
|
861
|
-
"Running the codemods in dry mode. No files will be modified during the process."
|
|
862
|
-
);
|
|
863
|
-
}
|
|
864
|
-
try {
|
|
865
|
-
const reports$1 = await this.project.runCodemods(codemods, { dry: this.isDry });
|
|
866
|
-
this.logger?.raw?.(reports(reports$1));
|
|
867
|
-
if (!this.isDry) {
|
|
868
|
-
const nbAffectedTotal = reports$1.flatMap((report) => report.report.ok).reduce((acc, nb) => acc + nb, 0);
|
|
869
|
-
this.logger?.debug?.(
|
|
870
|
-
`Successfully ran ${highlight(codemods.length)} codemod(s), ${highlight(nbAffectedTotal)} change(s) have been detected`
|
|
871
|
-
);
|
|
872
|
-
}
|
|
873
|
-
return successReport$1();
|
|
874
|
-
} catch (e) {
|
|
875
|
-
return erroredReport$1(unknownToError(e));
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
async runByUID(uid, codemodsDirectory) {
|
|
879
|
-
const repository = this.createRepository(codemodsDirectory);
|
|
880
|
-
if (!repository.has(uid)) {
|
|
881
|
-
throw new Error(`Unknown codemod UID provided: ${uid}`);
|
|
882
|
-
}
|
|
883
|
-
const codemods = repository.find({ uids: [uid] }).flatMap(({ codemods: codemods2 }) => codemods2);
|
|
884
|
-
return this.safeRunAndReport(codemods);
|
|
885
|
-
}
|
|
886
|
-
async run(codemodsDirectory) {
|
|
887
|
-
const repository = this.createRepository(codemodsDirectory);
|
|
888
|
-
const codemodsInRange = repository.find({ range: this.range });
|
|
889
|
-
const selectedCodemods = this.selectCodemodsCallback ? await this.selectCodemodsCallback(codemodsInRange) : codemodsInRange;
|
|
890
|
-
if (selectedCodemods.length === 0) {
|
|
891
|
-
this.logger?.debug?.(`Found no codemods to run for ${versionRange(this.range)}`);
|
|
892
|
-
return successReport$1();
|
|
893
|
-
}
|
|
894
|
-
const codemods = selectedCodemods.flatMap(({ codemods: codemods2 }) => codemods2);
|
|
895
|
-
const codemodsByVersion = fp.groupBy("version", codemods);
|
|
896
|
-
const fRange = versionRange(this.range);
|
|
897
|
-
this.logger?.debug?.(
|
|
898
|
-
`Found ${highlight(codemods.length)} codemods for ${highlight(fp.size(codemodsByVersion))} version(s) using ${fRange}`
|
|
899
|
-
);
|
|
900
|
-
for (const [version2, codemods2] of Object.entries(codemodsByVersion)) {
|
|
901
|
-
this.logger?.debug?.(`- ${version$1(semVerFactory(version2))} (${codemods2.length})`);
|
|
902
|
-
}
|
|
903
|
-
return this.safeRunAndReport(codemods);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
const codemodRunnerFactory = (project, range) => {
|
|
907
|
-
return new CodemodRunner(project, range);
|
|
908
|
-
};
|
|
909
|
-
const successReport$1 = () => ({ success: true, error: null });
|
|
910
|
-
const erroredReport$1 = (error) => ({ success: false, error });
|
|
911
|
-
class Upgrader {
|
|
912
|
-
project;
|
|
913
|
-
npmPackage;
|
|
914
|
-
target;
|
|
915
|
-
codemodsTarget;
|
|
916
|
-
isDry;
|
|
917
|
-
logger;
|
|
918
|
-
requirements;
|
|
919
|
-
confirmationCallback;
|
|
920
|
-
constructor(project, target, npmPackage) {
|
|
921
|
-
this.project = project;
|
|
922
|
-
this.npmPackage = npmPackage;
|
|
923
|
-
this.target = target;
|
|
924
|
-
this.syncCodemodsTarget();
|
|
925
|
-
this.isDry = false;
|
|
926
|
-
this.requirements = [];
|
|
927
|
-
this.logger = null;
|
|
928
|
-
this.confirmationCallback = null;
|
|
929
|
-
}
|
|
930
|
-
getNPMPackage() {
|
|
931
|
-
return this.npmPackage;
|
|
932
|
-
}
|
|
933
|
-
getProject() {
|
|
934
|
-
return this.project;
|
|
935
|
-
}
|
|
936
|
-
getTarget() {
|
|
937
|
-
return semVerFactory(this.target.raw);
|
|
938
|
-
}
|
|
939
|
-
setRequirements(requirements) {
|
|
940
|
-
this.requirements = requirements;
|
|
941
|
-
return this;
|
|
942
|
-
}
|
|
943
|
-
setTarget(target) {
|
|
944
|
-
this.target = target;
|
|
945
|
-
return this;
|
|
946
|
-
}
|
|
947
|
-
syncCodemodsTarget() {
|
|
948
|
-
this.codemodsTarget = semVerFactory(
|
|
949
|
-
`${this.target.major}.${this.target.minor}.${this.target.patch}`
|
|
950
|
-
);
|
|
951
|
-
this.logger?.debug?.(
|
|
952
|
-
`The codemods target has been synced with the upgrade target. The codemod runner will now look for ${version$1(
|
|
953
|
-
this.codemodsTarget
|
|
954
|
-
)}`
|
|
955
|
-
);
|
|
956
|
-
return this;
|
|
957
|
-
}
|
|
958
|
-
overrideCodemodsTarget(target) {
|
|
959
|
-
this.codemodsTarget = target;
|
|
960
|
-
this.logger?.debug?.(
|
|
961
|
-
`Overriding the codemods target. The codemod runner will now look for ${version$1(target)}`
|
|
962
|
-
);
|
|
963
|
-
return this;
|
|
964
|
-
}
|
|
965
|
-
setLogger(logger) {
|
|
966
|
-
this.logger = logger;
|
|
967
|
-
return this;
|
|
968
|
-
}
|
|
969
|
-
onConfirm(callback) {
|
|
970
|
-
this.confirmationCallback = callback;
|
|
971
|
-
return this;
|
|
972
|
-
}
|
|
973
|
-
dry(enabled = true) {
|
|
974
|
-
this.isDry = enabled;
|
|
975
|
-
return this;
|
|
976
|
-
}
|
|
977
|
-
addRequirement(requirement) {
|
|
978
|
-
this.requirements.push(requirement);
|
|
979
|
-
const fRequired = requirement.isRequired ? "(required)" : "(optional)";
|
|
980
|
-
this.logger?.debug?.(
|
|
981
|
-
`Added a new requirement to the upgrade: ${highlight(requirement.name)} ${fRequired}`
|
|
982
|
-
);
|
|
983
|
-
return this;
|
|
984
|
-
}
|
|
985
|
-
async upgrade() {
|
|
986
|
-
this.logger?.info?.(
|
|
987
|
-
`Upgrading from ${version$1(this.project.strapiVersion)} to ${version$1(this.target)}`
|
|
988
|
-
);
|
|
989
|
-
if (this.isDry) {
|
|
990
|
-
this.logger?.warn?.(
|
|
991
|
-
"Running the upgrade in dry mode. No files will be modified during the process."
|
|
992
|
-
);
|
|
993
|
-
}
|
|
994
|
-
const range = rangeFromVersions(this.project.strapiVersion, this.target);
|
|
995
|
-
const codemodsRange = rangeFromVersions(this.project.strapiVersion, this.codemodsTarget);
|
|
996
|
-
const npmVersionsMatches = this.npmPackage?.findVersionsInRange(range) ?? [];
|
|
997
|
-
this.logger?.debug?.(
|
|
998
|
-
`Found ${highlight(npmVersionsMatches.length)} versions satisfying ${versionRange(range)}`
|
|
999
|
-
);
|
|
1000
|
-
try {
|
|
1001
|
-
this.logger?.info?.(upgradeStep("Checking requirement", [1, 4]));
|
|
1002
|
-
await this.checkRequirements(this.requirements, {
|
|
1003
|
-
npmVersionsMatches,
|
|
1004
|
-
project: this.project,
|
|
1005
|
-
target: this.target
|
|
1006
|
-
});
|
|
1007
|
-
this.logger?.info?.(upgradeStep("Applying the latest code modifications", [2, 4]));
|
|
1008
|
-
await this.runCodemods(codemodsRange);
|
|
1009
|
-
this.logger?.debug?.("Refreshing project information...");
|
|
1010
|
-
this.project.refresh();
|
|
1011
|
-
this.logger?.info?.(upgradeStep("Upgrading Strapi dependencies", [3, 4]));
|
|
1012
|
-
await this.updateDependencies();
|
|
1013
|
-
this.logger?.info?.(upgradeStep("Installing dependencies", [4, 4]));
|
|
1014
|
-
await this.installDependencies();
|
|
1015
|
-
} catch (e) {
|
|
1016
|
-
return erroredReport(unknownToError(e));
|
|
1017
|
-
}
|
|
1018
|
-
return successReport();
|
|
1019
|
-
}
|
|
1020
|
-
async confirm(message) {
|
|
1021
|
-
if (typeof this.confirmationCallback !== "function") {
|
|
1022
|
-
return true;
|
|
1023
|
-
}
|
|
1024
|
-
return this.confirmationCallback(message);
|
|
1025
|
-
}
|
|
1026
|
-
async checkRequirements(requirements, context) {
|
|
1027
|
-
for (const requirement of requirements) {
|
|
1028
|
-
const { pass, error } = await requirement.test(context);
|
|
1029
|
-
if (pass) {
|
|
1030
|
-
await this.onSuccessfulRequirement(requirement, context);
|
|
1031
|
-
} else {
|
|
1032
|
-
await this.onFailedRequirement(requirement, error);
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
async onSuccessfulRequirement(requirement, context) {
|
|
1037
|
-
const hasChildren = requirement.children.length > 0;
|
|
1038
|
-
if (hasChildren) {
|
|
1039
|
-
await this.checkRequirements(requirement.children, context);
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
async onFailedRequirement(requirement, originalError) {
|
|
1043
|
-
const errorMessage = `Requirement failed: ${originalError.message} (${highlight(
|
|
1044
|
-
requirement.name
|
|
1045
|
-
)})`;
|
|
1046
|
-
const warningMessage = originalError.message;
|
|
1047
|
-
const confirmationMessage = `Ignore optional requirement "${highlight(requirement.name)}" ?`;
|
|
1048
|
-
const error = new Error(errorMessage);
|
|
1049
|
-
if (requirement.isRequired) {
|
|
1050
|
-
throw error;
|
|
1051
|
-
}
|
|
1052
|
-
this.logger?.warn?.(warningMessage);
|
|
1053
|
-
const response = await this.confirmationCallback?.(confirmationMessage);
|
|
1054
|
-
if (!response) {
|
|
1055
|
-
throw error;
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
async updateDependencies() {
|
|
1059
|
-
const { packageJSON, packageJSONPath } = this.project;
|
|
1060
|
-
const json = createJSONTransformAPI(packageJSON);
|
|
1061
|
-
const dependencies = json.get("dependencies", {});
|
|
1062
|
-
const strapiDependencies = this.getScopedStrapiDependencies(dependencies);
|
|
1063
|
-
this.logger?.debug?.(
|
|
1064
|
-
`Found ${highlight(strapiDependencies.length)} dependency(ies) to update`
|
|
1065
|
-
);
|
|
1066
|
-
strapiDependencies.forEach(
|
|
1067
|
-
(dependency) => this.logger?.debug?.(`- ${dependency[0]} (${dependency[1]} -> ${this.target})`)
|
|
1068
|
-
);
|
|
1069
|
-
if (strapiDependencies.length === 0) {
|
|
1070
|
-
return;
|
|
1071
|
-
}
|
|
1072
|
-
strapiDependencies.forEach(([name]) => json.set(`dependencies.${name}`, this.target.raw));
|
|
1073
|
-
const updatedPackageJSON = json.root();
|
|
1074
|
-
if (this.isDry) {
|
|
1075
|
-
this.logger?.debug?.(`Skipping dependencies update (${chalk__default.default.italic("dry mode")})`);
|
|
1076
|
-
return;
|
|
1077
|
-
}
|
|
1078
|
-
await saveJSON(packageJSONPath, updatedPackageJSON);
|
|
1079
|
-
}
|
|
1080
|
-
getScopedStrapiDependencies(dependencies) {
|
|
1081
|
-
const { strapiVersion } = this.project;
|
|
1082
|
-
const strapiDependencies = [];
|
|
1083
|
-
for (const [name, version2] of Object.entries(dependencies)) {
|
|
1084
|
-
const isScopedStrapiPackage = name.startsWith(SCOPED_STRAPI_PACKAGE_PREFIX);
|
|
1085
|
-
const isOnCurrentStrapiVersion = isValidSemVer(version2) && version2 === strapiVersion.raw;
|
|
1086
|
-
if (isScopedStrapiPackage && isOnCurrentStrapiVersion) {
|
|
1087
|
-
strapiDependencies.push([name, semVerFactory(version2)]);
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
return strapiDependencies;
|
|
1091
|
-
}
|
|
1092
|
-
async installDependencies() {
|
|
1093
|
-
const projectPath = this.project.cwd;
|
|
1094
|
-
const packageManagerName = await utils.packageManager.getPreferred(projectPath);
|
|
1095
|
-
this.logger?.debug?.(`Using ${highlight(packageManagerName)} as package manager`);
|
|
1096
|
-
if (this.isDry) {
|
|
1097
|
-
this.logger?.debug?.(`Skipping dependencies installation (${chalk__default.default.italic("dry mode")})`);
|
|
1098
|
-
return;
|
|
1099
|
-
}
|
|
1100
|
-
await utils.packageManager.installDependencies(projectPath, packageManagerName, {
|
|
1101
|
-
stdout: this.logger?.stdout,
|
|
1102
|
-
stderr: this.logger?.stderr
|
|
113
|
+
|
|
114
|
+
const DEFAULT_TARGET = logger.ReleaseType.Major;
|
|
115
|
+
const runCodemods = async (options)=>{
|
|
116
|
+
const { silent, debug } = options;
|
|
117
|
+
const logger$1 = logger.loggerFactory({
|
|
118
|
+
silent,
|
|
119
|
+
debug
|
|
1103
120
|
});
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
}
|
|
1114
|
-
const resolveNPMTarget = (project, target, npmPackage) => {
|
|
1115
|
-
if (isSemverInstance(target)) {
|
|
1116
|
-
const version2 = npmPackage.findVersion(target);
|
|
1117
|
-
if (!version2) {
|
|
1118
|
-
throw new NPMCandidateNotFoundError(target);
|
|
1119
|
-
}
|
|
1120
|
-
return version2;
|
|
1121
|
-
}
|
|
1122
|
-
if (isSemVerReleaseType(target)) {
|
|
1123
|
-
const range = rangeFromVersions(project.strapiVersion, target);
|
|
1124
|
-
const npmVersionsMatches = npmPackage.findVersionsInRange(range);
|
|
1125
|
-
const version2 = npmVersionsMatches.at(-1);
|
|
1126
|
-
if (!version2) {
|
|
1127
|
-
throw new NPMCandidateNotFoundError(range, `The project is already up-to-date (${target})`);
|
|
1128
|
-
}
|
|
1129
|
-
return version2;
|
|
1130
|
-
}
|
|
1131
|
-
throw new NPMCandidateNotFoundError(target);
|
|
1132
|
-
};
|
|
1133
|
-
const upgraderFactory = (project, target, npmPackage) => {
|
|
1134
|
-
const npmTarget = resolveNPMTarget(project, target, npmPackage);
|
|
1135
|
-
const semverTarget = semVerFactory(npmTarget.version);
|
|
1136
|
-
if (semver__default.default.eq(semverTarget, project.strapiVersion)) {
|
|
1137
|
-
throw new Error(`The project is already using v${semverTarget}`);
|
|
1138
|
-
}
|
|
1139
|
-
return new Upgrader(project, semverTarget, npmPackage);
|
|
1140
|
-
};
|
|
1141
|
-
const successReport = () => ({ success: true, error: null });
|
|
1142
|
-
const erroredReport = (error) => ({ success: false, error });
|
|
1143
|
-
const STRAPI_PACKAGE_NAME = "@strapi/strapi";
|
|
1144
|
-
class Requirement {
|
|
1145
|
-
isRequired;
|
|
1146
|
-
name;
|
|
1147
|
-
testCallback;
|
|
1148
|
-
children;
|
|
1149
|
-
constructor(name, testCallback, isRequired) {
|
|
1150
|
-
this.name = name;
|
|
1151
|
-
this.testCallback = testCallback;
|
|
1152
|
-
this.isRequired = isRequired ?? true;
|
|
1153
|
-
this.children = [];
|
|
1154
|
-
}
|
|
1155
|
-
setChildren(children) {
|
|
1156
|
-
this.children = children;
|
|
1157
|
-
return this;
|
|
1158
|
-
}
|
|
1159
|
-
addChild(child) {
|
|
1160
|
-
this.children.push(child);
|
|
1161
|
-
return this;
|
|
1162
|
-
}
|
|
1163
|
-
asOptional() {
|
|
1164
|
-
const newInstance = requirementFactory(this.name, this.testCallback, false);
|
|
1165
|
-
newInstance.setChildren(this.children);
|
|
1166
|
-
return newInstance;
|
|
1167
|
-
}
|
|
1168
|
-
asRequired() {
|
|
1169
|
-
const newInstance = requirementFactory(this.name, this.testCallback, true);
|
|
1170
|
-
newInstance.setChildren(this.children);
|
|
1171
|
-
return newInstance;
|
|
1172
|
-
}
|
|
1173
|
-
async test(context) {
|
|
1174
|
-
try {
|
|
1175
|
-
await this.testCallback?.(context);
|
|
1176
|
-
return ok();
|
|
1177
|
-
} catch (e) {
|
|
1178
|
-
if (e instanceof Error) {
|
|
1179
|
-
return errored(e);
|
|
1180
|
-
}
|
|
1181
|
-
if (typeof e === "string") {
|
|
1182
|
-
return errored(new Error(e));
|
|
1183
|
-
}
|
|
1184
|
-
return errored(new Error("Unknown error"));
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
const ok = () => ({ pass: true, error: null });
|
|
1189
|
-
const errored = (error) => ({ pass: false, error });
|
|
1190
|
-
const requirementFactory = (name, testCallback, isRequired) => new Requirement(name, testCallback, isRequired);
|
|
1191
|
-
const REQUIRE_AVAILABLE_NEXT_MAJOR = requirementFactory(
|
|
1192
|
-
"REQUIRE_AVAILABLE_NEXT_MAJOR",
|
|
1193
|
-
(context) => {
|
|
1194
|
-
const { project, target } = context;
|
|
1195
|
-
const currentMajor = project.strapiVersion.major;
|
|
1196
|
-
const targetedMajor = target.major;
|
|
1197
|
-
if (targetedMajor === currentMajor) {
|
|
1198
|
-
throw new Error(`You're already on the latest major version (v${currentMajor})`);
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
);
|
|
1202
|
-
const REQUIRE_LATEST_FOR_CURRENT_MAJOR = requirementFactory(
|
|
1203
|
-
"REQUIRE_LATEST_FOR_CURRENT_MAJOR",
|
|
1204
|
-
(context) => {
|
|
1205
|
-
const { project, target, npmVersionsMatches } = context;
|
|
1206
|
-
const { major: currentMajor } = project.strapiVersion;
|
|
1207
|
-
const invalidMatches = npmVersionsMatches.filter(
|
|
1208
|
-
(match) => semVerFactory(match.version).major === currentMajor
|
|
1209
|
-
);
|
|
1210
|
-
if (invalidMatches.length > 0) {
|
|
1211
|
-
const invalidVersions = invalidMatches.map((match) => match.version);
|
|
1212
|
-
const invalidVersionsCount = invalidVersions.length;
|
|
1213
|
-
throw new Error(
|
|
1214
|
-
`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.`
|
|
1215
|
-
);
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
);
|
|
1219
|
-
const REQUIRE_GIT_CLEAN_REPOSITORY = requirementFactory(
|
|
1220
|
-
"REQUIRE_GIT_CLEAN_REPOSITORY",
|
|
1221
|
-
async (context) => {
|
|
1222
|
-
const git = simpleGit__default.default({ baseDir: context.project.cwd });
|
|
1223
|
-
const status = await git.status();
|
|
1224
|
-
if (!status.isClean()) {
|
|
1225
|
-
throw new Error(
|
|
1226
|
-
"Repository is not clean. Please commit or stash any changes before upgrading"
|
|
1227
|
-
);
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
);
|
|
1231
|
-
const REQUIRE_GIT_REPOSITORY = requirementFactory(
|
|
1232
|
-
"REQUIRE_GIT_REPOSITORY",
|
|
1233
|
-
async (context) => {
|
|
1234
|
-
const git = simpleGit__default.default({ baseDir: context.project.cwd });
|
|
1235
|
-
const isRepo = await git.checkIsRepo();
|
|
1236
|
-
if (!isRepo) {
|
|
1237
|
-
throw new Error("Not a git repository (or any of the parent directories)");
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
).addChild(REQUIRE_GIT_CLEAN_REPOSITORY.asOptional());
|
|
1241
|
-
const REQUIRE_GIT_INSTALLED = requirementFactory(
|
|
1242
|
-
"REQUIRE_GIT_INSTALLED",
|
|
1243
|
-
async (context) => {
|
|
1244
|
-
const git = simpleGit__default.default({ baseDir: context.project.cwd });
|
|
1245
|
-
try {
|
|
1246
|
-
await git.version();
|
|
1247
|
-
} catch {
|
|
1248
|
-
throw new Error("Git is not installed");
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
).addChild(REQUIRE_GIT_REPOSITORY.asOptional());
|
|
1252
|
-
const REQUIRE_GIT = requirementFactory("REQUIRE_GIT", null).addChild(
|
|
1253
|
-
REQUIRE_GIT_INSTALLED.asOptional()
|
|
1254
|
-
);
|
|
1255
|
-
const latest = async (upgrader, options) => {
|
|
1256
|
-
if (options.target !== ReleaseType.Latest) {
|
|
1257
|
-
return;
|
|
1258
|
-
}
|
|
1259
|
-
const npmPackage = upgrader.getNPMPackage();
|
|
1260
|
-
const target = upgrader.getTarget();
|
|
1261
|
-
const project = upgrader.getProject();
|
|
1262
|
-
const { strapiVersion: current } = project;
|
|
1263
|
-
const fTargetMajor = highlight(`v${target.major}`);
|
|
1264
|
-
const fCurrentMajor = highlight(`v${current.major}`);
|
|
1265
|
-
const fTarget = version$1(target);
|
|
1266
|
-
const fCurrent = version$1(current);
|
|
1267
|
-
const isMajorUpgrade = target.major > current.major;
|
|
1268
|
-
if (isMajorUpgrade) {
|
|
1269
|
-
options.logger.warn(
|
|
1270
|
-
`Detected a major upgrade for the "${highlight(ReleaseType.Latest)}" tag: ${fCurrent} > ${fTarget}`
|
|
1271
|
-
);
|
|
1272
|
-
const newerPackageRelease = npmPackage.findVersionsInRange(rangeFactory(`>${current.raw} <${target.major}`)).at(-1);
|
|
1273
|
-
if (newerPackageRelease) {
|
|
1274
|
-
const fLatest = version$1(semVerFactory(newerPackageRelease.version));
|
|
1275
|
-
options.logger.warn(
|
|
1276
|
-
`It's recommended to first upgrade to the latest version of ${fCurrentMajor} (${fLatest}) before upgrading to ${fTargetMajor}.`
|
|
1277
|
-
);
|
|
1278
|
-
}
|
|
1279
|
-
const proceedAnyway = await upgrader.confirm(`I know what I'm doing. Proceed anyway!`);
|
|
1280
|
-
if (!proceedAnyway) {
|
|
1281
|
-
throw new AbortedError();
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
};
|
|
1285
|
-
const upgrade$1 = async (options) => {
|
|
1286
|
-
const timer = timerFactory();
|
|
1287
|
-
const { logger, codemodsTarget } = options;
|
|
1288
|
-
const cwd = path__default.default.resolve(options.cwd ?? process.cwd());
|
|
1289
|
-
const project = projectFactory(cwd);
|
|
1290
|
-
logger.debug(projectDetails(project));
|
|
1291
|
-
if (!isApplicationProject(project)) {
|
|
1292
|
-
throw new Error(
|
|
1293
|
-
`The "${options.target}" upgrade can only be run on a Strapi project; for plugins, please use "codemods".`
|
|
1294
|
-
);
|
|
1295
|
-
}
|
|
1296
|
-
logger.debug(
|
|
1297
|
-
`Application: VERSION=${version$1(project.packageJSON.version)}; STRAPI_VERSION=${version$1(project.strapiVersion)}`
|
|
1298
|
-
);
|
|
1299
|
-
const npmPackage = npmPackageFactory(STRAPI_PACKAGE_NAME);
|
|
1300
|
-
await npmPackage.refresh();
|
|
1301
|
-
const upgrader = upgraderFactory(project, options.target, npmPackage).dry(options.dry ?? false).onConfirm(options.confirm ?? null).setLogger(logger);
|
|
1302
|
-
if (codemodsTarget !== void 0) {
|
|
1303
|
-
upgrader.overrideCodemodsTarget(codemodsTarget);
|
|
1304
|
-
}
|
|
1305
|
-
await runUpgradePrompts(upgrader, options);
|
|
1306
|
-
addUpgradeRequirements(upgrader, options);
|
|
1307
|
-
const upgradeReport = await upgrader.upgrade();
|
|
1308
|
-
if (!upgradeReport.success) {
|
|
1309
|
-
throw upgradeReport.error;
|
|
1310
|
-
}
|
|
1311
|
-
timer.stop();
|
|
1312
|
-
logger.info(`Completed in ${durationMs(timer.elapsedMs)}ms`);
|
|
1313
|
-
};
|
|
1314
|
-
const runUpgradePrompts = async (upgrader, options) => {
|
|
1315
|
-
if (options.target === ReleaseType.Latest) {
|
|
1316
|
-
await latest(upgrader, options);
|
|
1317
|
-
}
|
|
1318
|
-
};
|
|
1319
|
-
const addUpgradeRequirements = (upgrader, options) => {
|
|
1320
|
-
if (options.target === ReleaseType.Major) {
|
|
1321
|
-
upgrader.addRequirement(REQUIRE_AVAILABLE_NEXT_MAJOR).addRequirement(REQUIRE_LATEST_FOR_CURRENT_MAJOR);
|
|
1322
|
-
}
|
|
1323
|
-
upgrader.addRequirement(REQUIRE_GIT.asOptional());
|
|
1324
|
-
};
|
|
1325
|
-
const resolvePath = (cwd) => path__default.default.resolve(cwd ?? process.cwd());
|
|
1326
|
-
const getRangeFromTarget = (currentVersion, target) => {
|
|
1327
|
-
if (isSemverInstance(target)) {
|
|
1328
|
-
return rangeFactory(target);
|
|
1329
|
-
}
|
|
1330
|
-
const { major, minor, patch } = currentVersion;
|
|
1331
|
-
switch (target) {
|
|
1332
|
-
case ReleaseType.Latest:
|
|
1333
|
-
throw new Error("Can't use <latest> to create a codemods range: not implemented");
|
|
1334
|
-
case ReleaseType.Major:
|
|
1335
|
-
return rangeFactory(`${major}`);
|
|
1336
|
-
case ReleaseType.Minor:
|
|
1337
|
-
return rangeFactory(`${major}.${minor}`);
|
|
1338
|
-
case ReleaseType.Patch:
|
|
1339
|
-
return rangeFactory(`${major}.${minor}.${patch}`);
|
|
1340
|
-
default:
|
|
1341
|
-
throw new Error(`Invalid target set: ${target}`);
|
|
1342
|
-
}
|
|
1343
|
-
};
|
|
1344
|
-
const findRangeFromTarget = (project, target) => {
|
|
1345
|
-
if (isRangeInstance(target)) {
|
|
1346
|
-
return target;
|
|
1347
|
-
}
|
|
1348
|
-
if (isApplicationProject(project)) {
|
|
1349
|
-
return getRangeFromTarget(project.strapiVersion, target);
|
|
1350
|
-
}
|
|
1351
|
-
return rangeFactory("*");
|
|
1352
|
-
};
|
|
1353
|
-
const runCodemods$1 = async (options) => {
|
|
1354
|
-
const timer = timerFactory();
|
|
1355
|
-
const { logger, uid } = options;
|
|
1356
|
-
const cwd = resolvePath(options.cwd);
|
|
1357
|
-
const project = projectFactory(cwd);
|
|
1358
|
-
const range = findRangeFromTarget(project, options.target);
|
|
1359
|
-
logger.debug(projectDetails(project));
|
|
1360
|
-
logger.debug(`Range: set to ${versionRange(range)}`);
|
|
1361
|
-
const codemodRunner = codemodRunnerFactory(project, range).dry(options.dry ?? false).onSelectCodemods(options.selectCodemods ?? null).setLogger(logger);
|
|
1362
|
-
let report;
|
|
1363
|
-
if (uid !== void 0) {
|
|
1364
|
-
logger.debug(`Running a single codemod: ${codemodUID(uid)}`);
|
|
1365
|
-
report = await codemodRunner.runByUID(uid);
|
|
1366
|
-
} else {
|
|
1367
|
-
report = await codemodRunner.run();
|
|
1368
|
-
}
|
|
1369
|
-
if (!report.success) {
|
|
1370
|
-
throw report.error;
|
|
1371
|
-
}
|
|
1372
|
-
timer.stop();
|
|
1373
|
-
logger.info(`Completed in ${timer.elapsedMs}`);
|
|
1374
|
-
};
|
|
1375
|
-
const listCodemods$1 = async (options) => {
|
|
1376
|
-
const { logger, target } = options;
|
|
1377
|
-
const cwd = resolvePath(options.cwd);
|
|
1378
|
-
const project = projectFactory(cwd);
|
|
1379
|
-
const range = findRangeFromTarget(project, target);
|
|
1380
|
-
logger.debug(projectDetails(project));
|
|
1381
|
-
logger.debug(`Range: set to ${versionRange(range)}`);
|
|
1382
|
-
const repo = codemodRepositoryFactory();
|
|
1383
|
-
repo.refresh();
|
|
1384
|
-
const groups = repo.find({ range });
|
|
1385
|
-
const codemods = groups.flatMap((collection) => collection.codemods);
|
|
1386
|
-
logger.debug(`Found ${highlight(codemods.length)} codemods`);
|
|
1387
|
-
if (codemods.length === 0) {
|
|
1388
|
-
logger.info(`Found no codemods matching ${versionRange(range)}`);
|
|
1389
|
-
return;
|
|
1390
|
-
}
|
|
1391
|
-
const fCodemods = codemodList(codemods);
|
|
1392
|
-
logger.raw(fCodemods);
|
|
1393
|
-
};
|
|
1394
|
-
const projectPathOption = new commander.Option(
|
|
1395
|
-
"-p, --project-path <project-path>",
|
|
1396
|
-
"Root path to the Strapi application or plugin"
|
|
1397
|
-
);
|
|
1398
|
-
const dryOption = new commander.Option(
|
|
1399
|
-
"-n, --dry",
|
|
1400
|
-
"Simulate the upgrade without updating any files"
|
|
1401
|
-
).default(false);
|
|
1402
|
-
const debugOption = new commander.Option("-d, --debug", "Get more logs in debug mode").default(false);
|
|
1403
|
-
const silentOption = new commander.Option("-s, --silent", "Don't log anything").default(false);
|
|
1404
|
-
const autoConfirmOption = new commander.Option(
|
|
1405
|
-
"-y, --yes",
|
|
1406
|
-
'Automatically answer "yes" to any prompts that the CLI might print on the command line.'
|
|
1407
|
-
).default(false);
|
|
1408
|
-
const rangeOption = new commander.Option(
|
|
1409
|
-
"-r, --range <range>",
|
|
1410
|
-
"Use a custom semver range for the codemods execution."
|
|
1411
|
-
).argParser((range) => {
|
|
1412
|
-
if (!isValidStringifiedRange(range)) {
|
|
1413
|
-
throw new commander.InvalidArgumentError("Expected a valid semver range");
|
|
1414
|
-
}
|
|
1415
|
-
return rangeFactory(range);
|
|
1416
|
-
});
|
|
1417
|
-
const upgrade = async (options) => {
|
|
1418
|
-
try {
|
|
1419
|
-
const { silent, debug, yes } = options;
|
|
1420
|
-
const logger = loggerFactory({ silent, debug });
|
|
1421
|
-
logger.warn(
|
|
1422
|
-
"Please make sure you've created a backup of your codebase and files before upgrading"
|
|
1423
|
-
);
|
|
1424
|
-
const confirm = async (message) => {
|
|
1425
|
-
if (yes) {
|
|
1426
|
-
return true;
|
|
1427
|
-
}
|
|
1428
|
-
const { confirm: confirm2 } = await prompts__default.default({
|
|
1429
|
-
name: "confirm",
|
|
1430
|
-
type: "confirm",
|
|
1431
|
-
message
|
|
1432
|
-
});
|
|
1433
|
-
return confirm2 ?? false;
|
|
121
|
+
logger$1.warn("Please make sure you've created a backup of your codebase and files before running the codemods");
|
|
122
|
+
const confirm = async (message)=>{
|
|
123
|
+
const { confirm } = await prompts({
|
|
124
|
+
name: 'confirm',
|
|
125
|
+
type: 'confirm',
|
|
126
|
+
message
|
|
127
|
+
});
|
|
128
|
+
// If confirm is undefined (Ctrl + C), default to false
|
|
129
|
+
return confirm ?? false;
|
|
1434
130
|
};
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
);
|
|
1478
|
-
}
|
|
1479
|
-
return semVerFactory(codemodsTarget);
|
|
1480
|
-
})
|
|
1481
|
-
).action(async (target, options) => {
|
|
1482
|
-
if (!isValidSemVer(target)) {
|
|
1483
|
-
console.error(`Invalid target supplied, expected a valid semver but got "${target}"`);
|
|
1484
|
-
process.exit(1);
|
|
1485
|
-
}
|
|
1486
|
-
return upgrade({ ...options, target: semVerFactory(target) });
|
|
1487
|
-
});
|
|
1488
|
-
};
|
|
1489
|
-
const DEFAULT_TARGET = ReleaseType.Major;
|
|
1490
|
-
const runCodemods = async (options) => {
|
|
1491
|
-
const { silent, debug } = options;
|
|
1492
|
-
const logger = loggerFactory({ silent, debug });
|
|
1493
|
-
logger.warn(
|
|
1494
|
-
"Please make sure you've created a backup of your codebase and files before running the codemods"
|
|
1495
|
-
);
|
|
1496
|
-
const confirm = async (message) => {
|
|
1497
|
-
const { confirm: confirm2 } = await prompts__default.default({
|
|
1498
|
-
name: "confirm",
|
|
1499
|
-
type: "confirm",
|
|
1500
|
-
message
|
|
1501
|
-
});
|
|
1502
|
-
return confirm2 ?? false;
|
|
1503
|
-
};
|
|
1504
|
-
const selectCodemods = async (codemods) => {
|
|
1505
|
-
const selectableCodemods = codemods.map(
|
|
1506
|
-
({ version: version2, codemods: codemods2 }) => codemods2.map((codemod) => ({
|
|
1507
|
-
title: `(${version2}) ${codemod.format()}`,
|
|
1508
|
-
value: codemod,
|
|
1509
|
-
selected: true
|
|
1510
|
-
}))
|
|
1511
|
-
).flat();
|
|
1512
|
-
if (selectableCodemods.length === 0) {
|
|
1513
|
-
logger.info("No codemods to run");
|
|
1514
|
-
return [];
|
|
1515
|
-
}
|
|
1516
|
-
const { selectedCodemods } = await prompts__default.default({
|
|
1517
|
-
type: "autocompleteMultiselect",
|
|
1518
|
-
name: "selectedCodemods",
|
|
1519
|
-
message: "Choose the codemods you would like to run:",
|
|
1520
|
-
choices: selectableCodemods
|
|
131
|
+
const selectCodemods = async (codemods)=>{
|
|
132
|
+
const selectableCodemods = codemods.map(({ version, codemods })=>codemods.map((codemod)=>({
|
|
133
|
+
title: `(${version}) ${codemod.format()}`,
|
|
134
|
+
value: codemod,
|
|
135
|
+
selected: true
|
|
136
|
+
}))).flat();
|
|
137
|
+
if (selectableCodemods.length === 0) {
|
|
138
|
+
logger$1.info('No codemods to run');
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
const { selectedCodemods } = await prompts({
|
|
142
|
+
type: 'autocompleteMultiselect',
|
|
143
|
+
name: 'selectedCodemods',
|
|
144
|
+
message: 'Choose the codemods you would like to run:',
|
|
145
|
+
choices: selectableCodemods
|
|
146
|
+
});
|
|
147
|
+
if (!selectedCodemods || selectedCodemods.length === 0) {
|
|
148
|
+
logger$1.info('No codemods selected');
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
return selectedCodemods.map((codemod)=>({
|
|
152
|
+
version: codemod.version,
|
|
153
|
+
codemods: [
|
|
154
|
+
codemod
|
|
155
|
+
]
|
|
156
|
+
}));
|
|
157
|
+
};
|
|
158
|
+
return logger.runCodemods({
|
|
159
|
+
logger: logger$1,
|
|
160
|
+
confirm,
|
|
161
|
+
selectCodemods,
|
|
162
|
+
dry: options.dry,
|
|
163
|
+
cwd: options.projectPath,
|
|
164
|
+
target: options.range ?? DEFAULT_TARGET,
|
|
165
|
+
uid: options.uid
|
|
166
|
+
}).catch((err)=>handleError(err, options.silent));
|
|
167
|
+
};
|
|
168
|
+
const listCodemods = async (options)=>{
|
|
169
|
+
const { silent, debug } = options;
|
|
170
|
+
const logger$1 = logger.loggerFactory({
|
|
171
|
+
silent,
|
|
172
|
+
debug
|
|
1521
173
|
});
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
selectCodemods,
|
|
1535
|
-
dry: options.dry,
|
|
1536
|
-
cwd: options.projectPath,
|
|
1537
|
-
target: options.range ?? DEFAULT_TARGET,
|
|
1538
|
-
uid: options.uid
|
|
1539
|
-
}).catch((err) => handleError(err, options.silent));
|
|
1540
|
-
};
|
|
1541
|
-
const listCodemods = async (options) => {
|
|
1542
|
-
const { silent, debug } = options;
|
|
1543
|
-
const logger = loggerFactory({ silent, debug });
|
|
1544
|
-
return listCodemods$1({
|
|
1545
|
-
cwd: options.projectPath,
|
|
1546
|
-
target: options.range ?? DEFAULT_TARGET,
|
|
1547
|
-
logger
|
|
1548
|
-
}).catch((err) => handleError(err, options.silent));
|
|
1549
|
-
};
|
|
1550
|
-
const register = (program) => {
|
|
1551
|
-
const codemodsCommand = program.command("codemods");
|
|
1552
|
-
codemodsCommand.command("run [uid]").description(
|
|
1553
|
-
`
|
|
174
|
+
return logger.listCodemods({
|
|
175
|
+
cwd: options.projectPath,
|
|
176
|
+
target: options.range ?? DEFAULT_TARGET,
|
|
177
|
+
logger: logger$1
|
|
178
|
+
}).catch((err)=>handleError(err, options.silent));
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Registers codemods related commands.
|
|
182
|
+
*/ const register = (program)=>{
|
|
183
|
+
const codemodsCommand = program.command('codemods');
|
|
184
|
+
// upgrade codemods run [options] [uid]
|
|
185
|
+
codemodsCommand.command('run [uid]').description(`
|
|
1554
186
|
Executes a set of codemods on the current project.
|
|
1555
187
|
|
|
1556
188
|
If the optional UID argument is provided, the command specifically runs the codemod associated with that UID.
|
|
@@ -1558,23 +190,24 @@ Without the UID, the command produces a list of all available codemods for your
|
|
|
1558
190
|
|
|
1559
191
|
By default, when executed on a Strapi application project, it offers codemods matching the current major version of the app.
|
|
1560
192
|
When executed on a Strapi plugin project, it shows every codemods.
|
|
1561
|
-
`
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
193
|
+
`).addOption(projectPathOption).addOption(dryOption).addOption(debugOption).addOption(silentOption).addOption(rangeOption).action(async (uid, options)=>{
|
|
194
|
+
return runCodemods({
|
|
195
|
+
...options,
|
|
196
|
+
uid
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
// upgrade codemods ls [options]
|
|
200
|
+
codemodsCommand.command('ls').description(`List available codemods`).addOption(projectPathOption).addOption(debugOption).addOption(silentOption).addOption(rangeOption).action(async (options)=>{
|
|
201
|
+
return listCodemods(options);
|
|
202
|
+
});
|
|
1568
203
|
};
|
|
1569
|
-
|
|
204
|
+
|
|
205
|
+
var version = "5.10.0";
|
|
206
|
+
|
|
1570
207
|
register$1(commander.program);
|
|
1571
208
|
register(commander.program);
|
|
1572
|
-
commander.program.usage(
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
)
|
|
1577
|
-
);
|
|
1578
|
-
process.exit(1);
|
|
1579
|
-
}).helpOption("-h, --help", "Print command line options").addHelpCommand("help [command]", "Print options for a specific command").version(version).parse(process.argv);
|
|
209
|
+
commander.program.usage('<command> [options]').on('command:*', ([invalidCmd])=>{
|
|
210
|
+
console.error(chalk.red(`[ERROR] Invalid command: ${invalidCmd}.${os.EOL} See --help for a list of available commands.`));
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}).helpOption('-h, --help', 'Print command line options').addHelpCommand('help [command]', 'Print options for a specific command').version(version).parse(process.argv);
|
|
1580
213
|
//# sourceMappingURL=cli.js.map
|