UNPKG

create-validator-ts

Version:

Create JSON Schema validator from TypeScript.

310 lines (242 loc) 9.37 kB
# create-validator-ts Create JSON Schema validator from TypeScript. ## Motivation - Generate JSON Schema Validator functions from TypeScript code - Make TypeScript the [Single source of truth](https://en.wikipedia.org/wiki/Single_source_of_truth) ### Structure ``` . └── src/ ├── hello/ │ ├── api-types.ts │ ├── api-types.validator.ts <- Generated │ └── index.ts └── status/ ├── api-types.ts ├── api-types.validator.ts <- Generated └── index.ts ``` ## Install Install with [npm](https://www.npmjs.com/): npm install create-validator-ts ## Usage Usage $ create-validator-ts [file|glob*] Options --watch [Boolean] If set the flag, start watch mode --check [Boolean] If set the flag, start test mode --cwd [Path:String] current working directory --tsconfigFilePath [Path:String] path to tsconfig.json --generatorScript [Path:String] A JavaScript file path that customize validator code generator --verbose [Boolean] If set the flag, show progressing logs ts-json-schema-generator options --sortProps [Boolean] Enable sortProps --no-sortProps --strictTuples [Boolean] Enable strictTuples --no-strictTuples --encodeRefs [Boolean] Enable encodeRefs --no-encodeRefs --skipTypeCheck [Boolean] Enable skipTypeCheck. true by default --no-skipTypeCheck --additionalProperties [Boolean] Enable additionalProperties. false by default --no-additionalProperties Examples $ create-validator-ts "src/**/api-types.ts" # use cache $ create-validator-ts --cache "src/**/api-types.ts" # custom tsconfig.json $ create-validator-ts "src/**/api-types.ts" --tsconfigFilePath ./tsconfig.app.json # custom validator code $ create-validator-ts "src/**/api-types.ts" --generatorScript ./custom.js ## Example You can generate validator code via following command $ create-validator-ts "src/**/api-types.ts" Default validator require [ajv](https://github.com/ajv-validator/ajv), and you need to install ajv into your project. $ npm install ajv Structure: ``` . ├ tsconfig.json └── src/ └─── api/ ├── api-types.ts └─── api-types.validator.ts <- Generated ``` `api-types.ts`: ```ts // Example api-types // GET /api export type GetAPIRequestQuery = { id: string; }; export type GetAPIResponseBody = { ok: boolean; }; ``` `api-types.validator.ts` (generated): ```ts // @ts-nocheck // eslint-disable // This file is generated by create-validator-ts import Ajv from 'ajv'; import * as apiTypes from './api-types'; const SCHEMA = { "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { "GetAPIRequestQuery": { "type": "object", "properties": { "id": { "type": "string" } }, "required": [ "id" ], "additionalProperties": false }, "GetAPIResponseBody": { "type": "object", "properties": { "ok": { "type": "boolean" } }, "required": [ "ok" ], "additionalProperties": false } } }; const ajv = new Ajv({ removeAdditional: true }).addSchema(SCHEMA, "SCHEMA"); export function validateGetAPIRequestQuery(payload: unknown): apiTypes.GetAPIRequestQuery { if (!isGetAPIRequestQuery(payload)) { const error = new Error('invalid payload: GetAPIRequestQuery'); error.name = "ValidatorError"; throw error; } return payload; } export function isGetAPIRequestQuery(payload: unknown): payload is apiTypes.GetAPIRequestQuery { /** Schema is defined in {@link SCHEMA.definitions.GetAPIRequestQuery } **/ const ajvValidate = ajv.compile({ "$ref": "SCHEMA#/definitions/GetAPIRequestQuery" }); return ajvValidate(payload); } export function validateGetAPIResponseBody(payload: unknown): apiTypes.GetAPIResponseBody { if (!isGetAPIResponseBody(payload)) { const error = new Error('invalid payload: GetAPIResponseBody'); error.name = "ValidatorError"; throw error; } return payload; } export function isGetAPIResponseBody(payload: unknown): payload is apiTypes.GetAPIResponseBody { /** Schema is defined in {@link SCHEMA.definitions.GetAPIResponseBody } **/ const ajvValidate = ajv.compile({ "$ref": "SCHEMA#/definitions/GetAPIResponseBody" }); return ajvValidate(payload); } ``` ## Check generated code When You can check if your generated codes are match with `api-types.ts`, you can run it via `--check` flag. $ create-validator-ts "src/**/api-types.ts" # $? → 0 or 1 It is useful for testing on CI. ## Custom Validator You can create custom validator using `--generatorScript` flag. $ create-validator-ts "src/**/api-types.ts" --generatorScript ./custom.js `custom.js` ```js "use strict"; const path = require("path"); const generator = ({ apiFilePath, apiFileCode, schema }) => { const apiFileName = path.basename(apiFilePath, ".ts"); const isExportedTypeInApiTypes = (apiName) => { return (apiFileCode.includes(`export type ${apiName} =`) || apiFileCode.includes(`export interface ${apiName} {`)); }; const banner = `// @ts-nocheck // eslint-disable // This file is generated by create-validator-ts import Ajv from 'ajv'; import logger from 'logger'; import * as apiTypes from './${apiFileName}'; `; // define SCHEMA to top, and we can refer it as "SCHEMA". // Note: { "$ref": "SCHEMA#/definitions/${apiName}" } const schemaDefinition = `const SCHEMA = ${JSON.stringify(schema, null, 4)}; const ajv = new Ajv({ removeAdditional: true }).addSchema(SCHEMA, "SCHEMA");`; const code = Object.entries(schema.definitions || {}) .filter(([apiName]) => { return isExportedTypeInApiTypes(apiName); }) .map(([apiName, _schema]) => { return `export function validate${apiName}(payload: unknown): apiTypes.${apiName} { if (!is${apiName}(payload)) { const error = new Error('invalid payload: ${apiName}'); error.name = "ValidatorError"; throw error; } return payload; } export function is${apiName}(payload: unknown): payload is apiTypes.${apiName} { /** Schema is defined in {@link SCHEMA.definitions.${apiName} } **/ const ajvValidate = ajv.compile({ "$ref": "SCHEMA#/definitions/${apiName}" }); return ajvValidate(payload); }`; }) .join("\n\n"); return `${banner} ${schemaDefinition} ${code} `; }; exports.generator = generator; exports.generatorOptions = { extraTags: ["<ajv-plugin-extra-tag>"] // optional }; ``` ## Ignore generated Code If you have used [Prettier](https://prettier.io/), you should add `*.validator.ts` to `.prettierignore` ``` *.validator.ts ``` - [Ignoring Code · Prettier](https://prettier.io/docs/en/ignore.html) ## Compares - create-validator-ts - It generate TypeScript validator from TypeScript types - [ts-to-zod](https://github.com/fabien0102/ts-to-zod) uses same approach - OpenAPI - It generate TypeScript validator from Schema file - TypeScript Validation library like [Zod](https://github.com/colinhacks/zod) - Is allow to share TypeScript code and types, but it need to use builder function ## FAQ ### Can not parse new TypeScript syntax It related to [vega/ts-json-schema-generator](https://github.com/vega/ts-json-schema-generator). Please report the issue to [vega/ts-json-schema-generator](https://github.com/vega/ts-json-schema-generator). ## Changelog See [Releases page](https://github.com/azu/create-validator-ts/releases). ## Running tests Install devDependencies and Run `npm test`: npm test ## Contributing Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/azu/create-validator-ts/issues). 1. Fork it! 2. Create your feature branch: `git checkout -b my-new-feature` 3. Commit your changes: `git commit -am 'Add some feature'` 4. Push to the branch: `git push origin my-new-feature` 5. Submit a pull request :D ## Author - azu: [GitHub](https://github.com/azu), [Twitter](https://twitter.com/azu_re) ## License MIT © azu ## Prior art - [ForbesLindesay/typescript-json-validator](https://github.com/ForbesLindesay/typescript-json-validator) Differences: - `create-validator-ts` support multiple types in a single file - `create-validator-ts` support more TypeScript that includes Utility types like `Pick<T>` - `typescript-json-validator` uses [YousefED/typescript-json-schema](https://github.com/YousefED/typescript-json-schema) - `create-validator-ts` uses [vega/ts-json-schema-generator](https://github.com/vega/ts-json-schema-generator) - See also [Future of schema generators · Issue #101 · vega/ts-json-schema-generator](https://github.com/vega/ts-json-schema-generator/issues/101) - `create-validator-ts` is minimal and support customization of validation code using `--generatorScript`