99 lines
3.6 KiB
JavaScript
99 lines
3.6 KiB
JavaScript
'use strict'
|
|
|
|
const genfun = require('./generate-function')
|
|
const { buildSchemas } = require('./pointer')
|
|
const { compile } = require('./compile')
|
|
const { deepEqual } = require('./scope-functions')
|
|
|
|
const jsonCheckWithErrors = (validate) =>
|
|
function validateIsJSON(data) {
|
|
if (!deepEqual(data, JSON.parse(JSON.stringify(data)))) {
|
|
validateIsJSON.errors = [{ instanceLocation: '#', error: 'not JSON compatible' }]
|
|
return false
|
|
}
|
|
const res = validate(data)
|
|
validateIsJSON.errors = validate.errors
|
|
return res
|
|
}
|
|
|
|
const jsonCheckWithoutErrors = (validate) => (data) =>
|
|
deepEqual(data, JSON.parse(JSON.stringify(data))) && validate(data)
|
|
|
|
const validator = (
|
|
schema,
|
|
{ parse = false, multi = false, jsonCheck = false, isJSON = false, schemas = [], ...opts } = {}
|
|
) => {
|
|
if (jsonCheck && isJSON) throw new Error('Can not specify both isJSON and jsonCheck options')
|
|
if (parse && (jsonCheck || isJSON))
|
|
throw new Error('jsonCheck and isJSON options are not applicable in parser mode')
|
|
const mode = parse ? 'strong' : 'default' // strong mode is default in parser, can be overriden
|
|
const willJSON = isJSON || jsonCheck || parse
|
|
const arg = multi ? schema : [schema]
|
|
const options = { mode, ...opts, schemas: buildSchemas(schemas, arg), isJSON: willJSON }
|
|
const { scope, refs } = compile(arg, options) // only a single ref
|
|
if (opts.dryRun) return
|
|
if (opts.lint) return scope.lintErrors
|
|
const fun = genfun()
|
|
if (parse) {
|
|
scope.parseWrap = opts.includeErrors ? parseWithErrors : parseWithoutErrors
|
|
} else if (jsonCheck) {
|
|
scope.deepEqual = deepEqual
|
|
scope.jsonCheckWrap = opts.includeErrors ? jsonCheckWithErrors : jsonCheckWithoutErrors
|
|
}
|
|
if (multi) {
|
|
fun.write('[')
|
|
for (const ref of refs.slice(0, -1)) fun.write('%s,', ref)
|
|
if (refs.length > 0) fun.write('%s', refs[refs.length - 1])
|
|
fun.write(']')
|
|
if (parse) fun.write('.map(parseWrap)')
|
|
else if (jsonCheck) fun.write('.map(jsonCheckWrap)')
|
|
} else {
|
|
if (parse) fun.write('parseWrap(%s)', refs[0])
|
|
else if (jsonCheck) fun.write('jsonCheckWrap(%s)', refs[0])
|
|
else fun.write('%s', refs[0])
|
|
}
|
|
const validate = fun.makeFunction(scope)
|
|
validate.toModule = ({ semi = true } = {}) => fun.makeModule(scope) + (semi ? ';' : '')
|
|
validate.toJSON = () => schema
|
|
return validate
|
|
}
|
|
|
|
const parseWithErrors = (validate) => (src) => {
|
|
if (typeof src !== 'string') return { valid: false, error: 'Input is not a string' }
|
|
try {
|
|
const value = JSON.parse(src)
|
|
if (!validate(value)) {
|
|
const { keywordLocation, instanceLocation } = validate.errors[0]
|
|
const keyword = keywordLocation.slice(keywordLocation.lastIndexOf('/') + 1)
|
|
const error = `JSON validation failed for ${keyword} at ${instanceLocation}`
|
|
return { valid: false, error, errors: validate.errors }
|
|
}
|
|
return { valid: true, value }
|
|
} catch ({ message }) {
|
|
return { valid: false, error: message }
|
|
}
|
|
}
|
|
|
|
const parseWithoutErrors = (validate) => (src) => {
|
|
if (typeof src !== 'string') return { valid: false }
|
|
try {
|
|
const value = JSON.parse(src)
|
|
if (!validate(value)) return { valid: false }
|
|
return { valid: true, value }
|
|
} catch (e) {
|
|
return { valid: false }
|
|
}
|
|
}
|
|
|
|
const parser = function(schema, { parse = true, ...opts } = {}) {
|
|
if (!parse) throw new Error('can not disable parse in parser')
|
|
return validator(schema, { parse, ...opts })
|
|
}
|
|
|
|
const lint = function(schema, { lint: lintOption = true, ...opts } = {}) {
|
|
if (!lintOption) throw new Error('can not disable lint option in lint()')
|
|
return validator(schema, { lint: lintOption, ...opts })
|
|
}
|
|
|
|
module.exports = { validator, parser, lint }
|