mcl-test
Version:
174 lines (162 loc) • 4.5 kB
text/typescript
interface JSONFlattenOptions {
key?: string;
index?: number;
showComma: boolean;
length: number;
type:
| 'content'
| 'objectStart'
| 'objectEnd'
| 'objectCollapsed'
| 'arrayStart'
| 'arrayEnd'
| 'arrayCollapsed';
}
export type JSONDataType = string | number | boolean | unknown[] | Record<string, unknown> | null;
export interface JSONFlattenReturnType extends JSONFlattenOptions {
content: string | number | null | boolean;
level: number;
path: string;
}
export function emitError(message: string): void {
throw new Error(`[VueJSONPretty] ${message}`);
}
export function getDataType(value: unknown): string {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}
export function jsonFlatten(
data: JSONDataType,
path = '',
level = 0,
options?: JSONFlattenOptions,
): JSONFlattenReturnType[] {
const {
key,
index,
type = 'content',
showComma = false,
length = 1,
} = options || ({} as JSONFlattenOptions);
const dataType = getDataType(data);
if (dataType === 'array') {
const inner = arrFlat(
(data as JSONDataType[]).map((item, idx, arr) =>
jsonFlatten(item, `${path}/${idx}`, level + 1, {
index: idx,
showComma: idx !== arr.length - 1,
length,
type,
}),
),
) as JSONFlattenReturnType[];
return [
jsonFlatten('[', path, level, {
showComma: false,
key,
length: (data as unknown[]).length,
type: 'arrayStart',
})[0],
].concat(
inner,
jsonFlatten(']', path, level, {
showComma,
length: (data as unknown[]).length,
type: 'arrayEnd',
})[0],
);
} else if (dataType === 'object') {
const keys = Object.keys(data as Record<string, JSONDataType>);
const inner = arrFlat(
keys.map((objKey, idx, arr) =>
jsonFlatten(
(data as Record<string, JSONDataType>)[objKey],
/^[a-zA-Z_]\w*$/.test(objKey) ? `${path}/${objKey}` : `${path}/${objKey}`,
level + 1,
{
key: objKey,
showComma: idx !== arr.length - 1,
length,
type,
},
),
),
) as JSONFlattenReturnType[];
return [
jsonFlatten('{', path, level, {
showComma: false,
key,
index,
length: keys.length,
type: 'objectStart',
})[0],
].concat(
inner,
jsonFlatten('}', path, level, { showComma, length: keys.length, type: 'objectEnd' })[0],
);
}
return [
{
content: data as JSONFlattenReturnType['content'],
level,
key,
index,
path,
showComma,
length,
type,
},
];
}
export function arrFlat<T extends unknown[]>(arr: T): unknown[] {
if (typeof Array.prototype.flat === 'function') {
return arr.flat();
}
const stack = [...arr];
const result = [];
while (stack.length) {
const first = stack.shift();
if (Array.isArray(first)) {
stack.unshift(...first);
} else {
result.push(first);
}
}
return result;
}
export function cloneDeep<T extends unknown>(source: T, hash = new WeakMap()): T {
if (source === null || source === undefined) return source;
if (source instanceof Date) return new Date(source) as T;
if (source instanceof RegExp) return new RegExp(source) as T;
if (typeof source !== 'object') return source;
if (hash.get(source as Record<string, unknown>))
return hash.get(source as Record<string, unknown>);
if (Array.isArray(source)) {
const output = source.map(item => cloneDeep(item, hash));
hash.set(source, output);
return output as T;
}
const output = {} as T;
for (const key in source) {
output[key] = cloneDeep(source[key], hash);
}
hash.set(source as Record<string, unknown>, output);
return output as T;
}
export function stringToAutoType(source: string): unknown {
let value;
if (source === 'null') value = null;
else if (source === 'undefined') value = undefined;
else if (source === 'true') value = true;
else if (source === 'false') value = false;
else if (
source[0] + source[source.length - 1] === '""' ||
source[0] + source[source.length - 1] === "''"
) {
value = source.slice(1, -1);
} else if ((typeof Number(source) === 'number' && !isNaN(Number(source))) || source === 'NaN') {
value = Number(source);
} else {
value = source;
}
return value;
}