UNPKG

jsoneo

Version:

A powerful JSON enhancement library that supports all JSON primitives, Date, RegExp, Symbol, Functions, Map, Set, TypedArray and much more! Almost everything in JavaScript.

127 lines (126 loc) 4.88 kB
import { DefaultEndTag, DefaultStartTag } from "./consts.js"; import { toSymbolString, WellKnownSymbols } from "./symbol.js"; export function serializeRecursively( // eslint-disable-next-line @typescript-eslint/no-explicit-any source, options) { const { startTag: ST = DefaultStartTag, endTag: ET = DefaultEndTag, parentPath = [], debug, printLabel, printPath } = options ?? {}; // const source = obj; if (debug && !parentPath.length) { console.log(`------------------ serializeRecursively${printLabel ? ` (${printLabel})` : ''}`, ['BEGIN'], ' ------------------'); console.log(source); } if (source === null) { return `${ST}null${ET}`; } else if (source === undefined) { return undefined; } else if ('isRawJSON' in JSON && typeof JSON.isRawJSON === 'function' && JSON.isRawJSON(source)) { return source; } else if (typeof source === 'number') { if (Number.isNaN(source)) { return `${ST}NaN${ET}`; } else if (source === Number.POSITIVE_INFINITY) { return `${ST}Infinity${ET}`; } else if (source === Number.NEGATIVE_INFINITY) { return `${ST}-Infinity${ET}`; } else { return `${ST}${source}${ET}`; } } else if (source instanceof Date) { if (Number.isNaN(source.getTime())) { return `${ST}new Date(NaN)${ET}`; } return `${ST}new Date('${source.toISOString()}')${ET}`; } else if (typeof source === 'bigint') { return `${ST}BigInt('${source.toString()}')${ET}`; } else if (source instanceof RegExp) { return `${ST}new RegExp('${source.source.replace(/\\\\/g, '\\')}', '${source.flags ?? ''}')${ET}`; // `value instanceof Date` never works, try testing date format instead } else if (typeof source === 'symbol') { if (WellKnownSymbols.includes(source)) { return `${ST}${source.description}${ET}`; } else if (Symbol.keyFor(source)) { return `${ST}Symbol.for('${Symbol.keyFor(source)}')${ET}`; } return `${ST}Symbol('${source.description}')${ET}`; } else if (source instanceof Error) { return `${ST}new Error('${source.message}')${ET}`; } else { if (typeof source === 'object') { // todo: 反序列化时,需要反解析两次,第一次作为source,第二个作为target。避免在处理types或apis时,source被修改从而导致patches获取不到子代的值 // Save the symbol keys to a string property, because symbol keys are not serializable. // They will be restored in the deserialization process. for (const symbol of Object.getOwnPropertySymbols(source)) { // only predefined symbols and keyed symbols are preserved const symbolString = toSymbolString(symbol); const subValue = source[symbol]; if (!symbolString || subValue === null) { continue; } source[symbolString] = subValue; // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete source[symbol]; } Object.keys(source).forEach(key => { const subValue = source[key]; const serializedValue = serializeRecursively(subValue, { ...options, parentPath: [...parentPath, key], printLabel }); if (debug) { console.log(`------------------ serializeRecursively${printLabel ? ` (${printLabel})` : ''}`, printPath ? printPath({ parentPath, key }) : [...parentPath, key], ' ------------------'); console.log('value:', subValue); console.log('result:'); console.log(serializedValue); } source[key] = serializedValue; }); return source; } else if (typeof source === 'function') { let funcStr = serializeFunction(source.toString()); if (funcStr === undefined) { return undefined; } funcStr = funcStr.replace(/"/g, "'"); return `${ST}(${funcStr})${ET}`; } else { // rest primitive types, including boolean and string return source; } } } export function serializeFunction(funcStr) { if (funcStr.includes('{ [native code] }')) { return undefined; } if ( // function () {} !funcStr.startsWith('function') && // async function () {} !funcStr.startsWith('async function') && // class {} !funcStr.startsWith('class') && // function* () {} !funcStr.startsWith('function*') && // async function* () {} !funcStr.startsWith('async function*') && // () => {} !funcStr.replace(/\s/g, '').match(/^\(?[^)]+\)?=>/)) { // If it's a computed property function, for example: { [Symbol.toPrimitive]() { return 1; } } // the funcStr is like: `[Symbol.toPrimitive]() { return 1; }`, so we can safely remove it. funcStr = funcStr.replace(/^\[[^\]]+\]/, ''); funcStr = `function ${funcStr}`; } return funcStr; } //# sourceMappingURL=serializeRecursively.js.map