1- var Flatted = ( function ( Primitive , primitive ) {
1+ var TJS = ( function ( Primitive , primitive ) {
2+
3+ var REF_KEY_PREFIX = '_' ;
4+ var REF_SEPARATOR = ';' ;
5+ var SINGLE_REF = REF_KEY_PREFIX + '0' ;
6+ var REF_PREFIX = {
7+ undefined : 'u' ,
8+ number : 'n' ,
9+ bigint : 'b' ,
10+ symbol : 's' ,
11+ RegExp : 'R' ,
12+ Map : 'M' ,
13+ Set : 'S'
14+ } ;
215
316 /*!
417 * ISC License
518 *
619 * Copyright (c) 2018, Andrea Giammarchi, @WebReflection
7- *
8- * Permission to use, copy, modify, and/or distribute this software for any
9- * purpose with or without fee is hereby granted, provided that the above
10- * copyright notice and this permission notice appear in all copies.
11- *
12- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
13- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
14- * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
15- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
16- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
17- * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
18- * PERFORMANCE OF THIS SOFTWARE.
1920 */
2021
21- var Flatted = {
22+ var TJS = {
2223
2324 parse : function parse ( text , reviver ) {
2425 var input = JSON . parse ( text , Primitives ) . map ( primitives ) ;
26+ var len = input . length ;
27+ var refs = len > 1 ? input [ len - 1 ] : [ ] ;
2528 var value = input [ 0 ] ;
2629 var $ = reviver || noop ;
2730 var tmp = typeof value === 'object' && value ?
28- revive ( input , new Set , value , $ ) :
29- value ;
31+ revive ( input , refs , new Set , value , $ ) :
32+ ( value === SINGLE_REF && refs . length ? reviveRefs ( refs , 0 ) : value ) ;
3033 return $ . call ( { '' : tmp } , '' , tmp ) ;
3134 } ,
3235
3336 stringify : function stringify ( value , replacer , space ) {
3437 for ( var
3538 firstRun ,
3639 known = new Map ,
40+ knownRefs = new Map ,
41+ refs = [ ] ,
3742 input = [ ] ,
3843 output = [ ] ,
3944 $ = replacer && typeof replacer === typeof input ?
@@ -43,13 +48,20 @@ var Flatted = (function (Primitive, primitive) {
4348 ( replacer || noop ) ,
4449 i = + set ( known , input , $ . call ( { '' : value } , '' , value ) ) ,
4550 replace = function ( key , value ) {
51+ var after = $ . call ( this , key , value ) ;
52+ var refIndex = setRefs ( knownRefs , refs , after ) ;
53+
54+ if ( refIndex ) {
55+ return refIndex ;
56+ }
57+
4658 if ( firstRun ) {
4759 firstRun = ! firstRun ;
4860 return value ;
4961 // this was invoking twice each root object
5062 // return i < 1 ? value : $.call(this, key, value);
5163 }
52- var after = $ . call ( this , key , value ) ;
64+
5365 switch ( typeof after ) {
5466 case 'object' :
5567 if ( after === null ) return after ;
@@ -63,26 +75,70 @@ var Flatted = (function (Primitive, primitive) {
6375 firstRun = true ;
6476 output [ i ] = JSON . stringify ( input [ i ] , replace , space ) ;
6577 }
78+ refs . length && output . push ( JSON . stringify ( refs ) ) ;
6679 return '[' + output . join ( ',' ) + ']' ;
6780 }
6881
6982 } ;
7083
71- return Flatted ;
84+ return TJS ;
7285
7386 function noop ( key , value ) {
7487 return value ;
7588 }
7689
77- function revive ( input , parsed , output , $ ) {
90+ function reviveRefs ( refs , index ) {
91+ var value = refs [ index ] . substring ( 2 ) ;
92+
93+ switch ( refs [ index ] . charAt ( 0 ) ) {
94+ case REF_PREFIX . undefined :
95+ refs [ index ] = undefined ;
96+ break ;
97+ case REF_PREFIX . number :
98+ refs [ index ] = Number ( value ) ;
99+ break ;
100+ case REF_PREFIX . bigint :
101+ refs [ index ] = BigInt ( value ) ;
102+ break ;
103+ case REF_PREFIX . symbol :
104+ refs [ index ] = Symbol . for ( value ) ;
105+ break ;
106+ case REF_PREFIX . RegExp :
107+ var parts = / \/ ( .* ) \/ ( .* ) / . exec ( value ) ;
108+ refs [ index ] = new RegExp ( parts [ 1 ] , parts [ 2 ] ) ;
109+ break ;
110+ case REF_PREFIX . Map :
111+ refs [ index ] = new Map ( TJS . parse ( value ) ) ;
112+ break ;
113+ case REF_PREFIX . Set :
114+ refs [ index ] = new Set ( TJS . parse ( value ) ) ;
115+ break ;
116+ }
117+
118+ return refs [ index ] ;
119+ }
120+
121+ function revive ( input , refs , parsed , output , $ ) {
78122 return Object . keys ( output ) . reduce (
79123 function ( output , key ) {
80124 var value = output [ key ] ;
81125 if ( value instanceof Primitive ) {
126+ if ( value . startsWith ( REF_KEY_PREFIX ) ) {
127+ var index = value . substring ( 1 ) ;
128+ var tmp = refs [ index ] ;
129+
130+ if ( refs [ index ] instanceof Primitive ) {
131+ reviveRefs ( refs , index ) ;
132+ }
133+
134+ output [ key ] = refs [ index ] ;
135+ return output ;
136+ }
137+
82138 var tmp = input [ value ] ;
83139 if ( typeof tmp === 'object' && ! parsed . has ( tmp ) ) {
84140 parsed . add ( tmp ) ;
85- output [ key ] = $ . call ( output , key , revive ( input , parsed , tmp , $ ) ) ;
141+ output [ key ] = $ . call ( output , key , revive ( input , refs , parsed , tmp , $ ) ) ;
86142 } else {
87143 output [ key ] = $ . call ( output , key , tmp ) ;
88144 }
@@ -100,6 +156,62 @@ var Flatted = (function (Primitive, primitive) {
100156 return index ;
101157 }
102158
159+ function setRefs ( known , refs , value ) {
160+ var after ;
161+
162+ switch ( typeof value ) {
163+ case 'undefined' :
164+ after = REF_PREFIX . undefined ;
165+ break ;
166+ case 'number' :
167+ if ( ! Number . isFinite ( value ) ) {
168+ after = REF_PREFIX . number + REF_SEPARATOR + Primitive ( value ) ;
169+ }
170+ break ;
171+ case 'bigint' :
172+ after = REF_PREFIX . bigint + REF_SEPARATOR + Primitive ( value ) ;
173+ break ;
174+ case 'symbol' :
175+ var description = Primitive ( value ) ;
176+
177+ after = REF_PREFIX . symbol + REF_SEPARATOR + description . substring ( 7 , description . length - 1 ) ;
178+ break ;
179+ case 'object' :
180+ if ( value instanceof RegExp ) {
181+ after = REF_PREFIX . RegExp + REF_SEPARATOR + Primitive ( value ) ;
182+ }
183+ else if ( value instanceof Map ) {
184+ var m = [ ] ;
185+ for ( var i of value . entries ( ) ) {
186+ m . push ( i ) ;
187+ }
188+ after = REF_PREFIX . Map + REF_SEPARATOR + TJS . stringify ( m ) ;
189+ }
190+ else if ( value instanceof Set ) {
191+ var s = [ ] ;
192+ for ( var i of value . values ( ) ) {
193+ s . push ( i ) ;
194+ }
195+ after = REF_PREFIX . Set + REF_SEPARATOR + TJS . stringify ( s ) ;
196+ }
197+ break ;
198+ }
199+
200+ if ( ! after ) {
201+ return ;
202+ }
203+
204+ var index = known . get ( after ) ;
205+
206+ if ( index ) {
207+ return index ;
208+ }
209+
210+ index = REF_KEY_PREFIX + Primitive ( refs . push ( after ) - 1 ) ;
211+ known . set ( after , index ) ;
212+ return index ;
213+ }
214+
103215 // the two kinds of primitives
104216 // 1. the real one
105217 // 2. the wrapped one
@@ -113,4 +225,4 @@ var Flatted = (function (Primitive, primitive) {
113225 }
114226
115227} ( String , 'string' ) ) ;
116- module . exports = Flatted ;
228+ module . exports = TJS ;
0 commit comments