@next/codemod 16.0.2 → 16.1.0-canary.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.
@@ -39,6 +39,8 @@ const program = new commander_1.Command(packageJson.name)
39
39
  program
40
40
  .command('upgrade')
41
41
  .description('Upgrade Next.js apps to desired versions with a single command.')
42
+ // The CLI interface must be backwards compatible at all times so that older
43
+ // versions of Next.js can still run the codemod successfully.
42
44
  .argument('[revision]', 'Specify the target Next.js version using an NPM dist tag (e.g. "latest", "canary", "rc", "beta") or an exact version number (e.g. "15.0.0").', packageJson.version.includes('-canary.')
43
45
  ? 'canary'
44
46
  : packageJson.version.includes('-rc.')
@@ -99,6 +99,13 @@ function runInstallation(packageManager, options) {
99
99
  try {
100
100
  execa_1.default.sync(packageManager, ['install'], {
101
101
  cwd: options.cwd,
102
+ env: {
103
+ ...process.env,
104
+ // In case NODE_ENV=production is set, we still want dev dependencies to
105
+ // be installed. Otherwise we won't be able to check for peer dependencies.
106
+ // --production=false is not implemented by every package manager.
107
+ NODE_ENV: 'development',
108
+ },
102
109
  stdio: 'inherit',
103
110
  shell: true,
104
111
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@next/codemod",
3
- "version": "16.0.2",
3
+ "version": "16.1.0-canary.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -113,6 +113,7 @@ function replaceFlatCompatInConfig(configPath) {
113
113
  const j = (0, parser_1.createParserFromPath)(configPath);
114
114
  const root = j(configContent);
115
115
  // Track if we need to add imports and preserve other configs
116
+ let needsNext = false;
116
117
  let needsNextVitals = false;
117
118
  let needsNextTs = false;
118
119
  let otherConfigs = [];
@@ -128,7 +129,10 @@ function replaceFlatCompatInConfig(configPath) {
128
129
  // Check arguments for all configs
129
130
  node.arguments.forEach((arg) => {
130
131
  if (arg.type === 'Literal' || arg.type === 'StringLiteral') {
131
- if (arg.value === 'next/core-web-vitals') {
132
+ if (arg.value === 'next') {
133
+ needsNext = true;
134
+ }
135
+ else if (arg.value === 'next/core-web-vitals') {
132
136
  needsNextVitals = true;
133
137
  }
134
138
  else if (arg.value === 'next/typescript') {
@@ -159,7 +163,10 @@ function replaceFlatCompatInConfig(configPath) {
159
163
  prop.value.elements?.forEach((element) => {
160
164
  if (element.type === 'Literal' ||
161
165
  element.type === 'StringLiteral') {
162
- if (element.value === 'next/core-web-vitals') {
166
+ if (element.value === 'next') {
167
+ needsNext = true;
168
+ }
169
+ else if (element.value === 'next/core-web-vitals') {
163
170
  needsNextVitals = true;
164
171
  }
165
172
  else if (element.value === 'next/typescript') {
@@ -177,11 +184,14 @@ function replaceFlatCompatInConfig(configPath) {
177
184
  });
178
185
  }
179
186
  });
180
- if (!needsNextVitals && !needsNextTs && otherConfigs.length === 0) {
187
+ if (!needsNext &&
188
+ !needsNextVitals &&
189
+ !needsNextTs &&
190
+ otherConfigs.length === 0) {
181
191
  console.warn(exports.prefixes.warn, ' No ESLint configs found in FlatCompat usage');
182
192
  return false;
183
193
  }
184
- if (!needsNextVitals && !needsNextTs) {
194
+ if (!needsNext && !needsNextVitals && !needsNextTs) {
185
195
  console.log(' No Next.js configs found, but preserving other configs');
186
196
  }
187
197
  // Only remove FlatCompat setup if no other configs need it
@@ -223,7 +233,10 @@ function replaceFlatCompatInConfig(configPath) {
223
233
  }
224
234
  // Add new imports after the eslint/config import
225
235
  const imports = [];
226
- // Add imports in correct order: core-web-vitals first, then typescript
236
+ // Add imports in correct order: next first, then core-web-vitals, then typescript
237
+ if (needsNext) {
238
+ imports.push(j.importDeclaration([j.importDefaultSpecifier(j.identifier('next'))], j.literal('eslint-config-next')));
239
+ }
227
240
  if (needsNextVitals) {
228
241
  imports.push(j.importDeclaration([j.importDefaultSpecifier(j.identifier('nextCoreWebVitals'))], j.literal('eslint-config-next/core-web-vitals')));
229
242
  }
@@ -265,7 +278,10 @@ function replaceFlatCompatInConfig(configPath) {
265
278
  const replacements = [];
266
279
  node.argument.arguments.forEach((arg) => {
267
280
  if (arg.type === 'Literal' || arg.type === 'StringLiteral') {
268
- if (arg.value === 'next/core-web-vitals') {
281
+ if (arg.value === 'next') {
282
+ replacements.push(j.spreadElement(j.identifier('next')));
283
+ }
284
+ else if (arg.value === 'next/core-web-vitals') {
269
285
  replacements.push(j.spreadElement(j.identifier('nextCoreWebVitals')));
270
286
  }
271
287
  else if (arg.value === 'next/typescript') {
@@ -311,7 +327,10 @@ function replaceFlatCompatInConfig(configPath) {
311
327
  prop.value.elements?.forEach((element) => {
312
328
  if (element.type === 'Literal' ||
313
329
  element.type === 'StringLiteral') {
314
- if (element.value === 'next/core-web-vitals') {
330
+ if (element.value === 'next') {
331
+ replacements.push(j.spreadElement(j.identifier('next')));
332
+ }
333
+ else if (element.value === 'next/core-web-vitals') {
315
334
  replacements.push(j.spreadElement(j.identifier('nextCoreWebVitals')));
316
335
  }
317
336
  else if (element.value === 'next/typescript') {
@@ -370,7 +389,10 @@ function replaceFlatCompatInConfig(configPath) {
370
389
  const replacements = [];
371
390
  prop.value.arguments.forEach((arg) => {
372
391
  if (arg.type === 'Literal' || arg.type === 'StringLiteral') {
373
- if (arg.value === 'next/core-web-vitals') {
392
+ if (arg.value === 'next') {
393
+ replacements.push(j.spreadElement(j.identifier('next')));
394
+ }
395
+ else if (arg.value === 'next/core-web-vitals') {
374
396
  replacements.push(j.spreadElement(j.identifier('nextCoreWebVitals')));
375
397
  }
376
398
  else if (arg.value === 'next/typescript') {
@@ -451,6 +473,7 @@ function updateExistingFlatConfig(configPath, isTypeScript = false) {
451
473
  return false;
452
474
  }
453
475
  // Check if Next.js configs are already imported directly
476
+ const hasNext = configContent.includes('eslint-config-next');
454
477
  const hasNextVitals = configContent.includes('eslint-config-next/core-web-vitals');
455
478
  const hasNextTs = configContent.includes('eslint-config-next/typescript');
456
479
  const hasNextConfigs = hasNextVitals || hasNextTs;
@@ -510,6 +533,9 @@ function updateExistingFlatConfig(configPath, isTypeScript = false) {
510
533
  // Add Next.js imports if not present
511
534
  const program = root.find(j.Program);
512
535
  const imports = [];
536
+ if (!hasNext) {
537
+ imports.push(j.importDeclaration([j.importDefaultSpecifier(j.identifier('next'))], j.literal('eslint-config-next')));
538
+ }
513
539
  if (!hasNextVitals) {
514
540
  imports.push(j.importDeclaration([j.importDefaultSpecifier(j.identifier('nextCoreWebVitals'))], j.literal('eslint-config-next/core-web-vitals')));
515
541
  }
@@ -525,6 +551,9 @@ function updateExistingFlatConfig(configPath, isTypeScript = false) {
525
551
  exportedArray.value.elements = [];
526
552
  }
527
553
  const spreadsToAdd = [];
554
+ if (!hasNext) {
555
+ spreadsToAdd.push(j.spreadElement(j.identifier('next')));
556
+ }
528
557
  if (!hasNextVitals) {
529
558
  spreadsToAdd.push(j.spreadElement(j.identifier('nextCoreWebVitals')));
530
559
  }