api-typescript-generator
Advanced tools
Comparing version 2.2.2 to 2.2.3
@@ -14,4 +14,6 @@ import { | ||
type CommonValidationSchemaStorageFormatErrorMessage = (error: Error) => string; | ||
type CommonValidationSchemaStorageFormatErrorMessage<T> = (error: Error, schema: T) => string; | ||
type CommonValidationSchemaStorageLazyGetter<T> = (callback: () => T) => T; | ||
export class CommonValidationSchemaStorage<T> { | ||
@@ -23,3 +25,4 @@ protected assertDataShape: CommonValidationSchemaStorageAssertDataShape<T>; | ||
protected errorClass: CommonHttpClientOptions['errorClass'] = CommonHttpClientError; | ||
protected formatErrorMessage: CommonValidationSchemaStorageFormatErrorMessage; | ||
protected formatErrorMessage: CommonValidationSchemaStorageFormatErrorMessage<T>; | ||
protected lazyGetter: CommonValidationSchemaStorageLazyGetter<T>; | ||
@@ -29,7 +32,9 @@ constructor({ | ||
makeExtensible, | ||
formatErrorMessage | ||
formatErrorMessage, | ||
lazyGetter | ||
}: { | ||
assertDataShape: CommonValidationSchemaStorageAssertDataShape<T>; | ||
makeExtensible: CommonValidationSchemaStorageMakeExtensible<T>; | ||
formatErrorMessage: CommonValidationSchemaStorageFormatErrorMessage; | ||
formatErrorMessage: CommonValidationSchemaStorageFormatErrorMessage<T>; | ||
lazyGetter: CommonValidationSchemaStorageLazyGetter<T>; | ||
}) { | ||
@@ -39,2 +44,3 @@ this.assertDataShape = assertDataShape; | ||
this.formatErrorMessage = formatErrorMessage; | ||
this.lazyGetter = lazyGetter; | ||
} | ||
@@ -63,4 +69,4 @@ | ||
lazy(key: string): () => T { | ||
return () => this.schemas[key]; | ||
lazy(key: string): T { | ||
return this.lazyGetter(() => this.schemas[key]); | ||
} | ||
@@ -72,3 +78,4 @@ | ||
return (data: D) => { | ||
if (!this.schemas[key]) { | ||
const schema = this.schemas[key]; | ||
if (schema === undefined) { | ||
throw new Error(`Schema with key "${key}" not found`); | ||
@@ -78,4 +85,4 @@ } | ||
try { | ||
this.assertDataShape(this.schemas[key], data); | ||
} catch (e) { | ||
this.assertDataShape(schema, data); | ||
} catch (error) { | ||
throw new this.errorClass( | ||
@@ -86,3 +93,3 @@ new URL(data.response.url), | ||
undefined, | ||
e instanceof Error ? this.formatErrorMessage(e) : String(e) | ||
error instanceof Error ? this.formatErrorMessage(error, schema) : String(error) | ||
); | ||
@@ -89,0 +96,0 @@ } |
@@ -220,5 +220,5 @@ "use strict"; | ||
(0, types_1.stringLiteral)(modelName), | ||
(0, dependencies_1.extendDependenciesAndGetResult)(validationProvider.generateSchema(schema, { | ||
getNamedSchemaReference: (name) => (0, dependencies_1.extendDependenciesAndGetResult)(validationProvider.generateLazyGetter((0, types_1.callExpression)((0, types_1.memberExpression)((0, types_1.identifier)(validationSchemaStorageArgumentName), (0, types_1.identifier)('get')), [(0, types_1.stringLiteral)(getModelName(name))])), dependencyImports) | ||
}), dependencyImports) | ||
(0, dependencies_1.extendDependenciesAndGetResult)(validationProvider.generateSetModelNameCall((0, dependencies_1.extendDependenciesAndGetResult)(validationProvider.generateSchema(schema, { | ||
getNamedSchemaReference: (name) => (0, types_1.callExpression)((0, types_1.memberExpression)((0, types_1.identifier)(validationSchemaStorageArgumentName), (0, types_1.identifier)('lazy')), [(0, types_1.stringLiteral)(getModelName(name))]) | ||
}), dependencyImports), modelName), dependencyImports) | ||
]))); | ||
@@ -225,0 +225,0 @@ } |
@@ -70,3 +70,3 @@ "use strict"; | ||
if (validationContext) { | ||
const { validationProvider, validationSchemaStorageImportName } = validationContext; | ||
const { validationSchemaStorageImportName } = validationContext; | ||
if (mediaType.schema === undefined) { | ||
@@ -86,3 +86,3 @@ mediaTypeSchemas[status][mediaTypeString] = null; | ||
} | ||
return (0, dependencies_1.extendDependenciesAndGetResult)(validationProvider.generateLazyGetter((0, types_1.callExpression)((0, types_1.memberExpression)((0, types_1.identifier)(validationSchemaStorageImportName), (0, types_1.identifier)('get')), [(0, types_1.stringLiteral)(modelData.modelName)])), dependencyImports); | ||
return (0, types_1.callExpression)((0, types_1.memberExpression)((0, types_1.identifier)(validationSchemaStorageImportName), (0, types_1.identifier)('lazy')), [(0, types_1.stringLiteral)(modelData.modelName)]); | ||
} | ||
@@ -105,3 +105,3 @@ }), dependencyImports); | ||
(0, types_1.stringLiteral)(validationSchemaName), | ||
(0, dependencies_1.extendDependenciesAndGetResult)(validationProvider.generateOperationResponseSchema(mediaTypeSchemas), dependencyImports) | ||
(0, dependencies_1.extendDependenciesAndGetResult)(validationProvider.generateSetModelNameCall((0, dependencies_1.extendDependenciesAndGetResult)(validationProvider.generateOperationResponseSchema(mediaTypeSchemas), dependencyImports), validationSchemaName), dependencyImports) | ||
]))); | ||
@@ -108,0 +108,0 @@ } |
@@ -16,2 +16,3 @@ import { Expression, TSType } from '@babel/types'; | ||
abstract generateAssertCall(validationSchema: Expression, data: Expression): ResultWithDependencyImports<Expression>; | ||
abstract generateSetModelNameCall(validationSchema: Expression, modelName: string): ResultWithDependencyImports<Expression>; | ||
abstract generateOperationResponseSchema(responses: { | ||
@@ -18,0 +19,0 @@ [statusCode: string]: { |
@@ -1,10 +0,82 @@ | ||
import {ZodError} from 'zod'; | ||
import { | ||
ZodArray, | ||
ZodError, | ||
ZodIntersection, | ||
ZodLazy, | ||
ZodNullable, | ||
ZodObject, | ||
ZodOptional, | ||
ZodTuple, | ||
ZodTypeAny, | ||
ZodUnion | ||
} from 'zod'; | ||
export const formatErrorMessage = function formatErrorMessage(error: Error) { | ||
export const formatErrorMessage = function formatErrorMessage(error: Error, schema: ZodTypeAny) { | ||
interface ModelNameWithFieldPath { | ||
modelName: string; | ||
path: (string | number)[]; | ||
} | ||
function findModelNameByPath(schema: ZodTypeAny, path: (string | number)[]): ModelNameWithFieldPath | undefined { | ||
let result: ModelNameWithFieldPath | undefined = undefined; | ||
if (schema instanceof ZodLazy) { | ||
return findModelNameByPath(schema.schema, path) ?? result; | ||
} | ||
if (schema.description !== undefined) { | ||
result = {modelName: schema.description, path}; | ||
} | ||
if (path.length === 0) { | ||
return result; | ||
} | ||
if (schema instanceof ZodObject) { | ||
const [pathBit, ...restPath] = path; | ||
const field = schema.shape[pathBit]; | ||
if (field !== undefined) { | ||
result = findModelNameByPath(field, restPath) ?? result; | ||
} | ||
const catchall = schema._def.catchall; | ||
if (catchall !== undefined) { | ||
result = findModelNameByPath(catchall, restPath) ?? result; | ||
} | ||
} else if (schema instanceof ZodArray) { | ||
const [, ...restPath] = path; | ||
result = findModelNameByPath(schema.element, restPath) ?? result; | ||
} else if (schema instanceof ZodTuple) { | ||
const [pathBit, ...restPath] = path; | ||
const element = schema.items[pathBit]; | ||
if (element !== undefined) { | ||
result = findModelNameByPath(element, restPath) ?? result; | ||
} | ||
} else if (schema instanceof ZodOptional) { | ||
result = findModelNameByPath(schema.unwrap(), path) ?? result; | ||
} else if (schema instanceof ZodNullable) { | ||
result = findModelNameByPath(schema.unwrap(), path) ?? result; | ||
} else if (schema instanceof ZodIntersection) { | ||
result = | ||
findModelNameByPath(schema._def.left, path) ?? findModelNameByPath(schema._def.right, path) ?? result; | ||
} else if (schema instanceof ZodUnion) { | ||
result = schema.options.reduce( | ||
(acc: ModelNameWithFieldPath | undefined, option: ZodTypeAny) => | ||
findModelNameByPath(option, path) ?? acc, | ||
result | ||
); | ||
} | ||
return result; | ||
} | ||
function pathToString(path: (string | number)[]): string { | ||
return path.map((p, index) => (typeof p === 'number' ? `[${p}]` : `${index > 0 ? '.' : ''}${p}`)).join(''); | ||
} | ||
if (error instanceof ZodError) { | ||
return error.errors | ||
.map(({message, path}) => `${message}${path.length > 0 ? ` at ${path.join('.')}` : ''}`) | ||
.join(', '); | ||
.map(({message, path}) => { | ||
const info = findModelNameByPath(schema, path); | ||
const modelInfo = info ? ` at '${pathToString([info.modelName, ...info.path])}'` : ''; | ||
return `${message}${path.length > 0 ? `${modelInfo}, full path: '${pathToString(path)}'.` : ''}`; | ||
}) | ||
.join(' '); | ||
} | ||
return error.message; | ||
}; |
@@ -15,3 +15,3 @@ import { Expression } from '@babel/types'; | ||
generateSchema(schema: OpenApiSchema, context: ValidationProviderContext): ResultWithDependencyImports<Expression>; | ||
generateSchemaItem(schema: OpenApiSchema, context: ValidationProviderContext): Expression; | ||
protected generateSchemaItem(schema: OpenApiSchema, context: ValidationProviderContext): Expression; | ||
generateLazyGetter(expression: Expression): { | ||
@@ -25,2 +25,3 @@ result: import("@babel/types").CallExpression; | ||
}; | ||
generateSetModelNameCall(validationSchema: Expression, modelName: string): ResultWithDependencyImports<Expression>; | ||
generateOperationResponseSchema(responses: { | ||
@@ -27,0 +28,0 @@ [statusCode: string]: { |
@@ -92,3 +92,3 @@ "use strict"; | ||
(0, dependencies_1.addDependencyImport)(dependencyImports, statement.source.value, specifier.local.name, { | ||
kind: specifier.importKind === 'type' ? 'type' : 'value', | ||
kind: specifier.importKind === 'type' || statement.importKind === 'type' ? 'type' : 'value', | ||
entity: { name: specifier.imported.name } | ||
@@ -394,3 +394,3 @@ }); | ||
generateLazyGetter(expression) { | ||
return this.withDependencyImports(zCall('lazy', [(0, types_1.arrowFunctionExpression)([], expression)])); | ||
return this.withDependencyImports(zCall('lazy', [expression])); | ||
} | ||
@@ -400,2 +400,5 @@ generateAssertCall(validationSchema, data) { | ||
} | ||
generateSetModelNameCall(validationSchema, modelName) { | ||
return this.withDependencyImports((0, types_1.callExpression)((0, types_1.memberExpression)(validationSchema, (0, types_1.identifier)('describe')), [(0, types_1.stringLiteral)(modelName)])); | ||
} | ||
generateOperationResponseSchema(responses) { | ||
@@ -402,0 +405,0 @@ function generateMediaTypeSchema(mediaType) { |
@@ -43,3 +43,4 @@ "use strict"; | ||
(0, types_1.objectProperty)((0, types_1.identifier)('makeExtensible'), (0, dependencies_1.extendDependenciesAndGetResult)(yield validationProvider.generateMakeExtensibleCallback(), dependencyImports)), | ||
(0, types_1.objectProperty)((0, types_1.identifier)('formatErrorMessage'), (0, dependencies_1.extendDependenciesAndGetResult)(yield validationProvider.generateFormatErrorMessageCallback(), dependencyImports)) | ||
(0, types_1.objectProperty)((0, types_1.identifier)('formatErrorMessage'), (0, dependencies_1.extendDependenciesAndGetResult)(yield validationProvider.generateFormatErrorMessageCallback(), dependencyImports)), | ||
(0, types_1.objectProperty)((0, types_1.identifier)('lazyGetter'), (0, types_1.arrowFunctionExpression)([(0, types_1.identifier)('getSchema')], (0, dependencies_1.extendDependenciesAndGetResult)(yield validationProvider.generateLazyGetter((0, types_1.identifier)('getSchema')), dependencyImports))) | ||
]) | ||
@@ -46,0 +47,0 @@ ]), { |
{ | ||
"name": "api-typescript-generator", | ||
"version": "2.2.2", | ||
"version": "2.2.3", | ||
"description": "Generates OpenAPI TypeScript client. Extremely fast and flexible.", | ||
@@ -48,2 +48,3 @@ "license": "MIT", | ||
"test": "jest", | ||
"test:watch": "jest --watch --watchPathIgnorePatterns tmp", | ||
"test:update": "ts-node src/cli/index.ts generate test/pet-store/api-typescript-generator-config.ts", | ||
@@ -50,0 +51,0 @@ "release": "standard-version" |
@@ -14,4 +14,6 @@ import { | ||
type CommonValidationSchemaStorageFormatErrorMessage = (error: Error) => string; | ||
type CommonValidationSchemaStorageFormatErrorMessage<T> = (error: Error, schema: T) => string; | ||
type CommonValidationSchemaStorageLazyGetter<T> = (callback: () => T) => T; | ||
export class CommonValidationSchemaStorage<T> { | ||
@@ -23,3 +25,4 @@ protected assertDataShape: CommonValidationSchemaStorageAssertDataShape<T>; | ||
protected errorClass: CommonHttpClientOptions['errorClass'] = CommonHttpClientError; | ||
protected formatErrorMessage: CommonValidationSchemaStorageFormatErrorMessage; | ||
protected formatErrorMessage: CommonValidationSchemaStorageFormatErrorMessage<T>; | ||
protected lazyGetter: CommonValidationSchemaStorageLazyGetter<T>; | ||
@@ -29,7 +32,9 @@ constructor({ | ||
makeExtensible, | ||
formatErrorMessage | ||
formatErrorMessage, | ||
lazyGetter | ||
}: { | ||
assertDataShape: CommonValidationSchemaStorageAssertDataShape<T>; | ||
makeExtensible: CommonValidationSchemaStorageMakeExtensible<T>; | ||
formatErrorMessage: CommonValidationSchemaStorageFormatErrorMessage; | ||
formatErrorMessage: CommonValidationSchemaStorageFormatErrorMessage<T>; | ||
lazyGetter: CommonValidationSchemaStorageLazyGetter<T>; | ||
}) { | ||
@@ -39,2 +44,3 @@ this.assertDataShape = assertDataShape; | ||
this.formatErrorMessage = formatErrorMessage; | ||
this.lazyGetter = lazyGetter; | ||
} | ||
@@ -63,4 +69,4 @@ | ||
lazy(key: string): () => T { | ||
return () => this.schemas[key]; | ||
lazy(key: string): T { | ||
return this.lazyGetter(() => this.schemas[key]); | ||
} | ||
@@ -72,3 +78,4 @@ | ||
return (data: D) => { | ||
if (!this.schemas[key]) { | ||
const schema = this.schemas[key]; | ||
if (schema === undefined) { | ||
throw new Error(`Schema with key "${key}" not found`); | ||
@@ -78,4 +85,4 @@ } | ||
try { | ||
this.assertDataShape(this.schemas[key], data); | ||
} catch (e) { | ||
this.assertDataShape(schema, data); | ||
} catch (error) { | ||
throw new this.errorClass( | ||
@@ -86,3 +93,3 @@ new URL(data.response.url), | ||
undefined, | ||
e instanceof Error ? this.formatErrorMessage(e) : String(e) | ||
error instanceof Error ? this.formatErrorMessage(error, schema) : String(error) | ||
); | ||
@@ -89,0 +96,0 @@ } |
@@ -336,17 +336,18 @@ import path from 'path'; | ||
extendDependenciesAndGetResult( | ||
validationProvider.generateSchema(schema, { | ||
getNamedSchemaReference: (name: string) => | ||
extendDependenciesAndGetResult( | ||
validationProvider.generateLazyGetter( | ||
validationProvider.generateSetModelNameCall( | ||
extendDependenciesAndGetResult( | ||
validationProvider.generateSchema(schema, { | ||
getNamedSchemaReference: (name: string) => | ||
callExpression( | ||
memberExpression( | ||
identifier(validationSchemaStorageArgumentName), | ||
identifier('get') | ||
identifier('lazy') | ||
), | ||
[stringLiteral(getModelName(name))] | ||
) | ||
), | ||
dependencyImports | ||
) | ||
}), | ||
}), | ||
dependencyImports | ||
), | ||
modelName | ||
), | ||
dependencyImports | ||
@@ -353,0 +354,0 @@ ) |
@@ -167,3 +167,3 @@ import { | ||
if (validationContext) { | ||
const {validationProvider, validationSchemaStorageImportName} = validationContext; | ||
const {validationSchemaStorageImportName} = validationContext; | ||
if (mediaType.schema === undefined) { | ||
@@ -190,13 +190,8 @@ mediaTypeSchemas[status][mediaTypeString] = null; | ||
} | ||
return extendDependenciesAndGetResult( | ||
validationProvider.generateLazyGetter( | ||
callExpression( | ||
memberExpression( | ||
identifier(validationSchemaStorageImportName), | ||
identifier('get') | ||
), | ||
[stringLiteral(modelData.modelName)] | ||
) | ||
return callExpression( | ||
memberExpression( | ||
identifier(validationSchemaStorageImportName), | ||
identifier('lazy') | ||
), | ||
dependencyImports | ||
[stringLiteral(modelData.modelName)] | ||
); | ||
@@ -241,3 +236,9 @@ } | ||
extendDependenciesAndGetResult( | ||
validationProvider.generateOperationResponseSchema(mediaTypeSchemas), | ||
validationProvider.generateSetModelNameCall( | ||
extendDependenciesAndGetResult( | ||
validationProvider.generateOperationResponseSchema(mediaTypeSchemas), | ||
dependencyImports | ||
), | ||
validationSchemaName | ||
), | ||
dependencyImports | ||
@@ -244,0 +245,0 @@ ) |
@@ -25,2 +25,6 @@ import {Expression, TSType} from '@babel/types'; | ||
): ResultWithDependencyImports<Expression>; | ||
abstract generateSetModelNameCall( | ||
validationSchema: Expression, | ||
modelName: string | ||
): ResultWithDependencyImports<Expression>; | ||
abstract generateOperationResponseSchema(responses: { | ||
@@ -27,0 +31,0 @@ [statusCode: string]: {[mediaType: string]: Expression | null}; |
@@ -1,10 +0,82 @@ | ||
import {ZodError} from 'zod'; | ||
import { | ||
ZodArray, | ||
ZodError, | ||
ZodIntersection, | ||
ZodLazy, | ||
ZodNullable, | ||
ZodObject, | ||
ZodOptional, | ||
ZodTuple, | ||
ZodTypeAny, | ||
ZodUnion | ||
} from 'zod'; | ||
export const formatErrorMessage = function formatErrorMessage(error: Error) { | ||
export const formatErrorMessage = function formatErrorMessage(error: Error, schema: ZodTypeAny) { | ||
interface ModelNameWithFieldPath { | ||
modelName: string; | ||
path: (string | number)[]; | ||
} | ||
function findModelNameByPath(schema: ZodTypeAny, path: (string | number)[]): ModelNameWithFieldPath | undefined { | ||
let result: ModelNameWithFieldPath | undefined = undefined; | ||
if (schema instanceof ZodLazy) { | ||
return findModelNameByPath(schema.schema, path) ?? result; | ||
} | ||
if (schema.description !== undefined) { | ||
result = {modelName: schema.description, path}; | ||
} | ||
if (path.length === 0) { | ||
return result; | ||
} | ||
if (schema instanceof ZodObject) { | ||
const [pathBit, ...restPath] = path; | ||
const field = schema.shape[pathBit]; | ||
if (field !== undefined) { | ||
result = findModelNameByPath(field, restPath) ?? result; | ||
} | ||
const catchall = schema._def.catchall; | ||
if (catchall !== undefined) { | ||
result = findModelNameByPath(catchall, restPath) ?? result; | ||
} | ||
} else if (schema instanceof ZodArray) { | ||
const [, ...restPath] = path; | ||
result = findModelNameByPath(schema.element, restPath) ?? result; | ||
} else if (schema instanceof ZodTuple) { | ||
const [pathBit, ...restPath] = path; | ||
const element = schema.items[pathBit]; | ||
if (element !== undefined) { | ||
result = findModelNameByPath(element, restPath) ?? result; | ||
} | ||
} else if (schema instanceof ZodOptional) { | ||
result = findModelNameByPath(schema.unwrap(), path) ?? result; | ||
} else if (schema instanceof ZodNullable) { | ||
result = findModelNameByPath(schema.unwrap(), path) ?? result; | ||
} else if (schema instanceof ZodIntersection) { | ||
result = | ||
findModelNameByPath(schema._def.left, path) ?? findModelNameByPath(schema._def.right, path) ?? result; | ||
} else if (schema instanceof ZodUnion) { | ||
result = schema.options.reduce( | ||
(acc: ModelNameWithFieldPath | undefined, option: ZodTypeAny) => | ||
findModelNameByPath(option, path) ?? acc, | ||
result | ||
); | ||
} | ||
return result; | ||
} | ||
function pathToString(path: (string | number)[]): string { | ||
return path.map((p, index) => (typeof p === 'number' ? `[${p}]` : `${index > 0 ? '.' : ''}${p}`)).join(''); | ||
} | ||
if (error instanceof ZodError) { | ||
return error.errors | ||
.map(({message, path}) => `${message}${path.length > 0 ? ` at ${path.join('.')}` : ''}`) | ||
.join(', '); | ||
.map(({message, path}) => { | ||
const info = findModelNameByPath(schema, path); | ||
const modelInfo = info ? ` at '${pathToString([info.modelName, ...info.path])}'` : ''; | ||
return `${message}${path.length > 0 ? `${modelInfo}, full path: '${pathToString(path)}'.` : ''}`; | ||
}) | ||
.join(' '); | ||
} | ||
return error.message; | ||
}; |
@@ -79,3 +79,3 @@ import * as fs from 'fs'; | ||
addDependencyImport(dependencyImports, statement.source.value, specifier.local.name, { | ||
kind: specifier.importKind === 'type' ? 'type' : 'value', | ||
kind: specifier.importKind === 'type' || statement.importKind === 'type' ? 'type' : 'value', | ||
entity: {name: specifier.imported.name} | ||
@@ -123,3 +123,3 @@ }); | ||
} | ||
generateSchemaItem(schema: OpenApiSchema, context: ValidationProviderContext): Expression { | ||
protected generateSchemaItem(schema: OpenApiSchema, context: ValidationProviderContext): Expression { | ||
if (schema === true) { | ||
@@ -469,3 +469,3 @@ return zCall('unknown', []); | ||
generateLazyGetter(expression: Expression) { | ||
return this.withDependencyImports(zCall('lazy', [arrowFunctionExpression([], expression)])); | ||
return this.withDependencyImports(zCall('lazy', [expression])); | ||
} | ||
@@ -477,2 +477,7 @@ generateAssertCall(validationSchema: Expression, data: Expression) { | ||
} | ||
generateSetModelNameCall(validationSchema: Expression, modelName: string): ResultWithDependencyImports<Expression> { | ||
return this.withDependencyImports( | ||
callExpression(memberExpression(validationSchema, identifier('describe')), [stringLiteral(modelName)]) | ||
); | ||
} | ||
generateOperationResponseSchema(responses: {[statusCode: string]: {[mediaType: string]: Expression | null}}) { | ||
@@ -479,0 +484,0 @@ function generateMediaTypeSchema(mediaType: string) { |
@@ -84,2 +84,12 @@ import { | ||
) | ||
), | ||
objectProperty( | ||
identifier('lazyGetter'), | ||
arrowFunctionExpression( | ||
[identifier('getSchema')], | ||
extendDependenciesAndGetResult( | ||
await validationProvider.generateLazyGetter(identifier('getSchema')), | ||
dependencyImports | ||
) | ||
) | ||
) | ||
@@ -86,0 +96,0 @@ ]) |
732854
13865
176