2
2
3
3
/* eslint no-prototype-builtins: 0 */
4
4
5
- const Ajv = require ( 'ajv' )
6
- const fastUri = require ( 'fast-uri' )
7
- const ajvFormats = require ( 'ajv-formats' )
8
5
const merge = require ( 'deepmerge' )
9
6
const clone = require ( 'rfdc' ) ( { proto : true } )
10
7
const fjsCloned = Symbol ( 'fast-json-stringify.cloned' )
11
8
const { randomUUID } = require ( 'crypto' )
12
9
13
10
const validate = require ( './schema-validator' )
11
+ const Serializer = require ( './serializer' )
12
+ const buildAjv = require ( './ajv' )
14
13
15
14
let largeArraySize = 2e4
16
15
let stringSimilarity = null
@@ -57,173 +56,6 @@ const schemaReferenceMap = new Map()
57
56
let ajvInstance = null
58
57
let contextFunctions = null
59
58
60
- class Serializer {
61
- constructor ( options = { } ) {
62
- switch ( options . rounding ) {
63
- case 'floor' :
64
- this . parseInteger = Math . floor
65
- break
66
- case 'ceil' :
67
- this . parseInteger = Math . ceil
68
- break
69
- case 'round' :
70
- this . parseInteger = Math . round
71
- break
72
- default :
73
- this . parseInteger = Math . trunc
74
- break
75
- }
76
- }
77
-
78
- asAny ( i ) {
79
- return JSON . stringify ( i )
80
- }
81
-
82
- asNull ( ) {
83
- return 'null'
84
- }
85
-
86
- asInteger ( i ) {
87
- if ( typeof i === 'bigint' ) {
88
- return i . toString ( )
89
- } else if ( Number . isInteger ( i ) ) {
90
- return '' + i
91
- } else {
92
- /* eslint no-undef: "off" */
93
- const integer = this . parseInteger ( i )
94
- if ( Number . isNaN ( integer ) ) {
95
- throw new Error ( `The value "${ i } " cannot be converted to an integer.` )
96
- } else {
97
- return '' + integer
98
- }
99
- }
100
- }
101
-
102
- asIntegerNullable ( i ) {
103
- return i === null ? 'null' : this . asInteger ( i )
104
- }
105
-
106
- asNumber ( i ) {
107
- const num = Number ( i )
108
- if ( Number . isNaN ( num ) ) {
109
- throw new Error ( `The value "${ i } " cannot be converted to a number.` )
110
- } else {
111
- return '' + num
112
- }
113
- }
114
-
115
- asNumberNullable ( i ) {
116
- return i === null ? 'null' : this . asNumber ( i )
117
- }
118
-
119
- asBoolean ( bool ) {
120
- return bool && 'true' || 'false' // eslint-disable-line
121
- }
122
-
123
- asBooleanNullable ( bool ) {
124
- return bool === null ? 'null' : this . asBoolean ( bool )
125
- }
126
-
127
- asDatetime ( date , skipQuotes ) {
128
- const quotes = skipQuotes === true ? '' : '"'
129
- if ( date instanceof Date ) {
130
- return quotes + date . toISOString ( ) + quotes
131
- }
132
- return this . asString ( date , skipQuotes )
133
- }
134
-
135
- asDatetimeNullable ( date , skipQuotes ) {
136
- return date === null ? 'null' : this . asDatetime ( date , skipQuotes )
137
- }
138
-
139
- asDate ( date , skipQuotes ) {
140
- const quotes = skipQuotes === true ? '' : '"'
141
- if ( date instanceof Date ) {
142
- return quotes + new Date ( date . getTime ( ) - ( date . getTimezoneOffset ( ) * 60000 ) ) . toISOString ( ) . slice ( 0 , 10 ) + quotes
143
- }
144
- return this . asString ( date , skipQuotes )
145
- }
146
-
147
- asDateNullable ( date , skipQuotes ) {
148
- return date === null ? 'null' : this . asDate ( date , skipQuotes )
149
- }
150
-
151
- asTime ( date , skipQuotes ) {
152
- const quotes = skipQuotes === true ? '' : '"'
153
- if ( date instanceof Date ) {
154
- return quotes + new Date ( date . getTime ( ) - ( date . getTimezoneOffset ( ) * 60000 ) ) . toISOString ( ) . slice ( 11 , 19 ) + quotes
155
- }
156
- return this . asString ( date , skipQuotes )
157
- }
158
-
159
- asTimeNullable ( date , skipQuotes ) {
160
- return date === null ? 'null' : this . asTime ( date , skipQuotes )
161
- }
162
-
163
- asString ( str , skipQuotes ) {
164
- const quotes = skipQuotes === true ? '' : '"'
165
- if ( str instanceof Date ) {
166
- return quotes + str . toISOString ( ) + quotes
167
- } else if ( str === null ) {
168
- return quotes + quotes
169
- } else if ( str instanceof RegExp ) {
170
- str = str . source
171
- } else if ( typeof str !== 'string' ) {
172
- str = str . toString ( )
173
- }
174
- // If we skipQuotes it means that we are using it as test
175
- // no need to test the string length for the render
176
- if ( skipQuotes ) {
177
- return str
178
- }
179
-
180
- if ( str . length < 42 ) {
181
- return this . asStringSmall ( str )
182
- } else {
183
- return JSON . stringify ( str )
184
- }
185
- }
186
-
187
- asStringNullable ( str ) {
188
- return str === null ? 'null' : this . asString ( str )
189
- }
190
-
191
- // magically escape strings for json
192
- // relying on their charCodeAt
193
- // everything below 32 needs JSON.stringify()
194
- // every string that contain surrogate needs JSON.stringify()
195
- // 34 and 92 happens all the time, so we
196
- // have a fast case for them
197
- asStringSmall ( str ) {
198
- const l = str . length
199
- let result = ''
200
- let last = 0
201
- let found = false
202
- let surrogateFound = false
203
- let point = 255
204
- // eslint-disable-next-line
205
- for ( var i = 0 ; i < l && point >= 32 ; i ++ ) {
206
- point = str . charCodeAt ( i )
207
- if ( point >= 0xD800 && point <= 0xDFFF ) {
208
- // The current character is a surrogate.
209
- surrogateFound = true
210
- }
211
- if ( point === 34 || point === 92 ) {
212
- result += str . slice ( last , i ) + '\\'
213
- last = i
214
- found = true
215
- }
216
- }
217
-
218
- if ( ! found ) {
219
- result = str
220
- } else {
221
- result += str . slice ( last )
222
- }
223
- return ( ( point < 32 ) || ( surrogateFound === true ) ) ? JSON . stringify ( str ) : '"' + result + '"'
224
- }
225
- }
226
-
227
59
function build ( schema , options ) {
228
60
arrayItemsReferenceSerializersMap . clear ( )
229
61
objectReferenceSerializersMap . clear ( )
@@ -232,31 +64,7 @@ function build (schema, options) {
232
64
contextFunctions = [ ]
233
65
options = options || { }
234
66
235
- ajvInstance = new Ajv ( { ...options . ajv , strictSchema : false , uriResolver : fastUri } )
236
- ajvFormats ( ajvInstance )
237
-
238
- const validateDateTimeFormat = ajvFormats . get ( 'date-time' ) . validate
239
- const validateDateFormat = ajvFormats . get ( 'date' ) . validate
240
- const validateTimeFormat = ajvFormats . get ( 'time' ) . validate
241
-
242
- ajvInstance . addKeyword ( {
243
- keyword : 'fjs_date_type' ,
244
- validate : ( schema , date ) => {
245
- if ( date instanceof Date ) {
246
- return true
247
- }
248
- if ( schema === 'date-time' ) {
249
- return validateDateTimeFormat ( date )
250
- }
251
- if ( schema === 'date' ) {
252
- return validateDateFormat ( date )
253
- }
254
- if ( schema === 'time' ) {
255
- return validateTimeFormat ( date )
256
- }
257
- return false
258
- }
259
- } )
67
+ ajvInstance = buildAjv ( options . ajv )
260
68
261
69
isValidSchema ( schema )
262
70
if ( options . schema ) {
@@ -320,9 +128,29 @@ function build (schema, options) {
320
128
const dependenciesName = [ 'ajv' , 'serializer' , contextFunctionCode ]
321
129
322
130
if ( options . debugMode ) {
131
+ options . mode = 'debug'
132
+ }
133
+
134
+ if ( options . mode === 'debug' ) {
323
135
return { code : dependenciesName . join ( '\n' ) , ajv : ajvInstance }
324
136
}
325
137
138
+ if ( options . mode === 'standalone' ) {
139
+ return `
140
+ 'use strict'
141
+
142
+ const Serializer = require('fast-json-stringify/serializer')
143
+ const buildAjv = require('fast-json-stringify/ajv')
144
+
145
+ const serializer = new Serializer(${ JSON . stringify ( options || { } ) } )
146
+ const ajv = buildAjv(${ JSON . stringify ( options . ajv || { } ) } )
147
+
148
+ ${ contextFunctionCode . replace ( 'return main' , '' ) }
149
+
150
+ module.exports = main
151
+ `
152
+ }
153
+
326
154
/* eslint no-new-func: "off" */
327
155
const contextFunc = new Function ( 'ajv' , 'serializer' , contextFunctionCode )
328
156
const stringifyFunc = contextFunc ( ajvInstance , serializer )
0 commit comments