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