UNPKG

@rickyli79/rich-json

Version:

Serialize and Deserialize javascript objects

165 lines (164 loc) 5.24 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.stringify = void 0; const lib_1 = require("./lib"); const MARK = Symbol('mark'); const IGNORE = Symbol('ignore'); let NATIVE_MARK; let nativeKeys; let usedMark; let buildinSerializers; function createRichValue(s) { if (s.content !== null && typeof s.content === 'object') { if (s.serializContent) { s.content[MARK] = s.raw; } else { s.content[IGNORE] = s.raw; } } return { [MARK]: MARK, [NATIVE_MARK]: 1, [nativeKeys.type]: s.type, [nativeKeys.className]: s.className, [nativeKeys.content]: s.content, }; } function isNativeProperty(value) { return typeof value === 'object' && value?.[MARK] === MARK; } function toRichRef(ref) { return createRichValue({ raw: undefined, type: '$ref', content: ref, serializContent: false, }); } function toRichClass(value, serializer) { const className = serializer.className; let isPlainContent = false; const content = serializer.toContent(value, () => { isPlainContent = true; }); if (isPlainContent) { return content; } return createRichValue({ raw: value, className, type: 'Class', content, serializContent: serializer.serializContent, }); } function nativeReplacer(key, value, map, fullpath) { if (value === null || value === undefined || typeof value === 'string') return value; const boxedValue = Object(value); if (value === boxedValue) map.set(value, fullpath); (0, lib_1.calMarkId)(value, nativeKeys, usedMark); for (const i in lib_1.customerSerializers) { const iConverter = lib_1.customerSerializers[i]; if (iConverter.isType(value)) { return toRichClass(value, iConverter); } } for (const i in buildinSerializers) { const iConverter = buildinSerializers[i]; if (iConverter.isType(boxedValue)) { return toRichClass(boxedValue, iConverter); } } return value; } function stringify(value, options) { const { pretty, excludKeys = [] } = options ?? {}; const _excludKeys = Array.isArray(excludKeys) ? excludKeys : [excludKeys]; const root = value; const map = new Map(); const toJsonMap = new Map(); NATIVE_MARK = '-MARK-' + Math.random() + Date.now() + Math.random(); const SP = '#'; nativeKeys = (0, lib_1.getNativeKeys)(SP); usedMark = []; try { buildinSerializers = (0, lib_1.getBuildinSerializers)(); buildinSerializers.forEach(iSerializer => { toJsonMap.set(iSerializer.class, iSerializer.class.prototype.toJSON); iSerializer.class.prototype.toJSON = function () { return this; }; }); if (lib_1.isNodeJs) { toJsonMap.set(Buffer, Buffer.prototype.toJSON); Buffer.prototype.toJSON = function () { return this; }; } lib_1.customerSerializers.forEach(iSerializer => { if (!toJsonMap.has(iSerializer.class)) { toJsonMap.set(iSerializer.class, iSerializer.class.prototype.toJSON); } iSerializer.class.prototype.toJSON = function () { return this; }; }); const wrap = { [MARK]: MARK, MARK: NATIVE_MARK, SP, root, }; let re = JSON.stringify(wrap, function (key, value) { if (value === wrap) return value; const include = this[MARK] ?? true; if (!include) return; if (value === undefined) return; if (_excludKeys.some(i => { if (i instanceof RegExp) { return i.test(key); } return i === key; })) { return; } if (value === null || typeof value === 'string') return value; let fullpath; const hasMark = isNativeProperty(this); if (Object.is(value, root)) { fullpath = ['$']; } else if (!hasMark && !this[IGNORE]) { const parentJp = map.get(this[MARK] ?? this); fullpath = [...parentJp, key]; } if (!hasMark && map.has(value)) { return toRichRef(map.get(value)); } return nativeReplacer.call(this, key, value, map, fullpath); }, pretty); for (const [clazz, toJSON] of toJsonMap.entries()) { clazz.prototype.toJSON = toJSON; } let nextMarkId = 0; let markKey; do { markKey = (0, lib_1.getMarkKey)(nextMarkId++, nativeKeys.sp); } while (usedMark.includes(markKey)); re = re.replace(new RegExp('"' + NATIVE_MARK + '"', 'g'), '"' + markKey + '"'); return re; } finally { usedMark = []; toJsonMap.clear(); map.clear(); } } exports.stringify = stringify;