Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"@mdx-js/loader": "2.2.1",
"@mdx-js/react": "2.2.1",
"@next/bundle-analyzer": "workspace:*",
"@next/codemod": "workspace:*",
"@next/env": "workspace:*",
"@next/eslint-plugin-next": "workspace:*",
"@next/font": "workspace:*",
Expand Down
31 changes: 27 additions & 4 deletions packages/next-codemod/bin/next-codemod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,30 @@
// Based on https://github.com/reactjs/react-codemod/blob/dd8671c9a470a2c342b221ec903c574cf31e9f57/bin/cli.js
// @next/codemod optional-name-of-transform optional/path/to/src [...options]

import type { InitialReturnValue } from 'prompts'
import { Command } from 'commander'
import { runUpgrade } from './upgrade'
import { runTransform } from './transform'

const handleSigTerm = () => process.exit(0)

process.on('SIGINT', handleSigTerm)
process.on('SIGTERM', handleSigTerm)

export const onPromptState = (state: {
value: InitialReturnValue
aborted: boolean
exited: boolean
}) => {
if (state.aborted) {
// If we don't re-enable the terminal cursor before exiting
// the program, the cursor will remain hidden
process.stdout.write('\x1B[?25h')
process.stdout.write('\n')
process.exit(1)
}
}

const packageJson = require('../package.json')
const program = new Command(packageJson.name)
.description('Codemods for updating Next.js apps.')
Expand All @@ -21,6 +41,8 @@ const program = new Command(packageJson.name)
'-v, --version',
'Output the current version of @next/codemod.'
)
.helpOption('-h, --help', 'Display this help message.')
.usage('[codemod] [source] [options]')
.argument(
'[codemod]',
'Codemod slug to run. See "https://github.com/vercel/next.js/tree/canary/packages/next-codemod".'
Expand All @@ -29,8 +51,6 @@ const program = new Command(packageJson.name)
'[source]',
'Path to source files or directory to transform including glob patterns.'
)
.usage('[codemod] [source] [options]')
.helpOption('-h, --help', 'Display this help message.')
.option('-f, --force', 'Bypass Git safety checks and forcibly run codemods')
.option('-d, --dry', 'Dry run (no changes are made to files)')
.option('-p, --print', 'Print transformed files to your terminal')
Expand All @@ -39,14 +59,17 @@ const program = new Command(packageJson.name)
'(Advanced) Pass options directly to jscodeshift'
)
.action(runTransform)
.allowUnknownOption()

program
.command('upgrade')
.description(
'Upgrade Next.js apps to desired versions with a single command.'
)
.usage('[options]')
.usage('[version]')
.argument(
'[version]',
'Version to upgrade to. Includes release tags like "canary", "rc", or "latest".'
)
.action(runUpgrade)

program.parse(process.argv)
59 changes: 27 additions & 32 deletions packages/next-codemod/bin/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import globby from 'globby'
import prompts from 'prompts'
import { join } from 'node:path'
import { installPackage, uninstallPackage } from '../lib/handle-package'
import { checkGitStatus, TRANSFORMER_INQUIRER_CHOICES } from '../lib/utils'
import { checkGitStatus, CODEMOD_CHOICES } from '../lib/utils'
import { onPromptState } from './next-codemod'

function expandFilePathsIfNeeded(filesBeforeExpansion) {
const shouldExpandFiles = filesBeforeExpansion.some((file) =>
Expand All @@ -18,67 +19,61 @@ export const jscodeshiftExecutable = require.resolve('.bin/jscodeshift')
export const transformerDirectory = join(__dirname, '../', 'transforms')

export async function runTransform(
transform: string,
path: string,
codemod: string,
source: string,
options: any
) {
let transformer = transform
let directory = path
const { dry, print, runInBand, jscodeshift, force } = options

if (!options.dry) {
checkGitStatus(options.force)
if (!dry) {
checkGitStatus(force)
}

if (
transform &&
!TRANSFORMER_INQUIRER_CHOICES.find((x) => x.value === transform)
) {
console.error('Invalid transform choice, pick one of:')
console.error(
TRANSFORMER_INQUIRER_CHOICES.map((x) => '- ' + x.value).join('\n')
)
if (codemod && !CODEMOD_CHOICES.find((x) => x.value === codemod)) {
console.error(`Invalid transform choice "${codemod}", pick one of:`)
console.error(CODEMOD_CHOICES.map((x) => '- ' + x.value).join('\n'))
process.exit(1)
}

if (!path) {
if (!source) {
const res = await prompts({
type: 'text',
name: 'path',
name: 'source',
message: 'On which files or directory should the codemods be applied?',
default: '.',
onState: onPromptState,
initial: '.',
})

directory = res.path
source = res.source
}
if (!transform) {
if (!codemod) {
const res = await prompts({
type: 'select',
name: 'transformer',
name: 'codemod',
message: 'Which transform would you like to apply?',
choices: TRANSFORMER_INQUIRER_CHOICES,
choices: CODEMOD_CHOICES,
onState: onPromptState,
})

transformer = res.transformer
codemod = res.codemod
}

const filesExpanded = expandFilePathsIfNeeded([directory])
const filesExpanded = expandFilePathsIfNeeded([source])

if (!filesExpanded.length) {
console.log(`No files found matching "${directory}"`)
console.log(`No files found matching "${source}"`)
return null
}

const transformerPath = join(transformerDirectory, `${transformer}.js`)
const transformerPath = join(transformerDirectory, `${codemod}.js`)

if (transformer === 'cra-to-next') {
if (codemod === 'cra-to-next') {
// cra-to-next transform doesn't use jscodeshift directly
return require(transformerPath).default(filesExpanded, options)
}

let args = []

const { dry, print, runInBand, jscodeshift } = options

if (dry) {
args.push('--dry')
}
Expand Down Expand Up @@ -115,7 +110,7 @@ export async function runTransform(
throw new Error(`jscodeshift exited with code ${result.exitCode}`)
}

if (!dry && transformer === 'built-in-next-font') {
if (!dry && codemod === 'built-in-next-font') {
console.log('Uninstalling `@next/font`')
try {
uninstallPackage('@next/font')
Expand All @@ -126,8 +121,8 @@ export async function runTransform(
}
}

if (!dry && transformer === 'next-request-geo-ip') {
if (!dry && codemod === 'next-request-geo-ip') {
console.log('Installing `@vercel/functions`...')
installPackage('@vercel/functions')
await installPackage('@vercel/functions')
}
}
Loading