@next/codemod 15.0.0-rc.0 → 15.0.0-rc.1

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.
Files changed (33) hide show
  1. package/bin/next-codemod.js +55 -3
  2. package/bin/shared.js +7 -0
  3. package/bin/transform.js +124 -0
  4. package/bin/upgrade.js +390 -0
  5. package/lib/cra-to-next/global-css-transform.js +5 -5
  6. package/lib/cra-to-next/index-to-component.js +5 -5
  7. package/lib/handle-package.js +110 -0
  8. package/lib/install.js +2 -3
  9. package/lib/parser.js +28 -0
  10. package/lib/run-jscodeshift.js +18 -2
  11. package/lib/utils.js +115 -0
  12. package/package.json +13 -6
  13. package/transforms/add-missing-react-import.js +4 -3
  14. package/transforms/app-dir-runtime-config-experimental-edge.js +34 -0
  15. package/transforms/built-in-next-font.js +4 -3
  16. package/transforms/cra-to-next.js +238 -236
  17. package/transforms/lib/async-request-api/index.js +16 -0
  18. package/transforms/lib/async-request-api/next-async-dynamic-api.js +274 -0
  19. package/transforms/lib/async-request-api/next-async-dynamic-prop.js +715 -0
  20. package/transforms/lib/async-request-api/utils.js +374 -0
  21. package/transforms/metadata-to-viewport-export.js +4 -3
  22. package/transforms/name-default-component.js +6 -6
  23. package/transforms/new-link.js +9 -7
  24. package/transforms/next-async-request-api.js +9 -0
  25. package/transforms/next-dynamic-access-named-export.js +66 -0
  26. package/transforms/next-image-experimental.js +12 -15
  27. package/transforms/next-image-to-legacy-image.js +8 -9
  28. package/transforms/next-og-import.js +4 -3
  29. package/transforms/next-request-geo-ip.js +339 -0
  30. package/transforms/url-to-withrouter.js +1 -1
  31. package/transforms/withamp-to-config.js +1 -1
  32. package/bin/cli.js +0 -216
  33. package/lib/uninstall-package.js +0 -32
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ "use strict";
2
3
  /**
3
4
  * Copyright 2015-present, Facebook, Inc.
4
5
  *
@@ -6,7 +7,58 @@
6
7
  * LICENSE file in the root directory of this source tree.
7
8
  *
8
9
  */
9
- // Based on https://github.com/reactjs/react-codemod/blob/dd8671c9a470a2c342b221ec903c574cf31e9f57/bin/react-codemod.js
10
- // next-codemod optional-name-of-transform optional/path/to/src [...options]
11
- require('./cli').run();
10
+ // Based on https://github.com/reactjs/react-codemod/blob/dd8671c9a470a2c342b221ec903c574cf31e9f57/bin/cli.js
11
+ // @next/codemod optional-name-of-transform optional/path/to/src [...options]
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ const commander_1 = require("commander");
14
+ const upgrade_1 = require("./upgrade");
15
+ const transform_1 = require("./transform");
16
+ const shared_1 = require("./shared");
17
+ const packageJson = require('../package.json');
18
+ const program = new commander_1.Command(packageJson.name)
19
+ .description('Codemods for updating Next.js apps.')
20
+ .version(packageJson.version, '-v, --version', 'Output the current version of @next/codemod.')
21
+ .argument('[codemod]', 'Codemod slug to run. See "https://github.com/vercel/next.js/tree/canary/packages/next-codemod".')
22
+ .argument('[source]', 'Path to source files or directory to transform including glob patterns.')
23
+ .usage('[codemod] [source] [options]')
24
+ .helpOption('-h, --help', 'Display this help message.')
25
+ .option('-f, --force', 'Bypass Git safety checks and forcibly run codemods')
26
+ .option('-d, --dry', 'Dry run (no changes are made to files)')
27
+ .option('-p, --print', 'Print transformed files to stdout, useful for development')
28
+ .option('--verbose', 'Show more information about the transform process')
29
+ .option('-j, --jscodeshift', '(Advanced) Pass options directly to jscodeshift')
30
+ .action(transform_1.runTransform)
31
+ .allowUnknownOption()
32
+ // This is needed for options for subcommands to be passed correctly.
33
+ // Because by default the options are not positional, which will pass options
34
+ // to the main command "@next/codemod" even if it was passed after subcommands,
35
+ // e.g. "@next/codemod upgrade --verbose" will be treated as "next-codemod --verbose upgrade"
36
+ // By enabling this, it will respect the position of the options and pass it to subcommands.
37
+ // x-ref: https://github.com/tj/commander.js/pull/1427
38
+ .enablePositionalOptions();
39
+ program
40
+ .command('upgrade')
41
+ .description('Upgrade Next.js apps to desired versions with a single command.')
42
+ .argument('[revision]', 'Specify the target Next.js version using an NPM dist tag (e.g. "latest", "canary", "rc") or an exact version number (e.g. "15.0.0").', packageJson.version.includes('-canary.')
43
+ ? 'canary'
44
+ : packageJson.version.includes('-rc.')
45
+ ? 'rc'
46
+ : 'latest')
47
+ .usage('[revision] [options]')
48
+ .option('--verbose', 'Verbose output', false)
49
+ .action(async (revision, options) => {
50
+ try {
51
+ await (0, upgrade_1.runUpgrade)(revision, options);
52
+ }
53
+ catch (error) {
54
+ if (!options.verbose && error instanceof shared_1.BadInput) {
55
+ console.error(error.message);
56
+ }
57
+ else {
58
+ console.error(error);
59
+ }
60
+ process.exit(1);
61
+ }
62
+ });
63
+ program.parse(process.argv);
12
64
  //# sourceMappingURL=next-codemod.js.map
