mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
## Why Sokra flagged in Slack that \`pnpm dlx @next/codemod@canary upgrade preview\` hangs forever when an agent invokes it: the recommended-codemods multiselect blocks waiting for keyboard input. Same hazard for any CI that pipes the command. Agents end up choosing the manual non-interactive path, which defeats the codemod entirely. ## What - Adds \`-y, --yes\` to \`@next/codemod upgrade\`. - Auto-detects \`!process.stdin.isTTY\` so agents and CI get non-interactive mode without needing to know the flag. - In non-interactive mode every prompt accepts its existing default: - React 18-or-19 -> upgrade (existing \`initial: false\` stays-on-18). - Turbopack enable -> yes (\`initial: true\`). - Custom-dev-script fallback -> leave the script untouched (we can't ask for a replacement without a TTY). - Recommended codemods multiselect -> apply all (matches \`selected: true\` on every choice). Logged to stdout so the agent sees what ran. - React 19 + React 19 Types codemod confirms -> yes. ## How Single \`nonInteractive\` flag plumbed into the four \`suggest*\` helpers, each short-circuits before its \`prompts(...)\` call and returns the default. Type-check clean. <!-- NEXT_JS_LLM_PR -->
110 lines
3.6 KiB
JavaScript
110 lines
3.6 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Copyright 2015-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
*/
|
|
// 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 { Command } from 'commander'
|
|
import { runUpgrade } from './upgrade'
|
|
import { runAgentsMd } from './agents-md'
|
|
import { runTransform } from './transform'
|
|
import { BadInput } from './shared'
|
|
|
|
const packageJson = require('../package.json')
|
|
const program = new Command(packageJson.name)
|
|
.description('Codemods for updating Next.js apps.')
|
|
.version(
|
|
packageJson.version,
|
|
'-v, --version',
|
|
'Output the current version of @next/codemod.'
|
|
)
|
|
.argument(
|
|
'[codemod]',
|
|
'Codemod slug to run. See "https://github.com/vercel/next.js/tree/canary/packages/next-codemod".'
|
|
)
|
|
.argument(
|
|
'[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 stdout, useful for development'
|
|
)
|
|
.option('--verbose', 'Show more information about the transform process')
|
|
.option(
|
|
'-j, --jscodeshift',
|
|
'(Advanced) Pass options directly to jscodeshift'
|
|
)
|
|
.action(runTransform)
|
|
.allowUnknownOption()
|
|
// This is needed for options for subcommands to be passed correctly.
|
|
// Because by default the options are not positional, which will pass options
|
|
// to the main command "@next/codemod" even if it was passed after subcommands,
|
|
// e.g. "@next/codemod upgrade --verbose" will be treated as "next-codemod --verbose upgrade"
|
|
// By enabling this, it will respect the position of the options and pass it to subcommands.
|
|
// x-ref: https://github.com/tj/commander.js/pull/1427
|
|
.enablePositionalOptions()
|
|
|
|
program
|
|
.command('upgrade')
|
|
.description(
|
|
'Upgrade Next.js apps to desired versions with a single command.'
|
|
)
|
|
.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".'
|
|
)
|
|
.usage('[revision] [options]')
|
|
.option('--verbose', 'Verbose output', false)
|
|
.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
|
|
)
|
|
.action(async (revision, options) => {
|
|
try {
|
|
await runUpgrade(revision, options)
|
|
} catch (error) {
|
|
if (!options.verbose && error instanceof BadInput) {
|
|
console.error(error.message)
|
|
} else {
|
|
console.error(error)
|
|
}
|
|
process.exit(1)
|
|
}
|
|
})
|
|
|
|
program
|
|
.command('agents-md')
|
|
.description(
|
|
'Generate Next.js documentation index for AI coding agents (Claude, Cursor, etc.).'
|
|
)
|
|
.option(
|
|
'--version <version>',
|
|
'Next.js version (auto-detected if not provided)'
|
|
)
|
|
.option('--output <file>', 'Target file path (e.g., CLAUDE.md, AGENTS.md)')
|
|
.action(async (options) => {
|
|
try {
|
|
await runAgentsMd(options)
|
|
} catch (error) {
|
|
if (error instanceof BadInput) {
|
|
console.error(error.message)
|
|
} else {
|
|
console.error(error)
|
|
}
|
|
process.exit(1)
|
|
}
|
|
})
|
|
|
|
program.parse(process.argv)
|