Skip to content

Commit 06c2388

Browse files
fix: Implement precompiled validator support in @rjsf/validator-ajv8
Fixes rjsf-team#3543 by implementing support for precompiled validators - In `@rjsf/validator-ajv8` added support for precompiled validators as follows: - Added a new `compileSchemaValidators()` API function used to generate the precompiled validators for a schema to an output file - Updated the documentation for the `customizeValidator()` API function - Added a new `AJV8PrecompiledValidator` implementation of the `ValidatorType` interface - Refactored a large piece of the raw validation error processing from the `AJV8Validator` into a new `processRawValidationErrors()` function also used by the `AJV8PrecompiledValidator` - Added a new `usePrecompiledValidator()` API function that is similar to `customizeValidator()` but returning a precompiled validator-based `ValidatorType` interface implementation - Added some new types to the `types.ts` file in support of precompiled validators - Updated the main `index.ts` file to export the new types and API functions - Added 100% unit test coverage of the new feature - This included implementing a node function to precompile the `superSchema.json` file found in the `test/harness` directory - Updated the `validation.md` documentation for the new precompiled validator functionality - Added a new `validator-ajv8.md` documentation file to the `api-reference` directory and the `sidebar.js` - Updated the `CHANGELOG.md` file accordingly
1 parent c09cc1a commit 06c2388

26 files changed

+2686
-173
lines changed

CHANGELOG.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ should change the heading of the (upcoming) version to include a major version b
2525
## @rjsf/core
2626

