Skip to content

Commit 87f783b

Browse files
committed
Fixed null support for Joi and simple schema.
1 parent 763b524 commit 87f783b

File tree

4 files changed

+40
-15
lines changed

4 files changed

+40
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
- Nullable fields weren't always detected in JSON Schemas.
1717
- Constraints weren't added when using default values for an adapter with introspection.
18+
- Fixed null support for Joi schemas.
1819

1920
## [2.3.0] - 2024-02-18
2021

src/lib/adapters/joi-to-json-schema/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// TODO: Need more tests
33

44
import type { JSONSchema } from '../../jsonSchema/index.js';
5-
import type { JSONSchema7TypeName } from 'json-schema';
5+
import type { JSONSchema7Definition, JSONSchema7TypeName } from 'json-schema';
66

77
function assert(condition: unknown, errorMessage: string) {
88
if (!condition) throw new Error(errorMessage);
@@ -295,6 +295,18 @@ export default function convert(joi: Record<string, any>, transformer?: Transfor
295295
result = transformer(result, joi as Joi);
296296
}
297297

298+
if (joi._valids?._values && joi._valids._values.size && !joi._flags.allowOnly) {
299+
const constants = Array.from(joi._valids._values).map((v) => ({
300+
const: v
301+
})) as JSONSchema7Definition[];
302+
303+
if (result.anyOf) {
304+
result.anyOf = [...constants, ...result.anyOf];
305+
} else {
306+
result = { anyOf: [...constants, result] };
307+
}
308+
}
309+
298310
return result;
299311
}
300312

src/lib/adapters/simple-schema/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export function simpleSchema(value: unknown): JSONSchema {
2828
),
2929
required: Object.keys(obj).filter(
3030
(key) =>
31-
(!obj[key] && obj[key] !== undefined) ||
31+
(!obj[key] && obj[key] !== undefined && obj[key] !== null) ||
3232
(Array.isArray(obj[key]) && !(obj[key] as unknown[]).length)
3333
),
3434
additionalProperties: false

src/tests/superValidate.test.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,15 @@ import { splitPath } from '$lib/stringPath.js';
5353
/*
5454
TEST SCHEMA TEMPLATE:
5555
56-
| field | type | required | constraints | default |
56+
| field | type | opt/null | constraints | default |
5757
| ------- | -------- | -------- | ----------------------- | --------- |
58-
| name | string | no | | "Unknown" |
58+
| name | string | opt | | "Unknown" |
5959
| email | string | yes | email format | |
6060
| tags | string[] | yes | array >= 3, string >= 2 | |
6161
| score | number | yes | integer, >= 0 | |
62-
| date | Date | no | | |
63-
| nospace | string | no | pattern /^\S*$/ | |
62+
| date | Date | opt | | |
63+
| nospace | string | opt | pattern /^\S*$/ | |
64+
| extra | string | null | | |
6465
*/
6566

6667
/**
@@ -73,7 +74,8 @@ const validData = {
7374
tags: ['Ok 1', 'Ok 2', 'Ok 3'],
7475
score: 10,
7576
date: new Date('2024-01-01'),
76-
nospace: 'Abc'
77+
nospace: 'Abc',
78+
extra: null
7779
};
7880

7981
/**
@@ -93,7 +95,8 @@ const defaults = {
9395
tags: [] as string[],
9496
score: 0,
9597
date: undefined,
96-
nospace: undefined
98+
nospace: undefined,
99+
extra: null
97100
};
98101

99102
/**
@@ -129,6 +132,7 @@ const simpleConstraints = {
129132
tags: {
130133
required: true
131134
}
135+
// TODO: extra should be required here too
132136
};
133137

134138
const nospacePattern = /^\S*$/;
@@ -142,7 +146,8 @@ describe('Yup', () => {
142146
tags: yupArray().of(yupString().min(2)).min(3).required(),
143147
score: yupNumber().integer().min(0).required(),
144148
date: yupDate(),
145-
nospace: yupString().matches(nospacePattern)
149+
nospace: yupString().matches(nospacePattern),
150+
extra: yupString().nullable()
146151
});
147152

148153
schemaTest(yup(schema));
@@ -155,7 +160,8 @@ describe('Joi', () => {
155160
tags: Joi.array().items(Joi.string().min(2)).min(3).required(),
156161
score: Joi.number().integer().min(0).required(),
157162
date: Joi.date(),
158-
nospace: Joi.string().pattern(nospacePattern)
163+
nospace: Joi.string().pattern(nospacePattern),
164+
extra: Joi.string().allow(null)
159165
});
160166

161167
schemaTest(joi(schema));
@@ -168,7 +174,8 @@ describe('TypeBox', () => {
168174
tags: Type.Array(Type.String({ minLength: 2 }), { minItems: 3 }),
169175
score: Type.Integer({ minimum: 0 }),
170176
date: Type.Optional(Type.Date()),
171-
nospace: Type.Optional(Type.String({ pattern: '^\\S*$' }))
177+
nospace: Type.Optional(Type.String({ pattern: '^\\S*$' })),
178+
extra: Type.Union([Type.String(), Type.Null()])
172179
});
173180

174181
schemaTest(typebox(schema));
@@ -183,7 +190,8 @@ describe('Arktype', () => {
183190
tags: '(string>=2)[]>=3',
184191
score: 'integer>=0',
185192
'date?': 'Date',
186-
'nospace?': nospacePattern
193+
'nospace?': nospacePattern,
194+
extra: 'string|null'
187195
});
188196

189197
const adapter = arktype(schema, { defaults });
@@ -199,11 +207,13 @@ describe('Valibot', () => {
199207
tags: v.array(v.string([v.minLength(2)]), [v.minLength(3)]),
200208
score: v.number([v.integer(), v.minValue(0)]),
201209
date: v.optional(v.date()),
202-
nospace: v.optional(v.string([v.regex(nospacePattern)]))
210+
nospace: v.optional(v.string([v.regex(nospacePattern)])),
211+
extra: v.nullable(v.string())
203212
});
204213

205214
describe('Introspection', () => {
206215
schemaTest(valibot(schema));
216+
//console.dir(valibot(schema).jsonSchema, { depth: 10 });
207217
});
208218

209219
describe('Defaults', () => {
@@ -276,7 +286,8 @@ describe('Zod', () => {
276286
tags: z.string().min(2).array().min(3),
277287
score: z.number().int().min(0),
278288
date: z.date().optional(),
279-
nospace: z.string().regex(nospacePattern).optional()
289+
nospace: z.string().regex(nospacePattern).optional(),
290+
extra: z.string().nullable()
280291
})
281292
.refine((a) => a)
282293
.refine((a) => a)
@@ -418,7 +429,8 @@ describe('vine', () => {
418429
tags: Vine.array(Vine.string().minLength(2)).minLength(3),
419430
score: Vine.number().min(0),
420431
date: Vine.date().optional(),
421-
nospace: Vine.string().regex(nospacePattern).optional()
432+
nospace: Vine.string().regex(nospacePattern).optional(),
433+
extra: Vine.string().nullable()
422434
});
423435

424436
const adapter = vine(schema, { defaults });

0 commit comments

Comments
 (0)