@@ -27,6 +27,8 @@ const validLargeArrayMechanisms = [
27
27
'json-stringify'
28
28
]
29
29
30
+ const addComma = '!addComma && (addComma = true) || (json += \',\')'
31
+
30
32
let schemaIdCounter = 0
31
33
32
34
const mergedSchemaRef = Symbol ( 'fjs-merged-schema-ref' )
@@ -260,7 +262,7 @@ function inferTypeByKeyword (schema) {
260
262
return schema . type
261
263
}
262
264
263
- function buildExtraObjectPropertiesSerializer ( context , location , addComma ) {
265
+ function buildExtraObjectPropertiesSerializer ( context , location ) {
264
266
const schema = location . schema
265
267
const propertiesKeys = Object . keys ( schema . properties || { } )
266
268
@@ -319,76 +321,88 @@ function buildExtraObjectPropertiesSerializer (context, location, addComma) {
319
321
}
320
322
321
323
function buildInnerObject ( context , location ) {
324
+ let code = ''
322
325
const schema = location . schema
326
+ const required = schema . required || [ ]
323
327
324
328
const propertiesLocation = location . getPropertyLocation ( 'properties' )
325
- const requiredProperties = schema . required || [ ]
326
-
327
- // Should serialize required properties first
328
- const propertiesKeys = Object . keys ( schema . properties || { } ) . sort (
329
- ( key1 , key2 ) => {
330
- const required1 = requiredProperties . includes ( key1 )
331
- const required2 = requiredProperties . includes ( key2 )
332
- return required1 === required2 ? 0 : required1 ? - 1 : 1
333
- }
334
- )
335
- const hasRequiredProperties = requiredProperties . includes ( propertiesKeys [ 0 ] )
336
329
337
- let code = ''
330
+ const requiredWithDefault = [ ]
331
+ const requiredWithoutDefault = [ ]
332
+ if ( schema . properties ) {
333
+ for ( const key of Object . keys ( schema . properties ) ) {
334
+ if ( required . indexOf ( key ) === - 1 ) {
335
+ continue
336
+ }
337
+ let propertyLocation = propertiesLocation . getPropertyLocation ( key )
338
+ if ( propertyLocation . schema . $ref ) {
339
+ propertyLocation = resolveRef ( context , propertyLocation )
340
+ }
338
341
339
- for ( const key of requiredProperties ) {
340
- if ( ! propertiesKeys . includes ( key ) ) {
341
- code += `if (obj['${ key } '] === undefined) throw new Error('"${ key } " is required!')\n`
342
+ const sanitizedKey = JSON . stringify ( key )
343
+
344
+ // Using obj['key'] !== undefined instead of obj.hasOwnProperty(prop) for perf reasons,
345
+ // see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
346
+ const defaultValue = propertyLocation . schema . default
347
+ if ( defaultValue === undefined ) {
348
+ code += `if (obj[${ sanitizedKey } ] === undefined) throw new Error('${ sanitizedKey } is required!')\n`
349
+ requiredWithoutDefault . push ( key )
350
+ }
351
+ requiredWithDefault . push ( key )
342
352
}
343
353
}
344
354
345
- code += 'let json = \'{\'\n'
346
-
347
- let addComma = ''
348
- if ( ! hasRequiredProperties ) {
349
- code += 'let addComma = false\n'
350
- addComma = '!addComma && (addComma = true) || (json += \',\')'
355
+ // handle extraneous required fields
356
+ for ( const requiredProperty of required ) {
357
+ if ( requiredWithDefault . indexOf ( requiredProperty ) !== - 1 ) continue
358
+ code += `if (obj['${ requiredProperty } '] === undefined) throw new Error('"${ requiredProperty } " is required!')\n`
351
359
}
352
360
353
- for ( const key of propertiesKeys ) {
354
- let propertyLocation = propertiesLocation . getPropertyLocation ( key )
355
- if ( propertyLocation . schema . $ref ) {
356
- propertyLocation = resolveRef ( context , propertyLocation )
357
- }
361
+ code += `
362
+ let addComma = false
363
+ let json = '{'
364
+ `
358
365
359
- const sanitizedKey = JSON . stringify ( key )
360
- const defaultValue = propertyLocation . schema . default
361
- const isRequired = requiredProperties . includes ( key )
366
+ if ( schema . properties ) {
367
+ for ( const key of Object . keys ( schema . properties ) ) {
368
+ let propertyLocation = propertiesLocation . getPropertyLocation ( key )
369
+ if ( propertyLocation . schema . $ref ) {
370
+ propertyLocation = resolveRef ( context , propertyLocation )
371
+ }
362
372
363
- code += `
364
- if (obj[${ sanitizedKey } ] !== undefined) {
373
+ const sanitizedKey = JSON . stringify ( key )
374
+
375
+ if ( requiredWithoutDefault . indexOf ( key ) !== - 1 ) {
376
+ code += `
365
377
${ addComma }
366
378
json += ${ JSON . stringify ( sanitizedKey + ':' ) }
367
379
${ buildValue ( context , propertyLocation , `obj[${ sanitizedKey } ]` ) }
368
- }`
369
-
370
- if ( defaultValue !== undefined ) {
371
- code += ` else {
372
- ${ addComma }
373
- json += ${ JSON . stringify ( sanitizedKey + ':' + JSON . stringify ( defaultValue ) ) }
374
- }
375
380
`
376
- } else if ( isRequired ) {
377
- code += ` else {
378
- throw new Error('${ sanitizedKey } is required!')
381
+ } else {
382
+ // Using obj['key'] !== undefined instead of obj.hasOwnProperty(prop) for perf reasons,
383
+ // see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
384
+ code += `
385
+ if (obj[${ sanitizedKey } ] !== undefined) {
386
+ ${ addComma }
387
+ json += ${ JSON . stringify ( sanitizedKey + ':' ) }
388
+ ${ buildValue ( context , propertyLocation , `obj[${ sanitizedKey } ]` ) }
389
+ }
390
+ `
391
+ const defaultValue = propertyLocation . schema . default
392
+ if ( defaultValue !== undefined ) {
393
+ code += `
394
+ else {
395
+ ${ addComma }
396
+ json += ${ JSON . stringify ( sanitizedKey + ':' + JSON . stringify ( defaultValue ) ) }
397
+ }
398
+ `
399
+ }
379
400
}
380
- `
381
- } else {
382
- code += '\n'
383
- }
384
-
385
- if ( hasRequiredProperties ) {
386
- addComma = 'json += \',\''
387
401
}
388
402
}
389
403
390
404
if ( schema . patternProperties || schema . additionalProperties ) {
391
- code += buildExtraObjectPropertiesSerializer ( context , location , addComma )
405
+ code += buildExtraObjectPropertiesSerializer ( context , location )
392
406
}
393
407
394
408
code += `
0 commit comments