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

97 lines (80 loc) 3.4 kB
import {type SchemaValidationProblemGroup, type SchemaValidationProblemPath} from '@sanity/types' import chalk from 'chalk' import logSymbols from 'log-symbols' import {isatty} from 'tty' const isTty = isatty(1) const headers = { error: isTty ? chalk.bold(chalk.bgRed(chalk.black(' ERROR '))) : chalk.red('[ERROR]'), warning: isTty ? chalk.bold(chalk.bgYellow(chalk.black(' WARN '))) : chalk.yellow('[WARN]'), } const severityValues = {error: 0, warning: 1} function formatPath(pathSegments: SchemaValidationProblemPath) { const format = ( [curr, ...next]: SchemaValidationProblemPath, mode: 'object' | 'array' = 'object', ): string => { if (!curr) return '' if (curr.kind === 'property') return format(next, curr.name === 'of' ? 'array' : 'object') const name = curr.name ? curr.name : `<anonymous_${curr.type}>` return `${mode === 'array' ? `[${name}]` : `.${name}`}${format(next)}` } return format(pathSegments.slice(1)).substring(1) // removes the top-level type and leading `.` } export function getAggregatedSeverity( groupOrGroups: SchemaValidationProblemGroup | SchemaValidationProblemGroup[], ): 'error' | 'warning' { const groups = Array.isArray(groupOrGroups) ? groupOrGroups : [groupOrGroups] return groups .flatMap((group) => group.problems.map((problem) => problem.severity)) .find((severity) => severity === 'error') ? 'error' : 'warning' } export function formatSchemaValidation(validation: SchemaValidationProblemGroup[]): string { let unnamedTopLevelTypeCount = 0 const validationByType = Object.entries( validation.reduce<Record<string, SchemaValidationProblemGroup[]>>((acc, next) => { const [firstSegment] = next.path if (!firstSegment) return acc if (firstSegment.kind !== 'type') return acc const topLevelType = firstSegment.name || `<unnamed_${firstSegment.type}_type_${unnamedTopLevelTypeCount++}>` const problems = acc[topLevelType] ?? [] problems.push(next) acc[topLevelType] = problems return acc }, {}), ) const formatted = validationByType .sort((a, b) => { const [aType, aGroups] = a const [bType, bGroups] = b const aValue = severityValues[getAggregatedSeverity(aGroups)] const bValue = severityValues[getAggregatedSeverity(bGroups)] if (aValue === bValue) return aType.localeCompare(bType, 'en-US') return aValue - bValue }) .map(([topLevelType, groups]) => { const formattedTopLevelType = isTty ? chalk.bgWhite(chalk.black(` ${topLevelType} `)) : `[${topLevelType}]` const header = `${headers[getAggregatedSeverity(groups)]} ${formattedTopLevelType}` const body = groups .sort( (a, b) => severityValues[getAggregatedSeverity(a)] - severityValues[getAggregatedSeverity(b)], ) .map((group) => { const formattedPath = ` ${chalk.bold(formatPath(group.path) || '(root)')}` const formattedMessages = group.problems .sort((a, b) => severityValues[a.severity] - severityValues[b.severity]) .map(({severity, message}) => ` ${logSymbols[severity]} ${message}`) .join('\n') return `${formattedPath}\n${formattedMessages}` }) .join('\n') return `${header}\n${body}` }) .join('\n\n') return formatted }