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

129 lines (108 loc) 4.08 kB
import {type Stats} from 'node:fs' import {readFile, stat} from 'node:fs/promises' import path, {join, resolve} from 'node:path' import {type CliOutputter} from '@sanity/cli' import chalk from 'chalk' import {type CreateManifest, type ManifestSchemaType} from '../../../../manifest/manifestTypes' import {MANIFEST_FILENAME} from '../../manifest/extractManifestAction' import {type DeploySchemasFlags} from '../deploySchemasAction' export type ManifestJsonReader = <T>( filePath: string, ) => Promise<JsonFileParseSuccess<T> | undefined> export type CreateManifestReaderFactory = (args: { manifestDir: string output: CliOutputter jsonReader?: <T>(filePath: string) => Promise<JsonFileParseSuccess<T> | undefined> }) => CreateManifestReader export interface CreateManifestReader { getManifest: () => Promise<CreateManifest> getWorkspaceSchema: (workspaceName: string) => Promise<ManifestSchemaType[]> } interface JsonFileParseSuccess<T> { parsedJson: T path: string lastModified: string } /** * The manifest reader will try to read manifest and workspace schema files _once_ and cache a successful result. * If you need to re-read the manifest from disk, create a new instance. */ export const createManifestReader: CreateManifestReaderFactory = ({ manifestDir, output, jsonReader = parseJsonFile, }) => { let parsedManifest: JsonFileParseSuccess<CreateManifest> const parsedWorkspaces: Record<string, JsonFileParseSuccess<ManifestSchemaType[]> | undefined> = {} const getManifest: CreateManifestReader['getManifest'] = async () => { if (parsedManifest) { return parsedManifest?.parsedJson } const manifestFile = path.join(manifestDir, MANIFEST_FILENAME) const result = await jsonReader<CreateManifest>(manifestFile) if (!result) { throw new Error( `Manifest does not exist at ${manifestFile}. To create the manifest file, omit --no-${'extract-manifest' satisfies keyof DeploySchemasFlags} or run "sanity manifest extract" first.`, ) } output.print( chalk.gray(`↳ Read manifest from ${manifestFile} (last modified: ${result.lastModified})`), ) parsedManifest = result return result.parsedJson } const getWorkspaceSchema: CreateManifestReader['getWorkspaceSchema'] = async (workspaceName) => { if (parsedWorkspaces[workspaceName]) { return parsedWorkspaces[workspaceName]?.parsedJson } const manifest = await getManifest() if (!manifest) { throw Error('Manifest is required to read workspace schema.') } const workspaceManifest = manifest.workspaces.find( (workspace) => workspace.name === workspaceName, ) if (!workspaceManifest) { throw Error(`No workspace named "${workspaceName}" found in manifest.`) } const workspaceSchemaFile = path.join(manifestDir, workspaceManifest.schema) const result = await jsonReader<ManifestSchemaType[]>(workspaceSchemaFile) if (!result) { throw Error(`Workspace schema file at "${workspaceSchemaFile}" does not exist.`) } parsedWorkspaces[workspaceName] = result return result.parsedJson } return { getManifest, getWorkspaceSchema, } } export function resolveManifestDirectory(workDir: string, customPath?: string): string { const defaultOutputDir = resolve(join(workDir, 'dist')) const outputDir = resolve(defaultOutputDir) const defaultStaticPath = join(outputDir, 'static') const staticPath = customPath ?? defaultStaticPath return path.resolve(process.cwd(), staticPath) } async function parseJsonFile<T>(filePath: string): Promise<JsonFileParseSuccess<T> | undefined> { let stats: Stats try { stats = await stat(filePath) } catch (err) { // file does not exist return undefined } const content = await readFile(filePath, 'utf-8') const lastModified = stats.mtime.toISOString() const json = JSON.parse(content) as T if (!json) { throw new Error(`JSON file "${filePath}" was empty.`) } return { parsedJson: json, path: filePath, lastModified, } }