fast-json-stringify 5.5.0 → 5.6.1
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/.github/workflows/benchmark.yml +1 -1
- package/.taprc +5 -0
- package/README.md +1 -1
- package/benchmark/bench.js +21 -0
- package/index.js +130 -123
- package/lib/serializer.js +34 -21
- package/lib/standalone.js +4 -5
- package/package.json +3 -6
- package/test/allof.test.js +52 -3
- package/test/any.test.js +77 -0
- package/test/anyof.test.js +6 -6
- package/test/array.test.js +133 -14
- package/test/date.test.js +117 -24
- package/test/fix-604.test.js +24 -0
- package/test/if-then-else.test.js +4 -3
- package/test/integer.test.js +8 -0
- package/test/multi-type-serializer.test.js +19 -0
- package/test/patternProperties.test.js +18 -0
- package/test/ref.test.js +73 -0
- package/test/string.test.js +50 -0
- package/test/typesArray.test.js +47 -1
- package/types/index.d.ts +19 -1
- package/types/index.test-d.ts +17 -0
package/.taprc
CHANGED
package/README.md
CHANGED
|
@@ -548,7 +548,7 @@ By default the library will handle automatically [BigInt](https://developer.mozi
|
|
|
548
548
|
<a name="integer"></a>
|
|
549
549
|
#### Integers
|
|
550
550
|
The `type: integer` property will be truncated if a floating point is provided.
|
|
551
|
-
You can customize this behaviour with the `rounding` option that will accept [`round`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round), [`ceil`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil)
|
|
551
|
+
You can customize this behaviour with the `rounding` option that will accept [`round`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round), [`ceil`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil), [`floor`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor) or [`trunc`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc). Default is `trunc`:
|
|
552
552
|
|
|
553
553
|
```js
|
|
554
554
|
const stringify = fastJson(schema, { rounding: 'ceil' })
|
package/benchmark/bench.js
CHANGED
|
@@ -214,6 +214,27 @@ const benchmarks = [
|
|
|
214
214
|
b5: true
|
|
215
215
|
}
|
|
216
216
|
},
|
|
217
|
+
{
|
|
218
|
+
name: 'simple object',
|
|
219
|
+
schema: {
|
|
220
|
+
title: 'Example Schema',
|
|
221
|
+
type: 'object',
|
|
222
|
+
properties: {
|
|
223
|
+
firstName: {
|
|
224
|
+
type: 'string'
|
|
225
|
+
},
|
|
226
|
+
lastName: {
|
|
227
|
+
type: ['string', 'null']
|
|
228
|
+
},
|
|
229
|
+
age: {
|
|
230
|
+
description: 'Age in years',
|
|
231
|
+
type: 'integer',
|
|
232
|
+
minimum: 0
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
input: { firstName: 'Max', lastName: 'Power', age: 22 }
|
|
237
|
+
},
|
|
217
238
|
{
|
|
218
239
|
name: 'object with const string property',
|
|
219
240
|
schema: {
|
package/index.js
CHANGED
|
@@ -14,18 +14,20 @@ const Location = require('./lib/location')
|
|
|
14
14
|
|
|
15
15
|
let largeArraySize = 2e4
|
|
16
16
|
let largeArrayMechanism = 'default'
|
|
17
|
+
|
|
18
|
+
const validRoundingMethods = [
|
|
19
|
+
'floor',
|
|
20
|
+
'ceil',
|
|
21
|
+
'round',
|
|
22
|
+
'trunc'
|
|
23
|
+
]
|
|
24
|
+
|
|
17
25
|
const validLargeArrayMechanisms = [
|
|
18
26
|
'default',
|
|
19
27
|
'json-stringify'
|
|
20
28
|
]
|
|
21
29
|
|
|
22
|
-
const addComma =
|
|
23
|
-
if (addComma) {
|
|
24
|
-
json += ','
|
|
25
|
-
} else {
|
|
26
|
-
addComma = true
|
|
27
|
-
}
|
|
28
|
-
`
|
|
30
|
+
const addComma = '!addComma && (addComma = true) || (json += \',\')'
|
|
29
31
|
|
|
30
32
|
function isValidSchema (schema, name) {
|
|
31
33
|
if (!validate(schema)) {
|
|
@@ -41,7 +43,7 @@ function isValidSchema (schema, name) {
|
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
function resolveRef (location, ref) {
|
|
46
|
+
function resolveRef (context, location, ref) {
|
|
45
47
|
let hashIndex = ref.indexOf('#')
|
|
46
48
|
if (hashIndex === -1) {
|
|
47
49
|
hashIndex = ref.length
|
|
@@ -50,7 +52,7 @@ function resolveRef (location, ref) {
|
|
|
50
52
|
const schemaId = ref.slice(0, hashIndex) || location.getOriginSchemaId()
|
|
51
53
|
const jsonPointer = ref.slice(hashIndex) || '#'
|
|
52
54
|
|
|
53
|
-
const schema = refResolver.getSchema(schemaId, jsonPointer)
|
|
55
|
+
const schema = context.refResolver.getSchema(schemaId, jsonPointer)
|
|
54
56
|
|
|
55
57
|
if (schema === undefined) {
|
|
56
58
|
throw new Error(`Cannot find reference "${ref}"`)
|
|
@@ -58,42 +60,38 @@ function resolveRef (location, ref) {
|
|
|
58
60
|
|
|
59
61
|
const newLocation = new Location(schema, schemaId, jsonPointer)
|
|
60
62
|
if (schema.$ref !== undefined) {
|
|
61
|
-
return resolveRef(newLocation, schema.$ref)
|
|
63
|
+
return resolveRef(context, newLocation, schema.$ref)
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
return newLocation
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
const contextFunctionsNamesBySchema = new Map()
|
|
68
|
-
|
|
69
|
-
let rootSchemaId = null
|
|
70
|
-
let refResolver = null
|
|
71
|
-
let contextFunctions = null
|
|
72
|
-
let validatorSchemasIds = null
|
|
73
|
-
|
|
74
69
|
function build (schema, options) {
|
|
75
|
-
|
|
70
|
+
isValidSchema(schema)
|
|
76
71
|
|
|
77
|
-
contextFunctions = []
|
|
78
|
-
validatorSchemasIds = new Set()
|
|
79
72
|
options = options || {}
|
|
80
73
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
74
|
+
const context = {
|
|
75
|
+
functions: [],
|
|
76
|
+
functionsCounter: 0,
|
|
77
|
+
functionsNamesBySchema: new Map(),
|
|
78
|
+
options,
|
|
79
|
+
refResolver: new RefResolver(),
|
|
80
|
+
rootSchemaId: schema.$id || randomUUID(),
|
|
81
|
+
validatorSchemasIds: new Set()
|
|
82
|
+
}
|
|
84
83
|
|
|
85
|
-
|
|
86
|
-
refResolver.addSchema(schema, rootSchemaId)
|
|
84
|
+
context.refResolver.addSchema(schema, context.rootSchemaId)
|
|
87
85
|
|
|
88
86
|
if (options.schema) {
|
|
89
87
|
for (const key of Object.keys(options.schema)) {
|
|
90
88
|
isValidSchema(options.schema[key], key)
|
|
91
|
-
refResolver.addSchema(options.schema[key], key)
|
|
89
|
+
context.refResolver.addSchema(options.schema[key], key)
|
|
92
90
|
}
|
|
93
91
|
}
|
|
94
92
|
|
|
95
93
|
if (options.rounding) {
|
|
96
|
-
if (!
|
|
94
|
+
if (!validRoundingMethods.includes(options.rounding)) {
|
|
97
95
|
throw new Error(`Unsupported integer rounding method ${options.rounding}`)
|
|
98
96
|
}
|
|
99
97
|
}
|
|
@@ -107,41 +105,58 @@ function build (schema, options) {
|
|
|
107
105
|
}
|
|
108
106
|
|
|
109
107
|
if (options.largeArraySize) {
|
|
110
|
-
if (
|
|
108
|
+
if (typeof options.largeArraySize === 'string' && Number.isFinite(Number.parseInt(options.largeArraySize, 10))) {
|
|
109
|
+
largeArraySize = Number.parseInt(options.largeArraySize, 10)
|
|
110
|
+
} else if (typeof options.largeArraySize === 'number' && Number.isInteger(options.largeArraySize)) {
|
|
111
111
|
largeArraySize = options.largeArraySize
|
|
112
|
+
} else if (typeof options.largeArraySize === 'bigint') {
|
|
113
|
+
largeArraySize = Number(options.largeArraySize)
|
|
112
114
|
} else {
|
|
113
|
-
throw new Error(`Unsupported large array size. Expected integer-like, got ${options.largeArraySize}`)
|
|
115
|
+
throw new Error(`Unsupported large array size. Expected integer-like, got ${typeof options.largeArraySize} with value ${options.largeArraySize}`)
|
|
114
116
|
}
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
const location = new Location(schema, rootSchemaId)
|
|
118
|
-
const code = buildValue(location, 'input')
|
|
119
|
+
const location = new Location(schema, context.rootSchemaId)
|
|
120
|
+
const code = buildValue(context, location, 'input')
|
|
121
|
+
|
|
122
|
+
let contextFunctionCode
|
|
119
123
|
|
|
120
|
-
|
|
124
|
+
// If we have only the invocation of the 'anonymous0' function, we would
|
|
125
|
+
// basically just wrap the 'anonymous0' function in the 'main' function and
|
|
126
|
+
// and the overhead of the intermediate variabe 'json'. We can avoid the
|
|
127
|
+
// wrapping and the unnecessary memory allocation by aliasing 'anonymous0' to
|
|
128
|
+
// 'main'
|
|
129
|
+
if (code === 'json += anonymous0(input)') {
|
|
130
|
+
contextFunctionCode = `
|
|
131
|
+
${context.functions.join('\n')}
|
|
132
|
+
const main = anonymous0
|
|
133
|
+
return main
|
|
134
|
+
`
|
|
135
|
+
} else {
|
|
136
|
+
contextFunctionCode = `
|
|
121
137
|
function main (input) {
|
|
122
138
|
let json = ''
|
|
123
139
|
${code}
|
|
124
140
|
return json
|
|
125
141
|
}
|
|
126
|
-
${
|
|
142
|
+
${context.functions.join('\n')}
|
|
127
143
|
return main
|
|
128
|
-
|
|
144
|
+
`
|
|
145
|
+
}
|
|
129
146
|
|
|
130
147
|
const serializer = new Serializer(options)
|
|
131
148
|
const validator = new Validator(options.ajv)
|
|
132
149
|
|
|
133
|
-
for (const schemaId of validatorSchemasIds) {
|
|
134
|
-
const schema = refResolver.getSchema(schemaId)
|
|
150
|
+
for (const schemaId of context.validatorSchemasIds) {
|
|
151
|
+
const schema = context.refResolver.getSchema(schemaId)
|
|
135
152
|
validator.addSchema(schema, schemaId)
|
|
136
153
|
|
|
137
|
-
const dependencies = refResolver.getSchemaDependencies(schemaId)
|
|
154
|
+
const dependencies = context.refResolver.getSchemaDependencies(schemaId)
|
|
138
155
|
for (const [schemaId, schema] of Object.entries(dependencies)) {
|
|
139
156
|
validator.addSchema(schema, schemaId)
|
|
140
157
|
}
|
|
141
158
|
}
|
|
142
159
|
|
|
143
|
-
const dependenciesName = ['validator', 'serializer', contextFunctionCode]
|
|
144
|
-
|
|
145
160
|
if (options.debugMode) {
|
|
146
161
|
options.mode = 'debug'
|
|
147
162
|
}
|
|
@@ -150,14 +165,14 @@ function build (schema, options) {
|
|
|
150
165
|
return {
|
|
151
166
|
validator,
|
|
152
167
|
serializer,
|
|
153
|
-
code:
|
|
168
|
+
code: `validator\nserializer\n${contextFunctionCode}`,
|
|
154
169
|
ajv: validator.ajv
|
|
155
170
|
}
|
|
156
171
|
}
|
|
157
172
|
|
|
158
173
|
if (options.mode === 'standalone') {
|
|
159
174
|
// lazy load
|
|
160
|
-
const isValidatorUsed = validatorSchemasIds.size > 0
|
|
175
|
+
const isValidatorUsed = context.validatorSchemasIds.size > 0
|
|
161
176
|
const buildStandaloneCode = require('./lib/standalone')
|
|
162
177
|
return buildStandaloneCode(options, validator, isValidatorUsed, contextFunctionCode)
|
|
163
178
|
}
|
|
@@ -166,22 +181,16 @@ function build (schema, options) {
|
|
|
166
181
|
const contextFunc = new Function('validator', 'serializer', contextFunctionCode)
|
|
167
182
|
const stringifyFunc = contextFunc(validator, serializer)
|
|
168
183
|
|
|
169
|
-
refResolver = null
|
|
170
|
-
rootSchemaId = null
|
|
171
|
-
contextFunctions = null
|
|
172
|
-
validatorSchemasIds = null
|
|
173
|
-
contextFunctionsNamesBySchema.clear()
|
|
174
|
-
|
|
175
184
|
return stringifyFunc
|
|
176
185
|
}
|
|
177
186
|
|
|
178
187
|
const objectKeywords = [
|
|
179
|
-
'maxProperties',
|
|
180
|
-
'minProperties',
|
|
181
|
-
'required',
|
|
182
188
|
'properties',
|
|
183
|
-
'
|
|
189
|
+
'required',
|
|
184
190
|
'additionalProperties',
|
|
191
|
+
'patternProperties',
|
|
192
|
+
'maxProperties',
|
|
193
|
+
'minProperties',
|
|
185
194
|
'dependencies'
|
|
186
195
|
]
|
|
187
196
|
|
|
@@ -232,7 +241,7 @@ function inferTypeByKeyword (schema) {
|
|
|
232
241
|
return schema.type
|
|
233
242
|
}
|
|
234
243
|
|
|
235
|
-
function buildExtraObjectPropertiesSerializer (location) {
|
|
244
|
+
function buildExtraObjectPropertiesSerializer (context, location) {
|
|
236
245
|
const schema = location.schema
|
|
237
246
|
const propertiesKeys = Object.keys(schema.properties || {})
|
|
238
247
|
|
|
@@ -254,18 +263,11 @@ function buildExtraObjectPropertiesSerializer (location) {
|
|
|
254
263
|
for (const propertyKey in patternPropertiesSchema) {
|
|
255
264
|
const propertyLocation = patternPropertiesLocation.getPropertyLocation(propertyKey)
|
|
256
265
|
|
|
257
|
-
try {
|
|
258
|
-
RegExp(propertyKey)
|
|
259
|
-
} catch (err) {
|
|
260
|
-
const jsonPointer = propertyLocation.getSchemaRef()
|
|
261
|
-
throw new Error(`${err.message}. Invalid pattern property regexp key ${propertyKey} at ${jsonPointer}`)
|
|
262
|
-
}
|
|
263
|
-
|
|
264
266
|
code += `
|
|
265
267
|
if (/${propertyKey.replace(/\\*\//g, '\\/')}/.test(key)) {
|
|
266
268
|
${addComma}
|
|
267
269
|
json += serializer.asString(key) + ':'
|
|
268
|
-
${buildValue(propertyLocation, 'value')}
|
|
270
|
+
${buildValue(context, propertyLocation, 'value')}
|
|
269
271
|
continue
|
|
270
272
|
}
|
|
271
273
|
`
|
|
@@ -286,7 +288,7 @@ function buildExtraObjectPropertiesSerializer (location) {
|
|
|
286
288
|
code += `
|
|
287
289
|
${addComma}
|
|
288
290
|
json += serializer.asString(key) + ':'
|
|
289
|
-
${buildValue(propertyLocation, 'value')}
|
|
291
|
+
${buildValue(context, propertyLocation, 'value')}
|
|
290
292
|
`
|
|
291
293
|
}
|
|
292
294
|
}
|
|
@@ -297,7 +299,7 @@ function buildExtraObjectPropertiesSerializer (location) {
|
|
|
297
299
|
return code
|
|
298
300
|
}
|
|
299
301
|
|
|
300
|
-
function buildInnerObject (location) {
|
|
302
|
+
function buildInnerObject (context, location) {
|
|
301
303
|
const schema = location.schema
|
|
302
304
|
const required = schema.required || []
|
|
303
305
|
|
|
@@ -307,11 +309,10 @@ function buildInnerObject (location) {
|
|
|
307
309
|
Object.keys(schema.properties || {}).forEach((key) => {
|
|
308
310
|
let propertyLocation = propertiesLocation.getPropertyLocation(key)
|
|
309
311
|
if (propertyLocation.schema.$ref) {
|
|
310
|
-
propertyLocation = resolveRef(location, propertyLocation.schema.$ref)
|
|
312
|
+
propertyLocation = resolveRef(context, location, propertyLocation.schema.$ref)
|
|
311
313
|
}
|
|
312
314
|
|
|
313
315
|
const sanitized = JSON.stringify(key)
|
|
314
|
-
const asString = JSON.stringify(sanitized)
|
|
315
316
|
|
|
316
317
|
// Using obj['key'] !== undefined instead of obj.hasOwnProperty(prop) for perf reasons,
|
|
317
318
|
// see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
|
|
@@ -319,17 +320,17 @@ function buildInnerObject (location) {
|
|
|
319
320
|
code += `
|
|
320
321
|
if (obj[${sanitized}] !== undefined) {
|
|
321
322
|
${addComma}
|
|
322
|
-
json += ${
|
|
323
|
+
json += ${JSON.stringify(sanitized + ':')}
|
|
323
324
|
`
|
|
324
325
|
|
|
325
|
-
code += buildValue(propertyLocation, `obj[${
|
|
326
|
+
code += buildValue(context, propertyLocation, `obj[${sanitized}]`)
|
|
326
327
|
|
|
327
328
|
const defaultValue = propertyLocation.schema.default
|
|
328
329
|
if (defaultValue !== undefined) {
|
|
329
330
|
code += `
|
|
330
331
|
} else {
|
|
331
332
|
${addComma}
|
|
332
|
-
json += ${
|
|
333
|
+
json += ${JSON.stringify(sanitized + ':' + JSON.stringify(defaultValue))}
|
|
333
334
|
`
|
|
334
335
|
} else if (required.includes(key)) {
|
|
335
336
|
code += `
|
|
@@ -349,13 +350,13 @@ function buildInnerObject (location) {
|
|
|
349
350
|
}
|
|
350
351
|
|
|
351
352
|
if (schema.patternProperties || schema.additionalProperties) {
|
|
352
|
-
code += buildExtraObjectPropertiesSerializer(location)
|
|
353
|
+
code += buildExtraObjectPropertiesSerializer(context, location)
|
|
353
354
|
}
|
|
354
355
|
|
|
355
356
|
return code
|
|
356
357
|
}
|
|
357
358
|
|
|
358
|
-
function mergeAllOfSchema (location, schema, mergedSchema) {
|
|
359
|
+
function mergeAllOfSchema (context, location, schema, mergedSchema) {
|
|
359
360
|
const allOfLocation = location.getPropertyLocation('allOf')
|
|
360
361
|
|
|
361
362
|
for (let i = 0; i < schema.allOf.length; i++) {
|
|
@@ -363,7 +364,7 @@ function mergeAllOfSchema (location, schema, mergedSchema) {
|
|
|
363
364
|
|
|
364
365
|
if (allOfSchema.$ref) {
|
|
365
366
|
const allOfSchemaLocation = allOfLocation.getPropertyLocation(i)
|
|
366
|
-
allOfSchema = resolveRef(allOfSchemaLocation, allOfSchema.$ref).schema
|
|
367
|
+
allOfSchema = resolveRef(context, allOfSchemaLocation, allOfSchema.$ref).schema
|
|
367
368
|
}
|
|
368
369
|
|
|
369
370
|
let allOfSchemaType = allOfSchema.type
|
|
@@ -444,18 +445,18 @@ function mergeAllOfSchema (location, schema, mergedSchema) {
|
|
|
444
445
|
}
|
|
445
446
|
|
|
446
447
|
if (allOfSchema.allOf !== undefined) {
|
|
447
|
-
mergeAllOfSchema(location, allOfSchema, mergedSchema)
|
|
448
|
+
mergeAllOfSchema(context, location, allOfSchema, mergedSchema)
|
|
448
449
|
}
|
|
449
450
|
}
|
|
450
451
|
delete mergedSchema.allOf
|
|
451
452
|
|
|
452
453
|
mergedSchema.$id = `merged_${randomUUID()}`
|
|
453
|
-
refResolver.addSchema(mergedSchema)
|
|
454
|
+
context.refResolver.addSchema(mergedSchema)
|
|
454
455
|
location.addMergedSchema(mergedSchema, mergedSchema.$id)
|
|
455
456
|
}
|
|
456
457
|
|
|
457
|
-
function addIfThenElse (location, input) {
|
|
458
|
-
validatorSchemasIds.add(location.getSchemaId())
|
|
458
|
+
function addIfThenElse (context, location, input) {
|
|
459
|
+
context.validatorSchemasIds.add(location.getSchemaId())
|
|
459
460
|
|
|
460
461
|
const schema = merge({}, location.schema)
|
|
461
462
|
const thenSchema = schema.then
|
|
@@ -476,9 +477,9 @@ function addIfThenElse (location, input) {
|
|
|
476
477
|
|
|
477
478
|
return `
|
|
478
479
|
if (validator.validate("${ifSchemaRef}", ${input})) {
|
|
479
|
-
${buildValue(thenLocation, input)}
|
|
480
|
+
${buildValue(context, thenLocation, input)}
|
|
480
481
|
} else {
|
|
481
|
-
${buildValue(elseLocation, input)}
|
|
482
|
+
${buildValue(context, elseLocation, input)}
|
|
482
483
|
}
|
|
483
484
|
`
|
|
484
485
|
}
|
|
@@ -490,19 +491,19 @@ function toJSON (variableName) {
|
|
|
490
491
|
`
|
|
491
492
|
}
|
|
492
493
|
|
|
493
|
-
function buildObject (location) {
|
|
494
|
+
function buildObject (context, location) {
|
|
494
495
|
const schema = location.schema
|
|
495
496
|
|
|
496
|
-
if (
|
|
497
|
-
return
|
|
497
|
+
if (context.functionsNamesBySchema.has(schema)) {
|
|
498
|
+
return context.functionsNamesBySchema.get(schema)
|
|
498
499
|
}
|
|
499
500
|
|
|
500
|
-
const functionName = generateFuncName()
|
|
501
|
-
|
|
501
|
+
const functionName = generateFuncName(context)
|
|
502
|
+
context.functionsNamesBySchema.set(schema, functionName)
|
|
502
503
|
|
|
503
504
|
let schemaRef = location.getSchemaRef()
|
|
504
|
-
if (schemaRef.startsWith(rootSchemaId)) {
|
|
505
|
-
schemaRef = schemaRef.replace(rootSchemaId, '')
|
|
505
|
+
if (schemaRef.startsWith(context.rootSchemaId)) {
|
|
506
|
+
schemaRef = schemaRef.replace(context.rootSchemaId, '')
|
|
506
507
|
}
|
|
507
508
|
|
|
508
509
|
let functionCode = `
|
|
@@ -511,44 +512,43 @@ function buildObject (location) {
|
|
|
511
512
|
`
|
|
512
513
|
|
|
513
514
|
functionCode += `
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
515
|
+
const obj = ${toJSON('input')}
|
|
516
|
+
let json = '{'
|
|
517
|
+
let addComma = false
|
|
517
518
|
`
|
|
518
519
|
|
|
519
|
-
functionCode += buildInnerObject(location)
|
|
520
|
+
functionCode += buildInnerObject(context, location)
|
|
520
521
|
functionCode += `
|
|
521
|
-
json
|
|
522
|
-
return json
|
|
522
|
+
return json + '}'
|
|
523
523
|
}
|
|
524
524
|
`
|
|
525
525
|
|
|
526
|
-
|
|
526
|
+
context.functions.push(functionCode)
|
|
527
527
|
return functionName
|
|
528
528
|
}
|
|
529
529
|
|
|
530
|
-
function buildArray (location) {
|
|
530
|
+
function buildArray (context, location) {
|
|
531
531
|
const schema = location.schema
|
|
532
532
|
|
|
533
533
|
let itemsLocation = location.getPropertyLocation('items')
|
|
534
534
|
itemsLocation.schema = itemsLocation.schema || {}
|
|
535
535
|
|
|
536
536
|
if (itemsLocation.schema.$ref) {
|
|
537
|
-
itemsLocation = resolveRef(itemsLocation, itemsLocation.schema.$ref)
|
|
537
|
+
itemsLocation = resolveRef(context, itemsLocation, itemsLocation.schema.$ref)
|
|
538
538
|
}
|
|
539
539
|
|
|
540
540
|
const itemsSchema = itemsLocation.schema
|
|
541
541
|
|
|
542
|
-
if (
|
|
543
|
-
return
|
|
542
|
+
if (context.functionsNamesBySchema.has(schema)) {
|
|
543
|
+
return context.functionsNamesBySchema.get(schema)
|
|
544
544
|
}
|
|
545
545
|
|
|
546
|
-
const functionName = generateFuncName()
|
|
547
|
-
|
|
546
|
+
const functionName = generateFuncName(context)
|
|
547
|
+
context.functionsNamesBySchema.set(schema, functionName)
|
|
548
548
|
|
|
549
549
|
let schemaRef = location.getSchemaRef()
|
|
550
|
-
if (schemaRef.startsWith(rootSchemaId)) {
|
|
551
|
-
schemaRef = schemaRef.replace(rootSchemaId, '')
|
|
550
|
+
if (schemaRef.startsWith(context.rootSchemaId)) {
|
|
551
|
+
schemaRef = schemaRef.replace(context.rootSchemaId, '')
|
|
552
552
|
}
|
|
553
553
|
|
|
554
554
|
let functionCode = `
|
|
@@ -558,12 +558,12 @@ function buildArray (location) {
|
|
|
558
558
|
|
|
559
559
|
functionCode += `
|
|
560
560
|
if (!Array.isArray(obj)) {
|
|
561
|
-
throw new TypeError(\`The value '
|
|
561
|
+
throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`)
|
|
562
562
|
}
|
|
563
563
|
const arrayLength = obj.length
|
|
564
564
|
`
|
|
565
565
|
|
|
566
|
-
if (!schema.additionalItems) {
|
|
566
|
+
if (!schema.additionalItems && Array.isArray(itemsSchema)) {
|
|
567
567
|
functionCode += `
|
|
568
568
|
if (arrayLength > ${itemsSchema.length}) {
|
|
569
569
|
throw new Error(\`Item at ${itemsSchema.length} does not match schema definition.\`)
|
|
@@ -586,7 +586,7 @@ function buildArray (location) {
|
|
|
586
586
|
if (Array.isArray(itemsSchema)) {
|
|
587
587
|
for (let i = 0; i < itemsSchema.length; i++) {
|
|
588
588
|
const item = itemsSchema[i]
|
|
589
|
-
const tmpRes = buildValue(itemsLocation.getPropertyLocation(i), `obj[${i}]`)
|
|
589
|
+
const tmpRes = buildValue(context, itemsLocation.getPropertyLocation(i), `obj[${i}]`)
|
|
590
590
|
functionCode += `
|
|
591
591
|
if (${i} < arrayLength) {
|
|
592
592
|
if (${buildArrayTypeCondition(item.type, `[${i}]`)}) {
|
|
@@ -606,15 +606,14 @@ function buildArray (location) {
|
|
|
606
606
|
if (schema.additionalItems) {
|
|
607
607
|
functionCode += `
|
|
608
608
|
for (let i = ${itemsSchema.length}; i < arrayLength; i++) {
|
|
609
|
-
|
|
610
|
-
jsonOutput += json
|
|
609
|
+
jsonOutput += JSON.stringify(obj[i])
|
|
611
610
|
if (i < arrayLength - 1) {
|
|
612
611
|
jsonOutput += ','
|
|
613
612
|
}
|
|
614
613
|
}`
|
|
615
614
|
}
|
|
616
615
|
} else {
|
|
617
|
-
const code = buildValue(itemsLocation, 'obj[i]')
|
|
616
|
+
const code = buildValue(context, itemsLocation, 'obj[i]')
|
|
618
617
|
functionCode += `
|
|
619
618
|
for (let i = 0; i < arrayLength; i++) {
|
|
620
619
|
let json = ''
|
|
@@ -630,7 +629,7 @@ function buildArray (location) {
|
|
|
630
629
|
return \`[\${jsonOutput}]\`
|
|
631
630
|
}`
|
|
632
631
|
|
|
633
|
-
|
|
632
|
+
context.functions.push(functionCode)
|
|
634
633
|
return functionName
|
|
635
634
|
}
|
|
636
635
|
|
|
@@ -671,12 +670,11 @@ function buildArrayTypeCondition (type, accessor) {
|
|
|
671
670
|
return condition
|
|
672
671
|
}
|
|
673
672
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
return 'anonymous' + genFuncNameCounter++
|
|
673
|
+
function generateFuncName (context) {
|
|
674
|
+
return 'anonymous' + context.functionsCounter++
|
|
677
675
|
}
|
|
678
676
|
|
|
679
|
-
function buildMultiTypeSerializer (location, input) {
|
|
677
|
+
function buildMultiTypeSerializer (context, location, input) {
|
|
680
678
|
const schema = location.schema
|
|
681
679
|
const types = schema.type.sort(t1 => t1 === 'null' ? -1 : 1)
|
|
682
680
|
|
|
@@ -684,7 +682,7 @@ function buildMultiTypeSerializer (location, input) {
|
|
|
684
682
|
|
|
685
683
|
types.forEach((type, index) => {
|
|
686
684
|
location.schema = { ...location.schema, type }
|
|
687
|
-
const nestedResult = buildSingleTypeSerializer(location, input)
|
|
685
|
+
const nestedResult = buildSingleTypeSerializer(context, location, input)
|
|
688
686
|
|
|
689
687
|
const statement = index === 0 ? 'if' : 'else if'
|
|
690
688
|
switch (type) {
|
|
@@ -735,14 +733,18 @@ function buildMultiTypeSerializer (location, input) {
|
|
|
735
733
|
}
|
|
736
734
|
}
|
|
737
735
|
})
|
|
736
|
+
let schemaRef = location.getSchemaRef()
|
|
737
|
+
if (schemaRef.startsWith(context.rootSchemaId)) {
|
|
738
|
+
schemaRef = schemaRef.replace(context.rootSchemaId, '')
|
|
739
|
+
}
|
|
738
740
|
code += `
|
|
739
|
-
else throw new
|
|
741
|
+
else throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`)
|
|
740
742
|
`
|
|
741
743
|
|
|
742
744
|
return code
|
|
743
745
|
}
|
|
744
746
|
|
|
745
|
-
function buildSingleTypeSerializer (location, input) {
|
|
747
|
+
function buildSingleTypeSerializer (context, location, input) {
|
|
746
748
|
const schema = location.schema
|
|
747
749
|
|
|
748
750
|
switch (schema.type) {
|
|
@@ -766,11 +768,11 @@ function buildSingleTypeSerializer (location, input) {
|
|
|
766
768
|
case 'boolean':
|
|
767
769
|
return `json += serializer.asBoolean(${input})`
|
|
768
770
|
case 'object': {
|
|
769
|
-
const funcName = buildObject(location)
|
|
771
|
+
const funcName = buildObject(context, location)
|
|
770
772
|
return `json += ${funcName}(${input})`
|
|
771
773
|
}
|
|
772
774
|
case 'array': {
|
|
773
|
-
const funcName = buildArray(location)
|
|
775
|
+
const funcName = buildArray(context, location)
|
|
774
776
|
return `json += ${funcName}(${input})`
|
|
775
777
|
}
|
|
776
778
|
case undefined:
|
|
@@ -807,7 +809,7 @@ function buildConstSerializer (location, input) {
|
|
|
807
809
|
return code
|
|
808
810
|
}
|
|
809
811
|
|
|
810
|
-
function buildValue (location, input) {
|
|
812
|
+
function buildValue (context, location, input) {
|
|
811
813
|
let schema = location.schema
|
|
812
814
|
|
|
813
815
|
if (typeof schema === 'boolean') {
|
|
@@ -815,7 +817,7 @@ function buildValue (location, input) {
|
|
|
815
817
|
}
|
|
816
818
|
|
|
817
819
|
if (schema.$ref) {
|
|
818
|
-
location = resolveRef(location, schema.$ref)
|
|
820
|
+
location = resolveRef(context, location, schema.$ref)
|
|
819
821
|
schema = location.schema
|
|
820
822
|
}
|
|
821
823
|
|
|
@@ -827,11 +829,11 @@ function buildValue (location, input) {
|
|
|
827
829
|
}
|
|
828
830
|
|
|
829
831
|
if (schema.if && schema.then) {
|
|
830
|
-
return addIfThenElse(location, input)
|
|
832
|
+
return addIfThenElse(context, location, input)
|
|
831
833
|
}
|
|
832
834
|
|
|
833
835
|
if (schema.allOf) {
|
|
834
|
-
mergeAllOfSchema(location, schema, clone(schema))
|
|
836
|
+
mergeAllOfSchema(context, location, schema, clone(schema))
|
|
835
837
|
schema = location.schema
|
|
836
838
|
}
|
|
837
839
|
|
|
@@ -840,7 +842,7 @@ function buildValue (location, input) {
|
|
|
840
842
|
let code = ''
|
|
841
843
|
|
|
842
844
|
if (type === undefined && (schema.anyOf || schema.oneOf)) {
|
|
843
|
-
validatorSchemasIds.add(location.getSchemaId())
|
|
845
|
+
context.validatorSchemasIds.add(location.getSchemaId())
|
|
844
846
|
|
|
845
847
|
const type = schema.anyOf ? 'anyOf' : 'oneOf'
|
|
846
848
|
const anyOfLocation = location.getPropertyLocation(type)
|
|
@@ -848,15 +850,20 @@ function buildValue (location, input) {
|
|
|
848
850
|
for (let index = 0; index < location.schema[type].length; index++) {
|
|
849
851
|
const optionLocation = anyOfLocation.getPropertyLocation(index)
|
|
850
852
|
const schemaRef = optionLocation.getSchemaRef()
|
|
851
|
-
const nestedResult = buildValue(optionLocation, input)
|
|
853
|
+
const nestedResult = buildValue(context, optionLocation, input)
|
|
852
854
|
code += `
|
|
853
855
|
${index === 0 ? 'if' : 'else if'}(validator.validate("${schemaRef}", ${input}))
|
|
854
856
|
${nestedResult}
|
|
855
857
|
`
|
|
856
858
|
}
|
|
857
859
|
|
|
860
|
+
let schemaRef = location.getSchemaRef()
|
|
861
|
+
if (schemaRef.startsWith(context.rootSchemaId)) {
|
|
862
|
+
schemaRef = schemaRef.replace(context.rootSchemaId, '')
|
|
863
|
+
}
|
|
864
|
+
|
|
858
865
|
code += `
|
|
859
|
-
else throw new
|
|
866
|
+
else throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`)
|
|
860
867
|
`
|
|
861
868
|
return code
|
|
862
869
|
}
|
|
@@ -873,9 +880,9 @@ function buildValue (location, input) {
|
|
|
873
880
|
if (schema.const !== undefined) {
|
|
874
881
|
code += buildConstSerializer(location, input)
|
|
875
882
|
} else if (Array.isArray(type)) {
|
|
876
|
-
code += buildMultiTypeSerializer(location, input)
|
|
883
|
+
code += buildMultiTypeSerializer(context, location, input)
|
|
877
884
|
} else {
|
|
878
|
-
code += buildSingleTypeSerializer(location, input)
|
|
885
|
+
code += buildSingleTypeSerializer(context, location, input)
|
|
879
886
|
}
|
|
880
887
|
|
|
881
888
|
if (nullable) {
|