All files / src parse.ts

100% Statements 20/20
100% Branches 3/3
100% Functions 4/4
100% Lines 20/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132                3x             3x 3x   3x 3x                                                                             3x 3x     19x 123x     123x           123x 121x       108x 62x 108x     3x 2x 1x       108x                         3x                                                            
import {
    Evaluate,
    Narrow,
    Exact,
    TreeOf,
    IsAny,
    WithDefaults
} from "@re-do/utils"
import { Root } from "./components"
import {
    AllowsOptions,
    ParseContext,
    ReferencesOptions,
    GenerateOptions
} from "./components/parser.js"
import { stringifyErrors, ValidationErrors } from "./components/errors.js"
import { format } from "./format.js"
import { TypeSet } from "./components"
import { typeOf } from "./typeOf.js"
import { typeDefProxy } from "./internal.js"
 
export type Definition = Root.Definition
 
export type Validate<Def, TypeSet> = IsAny<Def> extends true
    ? Def
    : Root.Validate<Def, TypeSet>
 
export type Parse<
    Def,
    Set,
    Options extends ParseTypeOptions = {}
> = IsAny<Def> extends true
    ? Def
    : Root.Parse<
          Def,
          TypeSet.Validate<Set>,
          WithDefaults<ParseTypeOptions, Options, DefaultParseTypeOptions>
      >
 
export type ParseTypeOptions = {
    onCycle?: Definition
    seen?: Record<string, boolean>
    deepOnCycle?: boolean
    onResolve?: Definition
}
 
export type DefaultParseTypeOptions = {
    onCycle: never
    seen: {}
    deepOnCycle: false
    onResolve: never
}
 
export type InferredMethods = {
    assert: (value: unknown, options?: AllowsOptions) => void
    check: (value: unknown, options?: AllowsOptions) => string
}
 
export const createParseFunction =
    <PredefinedTypeSet>(
        predefinedTypeSet: Narrow<PredefinedTypeSet>
    ): ParseFunction<PredefinedTypeSet> =>
    (definition, options) => {
        const formattedTypeSet: any = format(
            options?.typeSet ?? predefinedTypeSet
        )
        const context: ParseContext = {
            typeSet: formattedTypeSet,
            path: [],
            seen: [],
            shallowSeen: []
        }
        const formattedDefinition = format(definition)
        const { allows, references, generate } = Root.parse(
            formattedDefinition,
            context
        ) as any
        const check: InferredMethods["check"] = (value, options) =>
            stringifyErrors(allows(typeOf(value), options))
        const inferredMethods: InferredMethods = {
            check,
            assert: (value, options) => {
                const errorMessage = check(value, options)
                if (errorMessage) {
                    throw new Error(errorMessage)
                }
            }
        }
        return {
            type: typeDefProxy,
            typeSet: formattedTypeSet,
            definition: formattedDefinition,
            allows,
            references,
            generate,
            ...inferredMethods
        } as any
    }
 
// Exported parse function is equivalent to parse from an empty compile call,
// but optionally accepts a typeset as its second parameter
export const parse = createParseFunction({})
 
export type ParseFunction<PredefinedTypeSet> = <
    Def,
    Options extends ParseTypeOptions,
    ActiveTypeSet = PredefinedTypeSet
>(
    definition: Validate<Narrow<Def>, ActiveTypeSet>,
    options?: Narrow<
        Options & {
            typeSet?: Exact<ActiveTypeSet, TypeSet.Validate<ActiveTypeSet>>
        }
    >
) => Evaluate<ParsedType<Def, ActiveTypeSet, Options>>
 
export type ParsedType<
    Definition,
    TypeSet,
    Options,
    TypeOfParsed = Evaluate<Parse<Definition, TypeSet, Options>>
> = Evaluate<{
    definition: Definition
    type: TypeOfParsed
    typeSet: Evaluate<TypeSet>
    check: (value: unknown, options?: AllowsOptions) => string
    assert: (value: unknown, options?: AllowsOptions) => void
    allows: (value: unknown, options?: AllowsOptions) => ValidationErrors
    generate: (options?: GenerateOptions) => TypeOfParsed
    references: (options?: ReferencesOptions) => TreeOf<string[], true>
}>