Skip to content

Commit 79ffdb2

Browse files
committed
feat: includeClient option
1 parent b5fb44a commit 79ffdb2

24 files changed

+144
-65
lines changed

packages/typed-openapi/src/generator.ts

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import { wrapWithQuotesIfNeeded } from "./string-utils.ts";
99

1010
// Default success status codes (2xx and 3xx ranges)
1111
export const DEFAULT_SUCCESS_STATUS_CODES = [
12-
200, 201, 202, 203, 204, 205, 206, 207, 208, 226,
13-
300, 301, 302, 303, 304, 305, 306, 307, 308
12+
200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305, 306, 307, 308,
1413
] as const;
1514

1615
type GeneratorOptions = ReturnType<typeof mapOpenApiEndpoints> & {
1716
runtime?: "none" | keyof typeof runtimeValidationGenerator;
1817
schemasOnly?: boolean;
1918
successStatusCodes?: readonly number[];
19+
includeClient?: boolean;
2020
};
2121
type GeneratorContext = Required<GeneratorOptions>;
2222

@@ -70,12 +70,14 @@ export const generateFile = (options: GeneratorOptions) => {
7070
const ctx = {
7171
...options,
7272
runtime: options.runtime ?? "none",
73-
successStatusCodes: options.successStatusCodes ?? DEFAULT_SUCCESS_STATUS_CODES
73+
successStatusCodes: options.successStatusCodes ?? DEFAULT_SUCCESS_STATUS_CODES,
74+
includeClient: options.includeClient ?? true,
7475
} as GeneratorContext;
7576

7677
const schemaList = generateSchemaList(ctx);
7778
const endpointSchemaList = options.schemasOnly ? "" : generateEndpointSchemaList(ctx);
78-
const apiClient = options.schemasOnly ? "" : generateApiClient(ctx);
79+
const endpointByMethod = options.schemasOnly ? "" : generateEndpointByMethod(ctx);
80+
const apiClient = options.schemasOnly || !ctx.includeClient ? "" : generateApiClient(ctx);
7981

8082
const transform =
8183
ctx.runtime === "none"
@@ -107,6 +109,7 @@ export const generateFile = (options: GeneratorOptions) => {
107109

108110
const file = `
109111
${transform(schemaList + endpointSchemaList)}
112+
${endpointByMethod}
110113
${apiClient}
111114
`;
112115

@@ -226,11 +229,7 @@ const generateEndpointSchemaList = (ctx: GeneratorContext) => {
226229
}).value
227230
: endpoint.response.value
228231
},
229-
${
230-
endpoint.responses
231-
? `responses: ${generateResponsesObject(endpoint.responses, ctx)},`
232-
: ""
233-
}
232+
${endpoint.responses ? `responses: ${generateResponsesObject(endpoint.responses, ctx)},` : ""}
234233
${
235234
endpoint.responseHeaders
236235
? `responseHeaders: ${responseHeadersObjectToString(endpoint.responseHeaders, ctx)},`
@@ -259,9 +258,11 @@ const generateEndpointByMethod = (ctx: GeneratorContext) => {
259258
${Object.entries(byMethods)
260259
.map(([method, list]) => {
261260
return `${method}: {
262-
${list.map(
263-
(endpoint) => `"${endpoint.path}": ${ctx.runtime === "none" ? "Endpoints." : ""}${endpoint.meta.alias}`,
264-
)}
261+
${list
262+
.map(
263+
(endpoint) => `"${endpoint.path}": ${ctx.runtime === "none" ? "Endpoints." : ""}${endpoint.meta.alias}`,
264+
)
265+
.join(",\n")}
265266
}`;
266267
})
267268
.join(",\n")}
@@ -283,9 +284,12 @@ const generateEndpointByMethod = (ctx: GeneratorContext) => {
283284
};
284285

285286
const generateApiClient = (ctx: GeneratorContext) => {
287+
if (!ctx.includeClient) {
288+
return "";
289+
}
290+
286291
const { endpointList } = ctx;
287292
const byMethods = groupBy(endpointList, "method");
288-
const endpointSchemaList = generateEndpointByMethod(ctx);
289293

290294
// Generate the StatusCode type from the configured success status codes
291295
const generateStatusCodeType = (statusCodes: readonly number[]) => {
@@ -337,7 +341,7 @@ export type Fetcher = (method: Method, url: string, parameters?: EndpointParamet
337341
export type StatusCode = ${statusCodeType};
338342
339343
// Error handling types
340-
export type ApiResponse<TSuccess, TAllResponses extends Record<string | number, unknown> = {}> =
344+
export type TypedApiResponse<TSuccess, TAllResponses extends Record<string | number, unknown> = {}> =
341345
(keyof TAllResponses extends never
342346
? {
343347
ok: true;
@@ -376,7 +380,7 @@ export type ApiResponse<TSuccess, TAllResponses extends Record<string | number,
376380
377381
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
378382
? TResponses extends Record<string, unknown>
379-
? ApiResponse<TSuccess, TResponses>
383+
? TypedApiResponse<TSuccess, TResponses>
380384
: { ok: true; status: number; data: TSuccess }
381385
: TEndpoint extends { response: infer TSuccess }
382386
? { ok: true; status: number; data: TSuccess }
@@ -461,7 +465,13 @@ export class ApiClient {
461465
return this.fetcher("${method}", this.baseUrl + path, requestParams)
462466
.then(response => this.parseResponse(response))${match(ctx.runtime)
463467
.with("zod", "yup", () => `as Promise<${infer(`TEndpoint["response"]`)}>`)
464-
.with("arktype", "io-ts", "typebox", "valibot", () => `as Promise<${infer(`TEndpoint`) + `["response"]`}>`)
468+
.with(
469+
"arktype",
470+
"io-ts",
471+
"typebox",
472+
"valibot",
473+
() => `as Promise<${infer(`TEndpoint`) + `["response"]`}>`,
474+
)
465475
.otherwise(() => `as Promise<TEndpoint["response"]>`)};
466476
}
467477
}
@@ -529,5 +539,5 @@ export function createApiClient(fetcher: Fetcher, baseUrl?: string) {
529539
// </ApiClient
530540
`;
531541

532-
return endpointSchemaList + apiClientTypes + apiClient;
542+
return apiClientTypes + apiClient;
533543
};

packages/typed-openapi/tests/generator.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ describe("generator", () => {
348348
| 308;
349349
350350
// Error handling types
351-
export type ApiResponse<
351+
export type TypedApiResponse<
352352
TSuccess,
353353
TAllResponses extends Record<string | number, unknown> = {},
354354
> = keyof TAllResponses extends never
@@ -389,7 +389,7 @@ describe("generator", () => {
389389
390390
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
391391
? TResponses extends Record<string, unknown>
392-
? ApiResponse<TSuccess, TResponses>
392+
? TypedApiResponse<TSuccess, TResponses>
393393
: { ok: true; status: number; data: TSuccess }
394394
: TEndpoint extends { response: infer TSuccess }
395395
? { ok: true; status: number; data: TSuccess }
@@ -994,7 +994,7 @@ describe("generator", () => {
994994
| 308;
995995
996996
// Error handling types
997-
export type ApiResponse<
997+
export type TypedApiResponse<
998998
TSuccess,
999999
TAllResponses extends Record<string | number, unknown> = {},
10001000
> = keyof TAllResponses extends never
@@ -1035,7 +1035,7 @@ describe("generator", () => {
10351035
10361036
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
10371037
? TResponses extends Record<string, unknown>
1038-
? ApiResponse<TSuccess, TResponses>
1038+
? TypedApiResponse<TSuccess, TResponses>
10391039
: { ok: true; status: number; data: TSuccess }
10401040
: TEndpoint extends { response: infer TSuccess }
10411041
? { ok: true; status: number; data: TSuccess }
@@ -1316,7 +1316,7 @@ describe("generator", () => {
13161316
| 308;
13171317
13181318
// Error handling types
1319-
export type ApiResponse<
1319+
export type TypedApiResponse<
13201320
TSuccess,
13211321
TAllResponses extends Record<string | number, unknown> = {},
13221322
> = keyof TAllResponses extends never
@@ -1357,7 +1357,7 @@ describe("generator", () => {
13571357
13581358
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
13591359
? TResponses extends Record<string, unknown>
1360-
? ApiResponse<TSuccess, TResponses>
1360+
? TypedApiResponse<TSuccess, TResponses>
13611361
: { ok: true; status: number; data: TSuccess }
13621362
: TEndpoint extends { response: infer TSuccess }
13631363
? { ok: true; status: number; data: TSuccess }
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { it, expect } from "vitest";
2+
import type { OpenAPIObject } from "openapi3-ts/oas31";
3+
4+
import { generateFile } from "../src/generator.ts";
5+
import { mapOpenApiEndpoints } from "../src/map-openapi-endpoints.ts";
6+
import { prettify } from "../src/format.ts";
7+
8+
it("should exclude API client when includeClient is false", async () => {
9+
const openApiDoc: OpenAPIObject = {
10+
openapi: "3.0.0",
11+
info: { title: "Test API", version: "1.0.0" },
12+
paths: {
13+
"/test": {
14+
get: {
15+
operationId: "getTest",
16+
responses: {
17+
200: {
18+
description: "Success",
19+
content: {
20+
"application/json": {
21+
schema: { type: "object", properties: { message: { type: "string" } } },
22+
},
23+
},
24+
},
25+
},
26+
},
27+
},
28+
},
29+
};
30+
31+
const endpoints = mapOpenApiEndpoints(openApiDoc);
32+
33+
// Test with includeClient: false
34+
const withoutClient = await prettify(
35+
generateFile({
36+
...endpoints,
37+
includeClient: false,
38+
}),
39+
);
40+
41+
// Should not contain ApiClientTypes or ApiClient sections
42+
expect(withoutClient).not.toContain("// <ApiClientTypes>");
43+
expect(withoutClient).not.toContain("// <ApiClient>");
44+
expect(withoutClient).not.toContain("export class ApiClient");
45+
expect(withoutClient).not.toContain("export type EndpointParameters");
46+
expect(withoutClient).not.toContain("export type StatusCode");
47+
expect(withoutClient).not.toContain("export type TypedApiResponse");
48+
49+
// Should still contain schemas and endpoints
50+
expect(withoutClient).toContain("export namespace Schemas");
51+
expect(withoutClient).toContain("export namespace Endpoints");
52+
expect(withoutClient).toContain("export type EndpointByMethod");
53+
54+
// Test with includeClient: true (default)
55+
const withClient = await prettify(
56+
generateFile({
57+
...endpoints,
58+
includeClient: true,
59+
}),
60+
);
61+
62+
// Should contain ApiClientTypes and ApiClient sections
63+
expect(withClient).toContain("// <ApiClientTypes>");
64+
expect(withClient).toContain("// <ApiClient>");
65+
expect(withClient).toContain("export class ApiClient");
66+
expect(withClient).toContain("export type EndpointParameters");
67+
expect(withClient).toContain("export type StatusCode");
68+
expect(withClient).toContain("export type TypedApiResponse");
69+
});

packages/typed-openapi/tests/multiple-success-responses.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ describe("multiple success responses", () => {
206206
| 308;
207207
208208
// Error handling types
209-
export type ApiResponse<
209+
export type TypedApiResponse<
210210
TSuccess,
211211
TAllResponses extends Record<string | number, unknown> = {},
212212
> = keyof TAllResponses extends never
@@ -247,7 +247,7 @@ describe("multiple success responses", () => {
247247
248248
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
249249
? TResponses extends Record<string, unknown>
250-
? ApiResponse<TSuccess, TResponses>
250+
? TypedApiResponse<TSuccess, TResponses>
251251
: { ok: true; status: number; data: TSuccess }
252252
: TEndpoint extends { response: infer TSuccess }
253253
? { ok: true; status: number; data: TSuccess }

packages/typed-openapi/tests/snapshots/docker.openapi.client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2606,7 +2606,7 @@ export type StatusCode =
26062606
| 308;
26072607

26082608
// Error handling types
2609-
export type ApiResponse<
2609+
export type TypedApiResponse<
26102610
TSuccess,
26112611
TAllResponses extends Record<string | number, unknown> = {},
26122612
> = keyof TAllResponses extends never
@@ -2647,7 +2647,7 @@ export type ApiResponse<
26472647

26482648
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
26492649
? TResponses extends Record<string, unknown>
2650-
? ApiResponse<TSuccess, TResponses>
2650+
? TypedApiResponse<TSuccess, TResponses>
26512651
: { ok: true; status: number; data: TSuccess }
26522652
: TEndpoint extends { response: infer TSuccess }
26532653
? { ok: true; status: number; data: TSuccess }

packages/typed-openapi/tests/snapshots/docker.openapi.io-ts.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4371,7 +4371,7 @@ export type StatusCode =
43714371
| 308;
43724372

43734373
// Error handling types
4374-
export type ApiResponse<
4374+
export type TypedApiResponse<
43754375
TSuccess,
43764376
TAllResponses extends Record<string | number, unknown> = {},
43774377
> = keyof TAllResponses extends never
@@ -4412,7 +4412,7 @@ export type ApiResponse<
44124412

44134413
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
44144414
? TResponses extends Record<string, unknown>
4415-
? ApiResponse<TSuccess, TResponses>
4415+
? TypedApiResponse<TSuccess, TResponses>
44164416
: { ok: true; status: number; data: TSuccess }
44174417
: TEndpoint extends { response: infer TSuccess }
44184418
? { ok: true; status: number; data: TSuccess }

packages/typed-openapi/tests/snapshots/docker.openapi.typebox.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4648,7 +4648,7 @@ export type StatusCode =
46484648
| 308;
46494649

46504650
// Error handling types
4651-
export type ApiResponse<
4651+
export type TypedApiResponse<
46524652
TSuccess,
46534653
TAllResponses extends Record<string | number, unknown> = {},
46544654
> = keyof TAllResponses extends never
@@ -4689,7 +4689,7 @@ export type ApiResponse<
46894689

46904690
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
46914691
? TResponses extends Record<string, unknown>
4692-
? ApiResponse<TSuccess, TResponses>
4692+
? TypedApiResponse<TSuccess, TResponses>
46934693
: { ok: true; status: number; data: TSuccess }
46944694
: TEndpoint extends { response: infer TSuccess }
46954695
? { ok: true; status: number; data: TSuccess }

packages/typed-openapi/tests/snapshots/docker.openapi.valibot.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4283,7 +4283,7 @@ export type StatusCode =
42834283
| 308;
42844284

42854285
// Error handling types
4286-
export type ApiResponse<
4286+
export type TypedApiResponse<
42874287
TSuccess,
42884288
TAllResponses extends Record<string | number, unknown> = {},
42894289
> = keyof TAllResponses extends never
@@ -4324,7 +4324,7 @@ export type ApiResponse<
43244324

43254325
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
43264326
? TResponses extends Record<string, unknown>
4327-
? ApiResponse<TSuccess, TResponses>
4327+
? TypedApiResponse<TSuccess, TResponses>
43284328
: { ok: true; status: number; data: TSuccess }
43294329
: TEndpoint extends { response: infer TSuccess }
43304330
? { ok: true; status: number; data: TSuccess }

packages/typed-openapi/tests/snapshots/docker.openapi.yup.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4807,7 +4807,7 @@ export type StatusCode =
48074807
| 308;
48084808

48094809
// Error handling types
4810-
export type ApiResponse<
4810+
export type TypedApiResponse<
48114811
TSuccess,
48124812
TAllResponses extends Record<string | number, unknown> = {},
48134813
> = keyof TAllResponses extends never
@@ -4848,7 +4848,7 @@ export type ApiResponse<
48484848

48494849
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
48504850
? TResponses extends Record<string, unknown>
4851-
? ApiResponse<TSuccess, TResponses>
4851+
? TypedApiResponse<TSuccess, TResponses>
48524852
: { ok: true; status: number; data: TSuccess }
48534853
: TEndpoint extends { response: infer TSuccess }
48544854
? { ok: true; status: number; data: TSuccess }

packages/typed-openapi/tests/snapshots/docker.openapi.zod.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4269,7 +4269,7 @@ export type StatusCode =
42694269
| 308;
42704270

42714271
// Error handling types
4272-
export type ApiResponse<
4272+
export type TypedApiResponse<
42734273
TSuccess,
42744274
TAllResponses extends Record<string | number, unknown> = {},
42754275
> = keyof TAllResponses extends never
@@ -4310,7 +4310,7 @@ export type ApiResponse<
43104310

43114311
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
43124312
? TResponses extends Record<string, unknown>
4313-
? ApiResponse<TSuccess, TResponses>
4313+
? TypedApiResponse<TSuccess, TResponses>
43144314
: { ok: true; status: number; data: TSuccess }
43154315
: TEndpoint extends { response: infer TSuccess }
43164316
? { ok: true; status: number; data: TSuccess }

0 commit comments

Comments
 (0)