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

Merge pull request #2950 from hey-api/refactor/sdk-validator-dsl

refactor: use dsl in sdk validators

authored by

Lubos and committed by
GitHub
83d5749f f564a4df

+316 -351
+1 -1
.changeset/lemon-plants-shop.md
··· 2 2 '@hey-api/openapi-ts': patch 3 3 --- 4 4 5 - @pinia/colada: correctly access instantiated SDKs 5 + **@pinia/colada**: correctly access instantiated SDKs
+1 -1
.changeset/nine-cups-carry.md
··· 2 2 "@hey-api/openapi-ts": patch 3 3 --- 4 4 5 - valibot: use `.strictObject()` instead of `.objectWithRest()` when additional properties are not allowed 5 + **valibot**: use `.strictObject()` instead of `.objectWithRest()` when additional properties are not allowed
+1 -1
.changeset/upset-candies-kick.md
··· 2 2 '@hey-api/openapi-ts': patch 3 3 --- 4 4 5 - @tanstack/query: prettier query options 5 + **@tanstack/query**: prettier query options
+2 -6
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-valibot/sdk.gen.ts
··· 24 24 25 25 export const postFoo = <ThrowOnError extends boolean = false>(options?: Options<PostFooData, ThrowOnError>) => { 26 26 return (options?.client ?? client).post<PostFooResponses, unknown, ThrowOnError>({ 27 - requestValidator: async (data) => { 28 - return await v.parseAsync(vPostFooData, data); 29 - }, 27 + requestValidator: async (data) => await v.parseAsync(vPostFooData, data), 30 28 responseTransformer: postFooResponseTransformer, 31 - responseValidator: async (data) => { 32 - return await v.parseAsync(vPostFooResponse, data); 33 - }, 29 + responseValidator: async (data) => await v.parseAsync(vPostFooResponse, data), 34 30 url: '/foo', 35 31 ...options 36 32 });
+2 -6
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-zod/sdk.gen.ts
··· 22 22 23 23 export const postFoo = <ThrowOnError extends boolean = false>(options?: Options<PostFooData, ThrowOnError>) => { 24 24 return (options?.client ?? client).post<PostFooResponses, unknown, ThrowOnError>({ 25 - requestValidator: async (data) => { 26 - return await zPostFooData.parseAsync(data); 27 - }, 25 + requestValidator: async (data) => await zPostFooData.parseAsync(data), 28 26 responseTransformer: postFooResponseTransformer, 29 - responseValidator: async (data) => { 30 - return await zPostFooResponse.parseAsync(data); 31 - }, 27 + responseValidator: async (data) => await zPostFooResponse.parseAsync(data), 32 28 url: '/foo', 33 29 ...options 34 30 });
+2 -6
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-valibot/sdk.gen.ts
··· 24 24 25 25 export const postFoo = <ThrowOnError extends boolean = false>(options?: Options<PostFooData, ThrowOnError>) => { 26 26 return (options?.client ?? client).post<PostFooResponses, unknown, ThrowOnError>({ 27 - requestValidator: async (data) => { 28 - return await v.parseAsync(vPostFooData, data); 29 - }, 27 + requestValidator: async (data) => await v.parseAsync(vPostFooData, data), 30 28 responseTransformer: postFooResponseTransformer, 31 - responseValidator: async (data) => { 32 - return await v.parseAsync(vPostFooResponse, data); 33 - }, 29 + responseValidator: async (data) => await v.parseAsync(vPostFooResponse, data), 34 30 url: '/foo', 35 31 ...options 36 32 });
+2 -6
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-zod/sdk.gen.ts
··· 22 22 23 23 export const postFoo = <ThrowOnError extends boolean = false>(options?: Options<PostFooData, ThrowOnError>) => { 24 24 return (options?.client ?? client).post<PostFooResponses, unknown, ThrowOnError>({ 25 - requestValidator: async (data) => { 26 - return await zPostFooData.parseAsync(data); 27 - }, 25 + requestValidator: async (data) => await zPostFooData.parseAsync(data), 28 26 responseTransformer: postFooResponseTransformer, 29 - responseValidator: async (data) => { 30 - return await zPostFooResponse.parseAsync(data); 31 - }, 27 + responseValidator: async (data) => await zPostFooResponse.parseAsync(data), 32 28 url: '/foo', 33 29 ...options 34 30 });
+2 -6
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-valibot/sdk.gen.ts
··· 24 24 25 25 export const postFoo = <ThrowOnError extends boolean = false>(options?: Options<PostFooData, ThrowOnError>) => { 26 26 return (options?.client ?? client).post<PostFooResponses, unknown, ThrowOnError>({ 27 - requestValidator: async (data) => { 28 - return await v.parseAsync(vPostFooData, data); 29 - }, 27 + requestValidator: async (data) => await v.parseAsync(vPostFooData, data), 30 28 responseTransformer: postFooResponseTransformer, 31 - responseValidator: async (data) => { 32 - return await v.parseAsync(vPostFooResponse, data); 33 - }, 29 + responseValidator: async (data) => await v.parseAsync(vPostFooResponse, data), 34 30 url: '/foo', 35 31 ...options 36 32 });
+2 -6
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-zod/sdk.gen.ts
··· 22 22 23 23 export const postFoo = <ThrowOnError extends boolean = false>(options?: Options<PostFooData, ThrowOnError>) => { 24 24 return (options?.client ?? client).post<PostFooResponses, unknown, ThrowOnError>({ 25 - requestValidator: async (data) => { 26 - return await zPostFooData.parseAsync(data); 27 - }, 25 + requestValidator: async (data) => await zPostFooData.parseAsync(data), 28 26 responseTransformer: postFooResponseTransformer, 29 - responseValidator: async (data) => { 30 - return await zPostFooResponse.parseAsync(data); 31 - }, 27 + responseValidator: async (data) => await zPostFooResponse.parseAsync(data), 32 28 url: '/foo', 33 29 ...options 34 30 });
+3 -3
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/operation.ts
··· 503 503 } 504 504 505 505 const requestValidator = createRequestValidator({ operation, plugin }); 506 + const responseValidator = createResponseValidator({ operation, plugin }); 506 507 if (requestValidator) { 507 508 requestOptions.push({ 508 509 key: 'requestValidator', 509 - value: requestValidator, 510 + value: requestValidator.$render(), 510 511 }); 511 512 } 512 513 ··· 553 554 } 554 555 } 555 556 556 - const responseValidator = createResponseValidator({ operation, plugin }); 557 557 if (responseValidator) { 558 558 requestOptions.push({ 559 559 key: 'responseValidator', 560 - value: responseValidator, 560 + value: responseValidator.$render(), 561 561 }); 562 562 } 563 563
+3 -4
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/validator.ts
··· 1 - import type ts from 'typescript'; 2 - 3 1 import type { IR } from '~/ir/types'; 2 + import type { TsDsl } from '~/ts-dsl'; 4 3 5 4 import type { HeyApiSdkPlugin } from '../types'; 6 5 ··· 12 11 export const createRequestValidator = ({ 13 12 operation, 14 13 plugin, 15 - }: ValidatorProps): ts.ArrowFunction | undefined => { 14 + }: ValidatorProps): TsDsl | undefined => { 16 15 if (!plugin.config.validator.request) return; 17 16 18 17 const validator = plugin.getPluginOrThrow(plugin.config.validator.request); ··· 28 27 export const createResponseValidator = ({ 29 28 operation, 30 29 plugin, 31 - }: ValidatorProps): ts.ArrowFunction | undefined => { 30 + }: ValidatorProps): TsDsl | undefined => { 32 31 if (!plugin.config.validator.response) return; 33 32 34 33 const validator = plugin.getPluginOrThrow(plugin.config.validator.response);
+5 -7
packages/openapi-ts/src/plugins/arktype/api.ts
··· 1 - import type ts from 'typescript'; 1 + import type { TsDsl } from '~/ts-dsl'; 2 2 3 3 import type { ValidatorArgs } from './shared/types'; 4 4 import { createRequestValidatorV2, createResponseValidatorV2 } from './v2/api'; 5 5 6 6 export type IApi = { 7 - createRequestValidator: (args: ValidatorArgs) => ts.ArrowFunction | undefined; 8 - createResponseValidator: ( 9 - args: ValidatorArgs, 10 - ) => ts.ArrowFunction | undefined; 7 + createRequestValidator: (args: ValidatorArgs) => TsDsl | undefined; 8 + createResponseValidator: (args: ValidatorArgs) => TsDsl | undefined; 11 9 }; 12 10 13 11 export class Api implements IApi { 14 - createRequestValidator(args: ValidatorArgs): ts.ArrowFunction | undefined { 12 + createRequestValidator(args: ValidatorArgs): TsDsl | undefined { 15 13 return createRequestValidatorV2(args); 16 14 } 17 15 18 - createResponseValidator(args: ValidatorArgs): ts.ArrowFunction | undefined { 16 + createResponseValidator(args: ValidatorArgs): TsDsl | undefined { 19 17 return createResponseValidatorV2(args); 20 18 } 21 19 }
+26 -52
packages/openapi-ts/src/plugins/arktype/v2/api.ts
··· 1 - import type ts from 'typescript'; 2 - 3 - import { tsc } from '~/tsc'; 1 + import type { TsDsl } from '~/ts-dsl'; 2 + import { $ } from '~/ts-dsl'; 4 3 5 - // import { identifiers } from '../constants'; 6 4 import type { ValidatorArgs } from '../shared/types'; 7 5 8 6 export const createRequestValidatorV2 = ({ 9 7 operation, 10 8 plugin, 11 - }: ValidatorArgs): ts.ArrowFunction | undefined => { 9 + }: ValidatorArgs): TsDsl | undefined => { 12 10 const symbol = plugin.getSymbol({ 13 11 category: 'schema', 14 12 resource: 'operation', ··· 33 31 // console.log(`Hello, ${out.name}`) 34 32 // } 35 33 const dataParameterName = 'data'; 36 - 37 - return tsc.arrowFunction({ 38 - async: true, 39 - parameters: [ 40 - { 41 - name: dataParameterName, 42 - }, 43 - ], 44 - statements: [ 45 - tsc.returnStatement({ 46 - expression: tsc.awaitExpression({ 47 - expression: tsc.callExpression({ 48 - functionName: tsc.propertyAccessExpression({ 49 - expression: symbol.placeholder, 50 - name: 'parseAsync', 51 - // name: identifiers.parseAsync, 52 - }), 53 - parameters: [tsc.identifier({ text: dataParameterName })], 54 - }), 55 - }), 56 - }), 57 - ], 58 - }); 34 + return $.func() 35 + .async() 36 + .param(dataParameterName) 37 + .do( 38 + $.return( 39 + $(symbol.placeholder) 40 + .attr('parseAsync') 41 + .call($(dataParameterName)) 42 + .await(), 43 + ), 44 + ); 59 45 }; 60 46 61 47 export const createResponseValidatorV2 = ({ 62 48 operation, 63 49 plugin, 64 - }: ValidatorArgs): ts.ArrowFunction | undefined => { 50 + }: ValidatorArgs): TsDsl | undefined => { 65 51 const symbol = plugin.getSymbol({ 66 52 category: 'schema', 67 53 resource: 'operation', ··· 72 58 if (!symbol) return; 73 59 74 60 const dataParameterName = 'data'; 75 - 76 - return tsc.arrowFunction({ 77 - async: true, 78 - parameters: [ 79 - { 80 - name: dataParameterName, 81 - }, 82 - ], 83 - statements: [ 84 - tsc.returnStatement({ 85 - expression: tsc.awaitExpression({ 86 - expression: tsc.callExpression({ 87 - functionName: tsc.propertyAccessExpression({ 88 - expression: symbol.placeholder, 89 - name: 'parseAsync', 90 - // name: identifiers.parseAsync, 91 - }), 92 - parameters: [tsc.identifier({ text: dataParameterName })], 93 - }), 94 - }), 95 - }), 96 - ], 97 - }); 61 + return $.func() 62 + .async() 63 + .param(dataParameterName) 64 + .do( 65 + $.return( 66 + $(symbol.placeholder) 67 + .attr('parseAsync') 68 + .call($(dataParameterName)) 69 + .await(), 70 + ), 71 + ); 98 72 };
+5 -7
packages/openapi-ts/src/plugins/valibot/api.ts
··· 1 - import type ts from 'typescript'; 1 + import type { TsDsl } from '~/ts-dsl'; 2 2 3 3 import type { ValidatorArgs } from './shared/types'; 4 4 import { createRequestValidatorV1, createResponseValidatorV1 } from './v1/api'; 5 5 6 6 export type IApi = { 7 - createRequestValidator: (args: ValidatorArgs) => ts.ArrowFunction | undefined; 8 - createResponseValidator: ( 9 - args: ValidatorArgs, 10 - ) => ts.ArrowFunction | undefined; 7 + createRequestValidator: (args: ValidatorArgs) => TsDsl | undefined; 8 + createResponseValidator: (args: ValidatorArgs) => TsDsl | undefined; 11 9 }; 12 10 13 11 export class Api implements IApi { 14 - createRequestValidator(args: ValidatorArgs): ts.ArrowFunction | undefined { 12 + createRequestValidator(args: ValidatorArgs): TsDsl | undefined { 15 13 return createRequestValidatorV1(args); 16 14 } 17 15 18 - createResponseValidator(args: ValidatorArgs): ts.ArrowFunction | undefined { 16 + createResponseValidator(args: ValidatorArgs): TsDsl | undefined { 19 17 return createResponseValidatorV1(args); 20 18 } 21 19 }
+25 -57
packages/openapi-ts/src/plugins/valibot/v1/api.ts
··· 1 - import type ts from 'typescript'; 2 - 3 - import { tsc } from '~/tsc'; 1 + import { $, type TsDsl } from '~/ts-dsl'; 4 2 5 3 import type { ValidatorArgs } from '../shared/types'; 6 4 import { identifiers } from './constants'; ··· 8 6 export const createRequestValidatorV1 = ({ 9 7 operation, 10 8 plugin, 11 - }: ValidatorArgs): ts.ArrowFunction | undefined => { 9 + }: ValidatorArgs): TsDsl | undefined => { 12 10 const symbol = plugin.getSymbol({ 13 11 category: 'schema', 14 12 resource: 'operation', ··· 22 20 category: 'external', 23 21 resource: 'valibot.v', 24 22 }); 25 - 26 23 const dataParameterName = 'data'; 27 - 28 - return tsc.arrowFunction({ 29 - async: true, 30 - parameters: [ 31 - { 32 - name: dataParameterName, 33 - }, 34 - ], 35 - statements: [ 36 - tsc.returnStatement({ 37 - expression: tsc.awaitExpression({ 38 - expression: tsc.callExpression({ 39 - functionName: tsc.propertyAccessExpression({ 40 - expression: v.placeholder, 41 - name: identifiers.async.parseAsync, 42 - }), 43 - parameters: [ 44 - tsc.identifier({ text: symbol.placeholder }), 45 - tsc.identifier({ text: dataParameterName }), 46 - ], 47 - }), 48 - }), 49 - }), 50 - ], 51 - }); 24 + return $.func() 25 + .async() 26 + .param(dataParameterName) 27 + .do( 28 + $.return( 29 + $(v.placeholder) 30 + .attr(identifiers.async.parseAsync) 31 + .call($(symbol.placeholder), $(dataParameterName)) 32 + .await(), 33 + ), 34 + ); 52 35 }; 53 36 54 37 export const createResponseValidatorV1 = ({ 55 38 operation, 56 39 plugin, 57 - }: ValidatorArgs): ts.ArrowFunction | undefined => { 40 + }: ValidatorArgs): TsDsl | undefined => { 58 41 const symbol = plugin.getSymbol({ 59 42 category: 'schema', 60 43 resource: 'operation', ··· 68 51 category: 'external', 69 52 resource: 'valibot.v', 70 53 }); 71 - 72 54 const dataParameterName = 'data'; 73 - 74 - return tsc.arrowFunction({ 75 - async: true, 76 - parameters: [ 77 - { 78 - name: dataParameterName, 79 - }, 80 - ], 81 - statements: [ 82 - tsc.returnStatement({ 83 - expression: tsc.awaitExpression({ 84 - expression: tsc.callExpression({ 85 - functionName: tsc.propertyAccessExpression({ 86 - expression: v.placeholder, 87 - name: identifiers.async.parseAsync, 88 - }), 89 - parameters: [ 90 - tsc.identifier({ text: symbol.placeholder }), 91 - tsc.identifier({ text: dataParameterName }), 92 - ], 93 - }), 94 - }), 95 - }), 96 - ], 97 - }); 55 + return $.func() 56 + .async() 57 + .param(dataParameterName) 58 + .do( 59 + $.return( 60 + $(v.placeholder) 61 + .attr(identifiers.async.parseAsync) 62 + .call($(symbol.placeholder), $(dataParameterName)) 63 + .await(), 64 + ), 65 + ); 98 66 };
+5 -7
packages/openapi-ts/src/plugins/zod/api.ts
··· 1 - import type ts from 'typescript'; 1 + import type { TsDsl } from '~/ts-dsl'; 2 2 3 3 import { 4 4 createRequestValidatorMini, ··· 9 9 import { createRequestValidatorV4, createResponseValidatorV4 } from './v4/api'; 10 10 11 11 export type IApi = { 12 - createRequestValidator: (args: ValidatorArgs) => ts.ArrowFunction | undefined; 13 - createResponseValidator: ( 14 - args: ValidatorArgs, 15 - ) => ts.ArrowFunction | undefined; 12 + createRequestValidator: (args: ValidatorArgs) => TsDsl | undefined; 13 + createResponseValidator: (args: ValidatorArgs) => TsDsl | undefined; 16 14 }; 17 15 18 16 export class Api implements IApi { 19 - createRequestValidator(args: ValidatorArgs): ts.ArrowFunction | undefined { 17 + createRequestValidator(args: ValidatorArgs): TsDsl | undefined { 20 18 const { plugin } = args; 21 19 switch (plugin.config.compatibilityVersion) { 22 20 case 3: ··· 29 27 } 30 28 } 31 29 32 - createResponseValidator(args: ValidatorArgs): ts.ArrowFunction | undefined { 30 + createResponseValidator(args: ValidatorArgs): TsDsl | undefined { 33 31 const { plugin } = args; 34 32 switch (plugin.config.compatibilityVersion) { 35 33 case 3:
+26 -49
packages/openapi-ts/src/plugins/zod/mini/api.ts
··· 1 - import type ts from 'typescript'; 2 - 3 - import { tsc } from '~/tsc'; 1 + import type { TsDsl } from '~/ts-dsl'; 2 + import { $ } from '~/ts-dsl'; 4 3 5 4 import { identifiers } from '../constants'; 6 5 import type { ValidatorArgs } from '../shared/types'; ··· 8 7 export const createRequestValidatorMini = ({ 9 8 operation, 10 9 plugin, 11 - }: ValidatorArgs): ts.ArrowFunction | undefined => { 10 + }: ValidatorArgs): TsDsl | undefined => { 12 11 const symbol = plugin.getSymbol({ 13 12 category: 'schema', 14 13 resource: 'operation', ··· 19 18 if (!symbol) return; 20 19 21 20 const dataParameterName = 'data'; 22 - 23 - return tsc.arrowFunction({ 24 - async: true, 25 - parameters: [ 26 - { 27 - name: dataParameterName, 28 - }, 29 - ], 30 - statements: [ 31 - tsc.returnStatement({ 32 - expression: tsc.awaitExpression({ 33 - expression: tsc.callExpression({ 34 - functionName: tsc.propertyAccessExpression({ 35 - expression: symbol.placeholder, 36 - name: identifiers.parseAsync, 37 - }), 38 - parameters: [tsc.identifier({ text: dataParameterName })], 39 - }), 40 - }), 41 - }), 42 - ], 43 - }); 21 + return $.func() 22 + .async() 23 + .param(dataParameterName) 24 + .do( 25 + $.return( 26 + $(symbol.placeholder) 27 + .attr(identifiers.parseAsync) 28 + .call($(dataParameterName)) 29 + .await(), 30 + ), 31 + ); 44 32 }; 45 33 46 34 export const createResponseValidatorMini = ({ 47 35 operation, 48 36 plugin, 49 - }: ValidatorArgs): ts.ArrowFunction | undefined => { 37 + }: ValidatorArgs): TsDsl | undefined => { 50 38 const symbol = plugin.getSymbol({ 51 39 category: 'schema', 52 40 resource: 'operation', ··· 57 45 if (!symbol) return; 58 46 59 47 const dataParameterName = 'data'; 60 - 61 - return tsc.arrowFunction({ 62 - async: true, 63 - parameters: [ 64 - { 65 - name: dataParameterName, 66 - }, 67 - ], 68 - statements: [ 69 - tsc.returnStatement({ 70 - expression: tsc.awaitExpression({ 71 - expression: tsc.callExpression({ 72 - functionName: tsc.propertyAccessExpression({ 73 - expression: symbol.placeholder, 74 - name: identifiers.parseAsync, 75 - }), 76 - parameters: [tsc.identifier({ text: dataParameterName })], 77 - }), 78 - }), 79 - }), 80 - ], 81 - }); 48 + return $.func() 49 + .async() 50 + .param(dataParameterName) 51 + .do( 52 + $.return( 53 + $(symbol.placeholder) 54 + .attr(identifiers.parseAsync) 55 + .call($(dataParameterName)) 56 + .await(), 57 + ), 58 + ); 82 59 };
+26 -49
packages/openapi-ts/src/plugins/zod/v3/api.ts
··· 1 - import type ts from 'typescript'; 2 - 3 - import { tsc } from '~/tsc'; 1 + import type { TsDsl } from '~/ts-dsl'; 2 + import { $ } from '~/ts-dsl'; 4 3 5 4 import { identifiers } from '../constants'; 6 5 import type { ValidatorArgs } from '../shared/types'; ··· 8 7 export const createRequestValidatorV3 = ({ 9 8 operation, 10 9 plugin, 11 - }: ValidatorArgs): ts.ArrowFunction | undefined => { 10 + }: ValidatorArgs): TsDsl | undefined => { 12 11 const symbol = plugin.getSymbol({ 13 12 category: 'schema', 14 13 resource: 'operation', ··· 19 18 if (!symbol) return; 20 19 21 20 const dataParameterName = 'data'; 22 - 23 - return tsc.arrowFunction({ 24 - async: true, 25 - parameters: [ 26 - { 27 - name: dataParameterName, 28 - }, 29 - ], 30 - statements: [ 31 - tsc.returnStatement({ 32 - expression: tsc.awaitExpression({ 33 - expression: tsc.callExpression({ 34 - functionName: tsc.propertyAccessExpression({ 35 - expression: symbol.placeholder, 36 - name: identifiers.parseAsync, 37 - }), 38 - parameters: [tsc.identifier({ text: dataParameterName })], 39 - }), 40 - }), 41 - }), 42 - ], 43 - }); 21 + return $.func() 22 + .async() 23 + .param(dataParameterName) 24 + .do( 25 + $.return( 26 + $(symbol.placeholder) 27 + .attr(identifiers.parseAsync) 28 + .call($(dataParameterName)) 29 + .await(), 30 + ), 31 + ); 44 32 }; 45 33 46 34 export const createResponseValidatorV3 = ({ 47 35 operation, 48 36 plugin, 49 - }: ValidatorArgs): ts.ArrowFunction | undefined => { 37 + }: ValidatorArgs): TsDsl | undefined => { 50 38 const symbol = plugin.getSymbol({ 51 39 category: 'schema', 52 40 resource: 'operation', ··· 57 45 if (!symbol) return; 58 46 59 47 const dataParameterName = 'data'; 60 - 61 - return tsc.arrowFunction({ 62 - async: true, 63 - parameters: [ 64 - { 65 - name: dataParameterName, 66 - }, 67 - ], 68 - statements: [ 69 - tsc.returnStatement({ 70 - expression: tsc.awaitExpression({ 71 - expression: tsc.callExpression({ 72 - functionName: tsc.propertyAccessExpression({ 73 - expression: symbol.placeholder, 74 - name: identifiers.parseAsync, 75 - }), 76 - parameters: [tsc.identifier({ text: dataParameterName })], 77 - }), 78 - }), 79 - }), 80 - ], 81 - }); 48 + return $.func() 49 + .async() 50 + .param(dataParameterName) 51 + .do( 52 + $.return( 53 + $(symbol.placeholder) 54 + .attr(identifiers.parseAsync) 55 + .call($(dataParameterName)) 56 + .await(), 57 + ), 58 + ); 82 59 };
+26 -49
packages/openapi-ts/src/plugins/zod/v4/api.ts
··· 1 - import type ts from 'typescript'; 2 - 3 - import { tsc } from '~/tsc'; 1 + import type { TsDsl } from '~/ts-dsl'; 2 + import { $ } from '~/ts-dsl'; 4 3 5 4 import { identifiers } from '../constants'; 6 5 import type { ValidatorArgs } from '../shared/types'; ··· 8 7 export const createRequestValidatorV4 = ({ 9 8 operation, 10 9 plugin, 11 - }: ValidatorArgs): ts.ArrowFunction | undefined => { 10 + }: ValidatorArgs): TsDsl | undefined => { 12 11 const symbol = plugin.getSymbol({ 13 12 category: 'schema', 14 13 resource: 'operation', ··· 19 18 if (!symbol) return; 20 19 21 20 const dataParameterName = 'data'; 22 - 23 - return tsc.arrowFunction({ 24 - async: true, 25 - parameters: [ 26 - { 27 - name: dataParameterName, 28 - }, 29 - ], 30 - statements: [ 31 - tsc.returnStatement({ 32 - expression: tsc.awaitExpression({ 33 - expression: tsc.callExpression({ 34 - functionName: tsc.propertyAccessExpression({ 35 - expression: symbol.placeholder, 36 - name: identifiers.parseAsync, 37 - }), 38 - parameters: [tsc.identifier({ text: dataParameterName })], 39 - }), 40 - }), 41 - }), 42 - ], 43 - }); 21 + return $.func() 22 + .async() 23 + .param(dataParameterName) 24 + .do( 25 + $.return( 26 + $(symbol.placeholder) 27 + .attr(identifiers.parseAsync) 28 + .call(dataParameterName) 29 + .await(), 30 + ), 31 + ); 44 32 }; 45 33 46 34 export const createResponseValidatorV4 = ({ 47 35 operation, 48 36 plugin, 49 - }: ValidatorArgs): ts.ArrowFunction | undefined => { 37 + }: ValidatorArgs): TsDsl | undefined => { 50 38 const symbol = plugin.getSymbol({ 51 39 category: 'schema', 52 40 resource: 'operation', ··· 57 45 if (!symbol) return; 58 46 59 47 const dataParameterName = 'data'; 60 - 61 - return tsc.arrowFunction({ 62 - async: true, 63 - parameters: [ 64 - { 65 - name: dataParameterName, 66 - }, 67 - ], 68 - statements: [ 69 - tsc.returnStatement({ 70 - expression: tsc.awaitExpression({ 71 - expression: tsc.callExpression({ 72 - functionName: tsc.propertyAccessExpression({ 73 - expression: symbol.placeholder, 74 - name: identifiers.parseAsync, 75 - }), 76 - parameters: [tsc.identifier({ text: dataParameterName })], 77 - }), 78 - }), 79 - }), 80 - ], 81 - }); 48 + return $.func() 49 + .async() 50 + .param(dataParameterName) 51 + .do( 52 + $.return( 53 + $(symbol.placeholder) 54 + .attr(identifiers.parseAsync) 55 + .call(dataParameterName) 56 + .await(), 57 + ), 58 + ); 82 59 };
+7 -4
packages/openapi-ts/src/ts-dsl/attr.ts
··· 13 13 ts.PropertyAccessExpression | ts.ElementAccessExpression 14 14 > { 15 15 private left: MaybeTsDsl<WithString>; 16 - private right: WithString<number>; 16 + private right: WithString<ts.MemberName> | number; 17 17 18 - constructor(left: MaybeTsDsl<WithString>, right: WithString<number>) { 18 + constructor( 19 + left: MaybeTsDsl<WithString>, 20 + right: WithString<ts.MemberName> | number, 21 + ) { 19 22 super(); 20 23 this.left = left; 21 24 this.right = right; ··· 40 43 return ts.factory.createPropertyAccessChain( 41 44 leftNode, 42 45 ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), 43 - ts.factory.createIdentifier(this.right), 46 + this.$expr(this.right), 44 47 ); 45 48 } 46 49 return ts.factory.createPropertyAccessExpression( 47 50 leftNode, 48 - ts.factory.createIdentifier(this.right), 51 + this.$expr(this.right), 49 52 ); 50 53 } 51 54 }
+5 -5
packages/openapi-ts/src/ts-dsl/field.ts
··· 13 13 ReadonlyMixin, 14 14 StaticMixin, 15 15 } from './mixins/modifiers'; 16 - import { createTypeAccessor } from './mixins/type'; 16 + import { createTypeAccessor, type TypeAccessor } from './mixins/type'; 17 17 import { ValueMixin } from './mixins/value'; 18 18 19 19 export class FieldTsDsl extends TsDsl<ts.PropertyDeclaration> { 20 20 private modifiers = createModifierAccessor(this); 21 21 private name: string; 22 - private _type = createTypeAccessor(this); 22 + private _type: TypeAccessor<FieldTsDsl> = createTypeAccessor(this); 23 + 24 + /** Sets the property's type. */ 25 + type: TypeAccessor<FieldTsDsl>['fn'] = this._type.fn; 23 26 24 27 constructor(name: string, fn?: (f: FieldTsDsl) => void) { 25 28 super(); 26 29 this.name = name; 27 30 fn?.(this); 28 31 } 29 - 30 - /** Sets the property's type. */ 31 - type = this._type.fn; 32 32 33 33 /** Builds the `PropertyDeclaration` node. */ 34 34 $render(): ts.PropertyDeclaration {
+5 -6
packages/openapi-ts/src/ts-dsl/func.ts
··· 18 18 } from './mixins/modifiers'; 19 19 import { OptionalMixin } from './mixins/optional'; 20 20 import { ParamMixin } from './mixins/param'; 21 - import { createTypeAccessor } from './mixins/type'; 21 + import { createTypeAccessor, type TypeAccessor } from './mixins/type'; 22 22 23 23 type FuncMode = 'arrow' | 'decl' | 'expr'; 24 24 ··· 32 32 private mode: FuncMode; 33 33 private modifiers = createModifierAccessor(this); 34 34 private name?: string; 35 - private _returns = createTypeAccessor(this); 35 + private _returns: TypeAccessor<ImplFuncTsDsl<M>> = createTypeAccessor(this); 36 + 37 + /** Sets the return type. */ 38 + returns: TypeAccessor<ImplFuncTsDsl<M>>['fn'] = this._returns.fn; 36 39 37 40 constructor(); 38 41 constructor(fn: (f: ImplFuncTsDsl<'arrow'>) => void); ··· 67 70 this.mode = 'expr'; 68 71 return this as unknown as FuncTsDsl<'expr'>; 69 72 } 70 - 71 - /** Sets the return type. */ 72 - returns = this._returns.fn; 73 - 74 73 $render(): M extends 'decl' 75 74 ? ts.FunctionDeclaration 76 75 : M extends 'expr'
+5 -5
packages/openapi-ts/src/ts-dsl/method.ts
··· 18 18 } from './mixins/modifiers'; 19 19 import { OptionalMixin } from './mixins/optional'; 20 20 import { ParamMixin } from './mixins/param'; 21 - import { createTypeAccessor } from './mixins/type'; 21 + import { createTypeAccessor, type TypeAccessor } from './mixins/type'; 22 22 23 23 export class MethodTsDsl extends TsDsl<ts.MethodDeclaration> { 24 24 private modifiers = createModifierAccessor(this); 25 25 private name: string; 26 - private _returns = createTypeAccessor(this); 26 + private _returns: TypeAccessor<MethodTsDsl> = createTypeAccessor(this); 27 + 28 + /** Sets the return type. */ 29 + returns: TypeAccessor<MethodTsDsl>['fn'] = this._returns.fn; 27 30 28 31 constructor(name: string, fn?: (m: MethodTsDsl) => void) { 29 32 super(); 30 33 this.name = name; 31 34 fn?.(this); 32 35 } 33 - 34 - /** Sets the return type. */ 35 - returns = this._returns.fn; 36 36 37 37 /** Builds the `MethodDeclaration` node. */ 38 38 $render(): ts.MethodDeclaration {
+6 -1
packages/openapi-ts/src/ts-dsl/mixins/access.ts
··· 1 + import type ts from 'typescript'; 2 + 1 3 import { AttrTsDsl } from '../attr'; 2 4 import type { MaybeTsDsl, WithString } from '../base'; 3 5 import { CallTsDsl } from '../call'; 4 6 5 7 export class AccessMixin { 6 8 /** Accesses a property on the current expression (e.g. `this.foo`). */ 7 - attr(this: MaybeTsDsl<WithString>, name: string | number): AttrTsDsl { 9 + attr( 10 + this: MaybeTsDsl<WithString>, 11 + name: WithString<ts.MemberName> | number, 12 + ): AttrTsDsl { 8 13 return new AttrTsDsl(this, name); 9 14 } 10 15 /** Calls the current expression as a function (e.g. `fn(arg1, arg2)`). */
+1 -1
packages/openapi-ts/src/ts-dsl/mixins/describe.ts
··· 5 5 TBase extends new (...args: ReadonlyArray<any>) => ITsDsl, 6 6 >(Base: TBase) { 7 7 const Mixin = class extends Base { 8 - protected _desc?: DescribeTsDsl; 8 + _desc?: DescribeTsDsl; 9 9 10 10 describe( 11 11 lines?: MaybeArray<string>,
+108
packages/openapi-ts/src/ts-dsl/mixins/modifiers.ts
··· 2 2 3 3 import type { TsDsl } from '../base'; 4 4 5 + /** 6 + * Creates an accessor for adding TypeScript modifiers to a parent DSL node. 7 + * 8 + * @param parent - The parent DSL node to which modifiers will be added. 9 + * @returns An object with a `list` method that returns the collected modifiers. 10 + */ 5 11 export function createModifierAccessor<Parent extends TsDsl>(parent: Parent) { 6 12 const modifiers: Array<ts.Modifier> = []; 7 13 14 + /** 15 + * Adds a modifier of the specified kind to the modifiers list if the condition is true. 16 + * 17 + * @param kind - The syntax kind of the modifier to add. 18 + * @param condition - Whether to add the modifier (default: true). 19 + * @returns The parent DSL node for chaining. 20 + */ 8 21 function _m(kind: ts.ModifierSyntaxKind, condition = true): Parent { 9 22 if (condition) { 10 23 modifiers.push(ts.factory.createModifier(kind)); ··· 14 27 15 28 Object.assign(parent, { _m }); // attaches to parent 16 29 30 + /** 31 + * Returns the list of collected modifiers. 32 + * 33 + * @returns Array of TypeScript modifiers. 34 + */ 17 35 function list() { 18 36 return modifiers; 19 37 } ··· 24 42 type Target = object & { 25 43 _m?(kind: ts.ModifierSyntaxKind, condition?: boolean): unknown; 26 44 }; 45 + 46 + /** 47 + * Mixin that adds an `abstract` modifier to a node. 48 + */ 27 49 export class AbstractMixin { 50 + /** 51 + * Adds the `abstract` keyword modifier if the condition is true. 52 + * 53 + * @param condition - Whether to add the modifier (default: true). 54 + * @returns The target object for chaining. 55 + */ 28 56 abstract<T extends Target>(this: T, condition: boolean = true): T { 29 57 return this._m!(ts.SyntaxKind.AbstractKeyword, condition) as T; 30 58 } 31 59 } 60 + 61 + /** 62 + * Mixin that adds an `async` modifier to a node. 63 + */ 32 64 export class AsyncMixin { 65 + /** 66 + * Adds the `async` keyword modifier if the condition is true. 67 + * 68 + * @param condition - Whether to add the modifier (default: true). 69 + * @returns The target object for chaining. 70 + */ 33 71 async<T extends Target>(this: T, condition: boolean = true): T { 34 72 return this._m!(ts.SyntaxKind.AsyncKeyword, condition) as T; 35 73 } 36 74 } 75 + 76 + /** 77 + * Mixin that adds a `default` modifier to a node. 78 + */ 37 79 export class DefaultMixin { 80 + /** 81 + * Adds the `default` keyword modifier if the condition is true. 82 + * 83 + * @param condition - Whether to add the modifier (default: true). 84 + * @returns The target object for chaining. 85 + */ 38 86 default<T extends Target>(this: T, condition: boolean = true): T { 39 87 return this._m!(ts.SyntaxKind.DefaultKeyword, condition) as T; 40 88 } 41 89 } 90 + 91 + /** 92 + * Mixin that adds an `export` modifier to a node. 93 + */ 42 94 export class ExportMixin { 95 + /** 96 + * Adds the `export` keyword modifier if the condition is true. 97 + * 98 + * @param condition - Whether to add the modifier (default: true). 99 + * @returns The target object for chaining. 100 + */ 43 101 export<T extends Target>(this: T, condition: boolean = true): T { 44 102 return this._m!(ts.SyntaxKind.ExportKeyword, condition) as T; 45 103 } 46 104 } 105 + 106 + /** 107 + * Mixin that adds a `private` modifier to a node. 108 + */ 47 109 export class PrivateMixin { 110 + /** 111 + * Adds the `private` keyword modifier if the condition is true. 112 + * 113 + * @param condition - Whether to add the modifier (default: true). 114 + * @returns The target object for chaining. 115 + */ 48 116 private<T extends Target>(this: T, condition: boolean = true): T { 49 117 return this._m!(ts.SyntaxKind.PrivateKeyword, condition) as T; 50 118 } 51 119 } 120 + 121 + /** 122 + * Mixin that adds a `protected` modifier to a node. 123 + */ 52 124 export class ProtectedMixin { 125 + /** 126 + * Adds the `protected` keyword modifier if the condition is true. 127 + * 128 + * @param condition - Whether to add the modifier (default: true). 129 + * @returns The target object for chaining. 130 + */ 53 131 protected<T extends Target>(this: T, condition: boolean = true): T { 54 132 return this._m!(ts.SyntaxKind.ProtectedKeyword, condition) as T; 55 133 } 56 134 } 135 + 136 + /** 137 + * Mixin that adds a `public` modifier to a node. 138 + */ 57 139 export class PublicMixin { 140 + /** 141 + * Adds the `public` keyword modifier if the condition is true. 142 + * 143 + * @param condition - Whether to add the modifier (default: true). 144 + * @returns The target object for chaining. 145 + */ 58 146 public<T extends Target>(this: T, condition: boolean = true): T { 59 147 return this._m!(ts.SyntaxKind.PublicKeyword, condition) as T; 60 148 } 61 149 } 150 + 151 + /** 152 + * Mixin that adds a `readonly` modifier to a node. 153 + */ 62 154 export class ReadonlyMixin { 155 + /** 156 + * Adds the `readonly` keyword modifier if the condition is true. 157 + * 158 + * @param condition - Whether to add the modifier (default: true). 159 + * @returns The target object for chaining. 160 + */ 63 161 readonly<T extends Target>(this: T, condition: boolean = true): T { 64 162 return this._m!(ts.SyntaxKind.ReadonlyKeyword, condition) as T; 65 163 } 66 164 } 165 + 166 + /** 167 + * Mixin that adds a `static` modifier to a node. 168 + */ 67 169 export class StaticMixin { 170 + /** 171 + * Adds the `static` keyword modifier if the condition is true. 172 + * 173 + * @param condition - Whether to add the modifier (default: true). 174 + * @returns The target object for chaining. 175 + */ 68 176 static<T extends Target>(this: T, condition: boolean = true): T { 69 177 return this._m!(ts.SyntaxKind.StaticKeyword, condition) as T; 70 178 }
+9 -1
packages/openapi-ts/src/ts-dsl/mixins/type.ts
··· 4 4 import type { TypeInput } from '../type'; 5 5 import { TypeTsDsl } from '../type'; 6 6 7 + export interface TypeAccessor<Parent extends TsDsl> { 8 + $render(): ts.TypeNode | undefined; 9 + fn(): ReturnType<typeof TypeTsDsl>; 10 + fn(type: TypeInput): Parent; 11 + } 12 + 7 13 /** Provides `.type()`-like access with internal state management. */ 8 - export function createTypeAccessor<Parent extends TsDsl>(parent: Parent) { 14 + export function createTypeAccessor<Parent extends TsDsl>( 15 + parent: Parent, 16 + ): TypeAccessor<Parent> { 9 17 const $type = parent['$type'].bind(parent); 10 18 11 19 let _type: ReturnType<typeof TypeTsDsl> | undefined;
+5 -5
packages/openapi-ts/src/ts-dsl/param.ts
··· 6 6 import { DecoratorMixin } from './mixins/decorator'; 7 7 import { OptionalMixin } from './mixins/optional'; 8 8 import { PatternMixin } from './mixins/pattern'; 9 - import { createTypeAccessor } from './mixins/type'; 9 + import { createTypeAccessor, type TypeAccessor } from './mixins/type'; 10 10 import { ValueMixin } from './mixins/value'; 11 11 12 12 export class ParamTsDsl extends TsDsl<ts.ParameterDeclaration> { 13 13 private name?: string; 14 - private _type = createTypeAccessor(this); 14 + private _type: TypeAccessor<ParamTsDsl> = createTypeAccessor(this); 15 + 16 + /** Sets the parameter's type. */ 17 + type: TypeAccessor<ParamTsDsl>['fn'] = this._type.fn; 15 18 16 19 constructor( 17 20 name: string | ((p: ParamTsDsl) => void), ··· 25 28 name(this); 26 29 } 27 30 } 28 - 29 - /** Sets the parameter's type. */ 30 - type = this._type.fn; 31 31 32 32 $render(): ts.ParameterDeclaration { 33 33 const name = this.$pattern() ?? this.name;