source: imaps-frontend/node_modules/ajv/lib/compile/jtd/serialize.ts@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 3 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 8.0 KB
Line 
1import type Ajv from "../../core"
2import type {SchemaObject} from "../../types"
3import {jtdForms, JTDForm, SchemaObjectMap} from "./types"
4import {SchemaEnv, getCompilingSchema} from ".."
5import {_, str, and, getProperty, CodeGen, Code, Name} from "../codegen"
6import MissingRefError from "../ref_error"
7import N from "../names"
8import {isOwnProperty} from "../../vocabularies/code"
9import {hasRef} from "../../vocabularies/jtd/ref"
10import {useFunc} from "../util"
11import quote from "../../runtime/quote"
12
13const genSerialize: {[F in JTDForm]: (cxt: SerializeCxt) => void} = {
14 elements: serializeElements,
15 values: serializeValues,
16 discriminator: serializeDiscriminator,
17 properties: serializeProperties,
18 optionalProperties: serializeProperties,
19 enum: serializeString,
20 type: serializeType,
21 ref: serializeRef,
22}
23
24interface SerializeCxt {
25 readonly gen: CodeGen
26 readonly self: Ajv // current Ajv instance
27 readonly schemaEnv: SchemaEnv
28 readonly definitions: SchemaObjectMap
29 schema: SchemaObject
30 data: Code
31}
32
33export default function compileSerializer(
34 this: Ajv,
35 sch: SchemaEnv,
36 definitions: SchemaObjectMap
37): SchemaEnv {
38 const _sch = getCompilingSchema.call(this, sch)
39 if (_sch) return _sch
40 const {es5, lines} = this.opts.code
41 const {ownProperties} = this.opts
42 const gen = new CodeGen(this.scope, {es5, lines, ownProperties})
43 const serializeName = gen.scopeName("serialize")
44 const cxt: SerializeCxt = {
45 self: this,
46 gen,
47 schema: sch.schema as SchemaObject,
48 schemaEnv: sch,
49 definitions,
50 data: N.data,
51 }
52
53 let sourceCode: string | undefined
54 try {
55 this._compilations.add(sch)
56 sch.serializeName = serializeName
57 gen.func(serializeName, N.data, false, () => {
58 gen.let(N.json, str``)
59 serializeCode(cxt)
60 gen.return(N.json)
61 })
62 gen.optimize(this.opts.code.optimize)
63 const serializeFuncCode = gen.toString()
64 sourceCode = `${gen.scopeRefs(N.scope)}return ${serializeFuncCode}`
65 const makeSerialize = new Function(`${N.scope}`, sourceCode)
66 const serialize: (data: unknown) => string = makeSerialize(this.scope.get())
67 this.scope.value(serializeName, {ref: serialize})
68 sch.serialize = serialize
69 } catch (e) {
70 if (sourceCode) this.logger.error("Error compiling serializer, function code:", sourceCode)
71 delete sch.serialize
72 delete sch.serializeName
73 throw e
74 } finally {
75 this._compilations.delete(sch)
76 }
77 return sch
78}
79
80function serializeCode(cxt: SerializeCxt): void {
81 let form: JTDForm | undefined
82 for (const key of jtdForms) {
83 if (key in cxt.schema) {
84 form = key
85 break
86 }
87 }
88 serializeNullable(cxt, form ? genSerialize[form] : serializeEmpty)
89}
90
91function serializeNullable(cxt: SerializeCxt, serializeForm: (_cxt: SerializeCxt) => void): void {
92 const {gen, schema, data} = cxt
93 if (!schema.nullable) return serializeForm(cxt)
94 gen.if(
95 _`${data} === undefined || ${data} === null`,
96 () => gen.add(N.json, _`"null"`),
97 () => serializeForm(cxt)
98 )
99}
100
101function serializeElements(cxt: SerializeCxt): void {
102 const {gen, schema, data} = cxt
103 gen.add(N.json, str`[`)
104 const first = gen.let("first", true)
105 gen.forOf("el", data, (el) => {
106 addComma(cxt, first)
107 serializeCode({...cxt, schema: schema.elements, data: el})
108 })
109 gen.add(N.json, str`]`)
110}
111
112function serializeValues(cxt: SerializeCxt): void {
113 const {gen, schema, data} = cxt
114 gen.add(N.json, str`{`)
115 const first = gen.let("first", true)
116 gen.forIn("key", data, (key) => serializeKeyValue(cxt, key, schema.values, first))
117 gen.add(N.json, str`}`)
118}
119
120function serializeKeyValue(cxt: SerializeCxt, key: Name, schema: SchemaObject, first?: Name): void {
121 const {gen, data} = cxt
122 addComma(cxt, first)
123 serializeString({...cxt, data: key})
124 gen.add(N.json, str`:`)
125 const value = gen.const("value", _`${data}${getProperty(key)}`)
126 serializeCode({...cxt, schema, data: value})
127}
128
129function serializeDiscriminator(cxt: SerializeCxt): void {
130 const {gen, schema, data} = cxt
131 const {discriminator} = schema
132 gen.add(N.json, str`{${JSON.stringify(discriminator)}:`)
133 const tag = gen.const("tag", _`${data}${getProperty(discriminator)}`)
134 serializeString({...cxt, data: tag})
135 gen.if(false)
136 for (const tagValue in schema.mapping) {
137 gen.elseIf(_`${tag} === ${tagValue}`)
138 const sch = schema.mapping[tagValue]
139 serializeSchemaProperties({...cxt, schema: sch}, discriminator)
140 }
141 gen.endIf()
142 gen.add(N.json, str`}`)
143}
144
145function serializeProperties(cxt: SerializeCxt): void {
146 const {gen} = cxt
147 gen.add(N.json, str`{`)
148 serializeSchemaProperties(cxt)
149 gen.add(N.json, str`}`)
150}
151
152function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): void {
153 const {gen, schema, data} = cxt
154 const {properties, optionalProperties} = schema
155 const props = keys(properties)
156 const optProps = keys(optionalProperties)
157 const allProps = allProperties(props.concat(optProps))
158 let first = !discriminator
159 let firstProp: Name | undefined
160
161 for (const key of props) {
162 if (first) first = false
163 else gen.add(N.json, str`,`)
164 serializeProperty(key, properties[key], keyValue(key))
165 }
166 if (first) firstProp = gen.let("first", true)
167 for (const key of optProps) {
168 const value = keyValue(key)
169 gen.if(and(_`${value} !== undefined`, isOwnProperty(gen, data, key)), () => {
170 addComma(cxt, firstProp)
171 serializeProperty(key, optionalProperties[key], value)
172 })
173 }
174 if (schema.additionalProperties) {
175 gen.forIn("key", data, (key) =>
176 gen.if(isAdditional(key, allProps), () => serializeKeyValue(cxt, key, {}, firstProp))
177 )
178 }
179
180 function keys(ps?: SchemaObjectMap): string[] {
181 return ps ? Object.keys(ps) : []
182 }
183
184 function allProperties(ps: string[]): string[] {
185 if (discriminator) ps.push(discriminator)
186 if (new Set(ps).size !== ps.length) {
187 throw new Error("JTD: properties/optionalProperties/disciminator overlap")
188 }
189 return ps
190 }
191
192 function keyValue(key: string): Name {
193 return gen.const("value", _`${data}${getProperty(key)}`)
194 }
195
196 function serializeProperty(key: string, propSchema: SchemaObject, value: Name): void {
197 gen.add(N.json, str`${JSON.stringify(key)}:`)
198 serializeCode({...cxt, schema: propSchema, data: value})
199 }
200
201 function isAdditional(key: Name, ps: string[]): Code | true {
202 return ps.length ? and(...ps.map((p) => _`${key} !== ${p}`)) : true
203 }
204}
205
206function serializeType(cxt: SerializeCxt): void {
207 const {gen, schema, data} = cxt
208 switch (schema.type) {
209 case "boolean":
210 gen.add(N.json, _`${data} ? "true" : "false"`)
211 break
212 case "string":
213 serializeString(cxt)
214 break
215 case "timestamp":
216 gen.if(
217 _`${data} instanceof Date`,
218 () => gen.add(N.json, _`'"' + ${data}.toISOString() + '"'`),
219 () => serializeString(cxt)
220 )
221 break
222 default:
223 serializeNumber(cxt)
224 }
225}
226
227function serializeString({gen, data}: SerializeCxt): void {
228 gen.add(N.json, _`${useFunc(gen, quote)}(${data})`)
229}
230
231function serializeNumber({gen, data}: SerializeCxt): void {
232 gen.add(N.json, _`"" + ${data}`)
233}
234
235function serializeRef(cxt: SerializeCxt): void {
236 const {gen, self, data, definitions, schema, schemaEnv} = cxt
237 const {ref} = schema
238 const refSchema = definitions[ref]
239 if (!refSchema) throw new MissingRefError(self.opts.uriResolver, "", ref, `No definition ${ref}`)
240 if (!hasRef(refSchema)) return serializeCode({...cxt, schema: refSchema})
241 const {root} = schemaEnv
242 const sch = compileSerializer.call(self, new SchemaEnv({schema: refSchema, root}), definitions)
243 gen.add(N.json, _`${getSerialize(gen, sch)}(${data})`)
244}
245
246function getSerialize(gen: CodeGen, sch: SchemaEnv): Code {
247 return sch.serialize
248 ? gen.scopeValue("serialize", {ref: sch.serialize})
249 : _`${gen.scopeValue("wrapper", {ref: sch})}.serialize`
250}
251
252function serializeEmpty({gen, data}: SerializeCxt): void {
253 gen.add(N.json, _`JSON.stringify(${data})`)
254}
255
256function addComma({gen}: SerializeCxt, first?: Name): void {
257 if (first) {
258 gen.if(
259 first,
260 () => gen.assign(first, false),
261 () => gen.add(N.json, str`,`)
262 )
263 } else {
264 gen.add(N.json, str`,`)
265 }
266}
Note: See TracBrowser for help on using the repository browser.