@clynn-fe/akfe-editor-jsonf
Version:
JSON methods support function parse and stringify
111 lines (104 loc) • 3.62 kB
text/typescript
import { EInfinityString, ETypeStrings, LeFE_Prefix, Segment } from './defines'
import {
isArray,
isBigInt,
isDate,
isFunction,
isMap,
isNaN,
isNegativeInfinity,
isObject,
isPositiveInfinity,
isSet,
isSymbol,
isUndefined,
TAnyFunction
} from './type_detectors'
import { blockSelfToJSON, shouldBlockSelfToJSON } from './utils'
const assemble = (type: string, data?: string) =>
`${LeFE_Prefix}${Segment}${type}${data ? Segment + data : ''}`
const isTypesNeededTraverse = (
val: unknown
): val is unknown[] | Record<string, unknown> =>
isArray(val) || (isObject(val) && !isDate(val) && !isSet(val) && !isMap(val))
const isArrowFunction = (str: string) => {
// TODO: 这块判断有点不太好,后期考虑一下,搞个完善的正则什么的
const arr = str.split('=>')
if (arr.length === 1) {
// 只有一项说明字符串里没有 =>, 肯定不是箭头函数
return false
}
const part1 = arr[0].trim() // 取 => 前面的部分
if (part1.startsWith('(') && part1.endsWith(')')) {
// 如果这一部分是 (...) 的话,认为是箭头函数,排除这种: function () { return () => {} }
return true
}
// 如果不是 (....), 那只能是单独是形参变量名,不可能包含 {, 排除这种: function () { return a => {} }, 保住这种 a => {}
return part1.indexOf('{') === -1
}
const functionStringify = (func: TAnyFunction) => {
const str = func.toString()
if (
str.startsWith('function') ||
str.startsWith('async function') ||
isArrowFunction(str)
) {
return str
}
// 这个是为了处理方法,如 { test () {} }
return str.startsWith('async')
? `async function ${str.slice(6)}`
: `function ${str}`
}
const needNotTraverseHandler = (val: unknown) => {
switch (true) {
case isUndefined(val):
return assemble(ETypeStrings.undefined)
case isNegativeInfinity(val):
return assemble(ETypeStrings.Infinity, EInfinityString.negative)
case isPositiveInfinity(val):
return assemble(ETypeStrings.Infinity, EInfinityString.positive)
case isNaN(val):
return assemble(ETypeStrings.NaN)
case isBigInt(val):
return assemble(ETypeStrings.BigInt, (val as bigint).toString())
case isSymbol(val):
// TODO: 辣鸡 TS 有 Bug, 目前不支持 symbol.prototype.description 属性。
// 请关注官方 Release, 支持了以后升级改掉这个 any 类型声明。
return assemble(ETypeStrings.Symbol, (val as any).description)
case isFunction(val):
return assemble(
ETypeStrings.Function,
functionStringify(val as TAnyFunction)
)
case isDate(val):
return assemble(ETypeStrings.Date, `${(val as Date).getTime()}`)
case isMap(val):
return assemble(
ETypeStrings.Map,
stringify(Array.from((val as Map<unknown, unknown>).entries()))
)
case isSet(val):
return assemble(ETypeStrings.Set, stringify([...(val as Set<unknown>)]))
default:
return val
}
}
export const stringify = (data: Record<string, unknown> | unknown[]): string =>
JSON.stringify(data, (key: string, val: unknown) => {
if (!isTypesNeededTraverse(val)) {
return needNotTraverseHandler(val)
} else {
return isObject(val)
? Object.entries(val).reduce(
(prev, [k, v]) =>
Object.assign(prev, {
[k]: shouldBlockSelfToJSON(v) ? blockSelfToJSON(v) : v
}),
{}
)
: val.map(item =>
shouldBlockSelfToJSON(item) ? blockSelfToJSON(item) : item
)
}
})