@@ -6,274 +6,10 @@ See the accompanying LICENSE file for terms.
66
77'use strict' ;
88
9- // Generate an internal UID to make the regexp pattern harder to guess.
10- var UID_LENGTH = 16 ;
11- var UID = generateUID ( ) ;
12- var PLACE_HOLDER_REGEXP = new RegExp ( '(\\\\)?"@__(F|R|D|M|S|A|U|I|B|L)-' + UID + '-(\\d+)__@"' , 'g' ) ;
13-
14- var IS_NATIVE_CODE_REGEXP = / \{ \s * \[ n a t i v e c o d e \] \s * \} / g;
15- var IS_PURE_FUNCTION = / f u n c t i o n .* ?\( / ;
16- var IS_ARROW_FUNCTION = / .* ?= > .* ?/ ;
17- var UNSAFE_CHARS_REGEXP = / [ < > \/ \u2028 \u2029 ] / g;
18-
19- var RESERVED_SYMBOLS = [ '*' , 'async' ] ;
20-
21- // Mapping of unsafe HTML and invalid JavaScript line terminator chars to their
22- // Unicode char counterparts which are safe to use in JavaScript strings.
23- var ESCAPED_CHARS = {
24- '<' : '\\u003C' ,
25- '>' : '\\u003E' ,
26- '/' : '\\u002F' ,
27- '\u2028' : '\\u2028' ,
28- '\u2029' : '\\u2029'
29- } ;
30-
31- function escapeUnsafeChars ( unsafeChar ) {
32- return ESCAPED_CHARS [ unsafeChar ] ;
33- }
34-
35- function generateUID ( ) {
36- var bytes ;
37- if ( typeof crypto !== 'undefined' ) {
38- bytes = crypto . getRandomValues ( new Uint8Array ( UID_LENGTH ) ) ;
39- } else {
40- try {
41- var nodeCrypto = require ( 'crypto' ) ;
42- bytes = nodeCrypto . randomBytes ( UID_LENGTH ) ;
43- } catch {
44- // We'll throw an error later
45- }
46- }
47- if ( ! bytes ) {
48- throw new Error ( 'Secure random number generation is not supported by this platform.' ) ;
49- }
50- var result = '' ;
51- for ( var i = 0 ; i < UID_LENGTH ; ++ i ) {
52- result += bytes [ i ] . toString ( 16 ) ;
53- }
54- return result ;
55- }
56-
57- function deleteFunctions ( obj ) {
58- var functionKeys = [ ] ;
59- for ( var key in obj ) {
60- if ( typeof obj [ key ] === "function" ) {
61- functionKeys . push ( key ) ;
62- }
63- }
64- for ( var i = 0 ; i < functionKeys . length ; i ++ ) {
65- delete obj [ functionKeys [ i ] ] ;
66- }
67- }
68-
69- module . exports = function serialize ( obj , options ) {
70- options || ( options = { } ) ;
71-
72- // Backwards-compatibility for `space` as the second argument.
73- if ( typeof options === 'number' || typeof options === 'string' ) {
74- options = { space : options } ;
75- }
76-
77- var functions = [ ] ;
78- var regexps = [ ] ;
79- var dates = [ ] ;
80- var maps = [ ] ;
81- var sets = [ ] ;
82- var arrays = [ ] ;
83- var undefs = [ ] ;
84- var infinities = [ ] ;
85- var bigInts = [ ] ;
86- var urls = [ ] ;
87-
88- // Returns placeholders for functions and regexps (identified by index)
89- // which are later replaced by their string representation.
90- function replacer ( key , value ) {
91-
92- // For nested function
93- if ( options . ignoreFunction ) {
94- deleteFunctions ( value ) ;
95- }
96-
97- if ( ! value && value !== undefined && value !== BigInt ( 0 ) ) {
98- return value ;
99- }
100-
101- // If the value is an object w/ a toJSON method, toJSON is called before
102- // the replacer runs, so we use this[key] to get the non-toJSONed value.
103- var origValue = this [ key ] ;
104- var type = typeof origValue ;
105-
106- if ( type === 'object' ) {
107- if ( origValue instanceof RegExp ) {
108- return '@__R-' + UID + '-' + ( regexps . push ( origValue ) - 1 ) + '__@' ;
109- }
110-
111- if ( origValue instanceof Date ) {
112- return '@__D-' + UID + '-' + ( dates . push ( origValue ) - 1 ) + '__@' ;
113- }
114-
115- if ( origValue instanceof Map ) {
116- return '@__M-' + UID + '-' + ( maps . push ( origValue ) - 1 ) + '__@' ;
117- }
118-
119- if ( origValue instanceof Set ) {
120- return '@__S-' + UID + '-' + ( sets . push ( origValue ) - 1 ) + '__@' ;
121- }
122-
123- if ( origValue instanceof Array ) {
124- var isSparse = origValue . filter ( function ( ) { return true } ) . length !== origValue . length ;
125- if ( isSparse ) {
126- return '@__A-' + UID + '-' + ( arrays . push ( origValue ) - 1 ) + '__@' ;
127- }
128- }
129-
130- if ( origValue instanceof URL ) {
131- return '@__L-' + UID + '-' + ( urls . push ( origValue ) - 1 ) + '__@' ;
132- }
133- }
134-
135- if ( type === 'function' ) {
136- return '@__F-' + UID + '-' + ( functions . push ( origValue ) - 1 ) + '__@' ;
137- }
138-
139- if ( type === 'undefined' ) {
140- return '@__U-' + UID + '-' + ( undefs . push ( origValue ) - 1 ) + '__@' ;
141- }
142-
143- if ( type === 'number' && ! isNaN ( origValue ) && ! isFinite ( origValue ) ) {
144- return '@__I-' + UID + '-' + ( infinities . push ( origValue ) - 1 ) + '__@' ;
145- }
146-
147- if ( type === 'bigint' ) {
148- return '@__B-' + UID + '-' + ( bigInts . push ( origValue ) - 1 ) + '__@' ;
149- }
150-
151- return value ;
152- }
9+ var crypto = require ( 'crypto' ) ;
10+ var createSerializer = require ( './lib' ) ;
15311
154- function serializeFunc ( fn ) {
155- var serializedFn = fn . toString ( ) ;
156- if ( IS_NATIVE_CODE_REGEXP . test ( serializedFn ) ) {
157- throw new TypeError ( 'Serializing native function: ' + fn . name ) ;
158- }
159-
160- // pure functions, example: {key: function() {}}
161- if ( IS_PURE_FUNCTION . test ( serializedFn ) ) {
162- return serializedFn ;
163- }
164-
165- // arrow functions, example: arg1 => arg1+5
166- if ( IS_ARROW_FUNCTION . test ( serializedFn ) ) {
167- return serializedFn ;
168- }
169-
170- var argsStartsAt = serializedFn . indexOf ( '(' ) ;
171- var def = serializedFn . substr ( 0 , argsStartsAt )
172- . trim ( )
173- . split ( ' ' )
174- . filter ( function ( val ) { return val . length > 0 } ) ;
175-
176- var nonReservedSymbols = def . filter ( function ( val ) {
177- return RESERVED_SYMBOLS . indexOf ( val ) === - 1
178- } ) ;
179-
180- // enhanced literal objects, example: {key() {}}
181- if ( nonReservedSymbols . length > 0 ) {
182- return ( def . indexOf ( 'async' ) > - 1 ? 'async ' : '' ) + 'function'
183- + ( def . join ( '' ) . indexOf ( '*' ) > - 1 ? '*' : '' )
184- + serializedFn . substr ( argsStartsAt ) ;
185- }
186-
187- // arrow functions
188- return serializedFn ;
189- }
190-
191- // Check if the parameter is function
192- if ( options . ignoreFunction && typeof obj === "function" ) {
193- obj = undefined ;
194- }
195- // Protects against `JSON.stringify()` returning `undefined`, by serializing
196- // to the literal string: "undefined".
197- if ( obj === undefined ) {
198- return String ( obj ) ;
199- }
200-
201- var str ;
202-
203- // Creates a JSON string representation of the value.
204- // NOTE: Node 0.12 goes into slow mode with extra JSON.stringify() args.
205- if ( options . isJSON && ! options . space ) {
206- str = JSON . stringify ( obj ) ;
207- } else {
208- str = JSON . stringify ( obj , options . isJSON ? null : replacer , options . space ) ;
209- }
210-
211- // Protects against `JSON.stringify()` returning `undefined`, by serializing
212- // to the literal string: "undefined".
213- if ( typeof str !== 'string' ) {
214- return String ( str ) ;
215- }
216-
217- // Replace unsafe HTML and invalid JavaScript line terminator chars with
218- // their safe Unicode char counterpart. This _must_ happen before the
219- // regexps and functions are serialized and added back to the string.
220- if ( options . unsafe !== true ) {
221- str = str . replace ( UNSAFE_CHARS_REGEXP , escapeUnsafeChars ) ;
222- }
223-
224- if ( functions . length === 0 && regexps . length === 0 && dates . length === 0 && maps . length === 0 && sets . length === 0 && arrays . length === 0 && undefs . length === 0 && infinities . length === 0 && bigInts . length === 0 && urls . length === 0 ) {
225- return str ;
226- }
227-
228- // Replaces all occurrences of function, regexp, date, map and set placeholders in the
229- // JSON string with their string representations. If the original value can
230- // not be found, then `undefined` is used.
231- return str . replace ( PLACE_HOLDER_REGEXP , function ( match , backSlash , type , valueIndex ) {
232- // The placeholder may not be preceded by a backslash. This is to prevent
233- // replacing things like `"a\"@__R-<UID>-0__@"` and thus outputting
234- // invalid JS.
235- if ( backSlash ) {
236- return match ;
237- }
238-
239- if ( type === 'D' ) {
240- return "new Date(\"" + dates [ valueIndex ] . toISOString ( ) + "\")" ;
241- }
242-
243- if ( type === 'R' ) {
244- return "new RegExp(" + serialize ( regexps [ valueIndex ] . source ) + ", \"" + regexps [ valueIndex ] . flags + "\")" ;
245- }
246-
247- if ( type === 'M' ) {
248- return "new Map(" + serialize ( Array . from ( maps [ valueIndex ] . entries ( ) ) , options ) + ")" ;
249- }
250-
251- if ( type === 'S' ) {
252- return "new Set(" + serialize ( Array . from ( sets [ valueIndex ] . values ( ) ) , options ) + ")" ;
253- }
254-
255- if ( type === 'A' ) {
256- return "Array.prototype.slice.call(" + serialize ( Object . assign ( { length : arrays [ valueIndex ] . length } , arrays [ valueIndex ] ) , options ) + ")" ;
257- }
258-
259- if ( type === 'U' ) {
260- return 'undefined'
261- }
262-
263- if ( type === 'I' ) {
264- return infinities [ valueIndex ] ;
265- }
266-
267- if ( type === 'B' ) {
268- return "BigInt(\"" + bigInts [ valueIndex ] + "\")" ;
269- }
270-
271- if ( type === 'L' ) {
272- return "new URL(" + serialize ( urls [ valueIndex ] . toString ( ) , options ) + ")" ;
273- }
274-
275- var fn = functions [ valueIndex ] ;
12+ // Generate an internal UID to make the regexp pattern harder to guess.
13+ var UID_LENGTH = 16 ;
27614
277- return serializeFunc ( fn ) ;
278- } ) ;
279- }
15+ module . exports = createSerializer ( crypto . randomBytes ( UID_LENGTH ) ) ;
0 commit comments