Skip to content

Commit 719f04c

Browse files
committed
Move patternProperty to properties option and add some documentation
1 parent fd3862e commit 719f04c

File tree

4 files changed

+321
-17
lines changed

4 files changed

+321
-17
lines changed

Diff for: README.md

+283-4
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ s.object({
9393
s.property("email", s.string({ format: "email" })),
9494
s.property("phoneNumber", s.ref("phoneNumber")),
9595
s.property("billingAddress", s.oneOf(s.ref("ukAddress"), s.ref("usAddress"))),
96+
s.patternProperty("^[A-Za-z]$", s.string()),
9697
],
97-
patternProperties: [s.patternProperty("^[A-Za-z]$", s.string())],
9898
additionalProperties: s.array({
9999
items: s.number({ minimum: 0, maximum: 5000 }),
100100
}),
@@ -208,17 +208,296 @@ Or if you want to import a specific dialect:
208208
import { s } from "json-schema-fns/2020";
209209
```
210210

211-
### `object`
211+
All builder methods return a `SchemaBuilder`, and you can generate the JSON schema created by the builder using `toSchemaDocument` like so
212212

213-
### `array`
213+
```typescript
214+
s.object().toSchemaDocument();
215+
```
216+
217+
Which will result in the following document
218+
219+
```json
220+
{
221+
"$schema": "https://json-schema.org/draft/2020-12/schema",
222+
"type": "object"
223+
}
224+
```
225+
226+
If you don't want the `$schema` property, use `toSchema` instead:
227+
228+
```typescript
229+
s.object().toSchema(); // { "type": "object" }
230+
```
231+
232+
All builder methods also support the options in `AnnotationSchema`:
233+
234+
```typescript
235+
s.object({
236+
$id: "#/foobar",
237+
$comment: "This is a comment",
238+
default: {},
239+
title: "FooBar Object Schema",
240+
description: "This is the FooBar schema description",
241+
examples: [{ foo: "bar" }],
242+
deprecated: true,
243+
readOnly: true,
244+
writeOnly: false,
245+
}).toSchema();
246+
```
247+
248+
Produces the schema
249+
250+
```json
251+
{
252+
"type": "object",
253+
"$id": "#/foobar",
254+
"$comment": "This is a comment",
255+
"default": {},
256+
"title": "FooBar Object Schema",
257+
"description": "This is the FooBar schema description",
258+
"examples": [{ "foo": "bar" }],
259+
"deprecated": true,
260+
"readOnly": true,
261+
"writeOnly": false
262+
}
263+
```
264+
265+
### `s.object(options: ObjectOptions)`
266+
267+
Builds a schema of type `object`, accepting a single argument of type `ObjectOptions`
268+
269+
#### `ObjectOptions.properties`
270+
271+
An array of optional and required properties
272+
273+
```typescript
274+
s.object({
275+
properties: [
276+
s.property("name", s.string()),
277+
s.requiredProperty("email", s.string({ format: "email" })),
278+
],
279+
}).toSchema();
280+
```
281+
282+
Produces the schema
283+
284+
```json
285+
{
286+
"type": "object",
287+
"properties": {
288+
"name": { "type": "string" },
289+
"email": { "type": "string", "format": "email" }
290+
},
291+
"required": ["email"]
292+
}
293+
```
294+
295+
You can also add [patternProperties](https://json-schema.org/understanding-json-schema/reference/object.html#pattern-properties)
296+
297+
```typescript
298+
s.object({ properties: [s.patternProperty("^S_", s.string())] }).toSchema();
299+
```
300+
301+
which produces the schema
302+
303+
```json
304+
{
305+
"type": "object",
306+
"patternProperties": {
307+
"^S_": { "type": "string" }
308+
}
309+
}
310+
```
311+
312+
#### `ObjectOptions.additonalProperties`
313+
314+
Add an [additonalProperties](https://json-schema.org/understanding-json-schema/reference/object.html#additional-properties) schema:
315+
316+
```typescript
317+
s.object({ additionalProperties: s.number() }).toSchema();
318+
```
319+
320+
Produces the schema
321+
322+
```json
323+
{
324+
"type": "object",
325+
"additionalProperties": {
326+
"type": "number"
327+
}
328+
}
329+
```
330+
331+
#### `ObjectOptions.propertyNames`
332+
333+
Add a [propertyNames](https://json-schema.org/understanding-json-schema/reference/object.html#property-names) pattern:
334+
335+
```typescript
336+
s.object({ propertyNames: "^[A-Za-z_][A-Za-z0-9_]*$" }).toSchema();
337+
```
338+
339+
Produces the schema
340+
341+
```json
342+
{
343+
"type": "object",
344+
"propertyNames": {
345+
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
346+
}
347+
}
348+
```
349+
350+
#### `ObjectOptions.minProperties` and `ObjectOptions.maxProperties`
351+
352+
Validate the number of properties in an object using [min/maxProperties](https://json-schema.org/understanding-json-schema/reference/object.html#size)
353+
354+
```typescript
355+
s.object({ minProperties: 4, maxProperties: 10 }).toSchema();
356+
```
357+
358+
Produces the schema
359+
360+
```json
361+
{
362+
"type": "object",
363+
"minProperties": 4,
364+
"maxProperties": 10
365+
}
366+
```
367+
368+
#### `ObjectOptions.unevaluatedProperties`
369+
370+
Specify the handling of [unevaluatedProperties](https://json-schema.org/understanding-json-schema/reference/object.html#unevaluated-properties)
371+
372+
```typescript
373+
s.object({ unevaluatedProperties: false }).toSchema();
374+
```
375+
376+
Produces the schema
377+
378+
```json
379+
{
380+
"type": "object",
381+
"unevaluatedProperties": false
382+
}
383+
```
384+
385+
### `s.array(options: ArrayOptions)`
386+
387+
Builds a schema of type `array`, accepting a single argument of type `ArrayOptions`
388+
389+
#### `ArrayOptions.items`
390+
391+
Define the [items](https://json-schema.org/understanding-json-schema/reference/array.html#items) schema for an array:
392+
393+
```typescript
394+
s.array({ items: s.string() }).toSchema();
395+
```
396+
397+
Produces the schema
398+
399+
```json
400+
{
401+
"type": "array",
402+
"items": { "type": "string" }
403+
}
404+
```
405+
406+
#### `ArrayOptions.minItems` and `ArrayOptions.maxItems`
407+
408+
Define the array [length](https://json-schema.org/understanding-json-schema/reference/array.html#length)
409+
410+
```typescript
411+
s.array({ contains: { schema: s.number(), min: 1, max: 3 }).toSchema();
412+
```
413+
414+
Produces the schema
415+
416+
```json
417+
{
418+
"type": "array",
419+
"contains": { "type": "number" },
420+
"minContains": 1,
421+
"maxContains": 3
422+
}
423+
```
424+
425+
#### `ArrayOptions.prefixItems`
426+
427+
Allows you to perform [tuple validation](https://json-schema.org/understanding-json-schema/reference/array.html#tuple-validation):
428+
429+
```typescript
430+
s.array({ prefixItems: [s.string(), s.number()] }).toSchema();
431+
```
432+
433+
Produces the schema
434+
435+
```json
436+
{
437+
"type": "array",
438+
"prefixItems": [{ "type": "string" }, { "type": "number" }]
439+
}
440+
```
441+
442+
#### `ArrayOptions.unevaluatedItems`
443+
444+
Define the schema for [unevaluatedItems](https://json-schema.org/understanding-json-schema/reference/array.html#unevaluated-items)
445+
446+
```typescript
447+
s.array({ unevaluatedItems: s.object() }).toSchema();
448+
```
449+
450+
Produces the schema
451+
452+
```json
453+
{
454+
"type": "array",
455+
"unevaluatedItems": { "type": "object" }
456+
}
457+
```
458+
459+
#### `ArrayOptions.contains`
460+
461+
Define the schema [contains](https://json-schema.org/understanding-json-schema/reference/array.html#contains)
462+
463+
```typescript
464+
s.array({ contains: { schema: s.number(), min: 1, max: 3 }).toSchema();
465+
```
466+
467+
Produces the schema
468+
469+
```json
470+
{
471+
"type": "array",
472+
"contains": { "type": "number" },
473+
"minContains": 1,
474+
"maxContains": 3
475+
}
476+
```
214477
215478
### `string`
216479
217480
### `integer` and `number`
218481
482+
### `boolean`
483+
219484
### `nil`
220485
221-
### `boolean`
486+
### `nullable`
487+
488+
### `anyOf` | `allOf` | `oneOf`
489+
490+
### `ifThenElse` and `ifThen`
491+
492+
### `not`
493+
494+
### `def` and `ref`
495+
496+
### `$const`
497+
498+
### `$enumerator`
499+
500+
### `$true` and `$false`
222501
223502
## Roadmap
224503

Diff for: src/2020.ts

+22-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
22
import deepmerge from "deepmerge";
3+
import { access } from "fs";
34
import omit from "lodash/omit";
45

56
type TypeName = "string" | "number" | "integer" | "boolean" | "object" | "array" | "null";
@@ -198,7 +199,6 @@ const objectBuilder = (schema: Partial<ObjectSchema>): SchemaBuilder<ObjectSchem
198199
type ObjectOptions = {
199200
properties?: Array<SchemaBuilder<ObjectSchema>>;
200201
propertyNames?: string;
201-
patternProperties?: Array<SchemaBuilder<ObjectSchema>>;
202202
additionalProperties?: SchemaBuilder<Schema>;
203203
minProperties?: number;
204204
maxProperties?: number;
@@ -208,11 +208,9 @@ type ObjectOptions = {
208208

209209
function object(options?: ObjectOptions): SchemaBuilder<ObjectSchema> {
210210
const properties = options?.properties || [];
211-
const patternProperties = options?.patternProperties || [];
212211

213212
const additionalOptions = omit(options, [
214213
"properties",
215-
"patternProperties",
216214
"propertyNames",
217215
"additionalProperties",
218216
"defs",
@@ -227,10 +225,6 @@ function object(options?: ObjectOptions): SchemaBuilder<ObjectSchema> {
227225
schema.apply(property);
228226
}
229227

230-
for (const property of patternProperties) {
231-
schema.apply(property);
232-
}
233-
234228
if (options?.propertyNames) {
235229
schema.apply(
236230
objectBuilder({
@@ -490,6 +484,26 @@ function not(schema: SchemaBuilder<Schema>): SchemaBuilder<Schema> {
490484
});
491485
}
492486

487+
function concat(...schemas: SchemaBuilder<Schema>[]): SchemaBuilder<Schema> {
488+
return new SchemaBuilder(
489+
schemas.reduce((acc, s) => {
490+
const schema = s.toSchema();
491+
492+
if (typeof acc === "boolean") {
493+
if (typeof schema === "boolean") {
494+
return acc || schema;
495+
} else {
496+
return schema;
497+
}
498+
} else if (typeof schema === "boolean") {
499+
return acc;
500+
}
501+
502+
return { ...acc, ...schema };
503+
}, {} as Schema),
504+
);
505+
}
506+
493507
function ifThenElse(
494508
condition: SchemaBuilder<Schema>,
495509
then: SchemaBuilder<Schema>,
@@ -563,6 +577,7 @@ export const s = {
563577
allOf,
564578
oneOf,
565579
not,
580+
concat,
566581
ifThenElse,
567582
ifThen,
568583
def,

0 commit comments

Comments
 (0)