Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(next-upgrade): suggest React codemod #71016

Merged
merged 15 commits into from
Oct 11, 2024
Merged
Changes from 6 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
87 changes: 84 additions & 3 deletions packages/next-codemod/bin/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,31 @@ export async function runUpgrade(
return
}

// If the app is a pure Pages Router (no app dir) and the user wants to stay on React 18,
// we can skip the upgrading React and suggesting relevant codemods.
const { isPagesOnlyAndWantReact18 } = await prompts(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's only ask that if you're above .45 and still have 18 installed. Saves one prompt for the good citizen who are on 19.

Technically you can't withhold if you're between .45 and whatever version we landed support for React 18 in pages router (.169?) but that's not really a version range we need to be concerned with.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you find your request resolved?

{
type: 'confirm',
name: 'isPagesOnlyAndWantReact18',
message:
'Are you using Pages Router only (no App Router) and want to upgrade up until React 18?',
devjiwonchoi marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having a hard time understanding what this prompt is asking.

initial: false,
active: 'Yes',
inactive: 'No',
},
{ onCancel }
)

// We're resolving a specific version here to avoid including "ugly" version queries
// in the manifest.
// E.g. in peerDependencies we could have `^18.2.0 || ^19.0.0 || 20.0.0-canary`
// If we'd just `npm add` that, the manifest would read the same version query.
// This is basically a `npm --save-exact react@$versionQuery` that works for every package manager.
const targetReactVersion = await loadHighestNPMVersionMatching(
`react@${targetNextPackageJson.peerDependencies['react']}`
)
const targetReactVersion = !isPagesOnlyAndWantReact18
devjiwonchoi marked this conversation as resolved.
Show resolved Hide resolved
? await loadHighestNPMVersionMatching(
`react@${targetNextPackageJson.peerDependencies['react']}`
)
: '18.3.0'
devjiwonchoi marked this conversation as resolved.
Show resolved Hide resolved

if (compareVersions(targetNextVersion, '15.0.0-canary') >= 0) {
await suggestTurbopack(appPackageJson)
Expand Down Expand Up @@ -134,6 +151,12 @@ export async function runUpgrade(
await runTransform(codemod, process.cwd(), { force: true })
}

// The following React codemods are for React 19
if (!isPagesOnlyAndWantReact18) {
await suggestReactCodemods(packageManager)
await suggestReactTypesCodemods(packageManager)
}

console.log() // new line
if (codemods.length > 0) {
console.log(`${pc.green('✔')} Codemods have been applied successfully.`)
Expand Down Expand Up @@ -263,3 +286,61 @@ async function suggestCodemods(

return codemods
}

async function suggestReactCodemods(packageManager: PackageManager) {
const { runReactCodemod } = await prompts(
{
type: 'toggle',
name: 'runReactCodemod',
message: 'Do you want to run the React 19 upgrade codemod?',
devjiwonchoi marked this conversation as resolved.
Show resolved Hide resolved
devjiwonchoi marked this conversation as resolved.
Show resolved Hide resolved
initial: true,
active: 'Yes',
inactive: 'No',
},
{ onCancel }
)

if (runReactCodemod) {
const commandMap = {
yarn: 'yarn dlx',
pnpm: 'pnpx',
bun: 'bunx',
npm: 'npx',
}
const command = commandMap[packageManager] || 'npx'

// https://react.dev/blog/2024/04/25/react-19-upgrade-guide#run-all-react-19-codemods
execSync(`${command} codemod@latest react/19/migration-recipe`, {
stdio: 'inherit',
})
}
devjiwonchoi marked this conversation as resolved.
Show resolved Hide resolved
}

async function suggestReactTypesCodemods(packageManager: PackageManager) {
const { runReactTypesCodemod } = await prompts(
{
type: 'toggle',
name: 'runReactTypesCodemod',
message: 'Do you want to run the React Types codemod?',
devjiwonchoi marked this conversation as resolved.
Show resolved Hide resolved
devjiwonchoi marked this conversation as resolved.
Show resolved Hide resolved
initial: true,
active: 'Yes',
inactive: 'No',
},
{ onCancel }
)

if (runReactTypesCodemod) {
const commandMap = {
yarn: 'yarn dlx',
pnpm: 'pnpx',
bun: 'bunx',
npm: 'npx',
}
const command = commandMap[packageManager] || 'npx'

// https://react.dev/blog/2024/04/25/react-19-upgrade-guide#typescript-changes
execSync(`${command} types-react-codemod@latest preset-19 .`, {
devjiwonchoi marked this conversation as resolved.
Show resolved Hide resolved
stdio: 'inherit',
})
devjiwonchoi marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading