@next/codemod 16.4.0-canary.55 → 16.4.0-canary.57
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 +2 -0
- package/bin/transform.js +4 -1
- package/bin/upgrade.js +39 -16
- package/lib/handle-package.js +12 -0
- package/package.json +1 -1
package/bin/next-codemod.js
CHANGED
|
@@ -45,6 +45,8 @@ program
|
|
|
45
45
|
.option('--verbose', 'Verbose output', false)
|
|
46
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)
|
|
47
47
|
.option('--skip-adoption', 'Skip optional feature-adoption codemods while applying version migrations.', false)
|
|
48
|
+
.option('--skip-react-upgrade', 'Keep React dependencies, types, and overrides unchanged and skip React codemods.', false)
|
|
49
|
+
.option('--skip-eslint-upgrade', 'Keep ESLint dependencies unchanged and skip the next-lint-to-eslint-cli codemod.', false)
|
|
48
50
|
.action(async (revision, options) => {
|
|
49
51
|
try {
|
|
50
52
|
await (0, upgrade_1.runUpgrade)(revision, options);
|
package/bin/transform.js
CHANGED
|
@@ -109,7 +109,10 @@ async function runTransform(transform, path, options) {
|
|
|
109
109
|
const execaChildProcess = (0, execa_1.default)(exports.jscodeshiftExecutable, args, {
|
|
110
110
|
// include ANSI color codes
|
|
111
111
|
// Note: execa merges env with existing env by default.
|
|
112
|
-
env:
|
|
112
|
+
env: {
|
|
113
|
+
...process.env,
|
|
114
|
+
...(process.stdout.isTTY ? { FORCE_COLOR: 'true' } : {}),
|
|
115
|
+
},
|
|
113
116
|
});
|
|
114
117
|
// "\n" + "a\n" + "b\n"
|
|
115
118
|
let lastThreeLineBreaks = '';
|
package/bin/upgrade.js
CHANGED
|
@@ -190,6 +190,7 @@ async function runUpgrade(revision, options) {
|
|
|
190
190
|
// x-ref(release): https://github.com/vercel/next.js/releases/tag/v14.3.0-canary.45
|
|
191
191
|
(0, semver_1.compare)(targetNextVersion, '14.3.0-canary.45') >= 0 &&
|
|
192
192
|
installedReactVersion.startsWith('18') &&
|
|
193
|
+
!options.skipReactUpgrade &&
|
|
193
194
|
// Pure App Router always uses React 19
|
|
194
195
|
// The mixed case is tricky to handle from a types perspective.
|
|
195
196
|
// We'll recommend to upgrade in the prompt but users can decide to try 18.
|
|
@@ -218,9 +219,11 @@ async function runUpgrade(revision, options) {
|
|
|
218
219
|
// E.g. in peerDependencies we could have `^18.2.0 || ^19.0.0 || 20.0.0-canary`
|
|
219
220
|
// If we'd just `npm add` that, the manifest would read the same version query.
|
|
220
221
|
// This is basically a `npm --save-exact react@$versionQuery` that works for every package manager.
|
|
221
|
-
const targetReactVersion =
|
|
222
|
-
?
|
|
223
|
-
:
|
|
222
|
+
const targetReactVersion = options.skipReactUpgrade
|
|
223
|
+
? installedReactVersion
|
|
224
|
+
: shouldStayOnReact18
|
|
225
|
+
? '18.3.1'
|
|
226
|
+
: await loadHighestNPMVersionMatching(`react@${targetNextPackageJson.peerDependencies['react']}`);
|
|
224
227
|
if ((0, semver_1.compare)(targetNextVersion, '15.0.0-canary') >= 0 &&
|
|
225
228
|
(0, semver_1.compare)(targetNextVersion, '16.0.0-canary') < 0) {
|
|
226
229
|
await suggestTurbopack(appPackageJson, targetNextVersion, nonInteractive);
|
|
@@ -231,7 +234,8 @@ async function runUpgrade(revision, options) {
|
|
|
231
234
|
let shouldRunReactTypesCodemods = false;
|
|
232
235
|
let execCommand = 'npx --yes';
|
|
233
236
|
// The following React codemods are for React 19
|
|
234
|
-
if (!
|
|
237
|
+
if (!options.skipReactUpgrade &&
|
|
238
|
+
!shouldStayOnReact18 &&
|
|
235
239
|
(0, semver_1.compare)(targetReactVersion, '19.0.0-0') >= 0 &&
|
|
236
240
|
(0, semver_1.compare)(installedReactVersion, '19.0.0-0') < 0) {
|
|
237
241
|
shouldRunReactCodemods = await suggestReactCodemods(nonInteractive);
|
|
@@ -248,19 +252,31 @@ async function runUpgrade(revision, options) {
|
|
|
248
252
|
};
|
|
249
253
|
const versionMapping = {
|
|
250
254
|
next: { version: targetNextVersion, required: true },
|
|
251
|
-
react: { version: targetReactVersion, required: true },
|
|
252
|
-
'react-dom': { version: targetReactVersion, required: true },
|
|
253
|
-
'react-is': { version: targetReactVersion, required: false },
|
|
254
255
|
};
|
|
256
|
+
// Recovery retries must preserve the repaired React declarations and pins.
|
|
257
|
+
if (!options.skipReactUpgrade) {
|
|
258
|
+
Object.assign(versionMapping, {
|
|
259
|
+
react: { version: targetReactVersion, required: true },
|
|
260
|
+
'react-dom': { version: targetReactVersion, required: true },
|
|
261
|
+
'react-is': { version: targetReactVersion, required: false },
|
|
262
|
+
});
|
|
263
|
+
}
|
|
255
264
|
for (const optionalNextjsPackage of optionalNextjsPackages) {
|
|
265
|
+
// Preserve the lint stack together; its newer config can require newer ESLint.
|
|
266
|
+
if (options.skipEslintUpgrade &&
|
|
267
|
+
(optionalNextjsPackage === 'eslint-config-next' ||
|
|
268
|
+
optionalNextjsPackage === '@next/eslint-plugin-next')) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
256
271
|
versionMapping[optionalNextjsPackage] = {
|
|
257
272
|
version: targetNextVersion,
|
|
258
273
|
required: false,
|
|
259
274
|
};
|
|
260
275
|
}
|
|
261
|
-
if (
|
|
262
|
-
targetReactVersion.startsWith('19.0.0-
|
|
263
|
-
|
|
276
|
+
if (!options.skipReactUpgrade &&
|
|
277
|
+
(targetReactVersion.startsWith('19.0.0-canary') ||
|
|
278
|
+
targetReactVersion.startsWith('19.0.0-beta') ||
|
|
279
|
+
targetReactVersion.startsWith('19.0.0-rc'))) {
|
|
264
280
|
const [targetReactTypesVersion, targetReactDOMTypesVersion] = await Promise.all([
|
|
265
281
|
loadHighestNPMVersionMatching(`types-react@rc`),
|
|
266
282
|
loadHighestNPMVersionMatching(`types-react-dom@rc`),
|
|
@@ -278,7 +294,7 @@ async function runUpgrade(revision, options) {
|
|
|
278
294
|
};
|
|
279
295
|
}
|
|
280
296
|
}
|
|
281
|
-
else {
|
|
297
|
+
else if (!options.skipReactUpgrade) {
|
|
282
298
|
const [targetReactTypesVersion, targetReactDOMTypesVersion] = await Promise.all([
|
|
283
299
|
loadHighestNPMVersionMatching(`@types/react@${targetNextPackageJson.peerDependencies['react']}`),
|
|
284
300
|
loadHighestNPMVersionMatching(`@types/react-dom@${targetNextPackageJson.peerDependencies['react']}`),
|
|
@@ -306,7 +322,9 @@ async function runUpgrade(revision, options) {
|
|
|
306
322
|
// Only act when the project is actually using `eslint-config-next` — we
|
|
307
323
|
// don't want to silently upgrade eslint majors for projects that use
|
|
308
324
|
// eslint for unrelated reasons.
|
|
309
|
-
if (
|
|
325
|
+
if (!options.skipEslintUpgrade &&
|
|
326
|
+
allDependencies['eslint'] &&
|
|
327
|
+
allDependencies['eslint-config-next']) {
|
|
310
328
|
try {
|
|
311
329
|
const eslintConfigNextPeerDepsJSON = (0, child_process_1.execSync)(`npm --silent view "eslint-config-next@${targetNextVersion}" peerDependencies --json`, { encoding: 'utf-8' });
|
|
312
330
|
const eslintConfigNextPeerDeps = eslintConfigNextPeerDepsJSON.trim() === ''
|
|
@@ -336,10 +354,10 @@ async function runUpgrade(revision, options) {
|
|
|
336
354
|
// Even though we only need those if we alias `@types/react` to types-react,
|
|
337
355
|
// we still do it out of safety due to https://github.com/microsoft/DefinitelyTyped-tools/issues/433.
|
|
338
356
|
const overrides = {};
|
|
339
|
-
if (allDependencies['@types/react']) {
|
|
357
|
+
if (!options.skipReactUpgrade && allDependencies['@types/react']) {
|
|
340
358
|
overrides['@types/react'] = versionMapping['@types/react'].version;
|
|
341
359
|
}
|
|
342
|
-
if (allDependencies['@types/react-dom']) {
|
|
360
|
+
if (!options.skipReactUpgrade && allDependencies['@types/react-dom']) {
|
|
343
361
|
overrides['@types/react-dom'] = versionMapping['@types/react-dom'].version;
|
|
344
362
|
}
|
|
345
363
|
writeOverridesField(appPackageJson, packageManager, overrides);
|
|
@@ -362,7 +380,12 @@ async function runUpgrade(revision, options) {
|
|
|
362
380
|
// Common IDE formatters would add a newline as well.
|
|
363
381
|
os.EOL);
|
|
364
382
|
(0, handle_package_1.runInstallation)(packageManager, { cwd });
|
|
365
|
-
|
|
383
|
+
// Opting out skips the lint migration as well as its dependency upgrades.
|
|
384
|
+
const selectedCodemods = codemods.filter((codemod) => !options.skipEslintUpgrade || codemod !== 'next-lint-to-eslint-cli');
|
|
385
|
+
if (selectedCodemods.length !== codemods.length) {
|
|
386
|
+
console.log('Skipping next-lint-to-eslint-cli. Complete any required lint migration manually before verification.');
|
|
387
|
+
}
|
|
388
|
+
for (const codemod of selectedCodemods) {
|
|
366
389
|
await (0, transform_1.runTransform)(codemod, cwd, {
|
|
367
390
|
force: true,
|
|
368
391
|
verbose,
|
|
@@ -395,7 +418,7 @@ async function runUpgrade(revision, options) {
|
|
|
395
418
|
});
|
|
396
419
|
}
|
|
397
420
|
console.log(); // new line
|
|
398
|
-
if (
|
|
421
|
+
if (selectedCodemods.length > 0) {
|
|
399
422
|
console.log(`${picocolors_1.default.green('✔')} Codemods have been applied successfully.`);
|
|
400
423
|
}
|
|
401
424
|
try {
|
package/lib/handle-package.js
CHANGED
|
@@ -13,6 +13,7 @@ exports.addPackageDependency = addPackageDependency;
|
|
|
13
13
|
const find_up_1 = __importDefault(require("find-up"));
|
|
14
14
|
const execa_1 = __importDefault(require("execa"));
|
|
15
15
|
const node_child_process_1 = require("node:child_process");
|
|
16
|
+
const node_fs_1 = require("node:fs");
|
|
16
17
|
const node_path_1 = require("node:path");
|
|
17
18
|
/**
|
|
18
19
|
* Get the full version string for the given package manager.
|
|
@@ -57,6 +58,17 @@ function getPnpmMajorVersion() {
|
|
|
57
58
|
}
|
|
58
59
|
function getPkgManager(baseDir) {
|
|
59
60
|
try {
|
|
61
|
+
// The app's declared manager takes precedence over the command launcher.
|
|
62
|
+
const manifestPath = (0, node_path_1.join)(baseDir, 'package.json');
|
|
63
|
+
if ((0, node_fs_1.existsSync)(manifestPath)) {
|
|
64
|
+
const { packageManager } = JSON.parse((0, node_fs_1.readFileSync)(manifestPath, 'utf8'));
|
|
65
|
+
const match = typeof packageManager === 'string'
|
|
66
|
+
? packageManager.match(/^(npm|pnpm|yarn|bun)@/)
|
|
67
|
+
: null;
|
|
68
|
+
if (match) {
|
|
69
|
+
return match[1];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
60
72
|
const userAgent = process.env.npm_config_user_agent;
|
|
61
73
|
if (userAgent) {
|
|
62
74
|
if (userAgent.startsWith('yarn')) {
|