UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

1,015 lines (998 loc) • 42.7 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf, __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from == "object" || typeof from == "function") for (let key of __getOwnPropNames(from)) !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: !0 }) : target, mod )); var path = require("node:path"), debug$4 = require("debug"), readPkgUp = require("read-pkg-up"), escapeRegExp = require("lodash/escapeRegExp.js"), resolve = require("resolve.exports"), cli = require("./cli.js"), fs$1 = require("node:fs/promises"), chokidar = require("chokidar"), jsxRuntime = require("react/jsx-runtime"), fs = require("node:fs"), node_worker_threads = require("node:worker_threads"), chalk = require("chalk"), importFresh = require("import-fresh"), nodeHtmlParser = require("node-html-parser"), server = require("react-dom/server"), reactCompilerRuntime = require("react-compiler-runtime"); function _interopDefaultCompat(e) { return e && typeof e == "object" && "default" in e ? e : { default: e }; } var path__default = /* @__PURE__ */ _interopDefaultCompat(path), debug__default = /* @__PURE__ */ _interopDefaultCompat(debug$4), readPkgUp__default = /* @__PURE__ */ _interopDefaultCompat(readPkgUp), escapeRegExp__default = /* @__PURE__ */ _interopDefaultCompat(escapeRegExp), resolve__default = /* @__PURE__ */ _interopDefaultCompat(resolve), fs__default$1 = /* @__PURE__ */ _interopDefaultCompat(fs$1), chokidar__default = /* @__PURE__ */ _interopDefaultCompat(chokidar), fs__default = /* @__PURE__ */ _interopDefaultCompat(fs), chalk__default = /* @__PURE__ */ _interopDefaultCompat(chalk), importFresh__default = /* @__PURE__ */ _interopDefaultCompat(importFresh); const debug$3 = debug__default.default("sanity:server"); function createExternalFromImportMap({ imports = {} } = {}) { return Object.keys(imports).map((specifier) => specifier.endsWith("/") ? new RegExp(`^${escapeRegExp__default.default(specifier)}.+`) : specifier); } const browserCompatibleSanityPackageSpecifiers = ["sanity", "sanity/_createContext", "sanity/_singletons", "sanity/desk", "sanity/presentation", "sanity/router", "sanity/structure", "sanity/media-library", "sanity/package.json"], conditions = ["import", "browser", "default"]; function getSanityPkgExportAliases(sanityPkgPath) { const pkg = require(sanityPkgPath), dirname = path__default.default.dirname(sanityPkgPath); return browserCompatibleSanityPackageSpecifiers.reduce((acc, next) => { const dest = resolve__default.default.exports(pkg, next, { browser: !0, conditions })?.[0]; return dest && acc.push({ find: new RegExp(`^${escapeRegExp__default.default(next)}$`), replacement: path__default.default.resolve(dirname, dest) }), acc; }, []); } function normalizeBasePath(pathName) { return `/${pathName}/`.replace(/^\/+/, "/").replace(/\/+$/, "/"); } async function getMonorepoAliases(monorepoPath) { const { default: aliases } = await Promise.resolve().then(function() { return require("./dev-aliases.js"); }).then(function(n) { return n.devAliases; }); return Object.fromEntries(Object.entries(aliases).map(([pkgName, pkgPath]) => [pkgName, path__default.default.resolve(monorepoPath, path__default.default.join("packages", pkgPath))])); } async function loadSanityMonorepo(cwd) { let p = cwd; for (; p !== "/"; ) { const readResult = await readPkgUp__default.default({ cwd: p }); if (!readResult) return; if (readResult.packageJson.isSanityMonorepo) return { path: path__default.default.dirname(readResult.path) }; p = path__default.default.dirname(path__default.default.dirname(readResult.path)); } } function Favicons() { const $ = reactCompilerRuntime.c(1); let t0; return $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [ /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "icon", href: "/static/favicon.ico", sizes: "any" }), /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "icon", href: "/static/favicon.svg", type: "image/svg+xml" }), /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "apple-touch-icon", href: "/static/apple-touch-icon.png" }), /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "manifest", href: "/static/manifest.webmanifest" }) ] }), $[0] = t0) : t0 = $[0], t0; } const errorHandlerScript = ` ;(function () { // The error channel is provided so that error handling can be delegated to a view component. // If there is a subscriber to the error channel at the time the error happens, the error will be pushed to the subscriber instead of handled here. var errorChannel = (function () { var subscribers = Object.create(null) var nextId = 0 function subscribe(subscriber) { var id = nextId++ subscribers[id] = subscriber return function unsubscribe() { delete subscribers[id] } } function publish(event) { for (var id in subscribers) { if (Object.hasOwn(subscribers, id)) { subscribers[id](event) } } } return { subscribers, publish, subscribe } })() // NOTE: Store the error channel instance in the global scope so that the Studio application can // access it and subscribe to errors. window.__sanityErrorChannel = { subscribe: errorChannel.subscribe } function _handleError(event) { // If there are error channel subscribers, then we assume they will own error rendering, // and we defer to them (no console error). if (Object.keys(errorChannel.subscribers).length > 0) { errorChannel.publish(event) } else { _renderErrorOverlay(event) } } var ERROR_BOX_STYLE = [ 'background: #fff', 'border-radius: 6px', 'box-sizing: border-box', 'color: #121923', 'flex: 1', "font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue','Liberation Sans',Helvetica,Arial,system-ui,sans-serif", 'font-size: 16px', 'line-height: 21px', 'margin: 0 auto', 'max-width: 960px', 'overflow: auto', 'padding: 20px', 'width: 100%', ].join(';') var ERROR_CODE_STYLE = [ 'color: #972E2A', "font-family: -apple-system-ui-monospace, 'SF Mono', Menlo, Monaco, Consolas, monospace", 'font-size: 13px', 'line-height: 17px', 'margin: 0', ].join(';') function _renderErrorOverlay(event) { var errorElement = document.querySelector('#__sanityError') || document.createElement('div') var error = event.error var colno = event.colno var lineno = event.lineno var filename = event.filename errorElement.id = '__sanityError' errorElement.innerHTML = [ '<div style="' + ERROR_BOX_STYLE + '">', '<div style="font-weight: 700;">Uncaught error: ' + error.message + '</div>', '<div style="color: #515E72; font-size: 13px; line-height: 17px; margin: 10px 0;">' + filename + ':' + lineno + ':' + colno + '</div>', '<pre style="' + ERROR_CODE_STYLE + '">' + error.stack + '</pre>', '</div>', ].join('') errorElement.style.position = 'fixed' errorElement.style.zIndex = 1000000 errorElement.style.top = 0 errorElement.style.left = 0 errorElement.style.right = 0 errorElement.style.bottom = 0 errorElement.style.padding = '20px' errorElement.style.background = 'rgba(16,17,18,0.66)' errorElement.style.display = 'flex' errorElement.style.alignItems = 'center' errorElement.style.justifyContent = 'center' document.body.appendChild(errorElement) } // Error listener window.addEventListener('error', (event) => { _handleError({ type: 'error', error: event.error, lineno: event.lineno, colno: event.colno, filename: event.filename }) }) // Error listener window.addEventListener('unhandledrejection', (event) => { _handleError({ type: 'rejection', error: event.reason }) }) })() `; function GlobalErrorHandler() { const $ = reactCompilerRuntime.c(1); let t0; return $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsxRuntime.jsx("script", { dangerouslySetInnerHTML: { __html: errorHandlerScript } }), $[0] = t0) : t0 = $[0], t0; } const NoJsStyles = ` .sanity-app-no-js__root { position: absolute; top: 0; right: 0; left: 0; bottom: 0; background: #fff; } .sanity-app-no-js__content { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; font-family: helvetica, arial, sans-serif; } `; function NoJavascript() { const $ = reactCompilerRuntime.c(3); let t0, t1; $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsxRuntime.jsx("style", { type: "text/css", children: NoJsStyles }), t1 = /* @__PURE__ */ jsxRuntime.jsx("h1", { children: "JavaScript disabled" }), $[0] = t0, $[1] = t1) : (t0 = $[0], t1 = $[1]); let t2; return $[2] === Symbol.for("react.memo_cache_sentinel") ? (t2 = /* @__PURE__ */ jsxRuntime.jsx("noscript", { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sanity-app-no-js__root", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sanity-app-no-js__content", children: [ t0, t1, /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [ "Please ", /* @__PURE__ */ jsxRuntime.jsx("a", { href: "https://www.enable-javascript.com/", children: "enable JavaScript" }), " in your browser and reload the page to proceed." ] }) ] }) }) }), $[2] = t2) : t2 = $[2], t2; } const EMPTY_ARRAY$1 = []; function BasicDocument(props) { const $ = reactCompilerRuntime.c(20), { entryPath, css: t0 } = props, css = t0 === void 0 ? EMPTY_ARRAY$1 : t0; let t1, t2, t3, t4, t5, t6, t7; $[0] === Symbol.for("react.memo_cache_sentinel") ? (t1 = /* @__PURE__ */ jsxRuntime.jsx("meta", { charSet: "utf-8" }), t2 = /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1, viewport-fit=cover" }), t3 = /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "robots", content: "noindex" }), t4 = /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "referrer", content: "same-origin" }), t5 = /* @__PURE__ */ jsxRuntime.jsx(Favicons, {}), t6 = /* @__PURE__ */ jsxRuntime.jsx("title", { children: "Sanity Custom App" }), t7 = /* @__PURE__ */ jsxRuntime.jsx(GlobalErrorHandler, {}), $[0] = t1, $[1] = t2, $[2] = t3, $[3] = t4, $[4] = t5, $[5] = t6, $[6] = t7) : (t1 = $[0], t2 = $[1], t3 = $[2], t4 = $[3], t5 = $[4], t6 = $[5], t7 = $[6]); let t8; $[7] !== css ? (t8 = css.map(_temp$1), $[7] = css, $[8] = t8) : t8 = $[8]; let t9; $[9] !== t8 ? (t9 = /* @__PURE__ */ jsxRuntime.jsxs("head", { children: [ t1, t2, t3, t4, t5, t6, t7, t8 ] }), $[9] = t8, $[10] = t9) : t9 = $[10]; let t10; $[11] === Symbol.for("react.memo_cache_sentinel") ? (t10 = /* @__PURE__ */ jsxRuntime.jsx("div", { id: "root" }), $[11] = t10) : t10 = $[11]; let t11; $[12] !== entryPath ? (t11 = /* @__PURE__ */ jsxRuntime.jsx("script", { type: "module", src: entryPath }), $[12] = entryPath, $[13] = t11) : t11 = $[13]; let t12; $[14] === Symbol.for("react.memo_cache_sentinel") ? (t12 = /* @__PURE__ */ jsxRuntime.jsx(NoJavascript, {}), $[14] = t12) : t12 = $[14]; let t13; $[15] !== t11 ? (t13 = /* @__PURE__ */ jsxRuntime.jsxs("body", { children: [ t10, t11, t12 ] }), $[15] = t11, $[16] = t13) : t13 = $[16]; let t14; return $[17] !== t13 || $[18] !== t9 ? (t14 = /* @__PURE__ */ jsxRuntime.jsxs("html", { lang: "en", children: [ t9, t13 ] }), $[17] = t13, $[18] = t9, $[19] = t14) : t14 = $[19], t14; } function _temp$1(href) { return /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "stylesheet", href }, href); } const globalStyles = ` @font-face { font-family: Inter; font-style: normal; font-weight: 400; font-display: swap; src: url("https://studio-static.sanity.io/Inter-Regular.woff2") format("woff2"); } @font-face { font-family: Inter; font-style: italic; font-weight: 400; font-display: swap; src: url("https://studio-static.sanity.io/Inter-Italic.woff2") format("woff2"); } @font-face { font-family: Inter; font-style: normal; font-weight: 500; font-display: swap; src: url("https://studio-static.sanity.io/Inter-Medium.woff2") format("woff2"); } @font-face { font-family: Inter; font-style: italic; font-weight: 500; font-display: swap; src: url("https://studio-static.sanity.io/Inter-MediumItalic.woff2") format("woff2"); } @font-face { font-family: Inter; font-style: normal; font-weight: 600; font-display: swap; src: url("https://studio-static.sanity.io/Inter-SemiBold.woff2") format("woff2"); } @font-face { font-family: Inter; font-style: italic; font-weight: 600; font-display: swap; src: url("https://studio-static.sanity.io/Inter-SemiBoldItalic.woff2") format("woff2"); } @font-face { font-family: Inter; font-style: normal; font-weight: 700; font-display: swap; src: url("https://studio-static.sanity.io/Inter-Bold.woff2") format("woff2"); } @font-face { font-family: Inter; font-style: italic; font-weight: 700; font-display: swap; src: url("https://studio-static.sanity.io/Inter-BoldItalic.woff2") format("woff2"); } @font-face { font-family: Inter; font-style: normal; font-weight: 800; font-display: swap; src: url("https://studio-static.sanity.io/Inter-ExtraBold.woff2") format("woff2"); } @font-face { font-family: Inter; font-style: italic; font-weight: 800; font-display: swap; src: url("https://studio-static.sanity.io/Inter-ExtraBoldItalic.woff2") format("woff2"); } @font-face { font-family: Inter; font-style: normal; font-weight: 900; font-display: swap; src: url("https://studio-static.sanity.io/Inter-Black.woff2") format("woff2"); } @font-face { font-family: Inter; font-style: italic; font-weight: 900; font-display: swap; src: url("https://studio-static.sanity.io/Inter-BlackItalic.woff2") format("woff2"); } html { @media (prefers-color-scheme: dark) { background-color: #13141b; } @media (prefers-color-scheme: light) { background-color: #ffffff; } } html, body, #sanity { height: 100%; } body { margin: 0; -webkit-font-smoothing: antialiased; } `, EMPTY_ARRAY = []; function DefaultDocument(props) { const $ = reactCompilerRuntime.c(21), { entryPath, css: t0 } = props, css = t0 === void 0 ? EMPTY_ARRAY : t0; let t1, t2, t3, t4, t5, t6, t7; $[0] === Symbol.for("react.memo_cache_sentinel") ? (t1 = /* @__PURE__ */ jsxRuntime.jsx("meta", { charSet: "utf-8" }), t2 = /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover" }), t3 = /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "robots", content: "noindex" }), t4 = /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "referrer", content: "same-origin" }), t5 = /* @__PURE__ */ jsxRuntime.jsx(Favicons, {}), t6 = /* @__PURE__ */ jsxRuntime.jsx("title", { children: "Sanity Studio" }), t7 = /* @__PURE__ */ jsxRuntime.jsx(GlobalErrorHandler, {}), $[0] = t1, $[1] = t2, $[2] = t3, $[3] = t4, $[4] = t5, $[5] = t6, $[6] = t7) : (t1 = $[0], t2 = $[1], t3 = $[2], t4 = $[3], t5 = $[4], t6 = $[5], t7 = $[6]); let t8; $[7] !== css ? (t8 = css.map(_temp), $[7] = css, $[8] = t8) : t8 = $[8]; let t9; $[9] === Symbol.for("react.memo_cache_sentinel") ? (t9 = /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: globalStyles } }), $[9] = t9) : t9 = $[9]; let t10; $[10] !== t8 ? (t10 = /* @__PURE__ */ jsxRuntime.jsxs("head", { children: [ t1, t2, t3, t4, t5, t6, t7, t8, t9 ] }), $[10] = t8, $[11] = t10) : t10 = $[11]; let t11; $[12] === Symbol.for("react.memo_cache_sentinel") ? (t11 = /* @__PURE__ */ jsxRuntime.jsx("div", { id: "sanity" }), $[12] = t11) : t11 = $[12]; let t12; $[13] !== entryPath ? (t12 = /* @__PURE__ */ jsxRuntime.jsx("script", { type: "module", src: entryPath }), $[13] = entryPath, $[14] = t12) : t12 = $[14]; let t13; $[15] === Symbol.for("react.memo_cache_sentinel") ? (t13 = /* @__PURE__ */ jsxRuntime.jsx(NoJavascript, {}), $[15] = t13) : t13 = $[15]; let t14; $[16] !== t12 ? (t14 = /* @__PURE__ */ jsxRuntime.jsxs("body", { children: [ t11, t12, t13 ] }), $[16] = t12, $[17] = t14) : t14 = $[17]; let t15; return $[18] !== t10 || $[19] !== t14 ? (t15 = /* @__PURE__ */ jsxRuntime.jsxs("html", { lang: "en", children: [ t10, t14 ] }), $[18] = t10, $[19] = t14, $[20] = t15) : t15 = $[20], t15; } function _temp(href) { return /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "stylesheet", href }, href); } const TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT = `<script> // auto-generated script to add import map with timestamp const importsJson = document.getElementById('__imports')?.textContent; const { imports = {}, ...rest } = importsJson ? JSON.parse(importsJson) : {}; const importMapEl = document.createElement('script'); importMapEl.type = 'importmap'; const newTimestamp = \`/t\${Math.floor(Date.now() / 1000)}\`; importMapEl.textContent = JSON.stringify({ imports: Object.fromEntries( Object.entries(imports).map(([specifier, path]) => { try { const url = new URL(path); if (/^sanity-cdn\\.[a-zA-Z]+$/.test(url.hostname)) { url.pathname = url.pathname.replace(/\\/t\\d+/, newTimestamp); } return [specifier, url.toString()]; } catch { return [specifier, path]; } }) ), ...rest, }); document.head.appendChild(importMapEl); <\/script>`, debug$2 = debug$3.extend("renderDocument"), useThreads = typeof process.env.JEST_WORKER_ID > "u", hasWarnedAbout = /* @__PURE__ */ new Set(), defaultProps = { entryPath: "./.sanity/runtime/app.js" }, autoGeneratedWarning = ` This file is auto-generated from "sanity dev". Modifications to this file are automatically discarded. `.trim(); function renderDocument(options) { return new Promise((resolve2, reject) => { if (!useThreads) { resolve2(getDocumentHtml(options.studioRootPath, options.props, options.importMap, options.isApp)); return; } debug$2("Starting worker thread for %s", __filename); const worker = new node_worker_threads.Worker(__filename, { execArgv: void 0, workerData: { ...options, dev: !1, shouldWarn: !0 }, env: process.env }); worker.on("message", (msg) => { if (msg.type === "warning") { if (hasWarnedAbout.has(msg.warnKey)) return; Array.isArray(msg.message) ? msg.message.forEach((warning) => console.warn(`${chalk__default.default.yellow("[warn]")} ${warning}`)) : console.warn(`${chalk__default.default.yellow("[warn]")} ${msg.message}`), hasWarnedAbout.add(msg.warnKey); return; } if (msg.type === "error") { debug$2("Error from worker: %s", msg.error || "Unknown error"), reject(new Error(msg.error || "Document rendering worker stopped with an unknown error")); return; } msg.type === "result" && (debug$2("Document HTML rendered, %d bytes", msg.html.length), resolve2(msg.html)); }), worker.on("error", (err) => { debug$2("Worker errored: %s", err.message), reject(err); }), worker.on("exit", (code) => { code !== 0 && (debug$2("Worker stopped with code %d", code), reject(new Error(`Document rendering worker stopped with exit code ${code}`))); }); }); } function decorateIndexWithAutoGeneratedWarning(template) { return template.replace(/<head/, ` <!-- ${autoGeneratedWarning} --> <head`); } function decorateIndexWithBridgeScript(template) { const scriptURL = (process.env.SANITY_INTERNAL_ENV || "production") === "production" ? "https://core.sanity-cdn.com/bridge.js" : "https://core.sanity-cdn.work/bridge.js"; return template.replace("</head>", `<script src="${scriptURL}" async type="module" data-sanity-core><\/script> </head>`); } function getPossibleDocumentComponentLocations(studioRootPath) { return [path__default.default.join(studioRootPath, "_document.js"), path__default.default.join(studioRootPath, "_document.tsx")]; } function _prefixUrlWithBasePath(url, basePath) { const normalizedBasePath = basePath.startsWith("/") ? basePath : `/${basePath}`; return url.startsWith("/") ? normalizedBasePath.endsWith("/") ? `${normalizedBasePath.slice(0, -1)}${url}` : `${normalizedBasePath}${url}` : normalizedBasePath.endsWith("/") ? `${normalizedBasePath}${url}` : `${normalizedBasePath}/${url}`; } !node_worker_threads.isMainThread && node_worker_threads.parentPort && renderDocumentFromWorkerData(); async function renderDocumentFromWorkerData() { if (!node_worker_threads.parentPort || !node_worker_threads.workerData) throw new Error("Must be used as a Worker with a valid options object in worker data"); const { monorepo, studioRootPath, props, importMap, isApp } = node_worker_threads.workerData || {}; if (node_worker_threads.workerData?.dev && (global.__DEV__ = !0), typeof studioRootPath != "string") { node_worker_threads.parentPort.postMessage({ type: "error", message: "Missing/invalid `studioRootPath` option" }); return; } if (props && typeof props != "object") { node_worker_threads.parentPort.postMessage({ type: "error", message: "`props` must be an object if provided" }); return; } debug$2("Registering potential aliases"), monorepo && require("module-alias").addAliases(getMonorepoAliases(monorepo.path)), debug$2("Registering esbuild for node %s", process.version); const { unregister } = require("esbuild-register/dist/node").register({ target: `node${process.version.slice(1)}`, supported: { "dynamic-import": !0 }, jsx: "automatic", extensions: [".jsx", ".ts", ".tsx", ".mjs"] }); debug$2("Registering esbuild for .js files using jsx loader"); const { unregister: unregisterJs } = require("esbuild-register/dist/node").register({ target: `node${process.version.slice(1)}`, supported: { "dynamic-import": !0 }, extensions: [".js"], jsx: "automatic", loader: "jsx" }), html = getDocumentHtml(studioRootPath, props, importMap, isApp); node_worker_threads.parentPort.postMessage({ type: "result", html }), unregister(), unregisterJs(); } function getDocumentHtml(studioRootPath, props, importMap, isApp) { const Document = getDocumentComponent(studioRootPath, isApp), css = props?.css?.map((url) => { try { return new URL(url).toString(); } catch { return _prefixUrlWithBasePath(url, props.basePath); } }); return debug$2("Rendering document component using React"), `<!DOCTYPE html>${addTimestampedImportMapScriptToHtml(server.renderToStaticMarkup(/* @__PURE__ */ jsxRuntime.jsx(Document, { ...defaultProps, ...props, css })), importMap)}`; } function addTimestampedImportMapScriptToHtml(html, importMap) { if (!importMap) return html; let root = nodeHtmlParser.parse(html), htmlEl = root.querySelector("html"); if (!htmlEl) { const oldRoot = root; root = nodeHtmlParser.parse("<html></html>"), htmlEl = root.querySelector("html"), htmlEl.appendChild(oldRoot); } let headEl = htmlEl.querySelector("head"); return headEl || (htmlEl.insertAdjacentHTML("afterbegin", "<head></head>"), headEl = root.querySelector("head")), headEl.insertAdjacentHTML("beforeend", `<script type="application/json" id="__imports">${JSON.stringify(importMap)}<\/script>`), headEl.insertAdjacentHTML("beforeend", TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT), root.outerHTML; } function getDocumentComponent(studioRootPath, isApp) { debug$2("Loading default document component from `sanity` module"); const Document = isApp ? BasicDocument : DefaultDocument; debug$2("Attempting to load user-defined document component from %s", studioRootPath); const userDefined = tryLoadDocumentComponent(studioRootPath); if (!userDefined) return debug$2("Using default document component"), Document; debug$2("Found user defined document component at %s", userDefined.path); const DocumentComp = userDefined.component.default || userDefined.component; if (typeof DocumentComp == "function") return debug$2("User defined document component is a function, assuming valid"), DocumentComp; debug$2("User defined document component did not have a default export"); const userExports = Object.keys(userDefined.component).join(", ") || "None", relativePath = path__default.default.relative(process.cwd(), userDefined.path), typeHint = typeof userDefined.component.default > "u" ? "" : ` (type was ${typeof userDefined.component.default})`, warnKey = `${relativePath}/${userDefined.modified}`; return node_worker_threads.parentPort?.postMessage({ type: "warning", message: [`${relativePath} did not have a default export that is a React component${typeHint}`, `Named exports/properties found: ${userExports}`.trim(), 'Using default document component from "sanity".'], warnKey }), DefaultDocument; } function tryLoadDocumentComponent(studioRootPath) { const locations = getPossibleDocumentComponentLocations(studioRootPath); for (const componentPath of locations) { debug$2("Trying to load document component from %s", componentPath); try { return { component: importFresh__default.default(componentPath), path: componentPath, modified: Math.floor(fs__default.default.statSync(componentPath)?.mtimeMs) }; } catch (err) { if (err.code !== "MODULE_NOT_FOUND") throw debug$2("Failed to load document component: %s", err.message), err; debug$2("Document component not found at %s", componentPath); } } return null; } const entryChunkId = ".sanity/runtime/app.js"; function sanityBuildEntries(options) { const { cwd, monorepo, basePath, importMap, isApp } = options; return { name: "sanity/server/build-entries", apply: "build", buildStart() { this.emitFile({ type: "chunk", id: entryChunkId, name: "sanity" }); }, async generateBundle(_options, outputBundle) { const bundle = outputBundle, entryFile = Object.values(bundle).find((file) => file.type === "chunk" && file.name === "sanity" && file.facadeModuleId?.endsWith(entryChunkId)); if (!entryFile) throw new Error(`Failed to find entry file in bundle (${entryChunkId})`); if (entryFile.type !== "chunk") throw new Error("Entry file is not a chunk"); const entryFileName = entryFile.fileName, entryPath = [basePath.replace(/\/+$/, ""), entryFileName].join("/"); let css = []; if (entryFile.viteMetadata?.importedCss) { css = [...entryFile.viteMetadata.importedCss]; for (const key of entryFile.imports) { const entry = bundle[key], importedCss = entry && entry.type === "chunk" ? entry.viteMetadata.importedCss : void 0; importedCss && css.push(...importedCss); } } this.emitFile({ type: "asset", fileName: "index.html", source: decorateIndexWithBridgeScript(await renderDocument({ monorepo, studioRootPath: cwd, importMap, props: { basePath, entryPath, css }, isApp })) }); } }; } function generateWebManifest(basePath) { return { icons: [{ src: `${basePath}/favicon-96.png`, type: "image/png", sizes: "96x96" }, { src: `${basePath}/favicon-192.png`, type: "image/png", sizes: "192x192" }, { src: `${basePath}/favicon-512.png`, type: "image/png", sizes: "512x512" }] }; } const mimeTypes = { ".ico": "image/x-icon", ".svg": "image/svg+xml", ".png": "image/png" }; function sanityFaviconsPlugin({ defaultFaviconsPath, customFaviconsPath, staticUrlPath }) { const cache = {}; async function getFavicons() { return cache.favicons || (cache.favicons = await fs__default$1.default.readdir(defaultFaviconsPath)), cache.favicons; } async function hasCustomFavicon(fileName) { try { return await fs__default$1.default.access(path__default.default.join(customFaviconsPath, fileName)), !0; } catch { return !1; } } return { name: "sanity/server/sanity-favicons", apply: "serve", configureServer(viteDevServer) { const webManifest = JSON.stringify(generateWebManifest(staticUrlPath), null, 2), webManifestPath = `${staticUrlPath}/manifest.webmanifest`; viteDevServer.middlewares.use(async (req, res, next) => { if (req.url?.endsWith(webManifestPath)) { res.writeHead(200, "OK", { "content-type": "application/manifest+json" }), res.write(webManifest), res.end(); return; } const pathName = (req._parsedUrl || new URL(req.url || "/", "http://localhost:3333")).pathname || "", fileName = path__default.default.basename(pathName || ""), icons = await getFavicons(); if (!(pathName.startsWith("/favicon.ico") || icons.includes(fileName) && pathName.includes(staticUrlPath))) { next(); return; } const faviconPath = await hasCustomFavicon(fileName) ? path__default.default.join(customFaviconsPath, fileName) : path__default.default.join(defaultFaviconsPath, fileName), mimeType = mimeTypes[path__default.default.extname(fileName)] || "application/octet-stream"; res.writeHead(200, "OK", { "content-type": mimeType }), res.write(await fs__default$1.default.readFile(faviconPath)), res.end(); }); } }; } function sanityRuntimeRewritePlugin() { return { name: "sanity/server/sanity-runtime-rewrite", apply: "serve", configureServer(viteDevServer) { return () => { viteDevServer.middlewares.use((req, res, next) => { req.url === "/index.html" && (req.url = "/.sanity/runtime/index.html"), next(); }); }; } }; } async function getViteConfig(options) { const { cwd, mode, outputDir, // default to `true` when `mode=development` sourceMap = options.mode === "development", server: server2, minify, basePath: rawBasePath = "/", importMap, reactCompiler, isApp } = options, monorepo = await loadSanityMonorepo(cwd), basePath = normalizeBasePath(rawBasePath), sanityPkgPath = (await readPkgUp__default.default({ cwd: __dirname }))?.path; if (!sanityPkgPath) throw new Error("Unable to resolve `sanity` module root"); const customFaviconsPath = path__default.default.join(cwd, "static"), defaultFaviconsPath = path__default.default.join(path__default.default.dirname(sanityPkgPath), "static", "favicons"), staticPath = `${basePath}static`, { default: viteReact } = await import("@vitejs/plugin-react"), envVars = isApp ? cli.getAppEnvironmentVariables({ prefix: "process.env.", jsonEncode: !0 }) : cli.getStudioEnvironmentVariables({ prefix: "process.env.", jsonEncode: !0 }), viteConfig = { // Define a custom cache directory so that sanity's vite cache // does not conflict with any potential local vite projects cacheDir: "node_modules/.sanity/vite", root: cwd, base: basePath, build: { outDir: outputDir || path__default.default.resolve(cwd, "dist"), sourcemap: sourceMap }, server: { host: server2?.host, port: server2?.port || 3333, // Only enable strict port for studio, // since apps can run on any port strictPort: !isApp, /** * Significantly speed up startup time, * and most importantly eliminates the `new dependencies optimized: foobar. optimized dependencies changed. reloading` * types of initial reload loops that otherwise happen as vite discovers deps that need to be optimized. * This option starts the traversal up front, and warms up the dep tree required to render the userland sanity.config.ts file, * and thus avoids frustrating reload loops. */ warmup: { clientFiles: ["./.sanity/runtime/app.js"] } }, configFile: !1, mode, plugins: [viteReact(reactCompiler ? { babel: { plugins: [["babel-plugin-react-compiler", reactCompiler]] } } : {}), sanityFaviconsPlugin({ defaultFaviconsPath, customFaviconsPath, staticUrlPath: staticPath }), sanityRuntimeRewritePlugin(), sanityBuildEntries({ basePath, cwd, monorepo, importMap, isApp })], envPrefix: isApp ? "SANITY_APP_" : "SANITY_STUDIO_", logLevel: mode === "production" ? "silent" : "info", resolve: { alias: monorepo?.path ? await getMonorepoAliases(monorepo.path) : getSanityPkgExportAliases(sanityPkgPath), dedupe: ["styled-components"] }, define: { __SANITY_STAGING__: process.env.SANITY_INTERNAL_ENV === "staging", "process.env.MODE": JSON.stringify(mode), /** * Yes, double negatives are confusing. * The default value of `SC_DISABLE_SPEEDY` is `process.env.NODE_ENV === 'production'`: https://github.com/styled-components/styled-components/blob/99c02f52d69e8e509c0bf012cadee7f8e819a6dd/packages/styled-components/src/constants.ts#L34 * Which means that in production, use the much faster way of inserting CSS rules, based on the CSSStyleSheet API (https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule) * while in dev mode, use the slower way of inserting CSS rules, which appends text nodes to the `<style>` tag: https://github.com/styled-components/styled-components/blob/99c02f52d69e8e509c0bf012cadee7f8e819a6dd/packages/styled-components/src/sheet/Tag.ts#L74-L76 * There are historical reasons for this, primarily that browsers initially did not support editing CSS rules in the DevTools inspector if `CSSStyleSheet.insetRule` were used. * However, that's no longer the case (since Chrome 81 back in April 2020: https://developer.chrome.com/docs/css-ui/css-in-js), the latest version of FireFox also supports it, * and there is no longer any reason to use the much slower method in dev mode. */ "process.env.SC_DISABLE_SPEEDY": JSON.stringify("false"), ...envVars } }; return mode === "production" && (viteConfig.build = { ...viteConfig.build, assetsDir: "static", minify: minify ? "esbuild" : !1, emptyOutDir: !1, // Rely on CLI to do this rollupOptions: { onwarn: onRollupWarn, external: createExternalFromImportMap(importMap), input: { sanity: path__default.default.join(cwd, ".sanity", "runtime", "app.js") } } }), viteConfig; } function onRollupWarn(warning, warn) { suppressUnusedImport(warning) || warn(warning); } function suppressUnusedImport(warning) { return warning.code !== "UNUSED_EXTERNAL_IMPORT" ? !1 : !!(warning.names?.includes("useDebugValue") && (warning.names = warning.names.filter((n) => n !== "useDebugValue"), warning.names.length === 0) || warning.ids?.every((id) => id.includes("/node_modules/"))); } async function finalizeViteConfig(config) { if (typeof config.build?.rollupOptions?.input != "object") throw new Error("Vite config must contain `build.rollupOptions.input`, and it must be an object"); if (!config.root) throw new Error("Vite config must contain `root` property, and must point to the Sanity root directory"); const { mergeConfig } = await import("vite"); return mergeConfig(config, { build: { rollupOptions: { input: { sanity: path__default.default.join(config.root, ".sanity", "runtime", "app.js") } } } }); } async function extendViteConfigWithUserConfig(env, defaultConfig, userConfig) { let config = defaultConfig; if (typeof userConfig == "function") debug__default.default("Extending vite config using user-specified function"), config = await userConfig(config, env); else if (typeof userConfig == "object") { debug__default.default("Merging vite config using user-specified object"); const { mergeConfig } = await import("vite"); config = mergeConfig(config, userConfig); } return config; } const entryModule = ` // This file is auto-generated on 'sanity dev' // Modifications to this file is automatically discarded import studioConfig from %STUDIO_CONFIG_LOCATION% import {renderStudio} from "sanity" renderStudio( document.getElementById("sanity"), studioConfig, {reactStrictMode: %STUDIO_REACT_STRICT_MODE%, basePath: %STUDIO_BASE_PATH%} ) `, noConfigEntryModule = ` // This file is auto-generated on 'sanity dev' // Modifications to this file is automatically discarded import {renderStudio} from "sanity" const studioConfig = {missingConfigFile: true} renderStudio( document.getElementById("sanity"), studioConfig, {reactStrictMode: %STUDIO_REACT_STRICT_MODE%, basePath: %STUDIO_BASE_PATH%} ) `, appEntryModule = ` // This file is auto-generated on 'sanity dev' // Modifications to this file is automatically discarded import {createRoot} from 'react-dom/client' import {createElement} from 'react' import App from %ENTRY% const root = createRoot(document.getElementById('root')) const element = createElement(App) root.render(element) `; function getEntryModule(options) { const { reactStrictMode, relativeConfigLocation, basePath, entry, isApp } = options; return isApp ? appEntryModule.replace(/%ENTRY%/, JSON.stringify(entry || "./src/App")) : (relativeConfigLocation ? entryModule : noConfigEntryModule).replace(/%STUDIO_REACT_STRICT_MODE%/, JSON.stringify(!!reactStrictMode)).replace(/%STUDIO_CONFIG_LOCATION%/, JSON.stringify(relativeConfigLocation)).replace(/%STUDIO_BASE_PATH%/, JSON.stringify(basePath || "/")); } const debug$1 = debug$3.extend("config"); async function getSanityStudioConfigPath(studioRootPath) { const configPaths = [path__default.default.join(studioRootPath, "sanity.config.mjs"), path__default.default.join(studioRootPath, "sanity.config.js"), path__default.default.join(studioRootPath, "sanity.config.ts"), path__default.default.join(studioRootPath, "sanity.config.jsx"), path__default.default.join(studioRootPath, "sanity.config.tsx")]; debug$1("Looking for configuration file in %d possible locations", configPaths.length); const availableConfigs = (await Promise.all(configPaths.map(async (configPath) => ({ path: configPath, exists: await fileExists(configPath) })))).filter((config) => config.exists); return debug$1("Found %d available configuration files", availableConfigs.length), availableConfigs.length === 0 ? (console.warn("No `sanity.config.js`/`sanity.config.ts` found - using default studio config"), null) : (availableConfigs.length > 1 && (console.warn("Found multiple potential studio configs:"), availableConfigs.forEach((config) => console.warn(` - ${config.path}`)), console.warn(`Using ${availableConfigs[0].path}`)), availableConfigs[0].path); } function fileExists(filePath) { return fs__default$1.default.stat(filePath).then(() => !0, () => !1); } const debug = debug$3.extend("runtime"); async function writeSanityRuntime({ cwd, reactStrictMode, watch, basePath, entry, isApp }) { debug("Resolving Sanity monorepo information"); const monorepo = await loadSanityMonorepo(cwd), runtimeDir = path__default.default.join(cwd, ".sanity", "runtime"); debug("Making runtime directory"), await fs__default$1.default.mkdir(runtimeDir, { recursive: !0 }); async function renderAndWriteDocument() { debug("Rendering document template"); const indexHtml = decorateIndexWithBridgeScript(decorateIndexWithAutoGeneratedWarning(await renderDocument({ studioRootPath: cwd, monorepo, props: { entryPath: `/${path__default.default.relative(cwd, path__default.default.join(runtimeDir, "app.js"))}`, basePath: basePath || "/" }, isApp }))); debug("Writing index.html to runtime directory"), await fs__default$1.default.writeFile(path__default.default.join(runtimeDir, "index.html"), indexHtml); } watch && chokidar__default.default.watch(getPossibleDocumentComponentLocations(cwd)).on("all", () => renderAndWriteDocument()), await renderAndWriteDocument(), debug("Writing app.js to runtime directory"); let relativeConfigLocation = null; if (!isApp) { const studioConfigPath = await getSanityStudioConfigPath(cwd); relativeConfigLocation = studioConfigPath ? path__default.default.relative(runtimeDir, studioConfigPath) : null; } const relativeEntry = cwd ? path__default.default.resolve(cwd, entry || "./src/App") : entry, appJsContent = getEntryModule({ reactStrictMode, relativeConfigLocation, basePath, entry: relativeEntry, isApp }); await fs__default$1.default.writeFile(path__default.default.join(runtimeDir, "app.js"), appJsContent); } exports.createExternalFromImportMap = createExternalFromImportMap; exports.debug = debug$3; exports.extendViteConfigWithUserConfig = extendViteConfigWithUserConfig; exports.finalizeViteConfig = finalizeViteConfig; exports.generateWebManifest = generateWebManifest; exports.getViteConfig = getViteConfig; exports.writeSanityRuntime = writeSanityRuntime; //# sourceMappingURL=runtime.js.map