1 | import { TJS, fs, fsPath, value as valueUtil, AJV } from '../common';
|
2 | import { SchemaDefinition } from './SchemaDefinition';
|
3 | const defaultValue = valueUtil.defaultValue;
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | export type SchemaOptions = TJS.PartialArgs &
|
15 | TJS.CompilerOptions & {
|
16 | files: string | string[];
|
17 | basePath?: string;
|
18 | };
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | export class Schema {
|
24 | public static compile(args: SchemaOptions) {
|
25 | return new Schema(args);
|
26 | }
|
27 |
|
28 | public readonly basePath: string | undefined;
|
29 | public readonly files: string[];
|
30 | private _program: TJS.Program;
|
31 | private _generator: TJS.JsonSchemaGenerator;
|
32 |
|
33 | private constructor(args: SchemaOptions) {
|
34 |
|
35 | const required = defaultValue(args.required, true);
|
36 | const strictNullChecks = defaultValue(args.strictNullChecks, true);
|
37 | const ignoreErrors = defaultValue(args.ignoreErrors, true);
|
38 | args = { ...args, required, strictNullChecks, ignoreErrors };
|
39 |
|
40 |
|
41 | this.basePath = args.basePath ? fsPath.resolve(args.basePath) : undefined;
|
42 | const toPath = (path: string) => {
|
43 | path = (path || '').trim();
|
44 | if (path) {
|
45 | path = this.basePath ? fsPath.join(this.basePath, path) : path;
|
46 | path = fsPath.resolve(path);
|
47 | }
|
48 | return path;
|
49 | };
|
50 | this.files = Array.isArray(args.files) ? args.files : [args.files];
|
51 | this.files = this.files.map(path => toPath(path));
|
52 | this.files = this.files.filter(path => Boolean(path.trim()));
|
53 |
|
54 |
|
55 | if (this.files.length === 0) {
|
56 | throw new Error(`No files were given to the schema.`);
|
57 | }
|
58 | this.files.forEach(path => {
|
59 | if (!fs.pathExistsSync(path)) {
|
60 | throw new Error(
|
61 | `Failed to load schema. The file '${path}' does not exist.`,
|
62 | );
|
63 | }
|
64 | });
|
65 |
|
66 |
|
67 | this._program = TJS.getProgramFromFiles(this.files, args, this.basePath);
|
68 | const generator = TJS.buildGenerator(this._program, args);
|
69 | if (!generator) {
|
70 | throw new Error(`Failed to create a build-generator for the schema.`);
|
71 | }
|
72 | this._generator = generator;
|
73 | }
|
74 |
|
75 | |
76 |
|
77 |
|
78 | public forType(
|
79 | type: string | string[],
|
80 | options: { includeReffedDefinitions?: boolean } = {},
|
81 | ) {
|
82 | try {
|
83 | const g = this._generator;
|
84 | const def = Array.isArray(type)
|
85 | ? g.getSchemaForSymbols(type, options.includeReffedDefinitions)
|
86 | : g.getSchemaForSymbol(type, options.includeReffedDefinitions);
|
87 | return new SchemaDefinition({ type, def });
|
88 | } catch (error) {
|
89 | throw new Error(
|
90 | `Failed to generate schema for symbol '${type}'. ${error.message}`,
|
91 | );
|
92 | }
|
93 | }
|
94 |
|
95 | |
96 |
|
97 |
|
98 | public static async validator(schema: object | string) {
|
99 | const ajv = AJV();
|
100 | const def =
|
101 | typeof schema === 'string'
|
102 | ? await fs.readJson(fsPath.resolve(schema))
|
103 | : schema;
|
104 | const func = ajv.compile(def);
|
105 | return (data?: object) => func(data);
|
106 | }
|
107 |
|
108 | |
109 |
|
110 |
|
111 |
|
112 | public static async validate(schema: object, data?: object) {
|
113 | return (await Schema.validator(schema))(data);
|
114 | }
|
115 | }
|