• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hexojs / hexo-util / 16651986209

31 Jul 2025 02:35PM UTC coverage: 73.036% (-23.8%) from 96.875%
16651986209

Pull #426

github

web-flow
Merge e24598988 into d497bc760
Pull Request #426: build: restructure for dual ESM/CJS output, update tooling, and enhance utilities

811 of 1128 branches covered (71.9%)

1594 of 2028 new or added lines in 40 files covered. (78.6%)

6 existing lines in 1 file now uncovered.

5290 of 7243 relevant lines covered (73.04%)

69.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.33
/lib/json_stringify_circular.ts
1
/* eslint no-fallthrough: ["error", { "commentPattern": "break[\\s\\w]*omitted" }] */
2✔
2

2✔
3

2✔
4
/* ! (c) 2020 Andrea Giammarchi */
2✔
5
/* source https://github.com/WebReflection/flatted/blob/main/cjs/index.js */
2✔
6

2✔
7
const { parse: $parse, stringify: $stringify } = JSON;
2✔
8
const { keys } = Object;
2✔
9

2✔
10
const isObject = (value: unknown) => typeof value === 'object' && value !== null;
2✔
11

2✔
12
const ignore = {};
2✔
13

2✔
14
const noop = (_: unknown, value: unknown) => value;
2✔
15

2✔
16
/**
2✔
17
 * Recursively revives circular references in a parsed object.
2✔
18
 *
2✔
19
 * @param input - The array of parsed objects.
2✔
20
 * @param parsed - A set of already parsed objects to avoid infinite recursion.
2✔
21
 * @param output - The current output object being revived.
2✔
22
 * @param $ - The reviver function to apply to each key/value pair.
2✔
23
 * @returns The revived object with circular references restored.
2✔
24
 */
2✔
25
const revive = (
2✔
26
  input: unknown[],
2,012✔
27
  parsed: Set<unknown>,
2,012✔
28
  output: Record<string, unknown>,
2,012✔
29
  $: (key: string, value: unknown) => unknown
2,012✔
30
): unknown => {
2,012✔
31
  const lazy: Array<{ k: string; a: [unknown[], Set<unknown>, Record<string, unknown>, (key: string, value: unknown) => unknown] }> = [];
2,012✔
32
  for (let ke = keys(output), { length } = ke, y = 0; y < length; y++) {
2,012✔
33
    const k = ke[y];
5,026✔
34
    const value = output[k];
5,026✔
35
    if (typeof value === 'string' && /^\d+$/.test(value)) {
5,026✔
36
      // Only treat as reference if value is a string that is a number (index)
3,007✔
37
      const tmp = input[Number(value)];
3,007✔
38
      if (isObject(tmp) && !parsed.has(tmp)) {
3,007✔
39
        parsed.add(tmp);
2,006✔
40
        output[k] = ignore;
2,006✔
41
        lazy.push({ k, a: [input, parsed, tmp as Record<string, unknown>, $] });
2,006✔
42
      } else output[k] = $.call(output, k, tmp);
3,007✔
43
    } else if (output[k] !== ignore) output[k] = $.call(output, k, value);
5,026✔
44
  }
5,026✔
45
  for (let { length } = lazy, i = 0; i < length; i++) {
2,012✔
46
    const { k, a } = lazy[i];
2,006✔
47
    // eslint-disable-next-line prefer-spread
2,006✔
48
    output[k] = $.call(output, k, revive.apply(null, a));
2,006✔
49
  }
2,006✔
50
  return output;
2,012✔
51
};
2,012✔
52

2✔
53
/**
2✔
54
 * Adds a value to a set of known values and returns its index as a string.
2✔
55
 *
2✔
56
 * @param known - A map of known objects to their indices.
2✔
57
 * @param input - The array of input objects.
2✔
58
 * @param value - The value to add.
2✔
59
 * @returns The index of the value as a string.
2✔
60
 */
2✔
61
const set = (known: Map<unknown, string>, input: unknown[], value: unknown): string => {
2✔
62
  const index = String(input.push(value) - 1);
2,008✔
63
  known.set(value, index);
2,008✔
64
  return index;
2,008✔
65
};
2,008✔
66

2✔
67
/**
2✔
68
 * Parses a JSON string with support for circular references.
2✔
69
 *
2✔
70
 * @template T
2✔
71
 * @param text - The JSON string to parse.
2✔
72
 * @param reviver - Optional function to transform the parsed values.
2✔
73
 * @returns The parsed object of type T.
2✔
74
 */
2✔
75
const parse = <T = unknown>(text: string, reviver?: (this: unknown, key: string, value: unknown) => unknown): T => {
2✔
76
  const input = $parse(text);
6✔
77
  const value = input[0];
6✔
78
  const $ = reviver || noop;
6✔
79
  const tmp = isObject(value) ? revive(input, new Set(), value, $) : value;
6!
80
  return $.call({ '': tmp }, '', tmp) as T;
6✔
81
};
6✔
82

