From 92db76647ccfd62a47d2b43045def07ba280dd9e Mon Sep 17 00:00:00 2001 From: Dave Li Date: Fri, 28 Jun 2024 14:16:00 +0200 Subject: [PATCH] Upgrade to Astro 4 and Sanity 3.46.0. Also included support for Visual Editing. --- .env.example | 3 + README.md | 22 ++- astro.config.mjs | 25 +-- package-lock.json | 8 +- package.json | 1 + sanity.config.ts | 16 +- src/layouts/Layout.astro | 324 ++++++++++++++++++--------------------- src/utils/load-query.ts | 41 +++++ src/utils/sanity.ts | 23 +-- tsconfig.json | 2 +- 10 files changed, 263 insertions(+), 202 deletions(-) create mode 100644 src/utils/load-query.ts diff --git a/.env.example b/.env.example index 30b5291..a5ed598 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,6 @@ # Remember to add PUBLIC_ if generated by npx sanity init --env PUBLIC_SANITY_STUDIO_PROJECT_ID="r0z1eifg" PUBLIC_SANITY_STUDIO_DATASET="clean-dev" +PUBLIC_SANITY_STUDIO_PREVIEW_URL="http://localhost:4321" +SANITY_VISUAL_EDITING_ENABLED="true" +SANITY_API_READ_TOKEN="" diff --git a/README.md b/README.md index cdb704b..87a482a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This starter uses [Astro](https://astro.build/) for the front end and [Sanity](h ## Prerequisites -- [Node.js](https://nodejs.org/en/) (v16.12 or later) +- [Node.js](https://nodejs.org/en/) (v20.14 or later) ## Getting started @@ -35,6 +35,24 @@ Your Astro app should now be running on [http://localhost:4321/](http://localhos The schema for the `Post` document is defined in the `/schema` folder. You can [add more document types](https://www.sanity.io/docs/schema-types) to the Studio to suit your needs. +### Enabling Visual Editing + +Add the following variables to the .env file. + +- SANITY_VISUAL_EDITING_ENABLED="true" +- SANITY_API_READ_TOKEN="" + +You'll notice that we rely on a "read token" which is required in order to enable stega encoding and for authentication when Sanity Studio is live previewing your application. + +1. Go to https://sanity.io/manage and select your project. +2. Click on the 🔌 API tab. +3. Click on + Add API token. +4. Name it "SANITY_API_READ_TOKEN" and set Permissions to Viewer and hit Save. +5. Copy the token and add it to your `.env` file: `SANITY_API_READ_TOKEN=""` + +You can read more about Visual Editing (here)[https://www.sanity.io/docs/introduction-to-visual-editing]. +You can read more about the Astro integration (here)[https://github.com/sanity-io/sanity-astro?tab=readme-ov-file#enabling-visual-editing] + ## Removing TypeScript If you do not wish to use TypeScript, we've included a `remove-typescript.mjs` file in the root of this repository. You can run this file with `node remove-typescript.mjs` to strip all types from this project. Please run this before tampering with any code to ensure that all types are properly removed. @@ -43,7 +61,7 @@ If you intend to use TypeScript, you can safely remove the `remove-typescript.mj ## Removing the embedded Studio -If you wish to manage and host the Studio separately, you remove the `studioBasePath` property for the `sanity` configuration in `astro.config.mjs`. You can also remove the following dependencies: +If you wish to manage and host the Studio separately, you remove the `studioBasePath` and `stega` property for the `sanity` configuration in `astro.config.mjs`. You can also remove the following dependencies: - `output` in `astro.config.mjs`… - …and `adapter` in `astro.config.mjs` diff --git a/astro.config.mjs b/astro.config.mjs index 4e38fb2..c7d1e90 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -25,13 +25,18 @@ export default defineConfig({ // Hybrid+adapter is required to support embedded Sanity Studio output: "hybrid", adapter: vercel(), - integrations: [sanity({ - projectId, - dataset, - studioBasePath: "/admin", - useCdn: false, - // `false` if you want to ensure fresh data - apiVersion: "2023-03-20" // Set to date of setup to use the latest API version - }), react() // Required for Sanity Studio - ] -}); \ No newline at end of file + integrations: [ + sanity({ + projectId, + dataset, + studioBasePath: "/admin", + stega: { + studioUrl: "/admin", + }, + useCdn: false, + // `false` if you want to ensure fresh data + apiVersion: "2023-03-20", // Set to date of setup to use the latest API version + }), + react(), // Required for Sanity Studio + ], +}); diff --git a/package-lock.json b/package-lock.json index d22b45b..287f59b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@sanity/astro": "^3.1.4", "@sanity/image-url": "1.0.2", "@sanity/vision": "3.48.1", + "@sanity/visual-editing": "^2.1.5", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", "astro": "^4.11.3", @@ -5236,7 +5237,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@sanity/visual-editing/-/visual-editing-2.1.5.tgz", "integrity": "sha512-CWwqI60Fvcsg+KbvtwKrgfumAtQ+nJUJ9NSl7SlGSkeQLlpIKnLOsTpLx0JuNxkR57/j79jU2eOh56FGVDiACg==", - "peer": true, "dependencies": { "@sanity/preview-url-secret": "^1.6.17", "@vercel/stega": "0.1.2", @@ -5831,8 +5831,7 @@ "node_modules/@vercel/stega": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@vercel/stega/-/stega-0.1.2.tgz", - "integrity": "sha512-P7mafQXjkrsoyTRppnt0N21udKS9wUmLXHRyP9saLXLHw32j/FgUJ3FscSWgvSqRs4cj7wKZtwqJEvWJ2jbGmA==", - "peer": true + "integrity": "sha512-P7mafQXjkrsoyTRppnt0N21udKS9wUmLXHRyP9saLXLHw32j/FgUJ3FscSWgvSqRs4cj7wKZtwqJEvWJ2jbGmA==" }, "node_modules/@vitejs/plugin-react": { "version": "4.3.1", @@ -16640,8 +16639,7 @@ "node_modules/valibot": { "version": "0.31.1", "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.31.1.tgz", - "integrity": "sha512-2YYIhPrnVSz/gfT2/iXVTrSj92HwchCt9Cga/6hX4B26iCz9zkIsGTS0HjDYTZfTi1Un0X6aRvhBi1cfqs/i0Q==", - "peer": true + "integrity": "sha512-2YYIhPrnVSz/gfT2/iXVTrSj92HwchCt9Cga/6hX4B26iCz9zkIsGTS0HjDYTZfTi1Un0X6aRvhBi1cfqs/i0Q==" }, "node_modules/validate-npm-package-license": { "version": "3.0.4", diff --git a/package.json b/package.json index 87fcf0c..85eba4a 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@sanity/astro": "^3.1.4", "@sanity/image-url": "1.0.2", "@sanity/vision": "3.48.1", + "@sanity/visual-editing": "^2.1.5", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", "astro": "^4.11.3", diff --git a/sanity.config.ts b/sanity.config.ts index 918970a..d4b3ec3 100644 --- a/sanity.config.ts +++ b/sanity.config.ts @@ -6,10 +6,13 @@ const dataset = import.meta.env.PUBLIC_SANITY_STUDIO_DATASET! || import.meta.env.PUBLIC_SANITY_DATASET!; +const previewUrl = + import.meta.env.PUBLIC_SANITY_STUDIO_PREVIEW_URL || "http://localhost:4321"; + // Feel free to remove this check if you don't need it -if (!projectId || !dataset) { +if (!projectId || !dataset || !previewUrl) { throw new Error( - `Missing environment variable(s). Check if named correctly in .env file.\n\nShould be:\nPUBLIC_SANITY_STUDIO_PROJECT_ID=${projectId}\nPUBLIC_SANITY_STUDIO_DATASET=${dataset}\n\nAvailable environment variables:\n${JSON.stringify( + `Missing environment variable(s). Check if named correctly in .env file.\n\nShould be:\nPUBLIC_SANITY_STUDIO_PROJECT_ID=${projectId}\nPUBLIC_SANITY_STUDIO_DATASET=${dataset}\nPUBLIC_SANITY_STUDIO_PREVIEW_URL=${previewUrl}\n\nAvailable environment variables:\n${JSON.stringify( import.meta.env, null, 2 @@ -19,6 +22,7 @@ if (!projectId || !dataset) { import { defineConfig } from "sanity"; import { structureTool } from "sanity/structure"; +import { presentationTool } from "sanity/presentation"; import { visionTool } from "@sanity/vision"; import { schemaTypes } from "./schema"; @@ -27,7 +31,13 @@ export default defineConfig({ title: "Project Name", projectId, dataset, - plugins: [structureTool(), visionTool()], + plugins: [ + structureTool(), + presentationTool({ + previewUrl, + }), + visionTool(), + ], schema: { types: schemaTypes, }, diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 4f83f6b..c42a589 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -1,42 +1,28 @@ --- // https://docs.astro.build/en/guides/view-transitions/ -import { ViewTransitions } from 'astro:transitions'; +import { ViewTransitions } from "astro:transitions"; +import { VisualEditing } from "@sanity/astro/visual-editing"; interface Props { title: string; } const { title } = Astro.props; +const visualEditingEnabled = + import.meta.env.SANITY_VISUAL_EDITING_ENABLED == "true"; --- - + - - - + + + - - + +
- Astro + Sanity + Astro + Sanity
+
- - + + + - - + + + diff --git a/src/utils/load-query.ts b/src/utils/load-query.ts new file mode 100644 index 0000000..68c7294 --- /dev/null +++ b/src/utils/load-query.ts @@ -0,0 +1,41 @@ +import { type QueryParams } from "sanity"; +import { sanityClient } from "sanity:client"; + +const visualEditingEnabled = + import.meta.env.SANITY_VISUAL_EDITING_ENABLED === "true"; +const token = import.meta.env.SANITY_API_READ_TOKEN; + +export async function loadQuery({ + query, + params, +}: { + query: string; + params?: QueryParams; +}) { + if (visualEditingEnabled && !token) { + throw new Error( + "The `SANITY_API_READ_TOKEN` environment variable is required during Visual Editing." + ); + } + + const perspective = visualEditingEnabled ? "previewDrafts" : "published"; + + const { result, resultSourceMap } = await sanityClient.fetch( + query, + params ?? {}, + { + filterResponse: false, + perspective, + resultSourceMap: visualEditingEnabled ? "withKeyArraySelector" : false, + stega: visualEditingEnabled, + ...(visualEditingEnabled ? { token } : {}), + useCdn: !visualEditingEnabled, + } + ); + + return { + data: result, + sourceMap: resultSourceMap, + perspective, + }; +} diff --git a/src/utils/sanity.ts b/src/utils/sanity.ts index 719903e..a79b5ca 100644 --- a/src/utils/sanity.ts +++ b/src/utils/sanity.ts @@ -1,21 +1,24 @@ -import { sanityClient } from "sanity:client"; import type { PortableTextBlock } from "@portabletext/types"; import type { ImageAsset, Slug } from "@sanity/types"; -import groq from "groq"; +import { loadQuery } from "./load-query"; export async function getPosts(): Promise { - return await sanityClient.fetch( - groq`*[_type == "post" && defined(slug.current)] | order(_createdAt desc)` - ); + const { data: posts } = await loadQuery>({ + query: `*[_type == "post" && defined(slug.current)] | order(_createdAt desc)`, + }); + + return posts; } export async function getPost(slug: string): Promise { - return await sanityClient.fetch( - groq`*[_type == "post" && slug.current == $slug][0]`, - { + const { data: post } = await loadQuery({ + query: `*[_type == "post" && slug.current == $slug][0]`, + params: { slug, - } - ); + }, + }); + + return post; } export interface Post { diff --git a/tsconfig.json b/tsconfig.json index 3763eba..61fadf7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ "noEmit": false, "esModuleInterop": true, "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve"