Skip to content
Open
Show file tree
Hide file tree
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
28 changes: 14 additions & 14 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ function buildObject (context, location, input) {
// ${asComment(schemaRef)}
function ${functionName} (input) {
const obj = ${toJSON('input')}
if (obj === null) return ${nullable ? 'JSON_STR_NULL' : 'JSON_STR_EMPTY_OBJECT'}
if (obj == null) return ${nullable ? 'JSON_STR_NULL' : 'JSON_STR_EMPTY_OBJECT'}
let json = ''

${buildInnerObject(context, location, 'obj')}
Expand All @@ -834,7 +834,7 @@ function buildObject (context, location, input) {
const objVar = `obj_${context.uid++}`
const code = `
const ${objVar} = ${toJSON(input)}
if (${objVar} === null) {
if (${objVar} == null) {
json += ${nullable ? 'JSON_STR_NULL' : 'JSON_STR_EMPTY_OBJECT'}
} else {
${buildInnerObject(context, location, objVar)}
Expand Down Expand Up @@ -880,7 +880,7 @@ function buildArray (context, location, input) {
`

functionCode += `
if (obj === null) return ${nullable ? 'JSON_STR_NULL' : 'JSON_STR_EMPTY_ARRAY'}
if (obj == null) return ${nullable ? 'JSON_STR_NULL' : 'JSON_STR_EMPTY_ARRAY'}
if (!Array.isArray(obj)) {
throw new TypeError(${JSON.stringify(`The value of '${schemaRef}' does not match schema definition.`)})
}
Expand Down Expand Up @@ -964,7 +964,7 @@ function buildArray (context, location, input) {
const objVar = `obj_${context.uid++}`
let inlinedCode = `
const ${objVar} = ${input}
if (${objVar} === null) {
if (${objVar} == null) {
json += ${nullable ? 'JSON_STR_NULL' : 'JSON_STR_EMPTY_ARRAY'}
} else if (!Array.isArray(${objVar})) {
throw new TypeError(${JSON.stringify(`The value of '${safeSchemaRef}' does not match schema definition.`)})
Expand Down Expand Up @@ -1051,11 +1051,11 @@ function buildArrayTypeCondition (type, accessor) {
let condition
switch (type) {
case 'null':
condition = `${accessor} === null`
condition = `${accessor} == null`
break
case 'string':
condition = `typeof ${accessor} === 'string' ||
${accessor} === null ||
${accessor} == null ||
${accessor} instanceof Date ||
${accessor} instanceof RegExp ||
(
Expand Down Expand Up @@ -1108,7 +1108,7 @@ function buildMultiTypeSerializer (context, location, input) {
switch (type) {
case 'null':
code += `
${statement} (${input} === null) {
${statement} (${input} == null) {
${nestedResult}
}
`
Expand All @@ -1117,7 +1117,7 @@ function buildMultiTypeSerializer (context, location, input) {
code += `
${statement}(
typeof ${input} === "string" ||
${input} === null ||
${input} == null ||
${input} instanceof Date ||
${input} instanceof RegExp ||
(
Expand All @@ -1141,7 +1141,7 @@ function buildMultiTypeSerializer (context, location, input) {
}
case 'integer': {
code += `
${statement}(Number.isInteger(${input}) || ${input} === null) {
${statement}(Number.isInteger(${input}) || ${input} == null) {
${nestedResult}
}
`
Expand All @@ -1152,15 +1152,15 @@ function buildMultiTypeSerializer (context, location, input) {
// by this branch and serialized as an object (dropping its items). Exclude
// arrays here so a sibling `array` type in the same `type` list can match.
code += `
${statement}((typeof ${input} === "object" && !Array.isArray(${input})) || ${input} === null) {
${statement}((typeof ${input} === "object" && !Array.isArray(${input})) || ${input} == null) {
${nestedResult}
}
`
break
}
default: {
code += `
${statement}(typeof ${input} === "${type}" || ${input} === null) {
${statement}(typeof ${input} === "${type}" || ${input} == null) {
${nestedResult}
}
`
Expand Down Expand Up @@ -1193,7 +1193,7 @@ function buildSingleTypeSerializer (context, location, input) {
} else {
return `
if (typeof ${input} !== 'string') {
if (${input} === null) {
if (${input} == null) {
json += JSON_STR_EMPTY_STRING
} else if (${input} instanceof Date) {
json += JSON_STR_QUOTE + ${input}.toISOString() + JSON_STR_QUOTE
Expand Down Expand Up @@ -1328,7 +1328,7 @@ function buildConstSerializer (location, input) {

if (hasNullType) {
code += `
if (${input} === null) {
if (${input} == null) {
json += JSON_STR_NULL
} else {
`
Expand Down Expand Up @@ -1538,7 +1538,7 @@ function buildValue (context, location, input) {
const nullable = schema.nullable === true
if (nullable) {
code += `
if (${input} === null) {
if (${input} == null) {
json += JSON_STR_NULL
} else {
`
Expand Down
12 changes: 9 additions & 3 deletions lib/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ module.exports = class Serializer {
} else if (typeof i === 'bigint') {
return i.toString()
}
if (i == null) {
return '0'
}
/* eslint no-undef: "off" */
const integer = this.parseInteger(i)
// check if number is Infinity or NaN
Expand All @@ -77,6 +80,9 @@ module.exports = class Serializer {
}

asNumber (i) {
if (i == null) {
return '0'
}
// fast cast to number
const num = Number(i)
// check if number is NaN
Expand All @@ -95,7 +101,7 @@ module.exports = class Serializer {
}

asDateTime (date) {
if (date === null) return '""'
if (date == null) return '""'
if (date instanceof Date) {
return '"' + date.toISOString() + '"'
}
Expand All @@ -106,7 +112,7 @@ module.exports = class Serializer {
}

asDate (date) {
if (date === null) return '""'
if (date == null) return '""'
if (date instanceof Date) {
return '"' + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, 10) + '"'
}
Expand All @@ -117,7 +123,7 @@ module.exports = class Serializer {
}

asTime (date) {
if (date === null) return '""'
if (date == null) return '""'
if (date instanceof Date) {
return '"' + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(11, 19) + '"'
}
Expand Down
206 changes: 206 additions & 0 deletions test/missing-values.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,209 @@ test('handle null when value should be boolean', (t) => {

t.assert.equal('{"bool":false}', stringify({ bool: null }))
})

test('handle undefined when value should be string', (t) => {
t.plan(1)

const stringify = build({
type: 'string'
})

t.assert.equal('""', stringify(undefined))
})

test('handle undefined in array of strings', (t) => {
t.plan(1)

const stringify = build({
type: 'array',
items: {
type: 'string'
}
})

t.assert.equal('["test1","test2","test3",""]', stringify(['test1', 'test2', 'test3', undefined]))
})

test('handle undefined in array of integers', (t) => {
t.plan(1)

const stringify = build({
type: 'array',
items: {
type: 'integer'
}
})

t.assert.equal('[1,2,0]', stringify([1, 2, undefined]))
})

test('handle undefined in array of numbers', (t) => {
t.plan(1)

const stringify = build({
type: 'array',
items: {
type: 'number'
}
})

t.assert.equal('[1.5,0]', stringify([1.5, undefined]))
})

test('handle undefined in array of booleans', (t) => {
t.plan(1)

const stringify = build({
type: 'array',
items: {
type: 'boolean'
}
})

t.assert.equal('[true,false]', stringify([true, undefined]))
})

test('handle undefined in array of objects', (t) => {
t.plan(1)

const stringify = build({
type: 'array',
items: {
type: 'object',
properties: {
str: {
type: 'string'
}
}
}
})

t.assert.equal('[{"str":"test"},{}]', stringify([{ str: 'test' }, undefined]))
})

test('handle undefined in array of date-time strings', (t) => {
t.plan(1)

const stringify = build({
type: 'array',
items: {
type: 'string',
format: 'date-time'
}
})

t.assert.equal('["2020-01-01T00:00:00.000Z",""]', stringify(['2020-01-01T00:00:00.000Z', undefined]))
})

test('handle undefined in array of date strings', (t) => {
t.plan(1)

const stringify = build({
type: 'array',
items: {
type: 'string',
format: 'date'
}
})

t.assert.equal('["2020-01-01",""]', stringify(['2020-01-01', undefined]))
})

test('handle undefined in array of time strings', (t) => {
t.plan(1)

const stringify = build({
type: 'array',
items: {
type: 'string',
format: 'time'
}
})

t.assert.equal('["00:00:00",""]', stringify(['00:00:00', undefined]))
})

test('handle undefined when value is nullable', (t) => {
t.plan(1)

const stringify = build({
type: 'string',
nullable: true
})

t.assert.equal('null', stringify(undefined))
})

test('handle undefined in tuple of strings', (t) => {
t.plan(1)

const stringify = build({
type: 'array',
items: [
{
type: 'string'
},
{
type: 'string'
}
]
})

t.assert.equal('["test",""]', stringify(['test', undefined]))
})

test('handle undefined in array with multiple item types', (t) => {
t.plan(1)

const stringify = build({
type: 'array',
items: {
type: ['string', 'integer']
}
})

t.assert.equal('["test",""]', stringify(['test', undefined]))
})

test('handle undefined in nested arrays', (t) => {
t.plan(1)

const stringify = build({
type: 'array',
items: {
type: 'array',
items: {
type: 'string'
}
}
})

t.assert.equal('[["test"],[]]', stringify([['test'], undefined]))
})

test('undefined object properties are still skipped', (t) => {
t.plan(1)

const stringify = build({
type: 'object',
properties: {
str: {
type: 'string'
}
}
})

t.assert.equal('{}', stringify({ str: undefined }))
})

test('handle undefined in multi-type, tuple and const positions', (t) => {
t.plan(6)

t.assert.equal('["a",null]', build({ type: 'array', items: { type: ['null', 'string'] } })(['a', undefined]))
t.assert.equal('[1,0]', build({ type: 'array', items: { type: ['integer', 'string'] } })([1, undefined]))
t.assert.equal('[{},{}]', build({ type: 'array', items: { type: ['object', 'string'] } })([{}, undefined]))
t.assert.equal('[true,false]', build({ type: 'array', items: { type: ['boolean', 'string'] } })([true, undefined]))
t.assert.equal('[null]', build({ type: 'array', items: [{ type: 'null' }] })([undefined]))
t.assert.equal('["x",null]', build({ type: 'array', items: { type: ['string', 'null'], const: 'x' } })(['x', undefined]))
})
Loading