UNPKG

4.15 kBPlain TextView Raw
1import type AjvCore from "../core"
2import type {AnyValidateFunction, SourceCode} from "../types"
3import type {SchemaEnv} from "../compile"
4import {UsedScopeValues, UsedValueState, ValueScopeName, varKinds} from "../compile/codegen/scope"
5import {_, nil, _Code, Code, getProperty, getEsmExportName} from "../compile/codegen/code"
6
7function 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 if (ajv.opts.code.esm) {
34 // Always do named export as `validate` rather than the variable `n` which is `validateXX` for known export value
35 return `"use strict";${_n}export const validate = ${n};${_n}export default ${n};${_n}${vCode}`
36 }
37 return `"use strict";${_n}module.exports = ${n};${_n}module.exports.default = ${n};${_n}${vCode}`
38 }
39
40 function multiExportsCode<T extends SchemaEnv | string>(
41 schemas: {[K in string]?: T},
42 getValidateFunc: (schOrId: T) => AnyValidateFunction | undefined
43 ): string {
44 const usedValues: UsedScopeValues = {}
45 let code = _`"use strict";`
46 for (const name in schemas) {
47 const v = getValidateFunc(schemas[name] as T)
48 if (v) {
49 const vCode = validateCode(usedValues, v.source)
50 const exportSyntax = ajv.opts.code.esm
51 ? _`export const ${getEsmExportName(name)}`
52 : _`exports${getProperty(name)}`
53 code = _`${code}${_n}${exportSyntax} = ${v.source?.validateName};${_n}${vCode}`
54 }
55 }
56 return `${code}`
57 }
58
59 function validateCode(usedValues: UsedScopeValues, s?: SourceCode): Code {
60 if (!s) throw new Error('moduleCode: function does not have "source" property')
61 if (usedState(s.validateName) === UsedValueState.Completed) return nil
62 setUsedState(s.validateName, UsedValueState.Started)
63
64 const scopeCode = ajv.scope.scopeCode(s.scopeValues, usedValues, refValidateCode)
65 const code = new _Code(`${scopeCode}${_n}${s.validateCode}`)
66 return s.evaluated ? _`${code}${s.validateName}.evaluated = ${s.evaluated};${_n}` : code
67
68 function refValidateCode(n: ValueScopeName): Code | undefined {
69 const vRef = n.value?.ref
70 if (n.prefix === "validate" && typeof vRef == "function") {
71 const v = vRef as AnyValidateFunction
72 return validateCode(usedValues, v.source)
73 } else if ((n.prefix === "root" || n.prefix === "wrapper") && typeof vRef == "object") {
74 const {validate, validateName} = vRef as SchemaEnv
75 if (!validateName) throw new Error("ajv internal error")
76 const def = ajv.opts.code.es5 ? varKinds.var : varKinds.const
77 const wrapper = _`${def} ${n} = {validate: ${validateName}};`
78 if (usedState(validateName) === UsedValueState.Started) return wrapper
79 const vCode = validateCode(usedValues, validate?.source)
80 return _`${wrapper}${_n}${vCode}`
81 }
82 return undefined
83 }
84
85 function usedState(name: ValueScopeName): UsedValueState | undefined {
86 return usedValues[name.prefix]?.get(name)
87 }
88
89 function setUsedState(name: ValueScopeName, state: UsedValueState): void {
90 const {prefix} = name
91 const names = (usedValues[prefix] = usedValues[prefix] || new Map())
92 names.set(name, state)
93 }
94 }
95}
96
97module.exports = exports = standaloneCode
98Object.defineProperty(exports, "__esModule", {value: true})
99
100export default standaloneCode
101
\No newline at end of file