fast-json-stringify 5.11.1 → 5.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/benchmark/bench.js +24 -0
- package/index.js +284 -239
- package/lib/location.js +1 -30
- package/lib/merge-schemas.js +9 -0
- package/lib/serializer.js +8 -20
- package/package.json +3 -3
- package/test/allof.test.js +134 -17
- package/test/anyof.test.js +148 -0
- package/test/basic.test.js +6 -0
- package/test/if-then-else.test.js +58 -9
- package/test/required.test.js +4 -3
- package/test/string.test.js +34 -0
- package/types/index.d.ts +5 -2
- package/types/index.test-d.ts +79 -57
package/index.js
CHANGED
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
/* eslint no-prototype-builtins: 0 */
|
|
4
4
|
|
|
5
|
-
const merge = require('@fastify/deepmerge')()
|
|
6
|
-
const clone = require('rfdc')({ proto: true })
|
|
7
5
|
const { RefResolver } = require('json-schema-ref-resolver')
|
|
8
6
|
|
|
9
|
-
const validate = require('./lib/schema-validator')
|
|
10
7
|
const Serializer = require('./lib/serializer')
|
|
11
8
|
const Validator = require('./lib/validator')
|
|
12
9
|
const Location = require('./lib/location')
|
|
10
|
+
const validate = require('./lib/schema-validator')
|
|
11
|
+
const mergeSchemas = require('./lib/merge-schemas')
|
|
13
12
|
|
|
14
13
|
const SINGLE_TICK = /'/g
|
|
15
14
|
|
|
@@ -28,8 +27,6 @@ const validLargeArrayMechanisms = [
|
|
|
28
27
|
'json-stringify'
|
|
29
28
|
]
|
|
30
29
|
|
|
31
|
-
const addComma = '!addComma && (addComma = true) || (json += \',\')'
|
|
32
|
-
|
|
33
30
|
let schemaIdCounter = 0
|
|
34
31
|
|
|
35
32
|
function isValidSchema (schema, name) {
|
|
@@ -46,13 +43,15 @@ function isValidSchema (schema, name) {
|
|
|
46
43
|
}
|
|
47
44
|
}
|
|
48
45
|
|
|
49
|
-
function resolveRef (context, location
|
|
46
|
+
function resolveRef (context, location) {
|
|
47
|
+
const ref = location.schema.$ref
|
|
48
|
+
|
|
50
49
|
let hashIndex = ref.indexOf('#')
|
|
51
50
|
if (hashIndex === -1) {
|
|
52
51
|
hashIndex = ref.length
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
const schemaId = ref.slice(0, hashIndex) || location.
|
|
54
|
+
const schemaId = ref.slice(0, hashIndex) || location.schemaId
|
|
56
55
|
const jsonPointer = ref.slice(hashIndex) || '#'
|
|
57
56
|
|
|
58
57
|
const schema = context.refResolver.getSchema(schemaId, jsonPointer)
|
|
@@ -62,12 +61,17 @@ function resolveRef (context, location, ref) {
|
|
|
62
61
|
|
|
63
62
|
const newLocation = new Location(schema, schemaId, jsonPointer)
|
|
64
63
|
if (schema.$ref !== undefined) {
|
|
65
|
-
return resolveRef(context, newLocation
|
|
64
|
+
return resolveRef(context, newLocation)
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
return newLocation
|
|
69
68
|
}
|
|
70
69
|
|
|
70
|
+
function getMergedLocation (context, mergedSchemaId) {
|
|
71
|
+
const mergedSchema = context.refResolver.getSchema(mergedSchemaId, '#')
|
|
72
|
+
return new Location(mergedSchema, mergedSchemaId, '#')
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
function getSchemaId (schema, rootSchemaId) {
|
|
72
76
|
if (schema.$id && schema.$id.charAt(0) !== '#') {
|
|
73
77
|
return schema.$id
|
|
@@ -85,10 +89,10 @@ function build (schema, options) {
|
|
|
85
89
|
functionsCounter: 0,
|
|
86
90
|
functionsNamesBySchema: new Map(),
|
|
87
91
|
options,
|
|
88
|
-
wrapObjects: true,
|
|
89
92
|
refResolver: new RefResolver(),
|
|
90
93
|
rootSchemaId: schema.$id || `__fjs_root_${schemaIdCounter++}`,
|
|
91
|
-
validatorSchemasIds: new Set()
|
|
94
|
+
validatorSchemasIds: new Set(),
|
|
95
|
+
mergedSchemasIds: new Map()
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
const schemaId = getSchemaId(schema, context.rootSchemaId)
|
|
@@ -255,7 +259,7 @@ function inferTypeByKeyword (schema) {
|
|
|
255
259
|
return schema.type
|
|
256
260
|
}
|
|
257
261
|
|
|
258
|
-
function buildExtraObjectPropertiesSerializer (context, location) {
|
|
262
|
+
function buildExtraObjectPropertiesSerializer (context, location, addComma) {
|
|
259
263
|
const schema = location.schema
|
|
260
264
|
const propertiesKeys = Object.keys(schema.properties || {})
|
|
261
265
|
|
|
@@ -314,225 +318,138 @@ function buildExtraObjectPropertiesSerializer (context, location) {
|
|
|
314
318
|
}
|
|
315
319
|
|
|
316
320
|
function buildInnerObject (context, location) {
|
|
317
|
-
let code = ''
|
|
318
321
|
const schema = location.schema
|
|
319
|
-
const required = schema.required || []
|
|
320
322
|
|
|
321
323
|
const propertiesLocation = location.getPropertyLocation('properties')
|
|
324
|
+
const requiredProperties = schema.required || []
|
|
322
325
|
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
propertyLocation = resolveRef(context, location, propertyLocation.schema.$ref)
|
|
333
|
-
}
|
|
326
|
+
// Should serialize required properties first
|
|
327
|
+
const propertiesKeys = Object.keys(schema.properties || {}).sort(
|
|
328
|
+
(key1, key2) => {
|
|
329
|
+
const required1 = requiredProperties.includes(key1)
|
|
330
|
+
const required2 = requiredProperties.includes(key2)
|
|
331
|
+
return required1 === required2 ? 0 : required1 ? -1 : 1
|
|
332
|
+
}
|
|
333
|
+
)
|
|
334
|
+
const hasRequiredProperties = requiredProperties.includes(propertiesKeys[0])
|
|
334
335
|
|
|
335
|
-
|
|
336
|
+
let code = ''
|
|
336
337
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
if (defaultValue === undefined) {
|
|
341
|
-
code += `if (obj[${sanitizedKey}] === undefined) throw new Error('${sanitizedKey} is required!')\n`
|
|
342
|
-
requiredWithoutDefault.push(key)
|
|
343
|
-
}
|
|
344
|
-
requiredWithDefault.push(key)
|
|
338
|
+
for (const key of requiredProperties) {
|
|
339
|
+
if (!propertiesKeys.includes(key)) {
|
|
340
|
+
code += `if (obj['${key}'] === undefined) throw new Error('"${key}" is required!')\n`
|
|
345
341
|
}
|
|
346
342
|
}
|
|
347
343
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
344
|
+
code += 'let json = \'{\'\n'
|
|
345
|
+
|
|
346
|
+
let addComma = ''
|
|
347
|
+
if (!hasRequiredProperties) {
|
|
348
|
+
code += 'let addComma = false\n'
|
|
349
|
+
addComma = '!addComma && (addComma = true) || (json += \',\')'
|
|
352
350
|
}
|
|
353
351
|
|
|
354
|
-
|
|
355
|
-
let
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
context.wrapObjects = true
|
|
360
|
-
|
|
361
|
-
if (schema.properties) {
|
|
362
|
-
for (const key of Object.keys(schema.properties)) {
|
|
363
|
-
let propertyLocation = propertiesLocation.getPropertyLocation(key)
|
|
364
|
-
if (propertyLocation.schema.$ref) {
|
|
365
|
-
propertyLocation = resolveRef(context, location, propertyLocation.schema.$ref)
|
|
366
|
-
}
|
|
352
|
+
for (const key of propertiesKeys) {
|
|
353
|
+
let propertyLocation = propertiesLocation.getPropertyLocation(key)
|
|
354
|
+
if (propertyLocation.schema.$ref) {
|
|
355
|
+
propertyLocation = resolveRef(context, propertyLocation)
|
|
356
|
+
}
|
|
367
357
|
|
|
368
|
-
|
|
358
|
+
const sanitizedKey = JSON.stringify(key)
|
|
359
|
+
const defaultValue = propertyLocation.schema.default
|
|
360
|
+
const isRequired = requiredProperties.includes(key)
|
|
369
361
|
|
|
370
|
-
|
|
371
|
-
|
|
362
|
+
code += `
|
|
363
|
+
if (obj[${sanitizedKey}] !== undefined) {
|
|
372
364
|
${addComma}
|
|
373
365
|
json += ${JSON.stringify(sanitizedKey + ':')}
|
|
374
366
|
${buildValue(context, propertyLocation, `obj[${sanitizedKey}]`)}
|
|
367
|
+
}`
|
|
368
|
+
|
|
369
|
+
if (defaultValue !== undefined) {
|
|
370
|
+
code += ` else {
|
|
371
|
+
${addComma}
|
|
372
|
+
json += ${JSON.stringify(sanitizedKey + ':' + JSON.stringify(defaultValue))}
|
|
373
|
+
}
|
|
375
374
|
`
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
code += `
|
|
380
|
-
if (obj[${sanitizedKey}] !== undefined) {
|
|
381
|
-
${addComma}
|
|
382
|
-
json += ${JSON.stringify(sanitizedKey + ':')}
|
|
383
|
-
${buildValue(context, propertyLocation, `obj[${sanitizedKey}]`)}
|
|
384
|
-
}
|
|
385
|
-
`
|
|
386
|
-
const defaultValue = propertyLocation.schema.default
|
|
387
|
-
if (defaultValue !== undefined) {
|
|
388
|
-
code += `
|
|
389
|
-
else {
|
|
390
|
-
${addComma}
|
|
391
|
-
json += ${JSON.stringify(sanitizedKey + ':' + JSON.stringify(defaultValue))}
|
|
392
|
-
}
|
|
393
|
-
`
|
|
394
|
-
}
|
|
375
|
+
} else if (isRequired) {
|
|
376
|
+
code += ` else {
|
|
377
|
+
throw new Error('${sanitizedKey} is required!')
|
|
395
378
|
}
|
|
379
|
+
`
|
|
380
|
+
} else {
|
|
381
|
+
code += '\n'
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (hasRequiredProperties) {
|
|
385
|
+
addComma = 'json += \',\''
|
|
396
386
|
}
|
|
397
387
|
}
|
|
398
388
|
|
|
399
389
|
if (schema.patternProperties || schema.additionalProperties) {
|
|
400
|
-
code += buildExtraObjectPropertiesSerializer(context, location)
|
|
390
|
+
code += buildExtraObjectPropertiesSerializer(context, location, addComma)
|
|
401
391
|
}
|
|
402
392
|
|
|
403
|
-
context.wrapObjects = wrapObjects
|
|
404
393
|
code += `
|
|
405
|
-
|
|
394
|
+
return json + '}'
|
|
406
395
|
`
|
|
407
396
|
return code
|
|
408
397
|
}
|
|
409
398
|
|
|
410
|
-
function
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
if (allOfSchema.$ref) {
|
|
417
|
-
const allOfSchemaLocation = allOfLocation.getPropertyLocation(i)
|
|
418
|
-
allOfSchema = resolveRef(context, allOfSchemaLocation, allOfSchema.$ref).schema
|
|
399
|
+
function mergeLocations (context, mergedSchemaId, mergedLocations) {
|
|
400
|
+
for (let i = 0; i < mergedLocations.length; i++) {
|
|
401
|
+
const location = mergedLocations[i]
|
|
402
|
+
const schema = location.schema
|
|
403
|
+
if (schema.$ref) {
|
|
404
|
+
mergedLocations[i] = resolveRef(context, location)
|
|
419
405
|
}
|
|
406
|
+
}
|
|
420
407
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
408
|
+
const mergedSchemas = []
|
|
409
|
+
for (const location of mergedLocations) {
|
|
410
|
+
const schema = cloneOriginSchema(context, location.schema, location.schemaId)
|
|
411
|
+
delete schema.$id
|
|
425
412
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
mergedSchema.type !== undefined &&
|
|
429
|
-
mergedSchema.type !== allOfSchemaType
|
|
430
|
-
) {
|
|
431
|
-
throw new Error('allOf schemas have different type values')
|
|
432
|
-
}
|
|
433
|
-
mergedSchema.type = allOfSchemaType
|
|
434
|
-
}
|
|
413
|
+
mergedSchemas.push(schema)
|
|
414
|
+
}
|
|
435
415
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
mergedSchema.format !== undefined &&
|
|
439
|
-
mergedSchema.format !== allOfSchema.format
|
|
440
|
-
) {
|
|
441
|
-
throw new Error('allOf schemas have different format values')
|
|
442
|
-
}
|
|
443
|
-
mergedSchema.format = allOfSchema.format
|
|
444
|
-
}
|
|
416
|
+
const mergedSchema = mergeSchemas(mergedSchemas)
|
|
417
|
+
const mergedLocation = new Location(mergedSchema, mergedSchemaId)
|
|
445
418
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
mergedSchema.nullable !== allOfSchema.nullable
|
|
450
|
-
) {
|
|
451
|
-
throw new Error('allOf schemas have different nullable values')
|
|
452
|
-
}
|
|
453
|
-
mergedSchema.nullable = allOfSchema.nullable
|
|
454
|
-
}
|
|
419
|
+
context.refResolver.addSchema(mergedSchema, mergedSchemaId)
|
|
420
|
+
return mergedLocation
|
|
421
|
+
}
|
|
455
422
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
mergedSchema.properties = {}
|
|
459
|
-
}
|
|
460
|
-
Object.assign(mergedSchema.properties, allOfSchema.properties)
|
|
461
|
-
}
|
|
423
|
+
function cloneOriginSchema (context, schema, schemaId) {
|
|
424
|
+
const clonedSchema = Array.isArray(schema) ? [] : {}
|
|
462
425
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
426
|
+
if (
|
|
427
|
+
schema.$id !== undefined &&
|
|
428
|
+
schema.$id.charAt(0) !== '#'
|
|
429
|
+
) {
|
|
430
|
+
schemaId = schema.$id
|
|
431
|
+
}
|
|
469
432
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
Object.assign(mergedSchema.patternProperties, allOfSchema.patternProperties)
|
|
475
|
-
}
|
|
433
|
+
const mergedSchemaRef = context.mergedSchemasIds.get(schema)
|
|
434
|
+
if (mergedSchemaRef) {
|
|
435
|
+
context.mergedSchemasIds.set(clonedSchema, mergedSchemaRef)
|
|
436
|
+
}
|
|
476
437
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
mergedSchema.required = []
|
|
480
|
-
}
|
|
481
|
-
mergedSchema.required.push(...allOfSchema.required)
|
|
482
|
-
}
|
|
438
|
+
for (const key in schema) {
|
|
439
|
+
let value = schema[key]
|
|
483
440
|
|
|
484
|
-
if (
|
|
485
|
-
|
|
486
|
-
mergedSchema.oneOf = []
|
|
487
|
-
}
|
|
488
|
-
mergedSchema.oneOf.push(...allOfSchema.oneOf)
|
|
441
|
+
if (key === '$ref' && value.charAt(0) === '#') {
|
|
442
|
+
value = schemaId + value
|
|
489
443
|
}
|
|
490
444
|
|
|
491
|
-
if (
|
|
492
|
-
|
|
493
|
-
mergedSchema.anyOf = []
|
|
494
|
-
}
|
|
495
|
-
mergedSchema.anyOf.push(...allOfSchema.anyOf)
|
|
445
|
+
if (typeof value === 'object' && value !== null) {
|
|
446
|
+
value = cloneOriginSchema(context, value, schemaId)
|
|
496
447
|
}
|
|
497
448
|
|
|
498
|
-
|
|
499
|
-
mergeAllOfSchema(context, location, allOfSchema, mergedSchema)
|
|
500
|
-
}
|
|
449
|
+
clonedSchema[key] = value
|
|
501
450
|
}
|
|
502
|
-
delete mergedSchema.allOf
|
|
503
|
-
|
|
504
|
-
mergedSchema.$id = `__fjs_merged_${schemaIdCounter++}`
|
|
505
|
-
context.refResolver.addSchema(mergedSchema)
|
|
506
|
-
location.addMergedSchema(mergedSchema, mergedSchema.$id)
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
function addIfThenElse (context, location, input) {
|
|
510
|
-
context.validatorSchemasIds.add(location.getSchemaId())
|
|
511
|
-
|
|
512
|
-
const schema = merge({}, location.schema)
|
|
513
|
-
const thenSchema = schema.then
|
|
514
|
-
const elseSchema = schema.else || { additionalProperties: true }
|
|
515
|
-
|
|
516
|
-
delete schema.if
|
|
517
|
-
delete schema.then
|
|
518
|
-
delete schema.else
|
|
519
451
|
|
|
520
|
-
|
|
521
|
-
const ifSchemaRef = ifLocation.getSchemaRef()
|
|
522
|
-
|
|
523
|
-
const thenLocation = location.getPropertyLocation('then')
|
|
524
|
-
thenLocation.schema = merge(schema, thenSchema)
|
|
525
|
-
|
|
526
|
-
const elseLocation = location.getPropertyLocation('else')
|
|
527
|
-
elseLocation.schema = merge(schema, elseSchema)
|
|
528
|
-
|
|
529
|
-
return `
|
|
530
|
-
if (validator.validate("${ifSchemaRef}", ${input})) {
|
|
531
|
-
${buildValue(context, thenLocation, input)}
|
|
532
|
-
} else {
|
|
533
|
-
${buildValue(context, elseLocation, input)}
|
|
534
|
-
}
|
|
535
|
-
`
|
|
452
|
+
return clonedSchema
|
|
536
453
|
}
|
|
537
454
|
|
|
538
455
|
function toJSON (variableName) {
|
|
@@ -582,7 +499,7 @@ function buildArray (context, location) {
|
|
|
582
499
|
itemsLocation.schema = itemsLocation.schema || {}
|
|
583
500
|
|
|
584
501
|
if (itemsLocation.schema.$ref) {
|
|
585
|
-
itemsLocation = resolveRef(context, itemsLocation
|
|
502
|
+
itemsLocation = resolveRef(context, itemsLocation)
|
|
586
503
|
}
|
|
587
504
|
|
|
588
505
|
const itemsSchema = itemsLocation.schema
|
|
@@ -808,8 +725,24 @@ function buildSingleTypeSerializer (context, location, input) {
|
|
|
808
725
|
return `json += serializer.asDate(${input})`
|
|
809
726
|
} else if (schema.format === 'time') {
|
|
810
727
|
return `json += serializer.asTime(${input})`
|
|
728
|
+
} else if (schema.format === 'unsafe') {
|
|
729
|
+
return `json += serializer.asUnsafeString(${input})`
|
|
811
730
|
} else {
|
|
812
|
-
return `
|
|
731
|
+
return `
|
|
732
|
+
if (typeof ${input} !== 'string') {
|
|
733
|
+
if (${input} === null) {
|
|
734
|
+
json += '""'
|
|
735
|
+
} else if (${input} instanceof Date) {
|
|
736
|
+
json += '"' + ${input}.toISOString() + '"'
|
|
737
|
+
} else if (${input} instanceof RegExp) {
|
|
738
|
+
json += serializer.asString(${input}.source)
|
|
739
|
+
} else {
|
|
740
|
+
json += serializer.asString(${input}.toString())
|
|
741
|
+
}
|
|
742
|
+
} else {
|
|
743
|
+
json += serializer.asString(${input})
|
|
744
|
+
}
|
|
745
|
+
`
|
|
813
746
|
}
|
|
814
747
|
}
|
|
815
748
|
case 'integer':
|
|
@@ -860,81 +793,193 @@ function buildConstSerializer (location, input) {
|
|
|
860
793
|
return code
|
|
861
794
|
}
|
|
862
795
|
|
|
863
|
-
function
|
|
864
|
-
|
|
796
|
+
function buildAllOf (context, location, input) {
|
|
797
|
+
const schema = location.schema
|
|
865
798
|
|
|
866
|
-
|
|
867
|
-
|
|
799
|
+
let mergedSchemaId = context.mergedSchemasIds.get(schema)
|
|
800
|
+
if (mergedSchemaId) {
|
|
801
|
+
const mergedLocation = getMergedLocation(context, mergedSchemaId)
|
|
802
|
+
return buildValue(context, mergedLocation, input)
|
|
868
803
|
}
|
|
869
804
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
805
|
+
mergedSchemaId = `__fjs_merged_${schemaIdCounter++}`
|
|
806
|
+
context.mergedSchemasIds.set(schema, mergedSchemaId)
|
|
807
|
+
|
|
808
|
+
const { allOf, ...schemaWithoutAllOf } = location.schema
|
|
809
|
+
const locations = [
|
|
810
|
+
new Location(
|
|
811
|
+
schemaWithoutAllOf,
|
|
812
|
+
location.schemaId,
|
|
813
|
+
location.jsonPointer
|
|
814
|
+
)
|
|
815
|
+
]
|
|
816
|
+
|
|
817
|
+
const allOfsLocation = location.getPropertyLocation('allOf')
|
|
818
|
+
for (let i = 0; i < allOf.length; i++) {
|
|
819
|
+
locations.push(allOfsLocation.getPropertyLocation(i))
|
|
873
820
|
}
|
|
874
821
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
822
|
+
const mergedLocation = mergeLocations(context, mergedSchemaId, locations)
|
|
823
|
+
return buildValue(context, mergedLocation, input)
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function buildOneOf (context, location, input) {
|
|
827
|
+
context.validatorSchemasIds.add(location.schemaId)
|
|
828
|
+
|
|
829
|
+
const schema = location.schema
|
|
830
|
+
|
|
831
|
+
const type = schema.anyOf ? 'anyOf' : 'oneOf'
|
|
832
|
+
const { [type]: oneOfs, ...schemaWithoutAnyOf } = location.schema
|
|
833
|
+
|
|
834
|
+
const locationWithoutOneOf = new Location(
|
|
835
|
+
schemaWithoutAnyOf,
|
|
836
|
+
location.schemaId,
|
|
837
|
+
location.jsonPointer
|
|
838
|
+
)
|
|
839
|
+
const oneOfsLocation = location.getPropertyLocation(type)
|
|
840
|
+
|
|
841
|
+
let code = ''
|
|
842
|
+
|
|
843
|
+
for (let index = 0; index < oneOfs.length; index++) {
|
|
844
|
+
const optionLocation = oneOfsLocation.getPropertyLocation(index)
|
|
845
|
+
const optionSchema = optionLocation.schema
|
|
846
|
+
|
|
847
|
+
let mergedSchemaId = context.mergedSchemasIds.get(optionSchema)
|
|
848
|
+
let mergedLocation = null
|
|
849
|
+
if (mergedSchemaId) {
|
|
850
|
+
mergedLocation = getMergedLocation(context, mergedSchemaId)
|
|
851
|
+
} else {
|
|
852
|
+
mergedSchemaId = `__fjs_merged_${schemaIdCounter++}`
|
|
853
|
+
context.mergedSchemasIds.set(optionSchema, mergedSchemaId)
|
|
854
|
+
|
|
855
|
+
mergedLocation = mergeLocations(context, mergedSchemaId, [
|
|
856
|
+
locationWithoutOneOf,
|
|
857
|
+
optionLocation
|
|
858
|
+
])
|
|
879
859
|
}
|
|
860
|
+
|
|
861
|
+
const nestedResult = buildValue(context, mergedLocation, input)
|
|
862
|
+
const schemaRef = optionLocation.getSchemaRef()
|
|
863
|
+
code += `
|
|
864
|
+
${index === 0 ? 'if' : 'else if'}(validator.validate("${schemaRef}", ${input}))
|
|
865
|
+
${nestedResult}
|
|
866
|
+
`
|
|
880
867
|
}
|
|
881
868
|
|
|
882
|
-
|
|
883
|
-
|
|
869
|
+
let schemaRef = location.getSchemaRef()
|
|
870
|
+
if (schemaRef.startsWith(context.rootSchemaId)) {
|
|
871
|
+
schemaRef = schemaRef.replace(context.rootSchemaId, '')
|
|
884
872
|
}
|
|
885
873
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
874
|
+
code += `
|
|
875
|
+
else throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`)
|
|
876
|
+
`
|
|
877
|
+
|
|
878
|
+
return code
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
function buildIfThenElse (context, location, input) {
|
|
882
|
+
context.validatorSchemasIds.add(location.schemaId)
|
|
883
|
+
|
|
884
|
+
const {
|
|
885
|
+
if: ifSchema,
|
|
886
|
+
then: thenSchema,
|
|
887
|
+
else: elseSchema,
|
|
888
|
+
...schemaWithoutIfThenElse
|
|
889
|
+
} = location.schema
|
|
890
|
+
|
|
891
|
+
const rootLocation = new Location(
|
|
892
|
+
schemaWithoutIfThenElse,
|
|
893
|
+
location.schemaId,
|
|
894
|
+
location.jsonPointer
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
const ifLocation = location.getPropertyLocation('if')
|
|
898
|
+
const ifSchemaRef = ifLocation.getSchemaRef()
|
|
899
|
+
|
|
900
|
+
const thenLocation = location.getPropertyLocation('then')
|
|
901
|
+
let thenMergedSchemaId = context.mergedSchemasIds.get(thenSchema)
|
|
902
|
+
let thenMergedLocation = null
|
|
903
|
+
if (thenMergedSchemaId) {
|
|
904
|
+
thenMergedLocation = getMergedLocation(context, thenMergedSchemaId)
|
|
905
|
+
} else {
|
|
906
|
+
thenMergedSchemaId = `__fjs_merged_${schemaIdCounter++}`
|
|
907
|
+
context.mergedSchemasIds.set(thenSchema, thenMergedSchemaId)
|
|
908
|
+
|
|
909
|
+
thenMergedLocation = mergeLocations(context, thenMergedSchemaId, [
|
|
910
|
+
rootLocation,
|
|
911
|
+
thenLocation
|
|
912
|
+
])
|
|
889
913
|
}
|
|
890
914
|
|
|
891
|
-
|
|
915
|
+
if (!elseSchema) {
|
|
916
|
+
return `
|
|
917
|
+
if (validator.validate("${ifSchemaRef}", ${input})) {
|
|
918
|
+
${buildValue(context, thenMergedLocation, input)}
|
|
919
|
+
} else {
|
|
920
|
+
${buildValue(context, rootLocation, input)}
|
|
921
|
+
}
|
|
922
|
+
`
|
|
923
|
+
}
|
|
892
924
|
|
|
893
|
-
|
|
925
|
+
const elseLocation = location.getPropertyLocation('else')
|
|
926
|
+
let elseMergedSchemaId = context.mergedSchemasIds.get(elseSchema)
|
|
927
|
+
let elseMergedLocation = null
|
|
928
|
+
if (elseMergedSchemaId) {
|
|
929
|
+
elseMergedLocation = getMergedLocation(context, elseMergedSchemaId)
|
|
930
|
+
} else {
|
|
931
|
+
elseMergedSchemaId = `__fjs_merged_${schemaIdCounter++}`
|
|
932
|
+
context.mergedSchemasIds.set(elseSchema, elseMergedSchemaId)
|
|
894
933
|
|
|
895
|
-
|
|
896
|
-
|
|
934
|
+
elseMergedLocation = mergeLocations(context, elseMergedSchemaId, [
|
|
935
|
+
rootLocation,
|
|
936
|
+
elseLocation
|
|
937
|
+
])
|
|
938
|
+
}
|
|
897
939
|
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
json += ${funcName}(${input})
|
|
904
|
-
json += ','
|
|
905
|
-
`
|
|
940
|
+
return `
|
|
941
|
+
if (validator.validate("${ifSchemaRef}", ${input})) {
|
|
942
|
+
${buildValue(context, thenMergedLocation, input)}
|
|
943
|
+
} else {
|
|
944
|
+
${buildValue(context, elseMergedLocation, input)}
|
|
906
945
|
}
|
|
946
|
+
`
|
|
947
|
+
}
|
|
907
948
|
|
|
908
|
-
|
|
909
|
-
|
|
949
|
+
function buildValue (context, location, input) {
|
|
950
|
+
let schema = location.schema
|
|
910
951
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
const nestedResult = buildValue(context, optionLocation, input)
|
|
915
|
-
code += `
|
|
916
|
-
${index === 0 ? 'if' : 'else if'}(validator.validate("${schemaRef}", ${input}))
|
|
917
|
-
${nestedResult}
|
|
918
|
-
`
|
|
919
|
-
}
|
|
952
|
+
if (typeof schema === 'boolean') {
|
|
953
|
+
return `json += JSON.stringify(${input})`
|
|
954
|
+
}
|
|
920
955
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
956
|
+
if (schema.$ref) {
|
|
957
|
+
location = resolveRef(context, location)
|
|
958
|
+
schema = location.schema
|
|
959
|
+
}
|
|
925
960
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
961
|
+
if (schema.allOf) {
|
|
962
|
+
return buildAllOf(context, location, input)
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
if (schema.anyOf || schema.oneOf) {
|
|
966
|
+
return buildOneOf(context, location, input)
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
if (schema.if && schema.then) {
|
|
970
|
+
return buildIfThenElse(context, location, input)
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
if (schema.type === undefined) {
|
|
974
|
+
const inferredType = inferTypeByKeyword(schema)
|
|
975
|
+
if (inferredType) {
|
|
976
|
+
schema.type = inferredType
|
|
934
977
|
}
|
|
935
|
-
return code
|
|
936
978
|
}
|
|
937
979
|
|
|
980
|
+
let code = ''
|
|
981
|
+
|
|
982
|
+
const type = schema.type
|
|
938
983
|
const nullable = schema.nullable === true
|
|
939
984
|
if (nullable) {
|
|
940
985
|
code += `
|