Skip to content

feat: add custom keywords #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 42 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,60 +51,63 @@ Note that types will be validated automatically, but you can also use annotation
Usage: typescript-json-schema <path-to-typescript-file> <type>

Options:
--help Show help [boolean]
--version Show version number [boolean]
--refs Create shared ref definitions. [boolean] [default: true]
--aliasRefs Create shared ref definitions for the type aliases.
--help Show help [boolean]
--version Show version number [boolean]
--refs Create shared ref definitions.[boolean] [default: true]
--aliasRefs Create shared ref definitions for the type aliases.
[boolean] [default: false]
--topRef Create a top-level ref definition.
--topRef Create a top-level ref definition.
[boolean] [default: false]
--titles Creates titles in the output schema.
--titles Creates titles in the output schema.
[boolean] [default: false]
--defaultProps Create default properties definitions.
--defaultProps Create default properties definitions.
[boolean] [default: true]
--noExtraProps Disable additional properties in objects by default.
--noExtraProps Disable additional properties in objects by default.
[boolean] [default: false]
--propOrder Create property order definitions.
--propOrder Create property order definitions.
[boolean] [default: false]
--typeOfKeyword Use typeOf keyword (https://goo.gl/DC6sni) for
functions. [boolean] [default: false]
--required Create required array for non-optional properties.
--typeOfKeyword Use typeOf keyword (https://goo.gl/DC6sni) for
functions. [boolean] [default: false]
--required Create required array for non-optional properties.
[boolean] [default: true]
--strictNullChecks Make values non-nullable by default.
--strictNullChecks Make values non-nullable by default.
[boolean] [default: true]
--ignoreErrors Generate even if the program has errors.
--ignoreErrors Generate even if the program has errors.
[boolean] [default: false]
--validationKeywords Provide additional validation keywords to include.
--validationKeywords Provide additional validation keywords to include.
[array] [default: []]
--excludePrivate Exclude private members from the schema.
--excludePrivate Exclude private members from the schema.
[boolean] [default: false]
--uniqueNames Use unique names for type symbols.
--uniqueNames Use unique names for type symbols.
[boolean] [default: false]
--include Further limit tsconfig to include only matching files.
--include Further limit tsconfig to include only matching files.
[array]
--rejectDateType Rejects Date fields in type definitions.
--rejectDateType Rejects Date fields in type definitions.
[boolean] [default: false]
--id ID of schema. [string] [default: ""]
--uniqueItems Validate `uniqueItems` keyword [boolean] [default: true]
--unicode calculate correct length of strings with unicode pairs
(true by default). Pass false to use .length of strings
that is faster, but gives "incorrect" lengths of strings
with unicode pairs - each unicode pair is counted as two
characters. [boolean] [default: true]
--nullable support keyword "nullable" from Open API 3
specification. [boolean] [default: true]
--format formats validation mode ('fast' by default). Pass 'full'
for more correct and slow validation or false not to
validate formats at all. E.g., 25:00:00 and 2015/14/33
will be invalid time and date in 'full' mode but it will
be valid in 'fast' mode.
--id ID of schema. [string] [default: ""]
--uniqueItems Validate `uniqueItems` keyword[boolean] [default: true]
--unicode calculate correct length of strings with unicode pairs
(true by default). Pass false to use .length of strings
that is faster, but gives "incorrect" lengths of
strings with unicode pairs - each unicode pair is
counted as two characters. [boolean] [default: true]
--nullable support keyword "nullable" from Open API 3
specification. [boolean] [default: true]
--format formats validation mode ('fast' by default). Pass
'full' for more correct and slow validation or false
not to validate formats at all. E.g., 25:00:00 and
2015/14/33 will be invalid time and date in 'full' mode
but it will be valid in 'fast' mode.
[choices: "fast", "full"] [default: "fast"]
--coerceTypes Change data type of data to match type keyword. e.g.
parse numbers in strings [boolean] [default: false]
--collection Process the file as a collection of types, instead of
one single type. [boolean] [default: false]
--useNamedExport Type name is a named export, rather than the default
export of the file [boolean] [default: false]
--coerceTypes Change data type of data to match type keyword. e.g.
parse numbers in strings [boolean] [default: false]
--collection Process the file as a collection of types, instead of
one single type. [boolean] [default: false]
--useNamedExport Type name is a named export, rather than the default
export of the file [boolean] [default: false]
--customKeywordFnName Name of function which configures AJV custom keywords
[string]
--customKeywordPath Path to file containing customKeywordFnName [string]
-* [default: []]

```
Expand Down
56 changes: 56 additions & 0 deletions src/Example.validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* tslint:disable */
// generated by typescript-json-validator
import {inspect} from 'util';
import Ajv = require('ajv');
import ExampleType from './Example';

export const ajv = new Ajv({
allErrors: true,
coerceTypes: false,
format: 'fast',
nullable: true,
unicode: true,
uniqueItems: true,
useDefaults: true,
});

ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));

export {ExampleType};
export const ExampleTypeSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
defaultProperties: [],
properties: {
answer: {
default: 42,
type: 'number',
},
email: {
type: 'string',
},
value: {
type: 'string',
},
},
required: ['answer', 'value'],
type: 'object',
};
export type ValidateFunction<T> = ((data: unknown) => data is T) &
Pick<Ajv.ValidateFunction, 'errors'>;
export const isExampleType = ajv.compile(ExampleTypeSchema) as ValidateFunction<
ExampleType
>;
export default function validate(value: unknown): ExampleType {
if (isExampleType(value)) {
return value;
} else {
throw new Error(
ajv.errorsText(
isExampleType.errors!.filter((e: any) => e.keyword !== 'if'),
{dataVar: 'ExampleType'},
) +
'\n\n' +
inspect(value),
);
}
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export default function run(args?: string[]) {
options.useNamedExport,
normalizeSchema(schema),
`./${basename(fileName, /\.ts$/.test(fileName) ? '.ts' : '.tsx')}`,
options.customKeywordFnName,
options.customKeywordPath,
tsConfig,
options.ajv,
);
Expand Down
16 changes: 16 additions & 0 deletions src/parseArgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export interface Options {
>;
ajv: Ajv.Options;
useNamedExport: boolean;
customKeywordFnName: string | undefined;
customKeywordPath: string | undefined;
}
export interface File {
fileName: string;
Expand Down Expand Up @@ -146,6 +148,18 @@ export function parseArgs(args?: string[]): ParsedArgs {
'useNamedExport',
'Type name is a named export, rather than the default export of the file',
)
.string('customKeywordFnName')
.default('customKeywordFnName', undefined)
.describe(
'customKeywordFnName',
'Name of function which configures AJV custom keywords',
)
.string('customKeywordPath')
.default('customKeywordPath', undefined)
.describe(
'customKeywordPath',
'Path to file containing customKeywordFnName',
)
.parse(args);

const isCollection: boolean = parsedArgs.collection;
Expand Down Expand Up @@ -193,6 +207,8 @@ export function parseArgs(args?: string[]): ParsedArgs {
useDefaults: parsedArgs.defaultProps,
},
useNamedExport: parsedArgs.useNamedExport,
customKeywordFnName: parsedArgs.customKeywordFnName,
customKeywordPath: parsedArgs.customKeywordPath,
},
};
}
4 changes: 4 additions & 0 deletions src/printValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export function printSingleTypeValidator(
isNamedExport: boolean,
schema: TJS.Definition,
relativePath: string,
customKeywordFnName: string | undefined,
customKeywordPath: string | undefined,
tsConfig: any,
options: Ajv.Options = {},
) {
Expand All @@ -56,7 +58,9 @@ export function printSingleTypeValidator(
t.IMPORT_INSPECT,
t.IMPORT_AJV(tsConfig),
t.importType(typeName, relativePath, {isNamedExport}),
t.importCustomKeyword(customKeywordFnName, customKeywordPath),
t.declareAJV(options),
t.applyCustomKeyword(customKeywordFnName),
t.exportNamed([typeName]),
t.declareSchema(typeName + 'Schema', schema),
// TODO: koa implementation
Expand Down
8 changes: 8 additions & 0 deletions src/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,11 @@ export const VALIDATE_IMPLEMENTATION = `export function validate(typeName: strin
return value as any;
};
}`;

export const importCustomKeyword = (
fnName: string | undefined,
path: string | undefined,
) => (fnName && path ? `import {${fnName}} from '${path}';` : '');

export const applyCustomKeyword = (fnName: string | undefined) =>
fnName ? `${fnName}(ajv);` : '';