package/bin/shared.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BadInput = void 0;
4
+ class BadInput extends Error {
5
+ }
6
+ exports.BadInput = BadInput;
7
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.transformerDirectory = exports.jscodeshiftExecutable = void 0;
7
+ exports.runTransform = runTransform;
8
+ const execa_1 = __importDefault(require("execa"));
9
+ const globby_1 = __importDefault(require("globby"));
10
+ const prompts_1 = __importDefault(require("prompts"));
11
+ const node_path_1 = require("node:path");
12
+ const handle_package_1 = require("../lib/handle-package");
13
+ const utils_1 = require("../lib/utils");
14
+ function expandFilePathsIfNeeded(filesBeforeExpansion) {
15
+ const shouldExpandFiles = filesBeforeExpansion.some((file) => file.includes('*'));
16
+ return shouldExpandFiles
17
+ ? globby_1.default.sync(filesBeforeExpansion)
18
+ : filesBeforeExpansion;
19
+ }
20
+ exports.jscodeshiftExecutable = require.resolve('.bin/jscodeshift');
21
+ exports.transformerDirectory = (0, node_path_1.join)(__dirname, '../', 'transforms');
22
+ async function runTransform(transform, path, options) {
23
+ let transformer = transform;
24
+ let directory = path;
25
+ if (!options.dry) {
26
+ (0, utils_1.checkGitStatus)(options.force);
27
+ }
28
+ if (transform &&
29
+ !utils_1.TRANSFORMER_INQUIRER_CHOICES.find((x) => x.value === transform)) {
30
+ console.error('Invalid transform choice, pick one of:');
31
+ console.error(utils_1.TRANSFORMER_INQUIRER_CHOICES.map((x) => '- ' + x.value).join('\n'));
32
+ process.exit(1);
33
+ }
34
+ if (!path) {
35
+ const res = await (0, prompts_1.default)({
36
+ type: 'text',
37
+ name: 'path',
38
+ message: 'On which files or directory should the codemods be applied?',
39
+ initial: '.',
40
+ }, { onCancel: utils_1.onCancel });
41
+ directory = res.path;
42
+ }
43
+ if (!transform) {
44
+ const res = await (0, prompts_1.default)({
45
+ type: 'select',
46
+ name: 'transformer',
47
+ message: 'Which transform would you like to apply?',
48
+ choices: utils_1.TRANSFORMER_INQUIRER_CHOICES.reverse().map(({ title, value, version }) => {
49
+ return {
50
+ title: `(v${version}) ${value}`,
51
+ description: title,
52
+ value,
53
+ };
54
+ }),
55
+ }, { onCancel: utils_1.onCancel });
56
+ transformer = res.transformer;
57
+ }
58
+ const filesExpanded = expandFilePathsIfNeeded([directory]);
59
+ if (!filesExpanded.length) {
60
+ console.log(`No files found matching "${directory}"`);
61
+ return null;
62
+ }
63
+ const transformerPath = (0, node_path_1.join)(exports.transformerDirectory, `${transformer}.js`);
64
+ if (transformer === 'cra-to-next') {
65
+ // cra-to-next transform doesn't use jscodeshift directly
66
+ return require(transformerPath).default(filesExpanded, options);
67
+ }
68
+ let args = [];
69
+ const { dry, print, runInBand, jscodeshift, verbose } = options;
70
+ if (dry) {
71
+ args.push('--dry');
72
+ }
73
+ if (print) {
74
+ args.push('--print');
75
+ }
76
+ if (runInBand) {
77
+ args.push('--run-in-band');
78
+ }
79
+ if (verbose) {
80
+ args.push('--verbose=2');
81
+ }
82
+ args.push('--no-babel');
83
+ args.push('--ignore-pattern=**/node_modules/**');
84
+ args.push('--ignore-pattern=**/.next/**');
85
+ args.push('--extensions=tsx,ts,jsx,js');
86
+ args = args.concat(['--transform', transformerPath]);
87
+ if (jscodeshift) {
88
+ args = args.concat(jscodeshift);
89
+ }
90
+ args = args.concat(filesExpanded);
91
+ console.log(`Executing command: jscodeshift ${args.join(' ')}`);
92
+ const result = execa_1.default.sync(exports.jscodeshiftExecutable, args, {
93
+ stdio: 'inherit',
94
+ stripFinalNewline: false,
95
+ });
96
+ if (result.failed) {
97
+ throw new Error(`jscodeshift exited with code ${result.exitCode}`);
98
+ }
99
+ if (!dry && transformer === 'built-in-next-font') {
100
+ const { uninstallNextFont } = await (0, prompts_1.default)({
101
+ type: 'confirm',
102
+ name: 'uninstallNextFont',
103
+ message: 'Do you want to uninstall `@next/font`?',
104
+ initial: true,
105
+ });
106
+ if (uninstallNextFont) {
107
+ console.log('Uninstalling `@next/font`');
108
+ (0, handle_package_1.uninstallPackage)('@next/font');
109
+ }
110
+ }
111
+ if (!dry && transformer === 'next-request-geo-ip') {
112
+ const { installVercelFunctions } = await (0, prompts_1.default)({
113
+ type: 'confirm',
114
+ name: 'installVercelFunctions',
115
+ message: 'Do you want to install `@vercel/functions`?',
116
+ initial: true,
117
+ });
118
+ if (installVercelFunctions) {
119
+ console.log('Installing `@vercel/functions`...');
120
+ (0, handle_package_1.installPackages)(['@vercel/functions']);
121
+ }
122
+ }
123
+ }
124
+ //# sourceMappingURL=transform.js.map
package/bin/upgrade.js ADDED
@@ -0,0 +1,390 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runUpgrade = runUpgrade;
7
+ const prompts_1 = __importDefault(require("prompts"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const semver_1 = __importDefault(require("semver"));
10
+ const compare_1 = __importDefault(require("semver/functions/compare"));
11
+ const child_process_1 = require("child_process");
12
+ const path_1 = __importDefault(require("path"));
13
+ const picocolors_1 = __importDefault(require("picocolors"));
14
+ const handle_package_1 = require("../lib/handle-package");
15
+ const transform_1 = require("./transform");
16
+ const utils_1 = require("../lib/utils");
17
+ const shared_1 = require("./shared");
18
+ /**
19
+ * @param query
20
+ * @example loadHighestNPMVersionMatching("react@^18.3.0 || ^19.0.0") === Promise<"19.0.0">
21
+ */
22
+ async function loadHighestNPMVersionMatching(query) {
23
+ const versionsJSON = (0, child_process_1.execSync)(`npm --silent view "${query}" --json --field version`, { encoding: 'utf-8' });
24
+ const versionOrVersions = JSON.parse(versionsJSON);
25
+ if (versionOrVersions.length < 1) {
26
+ throw new Error(`Found no React versions matching "${query}". This is a bug in the upgrade tool.`);
27
+ }
28
+ // npm-view returns an array if there are multiple versions matching the query.
29
+ if (Array.isArray(versionOrVersions)) {
30
+ // The last entry will be the latest version published.
31
+ return versionOrVersions[versionOrVersions.length - 1];
32
+ }
33
+ return versionOrVersions;
34
+ }
35
+ function endMessage() {
36
+ console.log();
37
+ console.log(picocolors_1.default.white(picocolors_1.default.bold(`Please review the local changes and read the Next.js 15 migration guide to complete the migration.`)));
38
+ console.log(picocolors_1.default.underline('https://nextjs.org/docs/canary/app/building-your-application/upgrading/version-15'));
39
+ }
40
+ async function runUpgrade(revision, options) {
41
+ const { verbose } = options;
42
+ const appPackageJsonPath = path_1.default.resolve(process.cwd(), 'package.json');
43
+ let appPackageJson = JSON.parse(fs_1.default.readFileSync(appPackageJsonPath, 'utf8'));
44
+ let targetNextPackageJson;
45
+ const res = await fetch(`https://registry.npmjs.org/next/${revision}`);
46
+ if (res.status === 200) {
47
+ targetNextPackageJson = await res.json();
48
+ }
49
+ const validRevision = targetNextPackageJson !== null &&
50
+ typeof targetNextPackageJson === 'object' &&
51
+ 'version' in targetNextPackageJson &&
52
+ 'peerDependencies' in targetNextPackageJson;
53
+ if (!validRevision) {
54
+ throw new shared_1.BadInput(`Invalid revision provided: "${revision}". Please provide a valid Next.js version or dist-tag (e.g. "latest", "canary", "rc", or "15.0.0").\nCheck available versions at https://www.npmjs.com/package/next?activeTab=versions.`);
55
+ }
56
+ const installedNextVersion = getInstalledNextVersion();
57
+ const targetNextVersion = targetNextPackageJson.version;
58
+ if ((0, compare_1.default)(installedNextVersion, targetNextVersion) === 0) {
59
+ console.log(`${picocolors_1.default.green('✓')} Current Next.js version is already on the target version "v${targetNextVersion}".`);
60
+ endMessage();
61
+ return;
62
+ }
63
+ if ((0, compare_1.default)(installedNextVersion, targetNextVersion) > 0) {
64
+ console.log(`${picocolors_1.default.green('✓')} Current Next.js version is higher than the target version "v${targetNextVersion}".`);
65
+ endMessage();
66
+ return;
67
+ }
68
+ const installedReactVersion = getInstalledReactVersion();
69
+ // Align the prefix spaces
70
+ console.log(` Detected installed versions:`);
71
+ console.log(` - React: v${installedReactVersion}`);
72
+ console.log(` - Next.js: v${installedNextVersion}`);
73
+ let shouldStayOnReact18 = false;
74
+ if (
75
+ // From release v14.3.0-canary.45, Next.js expects the React version to be 19.0.0-beta.0
76
+ // If the user is on a version higher than this but is still on React 18, we ask them
77
+ // if they still want to stay on React 18 after the upgrade.
78
+ // IF THE USER USES APP ROUTER, we expect them to upgrade React to > 19.0.0-beta.0,
79
+ // we should only let the user stay on React 18 if they are using pure Pages Router.
80
+ // x-ref(PR): https://github.com/vercel/next.js/pull/65058
81
+ // x-ref(release): https://github.com/vercel/next.js/releases/tag/v14.3.0-canary.45
82
+ (0, compare_1.default)(installedNextVersion, '14.3.0-canary.45') >= 0 &&
83
+ installedReactVersion.startsWith('18')) {
84
+ const shouldStayOnReact18Res = await (0, prompts_1.default)({
85
+ type: 'confirm',
86
+ name: 'shouldStayOnReact18',
87
+ message: `Are you using ${picocolors_1.default.underline('only the Pages Router')} (no App Router) and prefer to stay on React 18?`,
88
+ initial: false,
89
+ active: 'Yes',
90
+ inactive: 'No',
91
+ }, { onCancel: utils_1.onCancel });
92
+ shouldStayOnReact18 = shouldStayOnReact18Res.shouldStayOnReact18;
93
+ }
94
+ // We're resolving a specific version here to avoid including "ugly" version queries
95
+ // in the manifest.
96
+ // E.g. in peerDependencies we could have `^18.2.0 || ^19.0.0 || 20.0.0-canary`
97
+ // If we'd just `npm add` that, the manifest would read the same version query.
98
+ // This is basically a `npm --save-exact react@$versionQuery` that works for every package manager.
99
+ const targetReactVersion = shouldStayOnReact18
100
+ ? '18.3.1'
101
+ : await loadHighestNPMVersionMatching(`react@${targetNextPackageJson.peerDependencies['react']}`);
102
+ if ((0, compare_1.default)(targetNextVersion, '15.0.0-canary') >= 0) {
103
+ await suggestTurbopack(appPackageJson);
104
+ }
105
+ const codemods = await suggestCodemods(installedNextVersion, targetNextVersion);
106
+ const packageManager = (0, handle_package_1.getPkgManager)(process.cwd());
107
+ let shouldRunReactCodemods = false;
108
+ let shouldRunReactTypesCodemods = false;
109
+ let execCommand = 'npx';
110
+ // The following React codemods are for React 19
111
+ if (!shouldStayOnReact18 &&
112
+ (0, compare_1.default)(targetReactVersion, '19.0.0-beta.0') >= 0) {
113
+ shouldRunReactCodemods = await suggestReactCodemods();
114
+ shouldRunReactTypesCodemods = await suggestReactTypesCodemods();
115
+ const execCommandMap = {
116
+ yarn: 'yarn dlx',
117
+ pnpm: 'pnpx',
118
+ bun: 'bunx',
119
+ npm: 'npx',
120
+ };
121
+ execCommand = execCommandMap[packageManager];
122
+ }
123
+ fs_1.default.writeFileSync(appPackageJsonPath, JSON.stringify(appPackageJson, null, 2));
124
+ const dependenciesToInstall = [];
125
+ const devDependenciesToInstall = [];
126
+ const allDependencies = {
127
+ ...appPackageJson.dependencies,
128
+ ...appPackageJson.devDependencies,
129
+ };
130
+ const versionMapping = {
131
+ next: { version: targetNextVersion, required: true },
132
+ react: { version: targetReactVersion, required: true },
133
+ 'react-dom': { version: targetReactVersion, required: true },
134
+ 'react-is': { version: targetReactVersion, required: false },
135
+ };
136
+ if (targetReactVersion.startsWith('19.0.0-canary') ||
137
+ targetReactVersion.startsWith('19.0.0-beta') ||
138
+ targetReactVersion.startsWith('19.0.0-rc')) {
139
+ const [targetReactTypesVersion, targetReactDOMTypesVersion] = await Promise.all([
140
+ loadHighestNPMVersionMatching(`types-react@rc`),
141
+ loadHighestNPMVersionMatching(`types-react-dom@rc`),
142
+ ]);
143
+ if (allDependencies['@types/react']) {
144
+ versionMapping['@types/react'] = {
145
+ version: `npm:types-react@${targetReactTypesVersion}`,
146
+ required: false,
147
+ };
148
+ }
149
+ if (allDependencies['@types/react-dom']) {
150
+ versionMapping['@types/react-dom'] = {
151
+ version: `npm:types-react-dom@${targetReactDOMTypesVersion}`,
152
+ required: false,
153
+ };
154
+ }
155
+ }
156
+ else {
157
+ const [targetReactTypesVersion, targetReactDOMTypesVersion] = await Promise.all([
158
+ loadHighestNPMVersionMatching(`@types/react@${targetNextPackageJson.peerDependencies['react']}`),
159
+ loadHighestNPMVersionMatching(`@types/react-dom@${targetNextPackageJson.peerDependencies['react']}`),
160
+ ]);
161
+ if (allDependencies['@types/react']) {
162
+ versionMapping['@types/react'] = {
163
+ version: targetReactTypesVersion,
164
+ required: false,
165
+ };
166
+ }
167
+ if (allDependencies['@types/react-dom']) {
168
+ versionMapping['@types/react-dom'] = {
169
+ version: targetReactDOMTypesVersion,
170
+ required: false,
171
+ };
172
+ }
173
+ }
174
+ // Even though we only need those if we alias `@types/react` to types-react,
175
+ // we still do it out of safety due to https://github.com/microsoft/DefinitelyTyped-tools/issues/433.
176
+ const overrides = {};
177
+ if (allDependencies['@types/react']) {
178
+ overrides['@types/react'] = versionMapping['@types/react'].version;
179
+ }
180
+ if (allDependencies['@types/react-dom']) {
181
+ overrides['@types/react-dom'] = versionMapping['@types/react-dom'].version;
182
+ }
183
+ writeOverridesField(appPackageJson, packageManager, overrides);
184
+ for (const [packageName, { version, required }] of Object.entries(versionMapping)) {
185
+ if (appPackageJson.devDependencies?.[packageName]) {
186
+ devDependenciesToInstall.push([packageName, version]);
187
+ }
188
+ else if (required || appPackageJson.dependencies?.[packageName]) {
189
+ dependenciesToInstall.push([packageName, version]);
190
+ }
191
+ }
192
+ console.log(`Upgrading your project to ${picocolors_1.default.blue('Next.js ' + targetNextVersion)}...\n`);
193
+ for (const [dep, version] of dependenciesToInstall) {
194
+ (0, handle_package_1.addPackageDependency)(appPackageJson, dep, version, false);
195
+ }
196
+ for (const [dep, version] of devDependenciesToInstall) {
197
+ (0, handle_package_1.addPackageDependency)(appPackageJson, dep, version, true);
198
+ }
199
+ fs_1.default.writeFileSync(appPackageJsonPath, JSON.stringify(appPackageJson, null, 2));
200
+ (0, handle_package_1.runInstallation)(packageManager);
201
+ for (const codemod of codemods) {
202
+ await (0, transform_1.runTransform)(codemod, process.cwd(), { force: true, verbose });
203
+ }
204
+ // To reduce user-side burden of selecting which codemods to run as it needs additional
205
+ // understanding of the codemods, we run all of the applicable codemods.
206
+ if (shouldRunReactCodemods) {
207
+ // https://react.dev/blog/2024/04/25/react-19-upgrade-guide#run-all-react-19-codemods
208
+ (0, child_process_1.execSync)(
209
+ // `--no-interactive` skips the interactive prompt that asks for confirmation
210
+ // https://github.com/codemod-com/codemod/blob/c0cf00d13161a0ec0965b6cc6bc5d54076839cc8/apps/cli/src/flags.ts#L160
211
+ `${execCommand} codemod@latest react/19/migration-recipe --no-interactive`, { stdio: 'inherit' });
212
+ }
213
+ if (shouldRunReactTypesCodemods) {
214
+ // https://react.dev/blog/2024/04/25/react-19-upgrade-guide#typescript-changes
215
+ // `--yes` skips prompts and applies all codemods automatically
216
+ // https://github.com/eps1lon/types-react-codemod/blob/8463103233d6b70aad3cd6bee1814001eae51b28/README.md?plain=1#L52
217
+ (0, child_process_1.execSync)(`${execCommand} types-react-codemod@latest --yes preset-19 .`, {
218
+ stdio: 'inherit',
219
+ });
220
+ }
221
+ console.log(); // new line
222
+ if (codemods.length > 0) {
223
+ console.log(`${picocolors_1.default.green('✔')} Codemods have been applied successfully.`);
224
+ }
225
+ endMessage();
226
+ }
227
+ function getInstalledNextVersion() {
228
+ try {
229
+ return require(require.resolve('next/package.json', {
230
+ paths: [process.cwd()],
231
+ })).version;
232
+ }
233
+ catch (error) {
234
+ throw new shared_1.BadInput(`Failed to get the installed Next.js version at "${process.cwd()}".\nIf you're using a monorepo, please run this command from the Next.js app directory.`, {
235
+ cause: error,
236
+ });
237
+ }
238
+ }
239
+ function getInstalledReactVersion() {
240
+ try {
241
+ return require(require.resolve('react/package.json', {
242
+ paths: [process.cwd()],
243
+ })).version;
244
+ }
245
+ catch (error) {
246
+ throw new shared_1.BadInput(`Failed to detect the installed React version in "${process.cwd()}".\nIf you're working in a monorepo, please run this command from the Next.js app directory.`, {
247
+ cause: error,
248
+ });
249
+ }
250
+ }
251
+ /*
252
+ * Heuristics are used to determine whether to Turbopack is enabled or not and
253
+ * to determine how to update the dev script.
254
+ *
255
+ * 1. If the dev script contains `--turbo` option, we assume that Turbopack is
256
+ * already enabled.
257
+ * 2. If the dev script contains the string `next dev`, we replace it to
258
+ * `next dev --turbo`.
259
+ * 3. Otherwise, we ask the user to manually add `--turbo` to their dev command,
260
+ * showing the current dev command as the initial value.
261
+ */
262
+ async function suggestTurbopack(packageJson) {
263
+ const devScript = packageJson.scripts['dev'];
264
+ if (devScript.includes('--turbo'))
265
+ return;
266
+ const responseTurbopack = await (0, prompts_1.default)({
267
+ type: 'confirm',
268
+ name: 'enable',
269
+ message: 'Enable Turbopack for next dev?',
270
+ initial: true,
271
+ }, { onCancel: utils_1.onCancel });
272
+ if (!responseTurbopack.enable) {
273
+ return;
274
+ }
275
+ if (devScript.includes('next dev')) {
276
+ packageJson.scripts['dev'] = devScript.replace('next dev', 'next dev --turbo');
277
+ return;
278
+ }
279
+ console.log(`${picocolors_1.default.yellow('⚠')} Could not find "${picocolors_1.default.bold('next dev')}" in your dev script.`);
280
+ const responseCustomDevScript = await (0, prompts_1.default)({
281
+ type: 'text',
282
+ name: 'customDevScript',
283
+ message: 'Please manually add "--turbo" to your dev command.',
284
+ initial: devScript,
285
+ }, { onCancel: utils_1.onCancel });
286
+ packageJson.scripts['dev'] =
287
+ responseCustomDevScript.customDevScript || devScript;
288
+ }
289
+ async function suggestCodemods(initialNextVersion, targetNextVersion) {
290
+ // Here we suggest pre-released codemods by their "stable" version.
291
+ // It is because if we suggest by the version range (installed ~ target),
292
+ // pre-released codemods for the target version are not suggested when upgrading.
293
+ // Let's say we have a codemod for v15.0.0-canary.x, and we're upgrading from
294
+ // v15.x -> v15.x. Our initial version is higher than the codemod's version,
295
+ // so the codemod will not be suggested.
296
+ // This is not ideal as the codemods for pre-releases are also targeting the major version.
297
+ // Also, when the user attempts to run the upgrade command twice, and have installed the
298
+ // target version, the behavior must be idempotent and suggest the codemods including the
299
+ // pre-releases of the target version.
300
+ const initial = semver_1.default.parse(initialNextVersion);
301
+ const initialVersionIndex = utils_1.TRANSFORMER_INQUIRER_CHOICES.findIndex((versionCodemods) => {
302
+ const codemod = semver_1.default.parse(versionCodemods.version);
303
+ return ((0, compare_1.default)(`${codemod.major}.${codemod.minor}.${codemod.patch}`, `${initial.major}.${initial.minor}.${initial.patch}`) >= 0);
304
+ });
305
+ if (initialVersionIndex === -1) {
306
+ return [];
307
+ }
308
+ let targetVersionIndex = utils_1.TRANSFORMER_INQUIRER_CHOICES.findIndex((versionCodemods) => (0, compare_1.default)(versionCodemods.version, targetNextVersion) > 0);
309
+ if (targetVersionIndex === -1) {
310
+ targetVersionIndex = utils_1.TRANSFORMER_INQUIRER_CHOICES.length;
311
+ }
312
+ const relevantCodemods = utils_1.TRANSFORMER_INQUIRER_CHOICES.slice(initialVersionIndex, targetVersionIndex);
313
+ if (relevantCodemods.length === 0) {
314
+ return [];
315
+ }
316
+ const { codemods } = await (0, prompts_1.default)({
317
+ type: 'multiselect',
318
+ name: 'codemods',
319
+ message: `The following ${picocolors_1.default.blue('codemods')} are recommended for your upgrade. Select the ones to apply.`,
320
+ choices: relevantCodemods.reverse().map(({ title, value, version }) => {
321
+ return {
322
+ title: `(v${version}) ${value}`,
323
+ description: title,
324
+ value,
325
+ selected: true,
326
+ };
327
+ }),
328
+ }, { onCancel: utils_1.onCancel });
329
+ return codemods;
330
+ }
331
+ async function suggestReactCodemods() {
332
+ const { runReactCodemod } = await (0, prompts_1.default)({
333
+ type: 'toggle',
334
+ name: 'runReactCodemod',
335
+ message: 'Would you like to run the React 19 upgrade codemod?',
336
+ initial: true,
337
+ active: 'Yes',
338
+ inactive: 'No',
339
+ }, { onCancel: utils_1.onCancel });
340
+ return runReactCodemod;
341
+ }
342
+ async function suggestReactTypesCodemods() {
343
+ const { runReactTypesCodemod } = await (0, prompts_1.default)({
344
+ type: 'toggle',
345
+ name: 'runReactTypesCodemod',
346
+ message: 'Would you like to run the React 19 Types upgrade codemod?',
347
+ initial: true,
348
+ active: 'Yes',
349
+ inactive: 'No',
350
+ }, { onCancel: utils_1.onCancel });
351
+ return runReactTypesCodemod;
352
+ }
353
+ function writeOverridesField(packageJson, packageManager, overrides) {
354
+ if (packageManager === 'bun' || packageManager === 'npm') {
355
+ if (!packageJson.overrides) {
356
+ packageJson.overrides = {};
357
+ }
358
+ for (const [key, value] of Object.entries(overrides)) {
359
+ packageJson.overrides[key] = value;
360
+ }
361
+ }
362
+ else if (packageManager === 'pnpm') {
363
+ // pnpm supports pnpm.overrides and pnpm.resolutions
364
+ if (packageJson.resolutions) {
365
+ for (const [key, value] of Object.entries(overrides)) {
366
+ packageJson.resolutions[key] = value;
367
+ }
368
+ }
369
+ else {
370
+ if (!packageJson.pnpm) {
371
+ packageJson.pnpm = {};
372
+ }
373
+ if (!packageJson.pnpm.overrides) {
374
+ packageJson.pnpm.overrides = {};
375
+ }
376
+ for (const [key, value] of Object.entries(overrides)) {
377
+ packageJson.pnpm.overrides[key] = value;
378
+ }
379
+ }
380
+ }
381
+ else if (packageManager === 'yarn') {
382
+ if (!packageJson.resolutions) {
383
+ packageJson.resolutions = {};
384
+ }
385
+ for (const [key, value] of Object.entries(overrides)) {
386
+ packageJson.resolutions[key] = value;
387
+ }
388
+ }
389
+ }
390
+ //# sourceMappingURL=upgrade.js.map
@@ -4,14 +4,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.globalCssContext = void 0;
7
+ exports.default = transformer;
7
8
  const path_1 = __importDefault(require("path"));
9
+ const parser_1 = require("../parser");
8
10
  exports.globalCssContext = {
9
11
  cssImports: new Set(),
10
12
  reactSvgImports: new Set(),
11
13
  };
12
14
  const globalStylesRegex = /(?<!\.module)\.(css|scss|sass)$/i;
13
- function transformer(file, api, options) {
14
- const j = api.jscodeshift.withParser('tsx');
15
+ function transformer(file, _api, options) {
16
+ const j = (0, parser_1.createParserFromPath)(file.path);
15
17
  const root = j(file.source);
16
18
  let hasModifications = false;
17
19
  root
@@ -37,8 +39,7 @@ function transformer(file, api, options) {
37
39
  }
38
40
  else if (value.endsWith('.svg')) {
39
41
  const isComponentImport = path.node.specifiers.some((specifier) => {
40
- var _a;
41
- return ((_a = specifier.imported) === null || _a === void 0 ? void 0 : _a.name) === 'ReactComponent';
42
+ return specifier.imported?.name === 'ReactComponent';
42
43
  });
43
44
  if (isComponentImport) {
44
45
  exports.globalCssContext.reactSvgImports.add(file.path);
@@ -52,5 +53,4 @@ function transformer(file, api, options) {
52
53
  ? root.toSource(options)
53
54
  : null;
54
55
  }
55
- exports.default = transformer;
56
56
  //# sourceMappingURL=global-css-transform.js.map
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.indexContext = void 0;
4
+ exports.default = transformer;
5
+ const parser_1 = require("../parser");
4
6
  exports.indexContext = {
5
7
  multipleRenderRoots: false,
6
8
  nestedRender: false,
7
9
  };
8
- function transformer(file, api, options) {
9
- const j = api.jscodeshift.withParser('tsx');
10
+ function transformer(file, _api, options) {
11
+ const j = (0, parser_1.createParserFromPath)(file.path);
10
12
  const root = j(file.source);
11
13
  let hasModifications = false;
12
14
  let foundReactRender = 0;
@@ -28,7 +30,6 @@ function transformer(file, api, options) {
28
30
  root
29
31
  .find(j.CallExpression)
30
32
  .filter((path) => {
31
- var _a, _b;
32
33
  const { node } = path;
33
34
  let found = false;
34
35
  if (defaultReactDomImport &&
@@ -43,7 +44,7 @@ function transformer(file, api, options) {
43
44
  if (found) {
44
45
  foundReactRender++;
45
46
  hasModifications = true;
46
- if (!Array.isArray((_b = (_a = path.parentPath) === null || _a === void 0 ? void 0 : _a.parentPath) === null || _b === void 0 ? void 0 : _b.value)) {
47
+ if (!Array.isArray(path.parentPath?.parentPath?.value)) {
47
48
  exports.indexContext.nestedRender = true;
48
49
  return false;
49
50
  }
@@ -73,5 +74,4 @@ function transformer(file, api, options) {
73
74
  // }).remove()
74
75
  return hasModifications ? root.toSource(options) : null;
75
76
  }
76
- exports.default = transformer;
77
77
  //# sourceMappingURL=index-to-component.js.map