UNPKG

@sanity/schema

Version:
120 lines (97 loc) 2.98 kB
import {flatten, uniq} from 'lodash' import {getDupes} from '../sanity/validation/utils/getDupes' type SchemaType = Record<string, any> type SchemaTypeDef = Record<string, any> type VisitContext = { isRoot: boolean isReserved: (typeName: string) => boolean visit: Visitor index: number isDuplicate: (typeName: string) => boolean getType: (typeName: string) => null | SchemaType getTypeNames: () => Array<string> } export type Visitor = (typeDef: SchemaTypeDef, arg1: VisitContext) => SchemaType const NOOP_VISITOR: Visitor = (typeDef) => typeDef export class UnknownType { name: string constructor(name: string) { this.name = name } } const TYPE_TYPE = {name: 'type', type: null} const FUTURE_RESERVED = ['any', 'time', 'date'] export function traverseSchema( types: SchemaTypeDef[] = [], coreTypes: SchemaTypeDef[] = [], visitor: Visitor = NOOP_VISITOR, ) { const coreTypesRegistry = Object.create(null) const registry = Object.create(null) const coreTypeNames = coreTypes.map((typeDef) => typeDef.name) const reservedTypeNames = FUTURE_RESERVED.concat(coreTypeNames) const typeNames = types.map((typeDef) => typeDef && typeDef.name).filter(Boolean) coreTypes.forEach((coreType) => { coreTypesRegistry[coreType.name] = coreType }) types.forEach((type, i) => { // Allocate a placeholder for each type registry[(type && type.name) || `__unnamed_${i}`] = {} }) function getType(typeName: any) { return typeName === 'type' ? TYPE_TYPE : coreTypesRegistry[typeName] || registry[typeName] || null } const duplicateNames = uniq(flatten(getDupes(typeNames))) function isDuplicate(typeName: any) { return duplicateNames.includes(typeName) } function getTypeNames() { return typeNames.concat(coreTypeNames) } function isReserved(typeName: any) { return typeName === 'type' || reservedTypeNames.includes(typeName) } const visitType = (isRoot: any) => (typeDef: any, index: any) => { return visitor(typeDef, { visit: visitType(false), isRoot, getType, getTypeNames, isReserved, isDuplicate, index, }) } coreTypes.forEach((coreTypeDef) => { Object.assign(coreTypesRegistry[coreTypeDef.name], visitType(coreTypeDef)) }) types.forEach((typeDef, i) => { Object.assign( registry[(typeDef && typeDef.name) || `__unnamed_${i}`], visitType(true)(typeDef, i), ) }) return { get(typeName: string) { const res = registry[typeName] || coreTypesRegistry[typeName] if (res) { return res } throw new Error(`No such type: ${typeName}`) }, has(typeName: string): boolean { return typeName in registry || typeName in coreTypesRegistry }, getTypeNames(): string[] { return Object.keys(registry) }, getTypes() { return this.getTypeNames().map(this.get) }, toJSON() { return this.getTypes() }, } }