UNPKG

@jsonjoy.com/json-type

Version:

High-performance JSON Pointer implementation

226 lines (225 loc) 8.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generate = exports.or = exports.ref = exports.map = exports.obj = exports.tup = exports.arr = exports.const_ = exports.bin = exports.str = exports.num = exports.bool = exports.any = void 0; const JsExpression_1 = require("@jsonjoy.com/util/lib/codegen/util/JsExpression"); const asString_1 = require("@jsonjoy.com/util/lib/strings/asString"); const normalizeAccessor_1 = require("@jsonjoy.com/util/lib/codegen/util/normalizeAccessor"); const any = (ctx, value, type) => { const codegen = ctx.codegen; codegen.link('Value'); const r = codegen.var(value.use()); codegen.if(`${r} instanceof Value`, () => { codegen.if(`${r}.type`, () => { codegen.js(`s += ${r}.type.jsonTextEncoder()(${r}.data);`); }, () => { const asStringFn = codegen.linkDependency(asString_1.asString); codegen.js(`s += ${asStringFn}(${r}.data);`); }); }, () => { const asStringFn = codegen.linkDependency(asString_1.asString); codegen.js(`s += ${asStringFn}(${r});`); }); }; exports.any = any; const bool = (ctx, value) => { ctx.js(/* js */ `s += ${value.use()} ? 'true' : 'false';`); }; exports.bool = bool; const num = (ctx, value) => { ctx.js(/* js */ `s += '' + ${value.use()};`); }; exports.num = num; const str = (ctx, value, type) => { const strType = type; // StrType if (strType.schema.noJsonEscape) { ctx.writeText('"'); ctx.js(/* js */ `s += ${value.use()};`); ctx.writeText('"'); } else { const asStringFn = ctx.codegen.linkDependency(asString_1.asString); ctx.js(/* js */ `s += ${asStringFn}(${value.use()});`); } }; exports.str = str; const bin = (ctx, value) => { const asStringFn = ctx.codegen.linkDependency(asString_1.asString); ctx.js(/* js */ `s += ${asStringFn}(${value.use()});`); }; exports.bin = bin; const const_ = (ctx, value, type) => { const constType = type; // ConType const constValue = constType.value(); const asStringFn = ctx.codegen.linkDependency(asString_1.asString); ctx.js(/* js */ `s += ${asStringFn}(${JSON.stringify(constValue)});`); }; exports.const_ = const_; const arr = (ctx, value, type, encodeFn) => { const arrType = type; // ArrType ctx.writeText('['); const codegen = ctx.codegen; const r = codegen.getRegister(); // array const rl = codegen.getRegister(); // array.length const rll = codegen.getRegister(); // last const ri = codegen.getRegister(); // index ctx.js(/* js */ `var ${r} = ${value.use()}, ${rl} = ${r}.length, ${rll} = ${rl} - 1, ${ri} = 0;`); ctx.js(/* js */ `for(; ${ri} < ${rll}; ${ri}++) ` + '{'); encodeFn(ctx, new JsExpression_1.JsExpression(() => `${r}[${ri}]`), arrType.type); ctx.js(/* js */ `s += ',';`); ctx.js(`}`); ctx.js(`if (${rl}) {`); encodeFn(ctx, new JsExpression_1.JsExpression(() => `${r}[${rll}]`), arrType.type); ctx.js(`}`); ctx.writeText(']'); }; exports.arr = arr; const tup = (ctx, value, type, encodeFn) => { const tupType = type; // TupType const codegen = ctx.codegen; const r = codegen.var(value.use()); const types = tupType.types; ctx.writeText('['); for (let i = 0; i < types.length; i++) { if (i > 0) ctx.writeText(','); encodeFn(ctx, new JsExpression_1.JsExpression(() => `${r}[${i}]`), types[i]); } ctx.writeText(']'); }; exports.tup = tup; const obj = (ctx, value, type, encodeFn) => { const objType = type; // ObjType const codegen = ctx.codegen; const r = codegen.var(value.use()); const encodeUnknownFields = !!objType.schema.encodeUnknownFields; if (encodeUnknownFields) { const asStringFn = codegen.linkDependency(asString_1.asString); ctx.js(/* js */ `s += ${asStringFn}(${r});`); return; } const fields = objType.fields; ctx.writeText('{'); let hasFields = false; for (const field of fields) { const key = field.key; const accessor = (0, normalizeAccessor_1.normalizeAccessor)(key); const isOptional = field.optional || field.constructor?.name === 'ObjectOptionalFieldType'; const writeField = () => { if (hasFields) ctx.writeText(','); ctx.writeText(`"${key}":`); encodeFn(ctx, new JsExpression_1.JsExpression(() => `${r}${accessor}`), field.value); hasFields = true; }; if (isOptional) { ctx.js(`if (${r}${accessor} !== undefined) {`); writeField(); ctx.js(`}`); } else { writeField(); } } ctx.writeText('}'); }; exports.obj = obj; const map = (ctx, value, type, encodeFn) => { const mapType = type; // MapType const codegen = ctx.codegen; const r = codegen.var(value.use()); const rKeys = codegen.var(`Object.keys(${r})`); const rKey = codegen.var(); const rLen = codegen.var(`${rKeys}.length`); const ri = codegen.var('0'); const rll = codegen.var(`${rLen} - 1`); ctx.writeText('{'); ctx.js(`var ${rKeys}, ${rKey}, ${rLen}, ${ri}, ${rll};`); ctx.js(`${rKeys} = Object.keys(${r});`); ctx.js(`${rLen} = ${rKeys}.length;`); ctx.js(`${rll} = ${rLen} - 1;`); ctx.js(`for (; ${ri} < ${rll}; ${ri}++) {`); ctx.js(`${rKey} = ${rKeys}[${ri}];`); ctx.js(`s += '"' + ${rKey} + '":';`); encodeFn(ctx, new JsExpression_1.JsExpression(() => `${r}[${rKey}]`), mapType.valueType); ctx.js(`s += ',';`); ctx.js(`}`); ctx.js(`if (${rLen}) {`); ctx.js(`${rKey} = ${rKeys}[${rll}];`); ctx.js(`s += '"' + ${rKey} + '":';`); encodeFn(ctx, new JsExpression_1.JsExpression(() => `${r}[${rKey}]`), mapType.valueType); ctx.js(`}`); ctx.writeText('}'); }; exports.map = map; const ref = (ctx, value, type) => { const refType = type; // RefType const system = ctx.options.system || refType.system; if (!system) throw new Error('NO_SYSTEM'); const encoder = system.resolve(refType.schema.ref).type.jsonTextEncoder(); const d = ctx.codegen.linkDependency(encoder); ctx.js(`s += ${d}(${value.use()});`); }; exports.ref = ref; const or = (ctx, value, type, encodeFn) => { const orType = type; // OrType const codegen = ctx.codegen; const discriminator = orType.discriminator(); const d = codegen.linkDependency(discriminator); const types = orType.types; codegen.switch(`${d}(${value.use()})`, types.map((childType, index) => [ index, () => { encodeFn(ctx, value, childType); }, ])); }; exports.or = or; /** * Main router function that dispatches JSON text encoding to the appropriate * encoder function based on the type's kind. */ const generate = (ctx, value, type) => { const kind = type.getTypeName(); switch (kind) { case 'any': (0, exports.any)(ctx, value, type); break; case 'bool': (0, exports.bool)(ctx, value); break; case 'num': (0, exports.num)(ctx, value); break; case 'str': (0, exports.str)(ctx, value, type); break; case 'bin': (0, exports.bin)(ctx, value); break; case 'con': (0, exports.const_)(ctx, value, type); break; case 'arr': (0, exports.arr)(ctx, value, type, exports.generate); break; case 'tup': (0, exports.tup)(ctx, value, type, exports.generate); break; case 'obj': (0, exports.obj)(ctx, value, type, exports.generate); break; case 'map': (0, exports.map)(ctx, value, type, exports.generate); break; case 'ref': (0, exports.ref)(ctx, value, type); break; case 'or': (0, exports.or)(ctx, value, type, exports.generate); break; default: throw new Error(`${kind} type JSON text encoding not implemented`); } }; exports.generate = generate;