fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #712 from hey-api/fix/transform-multiple-void

fix: infinite loop in transforms

authored by

Lubos and committed by
GitHub
9a1e8c98 83b6e993

+1386 -761
+5
.changeset/quick-trees-laugh.md
··· 1 + --- 2 + '@hey-api/openapi-ts': patch 3 + --- 4 + 5 + fix: handle void responses in transformers
+10
packages/openapi-ts/src/compiler/convert.ts
··· 1 + import ts from 'typescript'; 2 + 3 + export const convertExpressionToStatement = ({ 4 + expression, 5 + }: { 6 + expression: ts.Expression; 7 + }) => { 8 + const statement = ts.factory.createExpressionStatement(expression); 9 + return statement; 10 + };
+19
packages/openapi-ts/src/compiler/function.ts
··· 1 + import ts from 'typescript'; 2 + 3 + export const createCallExpression = ({ 4 + parameters, 5 + functionName, 6 + }: { 7 + parameters: Array<string>; 8 + functionName: string; 9 + }) => { 10 + const functionIdentifier = ts.factory.createIdentifier(functionName); 11 + 12 + const callExpression = ts.factory.createCallExpression( 13 + functionIdentifier, 14 + undefined, 15 + parameters.map((parameter) => ts.factory.createIdentifier(parameter)), 16 + ); 17 + 18 + return callExpression; 19 + };
+21 -1
packages/openapi-ts/src/compiler/index.ts
··· 4 4 import ts from 'typescript'; 5 5 6 6 import * as classes from './classes'; 7 + import * as convert from './convert'; 8 + import * as functions from './function'; 7 9 import * as module from './module'; 8 10 import * as _return from './return'; 9 11 import * as transform from './transform'; ··· 78 80 rmSync(this._path, options); 79 81 } 80 82 83 + /** 84 + * Removes last node form the stack. Works as undo. 85 + */ 86 + public removeNode() { 87 + this._items = this._items.slice(0, this._items.length - 1); 88 + } 89 + 81 90 private _setName(fileName: string) { 82 91 if (fileName.includes('index')) { 83 92 return fileName; ··· 124 133 create: classes.createClassDeclaration, 125 134 method: classes.createMethodDeclaration, 126 135 }, 136 + convert: { 137 + expressionToStatement: convert.convertExpressionToStatement, 138 + }, 127 139 export: { 128 140 all: module.createExportAllDeclaration, 129 141 const: module.createExportConstVariable, 130 142 named: module.createNamedExportDeclarations, 131 143 }, 144 + function: { 145 + call: functions.createCallExpression, 146 + }, 132 147 import: { 133 148 named: module.createNamedImportDeclarations, 134 149 }, 150 + logic: { 151 + access: transform.createAccessExpression, 152 + if: transform.createIfStatement, 153 + safeAccess: transform.createSafeAccessExpression, 154 + }, 135 155 return: { 136 156 functionCall: _return.createReturnFunctionCall, 157 + statement: _return.createReturnStatement, 137 158 }, 138 159 transform: { 139 160 alias: transform.createAlias, ··· 143 164 newDate: transform.createDateTransformerExpression, 144 165 responseArrayTransform: transform.createResponseArrayTransform, 145 166 transformItem: transform.createFunctionTransformMutation, 146 - transformMutationFunction: transform.createTransformMutationFunction, 147 167 }, 148 168 typedef: { 149 169 alias: typedef.createTypeAliasDeclaration,
+3 -1
packages/openapi-ts/src/compiler/module.ts
··· 69 69 constAssertion = false, 70 70 expression, 71 71 name, 72 + typeName, 72 73 }: { 73 74 comment?: Comments; 74 75 constAssertion?: boolean; 75 76 expression: ts.Expression; 76 77 name: string; 78 + typeName?: string; 77 79 }): ts.VariableStatement => { 78 80 const initializer = constAssertion 79 81 ? ts.factory.createAsExpression( ··· 84 86 const declaration = ts.factory.createVariableDeclaration( 85 87 ts.factory.createIdentifier(name), 86 88 undefined, 87 - undefined, 89 + typeName ? ts.factory.createTypeReferenceNode(typeName) : undefined, 88 90 initializer, 89 91 ); 90 92 const statement = ts.factory.createVariableStatement(
+7 -3
packages/openapi-ts/src/compiler/return.ts
··· 28 28 ) 29 29 .filter(isType<ts.Identifier | ts.Expression>), 30 30 ); 31 - 32 - const statement = ts.factory.createReturnStatement(expression); 33 - 31 + const statement = createReturnStatement({ expression }); 34 32 return statement; 35 33 }; 34 + 35 + export const createReturnStatement = ({ 36 + expression, 37 + }: { 38 + expression?: ts.Expression; 39 + }) => ts.factory.createReturnStatement(expression);
+85 -107
packages/openapi-ts/src/compiler/transform.ts
··· 1 1 import ts from 'typescript'; 2 2 3 - const getSafeAccessExpression = (path: string[]) => 3 + import { convertExpressionToStatement } from './convert'; 4 + import { createReturnStatement } from './return'; 5 + 6 + export const createSafeAccessExpression = (path: string[]) => 4 7 path 5 8 .slice(1) 6 9 .reduce<ts.Expression>( ··· 13 16 ts.factory.createIdentifier(path[0]), 14 17 ); 15 18 16 - const getAccessExpression = (path: string[]) => 19 + export const createAccessExpression = (path: string[]) => 17 20 path 18 21 .slice(1) 19 22 .reduce<ts.Expression>( ··· 25 28 ts.factory.createIdentifier(path[0]), 26 29 ); 27 30 31 + export const createIfStatement = ({ 32 + expression, 33 + thenStatement, 34 + elseStatement, 35 + }: { 36 + expression: ts.Expression; 37 + thenStatement: ts.Statement; 38 + elseStatement?: ts.Statement; 39 + }) => ts.factory.createIfStatement(expression, thenStatement, elseStatement); 40 + 28 41 export const createDateTransformMutation = ({ 29 42 path, 30 43 }: { 31 44 path: string[]; 32 45 }): ts.Statement => { 33 - const safeAccessExpression = getSafeAccessExpression(path); 34 - const accessExpression = getAccessExpression(path); 46 + const safeAccessExpression = createSafeAccessExpression(path); 47 + const accessExpression = createAccessExpression(path); 35 48 36 - const statement = ts.factory.createIfStatement( 37 - safeAccessExpression, 38 - ts.factory.createBlock([ 39 - ts.factory.createExpressionStatement( 40 - ts.factory.createBinaryExpression( 41 - accessExpression, 42 - ts.SyntaxKind.EqualsToken, 43 - ts.factory.createNewExpression( 44 - ts.factory.createIdentifier('Date'), 45 - undefined, 46 - [accessExpression], 47 - ), 49 + const thenStatement = ts.factory.createBlock([ 50 + convertExpressionToStatement({ 51 + expression: ts.factory.createBinaryExpression( 52 + accessExpression, 53 + ts.SyntaxKind.EqualsToken, 54 + ts.factory.createNewExpression( 55 + ts.factory.createIdentifier('Date'), 56 + undefined, 57 + [accessExpression], 48 58 ), 49 59 ), 50 - ]), 51 - ); 60 + }), 61 + ]); 62 + 63 + const statement = createIfStatement({ 64 + expression: safeAccessExpression, 65 + thenStatement, 66 + }); 52 67 53 68 return statement; 54 69 }; 55 70 56 71 export const createFunctionTransformMutation = ({ 57 72 path, 58 - transformer, 73 + transformerName, 59 74 }: { 60 75 path: string[]; 61 - transformer: string; 76 + transformerName: string; 62 77 }) => { 63 - const safeAccessExpression = getSafeAccessExpression(path); 64 - const accessExpression = getAccessExpression(path); 78 + const safeAccessExpression = createSafeAccessExpression(path); 79 + const accessExpression = createAccessExpression(path); 80 + 81 + const thenStatement = ts.factory.createBlock( 82 + [ 83 + convertExpressionToStatement({ 84 + expression: ts.factory.createCallExpression( 85 + ts.factory.createIdentifier(transformerName), 86 + undefined, 87 + [accessExpression], 88 + ), 89 + }), 90 + ], 91 + true, 92 + ); 65 93 66 94 const statement = [ 67 - ts.factory.createIfStatement( 68 - safeAccessExpression, 69 - ts.factory.createBlock( 70 - [ 71 - ts.factory.createExpressionStatement( 72 - ts.factory.createCallExpression( 73 - ts.factory.createIdentifier(transformer), 74 - undefined, 75 - [accessExpression], 76 - ), 77 - ), 78 - ], 79 - true, 80 - ), 81 - undefined, 82 - ), 95 + createIfStatement({ 96 + expression: safeAccessExpression, 97 + thenStatement, 98 + }), 83 99 ]; 84 100 85 101 return statement; ··· 87 103 88 104 export const createArrayTransformMutation = ({ 89 105 path, 90 - transformer, 106 + transformerName, 91 107 }: { 92 108 path: string[]; 93 - transformer: string; 109 + transformerName: string; 94 110 }): ts.Statement => { 95 - const safeAccessExpression = getSafeAccessExpression(path); 96 - const accessExpression = getAccessExpression(path); 111 + const safeAccessExpression = createSafeAccessExpression(path); 112 + const accessExpression = createAccessExpression(path); 97 113 98 - const statement = ts.factory.createIfStatement( 99 - ts.factory.createCallExpression( 114 + const statement = createIfStatement({ 115 + expression: ts.factory.createCallExpression( 100 116 ts.factory.createPropertyAccessExpression( 101 117 ts.factory.createIdentifier('Array'), 102 118 ts.factory.createIdentifier('isArray'), ··· 104 120 undefined, 105 121 [safeAccessExpression], 106 122 ), 107 - ts.factory.createBlock( 123 + thenStatement: ts.factory.createBlock( 108 124 [ 109 - ts.factory.createExpressionStatement( 110 - ts.factory.createCallChain( 125 + convertExpressionToStatement({ 126 + expression: ts.factory.createCallChain( 111 127 ts.factory.createPropertyAccessExpression( 112 128 accessExpression, 113 129 ts.factory.createIdentifier('forEach'), 114 130 ), 115 131 undefined, 116 132 undefined, 117 - [ts.factory.createIdentifier(transformer)], 133 + [ts.factory.createIdentifier(transformerName)], 118 134 ), 119 - ), 135 + }), 120 136 ], 121 137 true, 122 138 ), 123 - ); 139 + }); 124 140 125 141 return statement; 126 142 }; ··· 143 159 path: string[]; 144 160 transformExpression: ts.Expression; 145 161 }) => { 146 - const safeAccessExpression = getSafeAccessExpression(path); 147 - const accessExpression = getAccessExpression(path); 162 + const safeAccessExpression = createSafeAccessExpression(path); 163 + const accessExpression = createAccessExpression(path); 148 164 149 - const statement = ts.factory.createIfStatement( 150 - ts.factory.createCallExpression( 165 + const statement = createIfStatement({ 166 + expression: ts.factory.createCallExpression( 151 167 ts.factory.createPropertyAccessExpression( 152 168 ts.factory.createIdentifier('Array'), 153 169 ts.factory.createIdentifier('isArray'), ··· 155 171 undefined, 156 172 [safeAccessExpression], 157 173 ), 158 - ts.factory.createBlock( 174 + thenStatement: ts.factory.createBlock( 159 175 [ 160 - ts.factory.createExpressionStatement( 161 - ts.factory.createBinaryExpression( 176 + convertExpressionToStatement({ 177 + expression: ts.factory.createBinaryExpression( 162 178 accessExpression, 163 179 ts.factory.createToken(ts.SyntaxKind.EqualsToken), 164 180 ts.factory.createCallChain( ··· 189 205 ], 190 206 ), 191 207 ), 192 - ), 208 + }), 193 209 ], 194 210 true, 195 211 ), 196 - undefined, 197 - ); 212 + }); 198 213 199 214 return statement; 200 215 }; 201 216 202 - export const createTransformMutationFunction = ({ 203 - modelName, 204 - statements, 205 - }: { 206 - modelName: string; 207 - statements: ts.Statement[]; 208 - }) => { 209 - const transformFunction = ts.factory.createFunctionDeclaration( 210 - [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], 211 - undefined, 212 - ts.factory.createIdentifier(modelName), 213 - undefined, 214 - [ 215 - ts.factory.createParameterDeclaration( 216 - undefined, 217 - undefined, 218 - ts.factory.createIdentifier('data'), 219 - undefined, 220 - ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), 221 - undefined, 222 - ), 223 - ], 224 - ts.factory.createTypeReferenceNode( 225 - ts.factory.createIdentifier(modelName), 226 - undefined, 227 - ), 228 - ts.factory.createBlock( 229 - [ 230 - ...statements, 231 - ts.factory.createReturnStatement(ts.factory.createIdentifier('data')), 232 - ], 233 - true, 234 - ), 235 - ); 236 - 237 - return transformFunction; 238 - }; 239 - 240 217 export const createAlias = ({ 241 218 existingName, 242 219 name, ··· 283 260 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 284 261 ts.factory.createBlock( 285 262 [ 286 - ts.factory.createIfStatement( 287 - ts.factory.createCallExpression( 263 + createIfStatement({ 264 + expression: ts.factory.createCallExpression( 288 265 ts.factory.createPropertyAccessExpression( 289 266 ts.factory.createIdentifier('Array'), 290 267 ts.factory.createIdentifier('isArray'), ··· 292 269 undefined, 293 270 [ts.factory.createIdentifier('data')], 294 271 ), 295 - ts.factory.createBlock( 272 + thenStatement: ts.factory.createBlock( 296 273 [ 297 - ts.factory.createExpressionStatement( 298 - ts.factory.createCallExpression( 274 + convertExpressionToStatement({ 275 + expression: ts.factory.createCallExpression( 299 276 ts.factory.createPropertyAccessExpression( 300 277 ts.factory.createIdentifier('data'), 301 278 ts.factory.createIdentifier('forEach'), ··· 303 280 undefined, 304 281 [ts.factory.createIdentifier(transform)], 305 282 ), 306 - ), 283 + }), 307 284 ], 308 285 true, 309 286 ), 310 - undefined, 311 - ), 312 - ts.factory.createReturnStatement(ts.factory.createIdentifier('data')), 287 + }), 288 + createReturnStatement({ 289 + expression: ts.factory.createIdentifier('data'), 290 + }), 313 291 ], 314 292 true, 315 293 ),
+1 -1
packages/openapi-ts/src/compiler/types.ts
··· 11 11 isReadOnly?: boolean; 12 12 isRequired?: boolean; 13 13 name: string; 14 - type: any | ts.TypeNode; 14 + type?: any | ts.TypeNode; 15 15 }; 16 16 17 17 /**
-4
packages/openapi-ts/src/openApi/common/interfaces/client.ts
··· 110 110 * Name passed to the initial `getModel()` call. 111 111 */ 112 112 name: string; 113 - /** 114 - * If transformers are enabled and this type is detected as needing one, this will be set 115 - */ 116 - hasTransformer?: boolean; 117 113 } 118 114 119 115 export interface Model extends Schema {
+1 -1
packages/openapi-ts/src/openApi/common/parser/type.ts
··· 144 144 result.type = encodedType; 145 145 result.base = encodedType; 146 146 if (type.startsWith('#')) { 147 - result.$refs = [...result.$refs, type]; 147 + result.$refs = [...result.$refs, decodeURIComponent(type)]; 148 148 } 149 149 result.imports = [...result.imports, encodedType]; 150 150 return result;
+2 -2
packages/openapi-ts/src/openApi/v3/parser/getModel.ts
··· 82 82 83 83 if (definition.$ref) { 84 84 const definitionRef = getType({ type: definition.$ref }); 85 - model.$refs = [...model.$refs, definition.$ref]; 85 + model.$refs = [...model.$refs, decodeURIComponent(definition.$ref)]; 86 86 model.base = definitionRef.base; 87 87 model.export = 'reference'; 88 88 model.imports = [...model.imports, ...definitionRef.imports]; ··· 147 147 148 148 if (definition.items.$ref) { 149 149 const arrayItems = getType({ type: definition.items.$ref }); 150 - model.$refs = [...model.$refs, definition.items.$ref]; 150 + model.$refs = [...model.$refs, decodeURIComponent(definition.items.$ref)]; 151 151 model.base = arrayItems.base; 152 152 model.export = 'array'; 153 153 model.imports = [...model.imports, ...arrayItems.imports];
+23 -60
packages/openapi-ts/src/utils/postprocess.ts
··· 1 - import type { Enum, Model, Operation, Service } from '../openApi'; 1 + import type { Model, Operation, Service } from '../openApi'; 2 2 import type { Client } from '../types/client'; 3 3 import { sort } from './sort'; 4 4 import { unique } from './unique'; ··· 16 16 }; 17 17 } 18 18 19 - /** 20 - * Post processes the model. 21 - * This will clean up any double imports or enum values. 22 - * @param model 23 - */ 24 - export function postProcessModel(model: Model): Model { 25 - return { 26 - ...model, 27 - enum: postProcessModelEnum(model), 28 - enums: postProcessModelEnums(model), 29 - imports: postProcessModelImports(model), 30 - }; 31 - } 19 + const postProcessModel = (model: Model): Model => ({ 20 + ...model, 21 + $refs: model.$refs.filter((value, index, arr) => unique(value, index, arr)), 22 + enum: model.enum.filter( 23 + (value, index, arr) => 24 + arr.findIndex((item) => item.value === value.value) === index, 25 + ), 26 + enums: model.enums.filter( 27 + (value, index, arr) => 28 + arr.findIndex((item) => item.name === value.name) === index, 29 + ), 30 + imports: model.imports 31 + .filter( 32 + (value, index, arr) => unique(value, index, arr) && value !== model.name, 33 + ) 34 + .sort(sort), 35 + }); 32 36 33 - /** 34 - * Set unique enum values for the model 35 - * @param model 36 - */ 37 - export function postProcessModelEnum(model: Model): Enum[] { 38 - return model.enum.filter( 39 - (property, index, arr) => 40 - arr.findIndex((item) => item.value === property.value) === index, 41 - ); 42 - } 43 - 44 - /** 45 - * Set unique enum values for the model 46 - * @param model The model that is post-processed 47 - */ 48 - export function postProcessModelEnums(model: Model): Model[] { 49 - return model.enums.filter( 50 - (property, index, arr) => 51 - arr.findIndex((item) => item.name === property.name) === index, 52 - ); 53 - } 54 - 55 - /** 56 - * Set unique imports, sorted by name 57 - * @param model The model that is post-processed 58 - */ 59 - export function postProcessModelImports(model: Model): string[] { 60 - return model.imports 61 - .filter(unique) 62 - .sort(sort) 63 - .filter((name) => model.name !== name); 64 - } 65 - 66 - export function postProcessService(service: Service): Service { 37 + const postProcessService = (service: Service): Service => { 67 38 const clone = { ...service }; 68 39 clone.operations = postProcessServiceOperations(clone); 69 40 clone.operations.forEach((operation) => { 70 41 clone.imports.push(...operation.imports); 71 42 }); 72 - clone.imports = postProcessServiceImports(clone); 43 + clone.imports = clone.imports.filter(unique).sort(sort); 73 44 return clone; 74 - } 75 - 76 - /** 77 - * Set unique imports, sorted by name 78 - * @param service 79 - */ 80 - export function postProcessServiceImports(service: Service): string[] { 81 - return service.imports.filter(unique).sort(sort); 82 - } 45 + }; 83 46 84 - export function postProcessServiceOperations(service: Service): Operation[] { 47 + const postProcessServiceOperations = (service: Service): Operation[] => { 85 48 const names = new Map<string, number>(); 86 49 87 50 return service.operations.map((operation) => { ··· 104 67 105 68 return clone; 106 69 }); 107 - } 70 + };
+23
packages/openapi-ts/src/utils/write/client.ts
··· 11 11 import { processIndex } from './index'; 12 12 import { processSchemas } from './schemas'; 13 13 import { processServices } from './services'; 14 + import { processResponseTransformers } from './transformers'; 14 15 import { processTypes } from './types'; 15 16 16 17 /** ··· 69 70 }); 70 71 } 71 72 73 + // schemas 72 74 await processSchemas({ file: files.schemas, openApi }); 75 + 76 + // types 73 77 await processTypes({ client, files }); 78 + 79 + // transformers 80 + if ( 81 + files.services && 82 + client.services.length && 83 + config.types.dates === 'types+transform' 84 + ) { 85 + await processResponseTransformers({ 86 + client, 87 + onNode: (node) => { 88 + files.types?.add(node); 89 + }, 90 + onRemoveNode: () => { 91 + files.types?.removeNode(); 92 + }, 93 + }); 94 + } 95 + 96 + // services 74 97 await processServices({ client, files }); 75 98 76 99 // deprecated files
+69 -47
packages/openapi-ts/src/utils/write/services.ts
··· 20 20 import { escapeComment, escapeName } from '../escape'; 21 21 import { transformServiceName } from '../transform'; 22 22 import { unique } from '../unique'; 23 - import { uniqueTypeName } from './type'; 23 + import { setUniqueTypeName } from './type'; 24 24 25 25 type OnNode = (node: Node) => void; 26 26 type OnImport = (name: string) => void; ··· 28 28 const generateImport = ({ 29 29 meta, 30 30 onImport, 31 - ...uniqueTypeNameArgs 32 - }: Pick<Parameters<typeof uniqueTypeName>[0], 'client' | 'nameTransformer'> & 31 + ...setUniqueTypeNameArgs 32 + }: Pick<Parameters<typeof setUniqueTypeName>[0], 'client' | 'nameTransformer'> & 33 33 Pick<Model, 'meta'> & { 34 34 onImport: OnImport; 35 35 }) => { ··· 38 38 return; 39 39 } 40 40 41 - const { name } = uniqueTypeName({ meta, ...uniqueTypeNameArgs }); 41 + const { name } = setUniqueTypeName({ meta, ...setUniqueTypeNameArgs }); 42 42 if (name) { 43 43 onImport(name); 44 44 } 45 45 }; 46 46 47 + export const modelResponseTransformerTypeName = (name: string) => 48 + `${name}ModelResponseTransformer`; 49 + 47 50 export const operationDataTypeName = (name: string) => 48 51 `${camelcase(name, { pascalCase: true })}Data`; 49 52 50 53 export const operationErrorTypeName = (name: string) => 51 54 `${camelcase(name, { pascalCase: true })}Error`; 55 + 56 + // operation response type ends with "Response", it's enough to append "Transformer" 57 + export const operationResponseTransformerTypeName = (name: string) => 58 + `${name}Transformer`; 52 59 53 60 export const operationResponseTypeName = (name: string) => 54 61 `${camelcase(name, { pascalCase: true })}Response`; ··· 59 66 ): FunctionParameter[] => { 60 67 const config = getConfig(); 61 68 62 - const { name: importedType } = uniqueTypeName({ 69 + const { name: importedType } = setUniqueTypeName({ 63 70 client, 64 71 meta: { 65 72 // TODO: this should be exact ref to operation for consistency, ··· 129 136 // can't remove this logic without removing request/name config 130 137 // as it complicates things 131 138 if (operation.results.length) { 132 - const { name: importedType } = uniqueTypeName({ 139 + const { name: importedType } = setUniqueTypeName({ 133 140 client, 134 141 meta: { 135 142 // TODO: this should be exact ref to operation for consistency, ··· 203 210 const toRequestOptions = ( 204 211 client: Client, 205 212 operation: Operation, 213 + onImport: OnImport, 206 214 onClientImport: OnImport | undefined, 207 - responseType: string, 208 215 ) => { 209 216 const config = getConfig(); 210 217 211 - const hasResponseTransformer = client.types[responseType]?.['hasTransformer']; 212 - const responseTransformerName = responseType; 218 + const operationName = operationResponseTypeName(operation.name); 219 + const { name: responseTransformerName } = setUniqueTypeName({ 220 + client, 221 + meta: { 222 + $ref: `transformers/${operationName}`, 223 + name: operationName, 224 + }, 225 + nameTransformer: operationResponseTransformerTypeName, 226 + }); 227 + 228 + if (responseTransformerName) { 229 + onImport(responseTransformerName); 230 + } 213 231 214 232 if (isStandaloneClient(config)) { 215 233 let obj: ObjectValue[] = [ ··· 247 265 }, 248 266 ]; 249 267 250 - if (hasResponseTransformer) { 251 - obj.push({ 252 - key: 'responseTransformer', 253 - value: responseTransformerName, 254 - }); 268 + if (responseTransformerName) { 269 + obj = [ 270 + ...obj, 271 + { 272 + key: 'responseTransformer', 273 + value: responseTransformerName, 274 + }, 275 + ]; 255 276 } 256 277 257 278 return compiler.types.object({ ··· 327 348 obj.responseHeader = operation.responseHeader; 328 349 } 329 350 330 - if (hasResponseTransformer) { 331 - obj.responseTransformer = `${responseType}`; 351 + if (responseTransformerName) { 352 + obj.responseTransformer = responseTransformerName; 332 353 } 333 354 334 355 if (operation.errors.length) { ··· 357 378 const toOperationStatements = ( 358 379 client: Client, 359 380 operation: Operation, 381 + onImport: OnImport, 360 382 onClientImport?: OnImport, 361 383 ) => { 362 384 const config = getConfig(); 363 385 364 - const responseType = operation.results.length 365 - ? uniqueTypeName({ 366 - client, 367 - meta: { 368 - // TODO: this should be exact ref to operation for consistency, 369 - // but name should work too as operation ID is unique 370 - $ref: operation.name, 371 - name: operation.name, 372 - }, 373 - nameTransformer: operationResponseTypeName, 374 - }).name 375 - : 'void'; 376 - 377 - const options = toRequestOptions( 378 - client, 379 - operation, 380 - onClientImport, 381 - responseType, 382 - ); 386 + const options = toRequestOptions(client, operation, onImport, onClientImport); 383 387 384 388 if (isStandaloneClient(config)) { 385 - const errorType = uniqueTypeName({ 389 + const errorType = setUniqueTypeName({ 386 390 client, 387 391 meta: { 388 392 // TODO: this should be exact ref to operation for consistency, ··· 392 396 }, 393 397 nameTransformer: operationErrorTypeName, 394 398 }).name; 395 - 399 + const responseType = operation.results.length 400 + ? setUniqueTypeName({ 401 + client, 402 + meta: { 403 + // TODO: this should be exact ref to operation for consistency, 404 + // but name should work too as operation ID is unique 405 + $ref: operation.name, 406 + name: operation.name, 407 + }, 408 + nameTransformer: operationResponseTypeName, 409 + }).name 410 + : 'void'; 396 411 return [ 397 412 compiler.return.functionCall({ 398 413 args: [options], ··· 498 513 returnType: isStandalone 499 514 ? undefined 500 515 : toOperationReturnType(client, operation), 501 - statements: toOperationStatements(client, operation, onClientImport), 516 + statements: toOperationStatements( 517 + client, 518 + operation, 519 + onImport, 520 + onClientImport, 521 + ), 502 522 }); 503 523 const statement = compiler.export.const({ 504 524 comment: toOperationComment(operation), ··· 522 542 returnType: isStandalone 523 543 ? undefined 524 544 : toOperationReturnType(client, operation), 525 - statements: toOperationStatements(client, operation, onClientImport), 545 + statements: toOperationStatements( 546 + client, 547 + operation, 548 + onImport, 549 + onClientImport, 550 + ), 526 551 }); 527 552 return node; 528 553 }); ··· 653 678 654 679 // Import all models required by the services. 655 680 if (files.types && !files.types.isEmpty()) { 656 - const importedTypes = imports.filter(unique).map((name) => { 657 - const hasTransformer = client.types[name]?.hasTransformer === true; 658 - 659 - return { 660 - asType: !hasTransformer, 661 - name, 662 - }; 663 - }); 681 + const importedTypes = imports.filter(unique).map((name) => ({ 682 + // this detection could be done safer, but it shouldn't cause any issues 683 + asType: !name.endsWith('Transformer'), 684 + name, 685 + })); 664 686 files.services?.addImport(importedTypes, `./${files.types.getName(false)}`); 665 687 } 666 688 };
+328
packages/openapi-ts/src/utils/write/transformers.ts
··· 1 + import ts from 'typescript'; 2 + 3 + import { compiler } from '../../compiler'; 4 + import type { 5 + ModelMeta, 6 + OperationResponse, 7 + } from '../../openApi/common/interfaces/client'; 8 + import { getSuccessResponses } from '../../openApi/common/parser/operation'; 9 + import { getConfig } from '../config'; 10 + import { 11 + modelResponseTransformerTypeName, 12 + operationResponseTransformerTypeName, 13 + operationResponseTypeName, 14 + } from './services'; 15 + import { unsetUniqueTypeName } from './type'; 16 + import { generateType, type TypesProps } from './types'; 17 + 18 + interface ModelProps extends TypesProps { 19 + path: Array<string>; 20 + meta?: ModelMeta; 21 + } 22 + 23 + const dataVariableName = 'data'; 24 + 25 + const isVoidResponse = (response: OperationResponse) => 26 + response.base === 'unknown' && 27 + response.export === 'generic' && 28 + response.type === 'unknown'; 29 + 30 + const getRefModels = ({ 31 + client, 32 + model, 33 + }: Pick<TypesProps, 'client' | 'model'>) => { 34 + const refModels = model.$refs.map((ref) => { 35 + const refModel = client.models.find((model) => model.meta?.$ref === ref); 36 + if (!refModel) { 37 + throw new Error( 38 + `Ref ${ref} could not be found. Transformers cannot be generated without having access to all refs.`, 39 + ); 40 + } 41 + return refModel; 42 + }); 43 + return refModels; 44 + }; 45 + 46 + const ensureModelResponseTransformerExists = ( 47 + props: Omit<ModelProps, 'path'>, 48 + ) => { 49 + const modelName = props.model.meta!.name; 50 + 51 + const { name } = generateType({ 52 + ...props, 53 + meta: { 54 + $ref: `transformers/${modelName}`, 55 + name: modelName, 56 + }, 57 + nameTransformer: modelResponseTransformerTypeName, 58 + onCreated: (name) => { 59 + const statements = processModel({ 60 + ...props, 61 + meta: { 62 + $ref: `transformers/${modelName}`, 63 + name, 64 + }, 65 + path: [dataVariableName], 66 + }); 67 + generateResponseTransformer({ 68 + ...props, 69 + name, 70 + statements, 71 + }); 72 + }, 73 + type: `(${dataVariableName}: any) => ${modelName}`, 74 + }); 75 + 76 + const result = { 77 + created: Boolean(props.client.types[name]), 78 + name, 79 + }; 80 + return result; 81 + }; 82 + 83 + const processArray = (props: ModelProps) => { 84 + const { model } = props; 85 + const refModels = getRefModels(props); 86 + 87 + if (refModels.length === 1) { 88 + const { created, name: nameModelResponseTransformer } = 89 + ensureModelResponseTransformerExists({ ...props, model: refModels[0] }); 90 + 91 + if (!created) { 92 + return []; 93 + } 94 + 95 + return [ 96 + compiler.transform.arrayTransformMutation({ 97 + path: props.path, 98 + transformerName: nameModelResponseTransformer, 99 + }), 100 + ]; 101 + } 102 + 103 + if (model.format === 'date' || model.format === 'date-time') { 104 + return [ 105 + compiler.transform.mapArray({ 106 + path: props.path, 107 + transformExpression: compiler.transform.newDate({ 108 + parameterName: 'item', 109 + }), 110 + }), 111 + ]; 112 + } 113 + 114 + // Not transform for this type 115 + return []; 116 + }; 117 + 118 + const processProperty = (props: ModelProps) => { 119 + const { model } = props; 120 + const path = [...props.path, model.name]; 121 + 122 + if ( 123 + model.type === 'string' && 124 + model.export !== 'array' && 125 + (model.format === 'date-time' || model.format === 'date') 126 + ) { 127 + return [compiler.transform.dateTransformMutation({ path })]; 128 + } 129 + 130 + // otherwise we recurse in case it's an object/array, and if it's not that will just bail with [] 131 + return processModel({ 132 + ...props, 133 + model, 134 + path, 135 + }); 136 + }; 137 + 138 + const processModel = (props: ModelProps): ts.Statement[] => { 139 + const { model } = props; 140 + 141 + switch (model.export) { 142 + case 'array': 143 + return processArray(props); 144 + case 'interface': 145 + return model.properties.flatMap((property) => 146 + processProperty({ ...props, model: property }), 147 + ); 148 + case 'reference': { 149 + if (model.$refs.length !== 1) { 150 + return []; 151 + } 152 + const refModels = getRefModels(props); 153 + 154 + const { created, name: nameModelResponseTransformer } = 155 + ensureModelResponseTransformerExists({ ...props, model: refModels[0] }); 156 + 157 + if (!created) { 158 + return []; 159 + } 160 + 161 + return model.in === 'response' 162 + ? [ 163 + compiler.convert.expressionToStatement({ 164 + expression: compiler.function.call({ 165 + functionName: nameModelResponseTransformer, 166 + parameters: [dataVariableName], 167 + }), 168 + }), 169 + ] 170 + : compiler.transform.transformItem({ 171 + path: props.path, 172 + transformerName: nameModelResponseTransformer, 173 + }); 174 + } 175 + // unsupported 176 + default: 177 + return []; 178 + } 179 + }; 180 + 181 + const generateResponseTransformer = ({ 182 + client, 183 + name, 184 + onNode, 185 + onRemoveNode, 186 + statements, 187 + }: Pick<TypesProps, 'client' | 'onNode' | 'onRemoveNode'> & { 188 + name: string; 189 + statements: Array<ts.Statement>; 190 + }) => { 191 + const result = { 192 + created: false, 193 + name, 194 + }; 195 + 196 + if (!statements.length) { 197 + // clean up created type for response transformer if it turns out 198 + // the transformer was never generated 199 + unsetUniqueTypeName({ 200 + client, 201 + name, 202 + }); 203 + onRemoveNode?.(); 204 + return result; 205 + } 206 + 207 + const expression = compiler.types.function({ 208 + multiLine: true, 209 + parameters: [ 210 + { 211 + name: dataVariableName, 212 + }, 213 + ], 214 + statements: [ 215 + ...statements, 216 + compiler.return.statement({ 217 + expression: ts.factory.createIdentifier(dataVariableName), 218 + }), 219 + ], 220 + }); 221 + const statement = compiler.export.const({ 222 + expression, 223 + name, 224 + typeName: name, 225 + }); 226 + onNode(statement); 227 + 228 + return { 229 + created: true, 230 + name, 231 + }; 232 + }; 233 + 234 + export const processResponseTransformers = async ({ 235 + client, 236 + onNode, 237 + onRemoveNode, 238 + }: Pick<TypesProps, 'client' | 'onNode' | 'onRemoveNode'>) => { 239 + const config = getConfig(); 240 + 241 + for (const service of client.services) { 242 + for (const operation of service.operations) { 243 + const hasRes = operation.results.length; 244 + 245 + if (!hasRes) { 246 + continue; 247 + } 248 + 249 + const responses = getSuccessResponses(operation.results); 250 + 251 + const nonVoidResponses = responses.filter( 252 + (response) => !isVoidResponse(response), 253 + ); 254 + 255 + if (!nonVoidResponses.length) { 256 + continue; 257 + } 258 + 259 + if (nonVoidResponses.length > 1) { 260 + if (config.debug) { 261 + console.warn( 262 + `⚠️ Transformers warning: route ${operation.method} ${operation.path} has ${responses.length} success responses. This is currently not handled and we will not generate a response transformer. Please open an issue if you'd like this feature https://github.com/hey-api/openapi-ts/issues`, 263 + ); 264 + } 265 + continue; 266 + } 267 + 268 + const name = operationResponseTypeName(operation.name); 269 + generateType({ 270 + client, 271 + meta: { 272 + $ref: `transformers/${name}`, 273 + name, 274 + }, 275 + nameTransformer: operationResponseTransformerTypeName, 276 + onCreated: (nameCreated) => { 277 + const statements = 278 + responses.length > 1 279 + ? responses.flatMap((response) => { 280 + const statements = processModel({ 281 + client, 282 + meta: { 283 + $ref: `transformers/${name}`, 284 + name, 285 + }, 286 + model: response, 287 + onNode, 288 + onRemoveNode, 289 + path: [dataVariableName], 290 + }); 291 + 292 + // assume unprocessed responses are void 293 + if (!statements.length) { 294 + return []; 295 + } 296 + 297 + return [ 298 + compiler.logic.if({ 299 + expression: compiler.logic.safeAccess(['data']), 300 + thenStatement: ts.factory.createBlock(statements), 301 + }), 302 + ]; 303 + }) 304 + : processModel({ 305 + client, 306 + meta: { 307 + $ref: `transformers/${name}`, 308 + name, 309 + }, 310 + model: operation.results[0], 311 + onNode, 312 + onRemoveNode, 313 + path: [dataVariableName], 314 + }); 315 + generateResponseTransformer({ 316 + client, 317 + name: nameCreated, 318 + onNode, 319 + onRemoveNode, 320 + statements, 321 + }); 322 + }, 323 + onNode, 324 + type: `(${dataVariableName}: any) => ${name}`, 325 + }); 326 + } 327 + } 328 + };
-145
packages/openapi-ts/src/utils/write/transforms.ts
··· 1 - import ts from 'typescript'; 2 - 3 - import { compiler } from '../../compiler'; 4 - import type { Model } from '../../openApi'; 5 - import type { Client } from '../../types/client'; 6 - import { getConfig } from '../config'; 7 - import { processModel } from './types'; 8 - 9 - type OnNode = (node: ts.Node) => void; 10 - 11 - export const generateTransform = ( 12 - client: Client, 13 - model: Model, 14 - onNode: OnNode, 15 - ) => { 16 - const config = getConfig(); 17 - if (config.types.dates === 'types+transform') { 18 - if (model.meta?.hasTransformer) { 19 - // Transform already created (maybe the model is used in other models) so we just bail here 20 - return; 21 - } 22 - 23 - const generateForProperty = (rootPath: string[], property: Model) => { 24 - const localPath = [...rootPath, property.name]; 25 - 26 - if ( 27 - property.type === 'string' && 28 - property.export !== 'array' && 29 - (property.format === 'date-time' || property.format === 'date') 30 - ) { 31 - return [ 32 - compiler.transform.dateTransformMutation({ 33 - path: localPath, 34 - }), 35 - ]; 36 - } else { 37 - // otherwise we recurse in case it's an object/array, and if it's not that will just bail with [] 38 - return generateForModel(localPath, property); 39 - } 40 - }; 41 - 42 - function generateForArray( 43 - localPath: string[], 44 - localModel: Model, 45 - refModels: Model[], 46 - ) { 47 - if (localModel.export !== 'array') { 48 - throw new Error( 49 - 'generateForArray should only be called with array models', 50 - ); 51 - } 52 - 53 - if (refModels.length === 1) { 54 - const refModel = refModels[0]; 55 - 56 - if (client.types[refModel.meta!.name].hasTransformer) { 57 - return [ 58 - compiler.transform.arrayTransformMutation({ 59 - path: localPath, 60 - transformer: refModel.meta!.name, 61 - }), 62 - ]; 63 - } else { 64 - // We do not currently support union types for transforms since discriminating them is a challenge 65 - return []; 66 - } 67 - } 68 - 69 - if (localModel.format === 'date' || localModel.format === 'date-time') { 70 - return [ 71 - compiler.transform.mapArray({ 72 - path: localPath, 73 - transformExpression: compiler.transform.newDate({ 74 - parameterName: 'item', 75 - }), 76 - }), 77 - ]; 78 - } 79 - 80 - // Not transform for this type 81 - return []; 82 - } 83 - 84 - function generateForReference(localPath: string[], refModels: Model[]) { 85 - if ( 86 - refModels.length !== 1 || 87 - client.types[refModels[0].meta!.name].hasTransformer !== true 88 - ) { 89 - return []; 90 - } 91 - 92 - return compiler.transform.transformItem({ 93 - path: localPath, 94 - transformer: refModels[0].meta!.name, 95 - }); 96 - } 97 - 98 - function generateForModel( 99 - localPath: string[], 100 - localModel: Model, 101 - ): ts.Statement[] { 102 - // We pre-transform refs (if any) so that they can be referenced in this transform 103 - const refModels = localModel.$refs.map((ref) => { 104 - const refModel = client.models.find((m) => m.meta!.$ref === ref); 105 - if (!refModel) { 106 - throw new Error( 107 - `Model ${ref} could not be founded when building ref transform`, 108 - ); 109 - } 110 - 111 - // We have to jump the gun a bit here and get this pre-processed so any transformers can be consumed 112 - processModel(client, refModel, onNode); 113 - 114 - return refModel; 115 - }); 116 - 117 - switch (localModel.export) { 118 - case 'reference': 119 - return generateForReference(localPath, refModels); 120 - case 'interface': 121 - return localModel.properties.flatMap((property) => 122 - generateForProperty(localPath, property), 123 - ); 124 - case 'array': 125 - return generateForArray(localPath, localModel, refModels); 126 - 127 - default: 128 - // Unsupported 129 - return []; 130 - } 131 - } 132 - 133 - const transformStatements = generateForModel(['data'], model); 134 - if (transformStatements.length > 0) { 135 - const transformFunction = compiler.transform.transformMutationFunction({ 136 - modelName: model.meta!.name, 137 - statements: transformStatements, 138 - }); 139 - 140 - client.types[model.meta!.name].hasTransformer = true; 141 - 142 - onNode(transformFunction); 143 - } 144 - } 145 - };
+39 -6
packages/openapi-ts/src/utils/write/type.ts
··· 155 155 } 156 156 }; 157 157 158 - interface UniqueTypeNameResult { 158 + export interface SetUniqueTypeNameResult { 159 159 /** 160 160 * Did this function add a new property to the `client.types` object? 161 161 */ ··· 176 176 * value. In different contexts, a different strategy might be used. For 177 177 * example, slashes `/` are invalid in TypeScript identifiers, but okay in 178 178 * a JavaScript object key name. 179 - * @returns {UniqueTypeNameResult} 179 + * @returns {SetUniqueTypeNameResult} 180 180 */ 181 - export const uniqueTypeName = ({ 181 + export const setUniqueTypeName = ({ 182 182 client, 183 183 count = 1, 184 184 create = false, ··· 189 189 count?: number; 190 190 create?: boolean; 191 191 nameTransformer?: (value: string) => string; 192 - }): UniqueTypeNameResult => { 193 - let result: UniqueTypeNameResult = { 192 + }): SetUniqueTypeNameResult => { 193 + let result: SetUniqueTypeNameResult = { 194 194 created: false, 195 195 name: '', 196 196 }; ··· 216 216 name, 217 217 }; 218 218 } else { 219 - result = uniqueTypeName({ 219 + result = setUniqueTypeName({ 220 220 client, 221 221 count: count + 1, 222 222 create, ··· 226 226 } 227 227 return result; 228 228 }; 229 + 230 + export interface UnsetUniqueTypeNameResult { 231 + /** 232 + * Did this function delete a property from the `client.types` object? 233 + */ 234 + deleted: boolean; 235 + /** 236 + * Unique name removed from the `client.types` object. 237 + */ 238 + name: string; 239 + } 240 + 241 + export const unsetUniqueTypeName = ({ 242 + client, 243 + name, 244 + }: { 245 + client: Client; 246 + name: string; 247 + }): UnsetUniqueTypeNameResult => { 248 + let result: UnsetUniqueTypeNameResult = { 249 + deleted: false, 250 + name: '', 251 + }; 252 + if (!client.types[name]) { 253 + return result; 254 + } 255 + delete client.types[name]; 256 + result = { 257 + deleted: true, 258 + name, 259 + }; 260 + return result; 261 + };
+62 -82
packages/openapi-ts/src/utils/write/types.ts
··· 20 20 operationErrorTypeName, 21 21 operationResponseTypeName, 22 22 } from './services'; 23 - import { generateTransform } from './transforms'; 24 - import { toType, uniqueTypeName } from './type'; 23 + import { 24 + setUniqueTypeName, 25 + type SetUniqueTypeNameResult, 26 + toType, 27 + } from './type'; 25 28 26 - type OnNode = (node: Node) => void; 29 + export interface TypesProps { 30 + client: Client; 31 + model: Model; 32 + onNode: (node: Node) => void; 33 + onRemoveNode?: VoidFunction; 34 + } 27 35 28 36 const serviceExportedNamespace = () => '$OpenApiTs'; 29 37 30 - const emptyModel: Model = { 38 + export const emptyModel: Model = { 31 39 $refs: [], 32 40 base: '', 33 41 description: null, ··· 53 61 meta, 54 62 obj, 55 63 onNode, 56 - ...uniqueTypeNameArgs 64 + ...setUniqueTypeNameArgs 57 65 }: Omit<Parameters<typeof compiler.types.enum>[0], 'name'> & 58 - Pick<Parameters<typeof uniqueTypeName>[0], 'client' | 'nameTransformer'> & 59 - Pick<Model, 'meta'> & { 60 - onNode: OnNode; 61 - }) => { 66 + Pick<Parameters<typeof setUniqueTypeName>[0], 'client' | 'nameTransformer'> & 67 + Pick<Model, 'meta'> & 68 + Pick<TypesProps, 'onNode'>) => { 62 69 // generate types only for top-level models 63 70 if (!meta) { 64 71 return; 65 72 } 66 73 67 - const { created, name } = uniqueTypeName({ 74 + const { created, name } = setUniqueTypeName({ 68 75 create: true, 69 76 meta, 70 - ...uniqueTypeNameArgs, 77 + ...setUniqueTypeNameArgs, 71 78 }); 72 79 if (created) { 73 80 const node = compiler.types.enum({ ··· 80 87 } 81 88 }; 82 89 83 - const generateType = ({ 90 + export const generateType = ({ 84 91 comment, 85 92 meta, 86 93 onCreated, 87 94 onNode, 88 95 type, 89 - ...uniqueTypeNameArgs 96 + ...setUniqueTypeNameArgs 90 97 }: Omit<Parameters<typeof compiler.typedef.alias>[0], 'name'> & 91 - Pick<Parameters<typeof uniqueTypeName>[0], 'client' | 'nameTransformer'> & 92 - Pick<Model, 'meta'> & { 98 + Pick<Parameters<typeof setUniqueTypeName>[0], 'client' | 'nameTransformer'> & 99 + Pick<Model, 'meta'> & 100 + Pick<TypesProps, 'onNode'> & { 93 101 onCreated?: (name: string) => void; 94 - onNode: OnNode; 95 - }) => { 102 + }): SetUniqueTypeNameResult => { 96 103 // generate types only for top-level models 97 104 if (!meta) { 98 - return; 105 + return { 106 + created: false, 107 + name: '', 108 + }; 99 109 } 100 110 101 - const { created, name } = uniqueTypeName({ 111 + const result = setUniqueTypeName({ 102 112 create: true, 103 113 meta, 104 - ...uniqueTypeNameArgs, 114 + ...setUniqueTypeNameArgs, 105 115 }); 116 + const { created, name } = result; 106 117 if (created) { 107 118 const node = compiler.typedef.alias({ comment, name, type }); 108 119 onNode(node); 109 120 110 121 onCreated?.(name); 111 122 } 123 + return result; 112 124 }; 113 125 114 - const processComposition = (client: Client, model: Model, onNode: OnNode) => { 115 - processType(client, model, onNode); 116 - model.enums.forEach((enumerator) => processEnum(client, enumerator, onNode)); 126 + const processComposition = (props: TypesProps) => { 127 + processType(props); 128 + props.model.enums.forEach((enumerator) => 129 + processEnum({ ...props, model: enumerator }), 130 + ); 117 131 }; 118 132 119 - const processEnum = (client: Client, model: Model, onNode: OnNode) => { 133 + const processEnum = ({ client, model, onNode }: TypesProps) => { 120 134 const config = getConfig(); 121 135 122 136 const properties: Record<string | number, unknown> = {}; ··· 174 188 }); 175 189 }; 176 190 177 - const processType = (client: Client, model: Model, onNode: OnNode) => { 191 + const processType = ({ client, model, onNode }: TypesProps) => { 178 192 generateType({ 179 193 client, 180 194 comment: [ ··· 185 199 onNode, 186 200 type: toType(model), 187 201 }); 188 - 189 - generateTransform(client, model, onNode); 190 202 }; 191 203 192 - export const processModel = (client: Client, model: Model, onNode: OnNode) => { 193 - switch (model.export) { 204 + const processModel = (props: TypesProps) => { 205 + switch (props.model.export) { 194 206 case 'all-of': 195 207 case 'any-of': 196 208 case 'one-of': 197 209 case 'interface': 198 - return processComposition(client, model, onNode); 210 + return processComposition(props); 199 211 case 'enum': 200 - return processEnum(client, model, onNode); 212 + return processEnum(props); 201 213 default: 202 - return processType(client, model, onNode); 214 + return processType(props); 203 215 } 204 216 }; 205 217 ··· 215 227 216 228 type PathsMap = Record<string, PathMap>; 217 229 218 - const processServiceTypes = (client: Client, onNode: OnNode) => { 230 + const processServiceTypes = ({ 231 + client, 232 + onNode, 233 + }: Pick<TypesProps, 'client' | 'onNode'>) => { 219 234 const pathsMap: PathsMap = {}; 220 235 221 236 const config = getConfig(); 222 237 223 238 const isStandalone = isStandaloneClient(config); 224 239 225 - client.services.forEach((service) => { 226 - service.operations.forEach((operation) => { 240 + for (const service of client.services) { 241 + for (const operation of service.operations) { 227 242 const hasReq = operation.parameters.length; 228 243 const hasRes = operation.results.length; 229 244 const hasErr = operation.errors.length; 230 245 231 246 if (!hasReq && !hasRes && !hasErr) { 232 - return; 247 + continue; 233 248 } 234 249 235 250 if (!pathsMap[operation.path]) { ··· 336 351 } 337 352 338 353 if (Array.isArray(methodMap.res)) { 339 - return; 354 + continue; 340 355 } 341 356 342 357 operation.results.forEach((result) => { ··· 364 379 }), 365 380 }); 366 381 367 - if (config.types.dates === 'types+transform') { 368 - if (responses.length === 1) { 369 - const response = responses[0]; 370 - 371 - if (client.types[response.type]?.hasTransformer) { 372 - const name = operationResponseTypeName(operation.name); 373 - 374 - if (response.export === 'array') { 375 - const arrayTransformer = 376 - compiler.transform.responseArrayTransform({ 377 - name, 378 - transform: response.type, 379 - }); 380 - onNode(arrayTransformer); 381 - } else { 382 - const transformAlias = compiler.transform.alias({ 383 - existingName: response.type, 384 - name, 385 - }); 386 - 387 - onNode(transformAlias); 388 - } 389 - 390 - client.types[name].hasTransformer = true; 391 - } 392 - } else if (responses.length > 1) { 393 - console.log( 394 - '❗ Route', 395 - operation.method, 396 - operation.path, 397 - 'has more than 1 success response and will not currently have a transform generated. If you have a use case for this please open an issue https://github.com/hey-api/openapi-ts/issues', 398 - ); 399 - } 400 - } 401 - 402 382 if (isStandaloneClient(config)) { 403 383 const errorResults = getErrorResponses(operation.errors); 404 384 // create type export for operation error ··· 437 417 } 438 418 439 419 if (Array.isArray(methodMap.res)) { 440 - return; 420 + continue; 441 421 } 442 422 443 423 operation.errors.forEach((error) => { 444 424 methodMap.res![error.code] = error; 445 425 }); 446 426 } 447 - }); 448 - }); 427 + } 428 + } 449 429 450 430 const properties = Object.entries(pathsMap).map(([path, pathMap]) => { 451 431 const pathParameters = Object.entries(pathMap) ··· 456 436 457 437 if (methodMap.req) { 458 438 const operationName = methodMap.$ref!; 459 - const { name: base } = uniqueTypeName({ 439 + const { name: base } = setUniqueTypeName({ 460 440 client, 461 441 meta: { 462 442 // TODO: this should be exact ref to operation for consistency, ··· 540 520 client: Client; 541 521 files: Record<string, TypeScriptFile>; 542 522 }): Promise<void> => { 523 + const onNode: TypesProps['onNode'] = (node) => { 524 + files.types?.add(node); 525 + }; 526 + 543 527 for (const model of client.models) { 544 - processModel(client, model, (node) => { 545 - files.types?.add(node); 546 - }); 528 + processModel({ client, model, onNode }); 547 529 } 548 530 549 531 if (files.services && client.services.length) { 550 - processServiceTypes(client, (node) => { 551 - files.types?.add(node); 552 - }); 532 + processServiceTypes({ client, onNode }); 553 533 } 554 534 };
+16 -3
packages/openapi-ts/test/__snapshots__/test/generated/v3_angular_transform/services.gen.ts.snap
··· 5 5 import type { Observable } from 'rxjs'; 6 6 import { OpenAPI } from './core/OpenAPI'; 7 7 import { request as __request } from './core/request'; 8 - import { ModelWithDatesResponse, ModelWithDatesArrayResponse, type ArrayOfDatesResponse, type DateResponse, type MultipleResponsesResponse } from './types.gen'; 8 + import { type ParentModelWithDatesResponse, type ModelWithDatesResponse, type ModelWithDatesArrayResponse, type ArrayOfDatesResponse, type DateResponse, type MultipleResponsesResponse, ParentModelWithDatesResponseTransformer, ModelWithDatesResponseTransformer, ModelWithDatesArrayResponseTransformer } from './types.gen'; 9 9 10 10 @Injectable({ 11 11 providedIn: 'root' ··· 14 14 constructor(public readonly http: HttpClient) { } 15 15 16 16 /** 17 + * @returns ParentModelWithDates Success 18 + * @returns unknown Success 19 + * @throws ApiError 20 + */ 21 + public parentModelWithDates(): Observable<ParentModelWithDatesResponse> { 22 + return __request(OpenAPI, this.http, { 23 + method: 'POST', 24 + url: '/api/model-with-dates', 25 + responseTransformer: ParentModelWithDatesResponseTransformer 26 + }); 27 + } 28 + 29 + /** 17 30 * @returns ModelWithDates Success 18 31 * @throws ApiError 19 32 */ ··· 21 34 return __request(OpenAPI, this.http, { 22 35 method: 'PUT', 23 36 url: '/api/model-with-dates', 24 - responseTransformer: ModelWithDatesResponse 37 + responseTransformer: ModelWithDatesResponseTransformer 25 38 }); 26 39 } 27 40 ··· 33 46 return __request(OpenAPI, this.http, { 34 47 method: 'PUT', 35 48 url: '/api/model-with-dates-array', 36 - responseTransformer: ModelWithDatesArrayResponse 49 + responseTransformer: ModelWithDatesArrayResponseTransformer 37 50 }); 38 51 } 39 52
+68 -34
packages/openapi-ts/test/__snapshots__/test/generated/v3_angular_transform/types.gen.ts.snap
··· 20 20 readonly expires?: Date; 21 21 }; 22 22 23 - export function ModelWithDates(data: any): ModelWithDates { 24 - if (data?.modified) { 25 - data.modified = new Date(data.modified); 26 - } 27 - if (data?.expires) { 28 - data.expires = new Date(data.expires); 29 - } 30 - return data; 31 - } 32 - 33 23 /** 34 24 * This is a model that contains a some dates and arrays 35 25 */ ··· 44 34 strings?: Array<(string)>; 45 35 }; 46 36 47 - export function ParentModelWithDates(data: any): ParentModelWithDates { 48 - if (data?.modified) { 49 - data.modified = new Date(data.modified); 50 - } 51 - if (Array.isArray(data?.items)) { 52 - data.items.forEach(ModelWithDates); 53 - } 54 - if (data?.item) { 55 - ModelWithDates(data.item); 56 - } 57 - if (Array.isArray(data?.dates)) { 58 - data.dates = data.dates.map(item => new Date(item)); 59 - } 60 - return data; 61 - } 37 + export type ParentModelWithDatesResponse = ParentModelWithDates | unknown; 62 38 63 39 export type ModelWithDatesResponse = ModelWithDates; 64 40 65 - export const ModelWithDatesResponse = ModelWithDates; 66 - 67 41 export type ModelWithDatesArrayResponse = Array<ModelWithDates>; 68 42 69 - export const ModelWithDatesArrayResponse = (data: any) => { 70 - if (Array.isArray(data)) { 71 - data.forEach(ModelWithDates); 72 - } 73 - return data; 74 - }; 75 - 76 43 export type ArrayOfDatesResponse = Array<(Date)>; 77 44 78 45 export type DateResponse = Date; ··· 81 48 82 49 export type $OpenApiTs = { 83 50 '/api/model-with-dates': { 51 + post: { 52 + res: { 53 + /** 54 + * Success 55 + */ 56 + 200: ParentModelWithDates; 57 + /** 58 + * Success 59 + */ 60 + 201: unknown; 61 + }; 62 + }; 84 63 put: { 85 64 res: { 86 65 /** ··· 134 113 }; 135 114 }; 136 115 }; 116 + }; 117 + 118 + export type ParentModelWithDatesResponseTransformer = (data: any) => ParentModelWithDatesResponse; 119 + 120 + export type ParentModelWithDatesModelResponseTransformer = (data: any) => ParentModelWithDates; 121 + 122 + export type ModelWithDatesModelResponseTransformer = (data: any) => ModelWithDates; 123 + 124 + export const ModelWithDatesModelResponseTransformer: ModelWithDatesModelResponseTransformer = data => { 125 + if (data?.modified) { 126 + data.modified = new Date(data.modified); 127 + } 128 + if (data?.expires) { 129 + data.expires = new Date(data.expires); 130 + } 131 + return data; 132 + }; 133 + 134 + export const ParentModelWithDatesModelResponseTransformer: ParentModelWithDatesModelResponseTransformer = data => { 135 + if (data?.modified) { 136 + data.modified = new Date(data.modified); 137 + } 138 + if (Array.isArray(data?.items)) { 139 + data.items.forEach(ModelWithDatesModelResponseTransformer); 140 + } 141 + if (data?.item) { 142 + ModelWithDatesModelResponseTransformer(data.item); 143 + } 144 + if (Array.isArray(data?.dates)) { 145 + data.dates = data.dates.map(item => new Date(item)); 146 + } 147 + return data; 148 + }; 149 + 150 + export const ParentModelWithDatesResponseTransformer: ParentModelWithDatesResponseTransformer = data => { 151 + if (data) { 152 + ParentModelWithDatesModelResponseTransformer(data); 153 + } 154 + return data; 155 + }; 156 + 157 + export type ModelWithDatesResponseTransformer = (data: any) => ModelWithDatesResponse; 158 + 159 + export const ModelWithDatesResponseTransformer: ModelWithDatesResponseTransformer = data => { 160 + ModelWithDatesModelResponseTransformer(data); 161 + return data; 162 + }; 163 + 164 + export type ModelWithDatesArrayResponseTransformer = (data: any) => ModelWithDatesArrayResponse; 165 + 166 + export const ModelWithDatesArrayResponseTransformer: ModelWithDatesArrayResponseTransformer = data => { 167 + if (Array.isArray(data)) { 168 + data.forEach(ModelWithDatesModelResponseTransformer); 169 + } 170 + return data; 137 171 };
+16 -3
packages/openapi-ts/test/__snapshots__/test/generated/v3_axios_transform/services.gen.ts.snap
··· 3 3 import type { CancelablePromise } from './core/CancelablePromise'; 4 4 import { OpenAPI } from './core/OpenAPI'; 5 5 import { request as __request } from './core/request'; 6 - import { ModelWithDatesResponse, ModelWithDatesArrayResponse, type ArrayOfDatesResponse, type DateResponse, type MultipleResponsesResponse } from './types.gen'; 6 + import { type ParentModelWithDatesResponse, type ModelWithDatesResponse, type ModelWithDatesArrayResponse, type ArrayOfDatesResponse, type DateResponse, type MultipleResponsesResponse, ParentModelWithDatesResponseTransformer, ModelWithDatesResponseTransformer, ModelWithDatesArrayResponseTransformer } from './types.gen'; 7 7 8 8 export class DefaultService { 9 9 /** 10 + * @returns ParentModelWithDates Success 11 + * @returns unknown Success 12 + * @throws ApiError 13 + */ 14 + public static parentModelWithDates(): CancelablePromise<ParentModelWithDatesResponse> { 15 + return __request(OpenAPI, { 16 + method: 'POST', 17 + url: '/api/model-with-dates', 18 + responseTransformer: ParentModelWithDatesResponseTransformer 19 + }); 20 + } 21 + 22 + /** 10 23 * @returns ModelWithDates Success 11 24 * @throws ApiError 12 25 */ ··· 14 27 return __request(OpenAPI, { 15 28 method: 'PUT', 16 29 url: '/api/model-with-dates', 17 - responseTransformer: ModelWithDatesResponse 30 + responseTransformer: ModelWithDatesResponseTransformer 18 31 }); 19 32 } 20 33 ··· 26 39 return __request(OpenAPI, { 27 40 method: 'PUT', 28 41 url: '/api/model-with-dates-array', 29 - responseTransformer: ModelWithDatesArrayResponse 42 + responseTransformer: ModelWithDatesArrayResponseTransformer 30 43 }); 31 44 } 32 45
+68 -34
packages/openapi-ts/test/__snapshots__/test/generated/v3_axios_transform/types.gen.ts.snap
··· 20 20 readonly expires?: Date; 21 21 }; 22 22 23 - export function ModelWithDates(data: any): ModelWithDates { 24 - if (data?.modified) { 25 - data.modified = new Date(data.modified); 26 - } 27 - if (data?.expires) { 28 - data.expires = new Date(data.expires); 29 - } 30 - return data; 31 - } 32 - 33 23 /** 34 24 * This is a model that contains a some dates and arrays 35 25 */ ··· 44 34 strings?: Array<(string)>; 45 35 }; 46 36 47 - export function ParentModelWithDates(data: any): ParentModelWithDates { 48 - if (data?.modified) { 49 - data.modified = new Date(data.modified); 50 - } 51 - if (Array.isArray(data?.items)) { 52 - data.items.forEach(ModelWithDates); 53 - } 54 - if (data?.item) { 55 - ModelWithDates(data.item); 56 - } 57 - if (Array.isArray(data?.dates)) { 58 - data.dates = data.dates.map(item => new Date(item)); 59 - } 60 - return data; 61 - } 37 + export type ParentModelWithDatesResponse = ParentModelWithDates | unknown; 62 38 63 39 export type ModelWithDatesResponse = ModelWithDates; 64 40 65 - export const ModelWithDatesResponse = ModelWithDates; 66 - 67 41 export type ModelWithDatesArrayResponse = Array<ModelWithDates>; 68 42 69 - export const ModelWithDatesArrayResponse = (data: any) => { 70 - if (Array.isArray(data)) { 71 - data.forEach(ModelWithDates); 72 - } 73 - return data; 74 - }; 75 - 76 43 export type ArrayOfDatesResponse = Array<(Date)>; 77 44 78 45 export type DateResponse = Date; ··· 81 48 82 49 export type $OpenApiTs = { 83 50 '/api/model-with-dates': { 51 + post: { 52 + res: { 53 + /** 54 + * Success 55 + */ 56 + 200: ParentModelWithDates; 57 + /** 58 + * Success 59 + */ 60 + 201: unknown; 61 + }; 62 + }; 84 63 put: { 85 64 res: { 86 65 /** ··· 134 113 }; 135 114 }; 136 115 }; 116 + }; 117 + 118 + export type ParentModelWithDatesResponseTransformer = (data: any) => ParentModelWithDatesResponse; 119 + 120 + export type ParentModelWithDatesModelResponseTransformer = (data: any) => ParentModelWithDates; 121 + 122 + export type ModelWithDatesModelResponseTransformer = (data: any) => ModelWithDates; 123 + 124 + export const ModelWithDatesModelResponseTransformer: ModelWithDatesModelResponseTransformer = data => { 125 + if (data?.modified) { 126 + data.modified = new Date(data.modified); 127 + } 128 + if (data?.expires) { 129 + data.expires = new Date(data.expires); 130 + } 131 + return data; 132 + }; 133 + 134 + export const ParentModelWithDatesModelResponseTransformer: ParentModelWithDatesModelResponseTransformer = data => { 135 + if (data?.modified) { 136 + data.modified = new Date(data.modified); 137 + } 138 + if (Array.isArray(data?.items)) { 139 + data.items.forEach(ModelWithDatesModelResponseTransformer); 140 + } 141 + if (data?.item) { 142 + ModelWithDatesModelResponseTransformer(data.item); 143 + } 144 + if (Array.isArray(data?.dates)) { 145 + data.dates = data.dates.map(item => new Date(item)); 146 + } 147 + return data; 148 + }; 149 + 150 + export const ParentModelWithDatesResponseTransformer: ParentModelWithDatesResponseTransformer = data => { 151 + if (data) { 152 + ParentModelWithDatesModelResponseTransformer(data); 153 + } 154 + return data; 155 + }; 156 + 157 + export type ModelWithDatesResponseTransformer = (data: any) => ModelWithDatesResponse; 158 + 159 + export const ModelWithDatesResponseTransformer: ModelWithDatesResponseTransformer = data => { 160 + ModelWithDatesModelResponseTransformer(data); 161 + return data; 162 + }; 163 + 164 + export type ModelWithDatesArrayResponseTransformer = (data: any) => ModelWithDatesArrayResponse; 165 + 166 + export const ModelWithDatesArrayResponseTransformer: ModelWithDatesArrayResponseTransformer = data => { 167 + if (Array.isArray(data)) { 168 + data.forEach(ModelWithDatesModelResponseTransformer); 169 + } 170 + return data; 137 171 };
+16 -3
packages/openapi-ts/test/__snapshots__/test/generated/v3_client_transform/services.gen.ts.snap
··· 2 2 3 3 import type { CancelablePromise } from './core/CancelablePromise'; 4 4 import type { BaseHttpRequest } from './core/BaseHttpRequest'; 5 - import { ModelWithDatesResponse, ModelWithDatesArrayResponse, type ArrayOfDatesResponse, type DateResponse, type MultipleResponsesResponse } from './types.gen'; 5 + import { type ParentModelWithDatesResponse, type ModelWithDatesResponse, type ModelWithDatesArrayResponse, type ArrayOfDatesResponse, type DateResponse, type MultipleResponsesResponse, ParentModelWithDatesResponseTransformer, ModelWithDatesResponseTransformer, ModelWithDatesArrayResponseTransformer } from './types.gen'; 6 6 7 7 export class DefaultService { 8 8 constructor(public readonly httpRequest: BaseHttpRequest) { } 9 9 10 10 /** 11 + * @returns ParentModelWithDates Success 12 + * @returns unknown Success 13 + * @throws ApiError 14 + */ 15 + public parentModelWithDates(): CancelablePromise<ParentModelWithDatesResponse> { 16 + return this.httpRequest.request({ 17 + method: 'POST', 18 + url: '/api/model-with-dates', 19 + responseTransformer: ParentModelWithDatesResponseTransformer 20 + }); 21 + } 22 + 23 + /** 11 24 * @returns ModelWithDates Success 12 25 * @throws ApiError 13 26 */ ··· 15 28 return this.httpRequest.request({ 16 29 method: 'PUT', 17 30 url: '/api/model-with-dates', 18 - responseTransformer: ModelWithDatesResponse 31 + responseTransformer: ModelWithDatesResponseTransformer 19 32 }); 20 33 } 21 34 ··· 27 40 return this.httpRequest.request({ 28 41 method: 'PUT', 29 42 url: '/api/model-with-dates-array', 30 - responseTransformer: ModelWithDatesArrayResponse 43 + responseTransformer: ModelWithDatesArrayResponseTransformer 31 44 }); 32 45 } 33 46
+68 -34
packages/openapi-ts/test/__snapshots__/test/generated/v3_client_transform/types.gen.ts.snap
··· 20 20 readonly expires?: Date; 21 21 }; 22 22 23 - export function ModelWithDates(data: any): ModelWithDates { 24 - if (data?.modified) { 25 - data.modified = new Date(data.modified); 26 - } 27 - if (data?.expires) { 28 - data.expires = new Date(data.expires); 29 - } 30 - return data; 31 - } 32 - 33 23 /** 34 24 * This is a model that contains a some dates and arrays 35 25 */ ··· 44 34 strings?: Array<(string)>; 45 35 }; 46 36 47 - export function ParentModelWithDates(data: any): ParentModelWithDates { 48 - if (data?.modified) { 49 - data.modified = new Date(data.modified); 50 - } 51 - if (Array.isArray(data?.items)) { 52 - data.items.forEach(ModelWithDates); 53 - } 54 - if (data?.item) { 55 - ModelWithDates(data.item); 56 - } 57 - if (Array.isArray(data?.dates)) { 58 - data.dates = data.dates.map(item => new Date(item)); 59 - } 60 - return data; 61 - } 37 + export type ParentModelWithDatesResponse = ParentModelWithDates | unknown; 62 38 63 39 export type ModelWithDatesResponse = ModelWithDates; 64 40 65 - export const ModelWithDatesResponse = ModelWithDates; 66 - 67 41 export type ModelWithDatesArrayResponse = Array<ModelWithDates>; 68 42 69 - export const ModelWithDatesArrayResponse = (data: any) => { 70 - if (Array.isArray(data)) { 71 - data.forEach(ModelWithDates); 72 - } 73 - return data; 74 - }; 75 - 76 43 export type ArrayOfDatesResponse = Array<(Date)>; 77 44 78 45 export type DateResponse = Date; ··· 81 48 82 49 export type $OpenApiTs = { 83 50 '/api/model-with-dates': { 51 + post: { 52 + res: { 53 + /** 54 + * Success 55 + */ 56 + 200: ParentModelWithDates; 57 + /** 58 + * Success 59 + */ 60 + 201: unknown; 61 + }; 62 + }; 84 63 put: { 85 64 res: { 86 65 /** ··· 134 113 }; 135 114 }; 136 115 }; 116 + }; 117 + 118 + export type ParentModelWithDatesResponseTransformer = (data: any) => ParentModelWithDatesResponse; 119 + 120 + export type ParentModelWithDatesModelResponseTransformer = (data: any) => ParentModelWithDates; 121 + 122 + export type ModelWithDatesModelResponseTransformer = (data: any) => ModelWithDates; 123 + 124 + export const ModelWithDatesModelResponseTransformer: ModelWithDatesModelResponseTransformer = data => { 125 + if (data?.modified) { 126 + data.modified = new Date(data.modified); 127 + } 128 + if (data?.expires) { 129 + data.expires = new Date(data.expires); 130 + } 131 + return data; 132 + }; 133 + 134 + export const ParentModelWithDatesModelResponseTransformer: ParentModelWithDatesModelResponseTransformer = data => { 135 + if (data?.modified) { 136 + data.modified = new Date(data.modified); 137 + } 138 + if (Array.isArray(data?.items)) { 139 + data.items.forEach(ModelWithDatesModelResponseTransformer); 140 + } 141 + if (data?.item) { 142 + ModelWithDatesModelResponseTransformer(data.item); 143 + } 144 + if (Array.isArray(data?.dates)) { 145 + data.dates = data.dates.map(item => new Date(item)); 146 + } 147 + return data; 148 + }; 149 + 150 + export const ParentModelWithDatesResponseTransformer: ParentModelWithDatesResponseTransformer = data => { 151 + if (data) { 152 + ParentModelWithDatesModelResponseTransformer(data); 153 + } 154 + return data; 155 + }; 156 + 157 + export type ModelWithDatesResponseTransformer = (data: any) => ModelWithDatesResponse; 158 + 159 + export const ModelWithDatesResponseTransformer: ModelWithDatesResponseTransformer = data => { 160 + ModelWithDatesModelResponseTransformer(data); 161 + return data; 162 + }; 163 + 164 + export type ModelWithDatesArrayResponseTransformer = (data: any) => ModelWithDatesArrayResponse; 165 + 166 + export const ModelWithDatesArrayResponseTransformer: ModelWithDatesArrayResponseTransformer = data => { 167 + if (Array.isArray(data)) { 168 + data.forEach(ModelWithDatesModelResponseTransformer); 169 + } 170 + return data; 137 171 };
+9 -3
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios_transform/services.gen.ts.snap
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 3 import { client, type Options } from '@hey-api/client-axios'; 4 - import { type ModelWithDatesError, ModelWithDatesResponse, type ModelWithDatesArrayError, ModelWithDatesArrayResponse, type ArrayOfDatesError, type ArrayOfDatesResponse, type DateError, type DateResponse, type MultipleResponsesError, type MultipleResponsesResponse } from './types.gen'; 4 + import { type ParentModelWithDatesError, type ParentModelWithDatesResponse, type ModelWithDatesError, type ModelWithDatesResponse, type ModelWithDatesArrayError, type ModelWithDatesArrayResponse, type ArrayOfDatesError, type ArrayOfDatesResponse, type DateError, type DateResponse, type MultipleResponsesError, type MultipleResponsesResponse, ParentModelWithDatesResponseTransformer, ModelWithDatesResponseTransformer, ModelWithDatesArrayResponseTransformer } from './types.gen'; 5 + 6 + export const parentModelWithDates = (options?: Options) => { return (options?.client ?? client).post<ParentModelWithDatesResponse, ParentModelWithDatesError>({ 7 + ...options, 8 + url: '/api/model-with-dates', 9 + responseTransformer: ParentModelWithDatesResponseTransformer 10 + }); }; 5 11 6 12 export const modelWithDates = (options?: Options) => { return (options?.client ?? client).put<ModelWithDatesResponse, ModelWithDatesError>({ 7 13 ...options, 8 14 url: '/api/model-with-dates', 9 - responseTransformer: ModelWithDatesResponse 15 + responseTransformer: ModelWithDatesResponseTransformer 10 16 }); }; 11 17 12 18 export const modelWithDatesArray = (options?: Options) => { return (options?.client ?? client).put<ModelWithDatesArrayResponse, ModelWithDatesArrayError>({ 13 19 ...options, 14 20 url: '/api/model-with-dates-array', 15 - responseTransformer: ModelWithDatesArrayResponse 21 + responseTransformer: ModelWithDatesArrayResponseTransformer 16 22 }); }; 17 23 18 24 export const arrayOfDates = (options?: Options) => { return (options?.client ?? client).put<ArrayOfDatesResponse, ArrayOfDatesError>({
+70 -34
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios_transform/types.gen.ts.snap
··· 20 20 readonly expires?: Date; 21 21 }; 22 22 23 - export function ModelWithDates(data: any): ModelWithDates { 24 - if (data?.modified) { 25 - data.modified = new Date(data.modified); 26 - } 27 - if (data?.expires) { 28 - data.expires = new Date(data.expires); 29 - } 30 - return data; 31 - } 32 - 33 23 /** 34 24 * This is a model that contains a some dates and arrays 35 25 */ ··· 44 34 strings?: Array<(string)>; 45 35 }; 46 36 47 - export function ParentModelWithDates(data: any): ParentModelWithDates { 48 - if (data?.modified) { 49 - data.modified = new Date(data.modified); 50 - } 51 - if (Array.isArray(data?.items)) { 52 - data.items.forEach(ModelWithDates); 53 - } 54 - if (data?.item) { 55 - ModelWithDates(data.item); 56 - } 57 - if (Array.isArray(data?.dates)) { 58 - data.dates = data.dates.map(item => new Date(item)); 59 - } 60 - return data; 61 - } 37 + export type ParentModelWithDatesResponse = ParentModelWithDates | unknown; 62 38 63 - export type ModelWithDatesResponse = ModelWithDates; 39 + export type ParentModelWithDatesError = unknown; 64 40 65 - export const ModelWithDatesResponse = ModelWithDates; 41 + export type ModelWithDatesResponse = ModelWithDates; 66 42 67 43 export type ModelWithDatesError = unknown; 68 44 69 45 export type ModelWithDatesArrayResponse = Array<ModelWithDates>; 70 - 71 - export const ModelWithDatesArrayResponse = (data: any) => { 72 - if (Array.isArray(data)) { 73 - data.forEach(ModelWithDates); 74 - } 75 - return data; 76 - }; 77 46 78 47 export type ModelWithDatesArrayError = unknown; 79 48 ··· 91 60 92 61 export type $OpenApiTs = { 93 62 '/api/model-with-dates': { 63 + post: { 64 + res: { 65 + /** 66 + * Success 67 + */ 68 + '200': ParentModelWithDates; 69 + /** 70 + * Success 71 + */ 72 + '201': unknown; 73 + }; 74 + }; 94 75 put: { 95 76 res: { 96 77 /** ··· 144 125 }; 145 126 }; 146 127 }; 128 + }; 129 + 130 + export type ParentModelWithDatesResponseTransformer = (data: any) => ParentModelWithDatesResponse; 131 + 132 + export type ParentModelWithDatesModelResponseTransformer = (data: any) => ParentModelWithDates; 133 + 134 + export type ModelWithDatesModelResponseTransformer = (data: any) => ModelWithDates; 135 + 136 + export const ModelWithDatesModelResponseTransformer: ModelWithDatesModelResponseTransformer = data => { 137 + if (data?.modified) { 138 + data.modified = new Date(data.modified); 139 + } 140 + if (data?.expires) { 141 + data.expires = new Date(data.expires); 142 + } 143 + return data; 144 + }; 145 + 146 + export const ParentModelWithDatesModelResponseTransformer: ParentModelWithDatesModelResponseTransformer = data => { 147 + if (data?.modified) { 148 + data.modified = new Date(data.modified); 149 + } 150 + if (Array.isArray(data?.items)) { 151 + data.items.forEach(ModelWithDatesModelResponseTransformer); 152 + } 153 + if (data?.item) { 154 + ModelWithDatesModelResponseTransformer(data.item); 155 + } 156 + if (Array.isArray(data?.dates)) { 157 + data.dates = data.dates.map(item => new Date(item)); 158 + } 159 + return data; 160 + }; 161 + 162 + export const ParentModelWithDatesResponseTransformer: ParentModelWithDatesResponseTransformer = data => { 163 + if (data) { 164 + ParentModelWithDatesModelResponseTransformer(data); 165 + } 166 + return data; 167 + }; 168 + 169 + export type ModelWithDatesResponseTransformer = (data: any) => ModelWithDatesResponse; 170 + 171 + export const ModelWithDatesResponseTransformer: ModelWithDatesResponseTransformer = data => { 172 + ModelWithDatesModelResponseTransformer(data); 173 + return data; 174 + }; 175 + 176 + export type ModelWithDatesArrayResponseTransformer = (data: any) => ModelWithDatesArrayResponse; 177 + 178 + export const ModelWithDatesArrayResponseTransformer: ModelWithDatesArrayResponseTransformer = data => { 179 + if (Array.isArray(data)) { 180 + data.forEach(ModelWithDatesModelResponseTransformer); 181 + } 182 + return data; 147 183 };
+9 -3
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_transform/services.gen.ts.snap
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 3 import { client, type Options } from '@hey-api/client-fetch'; 4 - import { type ModelWithDatesError, ModelWithDatesResponse, type ModelWithDatesArrayError, ModelWithDatesArrayResponse, type ArrayOfDatesError, type ArrayOfDatesResponse, type DateError, type DateResponse, type MultipleResponsesError, type MultipleResponsesResponse } from './types.gen'; 4 + import { type ParentModelWithDatesError, type ParentModelWithDatesResponse, type ModelWithDatesError, type ModelWithDatesResponse, type ModelWithDatesArrayError, type ModelWithDatesArrayResponse, type ArrayOfDatesError, type ArrayOfDatesResponse, type DateError, type DateResponse, type MultipleResponsesError, type MultipleResponsesResponse, ParentModelWithDatesResponseTransformer, ModelWithDatesResponseTransformer, ModelWithDatesArrayResponseTransformer } from './types.gen'; 5 + 6 + export const parentModelWithDates = (options?: Options) => { return (options?.client ?? client).post<ParentModelWithDatesResponse, ParentModelWithDatesError>({ 7 + ...options, 8 + url: '/api/model-with-dates', 9 + responseTransformer: ParentModelWithDatesResponseTransformer 10 + }); }; 5 11 6 12 export const modelWithDates = (options?: Options) => { return (options?.client ?? client).put<ModelWithDatesResponse, ModelWithDatesError>({ 7 13 ...options, 8 14 url: '/api/model-with-dates', 9 - responseTransformer: ModelWithDatesResponse 15 + responseTransformer: ModelWithDatesResponseTransformer 10 16 }); }; 11 17 12 18 export const modelWithDatesArray = (options?: Options) => { return (options?.client ?? client).put<ModelWithDatesArrayResponse, ModelWithDatesArrayError>({ 13 19 ...options, 14 20 url: '/api/model-with-dates-array', 15 - responseTransformer: ModelWithDatesArrayResponse 21 + responseTransformer: ModelWithDatesArrayResponseTransformer 16 22 }); }; 17 23 18 24 export const arrayOfDates = (options?: Options) => { return (options?.client ?? client).put<ArrayOfDatesResponse, ArrayOfDatesError>({
+70 -34
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_transform/types.gen.ts.snap
··· 20 20 readonly expires?: Date; 21 21 }; 22 22 23 - export function ModelWithDates(data: any): ModelWithDates { 24 - if (data?.modified) { 25 - data.modified = new Date(data.modified); 26 - } 27 - if (data?.expires) { 28 - data.expires = new Date(data.expires); 29 - } 30 - return data; 31 - } 32 - 33 23 /** 34 24 * This is a model that contains a some dates and arrays 35 25 */ ··· 44 34 strings?: Array<(string)>; 45 35 }; 46 36 47 - export function ParentModelWithDates(data: any): ParentModelWithDates { 48 - if (data?.modified) { 49 - data.modified = new Date(data.modified); 50 - } 51 - if (Array.isArray(data?.items)) { 52 - data.items.forEach(ModelWithDates); 53 - } 54 - if (data?.item) { 55 - ModelWithDates(data.item); 56 - } 57 - if (Array.isArray(data?.dates)) { 58 - data.dates = data.dates.map(item => new Date(item)); 59 - } 60 - return data; 61 - } 37 + export type ParentModelWithDatesResponse = ParentModelWithDates | unknown; 62 38 63 - export type ModelWithDatesResponse = ModelWithDates; 39 + export type ParentModelWithDatesError = unknown; 64 40 65 - export const ModelWithDatesResponse = ModelWithDates; 41 + export type ModelWithDatesResponse = ModelWithDates; 66 42 67 43 export type ModelWithDatesError = unknown; 68 44 69 45 export type ModelWithDatesArrayResponse = Array<ModelWithDates>; 70 - 71 - export const ModelWithDatesArrayResponse = (data: any) => { 72 - if (Array.isArray(data)) { 73 - data.forEach(ModelWithDates); 74 - } 75 - return data; 76 - }; 77 46 78 47 export type ModelWithDatesArrayError = unknown; 79 48 ··· 91 60 92 61 export type $OpenApiTs = { 93 62 '/api/model-with-dates': { 63 + post: { 64 + res: { 65 + /** 66 + * Success 67 + */ 68 + '200': ParentModelWithDates; 69 + /** 70 + * Success 71 + */ 72 + '201': unknown; 73 + }; 74 + }; 94 75 put: { 95 76 res: { 96 77 /** ··· 144 125 }; 145 126 }; 146 127 }; 128 + }; 129 + 130 + export type ParentModelWithDatesResponseTransformer = (data: any) => ParentModelWithDatesResponse; 131 + 132 + export type ParentModelWithDatesModelResponseTransformer = (data: any) => ParentModelWithDates; 133 + 134 + export type ModelWithDatesModelResponseTransformer = (data: any) => ModelWithDates; 135 + 136 + export const ModelWithDatesModelResponseTransformer: ModelWithDatesModelResponseTransformer = data => { 137 + if (data?.modified) { 138 + data.modified = new Date(data.modified); 139 + } 140 + if (data?.expires) { 141 + data.expires = new Date(data.expires); 142 + } 143 + return data; 144 + }; 145 + 146 + export const ParentModelWithDatesModelResponseTransformer: ParentModelWithDatesModelResponseTransformer = data => { 147 + if (data?.modified) { 148 + data.modified = new Date(data.modified); 149 + } 150 + if (Array.isArray(data?.items)) { 151 + data.items.forEach(ModelWithDatesModelResponseTransformer); 152 + } 153 + if (data?.item) { 154 + ModelWithDatesModelResponseTransformer(data.item); 155 + } 156 + if (Array.isArray(data?.dates)) { 157 + data.dates = data.dates.map(item => new Date(item)); 158 + } 159 + return data; 160 + }; 161 + 162 + export const ParentModelWithDatesResponseTransformer: ParentModelWithDatesResponseTransformer = data => { 163 + if (data) { 164 + ParentModelWithDatesModelResponseTransformer(data); 165 + } 166 + return data; 167 + }; 168 + 169 + export type ModelWithDatesResponseTransformer = (data: any) => ModelWithDatesResponse; 170 + 171 + export const ModelWithDatesResponseTransformer: ModelWithDatesResponseTransformer = data => { 172 + ModelWithDatesModelResponseTransformer(data); 173 + return data; 174 + }; 175 + 176 + export type ModelWithDatesArrayResponseTransformer = (data: any) => ModelWithDatesArrayResponse; 177 + 178 + export const ModelWithDatesArrayResponseTransformer: ModelWithDatesArrayResponseTransformer = data => { 179 + if (Array.isArray(data)) { 180 + data.forEach(ModelWithDatesModelResponseTransformer); 181 + } 182 + return data; 147 183 };
+16 -3
packages/openapi-ts/test/__snapshots__/test/generated/v3_node_transform/services.gen.ts.snap
··· 3 3 import type { CancelablePromise } from './core/CancelablePromise'; 4 4 import { OpenAPI } from './core/OpenAPI'; 5 5 import { request as __request } from './core/request'; 6 - import { ModelWithDatesResponse, ModelWithDatesArrayResponse, type ArrayOfDatesResponse, type DateResponse, type MultipleResponsesResponse } from './types.gen'; 6 + import { type ParentModelWithDatesResponse, type ModelWithDatesResponse, type ModelWithDatesArrayResponse, type ArrayOfDatesResponse, type DateResponse, type MultipleResponsesResponse, ParentModelWithDatesResponseTransformer, ModelWithDatesResponseTransformer, ModelWithDatesArrayResponseTransformer } from './types.gen'; 7 7 8 8 export class DefaultService { 9 9 /** 10 + * @returns ParentModelWithDates Success 11 + * @returns unknown Success 12 + * @throws ApiError 13 + */ 14 + public static parentModelWithDates(): CancelablePromise<ParentModelWithDatesResponse> { 15 + return __request(OpenAPI, { 16 + method: 'POST', 17 + url: '/api/model-with-dates', 18 + responseTransformer: ParentModelWithDatesResponseTransformer 19 + }); 20 + } 21 + 22 + /** 10 23 * @returns ModelWithDates Success 11 24 * @throws ApiError 12 25 */ ··· 14 27 return __request(OpenAPI, { 15 28 method: 'PUT', 16 29 url: '/api/model-with-dates', 17 - responseTransformer: ModelWithDatesResponse 30 + responseTransformer: ModelWithDatesResponseTransformer 18 31 }); 19 32 } 20 33 ··· 26 39 return __request(OpenAPI, { 27 40 method: 'PUT', 28 41 url: '/api/model-with-dates-array', 29 - responseTransformer: ModelWithDatesArrayResponse 42 + responseTransformer: ModelWithDatesArrayResponseTransformer 30 43 }); 31 44 } 32 45
+68 -34
packages/openapi-ts/test/__snapshots__/test/generated/v3_node_transform/types.gen.ts.snap
··· 20 20 readonly expires?: Date; 21 21 }; 22 22 23 - export function ModelWithDates(data: any): ModelWithDates { 24 - if (data?.modified) { 25 - data.modified = new Date(data.modified); 26 - } 27 - if (data?.expires) { 28 - data.expires = new Date(data.expires); 29 - } 30 - return data; 31 - } 32 - 33 23 /** 34 24 * This is a model that contains a some dates and arrays 35 25 */ ··· 44 34 strings?: Array<(string)>; 45 35 }; 46 36 47 - export function ParentModelWithDates(data: any): ParentModelWithDates { 48 - if (data?.modified) { 49 - data.modified = new Date(data.modified); 50 - } 51 - if (Array.isArray(data?.items)) { 52 - data.items.forEach(ModelWithDates); 53 - } 54 - if (data?.item) { 55 - ModelWithDates(data.item); 56 - } 57 - if (Array.isArray(data?.dates)) { 58 - data.dates = data.dates.map(item => new Date(item)); 59 - } 60 - return data; 61 - } 37 + export type ParentModelWithDatesResponse = ParentModelWithDates | unknown; 62 38 63 39 export type ModelWithDatesResponse = ModelWithDates; 64 40 65 - export const ModelWithDatesResponse = ModelWithDates; 66 - 67 41 export type ModelWithDatesArrayResponse = Array<ModelWithDates>; 68 42 69 - export const ModelWithDatesArrayResponse = (data: any) => { 70 - if (Array.isArray(data)) { 71 - data.forEach(ModelWithDates); 72 - } 73 - return data; 74 - }; 75 - 76 43 export type ArrayOfDatesResponse = Array<(Date)>; 77 44 78 45 export type DateResponse = Date; ··· 81 48 82 49 export type $OpenApiTs = { 83 50 '/api/model-with-dates': { 51 + post: { 52 + res: { 53 + /** 54 + * Success 55 + */ 56 + 200: ParentModelWithDates; 57 + /** 58 + * Success 59 + */ 60 + 201: unknown; 61 + }; 62 + }; 84 63 put: { 85 64 res: { 86 65 /** ··· 134 113 }; 135 114 }; 136 115 }; 116 + }; 117 + 118 + export type ParentModelWithDatesResponseTransformer = (data: any) => ParentModelWithDatesResponse; 119 + 120 + export type ParentModelWithDatesModelResponseTransformer = (data: any) => ParentModelWithDates; 121 + 122 + export type ModelWithDatesModelResponseTransformer = (data: any) => ModelWithDates; 123 + 124 + export const ModelWithDatesModelResponseTransformer: ModelWithDatesModelResponseTransformer = data => { 125 + if (data?.modified) { 126 + data.modified = new Date(data.modified); 127 + } 128 + if (data?.expires) { 129 + data.expires = new Date(data.expires); 130 + } 131 + return data; 132 + }; 133 + 134 + export const ParentModelWithDatesModelResponseTransformer: ParentModelWithDatesModelResponseTransformer = data => { 135 + if (data?.modified) { 136 + data.modified = new Date(data.modified); 137 + } 138 + if (Array.isArray(data?.items)) { 139 + data.items.forEach(ModelWithDatesModelResponseTransformer); 140 + } 141 + if (data?.item) { 142 + ModelWithDatesModelResponseTransformer(data.item); 143 + } 144 + if (Array.isArray(data?.dates)) { 145 + data.dates = data.dates.map(item => new Date(item)); 146 + } 147 + return data; 148 + }; 149 + 150 + export const ParentModelWithDatesResponseTransformer: ParentModelWithDatesResponseTransformer = data => { 151 + if (data) { 152 + ParentModelWithDatesModelResponseTransformer(data); 153 + } 154 + return data; 155 + }; 156 + 157 + export type ModelWithDatesResponseTransformer = (data: any) => ModelWithDatesResponse; 158 + 159 + export const ModelWithDatesResponseTransformer: ModelWithDatesResponseTransformer = data => { 160 + ModelWithDatesModelResponseTransformer(data); 161 + return data; 162 + }; 163 + 164 + export type ModelWithDatesArrayResponseTransformer = (data: any) => ModelWithDatesArrayResponse; 165 + 166 + export const ModelWithDatesArrayResponseTransformer: ModelWithDatesArrayResponseTransformer = data => { 167 + if (Array.isArray(data)) { 168 + data.forEach(ModelWithDatesModelResponseTransformer); 169 + } 170 + return data; 137 171 };
+16 -3
packages/openapi-ts/test/__snapshots__/test/generated/v3_transform/services.gen.ts.snap
··· 3 3 import type { CancelablePromise } from './core/CancelablePromise'; 4 4 import { OpenAPI } from './core/OpenAPI'; 5 5 import { request as __request } from './core/request'; 6 - import { ModelWithDatesResponse, ModelWithDatesArrayResponse, type ArrayOfDatesResponse, type DateResponse, type MultipleResponsesResponse } from './types.gen'; 6 + import { type ParentModelWithDatesResponse, type ModelWithDatesResponse, type ModelWithDatesArrayResponse, type ArrayOfDatesResponse, type DateResponse, type MultipleResponsesResponse, ParentModelWithDatesResponseTransformer, ModelWithDatesResponseTransformer, ModelWithDatesArrayResponseTransformer } from './types.gen'; 7 7 8 8 export class DefaultService { 9 9 /** 10 + * @returns ParentModelWithDates Success 11 + * @returns unknown Success 12 + * @throws ApiError 13 + */ 14 + public static parentModelWithDates(): CancelablePromise<ParentModelWithDatesResponse> { 15 + return __request(OpenAPI, { 16 + method: 'POST', 17 + url: '/api/model-with-dates', 18 + responseTransformer: ParentModelWithDatesResponseTransformer 19 + }); 20 + } 21 + 22 + /** 10 23 * @returns ModelWithDates Success 11 24 * @throws ApiError 12 25 */ ··· 14 27 return __request(OpenAPI, { 15 28 method: 'PUT', 16 29 url: '/api/model-with-dates', 17 - responseTransformer: ModelWithDatesResponse 30 + responseTransformer: ModelWithDatesResponseTransformer 18 31 }); 19 32 } 20 33 ··· 26 39 return __request(OpenAPI, { 27 40 method: 'PUT', 28 41 url: '/api/model-with-dates-array', 29 - responseTransformer: ModelWithDatesArrayResponse 42 + responseTransformer: ModelWithDatesArrayResponseTransformer 30 43 }); 31 44 } 32 45
+68 -34
packages/openapi-ts/test/__snapshots__/test/generated/v3_transform/types.gen.ts.snap
··· 20 20 readonly expires?: Date; 21 21 }; 22 22 23 - export function ModelWithDates(data: any): ModelWithDates { 24 - if (data?.modified) { 25 - data.modified = new Date(data.modified); 26 - } 27 - if (data?.expires) { 28 - data.expires = new Date(data.expires); 29 - } 30 - return data; 31 - } 32 - 33 23 /** 34 24 * This is a model that contains a some dates and arrays 35 25 */ ··· 44 34 strings?: Array<(string)>; 45 35 }; 46 36 47 - export function ParentModelWithDates(data: any): ParentModelWithDates { 48 - if (data?.modified) { 49 - data.modified = new Date(data.modified); 50 - } 51 - if (Array.isArray(data?.items)) { 52 - data.items.forEach(ModelWithDates); 53 - } 54 - if (data?.item) { 55 - ModelWithDates(data.item); 56 - } 57 - if (Array.isArray(data?.dates)) { 58 - data.dates = data.dates.map(item => new Date(item)); 59 - } 60 - return data; 61 - } 37 + export type ParentModelWithDatesResponse = ParentModelWithDates | unknown; 62 38 63 39 export type ModelWithDatesResponse = ModelWithDates; 64 40 65 - export const ModelWithDatesResponse = ModelWithDates; 66 - 67 41 export type ModelWithDatesArrayResponse = Array<ModelWithDates>; 68 42 69 - export const ModelWithDatesArrayResponse = (data: any) => { 70 - if (Array.isArray(data)) { 71 - data.forEach(ModelWithDates); 72 - } 73 - return data; 74 - }; 75 - 76 43 export type ArrayOfDatesResponse = Array<(Date)>; 77 44 78 45 export type DateResponse = Date; ··· 81 48 82 49 export type $OpenApiTs = { 83 50 '/api/model-with-dates': { 51 + post: { 52 + res: { 53 + /** 54 + * Success 55 + */ 56 + 200: ParentModelWithDates; 57 + /** 58 + * Success 59 + */ 60 + 201: unknown; 61 + }; 62 + }; 84 63 put: { 85 64 res: { 86 65 /** ··· 134 113 }; 135 114 }; 136 115 }; 116 + }; 117 + 118 + export type ParentModelWithDatesResponseTransformer = (data: any) => ParentModelWithDatesResponse; 119 + 120 + export type ParentModelWithDatesModelResponseTransformer = (data: any) => ParentModelWithDates; 121 + 122 + export type ModelWithDatesModelResponseTransformer = (data: any) => ModelWithDates; 123 + 124 + export const ModelWithDatesModelResponseTransformer: ModelWithDatesModelResponseTransformer = data => { 125 + if (data?.modified) { 126 + data.modified = new Date(data.modified); 127 + } 128 + if (data?.expires) { 129 + data.expires = new Date(data.expires); 130 + } 131 + return data; 132 + }; 133 + 134 + export const ParentModelWithDatesModelResponseTransformer: ParentModelWithDatesModelResponseTransformer = data => { 135 + if (data?.modified) { 136 + data.modified = new Date(data.modified); 137 + } 138 + if (Array.isArray(data?.items)) { 139 + data.items.forEach(ModelWithDatesModelResponseTransformer); 140 + } 141 + if (data?.item) { 142 + ModelWithDatesModelResponseTransformer(data.item); 143 + } 144 + if (Array.isArray(data?.dates)) { 145 + data.dates = data.dates.map(item => new Date(item)); 146 + } 147 + return data; 148 + }; 149 + 150 + export const ParentModelWithDatesResponseTransformer: ParentModelWithDatesResponseTransformer = data => { 151 + if (data) { 152 + ParentModelWithDatesModelResponseTransformer(data); 153 + } 154 + return data; 155 + }; 156 + 157 + export type ModelWithDatesResponseTransformer = (data: any) => ModelWithDatesResponse; 158 + 159 + export const ModelWithDatesResponseTransformer: ModelWithDatesResponseTransformer = data => { 160 + ModelWithDatesModelResponseTransformer(data); 161 + return data; 162 + }; 163 + 164 + export type ModelWithDatesArrayResponseTransformer = (data: any) => ModelWithDatesArrayResponse; 165 + 166 + export const ModelWithDatesArrayResponseTransformer: ModelWithDatesArrayResponseTransformer = data => { 167 + if (Array.isArray(data)) { 168 + data.forEach(ModelWithDatesModelResponseTransformer); 169 + } 170 + return data; 137 171 };
+16 -3
packages/openapi-ts/test/__snapshots__/test/generated/v3_xhr_transform/services.gen.ts.snap
··· 3 3 import type { CancelablePromise } from './core/CancelablePromise'; 4 4 import { OpenAPI } from './core/OpenAPI'; 5 5 import { request as __request } from './core/request'; 6 - import { ModelWithDatesResponse, ModelWithDatesArrayResponse, type ArrayOfDatesResponse, type DateResponse, type MultipleResponsesResponse } from './types.gen'; 6 + import { type ParentModelWithDatesResponse, type ModelWithDatesResponse, type ModelWithDatesArrayResponse, type ArrayOfDatesResponse, type DateResponse, type MultipleResponsesResponse, ParentModelWithDatesResponseTransformer, ModelWithDatesResponseTransformer, ModelWithDatesArrayResponseTransformer } from './types.gen'; 7 7 8 8 export class DefaultService { 9 9 /** 10 + * @returns ParentModelWithDates Success 11 + * @returns unknown Success 12 + * @throws ApiError 13 + */ 14 + public static parentModelWithDates(): CancelablePromise<ParentModelWithDatesResponse> { 15 + return __request(OpenAPI, { 16 + method: 'POST', 17 + url: '/api/model-with-dates', 18 + responseTransformer: ParentModelWithDatesResponseTransformer 19 + }); 20 + } 21 + 22 + /** 10 23 * @returns ModelWithDates Success 11 24 * @throws ApiError 12 25 */ ··· 14 27 return __request(OpenAPI, { 15 28 method: 'PUT', 16 29 url: '/api/model-with-dates', 17 - responseTransformer: ModelWithDatesResponse 30 + responseTransformer: ModelWithDatesResponseTransformer 18 31 }); 19 32 } 20 33 ··· 26 39 return __request(OpenAPI, { 27 40 method: 'PUT', 28 41 url: '/api/model-with-dates-array', 29 - responseTransformer: ModelWithDatesArrayResponse 42 + responseTransformer: ModelWithDatesArrayResponseTransformer 30 43 }); 31 44 } 32 45
+68 -34
packages/openapi-ts/test/__snapshots__/test/generated/v3_xhr_transform/types.gen.ts.snap
··· 20 20 readonly expires?: Date; 21 21 }; 22 22 23 - export function ModelWithDates(data: any): ModelWithDates { 24 - if (data?.modified) { 25 - data.modified = new Date(data.modified); 26 - } 27 - if (data?.expires) { 28 - data.expires = new Date(data.expires); 29 - } 30 - return data; 31 - } 32 - 33 23 /** 34 24 * This is a model that contains a some dates and arrays 35 25 */ ··· 44 34 strings?: Array<(string)>; 45 35 }; 46 36 47 - export function ParentModelWithDates(data: any): ParentModelWithDates { 48 - if (data?.modified) { 49 - data.modified = new Date(data.modified); 50 - } 51 - if (Array.isArray(data?.items)) { 52 - data.items.forEach(ModelWithDates); 53 - } 54 - if (data?.item) { 55 - ModelWithDates(data.item); 56 - } 57 - if (Array.isArray(data?.dates)) { 58 - data.dates = data.dates.map(item => new Date(item)); 59 - } 60 - return data; 61 - } 37 + export type ParentModelWithDatesResponse = ParentModelWithDates | unknown; 62 38 63 39 export type ModelWithDatesResponse = ModelWithDates; 64 40 65 - export const ModelWithDatesResponse = ModelWithDates; 66 - 67 41 export type ModelWithDatesArrayResponse = Array<ModelWithDates>; 68 42 69 - export const ModelWithDatesArrayResponse = (data: any) => { 70 - if (Array.isArray(data)) { 71 - data.forEach(ModelWithDates); 72 - } 73 - return data; 74 - }; 75 - 76 43 export type ArrayOfDatesResponse = Array<(Date)>; 77 44 78 45 export type DateResponse = Date; ··· 81 48 82 49 export type $OpenApiTs = { 83 50 '/api/model-with-dates': { 51 + post: { 52 + res: { 53 + /** 54 + * Success 55 + */ 56 + 200: ParentModelWithDates; 57 + /** 58 + * Success 59 + */ 60 + 201: unknown; 61 + }; 62 + }; 84 63 put: { 85 64 res: { 86 65 /** ··· 134 113 }; 135 114 }; 136 115 }; 116 + }; 117 + 118 + export type ParentModelWithDatesResponseTransformer = (data: any) => ParentModelWithDatesResponse; 119 + 120 + export type ParentModelWithDatesModelResponseTransformer = (data: any) => ParentModelWithDates; 121 + 122 + export type ModelWithDatesModelResponseTransformer = (data: any) => ModelWithDates; 123 + 124 + export const ModelWithDatesModelResponseTransformer: ModelWithDatesModelResponseTransformer = data => { 125 + if (data?.modified) { 126 + data.modified = new Date(data.modified); 127 + } 128 + if (data?.expires) { 129 + data.expires = new Date(data.expires); 130 + } 131 + return data; 132 + }; 133 + 134 + export const ParentModelWithDatesModelResponseTransformer: ParentModelWithDatesModelResponseTransformer = data => { 135 + if (data?.modified) { 136 + data.modified = new Date(data.modified); 137 + } 138 + if (Array.isArray(data?.items)) { 139 + data.items.forEach(ModelWithDatesModelResponseTransformer); 140 + } 141 + if (data?.item) { 142 + ModelWithDatesModelResponseTransformer(data.item); 143 + } 144 + if (Array.isArray(data?.dates)) { 145 + data.dates = data.dates.map(item => new Date(item)); 146 + } 147 + return data; 148 + }; 149 + 150 + export const ParentModelWithDatesResponseTransformer: ParentModelWithDatesResponseTransformer = data => { 151 + if (data) { 152 + ParentModelWithDatesModelResponseTransformer(data); 153 + } 154 + return data; 155 + }; 156 + 157 + export type ModelWithDatesResponseTransformer = (data: any) => ModelWithDatesResponse; 158 + 159 + export const ModelWithDatesResponseTransformer: ModelWithDatesResponseTransformer = data => { 160 + ModelWithDatesModelResponseTransformer(data); 161 + return data; 162 + }; 163 + 164 + export type ModelWithDatesArrayResponseTransformer = (data: any) => ModelWithDatesArrayResponse; 165 + 166 + export const ModelWithDatesArrayResponseTransformer: ModelWithDatesArrayResponseTransformer = data => { 167 + if (Array.isArray(data)) { 168 + data.forEach(ModelWithDatesModelResponseTransformer); 169 + } 170 + return data; 137 171 };
+8 -5
packages/openapi-ts/test/sample.cjs
··· 4 4 /** @type {import('../src/node/index').UserConfig} */ 5 5 const config = { 6 6 client: '@hey-api/client-fetch', 7 - input: './test/spec/v3.json', 7 + // input: './test/spec/v3.json', 8 + debug: true, 9 + input: './test/spec/v3-transforms.json', 8 10 // input: 'https://mongodb-mms-prod-build-server.s3.amazonaws.com/openapi/2caffd88277a4e27c95dcefc7e3b6a63a3b03297-v2-2023-11-15.json', 9 11 output: { 10 12 path: './test/generated/sample/', ··· 13 15 export: false, 14 16 }, 15 17 services: { 16 - asClass: true, 17 - export: false, 18 + // asClass: true, 19 + // export: false, 18 20 // name: '^Parameters', 19 21 }, 20 22 types: { 23 + dates: 'types+transform', 21 24 enums: 'typescript', 22 - include: 23 - '^ModelWithPrefixItemsConstantSizeArray|ModelWithAnyOfConstantSizeArray', 25 + // include: 26 + // '^ModelWithPrefixItemsConstantSizeArray|ModelWithAnyOfConstantSizeArray', 24 27 // name: 'PascalCase', 25 28 }, 26 29 // useOptions: false,
+18
packages/openapi-ts/test/spec/v3-transforms.json
··· 11 11 ], 12 12 "paths": { 13 13 "/api/model-with-dates": { 14 + "post": { 15 + "operationId": "parentModelWithDates", 16 + "responses": { 17 + "200": { 18 + "description": "Success", 19 + "content": { 20 + "application/json; type=collection": { 21 + "schema": { 22 + "$ref": "#/components/schemas/ParentModelWithDates" 23 + } 24 + } 25 + } 26 + }, 27 + "201": { 28 + "description": "Success" 29 + } 30 + } 31 + }, 14 32 "put": { 15 33 "operationId": "modelWithDates", 16 34 "responses": {