index.ts 3.0 KB

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