From 3e878913a6261aa56e1257214cbe9bbdf7140782 Mon Sep 17 00:00:00 2001 From: Pedro Bonamin <46196328+pedrobonamin@users.noreply.github.com> Date: Thu, 24 Oct 2024 08:06:56 +0200 Subject: [PATCH] feat(cli): add warning and docs for react-19 and Next.Js combined (#7660) Co-authored-by: Ash Co-authored-by: Espen Hovlandsdal --- .../src/actions/init-project/initProject.ts | 32 +++++++++++++++++-- .../actions/init-project/readPackageJson.ts | 18 +++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 packages/@sanity/cli/src/actions/init-project/readPackageJson.ts diff --git a/packages/@sanity/cli/src/actions/init-project/initProject.ts b/packages/@sanity/cli/src/actions/init-project/initProject.ts index dac97466649..691a327039d 100644 --- a/packages/@sanity/cli/src/actions/init-project/initProject.ts +++ b/packages/@sanity/cli/src/actions/init-project/initProject.ts @@ -4,11 +4,13 @@ import path from 'node:path' import {type DatasetAclMode, type SanityProject} from '@sanity/client' import {type Framework} from '@vercel/frameworks' +import {type detectFrameworkRecord} from '@vercel/fs-detectors' import dotenv from 'dotenv' import execa, {type CommonOptions} from 'execa' import {deburr, noop} from 'lodash' import pFilter from 'p-filter' import resolveFrom from 'resolve-from' +import semver from 'semver' import {evaluate, patch} from 'silver-fleece' import which from 'which' @@ -55,6 +57,7 @@ import { promptForNextTemplate, promptForStudioPath, } from './prompts/nextjs' +import {readPackageJson} from './readPackageJson' import {reconfigureV2Project} from './reconfigureV2Project' import templates from './templates' import { @@ -110,7 +113,9 @@ export interface ProjectOrganization { // eslint-disable-next-line max-statements, complexity export default async function initSanity( args: CliCommandArguments, - context: CliCommandContext & {detectedFramework: Framework | null}, + context: CliCommandContext & { + detectedFramework: Awaited> + }, ): Promise { const { output, @@ -128,6 +133,8 @@ export default async function initSanity( const cliFlags = args.extOptions const unattended = cliFlags.y || cliFlags.yes const print = unattended ? noop : output.print + const warn = (msg: string) => output.warn(chalk.yellow.bgBlack(msg)) + const intendedPlan = cliFlags['project-plan'] const intendedCoupon = cliFlags.coupon const reconfigure = cliFlags.reconfigure @@ -298,7 +305,8 @@ export default async function initSanity( } let initNext = false - if (detectedFramework?.slug === 'nextjs') { + const isNextJs = detectedFramework?.slug === 'nextjs' + if (isNextJs) { initNext = await prompt.single({ type: 'confirm', message: @@ -327,6 +335,26 @@ export default async function initSanity( // Ensure we are using the output path provided by user outputPath = answers.outputPath + if (isNextJs) { + const packageJson = readPackageJson(`${outputPath}/package.json`) + const reactVersion = packageJson?.dependencies?.react + + if (reactVersion) { + const isUsingReact19 = semver.coerce(reactVersion)?.major === 19 + const isUsingNextJs15 = semver.coerce(detectedFramework?.detectedVersion)?.major === 15 + + if (isUsingNextJs15 && isUsingReact19) { + warn('╭────────────────────────────────────────────────────────────╮') + warn('│ │') + warn('│ It looks like you are using Next.js 15 and React 19 │') + warn('│ Please read our compatibility guide. │') + warn('│ https://www.sanity.io/help/react-19 │') + warn('│ │') + warn('╰────────────────────────────────────────────────────────────╯') + } + } + } + if (initNext) { const useTypeScript = unattended ? true : await promptForTypeScript(prompt) trace.log({step: 'useTypeScript', selectedOption: useTypeScript ? 'yes' : 'no'}) diff --git a/packages/@sanity/cli/src/actions/init-project/readPackageJson.ts b/packages/@sanity/cli/src/actions/init-project/readPackageJson.ts new file mode 100644 index 00000000000..ed638458a67 --- /dev/null +++ b/packages/@sanity/cli/src/actions/init-project/readPackageJson.ts @@ -0,0 +1,18 @@ +import fs from 'node:fs' + +import {type PackageJson} from '../../types' + +/** + * Read the `package.json` file at the given path + * + * @param filePath - Path to package.json to read + * @returns The parsed package.json + */ +export function readPackageJson(filePath: string): PackageJson | undefined { + try { + // eslint-disable-next-line no-sync + return JSON.parse(fs.readFileSync(filePath, 'utf8')) + } catch (err) { + return undefined + } +}