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
122 lines (102 loc) • 3.34 kB
text/typescript
import {type CliCommandArguments, type CliCommandContext} from '@sanity/cli'
import logSymbols from 'log-symbols'
import path from 'path'
import readPkgUp from 'read-pkg-up'
import {Worker} from 'worker_threads'
import {
type ValidateSchemaWorkerData,
type ValidateSchemaWorkerResult,
} from '../../threads/validateSchema'
import {formatSchemaValidation, getAggregatedSeverity} from './formatSchemaValidation'
interface ValidateFlags {
workspace?: string
format?: string
level?: 'error' | 'warning'
}
export type SchemaValidationFormatter = (result: ValidateSchemaWorkerResult) => string
export default async function validateAction(
args: CliCommandArguments<ValidateFlags>,
{workDir, output}: CliCommandContext,
): Promise<void> {
const flags = args.extOptions
const rootPkgPath = readPkgUp.sync({cwd: __dirname})?.path
if (!rootPkgPath) {
throw new Error('Could not find root directory for `sanity` package')
}
const workerPath = path.join(
path.dirname(rootPkgPath),
'lib',
'_internal',
'cli',
'threads',
'validateSchema.js',
)
const level = flags.level || 'warning'
if (level !== 'error' && level !== 'warning') {
throw new Error(`Invalid level. Available levels are 'error' and 'warning'.`)
}
const format = flags.format || 'pretty'
if (!['pretty', 'ndjson', 'json'].includes(format)) {
throw new Error(
`Did not recognize format '${flags.format}'. Available formats are 'pretty', 'ndjson', and 'json'.`,
)
}
let spinner
if (format === 'pretty') {
spinner = output
.spinner(
flags.workspace
? `Validating schema from workspace '${flags.workspace}'…`
: 'Validating schema…',
)
.start()
}
const worker = new Worker(workerPath, {
workerData: {
workDir,
level,
workspace: flags.workspace,
} satisfies ValidateSchemaWorkerData,
// eslint-disable-next-line no-process-env
env: process.env,
})
const {validation} = await new Promise<ValidateSchemaWorkerResult>((resolve, reject) => {
worker.addListener('message', resolve)
worker.addListener('error', reject)
})
const problems = validation.flatMap((group) => group.problems)
const errorCount = problems.filter((problem) => problem.severity === 'error').length
const warningCount = problems.filter((problem) => problem.severity === 'warning').length
const overallSeverity = getAggregatedSeverity(validation)
switch (format) {
case 'ndjson': {
for (const group of validation) {
output.print(JSON.stringify(group))
}
break
}
case 'json': {
output.print(JSON.stringify(validation))
break
}
default: {
spinner?.succeed('Validated schema')
output.print(`\nValidation results:`)
output.print(
`${logSymbols.error} Errors: ${errorCount.toLocaleString('en-US')} error${
errorCount === 1 ? '' : 's'
}`,
)
if (level !== 'error') {
output.print(
`${logSymbols.warning} Warnings: ${warningCount.toLocaleString('en-US')} warning${
warningCount === 1 ? '' : 's'
}`,
)
}
output.print()
output.print(formatSchemaValidation(validation))
}
}
process.exitCode = overallSeverity === 'error' ? 1 : 0
}