[6a3a178] | 1 | import type AjvCore from "../core"
|
---|
| 2 | import type {AnyValidateFunction, SourceCode} from "../types"
|
---|
| 3 | import type {SchemaEnv} from "../compile"
|
---|
| 4 | import {UsedScopeValues, UsedValueState, ValueScopeName, varKinds} from "../compile/codegen/scope"
|
---|
| 5 | import {_, nil, _Code, Code, getProperty} from "../compile/codegen/code"
|
---|
| 6 |
|
---|
| 7 | export default function standaloneCode(
|
---|
| 8 | ajv: AjvCore,
|
---|
| 9 | refsOrFunc?: {[K in string]?: string} | AnyValidateFunction
|
---|
| 10 | ): string {
|
---|
| 11 | if (!ajv.opts.code.source) {
|
---|
| 12 | throw new Error("moduleCode: ajv instance must have code.source option")
|
---|
| 13 | }
|
---|
| 14 | const {_n} = ajv.scope.opts
|
---|
| 15 | return typeof refsOrFunc == "function"
|
---|
| 16 | ? funcExportCode(refsOrFunc.source)
|
---|
| 17 | : refsOrFunc !== undefined
|
---|
| 18 | ? multiExportsCode<string>(refsOrFunc, getValidate)
|
---|
| 19 | : multiExportsCode<SchemaEnv>(ajv.schemas, (sch) =>
|
---|
| 20 | sch.meta ? undefined : ajv.compile(sch.schema)
|
---|
| 21 | )
|
---|
| 22 |
|
---|
| 23 | function getValidate(id: string): AnyValidateFunction {
|
---|
| 24 | const v = ajv.getSchema(id)
|
---|
| 25 | if (!v) throw new Error(`moduleCode: no schema with id ${id}`)
|
---|
| 26 | return v
|
---|
| 27 | }
|
---|
| 28 |
|
---|
| 29 | function funcExportCode(source?: SourceCode): string {
|
---|
| 30 | const usedValues: UsedScopeValues = {}
|
---|
| 31 | const n = source?.validateName
|
---|
| 32 | const vCode = validateCode(usedValues, source)
|
---|
| 33 | return `"use strict";${_n}module.exports = ${n};${_n}module.exports.default = ${n};${_n}${vCode}`
|
---|
| 34 | }
|
---|
| 35 |
|
---|
| 36 | function multiExportsCode<T extends SchemaEnv | string>(
|
---|
| 37 | schemas: {[K in string]?: T},
|
---|
| 38 | getValidateFunc: (schOrId: T) => AnyValidateFunction | undefined
|
---|
| 39 | ): string {
|
---|
| 40 | const usedValues: UsedScopeValues = {}
|
---|
| 41 | let code = _`"use strict";`
|
---|
| 42 | for (const name in schemas) {
|
---|
| 43 | const v = getValidateFunc(schemas[name] as T)
|
---|
| 44 | if (v) {
|
---|
| 45 | const vCode = validateCode(usedValues, v.source)
|
---|
| 46 | code = _`${code}${_n}exports${getProperty(name)} = ${v.source?.validateName};${_n}${vCode}`
|
---|
| 47 | }
|
---|
| 48 | }
|
---|
| 49 | return `${code}`
|
---|
| 50 | }
|
---|
| 51 |
|
---|
| 52 | function validateCode(usedValues: UsedScopeValues, s?: SourceCode): Code {
|
---|
| 53 | if (!s) throw new Error('moduleCode: function does not have "source" property')
|
---|
| 54 | if (usedState(s.validateName) === UsedValueState.Completed) return nil
|
---|
| 55 | setUsedState(s.validateName, UsedValueState.Started)
|
---|
| 56 |
|
---|
| 57 | const scopeCode = ajv.scope.scopeCode(s.scopeValues, usedValues, refValidateCode)
|
---|
| 58 | const code = new _Code(`${scopeCode}${_n}${s.validateCode}`)
|
---|
| 59 | return s.evaluated ? _`${code}${s.validateName}.evaluated = ${s.evaluated};${_n}` : code
|
---|
| 60 |
|
---|
| 61 | function refValidateCode(n: ValueScopeName): Code | undefined {
|
---|
| 62 | const vRef = n.value?.ref
|
---|
| 63 | if (n.prefix === "validate" && typeof vRef == "function") {
|
---|
| 64 | const v = vRef as AnyValidateFunction
|
---|
| 65 | return validateCode(usedValues, v.source)
|
---|
| 66 | } else if ((n.prefix === "root" || n.prefix === "wrapper") && typeof vRef == "object") {
|
---|
| 67 | const {validate, validateName} = vRef as SchemaEnv
|
---|
| 68 | if (!validateName) throw new Error("ajv internal error")
|
---|
| 69 | const def = ajv.opts.code.es5 ? varKinds.var : varKinds.const
|
---|
| 70 | const wrapper = _`${def} ${n} = {validate: ${validateName}};`
|
---|
| 71 | if (usedState(validateName) === UsedValueState.Started) return wrapper
|
---|
| 72 | const vCode = validateCode(usedValues, validate?.source)
|
---|
| 73 | return _`${wrapper}${_n}${vCode}`
|
---|
| 74 | }
|
---|
| 75 | return undefined
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | function usedState(name: ValueScopeName): UsedValueState | undefined {
|
---|
| 79 | return usedValues[name.prefix]?.get(name)
|
---|
| 80 | }
|
---|
| 81 |
|
---|
| 82 | function setUsedState(name: ValueScopeName, state: UsedValueState): void {
|
---|
| 83 | const {prefix} = name
|
---|
| 84 | const names = (usedValues[prefix] = usedValues[prefix] || new Map())
|
---|
| 85 | names.set(name, state)
|
---|
| 86 | }
|
---|
| 87 | }
|
---|
| 88 | }
|
---|