Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 96 additions & 38 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,14 +304,14 @@ function buildExtraObjectPropertiesSerializer (context, location, addComma, objV
const propertiesKeys = Object.keys(schema.properties || {})

let code = `
const propertiesKeys = ${JSON.stringify(propertiesKeys)}
for (const [key, value] of Object.entries(${objVar})) {
for (const key of Object.keys(${objVar})) {
if (
propertiesKeys.includes(key) ||
value === undefined ||
typeof value === 'function' ||
typeof value === 'symbol'
${propertiesKeys.length > 0 ? propertiesKeys.map(k => `key === ${JSON.stringify(k)}`).join(' || ') + ' ||' : ''}
${objVar}[key] === undefined ||
typeof ${objVar}[key] === 'function' ||
typeof ${objVar}[key] === 'symbol'
) continue
const value = ${objVar}[key]
`

const patternPropertiesLocation = location.getPropertyLocation('patternProperties')
Expand Down Expand Up @@ -385,45 +385,99 @@ function buildInnerObject (context, location, objVar) {

const localUid = context.uid++
let addComma = ''
const needsRuntimeComma = propertiesKeys.length > 1 || schema.patternProperties || (schema.additionalProperties !== undefined && schema.additionalProperties !== false)

if (needsRuntimeComma) {
code += `let addComma_${localUid} = false\n`
addComma = `!addComma_${localUid} && (addComma_${localUid} = true) || (json += JSON_STR_COMMA)`
}
if (requiredProperties.length > 0) {
// If we have required properties, we know that at least one property will be serialized.
// We can avoid the runtime check for the comma.

for (const key of propertiesKeys) {
let propertyLocation = propertiesLocation.getPropertyLocation(key)
if (propertyLocation.schema.$ref) {
propertyLocation = resolveRef(context, propertyLocation)
}
// The first property is required, so we don't need a comma.
// For the subsequent properties, we can blindly add a comma.

const sanitizedKey = JSON.stringify(key)
const value = `value_${key.replace(/[^a-zA-Z0-9]/g, '_')}_${context.uid++}`
const defaultValue = propertyLocation.schema.default
const isRequired = requiredProperties.includes(key)
for (let i = 0; i < propertiesKeys.length; i++) {
const key = propertiesKeys[i]
const propertyLocation = propertiesLocation.getPropertyLocation(key)

code += `
let resolvedLocation = propertyLocation
if (propertyLocation.schema.$ref) {
resolvedLocation = resolveRef(context, propertyLocation)
}

const sanitizedKey = JSON.stringify(key)
const value = `value_${key.replace(/[^a-zA-Z0-9]/g, '_')}_${context.uid++}`
const defaultValue = resolvedLocation.schema.default
const isRequired = requiredProperties.includes(key)

// i === 0 means it's the first property, and it IS required (due to sort). No comma.
// i > 0 means it follows a required property (or a sequence starting with one). Unconditional comma.
const currentAddComma = i === 0 ? '' : 'json += JSON_STR_COMMA'

code += `
const ${value} = ${objVar}[${sanitizedKey}]
if (${value} !== undefined) {
${addComma}
${currentAddComma}
json += ${JSON.stringify(sanitizedKey + ':')}
${buildValue(context, propertyLocation, `${value}`)}
${buildValue(context, resolvedLocation, `${value}`)}
}`

if (defaultValue !== undefined) {
code += ` else {
${addComma}
if (defaultValue !== undefined) {
code += ` else {
${currentAddComma}
json += ${JSON.stringify(sanitizedKey + ':' + JSON.stringify(defaultValue))}
}
`
} else if (isRequired) {
code += ` else {
} else if (isRequired) {
code += ` else {
throw new Error('${sanitizedKey.replace(/'/g, '\\\'')} is required!')
}
`
} else {
code += '\n'
} else {
code += '\n'
}
}

addComma = 'json += JSON_STR_COMMA'
} else {
const needsRuntimeComma = propertiesKeys.length > 1 || schema.patternProperties || (schema.additionalProperties !== undefined && schema.additionalProperties !== false)

if (needsRuntimeComma) {
code += `let addComma_${localUid} = false\n`
addComma = `!addComma_${localUid} && (addComma_${localUid} = true) || (json += JSON_STR_COMMA)`
}

for (const key of propertiesKeys) {
let propertyLocation = propertiesLocation.getPropertyLocation(key)
if (propertyLocation.schema.$ref) {
propertyLocation = resolveRef(context, propertyLocation)
}

const sanitizedKey = JSON.stringify(key)
const value = `value_${key.replace(/[^a-zA-Z0-9]/g, '_')}_${context.uid++}`
const defaultValue = propertyLocation.schema.default
const isRequired = requiredProperties.includes(key) // Should be false here but good to keep

code += `
const ${value} = ${objVar}[${sanitizedKey}]
if (${value} !== undefined) {
${addComma}
json += ${JSON.stringify(sanitizedKey + ':')}
${buildValue(context, propertyLocation, `${value}`)}
}`

if (defaultValue !== undefined) {
code += ` else {
${addComma}
json += ${JSON.stringify(sanitizedKey + ':' + JSON.stringify(defaultValue))}
}
`
} else if (isRequired) {
// Should not happen if requiredProperties.length === 0 but safety
code += ` else {
throw new Error('${sanitizedKey.replace(/'/g, '\\\'')} is required!')
}
`
} else {
code += '\n'
}
}
}

Expand Down Expand Up @@ -641,12 +695,14 @@ function buildArray (context, location, input) {
} else {
const code = buildValue(context, itemsLocation, 'value')
functionCode += `
for (let i = 0; i < arrayLength; i++) {
if (i) {
if (arrayLength > 0) {
const value = obj[0]
${code}
for (let i = 1; i < arrayLength; i++) {
json += JSON_STR_COMMA
const value = obj[i]
${code}
}
const value = obj[i]
${code}
}`
}

Expand Down Expand Up @@ -717,12 +773,14 @@ function buildArray (context, location, input) {
} else {
const code = buildValue(context, itemsLocation, 'value')
inlinedCode += `
for (let i = 0; i < arrayLength_${objVar}; i++) {
if (i) {
if (arrayLength_${objVar} > 0) {
const value = ${objVar}[0]
${code}
for (let i = 1; i < arrayLength_${objVar}; i++) {
json += JSON_STR_COMMA
const value = ${objVar}[i]
${code}
}
const value = ${objVar}[i]
${code}
}`
}

Expand Down