Skip to content
Playground

exodus/schemasafe

Form validator implementation based on @exodus/schemasafe a code-generating JSON Schema validator.

Terminal window
npm i @sjsf/schemasafe-validator@next ajv@8
<script lang="ts">
import { ON_INPUT, BasicForm } from "@sjsf/form";
import { createFormValidator } from "@sjsf/schemasafe-validator";
import { createMyForm } from "@/components/my-form";
import { initialValue, schema, uiSchema } from "../shared";
const validator = createFormValidator({
uiSchema,
});
const form = createMyForm({
schema,
uiSchema,
validator,
fieldsValidationMode: ON_INPUT,
initialValue,
});
</script>
<BasicForm {form} novalidate />
<pre>{JSON.stringify(form.value, null, 2)}</pre>

It is possible to use precompiled validator.

The first step in the process is to compile a schema into a set of validate functions.

import fs from "node:fs";
import path from "node:path";
import { validator } from '@exodus/schemasafe'
import { ON_INPUT } from '@sjsf/form';
import {
insertSubSchemaIds,
fragmentSchema,
} from "@sjsf/form/validators/precompile";
import { DEFAULT_VALIDATOR_OPTIONS, FORM_FORMATS } from '@sjsf/schemasafe-validator'
import inputSchema from './input-schema.json' with { type: "json" }
const fieldsValidationMode = ON_INPUT
// NOTE: After calling this function, be sure to save the `schema` and
// use it to generate the form
const patch = insertSubSchemaIds(inputSchema as any, { fieldsValidationMode });
// It is easier to save as a TS file
// https://github.com/microsoft/TypeScript/issues/32063
fs.writeFileSync(
path.join(import.meta.dirname, "patched-schema.ts"),
`import type { Schema } from "@sjsf/form";
export const fieldsValidationMode = ${fieldsValidationMode}
export const schema = ${JSON.stringify(patch.schema, null, 2)} as const satisfies Schema;`
);
const schemas = fragmentSchema(patch)
// @ts-expect-error No typing for `multi` version of function
const validate = validator(schemas, {
...DEFAULT_VALIDATOR_OPTIONS,
formats: {
...FORM_FORMATS,
"phone-us": /\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{4}$/,
"area-code": /\d{3}/,
},
schemas: new Map(schemas.map(schema => [schema.$id, schema])),
multi: true,
})
const validateFunctions = `export const [${schemas.map(s => s.$id).join(', ')}] = ${validate.toModule()}`
fs.writeFileSync(path.join(import.meta.dirname, "validate-functions.js"), validateFunctions)
Terminal window
node --experimental-strip-types compile-schema-script.ts
<script lang="ts">
import { BasicForm } from "@sjsf/form";
import { createFormValidator } from "@sjsf/schemasafe-validator/precompile";
import { createMyForm } from "@/components/my-form";
import { schema, fieldsValidationMode } from "./patched-schema";
import * as validateFunctions from "./validate-functions";
const validator = createFormValidator({ validateFunctions });
const form = createMyForm({
schema,
validator,
fieldsValidationMode,
});
</script>
<BasicForm {form} novalidate />
<pre>{JSON.stringify(form.value, null, 2)}</pre>