1+
2+
3+ export default function stringify ( object , options = { } ) {
4+
5+ options . style ??= 'modern' ;
6+
7+ if ( options . style !== 'modern' )
8+ throw 'Unsupported style' ;
9+
10+ const base = modern ( object ) ;
11+
12+ const pretty = base
13+ . replace ( / \[ \s + \{ / g, '[{' )
14+ . replace ( / \} \s + \] / g, '}]' )
15+ . replace ( / \} \s * , \s * \{ / g, '},{' ) ;
16+ // .replace(//g,`\n$1:\n\n'''`);
17+ // .replace(/\[\{\s*/g,'[{\n\n');
18+
19+ return [ ... indent ( pretty ) ] . join ( '\n' ) ;
20+ }
21+
22+ function * indent ( pretty ) {
23+
24+ const lines = pretty . split ( '\n' ) ;
25+
26+ let level = 0 ;
27+ let padding = ' ' ;
28+ let open = false ;
29+
30+
31+ for ( const line of lines ) {
32+
33+ const padded = ( ) =>
34+ padding . repeat ( Math . max ( level , 0 ) ) + line ;
35+
36+ const trimmed = line . trim ( ) ;
37+
38+ if ( trimmed . length < 1 ) {
39+ yield padded ( ) ;
40+ continue ;
41+ }
42+
43+ if ( trimmed . startsWith ( `'''` ) ) {
44+
45+ if ( ! open )
46+ level ++
47+
48+ yield padded ( ) ;
49+
50+ if ( open )
51+ level -- ;
52+
53+ open = ! open ;
54+ continue ;
55+ }
56+
57+ if (
58+ trimmed . startsWith ( '{' ) ||
59+ trimmed . endsWith ( '{' ) ||
60+ trimmed . endsWith ( '[' )
61+ ) {
62+ yield padded ( ) ;
63+ level ++ ;
64+ continue ;
65+ }
66+
67+ if (
68+ trimmed . startsWith ( '}' ) ||
69+ trimmed . endsWith ( '}' ) ||
70+ trimmed . endsWith ( ']' )
71+ ) {
72+ level -- ;
73+ yield padded ( ) ;
74+ continue ;
75+ }
76+
77+ yield padded ( ) ;
78+ }
79+ }
80+
81+
82+ function modern ( object ) {
83+
84+ let lines = toObject ( object ) ;
85+
86+ return lines . join ( '\n' ) ;
87+ }
88+
89+ function toItem ( item ) {
90+
91+ switch ( typeof item ) {
92+ case 'number' :
93+ return [ `${ item } ` ] ;
94+ case 'boolean' :
95+ return [ ( item ) ? 'true' : 'false' ] ;
96+ case 'string' :
97+
98+ if ( item . includes ( '\n' ) )
99+ return [ `'''` , item , `'''` ] ;
100+
101+ if (
102+ item . startsWith ( '#' ) ||
103+ / [ , : \[ \] \{ \} ] / g. test ( item )
104+ ) return [ `'${ item } '` ] ;
105+
106+ return [ `${ item } ` ] ;
107+ case 'array' :
108+ return toArray ( item ) ;
109+ case 'object' :
110+ return toObject ( item ) ;
111+ }
112+ }
113+ //
114+ // function indent(string){
115+ // return ' ' + string;
116+ // }
117+
118+ function toArray ( array ) {
119+
120+ const lines = [ '[' ] ;
121+
122+ for ( const item of array )
123+ lines . push ( ...toItem ( item ) ) ; //.map(indent)
124+
125+ lines . push ( ']' ) ;
126+
127+ return ( lines . length === 2 )
128+ ? [ '[]' ]
129+ : lines ;
130+ }
131+
132+ function toObject ( object ) {
133+
134+ const longestFirst = ( [ a ] , [ b ] ) => b . length - a . length ;
135+
136+ const
137+ entries = Object . entries ( object ) ,
138+ simple = entries
139+ . filter ( ( [ _ , value ] ) => [ 'string' , 'number' , 'boolean' ] . includes ( typeof value ) )
140+ . sort ( longestFirst )
141+ . map ( formString )
142+ . flat ( ) ;
143+ // .map((string) => ' ' + string);
144+
145+ const complex = entries
146+ . filter ( ( [ _ , value ] ) => [ 'object' , 'array' ] . includes ( typeof value ) )
147+ . sort ( longestFirst )
148+ . map ( toComplex )
149+ . flat ( ) ;
150+ // .map((string) => ' ' + string);
151+
152+ complex . shift ( ) ;
153+
154+ const lines = [ '{' ] ;
155+
156+ if ( simple . length && complex . length )
157+ lines . push ( '' ) ;
158+
159+ if ( simple . length )
160+ lines . push ( ...simple ) ;
161+
162+ if ( simple . length && complex . length )
163+ lines . push ( '' ) ;
164+
165+ if ( complex . length )
166+ lines . push ( ...complex ) ;
167+
168+ lines . push ( '}' ) ;
169+
170+ return ( lines . length === 2 )
171+ ? [ '{}' ]
172+ : lines ;
173+ }
174+
175+ function toComplex ( [ key , value ] ) {
176+
177+ if ( Array . isArray ( value ) ) {
178+
179+ const [ first , ...rest ] = toArray ( value ) ;
180+
181+ return [
182+ '' ,
183+ `${ toString ( key ) } : ${ first } ` ,
184+ ...rest
185+ ] ;
186+ } else {
187+ const [ first , ...rest ] = toObject ( value ) ;
188+
189+ return [
190+ '' ,
191+ `${ toString ( key ) } : ${ first } ` ,
192+ ...rest
193+ ] ;
194+ }
195+ }
196+
197+
198+ function formString ( [ key , value ] ) {
199+ switch ( typeof value ) {
200+ case 'number' :
201+ return [ `${ toString ( key ) } : ${ value } ` ] ;
202+ case 'boolean' :
203+ return [ `${ toString ( key ) } : ${ ( value )
204+ ? [ 'true' ]
205+ : [ 'false' ] } ` ]
206+ case 'string' :
207+
208+ if ( value . includes ( '\n' ) )
209+ return [ '' , `${ toString ( key ) } :` , '' , `'''` , value , `'''` ] ;
210+
211+ if (
212+ value . startsWith ( '#' ) ||
213+ / [ , : \[ \] \{ \} ] / g. test ( value )
214+ ) return [ `${ toString ( key ) } : '${ value } '` ] ;
215+
216+ return [ `${ toString ( key ) } : ${ value } ` ] ;
217+ }
218+ }
219+
220+ function toString ( value ) {
221+
222+ if (
223+ value . startsWith ( '#' ) ||
224+ / [ , : \[ \] \{ \} ] / g. test ( value )
225+ ) return `'${ value } '` ;
226+
227+ return value ;
228+ }
0 commit comments