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,
-);