Skip to content

Commit

Permalink
wip(objects): 扩展 safeJsonParse 方法,支持尝试加载 JSON5 进行解析
Browse files Browse the repository at this point in the history
  • Loading branch information
renxia committed Jan 1, 2024
1 parent 9cb3f1f commit b62b7b2
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 17 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"eslint-plugin-unicorn": "^50.0.1",
"husky": "^8.0.3",
"jest": "^29.7.0",
"json5": "^2.2.3",
"micromatch": "^4.0.5",
"npm-run-all": "^4.1.5",
"prettier": "^3.1.1",
Expand Down
8 changes: 8 additions & 0 deletions src/common/objects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
safeStringify,
ensureArray,
safeJsonParse,
tryLoadJSON5,
} from './objects';

describe('objects/assign', () => {
Expand All @@ -18,6 +19,13 @@ describe('objects/assign', () => {
expect(safeJsonParse(safeStringify(a))).toStrictEqual(a);
expect(safeJsonParse('')).toStrictEqual({});
expect(safeJsonParse(null as never)).toStrictEqual({});
expect(safeJsonParse('abc', true, true)).toStrictEqual({});
});

it('safeJsonParse use JSON5', async () => {
const jsonStr = `{\n// test\n "a": 1, 'b': 2, c: 3}`;
await tryLoadJSON5();
expect(safeJsonParse(jsonStr, true).c).toBe(3);
});

it('safeStringify', () => {
Expand Down
52 changes: 38 additions & 14 deletions src/common/objects.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,54 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-explicit-any, unicorn/prefer-top-level-await */
import { isObject, isSet, isMap, isArray } from './is';

export function safeJsonParse<T extends Record<string, string>>(input: string): T {
let JSON5: typeof globalThis.JSON;

export async function tryLoadJSON5() {
if (!JSON5) {
JSON5 = globalThis.JSON;
try {
// @ts-ignore
if (globalThis.JSON5) JSON5 = globalThis.JSON5;
else {
const t = await import('json5');
JSON5 = (t.default || t) as never;
}
} catch {
// quit
}
}
return JSON5;
}
tryLoadJSON5();

export function safeJsonParse<T extends Record<string, string>>(input: string, useJSON5 = false, ignoreError = false): T {
try {
if (input == null) return {} as T;
if (typeof input === 'object') return input;
return JSON.parse(input) as T;
return (useJSON5 ? JSON5 : JSON).parse(input) as T;
} catch (error: any) {
console.error('[safeJsonParse]parse error', error.message, error.stack);
if (!ignoreError) console.error('[safeJsonParse]parse error', error.message, error.stack);
}

return {} as T;
}

export function safeStringify(obj: any): string {
export function safeStringify(obj: any, space?: string | number, useJSON5 = false): string {
const seen = new Set<any>();
return JSON.stringify(obj, (_key, value) => {
if (isObject(value) || Array.isArray(value)) {
if (seen.has(value)) {
return '[Circular]';
} else {
seen.add(value);
return (useJSON5 ? JSON5 : JSON).stringify(
obj,
(_key, value) => {
if (isObject(value) || Array.isArray(value)) {
if (seen.has(value)) {
return '[Circular]';
} else {
seen.add(value);
}
}
}
return value;
});
return value;
},
space
);
}

/**
Expand Down
6 changes: 3 additions & 3 deletions src/node/LiteStorage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { homedir } from 'node:os';
import { dirname, resolve } from 'node:path';
import { fs } from './fs-system';
import { assign } from '../common/objects';
import { assign, safeJsonParse, safeStringify } from '../common/objects';

export interface LSCache<T> {
version: string;
Expand Down Expand Up @@ -84,7 +84,7 @@ export class LiteStorage<T extends object = Record<string, unknown>> {
const TOML = await import('@iarna/toml');
content = TOML.stringify(this.cache as never);
} else {
content = JSON.stringify(this.cache, null, 4);
content = safeStringify(this.cache, 4, true);
}
fs.writeFileSync(this.cachePath, content, 'utf8');
return this;
Expand All @@ -98,7 +98,7 @@ export class LiteStorage<T extends object = Record<string, unknown>> {
const TOML = await import('@iarna/toml');
localCache = JSON.parse(JSON.stringify(TOML.default.parse(content)));
} else {
localCache = JSON.parse(content) as LSCache<T>;
localCache = safeJsonParse<never>(content, true) as LSCache<T>;
}

if (localCache.version === this.options.version) {
Expand Down

0 comments on commit b62b7b2

Please sign in to comment.