@@ -19,16 +19,6 @@ export const getKeysForIndexSignature = (
1919 }
2020}
2121
22- /**
23- * JavaScript does not store the insertion order of properties in a way that
24- * combines both string and symbol keys. The internal order groups string keys
25- * and symbol keys separately. Hence concatenating the keys is fine.
26- *
27- * @internal
28- */
29- export const ownKeys = ( o : object ) : Array < PropertyKey > =>
30- ( Object . keys ( o ) as Array < PropertyKey > ) . concat ( Object . getOwnPropertySymbols ( o ) )
31-
3222/** @internal */
3323export const memoizeThunk = < A > ( f : ( ) => A ) : ( ) => A => {
3424 let done = false
@@ -52,58 +42,94 @@ export const formatDate = (date: Date): string => {
5242 }
5343}
5444
45+ const CIRCULAR = "[Circular]"
46+
5547/** @internal */
56- export const formatUnknown = ( u : unknown , checkCircular : boolean = true ) : string => {
57- if ( Array . isArray ( u ) ) {
58- return `[${ u . map ( ( i ) => formatUnknown ( i , checkCircular ) ) . join ( "," ) } ]`
59- }
60- if ( Predicate . isDate ( u ) ) {
61- return formatDate ( u )
62- }
63- if (
64- Predicate . hasProperty ( u , "toString" )
65- && Predicate . isFunction ( u [ "toString" ] )
66- && u [ "toString" ] !== Object . prototype . toString
67- ) {
68- return u [ "toString" ] ( )
69- }
70- if ( Predicate . isString ( u ) ) {
71- return JSON . stringify ( u )
72- }
73- if (
74- Predicate . isNumber ( u )
75- || u == null
76- || Predicate . isBoolean ( u )
77- || Predicate . isSymbol ( u )
78- ) {
79- return String ( u )
48+ export function formatUnknown ( input : unknown , whitespace : number | string | undefined = 0 ) : string {
49+ const seen = new WeakSet < object > ( )
50+ const gap = ! whitespace ? "" : ( typeof whitespace === "number" ? " " . repeat ( whitespace ) : whitespace )
51+ const ind = ( d : number ) => gap . repeat ( d )
52+
53+ const safeToString = ( x : any ) : string => {
54+ try {
55+ const s = x . toString ( )
56+ return typeof s === "string" ? s : String ( s )
57+ } catch {
58+ return "[toString threw]"
59+ }
8060 }
81- if ( Predicate . isBigInt ( u ) ) {
82- return String ( u ) + "n"
61+
62+ const wrap = ( v : unknown , body : string ) : string => {
63+ const ctor = ( v as any ) ?. constructor
64+ return ctor && ctor !== Object . prototype . constructor && ctor . name ? `${ ctor . name } (${ body } )` : body
8365 }
84- if ( Predicate . isIterable ( u ) ) {
85- return `${ u . constructor . name } (${ formatUnknown ( Array . from ( u ) , checkCircular ) } )`
66+
67+ const ownKeys = ( o : object ) : Array < PropertyKey > => {
68+ try {
69+ return Reflect . ownKeys ( o )
70+ } catch {
71+ return [ "[ownKeys threw]" ]
72+ }
8673 }
87- try {
88- if ( checkCircular ) {
89- JSON . stringify ( u ) // check for circular references
74+
75+ function go ( v : unknown , d = 0 ) : string {
76+ if ( Array . isArray ( v ) ) {
77+ if ( seen . has ( v ) ) return CIRCULAR
78+ seen . add ( v )
79+ if ( ! gap || v . length <= 1 ) return `[${ v . map ( ( x ) => go ( x , d ) ) . join ( "," ) } ]`
80+ const inner = v . map ( ( x ) => go ( x , d + 1 ) ) . join ( ",\n" + ind ( d + 1 ) )
81+ return `[\n${ ind ( d + 1 ) } ${ inner } \n${ ind ( d ) } ]`
9082 }
91- const pojo = `{${
92- ownKeys ( u ) . map ( ( k ) =>
93- `${ Predicate . isString ( k ) ? JSON . stringify ( k ) : String ( k ) } :${ formatUnknown ( ( u as any ) [ k ] , false ) } `
94- )
95- . join ( "," )
96- } }`
97- const name = u . constructor . name
98- return u . constructor !== Object . prototype . constructor ? `${ name } (${ pojo } )` : pojo
99- } catch {
100- return "<circular structure>"
83+
84+ if ( Predicate . isDate ( v ) ) return formatDate ( v )
85+
86+ if (
87+ Predicate . hasProperty ( v , "toString" ) &&
88+ Predicate . isFunction ( ( v as any ) [ "toString" ] ) &&
89+ ( v as any ) [ "toString" ] !== Object . prototype . toString
90+ ) return safeToString ( v )
91+
92+ if ( Predicate . isString ( v ) ) return JSON . stringify ( v )
93+
94+ if (
95+ Predicate . isNumber ( v ) ||
96+ v == null ||
97+ Predicate . isBoolean ( v ) ||
98+ Predicate . isSymbol ( v )
99+ ) return String ( v )
100+
101+ if ( Predicate . isBigInt ( v ) ) return String ( v ) + "n"
102+
103+ if ( v instanceof Set || v instanceof Map ) {
104+ if ( seen . has ( v ) ) return CIRCULAR
105+ seen . add ( v )
106+ return `${ v . constructor . name } (${ go ( Array . from ( v ) , d ) } )`
107+ }
108+
109+ if ( Predicate . isObject ( v ) ) {
110+ if ( seen . has ( v ) ) return CIRCULAR
111+ seen . add ( v )
112+ const keys = ownKeys ( v )
113+ if ( ! gap || keys . length <= 1 ) {
114+ const body = `{${ keys . map ( ( k ) => `${ formatPropertyKey ( k ) } :${ go ( ( v as any ) [ k ] , d ) } ` ) . join ( "," ) } }`
115+ return wrap ( v , body )
116+ }
117+ const body = `{\n${
118+ keys . map ( ( k ) => `${ ind ( d + 1 ) } ${ formatPropertyKey ( k ) } : ${ go ( ( v as any ) [ k ] , d + 1 ) } ` ) . join ( ",\n" )
119+ } \n${ ind ( d ) } }`
120+ return wrap ( v , body )
121+ }
122+
123+ return String ( v )
101124 }
125+
126+ return go ( input , 0 )
102127}
103128
104129/** @internal */
105- export const formatPropertyKey = ( name : PropertyKey ) : string =>
106- typeof name === "string" ? JSON . stringify ( name ) : String ( name )
130+ export function formatPropertyKey ( name : PropertyKey ) : string {
131+ return Predicate . isString ( name ) ? JSON . stringify ( name ) : String ( name )
132+ }
107133
108134/** @internal */
109135export type SingleOrArray < A > = A | ReadonlyArray < A >
0 commit comments