2✔
83
/**
2✔
84
 * Stringifies an object into JSON with support for circular references.
2✔
85
 *
2✔
86
 * @param value - The object to stringify.
2✔
87
 * @param replacer - Optional function or array of strings to transform the values before stringifying.
2✔
88
 * @param space - Optional number or string to use as white space in the output.
2✔
89
 * @returns The JSON string representation of the object.
2✔
90
 */
2✔
91
const stringify = (
2✔
92
  value: unknown,
6✔
93
  replacer?: ((this: unknown, key: string, value: unknown) => unknown) | string[],
6✔
94
  space?: string | number
6✔
95
): string => {
6✔
96
  const isCallable = typeof replacer === 'function';
6✔
97
  let $: (k: string, v: unknown) => unknown;
6✔
98
  if (isCallable) {
6!
NEW
99
    $ = replacer as (k: string, v: unknown) => unknown;
×
100
  } else if (typeof replacer === 'object') {
6!
NEW
101
    $ = (k: string, v: unknown) => {
×
NEW
102
      if (k === '' || (replacer as string[]).indexOf(k) !== -1) return v;
×
NEW
103
      return undefined;
×
NEW
104
    };
×
105
  } else {
6✔
106
    $ = noop;
6✔
107
  }
6✔
108
  const known = new Map<unknown, string>();
6✔
109
  const input: unknown[] = [];
6✔
110
  const output: string[] = [];
6✔
111
  let i = +set(known, input, $.call({ '': value }, '', value));
6✔
112
  let firstRun = !i;
6✔
113
  while (i < input.length) {
6✔
114
    firstRun = true;
2,008✔
115
    output[i] = $stringify(input[i++], replace, space);
2,008✔
116
  }
2,008✔
117
  return '[' + output.join(',') + ']';
6✔
118

6✔
119
  function replace(this: unknown, key: string, value: unknown): unknown {
6✔
120
    if (firstRun) {
7,025✔
121
      firstRun = false;
2,008✔
122
      return value;
2,008✔
123
    }
2,008✔
124
    const after = $.call(this, key, value);
5,017✔
125
    if (isObject(after)) {
7,025✔
126
      if (after === null) return after;
3,007!
127
      return known.get(after) || set(known, input, after);
3,007✔
128
    }
3,007✔
129
    return after;
2,010✔
130
  }
2,010✔
131
};
6✔
132

2✔
133
/**
2✔
134
 * Converts an object with circular references to a JSON-compatible object.
2✔
135
 *
2✔
136
 * @param anyData - The object to convert.
2✔
137
 * @returns The JSON-compatible representation of the object.
2✔
138
 */
2✔
139
const toJSON = (anyData: unknown): unknown => $parse(stringify(anyData));
2✔
140
export { toJSON };
2✔
141

2✔
142
/**
2✔
143
 * Parses a circular object from a JSON string.
2✔
144
 *
2✔
145
 * @template T
2✔
146
 * @param anyData - The JSON string to parse.
2✔
147
 * @returns The parsed object of type T.
2✔
148
 */
2✔
149
const fromJSON = <T = unknown>(anyData: string): T => parse<T>($stringify(anyData));
2✔
150
export { fromJSON, parse, stringify };
2✔
151

2✔
152
/**
2✔
153
 * Transforms any object to a JSON string, suppressing `TypeError: Converting circular structure to JSON`.
2✔
154
 *
2✔
155
 * @param data - The object to stringify.
2✔
156
 * @returns The JSON string representation.
2✔
157
 */
2✔
158
function jsonStringifyWithCircular(data: unknown): string {
6✔
159
  return stringify(data);
6✔
160
}
6✔
161
export { jsonStringifyWithCircular as jsonStringify };
2✔
162

2✔
163
/**
2✔
164
 * Parses a JSON string that was stringified with circular references (browser version).
2✔
165
 *
2✔
166
 * @template T
2✔
167
 * @param data - The JSON string to parse.
2✔
168
 * @returns The parsed object of type T.
2✔
169
 */
2✔
170
function jsonParseWithCircular<T>(data: string): T {
6✔
171
  return parse(data) as T;
6✔
172
}
6✔
173
export { jsonParseWithCircular as jsonParse };
2✔
174

2✔
175
// for CommonJS compatibility
2✔
176
if (typeof module !== 'undefined' && typeof module.exports === 'object' && module.exports !== null) {
2!
177
  module.exports = {
1✔
178
    parse,
1✔
179
    stringify,
1✔
180
    jsonStringifyWithCircular,
1✔
181
    jsonStringify: jsonStringifyWithCircular,
1✔
182
    jsonParseWithCircular,
1✔
183
    jsonParse: jsonParseWithCircular,
1✔
184
    toJSON,
1✔
185
    fromJSON
1✔
186
  };
1✔
187
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc