ref.ts 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. import type {CodeKeywordDefinition, AnySchemaObject} from "../../types"
  2. import type KeywordCxt from "../../compile/context"
  3. import {compileSchema, SchemaEnv} from "../../compile"
  4. import {_, not, nil, stringify} from "../../compile/codegen"
  5. import {MissingRefError} from "../../compile/error_classes"
  6. import N from "../../compile/names"
  7. import {getValidate, callRef} from "../core/ref"
  8. import {checkMetadata} from "./metadata"
  9. const def: CodeKeywordDefinition = {
  10. keyword: "ref",
  11. schemaType: "string",
  12. code(cxt: KeywordCxt) {
  13. checkMetadata(cxt)
  14. const {gen, data, schema: ref, parentSchema, it} = cxt
  15. const {
  16. schemaEnv: {root},
  17. } = it
  18. const valid = gen.name("valid")
  19. if (parentSchema.nullable) {
  20. gen.var(valid, _`${data} === null`)
  21. gen.if(not(valid), validateJtdRef)
  22. } else {
  23. gen.var(valid, false)
  24. validateJtdRef()
  25. }
  26. cxt.ok(valid)
  27. function validateJtdRef(): void {
  28. const refSchema = (root.schema as AnySchemaObject).definitions?.[ref]
  29. if (!refSchema) throw new MissingRefError("", ref, `No definition ${ref}`)
  30. if (hasRef(refSchema) || !it.opts.inlineRefs) callValidate(refSchema)
  31. else inlineRefSchema(refSchema)
  32. }
  33. function callValidate(schema: AnySchemaObject): void {
  34. const sch = compileSchema.call(it.self, new SchemaEnv({schema, root}))
  35. const v = getValidate(cxt, sch)
  36. const errsCount = gen.const("_errs", N.errors)
  37. callRef(cxt, v, sch, sch.$async)
  38. gen.assign(valid, _`${errsCount} === ${N.errors}`)
  39. }
  40. function inlineRefSchema(schema: AnySchemaObject): void {
  41. const schName = gen.scopeValue(
  42. "schema",
  43. it.opts.code.source === true ? {ref: schema, code: stringify(schema)} : {ref: schema}
  44. )
  45. cxt.subschema(
  46. {
  47. schema,
  48. dataTypes: [],
  49. schemaPath: nil,
  50. topSchemaRef: schName,
  51. errSchemaPath: `/definitions/${ref}`,
  52. },
  53. valid
  54. )
  55. }
  56. },
  57. }
  58. export function hasRef(schema: AnySchemaObject): boolean {
  59. for (const key in schema) {
  60. let sch: AnySchemaObject
  61. if (key === "ref" || (typeof (sch = schema[key]) == "object" && hasRef(sch))) return true
  62. }
  63. return false
  64. }
  65. export default def