@next/codemod 16.3.0-canary.71 → 16.3.0-canary.72
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/bin/next-codemod.js +1 -0
- package/bin/upgrade.js +63 -29
- package/package.json +1 -1
package/bin/next-codemod.js
CHANGED
|
@@ -43,6 +43,7 @@ program
|
|
|
43
43
|
.argument('[revision]', 'Specify the upgrade type ("patch", "minor", "major"), an NPM dist tag (e.g. "latest", "canary", "rc"), or an exact version (e.g. "15.0.0"). Defaults to "minor".')
|
|
44
44
|
.usage('[revision] [options]')
|
|
45
45
|
.option('--verbose', 'Verbose output', false)
|
|
46
|
+
.option('-y, --yes', 'Skip every interactive prompt and accept its default. Also auto-enabled when stdin is not a TTY (e.g. running under an agent or in CI).', false)
|
|
46
47
|
.action(async (revision, options) => {
|
|
47
48
|
try {
|
|
48
49
|
await (0, upgrade_1.runUpgrade)(revision, options);
|
package/bin/upgrade.js
CHANGED
|
@@ -114,6 +114,10 @@ function resolveSemanticRevision(revision, installedVersion) {
|
|
|
114
114
|
}
|
|
115
115
|
async function runUpgrade(revision, options) {
|
|
116
116
|
const { verbose } = options;
|
|
117
|
+
const nonInteractive = options.yes === true || !process.stdin.isTTY;
|
|
118
|
+
if (nonInteractive) {
|
|
119
|
+
console.log(` Running in non-interactive mode. Every prompt will accept its default.`);
|
|
120
|
+
}
|
|
117
121
|
const appPackageJsonPath = path_1.default.resolve(cwd, 'package.json');
|
|
118
122
|
let appPackageJson = JSON.parse(fs_1.default.readFileSync(appPackageJsonPath, 'utf8'));
|
|
119
123
|
const installedNextVersion = getInstalledNextVersion();
|
|
@@ -189,18 +193,24 @@ async function runUpgrade(revision, options) {
|
|
|
189
193
|
// The mixed case is tricky to handle from a types perspective.
|
|
190
194
|
// We'll recommend to upgrade in the prompt but users can decide to try 18.
|
|
191
195
|
!isPureAppRouter) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
196
|
+
if (nonInteractive) {
|
|
197
|
+
// Default: upgrade React past 18.
|
|
198
|
+
shouldStayOnReact18 = false;
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
const shouldStayOnReact18Res = await (0, prompts_1.default)({
|
|
202
|
+
type: 'confirm',
|
|
203
|
+
name: 'shouldStayOnReact18',
|
|
204
|
+
message: `Do you prefer to stay on React 18?` +
|
|
205
|
+
(isMixedApp
|
|
206
|
+
? " Since you're using both pages/ and app/, we recommend upgrading React to use a consistent version throughout your app."
|
|
207
|
+
: ''),
|
|
208
|
+
initial: false,
|
|
209
|
+
active: 'Yes',
|
|
210
|
+
inactive: 'No',
|
|
211
|
+
}, { onCancel: utils_1.onCancel });
|
|
212
|
+
shouldStayOnReact18 = shouldStayOnReact18Res.shouldStayOnReact18;
|
|
213
|
+
}
|
|
204
214
|
}
|
|
205
215
|
// We're resolving a specific version here to avoid including "ugly" version queries
|
|
206
216
|
// in the manifest.
|
|
@@ -212,9 +222,9 @@ async function runUpgrade(revision, options) {
|
|
|
212
222
|
: await loadHighestNPMVersionMatching(`react@${targetNextPackageJson.peerDependencies['react']}`);
|
|
213
223
|
if ((0, semver_1.compare)(targetNextVersion, '15.0.0-canary') >= 0 &&
|
|
214
224
|
(0, semver_1.compare)(targetNextVersion, '16.0.0-canary') < 0) {
|
|
215
|
-
await suggestTurbopack(appPackageJson, targetNextVersion);
|
|
225
|
+
await suggestTurbopack(appPackageJson, targetNextVersion, nonInteractive);
|
|
216
226
|
}
|
|
217
|
-
const codemods = await suggestCodemods(installedNextVersion, targetNextVersion);
|
|
227
|
+
const codemods = await suggestCodemods(installedNextVersion, targetNextVersion, nonInteractive);
|
|
218
228
|
const packageManager = (0, handle_package_1.getPkgManager)(cwd);
|
|
219
229
|
let shouldRunReactCodemods = false;
|
|
220
230
|
let shouldRunReactTypesCodemods = false;
|
|
@@ -223,8 +233,9 @@ async function runUpgrade(revision, options) {
|
|
|
223
233
|
if (!shouldStayOnReact18 &&
|
|
224
234
|
(0, semver_1.compare)(targetReactVersion, '19.0.0-0') >= 0 &&
|
|
225
235
|
(0, semver_1.compare)(installedReactVersion, '19.0.0-0') < 0) {
|
|
226
|
-
shouldRunReactCodemods = await suggestReactCodemods();
|
|
227
|
-
shouldRunReactTypesCodemods =
|
|
236
|
+
shouldRunReactCodemods = await suggestReactCodemods(nonInteractive);
|
|
237
|
+
shouldRunReactTypesCodemods =
|
|
238
|
+
await suggestReactTypesCodemods(nonInteractive);
|
|
228
239
|
execCommand = getNpxCommand(packageManager);
|
|
229
240
|
}
|
|
230
241
|
fs_1.default.writeFileSync(appPackageJsonPath, JSON.stringify(appPackageJson, null, 2));
|
|
@@ -320,10 +331,11 @@ async function runUpgrade(revision, options) {
|
|
|
320
331
|
// understanding of the codemods, we run all of the applicable codemods.
|
|
321
332
|
if (shouldRunReactCodemods) {
|
|
322
333
|
// https://react.dev/blog/2024/04/25/react-19-upgrade-guide#run-all-react-19-codemods
|
|
323
|
-
(0, child_process_1.execSync)(
|
|
324
334
|
// `--no-interactive` skips the interactive prompt that asks for confirmation
|
|
325
335
|
// https://github.com/codemod-com/codemod/blob/c0cf00d13161a0ec0965b6cc6bc5d54076839cc8/apps/cli/src/flags.ts#L160
|
|
326
|
-
|
|
336
|
+
// `--allow-dirty` is required because the upgrade above modified package.json
|
|
337
|
+
// and the lockfile; the recipe refuses to run on a dirty tree otherwise.
|
|
338
|
+
(0, child_process_1.execSync)(`${execCommand} codemod@latest react/19/migration-recipe --no-interactive --allow-dirty`, { stdio: 'inherit' });
|
|
327
339
|
}
|
|
328
340
|
if (shouldRunReactTypesCodemods) {
|
|
329
341
|
// https://react.dev/blog/2024/04/25/react-19-upgrade-guide#typescript-changes
|
|
@@ -383,7 +395,7 @@ function isUsingAppDir(projectPath) {
|
|
|
383
395
|
* 3. Otherwise, we ask the user to manually add `--turbopack` to their dev command,
|
|
384
396
|
* showing the current dev command as the initial value.
|
|
385
397
|
*/
|
|
386
|
-
async function suggestTurbopack(packageJson, targetNextVersion) {
|
|
398
|
+
async function suggestTurbopack(packageJson, targetNextVersion, nonInteractive) {
|
|
387
399
|
const devScript = packageJson.scripts?.['dev'];
|
|
388
400
|
// Turbopack flag was changed from `--turbo` to `--turbopack` in v15.0.1-canary.3
|
|
389
401
|
// PR: https://github.com/vercel/next.js/pull/71657
|
|
@@ -406,19 +418,28 @@ async function suggestTurbopack(packageJson, targetNextVersion) {
|
|
|
406
418
|
}
|
|
407
419
|
return;
|
|
408
420
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
421
|
+
let enable = true;
|
|
422
|
+
if (!nonInteractive) {
|
|
423
|
+
const responseTurbopack = await (0, prompts_1.default)({
|
|
424
|
+
type: 'confirm',
|
|
425
|
+
name: 'enable',
|
|
426
|
+
message: `Enable Turbopack for ${picocolors_1.default.bold('next dev')}?`,
|
|
427
|
+
initial: true,
|
|
428
|
+
}, { onCancel: utils_1.onCancel });
|
|
429
|
+
enable = responseTurbopack.enable;
|
|
430
|
+
}
|
|
431
|
+
if (!enable) {
|
|
416
432
|
return;
|
|
417
433
|
}
|
|
418
434
|
packageJson.scripts['dev'] = devScript.replace('next dev', `next dev ${turboPackFlag}`);
|
|
419
435
|
return;
|
|
420
436
|
}
|
|
421
437
|
console.log(`${picocolors_1.default.yellow('⚠')} Could not find "${picocolors_1.default.bold('next dev')}" in your dev script.`);
|
|
438
|
+
if (nonInteractive) {
|
|
439
|
+
// Without a TTY we can't ask the user for a replacement script.
|
|
440
|
+
// Keep the existing dev script untouched.
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
422
443
|
const responseCustomDevScript = await (0, prompts_1.default)({
|
|
423
444
|
type: 'text',
|
|
424
445
|
name: 'customDevScript',
|
|
@@ -428,7 +449,7 @@ async function suggestTurbopack(packageJson, targetNextVersion) {
|
|
|
428
449
|
packageJson.scripts['dev'] =
|
|
429
450
|
responseCustomDevScript.customDevScript || devScript;
|
|
430
451
|
}
|
|
431
|
-
async function suggestCodemods(initialNextVersion, targetNextVersion) {
|
|
452
|
+
async function suggestCodemods(initialNextVersion, targetNextVersion, nonInteractive) {
|
|
432
453
|
// example:
|
|
433
454
|
// codemod version: 15.0.0-canary.45
|
|
434
455
|
// 14.3 -> 15.0.0-canary.45: apply
|
|
@@ -451,6 +472,13 @@ async function suggestCodemods(initialNextVersion, targetNextVersion) {
|
|
|
451
472
|
if (relevantCodemods.length === 0) {
|
|
452
473
|
return [];
|
|
453
474
|
}
|
|
475
|
+
if (nonInteractive) {
|
|
476
|
+
// Default: apply every recommended codemod, matching `selected: true` below.
|
|
477
|
+
const all = relevantCodemods.map(({ value }) => value);
|
|
478
|
+
console.log(` Applying all ${picocolors_1.default.blue('codemods')} recommended for your upgrade:\n` +
|
|
479
|
+
all.map((value) => ` - ${value}`).join('\n'));
|
|
480
|
+
return all;
|
|
481
|
+
}
|
|
454
482
|
const { codemods } = await (0, prompts_1.default)({
|
|
455
483
|
type: 'multiselect',
|
|
456
484
|
name: 'codemods',
|
|
@@ -466,7 +494,10 @@ async function suggestCodemods(initialNextVersion, targetNextVersion) {
|
|
|
466
494
|
}, { onCancel: utils_1.onCancel });
|
|
467
495
|
return codemods;
|
|
468
496
|
}
|
|
469
|
-
async function suggestReactCodemods() {
|
|
497
|
+
async function suggestReactCodemods(nonInteractive) {
|
|
498
|
+
if (nonInteractive) {
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
470
501
|
const { runReactCodemod } = await (0, prompts_1.default)({
|
|
471
502
|
type: 'confirm',
|
|
472
503
|
name: 'runReactCodemod',
|
|
@@ -475,7 +506,10 @@ async function suggestReactCodemods() {
|
|
|
475
506
|
}, { onCancel: utils_1.onCancel });
|
|
476
507
|
return runReactCodemod;
|
|
477
508
|
}
|
|
478
|
-
async function suggestReactTypesCodemods() {
|
|
509
|
+
async function suggestReactTypesCodemods(nonInteractive) {
|
|
510
|
+
if (nonInteractive) {
|
|
511
|
+
return true;
|
|
512
|
+
}
|
|
479
513
|
const { runReactTypesCodemod } = await (0, prompts_1.default)({
|
|
480
514
|
type: 'confirm',
|
|
481
515
|
name: 'runReactTypesCodemod',
|