serialize.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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. //# sourceMappingURL=serialize.js.map