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
98 lines (84 loc) • 2.88 kB
text/typescript
import fs from 'fs/promises'
import path from 'path'
import {type Plugin} from 'vite'
import {generateWebManifest} from '../webManifest'
const mimeTypes: Record<string, string | undefined> = {
'.ico': 'image/x-icon',
'.svg': 'image/svg+xml',
'.png': 'image/png',
}
/**
* Fallback favicons plugin for Sanity.
*
* If a favicon is not found in the static folder, this plugin will serve the default
* Sanity favicons from the npm bundle. If a custom `favicon.ico` is found in the static
* folder, it will also be served for a root `/favicon.ico` request.
*
* @param options - Options for the plugin
* @returns A Vite plugin
* @internal
*/
export function sanityFaviconsPlugin({
defaultFaviconsPath,
customFaviconsPath,
staticUrlPath,
}: {
defaultFaviconsPath: string
customFaviconsPath: string
staticUrlPath: string
}): Plugin {
const cache: {favicons?: string[]} = {}
async function getFavicons(): Promise<string[]> {
if (cache.favicons) {
return cache.favicons
}
cache.favicons = await fs.readdir(defaultFaviconsPath)
return cache.favicons
}
async function hasCustomFavicon(): Promise<boolean> {
try {
await fs.access(path.join(customFaviconsPath, 'favicon.ico'))
return true
} catch (err) {
return false
}
}
return {
name: 'sanity/server/sanity-favicons',
apply: 'serve',
configureServer(viteDevServer) {
const webManifest = JSON.stringify(generateWebManifest(staticUrlPath), null, 2)
const webManifestPath = `${staticUrlPath}/manifest.webmanifest`
return () => {
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 parsedUrl =
((req as any)._parsedUrl as URL) || new URL(req.url || '/', 'http://localhost:3333')
const pathName = parsedUrl.pathname || ''
const fileName = path.basename(pathName || '')
const icons = await getFavicons()
const isIconRequest =
pathName.startsWith('/favicon.ico') ||
(icons.includes(fileName) && pathName.includes(staticUrlPath))
if (!isIconRequest) {
next()
return
}
const faviconPath =
fileName === 'favicon.ico' && (await hasCustomFavicon())
? path.join(customFaviconsPath, 'favicon.ico')
: path.join(defaultFaviconsPath, fileName)
const mimeType = mimeTypes[path.extname(fileName)] || 'application/octet-stream'
res.writeHead(200, 'OK', {'content-type': mimeType})
res.write(await fs.readFile(faviconPath))
res.end()
})
}
},
}
}