Skip to content

Commit bc0691a

Browse files
committed
Refactor object types, pass key down stringify
1 parent 3dc0f04 commit bc0691a

File tree

5 files changed

+88
-99
lines changed

5 files changed

+88
-99
lines changed

src/array.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { Next } from "./types";
1+
import { ToString } from "./types";
22

33
/**
44
* Stringify an array of values.
55
*/
6-
export function arrayToString(array: any[], space: string, next: Next) {
6+
export const arrayToString: ToString = (array: any[], space, next) => {
77
// Map array values to their stringified values with correct indentation.
88
const values = array
99
.map(function(value, index) {
@@ -15,10 +15,6 @@ export function arrayToString(array: any[], space: string, next: Next) {
1515
})
1616
.join(space ? ",\n" : ",");
1717

18-
// Wrap the array in newlines if we have indentation set.
19-
if (space && values) {
20-
return "[\n" + values + "\n]";
21-
}
22-
23-
return "[" + values + "]";
24-
}
18+
const eol = space && values ? "\n" : "";
19+
return `[${eol}${values}${eol}]`;
20+
};

src/index.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,16 @@ export function stringify(
4747
if (path.length > maxDepth) return;
4848

4949
// An undefined key is treated as an out-of-band "value".
50-
if (key === undefined) return valueToString(value, space, onNext);
50+
if (key === undefined) return valueToString(value, space, onNext, key);
5151

5252
path.push(key);
53-
const result = builder(value);
53+
const result = builder(value, key === ROOT_SENTINEL ? undefined : key);
5454
path.pop();
5555
return result;
5656
};
5757

58-
const builder = references
59-
? (value: any): string | undefined => {
58+
const builder: Next = references
59+
? (value, key) => {
6060
if (
6161
value !== null &&
6262
(typeof value === "object" ||
@@ -73,14 +73,14 @@ export function stringify(
7373
tracking.set(value, path.slice(1));
7474
}
7575

76-
return valueToString(value, space, onNext);
76+
return valueToString(value, space, onNext, key);
7777
}
78-
: (value: any): string | undefined => {
78+
: (value, key) => {
7979
// Stop on recursion.
8080
if (stack.has(value)) return;
8181

8282
stack.add(value);
83-
const result = valueToString(value, space, onNext);
83+
const result = valueToString(value, space, onNext, key);
8484
stack.delete(value);
8585
return result;
8686
};
@@ -109,10 +109,15 @@ export function stringify(
109109
/**
110110
* Create `toString()` function from replacer.
111111
*/
112-
function replacerToString(replacer?: ToString | null) {
112+
function replacerToString(replacer?: ToString | null): ToString {
113113
if (!replacer) return toString;
114114

115-
return (value: any, space: string, next: Next) => {
116-
return replacer(value, space, (value: any) => toString(value, space, next));
115+
return (value, space, next, key) => {
116+
return replacer(
117+
value,
118+
space,
119+
(value: any) => toString(value, space, next, key),
120+
key
121+
);
117122
};
118123
}

src/object.ts

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
1-
import { Next } from "./types";
1+
import { Next, ToString } from "./types";
22
import { quoteKey } from "./quote";
3-
import { FunctionParser } from "./function";
3+
import { USED_METHOD_KEY } from "./function";
4+
import { arrayToString } from "./array";
5+
6+
/**
7+
* Transform an object into a string.
8+
*/
9+
export const objectToString: ToString = (value, space, next, key) => {
10+
if (typeof (Buffer as unknown) === "function" && Buffer.isBuffer(value)) {
11+
return `new Buffer(${next(value.toString())})`;
12+
}
13+
14+
// Use the internal object string to select stringify method.
15+
const toString = OBJECT_TYPES[Object.prototype.toString.call(value)];
16+
return toString ? toString(value, space, next, key) : undefined;
17+
};
418

519
/**
620
* Stringify an object of keys and values.
721
*/
8-
export function objectToString(obj: any, indent: string, next: Next) {
22+
const rawObjectToString: ToString = (obj, indent, next) => {
923
const eol = indent ? "\n" : "";
24+
const space = indent ? " " : "";
1025

1126
// Iterate over object keys and concat string together.
1227
const values = Object.keys(obj)
@@ -41,4 +56,43 @@ export function objectToString(obj: any, indent: string, next: Next) {
4156
if (values === "") return "{}";
4257

4358
return `{${eol}${values}${eol}}`;
44-
}
59+
};
60+
61+
/**
62+
* Stringify global variable access.
63+
*/
64+
const globalToString: ToString = (value, space, next) => {
65+
return `Function(${next("return this")})()`;
66+
};
67+
68+
/**
69+
* Convert JavaScript objects into strings.
70+
*/
71+
const OBJECT_TYPES: Record<string, ToString> = {
72+
"[object Array]": arrayToString,
73+
"[object Object]": rawObjectToString,
74+
"[object Error]": (error: Error, space: string, next: Next) => {
75+
return `new Error(${next(error.message)})`;
76+
},
77+
"[object Date]": (date: Date) => {
78+
return `new Date(${date.getTime()})`;
79+
},
80+
"[object String]": (str: String, space: string, next: Next) => {
81+
return `new String(${next(str.toString())})`;
82+
},
83+
"[object Number]": (num: number) => {
84+
return `new Number(${num})`;
85+
},
86+
"[object Boolean]": (bool: boolean) => {
87+
return `new Boolean(${bool})`;
88+
},
89+
"[object Set]": (set: Set<any>, space: string, next: Next) => {
90+
return `new Set(${next(Array.from(set))})`;
91+
},
92+
"[object Map]": (map: Map<any, any>, space: string, next: Next) => {
93+
return `new Map(${next(Array.from(map))})`;
94+
},
95+
"[object RegExp]": String,
96+
"[object global]": globalToString,
97+
"[object Window]": globalToString
98+
};

src/stringify.ts

Lines changed: 8 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { quoteString } from "./quote";
2-
import { Next } from "./types";
3-
import { arrayToString } from "./array";
2+
import { Next, ToString } from "./types";
43
import { objectToString } from "./object";
54
import { functionToString } from "./function";
65

76
/**
87
* Stringify primitive values.
98
*/
10-
const PRIMITIVE_TYPES = {
9+
const PRIMITIVE_TYPES: Record<string, ToString> = {
1110
string: quoteString,
1211
number: (value: number) => (Object.is(value, -0) ? "-0" : String(value)),
1312
boolean: String,
@@ -19,82 +18,16 @@ const PRIMITIVE_TYPES = {
1918
// ES2018 `Symbol.description`.
2019
return `Symbol(${next((value as any).description)})`;
2120
},
22-
undefined: String
23-
};
24-
25-
/**
26-
* Stringify global variable access.
27-
*/
28-
function globalToString(value: any, space: string, next: Next) {
29-
return `Function(${next("return this")})()`;
30-
}
31-
32-
/**
33-
* Convert JavaScript objects into strings.
34-
*/
35-
const OBJECT_TYPES = {
36-
"[object Array]": arrayToString,
37-
"[object Object]": objectToString,
38-
"[object Error]": function(error: Error, space: string, next: Next) {
39-
return `new Error(${next(error.message)})`;
40-
},
41-
"[object Date]": function(date: Date) {
42-
return `new Date(${date.getTime()})`;
43-
},
44-
"[object String]": function(str: String, space: string, next: Next) {
45-
return `new String(${next(str.toString())})`;
46-
},
47-
"[object Number]": function(num: number) {
48-
return `new Number(${num})`;
49-
},
50-
"[object Boolean]": function(bool: boolean) {
51-
return `new Boolean(${bool})`;
52-
},
53-
"[object Set]": function(set: Set<any>, space: string, next: Next) {
54-
return `new Set(${next(Array.from(set))})`;
55-
},
56-
"[object Map]": function(map: Map<any, any>, space: string, next: Next) {
57-
return `new Map(${next(Array.from(map))})`;
58-
},
59-
"[object RegExp]": String,
60-
"[object Function]": functionToString,
61-
"[object GeneratorFunction]": functionToString,
62-
"[object AsyncFunction]": functionToString,
63-
"[object AsyncGeneratorFunction]": functionToString,
64-
"[object global]": globalToString,
65-
"[object Window]": globalToString
21+
undefined: String,
22+
object: objectToString,
23+
function: functionToString
6624
};
6725

6826
/**
6927
* Stringify a value recursively.
7028
*/
71-
export function toString(value: any, space: string, next: Next) {
29+
export const toString: ToString = (value, space, next, key) => {
7230
if (value === null) return "null";
7331

74-
const typeOf = typeof value;
75-
76-
if (PRIMITIVE_TYPES.hasOwnProperty(typeOf)) {
77-
return PRIMITIVE_TYPES[typeOf as keyof typeof PRIMITIVE_TYPES](
78-
value,
79-
space,
80-
next
81-
);
82-
}
83-
84-
// Handle buffer objects before object types (node < 6 was an object, node >= 6 is a `Uint8Array`).
85-
if (typeof (Buffer as unknown) === "function" && Buffer.isBuffer(value)) {
86-
return `new Buffer(${next(value.toString())})`;
87-
}
88-
89-
// Use the internal object string to select stringify method.
90-
const toString = Object.prototype.toString.call(value);
91-
92-
// Convert objects into strings.
93-
if (OBJECT_TYPES.hasOwnProperty(toString)) {
94-
return OBJECT_TYPES[toString as keyof typeof OBJECT_TYPES](
95-
value,
96-
space,
97-
next
98-
);
99-
}
100-
}
32+
return PRIMITIVE_TYPES[typeof value](value, space, next, key);
33+
};

src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ export type Next = (value: any, key?: PropertyKey) => string | undefined;
99
export type ToString = (
1010
value: any,
1111
space: string,
12-
next: Next
12+
next: Next,
13+
key: PropertyKey | undefined
1314
) => string | undefined;

0 commit comments

Comments
 (0)