UNPKG

@nuxtjs/sanity

Version:
282 lines (278 loc) 11.5 kB
import { fileURLToPath } from 'node:url'; import crypto from 'node:crypto'; import { existsSync } from 'node:fs'; import { createJiti } from 'jiti'; import { createRegExp, exactly } from 'magic-regexp'; import { useLogger, defineNuxtModule, resolvePath, addTemplate, addPlugin, isNuxtMajorVersion, addImports, addComponentsDir, addServerHandler } from '@nuxt/kit'; import { colors } from 'consola/utils'; import { resolve, relative, join } from 'pathe'; import { defu } from 'defu'; import { genExport } from 'knitwork'; const name = "@nuxtjs/sanity"; const version = "1.14.1"; const logger = useLogger("@nuxtjs/sanity"); const CONFIG_KEY = "sanity"; const module = defineNuxtModule({ meta: { name, version, configKey: CONFIG_KEY, compatibility: { nuxt: ">=3.7.0", // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore TODO: remove when we update to v4 bridge: true } }, defaults: { additionalClients: {}, apiVersion: "1", disableSmartCdn: false, perspective: "raw", withCredentials: false, configFile: "~~/cms/sanity.config" }, async setup(options, nuxt) { if (!options.projectId || !options.dataset) { const sanityConfigPath = await resolvePath(options.configFile) || /* backwards compatibility */ resolve(nuxt.options.rootDir, "./sanity.json"); const relativeSanityConfigPath = relative(nuxt.options.rootDir, sanityConfigPath); if (!relativeSanityConfigPath.startsWith("..")) { nuxt.options.watch.push(createRegExp(exactly(relativeSanityConfigPath))); } const jiti = createJiti(import.meta.url, { jsx: true }); if (existsSync(sanityConfigPath)) { const sanityConfig = await jiti.import(sanityConfigPath, { default: true, try: true }); if (sanityConfig) { options.projectId ||= sanityConfig.projectId; options.dataset ||= sanityConfig.dataset; } } } options.dataset ||= "production"; if (options.liveContent && options.minimal) { throw new Error("Live Content API is not supported by the minimal client."); } if (options.visualEditing) { try { if (options.minimal) { throw new Error("Minimal client is enabled."); } if (!options.visualEditing.token) { throw new Error(`'token' is required.`); } if (!options.visualEditing.studioUrl) { throw new Error(`'studioUrl' is required.`); } if (options.apiVersion === "1") { throw new Error(`The specified API Version must be ${colors.bold("2021-03-25")} or later.`); } } catch (e) { options.visualEditing = void 0; if (e instanceof Error) { logger.warn(`Could not enable visual editing: ${e.message}`); } } } const runtimeConfig = {}; const publicRuntimeConfig = { additionalClients: options.additionalClients || {}, apiVersion: options.apiVersion || "1", dataset: options.dataset, disableSmartCdn: options.disableSmartCdn ?? false, perspective: options.perspective || "raw", projectId: options.projectId || "", stega: options.visualEditing && options.visualEditing.stega !== false && { enabled: true, studioUrl: options.visualEditing.studioUrl } || {}, token: options.token || "", useCdn: options.useCdn ?? true, withCredentials: options.withCredentials ?? false }; if (options.visualEditing) { const previewMode = options.visualEditing.previewMode !== false ? defu(options.visualEditing.previewMode, { enable: "/preview/enable", disable: "/preview/disable" }) : false; runtimeConfig.visualEditing = { previewModeId: previewMode ? crypto.randomBytes(16).toString("hex") : "", token: options.visualEditing.token || "" }; publicRuntimeConfig.visualEditing = { mode: options.visualEditing.mode || "live-visual-editing", previewMode, previewModeId: "", proxyEndpoint: options.visualEditing.proxyEndpoint || "/_sanity/fetch", studioUrl: options.visualEditing.studioUrl || "", token: "", zIndex: options.visualEditing.zIndex }; } if (options.liveContent) { runtimeConfig.liveContent = { serverToken: options.liveContent.serverToken || "" }; publicRuntimeConfig.liveContent = { browserToken: options.liveContent.browserToken || "", serverToken: "" }; } nuxt.options.runtimeConfig.sanity = defu(nuxt.options.runtimeConfig.sanity, runtimeConfig); const { projectId, dataset } = nuxt.options.runtimeConfig.public.sanity = defu(nuxt.options.runtimeConfig.public.sanity, publicRuntimeConfig); if (!projectId) { logger.warn(`No Sanity project found. Make sure you specify a ${colors.bold("projectId")} in your Sanity config.`); } else { logger.info(`Running with Sanity project ${colors.bold(projectId)} (${colors.bold(dataset)}).`); } const runtimeDir = fileURLToPath(new URL("./runtime", import.meta.url)); nuxt.options.build.transpile.push(runtimeDir, "@nuxtjs/sanity"); nuxt.options.build.transpile.push("@sanity/core-loader", "@sanity/preview-url-secret"); const clientSpecifier = options.minimal ? join(runtimeDir, "minimal-client") : "@sanity/client"; addTemplate({ filename: "sanity-client.mjs", getContents: () => genExport(clientSpecifier, ["createClient"]), write: true }); if (options.globalHelper) { addPlugin({ src: join(runtimeDir, "plugins/global-helper") }); if (isNuxtMajorVersion(2)) { nuxt.hook("prepare:types", ({ references }) => { references.push({ types: "@nuxtjs/sanity/dist/runtime/plugins/global-helper" }); }); } } const composablesPath = join(runtimeDir, "composables/index"); addImports([ { name: "useSanity", from: composablesPath }, { name: "createClient", as: "createSanityClient", from: "#build/sanity-client.mjs" }, { name: "groq", from: join(runtimeDir, "groq") } ]); addImports([ ...isNuxtMajorVersion(2) ? [] : [{ name: "useSanityQuery", from: composablesPath }], { name: "useSanityQuery", from: composablesPath }, { name: "useLazySanityQuery", from: composablesPath }, { name: "useSanityConfig", from: composablesPath }, { name: "useSanityPerspective", from: composablesPath }, ...isNuxtMajorVersion(2) ? [] : [{ name: "useSanityVisualEditingState", from: composablesPath }], { name: "useIsSanityLivePreview", from: composablesPath }, { name: "useIsSanityPresentationTool", from: composablesPath }, { name: "useSanityPreviewPerspective", from: composablesPath }, { name: "useSanityPreviewEnvironment", from: composablesPath }, // Visual Editing ...isNuxtMajorVersion(2) ? [] : [{ name: "createDataAttribute", from: "@sanity/visual-editing", as: "createSanityDataAttribute" }], { name: "sanityVisualEditingRefresh", from: "#build/sanity-visual-editing-refresh.mjs" }, ...isNuxtMajorVersion(2) ? [] : [{ name: "useSanityLiveMode", from: composablesPath }], ...isNuxtMajorVersion(2) ? [] : [{ name: "useSanityVisualEditing", from: composablesPath }] ]); const clientPath = await resolvePath(clientSpecifier); nuxt.hook("prepare:types", async ({ tsConfig }) => { tsConfig.compilerOptions ||= {}; tsConfig.compilerOptions.paths["#sanity-client"] = [clientPath]; tsConfig.compilerOptions.paths["#sanity-composables"] = [composablesPath]; }); nuxt.hook("nitro:config", (config) => { config.typescript = defu(config.typescript, { tsConfig: { compilerOptions: { paths: { ["#sanity-client"]: [clientPath], ["#sanity-composables"]: [composablesPath] } } } }); if (config.imports === false) return; config.virtual ||= {}; config.virtual["#sanity-client"] = genExport(clientSpecifier, ["createClient"]); config.externals ||= {}; config.externals.inline ||= []; config.externals.inline.push(runtimeDir); config.imports = defu(config.imports, { presets: [ { from: "#sanity-client", imports: [{ name: "createClient", as: "createSanityClient" }] }, { from: join(runtimeDir, "server/utils/index"), imports: ["useSanity"] }, { from: join(runtimeDir, "groq"), imports: ["groq"] } ] }); }); await addComponentsDir({ path: join(runtimeDir, "components"), extensions: ["js", "ts", "mjs"] }); if (options.liveContent) { addPlugin({ mode: "client", src: join(runtimeDir, "plugins", "live-content.client") }); } if (publicRuntimeConfig.visualEditing) { nuxt.options.build.transpile.push("async-cache-dedupe"); nuxt.options.vite.resolve = defu(nuxt.options.vite.resolve, { dedupe: ["@sanity/client"] }); nuxt.options.vite.optimizeDeps = defu(nuxt.options.vite.optimizeDeps, { include: [ "@nuxtjs/sanity > @sanity/visual-editing > @sanity/visual-editing > react-is", "@nuxtjs/sanity > @sanity/visual-editing > @sanity/mutate > lodash/groupBy.js", "@nuxtjs/sanity > @sanity/visual-editing > react", "@nuxtjs/sanity > @sanity/visual-editing > react/jsx-runtime", "@nuxtjs/sanity > @sanity/visual-editing > react-dom", "@nuxtjs/sanity > @sanity/visual-editing > react-dom/client", "@nuxtjs/sanity > @sanity/visual-editing > react-compiler-runtime", "@sanity/client" ] }); addTemplate({ filename: "sanity-visual-editing-refresh.mjs", getContents: () => ` export const sanityVisualEditingRefresh = ${options.visualEditing?.refresh?.toString() || "undefined"} `, write: true }); addPlugin({ mode: "server", src: join(runtimeDir, "plugins", "visual-editing.server") }); if (publicRuntimeConfig.visualEditing.mode !== "custom") { addPlugin({ mode: "client", src: join(runtimeDir, "plugins", "visual-editing.client") }); logger.info(`Visual editing enabled globally.`); } else { logger.info(`Call ${colors.bold("useSanityVisualEditing()")} in your application to enable visual editing.`); } addServerHandler({ method: "post", route: publicRuntimeConfig.visualEditing.proxyEndpoint, handler: join(runtimeDir, "server/routes/preview/proxy") }); if (publicRuntimeConfig.visualEditing.previewMode !== false) { addServerHandler({ method: "get", route: publicRuntimeConfig.visualEditing.previewMode.enable, handler: join(runtimeDir, "server/routes/preview/enable") }); addServerHandler({ method: "get", route: publicRuntimeConfig.visualEditing.previewMode.disable, handler: join(runtimeDir, "server/routes/preview/disable") }); logger.info( `Preview mode enabled. Added routes at: ${Object.values(publicRuntimeConfig.visualEditing.previewMode).map((route) => colors.bold(route)).join(", ")}.` ); } } } }); export { module as default };