serialize.js 8.4 KB

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