2727
- Updated the `MultiSchemaField` to use the new `getDiscriminatorFieldFromSchema()` API
28-
2928
- Added new `experimental_defaultFormStateBehavior` prop to `Form` to specify alternate behavior when dealing with the rendering of array fields where `minItems` is set but field is not `required` (fixes [3363](https://github.com/rjsf-team/react-jsonschema-form/issues/3363)) ([3604](https://github.com/rjsf-team/react-jsonschema-form/pull/3604))
3029

3130
## @rjsf/fluent-ui
@@ -38,12 +37,18 @@ should change the heading of the (upcoming) version to include a major version b
3837
- Updated `getDefaultFormState()` and `toPathSchema()` to use `getDiscriminatorFieldFromSchema()` to provide a discriminator field to `getClosestMatchingOption()` calls.
3938
- Refactored the `retrieveSchema()` internal API functions to support implementing an internal `schemaParser()` API for use in precompiling schemas, in support of [#3543](https://github.com/rjsf-team/react-jsonschema-form/issues/3543)
4039
- Fixed `toPathSchema()` to handle `properties` in an object along with `anyOf`/`oneOf`, fixing [#3628](https://github.com/rjsf-team/react-jsonschema-form/issues/3628) and [#1628](https://github.com/rjsf-team/react-jsonschema-form/issues/1628)
41-
- Refactored optional parameters for `computeDefaults()` into destructured props object to reduce clutter when only specifying later of the optional argument ([3604](https://github.com/rjsf-team/react-jsonschema-form/pull/3604))
40+
- Refactored optional parameters for `computeDefaults()` into destructured props object to reduce clutter when only specifying later of the optional argument, fixing [#3602](https://github.com/rjsf-team/react-jsonschema-form/issues/3602)
41+
42+
## @rjsf/validator-ajv8
43+
44+
- Added two new APIs `compileSchemaValidators()` and `usePrecompiledValidator()` implemented to support using precompiled validators build with AJV 8, fixing [#3543](https://github.com/rjsf-team/react-jsonschema-form/issues/3543)
4245

4346
## Dev / docs / playground
4447

4548
- Added documentation to `custom-templates` describing how to extend the `BaseInputTemplate`
46-
- Added **minItems behavior for array field** live setting ([3604](https://github.com/rjsf-team/react-jsonschema-form/pull/3604))
49+
- Added **minItems behavior for array field** live setting, fixing [#3602](https://github.com/rjsf-team/react-jsonschema-form/issues/3602)
50+
- Added documentation to `validation` describing the new precompiled validators feature
51+
- Added new `validator-ajv8.md` documentation to the `api-reference` directory as well as putting it into the `sidebar.js`
4752

4853
# 5.6.2
4954

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030
- [Ant Design](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/antd)
3131
- [Bootstrap 3](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/core)
3232
- [Bootstrap 4](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/bootstrap-4)
33+
- [Chakra UI](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/chakra-ui)
3334
- [Fluent UI](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/fluent-ui)
3435
- [Material UI 4](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/material-ui)
3536
- [Material UI 5](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/mui)
3637
- [Semantic UI](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/semantic-ui)
37-
- [Chakra UI](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/chakra-ui)
3838

3939
## Documentation
4040

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# @rjsf/validator-ajv8 APIs
2+
3+
In RJSF version 5, the original, embedded AJV 6 validator implementation from earlier versions was extracted into its own package, `@rjsf/validator-ajv6`, which was immediately deprecated since AJV 6 is no longer getting maintenance updates.
4+
A new `@rjsf/validator-ajv8` package was added that uses the AJV 8 package, including adding support for using precompiled validators.
5+
Below are the exported API functions that are provided by this package.
6+
See the [Validation documentation]('../usage/validation.md) for examples of using these APIs.
7+
8+
## Types
9+
10+
There are a few Typescript types that are exported by `@rjsf/validator-ajv8` in support of the APIs.
11+
12+
These types can be found on GitHub [here](https://github.com/rjsf-team/react-jsonschema-form/blob/main/packages/validator-ajv8/src/types.ts).
13+
14+
## APIs
15+
16+
### customizeValidator<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
17+
18+
Creates and returns a customized implementation of the `ValidatorType` with the given customization `options` if provided.
19+
If a `localizer` is provided, it is used to translate the messages generated by the underlying AJV validation.
20+
21+
#### Parameters
22+
23+
- [options={}]: CustomValidatorOptionsType - The optional map of `CustomValidatorOptionsType` options that are used to create the `ValidatorType` instance
24+
- [localizer]: Localizer | undefined - If provided, is used to localize a list of Ajv `ErrorObject`s after running the form validation using AJV
25+
26+
#### Returns
27+
28+
- ValidatorType<T, S, F>: The custom validator implementation resulting from the set of parameters provided
29+
30+
### compileSchemaValidators&lt;S extends StrictRJSFSchema = RJSFSchema>()
31+
32+
The function used to compile a schema into an output file in the form that allows it to be used as a precompiled validator.
33+
The main reasons for using a precompiled validator is reducing code size, improving validation speed and, most importantly, avoiding dynamic code compilation when prohibited by a browser's Content Security Policy.
34+
For more information about AJV code compilation see: https://ajv.js.org/standalone.html
35+
36+
#### Parameters
37+
38+
- schema: S - The schema to be compiled into a set of precompiled validators functions
39+
- output: string - The name of the file into which the precompiled validator functions will be generated
40+
- [options={}]: CustomValidatorOptionsType - The set of `CustomValidatorOptionsType` information used to alter the AJV validator used for compiling the schema. They are the same options that are passed to the `customizeValidator()` function in order to modify the behavior of the regular AJV-based validator.
41+
42+
### usePrecompiledValidator<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
43+
44+
Creates and returns a `ValidatorType` interface that is implemented with a precompiled validator.
45+
If a `localizer` is provided, it is used to translate the messages generated by the underlying AJV validation.
46+
47+
> NOTE: The `validateFns` parameter is an object obtained by importing from a precompiled validation file created via the `compileSchemaValidators()` function.
48+
49+
#### Parameters
50+
51+
- validateFns: ValidatorFunctions<T> - The map of the validation functions that are created by the `compileSchemaValidators()` function
52+
- rootSchema: S - The root schema that was used with the `compileSchemaValidators()` function
53+
- [localizer]: Localizer | undefined - If provided, is used to localize a list of Ajv `ErrorObject`s after running the form validation using AJV
54+
55+
#### Returns
56+
57+
- ValidatorType<T, S, F>: The precompiled validator implementation resulting from the set of parameters provided

packages/docs/docs/usage/validation.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,82 @@ If you depend on having specifically formatted messages, then using this validat
1313

1414
It is also possible for you to provide your own implementation if you desire, as long as it fulfills the `ValidatorType` interface specified in `@rjsf/utils`.
1515

16+
## API documentation
17+
18+
The documentation for the APIs associated with the AJV 8 validator package can be found [here]('../api-reference/validator-ajv8.md')
19+
20+
## Precompiled validators
21+
22+
In 5.7.0, support for precompiled validators was added to the `@rjsf/validator-ajv8` package.
23+
The main use case for this is to overcome issues with `unsafe-eval` warnings from the browser caused by strict Content Security Policy settings.
24+
See the [Standalone Validation Code](https://ajv.js.org/standalone.html) section of the AJV documentation for more details about precompiled validators.
25+
26+
Due to how RJSF uses the AJV validator in determining `anyOf/oneOf` selections and how it resolves dependencies, if-then-else and references ($ref) in schemas via the `retrieveSchema()` utility method, RJSF provides its own schema compilation API built on-top-of the one provided by AJV 8.
27+
If you are wanting to use a precompiled validator, two steps are required:
28+
29+
1. Precompiling the schema into a set of validator functions
30+
2. Providing those precompiled validator functions to a `ValidatorType` implementation on the `Form`
31+
32+
### Schema precompilation
33+
34+
The first step in the process is to compile a schema into a set of validator functions that are saved into a commonJS-based Javascript file.
35+
The `@rjsf/validator-ajv8` package exports the `compileSchemaValidators()` function that does this.
36+
It is expected that this function will be used in a manner similar to the following:
37+
38+
```ts
39+
const { compileSchemaValidators } = require('@rjsf/validator-ajv8');
40+
const yourSchema = require('path_to/yourSchema'); // If your schema is a js file
41+
42+
compileSchemaValidators(yourSchema, 'path_to/yourCompiledSchema.js');
43+
```
44+
45+
If you are currently using the `customizeValidator()` function to provide `additionalMetaSchemas`, `customFormats`, `ajvOptionsOverrides` and/or `ajvFormatOptions` then you can pass those in as the optional third parameter to the `compileSchemaValidators()` function in a manner similar to:
46+
47+
```ts
48+
const { compileSchemaValidators } = require('@rjsf/validator-ajv8');
49+
const yourSchema = require('path_to/yourSchema.json'); // If your schema is a json file
50+
51+
const options = {
52+
additionalMetaSchemas: [require('ajv/lib/refs/json-schema-draft-06.json')],
53+
customFormats: { 'phone-us': /\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{4}$/, 'area-code': /\d{3}/ },
54+
ajvOptionsOverrides: {
55+
$data: true,
56+
verbose: true,
57+
},
58+
ajvFormatOptions: {
59+
mode: 'fast',
60+
},
61+
};
62+
63+
compileSchemaValidators(yourSchema, 'path_to/yourCompiledSchema.js', options);
64+
```
65+
66+
It is highly recommended to create a `compileYourSchema.js` file (or what ever name you want) with code similar to what is shown above and then, using node, run the code as follows:
67+
68+
```
69+
node compileYourSchema.js
70+
```
71+
72+
> NOTE: You must have your schema provided within a file that can be parsed and turned into the set of precompiled validator functions.
73+
74+
### Using the precompiled validator
75+
76+
After you have completed step 1 and have generated your precompiled schema functions into the `yourCompiledSchema.js` output file (or whatever you called it), then you need to create a `ValidatorType` implementation from it to use in the `Form`.
77+
The `@rjsf/validator-ajv8` package exports the `usePrecompiledValidator()` function for this.
78+
Here is an example of how to use your precompiled validator with your `Form`:
79+
80+
```tsx
81+
import { usePrecompiledValidator, ValidatorFunctions } from '@rjsf/validator-ajv8';
82+
import Form from '@rjsf/core'; // Or whatever theme you use
83+
84+
import yourSchema from 'path_to/yourSchema'; // This needs to be the same file that was precompiled
85+
import * as precompiledValidator from 'path_to/yourCompiledSchema';
86+
87+
const validator = usePrecompiledValidator(precompiledValidator as ValidatorFunctions);
88+
89+
render(<Form schema={schema} validator={validator} />, document.getElementById('app'));
90+
```
91+
1692
## Live validation
1793

1894
By default, form data are only validated when the form is submitted or when a new `formData` prop is passed to the `Form` component.

packages/docs/sidebars.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ const sidebars = {
7979
'api-reference/themes/chakra-ui/uiSchema',
8080
'api-reference/form-props',
8181
'api-reference/utility-functions',
82+
'api-reference/validator-ajv8'
8283
],
8384
},
8485
{
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import fs from 'fs';
2+
import standaloneCode from 'ajv/dist/standalone';
3+
import { RJSFSchema, StrictRJSFSchema, schemaParser } from '@rjsf/utils';
4+
5+
import createAjvInstance from './createAjvInstance';
6+
import { CustomValidatorOptionsType } from './types';
7+
8+
/** The function used to compile a schema into an output file in the form that allows it to be used as a precompiled
9+
* validator. The main reasons for using a precompiled validator is reducing code size, improving validation speed and,
10+
* most importantly, avoiding dynamic code compilation when prohibited by a browser's Content Security Policy. For more
11+
* information about AJV code compilation see: https://ajv.js.org/standalone.html
12+
*
13+
* @param schema - The schema to be compiled into a set of precompiled validators functions
14+
* @param output - The name of the file into which the precompiled validator functions will be generated
15+
* @param [options={}] - The set of `CustomValidatorOptionsType` information used to alter the AJV validator used for
16+
* compiling the schema. They are the same options that are passed to the `customizeValidator()` function in
17+
* order to modify the behavior of the regular AJV-based validator.
18+
*/
19+
export default function compileSchemaValidators<S extends StrictRJSFSchema = RJSFSchema>(
20+
schema: S,
21+
output: string,
22+
options: CustomValidatorOptionsType = {}
23+
) {
24+
console.log('parsing the schema');
25+
const schemaMaps = schemaParser(schema);
26+
const schemas = Object.values(schemaMaps);
27+
28+
const { additionalMetaSchemas, customFormats, ajvOptionsOverrides = {}, ajvFormatOptions, AjvClass } = options;
29+
const compileOptions = { ...ajvOptionsOverrides, code: { source: true, lines: true }, schemas };
30+
const ajv = createAjvInstance(additionalMetaSchemas, customFormats, compileOptions, ajvFormatOptions, AjvClass);
31+
32+
const moduleCode = standaloneCode(ajv);
33+
console.log(`writing ${output}`);
34+
fs.writeFileSync(output, moduleCode);
35+
}

packages/validator-ajv8/src/customizeValidator.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import { CustomValidatorOptionsType, Localizer } from './types';
44
import AJV8Validator from './validator';
55

66
/** Creates and returns a customized implementation of the `ValidatorType` with the given customization `options` if
7-
* provided.
7+
* provided. If a `localizer` is provided, it is used to translate the messages generated by the underlying AJV
8+
* validation.
89
*
910
* @param [options={}] - The `CustomValidatorOptionsType` options that are used to create the `ValidatorType` instance
1011
* @param [localizer] - If provided, is used to localize a list of Ajv `ErrorObject`s
12+
* @returns - The custom validator implementation resulting from the set of parameters provided
1113
*/
1214
export default function customizeValidator<
1315
T = any,

packages/validator-ajv8/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import customizeValidator from './customizeValidator';
2+
import compileSchemaValidators from './compileSchemaValidators';
3+
import usePrecompiledValidator from './usePrecompiledValidator';
24

3-
export { customizeValidator };
5+
export { customizeValidator, compileSchemaValidators, usePrecompiledValidator };
46
export * from './types';
57

68
export default customizeValidator();

0 commit comments

Comments
 (0)