Skip to content

Commit a718a33

Browse files
authored
feat: add CLI option --schemas-only to allow generation of only the schema without endpoints and api client (astahmer#74)
1 parent 324f3f4 commit a718a33

File tree

5 files changed

+89
-30
lines changed

5 files changed

+89
-30
lines changed

.changeset/thick-readers-turn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"typed-openapi": minor
3+
---
4+
5+
Add CLI option `--schemas-only` to allow generation of only the schema without endpoints and api client

packages/typed-openapi/src/cli.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ cli
1616
{ default: "none" },
1717
)
1818
.option("--tanstack [name]", "Generate tanstack client, defaults to false, can optionally specify a name for the generated file")
19+
.option(
20+
"--schemas-only",
21+
"Only generate schemas, skipping client generation (defaults to false)",
22+
{ default : false },
23+
)
1924
.action(async (input, _options) => {
2025
return generateClientFiles(input, _options);
2126
});

packages/typed-openapi/src/generate-client-files.ts

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,35 +12,40 @@ const cwd = process.cwd();
1212
const now = new Date();
1313

1414
export const optionsSchema = type({
15-
"output?": "string",
16-
runtime: allowedRuntimes,
17-
tanstack: "boolean | string"
15+
"output?": "string",
16+
runtime: allowedRuntimes,
17+
tanstack: "boolean | string",
18+
schemasOnly: "boolean",
1819
});
1920

2021
export async function generateClientFiles(input: string, options: typeof optionsSchema.infer) {
21-
const openApiDoc = (await SwaggerParser.bundle(input)) as OpenAPIObject;
22-
23-
const ctx = mapOpenApiEndpoints(openApiDoc);
24-
console.log(`Found ${ctx.endpointList.length} endpoints`);
25-
26-
const content = await prettify(generateFile({ ...ctx, runtime: options.runtime }));
27-
const outputPath = join(
28-
cwd,
29-
options.output ?? input + `.${options.runtime === "none" ? "client" : options.runtime}.ts`,
30-
);
31-
32-
console.log("Generating client...", outputPath);
33-
await writeFile(outputPath, content);
34-
35-
if (options.tanstack) {
36-
const tanstackContent = await generateTanstackQueryFile({
37-
...ctx,
38-
relativeApiClientPath: './' + basename(outputPath),
39-
});
40-
const tanstackOutputPath = join(dirname(outputPath), typeof options.tanstack === "string" ? options.tanstack : `tanstack.client.ts`);
41-
console.log("Generating tanstack client...", tanstackOutputPath);
42-
await writeFile(tanstackOutputPath, tanstackContent);
43-
}
44-
45-
console.log(`Done in ${new Date().getTime() - now.getTime()}ms !`);
22+
const openApiDoc = (await SwaggerParser.bundle(input)) as OpenAPIObject;
23+
24+
const ctx = mapOpenApiEndpoints(openApiDoc);
25+
console.log(`Found ${ctx.endpointList.length} endpoints`);
26+
27+
const content = await prettify(generateFile({
28+
...ctx,
29+
runtime: options.runtime,
30+
schemasOnly: options.schemasOnly,
31+
}));
32+
const outputPath = join(
33+
cwd,
34+
options.output ?? input + `.${options.runtime === "none" ? "client" : options.runtime}.ts`,
35+
);
36+
37+
console.log("Generating client...", outputPath);
38+
await writeFile(outputPath, content);
39+
40+
if (options.tanstack) {
41+
const tanstackContent = await generateTanstackQueryFile({
42+
...ctx,
43+
relativeApiClientPath: './' + basename(outputPath),
44+
});
45+
const tanstackOutputPath = join(dirname(outputPath), typeof options.tanstack === "string" ? options.tanstack : `tanstack.client.ts`);
46+
console.log("Generating tanstack client...", tanstackOutputPath);
47+
await writeFile(tanstackOutputPath, tanstackContent);
48+
}
49+
50+
console.log(`Done in ${new Date().getTime() - now.getTime()}ms !`);
4651
}

packages/typed-openapi/src/generator.ts

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

1010
type GeneratorOptions = ReturnType<typeof mapOpenApiEndpoints> & {
1111
runtime?: "none" | keyof typeof runtimeValidationGenerator;
12+
schemasOnly?: boolean;
1213
};
1314
type GeneratorContext = Required<GeneratorOptions>;
1415

@@ -56,8 +57,8 @@ export const generateFile = (options: GeneratorOptions) => {
5657
const ctx = { ...options, runtime: options.runtime ?? "none" } as GeneratorContext;
5758

5859
const schemaList = generateSchemaList(ctx);
59-
const endpointSchemaList = generateEndpointSchemaList(ctx);
60-
const apiClient = generateApiClient(ctx);
60+
const endpointSchemaList = options.schemasOnly ? "" : generateEndpointSchemaList(ctx);
61+
const apiClient = options.schemasOnly ? "" : generateApiClient(ctx);
6162

6263
const transform =
6364
ctx.runtime === "none"

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,49 @@ describe("generator", () => {
414414
`);
415415
});
416416

417+
test("petstore schema only", async ({ expect }) => {
418+
const openApiDoc = (await SwaggerParser.parse("./tests/samples/petstore.yaml")) as OpenAPIObject;
419+
expect(await prettify(generateFile({...mapOpenApiEndpoints(openApiDoc), schemasOnly: true}))).toMatchInlineSnapshot(`
420+
"export namespace Schemas {
421+
// <Schemas>
422+
export type Order = Partial<{
423+
id: number;
424+
petId: number;
425+
quantity: number;
426+
shipDate: string;
427+
status: "placed" | "approved" | "delivered";
428+
complete: boolean;
429+
}>;
430+
export type Address = Partial<{ street: string; city: string; state: string; zip: string }>;
431+
export type Customer = Partial<{ id: number; username: string; address: Array<Address> }>;
432+
export type Category = Partial<{ id: number; name: string }>;
433+
export type User = Partial<{
434+
id: number;
435+
username: string;
436+
firstName: string;
437+
lastName: string;
438+
email: string;
439+
password: string;
440+
phone: string;
441+
userStatus: number;
442+
}>;
443+
export type Tag = Partial<{ id: number; name: string }>;
444+
export type Pet = {
445+
id?: number | undefined;
446+
name: string;
447+
category?: Category | undefined;
448+
photoUrls: Array<string>;
449+
tags?: Array<Tag> | undefined;
450+
status?: ("available" | "pending" | "sold") | undefined;
451+
};
452+
export type ApiResponse = Partial<{ code: number; type: string; message: string }>;
453+
454+
// </Schemas>
455+
}
456+
"
457+
`);
458+
});
459+
417460
test("nullable string", async ({ expect }) => {
418461
expect(await prettify(generateFile(mapOpenApiEndpoints({
419462
"openapi": "3.0.0",

0 commit comments

Comments
 (0)