diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0244819de4..1814ee1cd2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,9 @@ jobs: - run: npm ci - run: npm run lint if: github.event.inputs.test != 'false' + # @TODO find out how to make this command work on the CI + #- run: npm run update:icons + # if: github.event.inputs.test != 'false' - run: npm run prepublishOnly - run: npm run test:node-esm-cjs name: Test if Node.js ESM native modes works, without breaking CJS modes diff --git a/README.md b/README.md index 8cfeb30cc4..3a3862540d 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,17 @@ - [Limits](#limits) - [`next-sanity/studio` (dev-preview)](#next-sanitystudio-dev-preview) - [Usage](#usage) + - [Next 13 `app/studio`](#next-13-appstudio) + - [Next 12 or `pages/studio`](#next-12-or-pagesstudio) - [Opt-in to using `StudioProvider` and `StudioLayout`](#opt-in-to-using-studioprovider-and-studiolayout) - - [Customize ``](#customize-serverstylesheetdocument-) - - [Full-control mode](#full-control-mode) - [`next-sanity/webhook`](#next-sanitywebhook) - [Migrate](#migrate) + - [From `v2`](#from-v2) + - [`NextStudioGlobalStyle` is removed](#nextstudioglobalstyle-is-removed) + - [`ServerStyleSheetDocument` is removed](#serverstylesheetdocument-is-removed) + - [The internal `isWorkspaceWithTheme` and `isWorkspaces` utils are no longer exported](#the-internal-isworkspacewiththeme-and-isworkspaces-utils-are-no-longer-exported) + - [The `useBackgroundColorsFromTheme`, `useBasePath`, `useConfigWithBasePath`, and `useTextFontFamilyFromTheme`, hooks are removed](#the-usebackgroundcolorsfromtheme-usebasepath-useconfigwithbasepath-and-usetextfontfamilyfromtheme-hooks-are-removed) + - [The `NextStudioHead` component has moved from `next-sanity/studio` to `next-sanity/studio/head`](#the-nextstudiohead-component-has-moved-from-next-sanitystudio-to-next-sanitystudiohead) - [From `v1`](#from-v1) - [`createPreviewSubscriptionHook` is replaced with `definePreview`](#createpreviewsubscriptionhook-is-replaced-with-definepreview) - [Before](#before) @@ -450,149 +456,142 @@ The latest version of Sanity Studio allows you to embed a near-infinitely config ### Usage -The basic setup is two files: +The basic setup is 2 components, `NextStudio` and `NextStudioHead`. +`NextStudio` loads up the `import {Studio} from 'sanity'` component for you and wraps it in a Next-friendly layout. +While `NextStudioHead` sets necessary `` meta tags such as `` to ensure the responsive CSS in the Studio works as expected. -1. `pages/[[...index]].tsx` +Both the Next 13 and 12 examples uses this config file: +`sanity.config.ts`: -```tsx -// Import your sanity.config.ts file -import config from '../sanity.config' -import {NextStudio} from 'next-sanity/studio' +```ts +import {visionTool} from '@sanity/vision' +import {defineConfig} from 'sanity' +import {deskTool} from 'sanity/desk' -export default function StudioPage() { - // Loads the Studio, with all the needed meta tags and global CSS required for it to render correctly - return -} -``` +import {schemaTypes} from './schemas' -The `` wraps `` component and supports forwarding all its props: +const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID! +const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET! -```tsx -import {Studio} from 'sanity' +export default defineConfig({ + basePath: '/studio', // <-- important that `basePath` matches the route you're mounting your studio from, it applies to both `/pages` and `/app` + + projectId, + dataset, + + plugins: [deskTool(), visionTool()], + + schema: { + types: schemaTypes, + }, +}) ``` -2. `pages/_document.tsx` +To use `sanity.cli.ts` with the same `projectId` and `dataset` as your `sanity.config.ts`: -```tsx -import {ServerStyleSheetDocument} from 'next-sanity/studio' +```ts +/* eslint-disable no-process-env */ +import {loadEnvConfig} from '@next/env' +import {defineCliConfig} from 'sanity/cli' + +const dev = process.env.NODE_ENV !== 'production' +loadEnvConfig(__dirname, dev, {info: () => null, error: console.error}) -// Set up SSR for styled-components, ensuring there's no missing CSS when deploying a Studio in Next.js into production -export default class Document extends ServerStyleSheetDocument {} +const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID +const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET + +export default defineCliConfig({api: {projectId, dataset}}) ``` -### Opt-in to using `StudioProvider` and `StudioLayout` +Now you can run commands like `npx sanity cors add`. See `npx sanity help` for a full list of what you can do. -If you want to go lower level and have more control over the studio you can pass `StudioProvider` and `StudioLayout` from `sanity` as `children`: +#### Next 13 `app/studio` + +In Next 13's `appDir` mode you use `page.tsx` to load `NextStudio`, and optionally (recommended, especially if you want great support for iPhones and other devices with display cutouts like "The Notch" or "Dynamic Island") export `NextStudioHead` in a `head.tsx`. +In routes that load `NextStudio` ensure you have `'use client'` at the top of your file. + +`app/studio/[[...index]]/page.tsx`: ```tsx +'use client' + import {NextStudio} from 'next-sanity/studio' -import {StudioProvider, StudioLayout} from 'sanity' -import config from '../sanity.config' +import config from '../../../sanity.config' -function StudioPage() { - return ( - - - {/* Put components here and you'll have access to the same React hooks as Studio gives you when writing plugins */} - - - - ) +export default function StudioPage() { + // Supports the same props as `import {Studio} from 'sanity'`, `config` is required + return } ``` -### Customize `` - -You can still customize `_document.tsx`, the same way you would the default `` component from `next/document`: +`app/studio/[[...index]]/head.tsx`: ```tsx -import {ServerStyleSheetDocument} from 'next-sanity/studio' - -export default class Document extends ServerStyleSheetDocument { - static async getInitialProps(ctx: DocumentContext) { - // You can still override renderPage: - const originalRenderPage = ctx.renderPage - ctx.renderPage = () => - originalRenderPage({ - enhanceApp: (App) => (props) => , - }) - - const initialProps = await ServerStyleSheetDocument.getInitialProps(ctx) - - const extraStyles = await getStyles() - return { - ...initialProps, - // Add to the default styles if you want - styles: [initialProps.styles, extraStyles], - } - } - render() { - // do the same stuff as in `next/document` - } +// Re-export `NextStudioHead` as default if you're happy with the default behavior +export {NextStudioHead as default} from 'next-sanity/studio/head' + +// To customize it, use it as a children component: +import {NextStudioHead} from 'next-sanity/studio/head' + +export default function CustomStudioHead() { + return ( + <> + + + + ) } ``` -### Full-control mode +#### Next 12 or `pages/studio` + +Using just `NextStudio` gives you a fully working Sanity Studio v3. However we recommend also using `NextStudioHead` as it ensures CSS Media Queries that target mobile devices with display cutouts (for example iPhone's "The Notch" and "Dynamic Island") and other details. -If you only need parts of what `` does for you, but not all of it. -No problem. You can import any which one of the components that `` is importing and assemble them in any way you want. +`/pages/studio/[[...index]].tsx`: ```tsx -import {Studio, type Config} from 'sanity' -import {NextStudioGlobalStyle, NextStudioHead} from 'next-sanity/studio' -// This implementation will only load the bare minimum of what's required for the Studio to render correctly. No favicons, fancy tags or the like -export default function CustomNextStudio({config}: {config: Config}) { +import Head from 'next/head' +import {NextStudio} from 'next-sanity/studio' +import {NextStudioHead} from 'next-sanity/studio/head' + +import config from '../../sanity.config' + +export default function StudioPage() { return ( <> - - {/* Custom extra stuff in */} - + + + + ) } ``` -And while `` have all features enabled by default allowing you to opt-out by giving it props, the inner components `` and `` are opt-in. -This means that these two `StudioPage` components are functionally identical: +### Opt-in to using `StudioProvider` and `StudioLayout` + +If you want to go lower level and have more control over the studio you can pass `StudioProvider` and `StudioLayout` from `sanity` as `children`: ```tsx -import { - NextStudio, - NextStudioGlobalStyle, - NextStudioHead, - useTheme, - useBackgroundColorsFromTheme, -} from 'next-sanity/studio' -import {Studio} from 'sanity' -import config from '../sanity.config' - -// Turning all the features off, leaving only bare minimum required meta tags and styling -function StudioPage() { - return ( - - unstable__bg="" - unstable__noTailwindSvgFix - unstable__noFavicons - // an empty string turns off the tag - unstable__document_title="" - /> - ) -} +import {NextStudio} from 'next-sanity/studio' +import {StudioProvider, StudioLayout} from 'sanity' -// Since no features are enabled it works the same way -function Studiopage() { - const theme = useTheme(config) - const {themeColorLight, themeColorDark} = useBackgroundColorsFromTheme(theme) +import config from '../../../sanity.config' +function StudioPage() { return ( - <> - <Studio config={config} /> - <NextStudioHead themeColorLight={themeColorLight} themeColorDark={themeColorDark} /> - <NextStudioGlobalStyle /> - </> + <NextStudio config={config}> + <StudioProvider config={config}> + {/* Put components here and you'll have access to the same React hooks as Studio gives you when writing plugins */} + <StudioLayout /> + </StudioProvider> + </NextStudio> ) } ``` @@ -635,6 +634,51 @@ export default async function revalidate(req: NextApiRequest, res: NextApiRespon ## Migrate +### From `v2` + +The `v3` release only contains breaking changes on the `next-sanity/studio` imports. If you're only using `import {createClient, groq} from 'next-sanity'` or `import {definePreview, PreviewSuspense} from 'next-sanity/preview'` then there's no migration for you to do. + +#### `NextStudioGlobalStyle` is removed + +The layout is no longer using global CSS to set the Studio height. The switch to local CSS helps interop between Next 12 and 13 layouts. + +#### `ServerStyleSheetDocument` is removed + +It's no longer necessary to setup `styled-components` SSR for the Studio to render correctly. + +#### The internal `isWorkspaceWithTheme` and `isWorkspaces` utils are no longer exported + +The `useTheme` hook is still available if you're building abstractions that need to know what the initial workspace theme variables are. + +#### The `useBackgroundColorsFromTheme`, `useBasePath`, `useConfigWithBasePath`, and `useTextFontFamilyFromTheme`, hooks are removed + +You can `useTheme` to replace `useBackgroundColorsFromTheme` and `useTextFontFamilyFromTheme`: + +```tsx +import {useMemo} from 'react' +import {useTheme} from 'next-sanity/studio' +import type {StudioProps} from 'sanity' +export default function MyComponent(props: Pick<StudioProps, 'config'>) { + const theme = useTheme(config) + // useBackgroundColorsFromTheme + const {themeColorLight, themeColorDark} = useMemo( + () => ({ + themeColorLight: theme.color.light.default.base.bg, + themeColorDark: theme.color.dark.default.base.bg, + }), + [theme] + ) + // useTextFontFamilyFromTheme + const fontFamily = useMemo(() => theme.fonts.text.family, [theme]) +} +``` + +The reason why `useBasePath` and `useConfigWithBasePath` got removed is because Next 12 and 13 diverge too much in how they declare dynamic segments. Thus you'll need to specify `basePath` in your `sanity.config.ts` manually to match the route you're loading the studio, for the time being. + +#### The `NextStudioHead` component has moved from `next-sanity/studio` to `next-sanity/studio/head` + +Its props are also quite different and it now requires you to wrap it in `import Head from 'next/head'` if you're not using a `head.tsx` in `appDir`. Make sure you use TypeScript to ease the migration. + ### From `v1` #### `createPreviewSubscriptionHook` is replaced with `definePreview` diff --git a/app/page.tsx b/app/page.tsx index 391e62e486..23aaa02935 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -4,6 +4,7 @@ import PreviewPosts from 'app/PreviewPosts' import {createClient} from 'app/sanity.client' import {PreviewSuspense} from 'app/sanity.preview' import {previewData} from 'next/headers' +import Link from 'next/link' export default async function IndexPage() { const thePreviewData = previewData() @@ -66,12 +67,12 @@ export default async function IndexPage() { </div> </div> <div className="text-center"> - <a + <Link href="/studio" className="mx-2 my-4 inline-block rounded-full border border-gray-200 px-4 py-1 text-sm font-semibold text-gray-600 hover:border-transparent hover:bg-gray-600 hover:text-white focus:outline-none focus:ring-2 focus:ring-gray-600 focus:ring-offset-2" > Open Studio - </a> + </Link> </div> </> ) diff --git a/app/studio/[[...tool]]/head.tsx b/app/studio/[[...tool]]/head.tsx new file mode 100644 index 0000000000..7d18c55ecf --- /dev/null +++ b/app/studio/[[...tool]]/head.tsx @@ -0,0 +1 @@ +export {NextStudioHead as default} from 'src/studio/head' diff --git a/pages/fixtures/useConfigWithBasePath/[[...index]].tsx b/app/studio/[[...tool]]/page.tsx similarity index 76% rename from pages/fixtures/useConfigWithBasePath/[[...index]].tsx rename to app/studio/[[...tool]]/page.tsx index 4aea1e56ac..6fde3dcb24 100644 --- a/pages/fixtures/useConfigWithBasePath/[[...index]].tsx +++ b/app/studio/[[...tool]]/page.tsx @@ -1,10 +1,12 @@ +'use client' + import config from 'sanity.config' -import {NextStudio, useConfigWithBasePath} from 'src/studio' +import {NextStudio} from 'src/studio' export default function StudioPage() { return ( <NextStudio - config={useConfigWithBasePath(config)} + config={config} // Turn off login in production so that anyone can look around in the Studio and see how it works // eslint-disable-next-line no-process-env unstable_noAuthBoundary={process.env.NEXT_PUBLIC_VERCEL_ENV === 'production'} diff --git a/favicons.d.ts b/favicons.d.ts new file mode 100644 index 0000000000..00e527b3d3 --- /dev/null +++ b/favicons.d.ts @@ -0,0 +1,14 @@ +declare module '*.ico' { + const value: string + export default value +} + +declare module '*.svg' { + const value: string + export default value +} + +declare module '*.png' { + const value: string + export default value +} diff --git a/next-env.d.ts b/next-env.d.ts index 4f11a03dc6..2532e77aba 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,5 +1,4 @@ /// <reference types="next" /> -/// <reference types="next/image-types/global" /> // NOTE: This file should not be edited // see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/next.config.mjs b/next.config.mjs index 8146559346..c9cf98fc8c 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -7,6 +7,17 @@ const nextConfig = { // We run these checks in the CI pipeline, so we don't need to run them on Vercel typescript: {ignoreBuildErrors: true}, eslint: {ignoreDuringBuilds: true}, + + // Handle static image imports the same in `npx next dev` as in `npm run build` + webpack: (config) => { + config.module.rules.push({ + test: /\.(ico|svg|png)$/i, + use: [{loader: 'url-loader', options: {}}], + }) + + return config + }, + images: {disableStaticImages: true}, } export default nextConfig diff --git a/package-lock.json b/package-lock.json index daa05b1b50..874e09de1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "@typescript-eslint/eslint-plugin": "^5.43.0", "autoprefixer": "^10.4.13", "eslint": "^8.28.0", - "eslint-config-next": "13.0.5-canary.3", + "eslint-config-next": "13.0.5-canary.4", "eslint-config-prettier": "^8.5.0", "eslint-config-sanity": "^6.0.0", "eslint-gitignore": "^0.1.0", @@ -40,7 +40,7 @@ "groqd": "^0.0.4", "jest": "^29.3.1", "jest-environment-jsdom": "^29.3.1", - "next": "13.0.5-canary.3", + "next": "13.0.5-canary.4", "postcss": "^8.4.19", "prettier": "^2.7.1", "prettier-plugin-packagejson": "^2.3.0", @@ -52,22 +52,31 @@ "sanity": "3.0.0-rc.2", "styled-components": "^5.3.6", "tailwindcss": "^3.2.4", - "typescript": "^4.9.3" + "typescript": "^4.9.3", + "url-loader": "^4.1.1" }, "engines": { "node": ">=16" }, "peerDependencies": { + "@sanity/icons": "*", "@sanity/types": "*", - "next": "^12 || ^13", - "react": "^16.3 || ^17 || ^18", - "sanity": "dev-preview", - "styled-components": "^5.2" + "@sanity/ui": "*", + "next": "^13", + "react": "^18", + "sanity": "dev-preview || ^3", + "styled-components": "*" }, "peerDependenciesMeta": { + "@sanity/icons": { + "optional": true + }, "@sanity/types": { "optional": true }, + "@sanity/ui": { + "optional": true + }, "sanity": { "optional": true }, @@ -3181,15 +3190,15 @@ } }, "node_modules/@next/env": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.5-canary.3.tgz", - "integrity": "sha512-x8hWJis9ICO6y5e7BNBPjFmFvgDRe5URd5YV3qgkw1flCRDLL3QtfTLig6nXH9DaBHaQ1a0ej7vvlzyDq6iHEg==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.5-canary.4.tgz", + "integrity": "sha512-qHYqPwCpMbpq6d2ylu2DI3ni9vYy4Fw5TlIkQGBrQsErUVnjlsjAB7+8bLAW9tFbukYz64wp8mZiBbz2GSR13g==", "dev": true }, "node_modules/@next/eslint-plugin-next": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.0.5-canary.3.tgz", - "integrity": "sha512-HSho43Ul+m159QXy1ZFcA5KIKQ2J6eB35chITAEC5pREQpBLydi+XrjjwbkYiyYtpST97HgIcV0lrMXEznOi/g==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.0.5-canary.4.tgz", + "integrity": "sha512-8z9lziST8sgoGHttpc56/9/m4o+L7uSZTrvXSBNsD7TzTRgFiCwAgqdJDOeSADjk3zGUlwAFMeFH/mXbDwgubw==", "dev": true, "dependencies": { "glob": "7.1.7" @@ -3216,9 +3225,9 @@ } }, "node_modules/@next/swc-android-arm-eabi": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.5-canary.3.tgz", - "integrity": "sha512-t/HW4YMqsXMnyaOvgK4U5wue8sgV6+yjp7eNKVqLCDxMdXemLUIqPTdVOdfU2oVScUNYj5VQFWO52HE/x6LugQ==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.5-canary.4.tgz", + "integrity": "sha512-WGM8AOnPr9ZS3F5ovjs2YVLOKMLf6YoxQwnR6m4EpPFaKM10MSiGDVA1BHb4ZC36joImyDVbk0t1gw6qIkdYMg==", "cpu": [ "arm" ], @@ -3232,9 +3241,9 @@ } }, "node_modules/@next/swc-android-arm64": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.5-canary.3.tgz", - "integrity": "sha512-WLNjvoC9sDWqDgYOJDEPKeYwS/tymTvUDBDzQfGyl0XOW8g01wD4S1TBlHDP+D9y85BxLdwlUJoYbOwhAfwI0A==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.5-canary.4.tgz", + "integrity": "sha512-Z3ARgaLWsLA78oFkcEr7jJKO/6HCUSL6Yxmv6D8VA3Pq8jS6RdhRpwjHS+mvpD8zughPPr+D72XGpu5AjHYjIg==", "cpu": [ "arm64" ], @@ -3248,9 +3257,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.5-canary.3.tgz", - "integrity": "sha512-JeGAnktTy7fQeF8PrQJgT25LlNnRFiH6zXWVfHTRk4ufcERifaOnwpzPLjIxF6euo3qD8VynCQhYH/JsVS5HNQ==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.5-canary.4.tgz", + "integrity": "sha512-33LjUwHXLnRut9l1aEiVlmFeMOrmfhZ0zY2OiD/8efOK/rJhrvaVT9Jlkc2m6qYcQ5KYJIGE72hP859wuamVwQ==", "cpu": [ "arm64" ], @@ -3264,9 +3273,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.5-canary.3.tgz", - "integrity": "sha512-yehsw+60uE47but0FUJD2+/06tQSsIm1/XFlFFhHv4d+bq39GB2fT+wL+h/aLK+es0yY7rx1+6Yad7CQ7a2J+Q==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.5-canary.4.tgz", + "integrity": "sha512-Wc+hZt0HitEOONXW/5z85rUYjiRofCWazQGaQpSP8ITSjXisgOvbT1ueV9B02xiy1VvfawZBDSq2mhlgRRtgeA==", "cpu": [ "x64" ], @@ -3280,9 +3289,9 @@ } }, "node_modules/@next/swc-freebsd-x64": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.5-canary.3.tgz", - "integrity": "sha512-Nd1RicUhogxTsassJNGG+4ha5ezyjJb58J6Vjs9kT5I40Iu8Mw98hCwn6VmiCtJMpY4HGJcuYtCYyXh5hY6ByQ==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.5-canary.4.tgz", + "integrity": "sha512-R3rwaWAKAdDoe5YmRYu77oba0ZeOYBmOf6rGGS8V6FoMbdIalE1AOT2ZQTabiWRSqnm6QRDc8W+qOujdIUIH8Q==", "cpu": [ "x64" ], @@ -3296,9 +3305,9 @@ } }, "node_modules/@next/swc-linux-arm-gnueabihf": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.5-canary.3.tgz", - "integrity": "sha512-t8TNq1rVBaYJz5dzAJIXgfYh7KRFich6gIT38zJ82McbKWFccuY6+9+FP6n0zkNFAZAd0CBvTQJ8fQFX6XPMog==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.5-canary.4.tgz", + "integrity": "sha512-5yI5/Imgl6nDLZeb7CkdvyHJcg9yasgtJoQfvWVmGjaMvTXm9mBN5NzgDR3L54QfzAXm3E+6FL9YfbW2hNWTew==", "cpu": [ "arm" ], @@ -3312,9 +3321,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.5-canary.3.tgz", - "integrity": "sha512-jsHOBJSkBn247KlW2BG5j1hoLG3EIa1gE/FLCj7w/cQBCtrhb83oGBxat52Wh7UmwajrvNl13clQV0JlmkX3sg==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.5-canary.4.tgz", + "integrity": "sha512-gd+cBGwuPwjq6ymW0VGIlVKSgcvh9mAYfmuuf5wGTNVL5q7DrjAu2vsVJucoIjND4nZmbpDMc+r3CNqP9hKpiQ==", "cpu": [ "arm64" ], @@ -3328,9 +3337,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.5-canary.3.tgz", - "integrity": "sha512-AVsfjFzRKx9I4rbHBOK1WZyl3jF635uLcb7ssrJtxyMGHcr4R9KCTcl+kv7RZ2WNKVafun0pUAITKHoqKVO4fg==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.5-canary.4.tgz", + "integrity": "sha512-U/iT04PDx389XuW0U/RMlX0esG6MMvkarhZxqzCmyov2UokmIBqQFgno5z5qeI5WU6K3d9PAHqIzM/Phc8OZHg==", "cpu": [ "arm64" ], @@ -3344,9 +3353,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.5-canary.3.tgz", - "integrity": "sha512-E83c9cs+8hwvpTUcn991ec/Q6tDG54SzvVrvNqAj23yiWGfHx0MDt1NqYtF0WOJ7W+6G6PtlD9SR0KKiaPMSeA==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.5-canary.4.tgz", + "integrity": "sha512-6wFkzS4T/zMr26obiF4tilSeDKpNJ6zL0RhVwKwE4CoqbuGb7DFwLagJ08X1+uJq+iL+zI1YZ5uK2aSgBSXm1w==", "cpu": [ "x64" ], @@ -3360,9 +3369,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.5-canary.3.tgz", - "integrity": "sha512-d9CT6qNDxf/C2j8oU6sb2XK2C0qE5N6uI+JL2r19fZKgQIgjCMEWbuyI2oVpq/LrwJ4w0vmjxuRg/9YEb8zX9w==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.5-canary.4.tgz", + "integrity": "sha512-ylWljx2V5XXDLWw2BTOk8AXP6nq7DNgGM6KCBUGivRVwXzdGBUt4kLu5kblpBHCg8357eXCpqF3MVdw1DLOk7g==", "cpu": [ "x64" ], @@ -3376,9 +3385,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.5-canary.3.tgz", - "integrity": "sha512-4Mcw9pc8HgZ0FoOybyfDW17dXXgJnqs8TDXbWDlV9tGrbYJ5yPPe2r0gZaPCGCtbYLLz3XQfd6kDDf2J0hx7Kw==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.5-canary.4.tgz", + "integrity": "sha512-cQfyuXRGuWIrbXVkA0PHgOf6858a8du2lxgznjmjolLrBLFIHCZsDOFjYgFLC25cAGFDVhHH94F6E+o1RoDxoQ==", "cpu": [ "arm64" ], @@ -3392,9 +3401,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.5-canary.3.tgz", - "integrity": "sha512-vGHnW+l+aE264lmz7warC7FPEUb3RDypooKZHDb7fTtlbqnEBf8MVlPYLVj73MSwbW8vKfoatScCG09tljYztA==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.5-canary.4.tgz", + "integrity": "sha512-JQvizP49QnkXfBYp+L+Gf9txV5fOwUqBlCRA/44N3kWnkrhs/oceivI/l1dw8OUMzwtwHCrTyjY4tJbNYZ3aMA==", "cpu": [ "ia32" ], @@ -3408,9 +3417,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.5-canary.3.tgz", - "integrity": "sha512-BvqxBWnY2D0DDBh+NxSQ70+f/kLEEQAbkbyjOcKIxjtYY9Gu6FHAHHyyTNwXMRKr7X3yiX/olLTy1EekCZ0M6A==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.5-canary.4.tgz", + "integrity": "sha512-POPU0u5+1kh2afknud/rqfxN9tBdJtLNYEEPrSO8hSGMlWGv+Mzqnr/xtcb1lHOagXjNRSSjJ2T2DGTyu8Aj6A==", "cpu": [ "x64" ], @@ -7160,6 +7169,15 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -7743,6 +7761,15 @@ "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", "dev": true }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -9200,6 +9227,15 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -9844,12 +9880,12 @@ } }, "node_modules/eslint-config-next": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.0.5-canary.3.tgz", - "integrity": "sha512-fgEDrlAx2Em3bsifMJ9MkJkfxQkh7UM5Bb4D7QHHhYgExRYuhmFGPCGRKw9EXNN0fvnLmd1vSIj1EXMSzA9YgQ==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.0.5-canary.4.tgz", + "integrity": "sha512-8PMhPj9BvDJVoEVN8//MCGDy6p+NrZ3L48fsukq74ISMMkukHjt9gvYDaI1qJRGb5UmLr47MX2mLUSsCYvJAEg==", "dev": true, "dependencies": { - "@next/eslint-plugin-next": "13.0.5-canary.3", + "@next/eslint-plugin-next": "13.0.5-canary.4", "@rushstack/eslint-patch": "^1.1.3", "@typescript-eslint/parser": "^5.42.0", "eslint-import-resolver-node": "^0.3.6", @@ -11497,20 +11533,6 @@ "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==", "dev": true }, - "node_modules/history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -14308,6 +14330,20 @@ "node": ">=4" } }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -14924,12 +14960,12 @@ "dev": true }, "node_modules/next": { - "version": "13.0.5-canary.3", - "resolved": "https://registry.npmjs.org/next/-/next-13.0.5-canary.3.tgz", - "integrity": "sha512-u3As6SkLXf2u9Mt06B3gBdQzVi45qoH27fyj2UGH+GcWjloz0x0Q+99CEz0sr93zS6iQRZuDn93PMZXoXIa8Og==", + "version": "13.0.5-canary.4", + "resolved": "https://registry.npmjs.org/next/-/next-13.0.5-canary.4.tgz", + "integrity": "sha512-EW6TvRv/paH48MhiPxxpRHIucFjJCcHXiBL/23IG3P/vOO5kJi76/HUeMDowcsv7UmrZAhwndLlnS5sOQplojw==", "dev": true, "dependencies": { - "@next/env": "13.0.5-canary.3", + "@next/env": "13.0.5-canary.4", "@swc/helpers": "0.4.14", "caniuse-lite": "^1.0.30001406", "postcss": "8.4.14", @@ -14943,19 +14979,19 @@ "node": ">=14.6.0" }, "optionalDependencies": { - "@next/swc-android-arm-eabi": "13.0.5-canary.3", - "@next/swc-android-arm64": "13.0.5-canary.3", - "@next/swc-darwin-arm64": "13.0.5-canary.3", - "@next/swc-darwin-x64": "13.0.5-canary.3", - "@next/swc-freebsd-x64": "13.0.5-canary.3", - "@next/swc-linux-arm-gnueabihf": "13.0.5-canary.3", - "@next/swc-linux-arm64-gnu": "13.0.5-canary.3", - "@next/swc-linux-arm64-musl": "13.0.5-canary.3", - "@next/swc-linux-x64-gnu": "13.0.5-canary.3", - "@next/swc-linux-x64-musl": "13.0.5-canary.3", - "@next/swc-win32-arm64-msvc": "13.0.5-canary.3", - "@next/swc-win32-ia32-msvc": "13.0.5-canary.3", - "@next/swc-win32-x64-msvc": "13.0.5-canary.3" + "@next/swc-android-arm-eabi": "13.0.5-canary.4", + "@next/swc-android-arm64": "13.0.5-canary.4", + "@next/swc-darwin-arm64": "13.0.5-canary.4", + "@next/swc-darwin-x64": "13.0.5-canary.4", + "@next/swc-freebsd-x64": "13.0.5-canary.4", + "@next/swc-linux-arm-gnueabihf": "13.0.5-canary.4", + "@next/swc-linux-arm64-gnu": "13.0.5-canary.4", + "@next/swc-linux-arm64-musl": "13.0.5-canary.4", + "@next/swc-linux-x64-gnu": "13.0.5-canary.4", + "@next/swc-linux-x64-musl": "13.0.5-canary.4", + "@next/swc-win32-arm64-msvc": "13.0.5-canary.4", + "@next/swc-win32-ia32-msvc": "13.0.5-canary.4", + "@next/swc-win32-x64-msvc": "13.0.5-canary.4" }, "peerDependencies": { "fibers": ">= 3.1.0", @@ -20512,6 +20548,20 @@ "node": ">=8" } }, + "node_modules/sanity/node_modules/history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, "node_modules/sanity/node_modules/json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", @@ -20630,6 +20680,24 @@ "loose-envify": "^1.1.0" } }, + "node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/scroll-into-view-if-needed": { "version": "2.2.29", "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz", @@ -22178,6 +22246,33 @@ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", diff --git a/package.config.ts b/package.config.ts index 805569887b..68279ae988 100644 --- a/package.config.ts +++ b/package.config.ts @@ -18,7 +18,7 @@ export default defineConfig({ url({ fileName: '[dirname][hash][extname]', sourceDir: path.join(__dirname, 'src'), - include: ['**/*.ico', '**/*.svg', '**/*.png', '**/*.jp(e)?g', '**/*.gif', '**/*.webp'], + include: ['**/*.ico', '**/*.svg', '**/*.png'], }), ], }, diff --git a/package.json b/package.json index a6ba718bb8..19f69195f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-sanity", - "version": "2.1.0", + "version": "3.0.0-app-dir.3", "description": "Sanity.io toolkit for Next.js", "keywords": [ "sanity", @@ -39,16 +39,23 @@ "default": "./dist/preview.js" }, "./studio": { - "types": "./dist/studio.d.ts", + "types": "./dist/studio/index.d.ts", "source": "./src/studio/index.ts", "node": { "source": "./src/studio/index.ts", - "import": "./dist/studio.mjs", - "require": "./dist/studio.cjs" + "import": "./dist/studio/index.mjs", + "require": "./dist/studio/index.cjs" }, - "import": "./dist/studio.js", - "require": "./dist/studio.cjs", - "default": "./dist/studio.js" + "import": "./dist/studio/index.js", + "require": "./dist/studio/index.cjs", + "default": "./dist/studio/index.js" + }, + "./studio/head": { + "types": "./dist/studio/head.d.ts", + "source": "./src/studio/head/index.ts", + "import": "./dist/studio/head.js", + "require": "./dist/studio/head.cjs", + "default": "./dist/studio/head.js" }, "./webhook": { "types": "./dist/webhook.d.ts", @@ -69,7 +76,10 @@ "./dist/preview.d.ts" ], "studio": [ - "./dist/studio.d.ts" + "./dist/studio/index.d.ts" + ], + "studio/head": [ + "./dist/studio/head.d.ts" ], "webhook": [ "./dist/webhook.d.ts" @@ -85,7 +95,7 @@ "prebuild": "npm run clean", "build": "pkg build --tsconfig tsconfig.build.json --strict", "postbuild": "npm run build:studio.mjs", - "build:studio.mjs": "cp studio.mjs dist/studio.mjs", + "build:studio.mjs": "cp studio.mjs dist/studio/index.mjs", "clean": "rimraf dist", "coverage": "npm test -- --coverage", "dev": "next", @@ -96,7 +106,7 @@ "test": "jest", "test:node-esm-cjs": "node test.mjs && node test.cjs", "type-check": "tsc --noEmit", - "update:icons": "cp node_modules/@sanity/server/lib/static/favicons/* src/studio && cp src/studio/favicon-{192,512}.png public && cp src/studio/webmanifest.json public/manifest.webmanifest" + "update:icons": "cp node_modules/@sanity/server/static/favicons/{apple-touch-icon.png,favicon.ico,favicon.svg} src/studio/head" }, "browserslist": [ "> 0.2% and supports es6-module and supports es6-module-dynamic-import and not dead and not IE 11", @@ -131,7 +141,7 @@ "@typescript-eslint/eslint-plugin": "^5.43.0", "autoprefixer": "^10.4.13", "eslint": "^8.28.0", - "eslint-config-next": "13.0.5-canary.3", + "eslint-config-next": "13.0.5-canary.4", "eslint-config-prettier": "^8.5.0", "eslint-config-sanity": "^6.0.0", "eslint-gitignore": "^0.1.0", @@ -140,7 +150,7 @@ "groqd": "^0.0.4", "jest": "^29.3.1", "jest-environment-jsdom": "^29.3.1", - "next": "13.0.5-canary.3", + "next": "13.0.5-canary.4", "postcss": "^8.4.19", "prettier": "^2.7.1", "prettier-plugin-packagejson": "^2.3.0", @@ -152,19 +162,28 @@ "sanity": "3.0.0-rc.2", "styled-components": "^5.3.6", "tailwindcss": "^3.2.4", - "typescript": "^4.9.3" + "typescript": "^4.9.3", + "url-loader": "^4.1.1" }, "peerDependencies": { + "@sanity/icons": "*", "@sanity/types": "*", - "next": "^12 || ^13", - "react": "^16.3 || ^17 || ^18", - "sanity": "dev-preview", - "styled-components": "^5.2" + "@sanity/ui": "*", + "next": "^13", + "react": "^18", + "sanity": "dev-preview || ^3", + "styled-components": "*" }, "peerDependenciesMeta": { + "@sanity/icons": { + "optional": true + }, "@sanity/types": { "optional": true }, + "@sanity/ui": { + "optional": true + }, "sanity": { "optional": true }, diff --git a/pages/_document.tsx b/pages/_document.tsx deleted file mode 100644 index 000907d953..0000000000 --- a/pages/_document.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import {ServerStyleSheetDocument} from 'src/studio' - -export default class Document extends ServerStyleSheetDocument {} diff --git a/pages/studio/[[...tool]].tsx b/pages/studio/[[...tool]].tsx deleted file mode 100644 index 030dc595ff..0000000000 --- a/pages/studio/[[...tool]].tsx +++ /dev/null @@ -1,15 +0,0 @@ -import _config from 'sanity.config' -import {NextStudio, useConfigWithBasePath} from 'src/studio' - -export default function StudioPage() { - const config = useConfigWithBasePath(_config) - - return ( - <NextStudio - config={config} - // Turn off login in production so that anyone can look around in the Studio and see how it works - // eslint-disable-next-line no-process-env - unstable_noAuthBoundary={process.env.NEXT_PUBLIC_VERCEL_ENV === 'production'} - /> - ) -} diff --git a/public/favicon-192.png b/public/favicon-192.png deleted file mode 100644 index 1323376b82..0000000000 Binary files a/public/favicon-192.png and /dev/null differ diff --git a/public/favicon-512.png b/public/favicon-512.png deleted file mode 100644 index 6320b50010..0000000000 Binary files a/public/favicon-512.png and /dev/null differ diff --git a/public/manifest.webmanifest b/public/manifest.webmanifest deleted file mode 100644 index b55b317f4c..0000000000 --- a/public/manifest.webmanifest +++ /dev/null @@ -1,14 +0,0 @@ -{ - "icons": [ - { - "src": "./favicon-192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "./favicon-512.png", - "type": "image/png", - "sizes": "512x512" - } - ] -} diff --git a/sanity.cli.ts b/sanity.cli.ts index 669f9c6b8c..ddb9b439a0 100644 --- a/sanity.cli.ts +++ b/sanity.cli.ts @@ -1,6 +1,6 @@ /* eslint-disable no-process-env */ import {loadEnvConfig} from '@next/env' -import {createCliConfig} from 'sanity/cli' +import {defineCliConfig} from 'sanity/cli' const dev = process.env.NODE_ENV !== 'production' loadEnvConfig(__dirname, dev, {info: () => null, error: console.error}) @@ -8,4 +8,4 @@ loadEnvConfig(__dirname, dev, {info: () => null, error: console.error}) const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET -export default createCliConfig({api: {projectId, dataset}}) +export default defineCliConfig({api: {projectId, dataset}}) diff --git a/sanity.config.ts b/sanity.config.ts index ac2bf47b5e..4fe0b3a500 100644 --- a/sanity.config.ts +++ b/sanity.config.ts @@ -10,6 +10,7 @@ const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET! export default defineConfig({ title: 'next-sanity', + basePath: '/studio', projectId, dataset, diff --git a/src/studio/LazyStudio.tsx b/src/studio/LazyStudio.tsx new file mode 100644 index 0000000000..1d0e101ed0 --- /dev/null +++ b/src/studio/LazyStudio.tsx @@ -0,0 +1 @@ +export {Studio as default} from 'sanity' diff --git a/src/studio/NextStudio.tsx b/src/studio/NextStudio.tsx index a6dae283af..4354aa6b0e 100644 --- a/src/studio/NextStudio.tsx +++ b/src/studio/NextStudio.tsx @@ -1,54 +1,28 @@ -import {memo} from 'react' -import {type StudioProps, Studio} from 'sanity' +import {lazy, memo} from 'react' +import type {StudioProps} from 'sanity' -import {NextStudioGlobalStyle, NextStudioGlobalStyleProps} from './NextStudioGlobalStyle' -import {type NextStudioHeadProps, NextStudioHead} from './NextStudioHead' +import {NextStudioFallback} from './NextStudioFallback' +import {type NextStudioLayoutProps, NextStudioLayout} from './NextStudioLayout' import {NextStudioNoScript} from './NextStudioNoScript' -import {useBackgroundColorsFromTheme, useTextFontFamilyFromTheme, useTheme} from './utils' +import {NextStudioSuspense} from './NextStudioSuspense' + +const Studio = memo(lazy(() => import('./LazyStudio'))) /** @beta */ export interface NextStudioProps extends StudioProps { children?: React.ReactNode /** - * Turns off the default global styling - * @alpha - */ - unstable__noGlobalStyle?: boolean - /** - * Apply fix with SVG icon centering that happens if TailwindCSS is loaded, on by default - * @alpha - */ - unstable__noTailwindSvgFix?: NextStudioGlobalStyleProps['unstable__tailwindSvgFix'] - /** - * Add stuff to the head with next/head - * @alpha - */ - unstable__head?: NextStudioHeadProps['children'] - /** - * Sets the document title - * @alpha - */ - unstable__document_title?: NextStudioHeadProps['title'] - /** - * Sets the background color of <html> - * @alpha - */ - unstable__bg?: NextStudioGlobalStyleProps['bg'] - /** - * Sets the font-family of #__next - * @alpha - */ - unstable__fontFamily?: NextStudioGlobalStyleProps['fontFamily'] - /** - * Don't load the favicon meta tags + * Apply fix with SVG icon centering that happens if TailwindCSS is loaded + * @defaultValue true * @alpha */ - unstable__noFavicons?: boolean + unstable__tailwindSvgFix?: NextStudioLayoutProps['unstable__tailwindSvgFix'] /** - * Don't render the <noscript> tag + * Render the <noscript> tag + * @defaultValue true * @alpha */ - unstable__noNoScript?: boolean + unstable__noScript?: boolean } /** * Intended to render at the root of a page, letting the Studio own that page and render much like it would if you used `npx sanity start` to render @@ -57,38 +31,25 @@ export interface NextStudioProps extends StudioProps { const NextStudioComponent = ({ children, config, - unstable__noGlobalStyle, - unstable__noTailwindSvgFix, - unstable__head, - unstable__document_title, - unstable__bg, - unstable__fontFamily, - unstable__noFavicons, - unstable__noNoScript, + unstable__tailwindSvgFix = true, + unstable__noScript = true, + scheme, ...props }: NextStudioProps) => { - const theme = useTheme(config) - const {themeColorLight, themeColorDark} = useBackgroundColorsFromTheme(theme) - const themeFontFamily = useTextFontFamilyFromTheme(theme) return ( <> - {children || <Studio config={config} {...props} />} - <NextStudioHead - themeColorLight={themeColorLight} - themeColorDark={themeColorDark} - title={unstable__document_title} - favicons={!unstable__noFavicons} - > - {unstable__head} - </NextStudioHead> - {!unstable__noGlobalStyle && ( - <NextStudioGlobalStyle - bg={unstable__bg ?? themeColorLight} - fontFamily={unstable__fontFamily ?? themeFontFamily} - unstable__tailwindSvgFix={!unstable__noTailwindSvgFix} - /> - )} - {!unstable__noNoScript && <NextStudioNoScript />} + {!unstable__noScript && <NextStudioNoScript />} + <NextStudioSuspense fallback={<NextStudioFallback config={config} scheme={scheme} />}> + <NextStudioLayout + config={config} + scheme={scheme} + unstable__tailwindSvgFix={unstable__tailwindSvgFix} + > + {children || ( + <Studio config={config} scheme={scheme} unstable_globalStyles {...props} /> + )} + </NextStudioLayout> + </NextStudioSuspense> </> ) } diff --git a/src/studio/NextStudioFallback.tsx b/src/studio/NextStudioFallback.tsx new file mode 100644 index 0000000000..28072bdb68 --- /dev/null +++ b/src/studio/NextStudioFallback.tsx @@ -0,0 +1,86 @@ +// Intentionally not using `styled-components` to ensure it works in any `next` setup. +// Wether 'styled-components' SSR is setup or not. + +import {SpinnerIcon} from '@sanity/icons' +import {_responsive, rem} from '@sanity/ui' +import {memo} from 'react' +import type {StudioProps} from 'sanity' + +import {useTheme} from './useTheme' + +/** @alpha */ +export type NextStudioFallbackProps = Pick<StudioProps, 'config' | 'scheme'> + +const keyframes = ` +from { + transform: rotate(0deg); +} + +to { + transform: rotate(360deg); +} +` + +function NextStudioFallbackComponent(props: NextStudioFallbackProps) { + const {config, scheme = 'light'} = props + const id = 'next-sanity-spinner' + const theme = useTheme(config) + const {fonts, media} = theme + + const styles: any = _responsive(media, [2], (size: number) => { + const {ascenderHeight, descenderHeight, lineHeight, iconSize} = fonts.text.sizes[size] + const capHeight = lineHeight - ascenderHeight - descenderHeight + + return { + wrapper: { + animation: `${id} 500ms linear infinite`, + color: theme.color[scheme].default.muted.default.enabled.muted.fg, + width: rem(capHeight), + height: rem(capHeight), + }, + svg: { + display: 'block', + width: rem(iconSize), + height: rem(iconSize), + margin: (capHeight - iconSize) / 2, + }, + } + })[0] + + return ( + <div + style={{ + fontFamily: fonts.text.family, + backgroundColor: theme.color[scheme].default.base.bg, + height: '100vh', + maxHeight: '100dvh', + overscrollBehavior: 'none', + WebkitFontSmoothing: 'antialiased', + overflow: 'auto', + }} + > + <div + data-ui="Flex" + style={{ + display: 'flex', + minWidth: 0, + minHeight: 0, + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'column', + height: '100%', + margin: 0, + padding: 0, + }} + > + <style key={scheme}>{`@keyframes ${id} {${keyframes}}`}</style> + <div data-ui="Spinner" style={styles.wrapper}> + <SpinnerIcon style={styles.svg} /> + </div> + </div> + </div> + ) +} + +/** @alpha */ +export const NextStudioFallback = memo(NextStudioFallbackComponent) diff --git a/src/studio/NextStudioGlobalStyle.ts b/src/studio/NextStudioGlobalStyle.ts deleted file mode 100644 index 6478ff1d5a..0000000000 --- a/src/studio/NextStudioGlobalStyle.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {createGlobalStyle, css} from 'styled-components' - -/** @alpha */ -export interface NextStudioGlobalStyleProps { - fontFamily?: string - bg?: string - unstable__tailwindSvgFix?: boolean -} -/** @alpha */ -export const NextStudioGlobalStyle = createGlobalStyle<NextStudioGlobalStyleProps>` -${({bg}) => - bg - ? css` - html { - background-color: ${bg}; - } - ` - : ''} -html, -body, -#__next { - height: 100%; -} -body { - margin: 0; - overscroll-behavior: none; - -webkit-font-smoothing: antialiased; -} -${({fontFamily}) => - fontFamily - ? css` - #__next { - font-family: ${fontFamily}; - } - ` - : ''} -${({unstable__tailwindSvgFix}) => - unstable__tailwindSvgFix - ? css` - /* override tailwind reset */ - :root svg { - display: inline; - } - ` - : ''}` diff --git a/src/studio/NextStudioHead.tsx b/src/studio/NextStudioHead.tsx deleted file mode 100644 index 082f252782..0000000000 --- a/src/studio/NextStudioHead.tsx +++ /dev/null @@ -1,110 +0,0 @@ -/* eslint-disable no-process-env */ -import _Head from 'next/head' -import {type ReactNode, memo, useCallback} from 'react' - -// @ts-ignore -import iconApple from './apple-touch-icon.png' -// @ts-ignore -import iconIco from './favicon.ico' -// @ts-ignore -import iconSvg from './favicon.svg' -// @ts-ignore -import icon192 from './favicon-192.png' -// @ts-ignore -import icon512 from './favicon-512.png' -import type {MetaThemeColors} from './utils' -import webmanifest from './webmanifest.json' - -// Workaround ESM + CJS interop issues -/** @internal */ -const Head = ('default' in _Head ? _Head.default : _Head) as typeof _Head - -// Interop between how Parcel and Next deals with asset imports -const interop = (href: string | {src: string}): string => - typeof href === 'string' ? href : href.src - -/** @alpha */ -export interface NextStudioHeadProps extends Partial<MetaThemeColors> { - children?: ReactNode - title?: string - favicons?: boolean -} -const NextStudioHeadComponent = ({ - children, - themeColorDark, - themeColorLight, - title = 'Sanity Studio', - favicons, -}: NextStudioHeadProps) => { - const inlineWebmanifest = useCallback(() => { - const manifest = JSON.parse(JSON.stringify(webmanifest)) - const icons = manifest.icons.map((icon: any) => { - // Inline manifests works best when URLs are absolute - const src = - // eslint-disable-next-line no-nested-ternary - icon.src === './favicon-192.png' - ? interop(icon192) - : icon.src === './favicon-512.png' - ? interop(icon512) - : icon.src - return { - ...icon, - src: process.env.NEXT_PUBLIC_VERCEL_URL - ? new URL(src, `https://${process.env.NEXT_PUBLIC_VERCEL_URL}`).toString() - : src, - } - }) - return `data:application/manifest+json,${encodeURIComponent( - JSON.stringify({...manifest, icons}) - )}` - }, []) - - return ( - <Head> - <meta - name="viewport" - // Studio implements display cutouts CSS (The iPhone Notch ™ ) and needs `viewport-fit=covered` for it to work correctly - content="width=device-width, initial-scale=1, viewport-fit=cover" - /> - <meta name="robots" content="noindex" /> - <meta name="referrer" content="same-origin" /> - {title && <title>{title}} - {favicons && } - {favicons && } - {favicons && } - {favicons && ( - - )} - {/* These theme-color tags makes the Studio look really really good on devices like iPads as the browser chrome adopts the Studio background */} - {themeColorLight && ( - - )} - {themeColorDark && ( - - )} - {children} - - ) -} - -/** @alpha */ -export const NextStudioHead = memo(NextStudioHeadComponent) diff --git a/src/studio/NextStudioLayout.tsx b/src/studio/NextStudioLayout.tsx new file mode 100644 index 0000000000..e0235dfd05 --- /dev/null +++ b/src/studio/NextStudioLayout.tsx @@ -0,0 +1,64 @@ +/* eslint-disable camelcase */ +import {memo} from 'react' +import type {StudioProps} from 'sanity' +import styled, {css} from 'styled-components' + +import {useTheme} from './useTheme' + +/** @alpha */ +export interface NextStudioLayoutProps extends Pick { + children: React.ReactNode + /** + * Apply fix with SVG icon centering that happens if TailwindCSS is loaded + * @defaultValue true + */ + unstable__tailwindSvgFix?: boolean +} + +type LayoutProps = { + $unstable__tailwindSvgFix: NextStudioLayoutProps['unstable__tailwindSvgFix'] + $bg: string + $fontFamily: string +} +const Layout = styled.div` + font-family: ${({$fontFamily}) => $fontFamily}; + background-color: ${({$bg}: any) => $bg}; + height: 100vh; + max-height: 100dvh; + overscroll-behavior: none; + -webkit-font-smoothing: antialiased; + overflow: auto; + + ${({$unstable__tailwindSvgFix}: any) => + $unstable__tailwindSvgFix + ? css` + /* override tailwind reset */ + *:not([data-ui='Popover__arrow']):not([data-ui='Tooltip__arrow']) > svg { + display: inline; + } + ` + : ''} +` + +const NextStudioLayoutComponent = ({ + children, + config, + scheme = 'light', + unstable__tailwindSvgFix = true, +}: NextStudioLayoutProps) => { + const theme = useTheme(config) + + return ( + + {children} + + ) +} + +/** @alpha */ +export const NextStudioLayout = memo(NextStudioLayoutComponent) diff --git a/src/studio/NextStudioNoScript.tsx b/src/studio/NextStudioNoScript.tsx index 076fc611bc..3d3fa2ef66 100644 --- a/src/studio/NextStudioNoScript.tsx +++ b/src/studio/NextStudioNoScript.tsx @@ -9,6 +9,7 @@ const style = { left: 0; bottom: 0; background: #fff; + z-index: 1; } .sanity-app-no-js__content { diff --git a/src/studio/NextStudioSuspense.tsx b/src/studio/NextStudioSuspense.tsx new file mode 100644 index 0000000000..a17a319c8d --- /dev/null +++ b/src/studio/NextStudioSuspense.tsx @@ -0,0 +1,15 @@ +import {type ReactNode, Suspense, useEffect, useReducer} from 'react' + +/** @alpha */ +export type NextStudioSuspenseProps = { + children: ReactNode + fallback: ReactNode +} + +/** @alpha */ +export function NextStudioSuspense({children, fallback}: NextStudioSuspenseProps) { + const [mounted, mount] = useReducer(() => true, false) + useEffect(mount, [mount]) + + return {mounted ? children : fallback} +} diff --git a/src/studio/ServerStyleSheetDocument.tsx b/src/studio/ServerStyleSheetDocument.tsx deleted file mode 100644 index 05caab10e2..0000000000 --- a/src/studio/ServerStyleSheetDocument.tsx +++ /dev/null @@ -1,62 +0,0 @@ -// We can disable this rule safely as we're not trying to use it outside pages/document, we're shipping a wrapper -// eslint-disable-next-line @next/next/no-document-import-in-page -import _Document, {type DocumentContext} from 'next/document' -import {ServerStyleSheet} from 'styled-components' - -// Workaround ESM + CJS interop issues -/** @internal */ -const Document = ('default' in _Document ? _Document.default : _Document) as typeof _Document - -/** - * Usage, from a pages/_document.tsx file: - * ``` - * import {ServerStyleSheetDocument} from 'next-sanity/studio' - * - * export default class MyDocument extends ServerStyleSheetDocument {} - * ``` - * - * To do extra stuff in getInitialProps: - * ``` - * import {ServerStyleSheetDocument} from 'next-sanity/studio' - * import { type DocumentContext } from 'next/document' - * - * export default class MyDocument extends ServerStyleSheetDocument { - * static async getInitialProps(ctx: DocumentContext) { - * // You can still override renderPage: - * const originalRenderPage = ctx.renderPage - * ctx.renderPage = () => originalRenderPage({ - * enhanceApp: (App) => (props) => - * }) - * - * const initialProps = await ServerStyleSheetDocument.getInitialProps(ctx) - * const extraStyles = await getStyles() - * return { - * ...initialProps, - * styles: [initialProps.styles, extraStyles], - * } - * } - * } - * ``` - * @beta - */ -export class ServerStyleSheetDocument extends Document { - static async getInitialProps(ctx: DocumentContext) { - const sheet = new ServerStyleSheet() - const originalRenderPage = ctx.renderPage - - try { - ctx.renderPage = () => - originalRenderPage({ - enhanceApp: (App) => (props) => sheet.collectStyles(), - }) - - const initialProps = await Document.getInitialProps(ctx) - return { - ...initialProps, - styles: [initialProps.styles, sheet.getStyleElement()], - } - } finally { - sheet.seal() - } - } -} diff --git a/src/studio/favicon-192.png b/src/studio/favicon-192.png deleted file mode 100644 index 1323376b82..0000000000 Binary files a/src/studio/favicon-192.png and /dev/null differ diff --git a/src/studio/favicon-512.png b/src/studio/favicon-512.png deleted file mode 100644 index 6320b50010..0000000000 Binary files a/src/studio/favicon-512.png and /dev/null differ diff --git a/src/studio/head/NextStudioHead.tsx b/src/studio/head/NextStudioHead.tsx new file mode 100644 index 0000000000..327726502a --- /dev/null +++ b/src/studio/head/NextStudioHead.tsx @@ -0,0 +1,102 @@ +import faviconPng from './apple-touch-icon.png' +import faviconIco from './favicon.ico' +import faviconSvg from './favicon.svg' + +/** @public */ +export interface NextStudioHeadProps { + /** + * Sets the viewport to `viewport-fit=cover` to integrate with iOS devices with display cutouts (The Notch, Dynamic Island). + * Also sets `width=device-width, initial-scale=1` to make the studio page responsive. + * @defaultValue true + */ + viewport?: boolean + /** + * It's common practice to hide the address to your Sanity Studio from search engines by setting `robots` to `noindex` + * @defaultValue 'noindex' + */ + robots?: false | string + /** + * @defaultValue 'same-origin' + */ + referrer?: false | string + /** + * Adds the same favicons as the `npx sanity dev` pipeline. + * @defaultValue true + */ + favicons?: boolean + /** + * @defaultValue 'Sanity' + */ + title?: false | string +} + +/** + * In Next 13 appDir mode (`/app/studio/[[...index]]/head.tsx`): + * ```tsx + * // If you don't want to change any defaults you can just re-export the head component directly: + * export {NextStudioHead as default} from 'next-sanity/studio/head' + * + * // To customize it, use it as a children component: + * import {NextStudioHead} from 'next-sanity/studio/head' + * + * export default function CustomStudioHead() { + * return ( + * <> + * + * + * + * ) + * } + * ``` + * If you're using Next 12 or the `pages` folder (`/pages/studio/[[...index]].tsx`): + * ```tsx + * import Head from 'next/head' + * import {NextStudio} from 'next-sanity/studio' + * import {NextStudioHead} from 'next-sanity/studio/head' + * + * export default function StudioPage() { + * return ( + * <> + * + * + * + * + * + * ) + * } + * ``` + * @public + */ +export function NextStudioHead(props: NextStudioHeadProps) { + const { + viewport = true, + robots = 'noindex', + referrer = 'same-origin', + favicons = true, + title = 'Sanity', + } = props + + return ( + <> + {viewport && ( + + )} + {robots && } + {referrer && } + {title && {title}} + {favicons && } + {favicons && } + {favicons && } + + ) +} diff --git a/src/studio/apple-touch-icon.png b/src/studio/head/apple-touch-icon.png similarity index 100% rename from src/studio/apple-touch-icon.png rename to src/studio/head/apple-touch-icon.png diff --git a/src/studio/favicon.ico b/src/studio/head/favicon.ico similarity index 100% rename from src/studio/favicon.ico rename to src/studio/head/favicon.ico diff --git a/src/studio/favicon.svg b/src/studio/head/favicon.svg similarity index 100% rename from src/studio/favicon.svg rename to src/studio/head/favicon.svg diff --git a/src/studio/head/index.ts b/src/studio/head/index.ts new file mode 100644 index 0000000000..8615d63cff --- /dev/null +++ b/src/studio/head/index.ts @@ -0,0 +1 @@ +export * from './NextStudioHead' diff --git a/src/studio/index.ts b/src/studio/index.ts index 3daa05faf5..e6cc968c47 100644 --- a/src/studio/index.ts +++ b/src/studio/index.ts @@ -1,6 +1,7 @@ export * from './NextStudio' -export * from './NextStudioGlobalStyle' -export * from './NextStudioHead' +export * from './NextStudioFallback' +export * from './NextStudioLayout' export * from './NextStudioNoScript' -export * from './ServerStyleSheetDocument' -export * from './utils' +export * from './NextStudioSuspense' +export * from './usePrefersColorScheme' +export * from './useTheme' diff --git a/src/studio/usePrefersColorScheme.ts b/src/studio/usePrefersColorScheme.ts new file mode 100644 index 0000000000..ce109ceef4 --- /dev/null +++ b/src/studio/usePrefersColorScheme.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type {ThemeColorSchemeKey} from '@sanity/ui' +import {useSyncExternalStore} from 'react' + +function createStore() { + if (typeof document === 'undefined') { + return { + subscribe: () => () => {}, + getSnapshot: () => 'light' as const, + getServerSnapshot: () => 'light' as const, + } + } + + const matchMedia = window.matchMedia('(prefers-color-scheme: dark)') + + return { + subscribe: (onStoreChange: () => void) => { + matchMedia.addEventListener('change', onStoreChange) + return () => matchMedia.removeEventListener('change', onStoreChange) + }, + getSnapshot: () => (matchMedia.matches ? 'dark' : 'light'), + getServerSnapshot: () => 'light' as const, + } +} +const store = createStore() + +/** @alpha */ +export function usePrefersColorScheme(): ThemeColorSchemeKey { + return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot) +} diff --git a/src/studio/useTheme.ts b/src/studio/useTheme.ts new file mode 100644 index 0000000000..ed15bdab72 --- /dev/null +++ b/src/studio/useTheme.ts @@ -0,0 +1,36 @@ +import {useMemo} from 'react' +import { + type Config, + type SingleWorkspace, + type StudioTheme, + type WorkspaceOptions, + defaultTheme, +} from 'sanity' + +type WithTheme = { + theme: StudioTheme +} +type SingleWorkspaceWithTheme = Omit & WithTheme +type WorkspaceOptionsWithTheme = Omit & WithTheme + +function isWorkspaces(config: Config): config is WorkspaceOptions[] { + return Array.isArray(config) +} + +function isWorkspaceWithTheme( + workspace: SingleWorkspace | WorkspaceOptions +): workspace is SingleWorkspaceWithTheme | WorkspaceOptionsWithTheme { + return Boolean(workspace.theme) +} + +/** @alpha */ +export function useTheme(config: Config): StudioTheme { + const workspace = useMemo( + () => (isWorkspaces(config) ? config[0] : config), + [config] + ) + return useMemo( + () => (isWorkspaceWithTheme(workspace) ? workspace.theme : defaultTheme), + [workspace] + ) +} diff --git a/src/studio/utils.ts b/src/studio/utils.ts deleted file mode 100644 index 8fc80d6ba4..0000000000 --- a/src/studio/utils.ts +++ /dev/null @@ -1,105 +0,0 @@ -import {useRouter} from 'next/router' -import {useMemo} from 'react' -import { - type Config, - type SingleWorkspace, - type StudioTheme, - type WorkspaceOptions, - defaultTheme, -} from 'sanity' - -/** @alpha */ -export type WithTheme = { - theme: StudioTheme -} -/** @alpha */ -export type SingleWorkspaceWithTheme = Omit & WithTheme -/** @alpha */ -export type WorkspaceOptionsWithTheme = Omit & WithTheme -/** @alpha */ -export type ConfigWithTheme = SingleWorkspaceWithTheme | WorkspaceOptionsWithTheme[] - -/** @alpha */ -export function isWorkspaces(config: Config): config is WorkspaceOptions[] { - return Array.isArray(config) -} - -/** @alpha */ -export function isWorkspaceWithTheme( - workspace: SingleWorkspace | WorkspaceOptions -): workspace is SingleWorkspaceWithTheme | WorkspaceOptionsWithTheme { - return Boolean(workspace.theme) -} - -/** @alpha */ -export function useTheme(config: Config): StudioTheme { - const workspace = useMemo( - () => (isWorkspaces(config) ? config[0] : config), - [config] - ) - return useMemo( - () => (isWorkspaceWithTheme(workspace) ? workspace.theme : defaultTheme), - [workspace] - ) -} - -/** @alpha */ -export type MetaThemeColors = { - themeColorLight: string - themeColorDark: string -} -/** @alpha */ -export const useBackgroundColorsFromTheme = (theme: StudioTheme): MetaThemeColors => { - return useMemo( - () => ({ - themeColorLight: theme.color.light.default.base.bg, - themeColorDark: theme.color.dark.default.base.bg, - }), - [theme] - ) -} - -/** @alpha */ -export const useTextFontFamilyFromTheme = (theme: StudioTheme): string => { - return useMemo(() => theme.fonts.text.family, [theme]) -} - -/** - * Parses the next route to determine the what the base path for Sanity Studio should be - * @alpha - */ -export function useBasePath(): string { - const router = useRouter() - return useMemo(() => { - const [basePath = '/'] = router.route.split('/[') - return basePath - }, [router.route]) -} - -/** @alpha */ -export interface WorkspaceWithBasePath extends Omit { - basePath: string -} -/** @alpha */ -export type SingleWorkspaceWithBasePath = Omit & {basePath: string} -/** @alpha */ -export type ConfigWithBasePath = SingleWorkspaceWithBasePath | WorkspaceOptions[] -/** - * Apply the base path from next to the config, prefixing any defined base path - * @alpha - */ -export function useConfigWithBasePath(config: Config): ConfigWithBasePath { - const basePath = useBasePath() - return useMemo(() => { - if (isWorkspaces(config)) { - return config.map((workspace) => ({ - ...workspace, - basePath: workspace.basePath === '/' ? basePath : `${basePath}${workspace.basePath}`, - })) - } - return { - ...config, - basePath: config.basePath === '/' ? basePath : `${basePath}${config.basePath || ''}`, - } - }, [config, basePath]) -} diff --git a/src/studio/webmanifest.json b/src/studio/webmanifest.json deleted file mode 100644 index b55b317f4c..0000000000 --- a/src/studio/webmanifest.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "icons": [ - { - "src": "./favicon-192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "./favicon-512.png", - "type": "image/png", - "sizes": "512x512" - } - ] -} diff --git a/studio.mjs b/studio.mjs index b6f027f7fb..0c12b26379 100644 --- a/studio.mjs +++ b/studio.mjs @@ -1,33 +1,23 @@ -// Copied into dist/ by the build process, to workaround `next/head` not being available to native Nodejs ESM +// Copied into dist/studio/ by the build process, to workaround `@sanity/icons` and other packages not using "type": "module" -import studioCjs from './studio.cjs' +import studioCjs from './index.cjs' const { - isWorkspaces, - isWorkspaceWithTheme, NextStudio, - NextStudioGlobalStyle, - NextStudioHead, + NextStudioFallback, + NextStudioLayout, NextStudioNoScript, - ServerStyleSheetDocument, - useBackgroundColorsFromTheme, - useBasePath, - useConfigWithBasePath, - useTextFontFamilyFromTheme, + NextStudioSuspense, + usePrefersColorScheme, useTheme, } = studioCjs export { - isWorkspaces, - isWorkspaceWithTheme, NextStudio, - NextStudioGlobalStyle, - NextStudioHead, + NextStudioFallback, + NextStudioLayout, NextStudioNoScript, - ServerStyleSheetDocument, - useBackgroundColorsFromTheme, - useBasePath, - useConfigWithBasePath, - useTextFontFamilyFromTheme, + NextStudioSuspense, + usePrefersColorScheme, useTheme, } diff --git a/test.cjs b/test.cjs index 6844b23378..cb7cf6df2a 100644 --- a/test.cjs +++ b/test.cjs @@ -19,31 +19,26 @@ assert.equal(typeof PreviewSuspense, 'function') const nextSanityStudio = require('next-sanity/studio') const { NextStudio, - NextStudioGlobalStyle, - NextStudioHead, + NextStudioFallback, + NextStudioLayout, NextStudioNoScript, - ServerStyleSheetDocument, - isWorkspaceWithTheme, - isWorkspaces, - useBackgroundColorsFromTheme, - useBasePath, - useConfigWithBasePath, - useTextFontFamilyFromTheme, + NextStudioSuspense, + usePrefersColorScheme, useTheme, } = nextSanityStudio assert.equal(typeof NextStudio?.type, 'function') -assert.equal(typeof NextStudioGlobalStyle?.type, 'function') -assert.equal(typeof NextStudioHead?.type, 'function') +assert.equal(typeof NextStudioFallback?.type, 'function') +assert.equal(typeof NextStudioLayout?.type, 'function') assert.equal(typeof NextStudioNoScript, 'function') -assert.equal(typeof ServerStyleSheetDocument, 'function') -assert.equal(typeof isWorkspaceWithTheme, 'function') -assert.equal(typeof isWorkspaces, 'function') -assert.equal(typeof useBackgroundColorsFromTheme, 'function') -assert.equal(typeof useBasePath, 'function') -assert.equal(typeof useConfigWithBasePath, 'function') -assert.equal(typeof useTextFontFamilyFromTheme, 'function') +assert.equal(typeof NextStudioSuspense, 'function') +assert.equal(typeof usePrefersColorScheme, 'function') assert.equal(typeof useTheme, 'function') +// Testing pkg.exports[./studio/head] +const nextSanityStudioHead = require('next-sanity/studio/head') +const {NextStudioHead} = nextSanityStudioHead +assert.equal(typeof NextStudioHead, 'function') + // Testing pkg.exports[./webhook] const nextSanityWebhook = require('next-sanity/webhook') const {config, parseBody} = nextSanityWebhook diff --git a/test.mjs b/test.mjs index 7d3f4dc759..4c55bfa76a 100644 --- a/test.mjs +++ b/test.mjs @@ -16,33 +16,27 @@ assert.equal(typeof PreviewSuspense, 'function') // Testing pkg.exports[./studio] import { - isWorkspaces, - isWorkspaceWithTheme, NextStudio, - NextStudioGlobalStyle, - NextStudioHead, + NextStudioFallback, + NextStudioLayout, NextStudioNoScript, - ServerStyleSheetDocument, - useBackgroundColorsFromTheme, - useBasePath, - useConfigWithBasePath, - useTextFontFamilyFromTheme, + NextStudioSuspense, + usePrefersColorScheme, useTheme, } from 'next-sanity/studio' assert.equal(typeof NextStudio?.type, 'function') -assert.equal(typeof NextStudioGlobalStyle?.type, 'function') -assert.equal(typeof NextStudioHead?.type, 'function') +assert.equal(typeof NextStudioFallback?.type, 'function') +assert.equal(typeof NextStudioLayout?.type, 'function') assert.equal(typeof NextStudioNoScript, 'function') -assert.equal(typeof ServerStyleSheetDocument, 'function') -assert.equal(typeof isWorkspaceWithTheme, 'function') -assert.equal(typeof isWorkspaces, 'function') -assert.equal(typeof useBackgroundColorsFromTheme, 'function') -assert.equal(typeof useBasePath, 'function') -assert.equal(typeof useConfigWithBasePath, 'function') -assert.equal(typeof useTextFontFamilyFromTheme, 'function') +assert.equal(typeof NextStudioSuspense, 'function') +assert.equal(typeof usePrefersColorScheme, 'function') assert.equal(typeof useTheme, 'function') +// Testing pkg.exports[./studio/head] +import {NextStudioHead} from 'next-sanity/studio/head' +assert.equal(typeof NextStudioHead, 'function') + // Testing pkg.exports[./webhook] import {config, parseBody} from 'next-sanity/webhook' assert.equal(typeof config, 'object')