UNPKG

@jsonjoy.com/json-type

Version:

High-performance JSON Pointer implementation

279 lines 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JsonTextCodegen = void 0; const toBase64_1 = require("@jsonjoy.com/base64/lib/toBase64"); const codegen_1 = require("@jsonjoy.com/codegen"); const JsExpression_1 = require("@jsonjoy.com/codegen/lib/util/JsExpression"); const normalizeAccessor_1 = require("@jsonjoy.com/codegen/lib/util/normalizeAccessor"); const codec_1 = require("@jsonjoy.com/json-pack/lib/json-binary/codec"); const asString_1 = require("@jsonjoy.com/util/lib/strings/asString"); const type_1 = require("../../type"); const discriminator_1 = require("../discriminator"); const util_1 = require("../util"); const value_1 = require("../../value"); class WriteTextStep { constructor(str) { this.str = str; } } class JsonTextCodegen { constructor(type, name) { this.type = type; this.codegen = new codegen_1.Codegen({ name: 'toJson' + (name ? '_' + name : ''), prologue: `var s = '';`, epilogue: `return s;`, linkable: { toBase64: toBase64_1.toBase64, Value: value_1.Value, getEncoder: JsonTextCodegen.get, }, processSteps: (steps) => { const stepsJoined = []; for (let i = 0; i < steps.length; i++) { const step = steps[i]; if (step instanceof codegen_1.CodegenStepExecJs) stepsJoined.push(step); else if (step instanceof WriteTextStep) { const last = stepsJoined[stepsJoined.length - 1]; if (last instanceof WriteTextStep) last.str += step.str; else stepsJoined.push(step); } } const execSteps = []; for (const step of stepsJoined) { if (step instanceof codegen_1.CodegenStepExecJs) { execSteps.push(step); } else if (step instanceof WriteTextStep) { const js = /* js */ `s += ${JSON.stringify(step.str)};`; execSteps.push(new codegen_1.CodegenStepExecJs(js)); } } return execSteps; }, }); this.codegen.linkDependency(asString_1.asString, 'asString'); this.codegen.linkDependency(codec_1.stringify, 'stringify'); } js(js) { this.codegen.js(js); } writeText(str) { this.codegen.step(new WriteTextStep(str)); } compile() { return this.codegen.compile(); } onArr(value, type) { this.writeText('['); const codegen = this.codegen; const r = codegen.getRegister(); // array const rl = codegen.getRegister(); // array.length const rll = codegen.getRegister(); // last const ri = codegen.getRegister(); // index this.js(/* js */ `var ${r} = ${value.use()}, ${rl} = ${r}.length, ${rll} = ${rl} - 1, ${ri} = 0;`); this.js(/* js */ `for(; ${ri} < ${rll}; ${ri}++) ` + '{'); this.onNode(new JsExpression_1.JsExpression(() => `${r}[${ri}]`), type._type); this.js(/* js */ `s += ',';`); this.js(/* js */ `}`); this.js(/* js */ `if (${rl}) {`); this.onNode(new JsExpression_1.JsExpression(() => `${r}[${rll}]`), type._type); this.js(/* js */ `}`); this.writeText(']'); } onObj(value, objType) { const { keys: fields } = objType; const schema = objType.getOptions(); const codegen = this.codegen; const r = codegen.getRegister(); this.js(/* js */ `var ${r} = ${value.use()};`); const rKeys = this.codegen.getRegister(); if (schema.encodeUnknownKeys) { this.js(/* js */ `var ${rKeys} = new Set(Object.keys(${r}));`); } const requiredFields = fields.filter((field) => !(field instanceof type_1.KeyOptType)); const optionalFields = fields.filter((field) => field instanceof type_1.KeyOptType); this.writeText('{'); for (let i = 0; i < requiredFields.length; i++) { const field = requiredFields[i]; if (i) this.writeText(','); this.writeText(JSON.stringify(field.key) + ':'); const accessor = (0, normalizeAccessor_1.normalizeAccessor)(field.key); const valueExpression = new JsExpression_1.JsExpression(() => `${r}${accessor}`); if (schema.encodeUnknownKeys) this.js(/* js */ `${rKeys}.delete(${JSON.stringify(field.key)});`); this.onNode(valueExpression, field.val); } const rHasFields = codegen.getRegister(); if (!requiredFields.length) this.js(/* js */ `var ${rHasFields} = false;`); for (let i = 0; i < optionalFields.length; i++) { const field = optionalFields[i]; const accessor = (0, normalizeAccessor_1.normalizeAccessor)(field.key); const rValue = codegen.getRegister(); if (schema.encodeUnknownKeys) this.js(/* js */ `${rKeys}.delete(${JSON.stringify(field.key)});`); this.js(/* js */ `var ${rValue} = ${r}${accessor};`); this.js(`if (${rValue} !== undefined) {`); if (requiredFields.length) { this.writeText(','); } else { this.js(`if (${rHasFields}) s += ',';`); this.js(/* js */ `${rHasFields} = true;`); } this.writeText(JSON.stringify(field.key) + ':'); const valueExpression = new JsExpression_1.JsExpression(() => `${rValue}`); this.onNode(valueExpression, field.val); this.js(`}`); } if (schema.encodeUnknownKeys) { const [rList, ri, rLength, rk] = [codegen.r(), codegen.r(), codegen.r(), codegen.r()]; this.js(`var ${rLength} = ${rKeys}.size; if (${rLength}) { var ${rk}, ${rList} = Array.from(${rKeys}.values()); for (var ${ri} = 0; ${ri} < ${rLength}; ${ri}++) { ${rk} = ${rList}[${ri}]; s += ',' + asString(${rk}) + ':' + stringify(${r}[${rk}]); } }`); } this.writeText('}'); } onMap(value, type) { this.writeText('{'); const r = this.codegen.var(value.use()); const rKeys = this.codegen.var(/* js */ `Object.keys(${r})`); const rLength = this.codegen.var(/* js */ `${rKeys}.length`); const rKey = this.codegen.var(); this.codegen.if(/* js */ `${rLength}`, () => { this.js(/* js */ `${rKey} = ${rKeys}[0];`); this.js(/* js */ `s += asString(${rKey}) + ':';`); const innerValue = new JsExpression_1.JsExpression(() => /* js */ `${r}[${rKey}]`); this.onNode(innerValue, type._value); }); this.js(/* js */ `for (var i = 1; i < ${rLength}; i++) {`); this.js(/* js */ `${rKey} = ${rKeys}[i];`); this.js(/* js */ `s += ',' + asString(${rKey}) + ':';`); const innerValue = new JsExpression_1.JsExpression(() => /* js */ `${r}[${rKey}]`); this.onNode(innerValue, type._value); this.js(/* js */ `}`); this.writeText('}'); } onRef(value, ref) { const system = ref.system; if (!system) throw new Error('NO_SYSTEM'); const alias = system.resolve(ref.ref()); const fn = JsonTextCodegen.get(alias.type, alias.id); const d = this.codegen.linkDependency(fn); this.js(/* js */ `s += ${d}(${value.use()});`); } onOr(value, type) { const codegen = this.codegen; const discriminator = discriminator_1.DiscriminatorCodegen.get(type); const d = codegen.linkDependency(discriminator); const types = type.types; codegen.switch(`${d}(${value.use()})`, types.map((childType, index) => [ index, () => { this.onNode(value, childType); }, ])); } onNode(value, type) { const kind = type.kind(); const codegen = this.codegen; switch (kind) { case 'any': { const r = codegen.var(value.use()); codegen.link('Value'); codegen.link('getEncoder'); codegen.if( /* js */ `${r} instanceof Value`, () => { const rType = codegen.var(/* js */ `${r}.type`); const rData = codegen.var(/* js */ `${r}.data`); codegen.if( /* js */ `${rType}`, () => { codegen.js(/* js */ `s += getEncoder(${rType})(${rData});`); }, () => { codegen.js(`s += stringify(${rData});`); }); }, () => { codegen.js(`s += stringify(${r});`); }); break; } case 'bool': { this.js(/* js */ `s += ${value.use()} ? 'true' : 'false';`); break; } case 'num': { this.js(/* js */ `s += '' + ${value.use()};`); break; } case 'str': { const strType = type; if (strType.getSchema().noJsonEscape) { this.writeText('"'); this.js(/* js */ `s += ${value.use()};`); this.writeText('"'); } else { this.js(/* js */ `s += asString(${value.use()});`); } break; } case 'bin': { this.codegen.link('toBase64'); this.writeText('"data:application/octet-stream;base64,'); this.js(/* js */ `s += toBase64(${value.use()});`); this.writeText('"'); break; } case 'con': { const constType = type; this.js(/* js */ `s += ${JSON.stringify((0, codec_1.stringify)(constType.literal()))}`); break; } case 'arr': { this.onArr(value, type); break; } // case 'tup': // tup(ctx, value, type, generate); // break; case 'obj': { this.onObj(value, type); break; } case 'map': { this.onMap(value, type); break; } case 'ref': { this.onRef(value, type); break; } case 'or': { this.onOr(value, type); break; } default: throw new Error(`${kind} type JSON text encoding not implemented`); } } } exports.JsonTextCodegen = JsonTextCodegen; JsonTextCodegen.get = (0, util_1.lazyKeyedFactory)((type, name) => { const codegen = new JsonTextCodegen(type, name); const r = codegen.codegen.options.args[0]; const expression = new JsExpression_1.JsExpression(() => r); codegen.onNode(expression, type); return codegen.compile(); }); //# sourceMappingURL=JsonTextCodegen.js.map