@jsonjoy.com/json-type
Version:
High-performance JSON Pointer implementation
226 lines (225 loc) • 8.19 kB
JavaScript
;
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;