From cfd816ddb8af2e075d605b3b66d6794795a77e80 Mon Sep 17 00:00:00 2001 From: Cody Olsen <81981+stipsan@users.noreply.github.com> Date: Tue, 22 Oct 2024 22:14:43 +0200 Subject: [PATCH] Update Sanity example to next v15 (#71640) --- examples/cms-sanity/app/(blog)/actions.ts | 2 +- examples/cms-sanity/app/(blog)/layout.tsx | 82 +++++++++---------- .../app/(blog)/posts/[slug]/page.tsx | 8 +- .../app/api/draft-mode/enable/route.ts | 8 ++ examples/cms-sanity/app/api/draft/route.tsx | 27 ------ examples/cms-sanity/next.config.js | 10 --- examples/cms-sanity/next.config.ts | 10 +++ examples/cms-sanity/package.json | 40 ++++----- examples/cms-sanity/sanity.config.ts | 2 +- examples/cms-sanity/sanity/lib/fetch.ts | 19 +++-- examples/cms-sanity/sanity/lib/token.ts | 8 -- 11 files changed, 96 insertions(+), 120 deletions(-) create mode 100644 examples/cms-sanity/app/api/draft-mode/enable/route.ts delete mode 100644 examples/cms-sanity/app/api/draft/route.tsx delete mode 100644 examples/cms-sanity/next.config.js create mode 100644 examples/cms-sanity/next.config.ts diff --git a/examples/cms-sanity/app/(blog)/actions.ts b/examples/cms-sanity/app/(blog)/actions.ts index 7b5cc9ee80a92..b8f951f237292 100644 --- a/examples/cms-sanity/app/(blog)/actions.ts +++ b/examples/cms-sanity/app/(blog)/actions.ts @@ -5,7 +5,7 @@ import { draftMode } from "next/headers"; export async function disableDraftMode() { "use server"; await Promise.allSettled([ - draftMode().disable(), + (await draftMode()).disable(), // Simulate a delay to show the loading state new Promise((resolve) => setTimeout(resolve, 1000)), ]); diff --git a/examples/cms-sanity/app/(blog)/layout.tsx b/examples/cms-sanity/app/(blog)/layout.tsx index 6a23f51370b07..b6954df828195 100644 --- a/examples/cms-sanity/app/(blog)/layout.tsx +++ b/examples/cms-sanity/app/(blog)/layout.tsx @@ -9,7 +9,6 @@ import { } from "next-sanity"; import { Inter } from "next/font/google"; import { draftMode } from "next/headers"; -import { Suspense } from "react"; import AlertBanner from "./alert-banner"; import PortableText from "./portable-text"; @@ -56,60 +55,53 @@ const inter = Inter({ display: "swap", }); -async function Footer() { - const data = await sanityFetch({ query: settingsQuery }); - const footer = data?.footer || []; - - return ( - - ); -} - -export default function RootLayout({ +export default async function RootLayout({ children, }: { children: React.ReactNode; }) { + const data = await sanityFetch({ query: settingsQuery }); + const footer = data?.footer || []; + const { isEnabled: isDraftMode } = await draftMode(); + return (
- {draftMode().isEnabled && } + {isDraftMode && }
{children}
- -
- {draftMode().isEnabled && } + {isDraftMode && } diff --git a/examples/cms-sanity/app/(blog)/posts/[slug]/page.tsx b/examples/cms-sanity/app/(blog)/posts/[slug]/page.tsx index c8a99e39e3dc6..125c419f18c56 100644 --- a/examples/cms-sanity/app/(blog)/posts/[slug]/page.tsx +++ b/examples/cms-sanity/app/(blog)/posts/[slug]/page.tsx @@ -17,7 +17,7 @@ import { postQuery, settingsQuery } from "@/sanity/lib/queries"; import { resolveOpenGraphImage } from "@/sanity/lib/utils"; type Props = { - params: { slug: string }; + params: Promise<{ slug: string }>; }; const postSlugs = defineQuery( @@ -36,7 +36,11 @@ export async function generateMetadata( { params }: Props, parent: ResolvingMetadata, ): Promise { - const post = await sanityFetch({ query: postQuery, params, stega: false }); + const post = await sanityFetch({ + query: postQuery, + params, + stega: false, + }); const previousImages = (await parent).openGraph?.images || []; const ogImage = resolveOpenGraphImage(post?.coverImage); diff --git a/examples/cms-sanity/app/api/draft-mode/enable/route.ts b/examples/cms-sanity/app/api/draft-mode/enable/route.ts new file mode 100644 index 0000000000000..91986b76f3fb0 --- /dev/null +++ b/examples/cms-sanity/app/api/draft-mode/enable/route.ts @@ -0,0 +1,8 @@ +import { defineEnableDraftMode } from "next-sanity/draft-mode"; + +import { client } from "@/sanity/lib/client"; +import { token } from "@/sanity/lib/token"; + +export const { GET } = defineEnableDraftMode({ + client: client.withConfig({ token }), +}); diff --git a/examples/cms-sanity/app/api/draft/route.tsx b/examples/cms-sanity/app/api/draft/route.tsx deleted file mode 100644 index 38bf40eab07bc..0000000000000 --- a/examples/cms-sanity/app/api/draft/route.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/** - * This file is used to allow Presentation to set the app in Draft Mode, which will load Visual Editing - * and query draft content and preview the content as it will appear once everything is published - */ - -import { validatePreviewUrl } from "@sanity/preview-url-secret"; -import { draftMode } from "next/headers"; -import { redirect } from "next/navigation"; - -import { client } from "@/sanity/lib/client"; -import { token } from "@/sanity/lib/token"; - -const clientWithToken = client.withConfig({ token }); - -export async function GET(request: Request) { - const { isValid, redirectTo = "/" } = await validatePreviewUrl( - clientWithToken, - request.url, - ); - if (!isValid) { - return new Response("Invalid secret", { status: 401 }); - } - - draftMode().enable(); - - redirect(redirectTo); -} diff --git a/examples/cms-sanity/next.config.js b/examples/cms-sanity/next.config.js deleted file mode 100644 index 0a9e8f602209c..0000000000000 --- a/examples/cms-sanity/next.config.js +++ /dev/null @@ -1,10 +0,0 @@ -/** @type {import('next').NextConfig} */ -module.exports = { - experimental: { - // Used to guard against accidentally leaking SANITY_API_READ_TOKEN to the browser - taint: true, - }, - logging: { - fetches: { fullUrl: false }, - }, -}; diff --git a/examples/cms-sanity/next.config.ts b/examples/cms-sanity/next.config.ts new file mode 100644 index 0000000000000..431420e579f95 --- /dev/null +++ b/examples/cms-sanity/next.config.ts @@ -0,0 +1,10 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + env: { + // Matches the behavior of `sanity dev` which sets styled-components to use the fastest way of inserting CSS rules in both dev and production. It's default behavior is to disable it in dev mode. + SC_DISABLE_SPEEDY: "false", + }, +}; + +export default nextConfig; diff --git a/examples/cms-sanity/package.json b/examples/cms-sanity/package.json index 6c0be210bc48a..a0a9becc69ff4 100644 --- a/examples/cms-sanity/package.json +++ b/examples/cms-sanity/package.json @@ -2,7 +2,7 @@ "private": true, "scripts": { "predev": "npm run typegen", - "dev": "next", + "dev": "next --turbo", "prebuild": "npm run typegen", "build": "next build", "start": "next start", @@ -13,32 +13,32 @@ "typegen": "sanity schema extract && sanity typegen generate" }, "dependencies": { - "@sanity/assist": "^3.0.6", - "@sanity/icons": "^3.3.1", + "@sanity/assist": "^3.0.8", + "@sanity/icons": "^3.4.0", "@sanity/image-url": "^1.0.2", - "@sanity/preview-url-secret": "^1.6.20", - "@sanity/vision": "^3.55.0", - "@tailwindcss/typography": "^0.5.14", - "@types/node": "^20.14.13", - "@types/react": "^18.3.4", - "@types/react-dom": "^18.3.0", - "@vercel/speed-insights": "^1.0.12", + "@sanity/preview-url-secret": "^2.0.0", + "@sanity/vision": "^3.62.0", + "@tailwindcss/typography": "^0.5.15", + "@types/node": "^22.7.8", + "@types/react": "^18.3.11", + "@types/react-dom": "^18.3.1", + "@vercel/speed-insights": "^1.0.13", "autoprefixer": "^10.4.20", - "date-fns": "^3.6.0", - "next": "^14.2.5", - "next-sanity": "^9.4.7", - "postcss": "^8.4.41", + "date-fns": "^4.1.0", + "next": "^15.0.0", + "next-sanity": "^9.7.0", + "postcss": "^8.4.47", "react": "^18.3.1", "react-dom": "^18.3.1", - "sanity": "^3.55.0", + "sanity": "^3.62.0", "sanity-plugin-asset-source-unsplash": "^3.0.1", "server-only": "^0.0.1", - "styled-components": "^6.1.12", - "tailwindcss": "^3.4.10", - "typescript": "5.5.4" + "styled-components": "^6.1.13", + "tailwindcss": "^3.4.14", + "typescript": "5.6.3" }, "devDependencies": { - "eslint": "^8.57.0", - "eslint-config-next": "^14.2.5" + "eslint": "^9.13.0", + "eslint-config-next": "^15.0.0" } } diff --git a/examples/cms-sanity/sanity.config.ts b/examples/cms-sanity/sanity.config.ts index 4184819fe369a..1372daaacf802 100644 --- a/examples/cms-sanity/sanity.config.ts +++ b/examples/cms-sanity/sanity.config.ts @@ -71,7 +71,7 @@ export default defineConfig({ }), }, }, - previewUrl: { previewMode: { enable: "/api/draft" } }, + previewUrl: { previewMode: { enable: "/api/draft-mode/enable" } }, }), structureTool({ structure: pageStructure([settings]) }), // Configures the global "new document" button, and document actions, to suit the Settings document singleton diff --git a/examples/cms-sanity/sanity/lib/fetch.ts b/examples/cms-sanity/sanity/lib/fetch.ts index f4d79180f5ee2..3c11d0d4c5ae7 100644 --- a/examples/cms-sanity/sanity/lib/fetch.ts +++ b/examples/cms-sanity/sanity/lib/fetch.ts @@ -13,22 +13,29 @@ import { token } from "@/sanity/lib/token"; export async function sanityFetch({ query, params = {}, - perspective = draftMode().isEnabled ? "previewDrafts" : "published", + perspective: _perspective, /** * Stega embedded Content Source Maps are used by Visual Editing by both the Sanity Presentation Tool and Vercel Visual Editing. * The Sanity Presentation Tool will enable Draft Mode when loading up the live preview, and we use it as a signal for when to embed source maps. * When outside of the Sanity Studio we also support the Vercel Toolbar Visual Editing feature, which is only enabled in production when it's a Vercel Preview Deployment. */ - stega = perspective === "previewDrafts" || - process.env.VERCEL_ENV === "preview", + stega: _stega, }: { query: QueryString; - params?: QueryParams; + params?: QueryParams | Promise; perspective?: Omit; stega?: boolean; }) { + const perspective = + _perspective || (await draftMode()).isEnabled + ? "previewDrafts" + : "published"; + const stega = + _stega || + perspective === "previewDrafts" || + process.env.VERCEL_ENV === "preview"; if (perspective === "previewDrafts") { - return client.fetch(query, params, { + return client.fetch(query, await params, { stega, perspective: "previewDrafts", // The token is required to fetch draft content @@ -39,7 +46,7 @@ export async function sanityFetch({ next: { revalidate: 0 }, }); } - return client.fetch(query, params, { + return client.fetch(query, await params, { stega, perspective: "published", // The `published` perspective is available on the API CDN diff --git a/examples/cms-sanity/sanity/lib/token.ts b/examples/cms-sanity/sanity/lib/token.ts index dd8757abdb724..0908fa68462de 100644 --- a/examples/cms-sanity/sanity/lib/token.ts +++ b/examples/cms-sanity/sanity/lib/token.ts @@ -1,15 +1,7 @@ import "server-only"; -import { experimental_taintUniqueValue } from "react"; - export const token = process.env.SANITY_API_READ_TOKEN; if (!token) { throw new Error("Missing SANITY_API_READ_TOKEN"); } - -experimental_taintUniqueValue( - "Do not pass the sanity API read token to the client.", - process, - token, -);