Skip to content

Commit 11f23c6

Browse files
authored
Merge pull request #24 from povio/feature/exclude-path
Exclude path regex & Exclude redundant zod schemas
2 parents e42198b + 4fd1eeb commit 11f23c6

24 files changed

Lines changed: 594 additions & 544 deletions

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ yarn openapi-codegen generate --input http://localhost:3001/docs-json --standalo
3636

3737
--splitByTags Organize output into separate folders based on OpenAPI operation tags (default: true)
3838
--defaultTag (Requires `--splitByTags`) Default tag for shared code across multiple tags (default: 'Common')
39-
--excludeTags (Requires `--splitByTags`) Comma-separated list of tags to exclude from generation
39+
40+
--excludeTags Comma-separated list of tags to exclude from generation
41+
--excludePathRegex Exclude operations whose paths match the given regular expression
42+
--excludeRedundantZodSchemas Exclude any redundant Zod schemas (default: true)
4043

4144
--tsNamespaces Wrap generated files in TypeScript namespaces (default: true)
4245
--importPath Module import style for generated files (default: 'ts'; options: 'ts' | 'relative' | 'absolute')
@@ -60,7 +63,10 @@ yarn openapi-codegen generate --input http://localhost:3001/docs-json --standalo
6063
6164
--splitByTags Organize output into separate folders based on OpenAPI operation tags (default: true)
6265
--defaultTag (Requires `--splitByTags`) Default tag for shared code across multiple tags (default: 'Common')
63-
--excludeTags (Requires `--splitByTags`) Comma-separated list of tags to exclude from generation
66+
67+
--excludeTags Comma-separated list of tags to exclude from generation
68+
--excludePathRegex Exclude operations whose paths match the given regular expression
69+
--excludeRedundantZodSchemas Exclude any redundant Zod schemas (default: true)
6470
```
6571
6672
## Development

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@povio/openapi-codegen-cli",
3-
"version": "0.9.2",
3+
"version": "0.10.0",
44
"main": "./dist/index.js",
55
"bin": {
66
"openapi-codegen": "./dist/sh.js"

src/commands/check.command.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { DEFAULT_GENERATE_OPTIONS } from "src/generators/const/options.const";
12
import yargs from "yargs";
23
import { logBanner, logVariable } from "../helpers/cli.helper";
34
import { getVersion } from "../helpers/version.helper";
@@ -8,15 +9,25 @@ class CheckOptions implements CheckParams {
89
@YargOption({ envAlias: "input", demandOption: true })
910
input!: string;
1011

11-
@YargOption({ envAlias: "splitByTags", default: true, type: "boolean" })
12+
@YargOption({ envAlias: "splitByTags", default: DEFAULT_GENERATE_OPTIONS.splitByTags, type: "boolean" })
1213
splitByTags!: boolean;
1314

14-
@YargOption({ envAlias: "defaultTag", default: "Common" })
15+
@YargOption({ envAlias: "defaultTag", default: DEFAULT_GENERATE_OPTIONS.defaultTag })
1516
defaultTag!: string;
1617

17-
@YargOption({ envAlias: "excludeTags", default: "" })
18+
@YargOption({ envAlias: "excludeTags", default: DEFAULT_GENERATE_OPTIONS.excludeTags.join(",") })
1819
excludeTags!: string;
1920

21+
@YargOption({ envAlias: "excludePathRegex", default: DEFAULT_GENERATE_OPTIONS.excludePathRegex })
22+
excludePathRegex!: string;
23+
24+
@YargOption({
25+
envAlias: "excludeRedundantZodSchemas",
26+
default: DEFAULT_GENERATE_OPTIONS.excludeRedundantZodSchemas,
27+
type: "boolean",
28+
})
29+
excludeRedundantZodSchemas!: boolean;
30+
2031
@YargOption({ envAlias: "verbose", default: false, type: "boolean" })
2132
verbose!: boolean;
2233
}

src/commands/generate.command.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
import { DEFAULT_GENERATE_OPTIONS } from "src/generators/const/options.const";
12
import yargs from "yargs";
23
import { logBanner, logVariable } from "../helpers/cli.helper";
34
import { getVersion } from "../helpers/version.helper";
45
import { getBuilder, YargOption } from "../helpers/yargs.helper";
56
import { generate, GenerateParams } from "./generate";
6-
import { DEFAULT_GENERATE_OPTIONS } from "src/generators/const/options.const";
77

88
class GenerateOptions implements GenerateParams {
99
@YargOption({ envAlias: "input", demandOption: true })
@@ -24,6 +24,16 @@ class GenerateOptions implements GenerateParams {
2424
@YargOption({ envAlias: "excludeTags", default: DEFAULT_GENERATE_OPTIONS.excludeTags.join(",") })
2525
excludeTags!: string;
2626

27+
@YargOption({ envAlias: "excludePathRegex", default: DEFAULT_GENERATE_OPTIONS.excludePathRegex })
28+
excludePathRegex!: string;
29+
30+
@YargOption({
31+
envAlias: "excludeRedundantZodSchemas",
32+
default: DEFAULT_GENERATE_OPTIONS.excludeRedundantZodSchemas,
33+
type: "boolean",
34+
})
35+
excludeRedundantZodSchemas!: boolean;
36+
2737
@YargOption({ envAlias: "importPath", default: DEFAULT_GENERATE_OPTIONS.importPath })
2838
importPath!: "ts" | "relative" | "absolute";
2939

src/generators/checkOpenAPIDoc.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ export function checkOpenAPIDoc(openApiDoc: OpenAPIV3.Document, cliOptions?: Par
2323
);
2424
});
2525
} else {
26-
const outputs = [...data.keys()].reduce((acc, tag) => {
27-
const excludedTag = options.excludeTags.find((excludeTag) => excludeTag.toLowerCase() === tag.toLowerCase());
28-
return excludedTag ? acc : [...acc, ...getOutputFileNames(tag, options)];
29-
}, [] as string[]);
26+
const outputs = [...data.keys()].reduce(
27+
(acc, tag) => [...acc, ...getOutputFileNames(tag, options)],
28+
[] as string[],
29+
);
3030
console.log(`${chk.green("Outputs:")}\n${outputs.map((output) => `- ${output}`).join("\n")}\n`);
3131
}
3232

src/generators/const/options.const.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ export const DEFAULT_GENERATE_OPTIONS: GenerateOptions = {
88
output: "output",
99
splitByTags: true,
1010
defaultTag: "Common",
11-
excludeTags: [], // TODO: Only works for isolated tags
11+
excludeTags: [],
12+
excludePathRegex: "",
13+
excludeRedundantZodSchemas: true,
1214
tsNamespaces: true,
1315
importPath: "ts",
1416
configs: {

src/generators/core/SchemaResolver.class.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,18 @@ import {
1010
autocorrectRef,
1111
getSchemaNameByRef,
1212
getSchemaRef,
13-
getUniqueOperationName,
14-
getUniqueOperationNamesWithoutSplitByTags,
1513
isMediaTypeAllowed,
1614
isParamMediaTypeAllowed,
15+
isPathExcluded,
1716
} from "../utils/openapi.utils";
17+
import {
18+
getOperationsByTag,
19+
getUniqueOperationName,
20+
getUniqueOperationNamesWithoutSplitByTags,
21+
isOperationExcluded,
22+
} from "../utils/operation.utils";
1823
import { snakeToCamel } from "../utils/string.utils";
19-
import { formatTag, getOperationsByTag, getOperationTag } from "../utils/tag.utils";
24+
import { formatTag, getOperationTag } from "../utils/tag.utils";
2025
import {
2126
getBodyZodSchemaName,
2227
getResponseZodSchemaName,
@@ -26,18 +31,17 @@ import {
2631
import { DependencyGraph, getOpenAPISchemaDependencyGraph } from "./openapi/getOpenAPISchemaDependencyGraph";
2732
import { getDeepSchemaRefObjs, getSchemaRefObjs } from "./openapi/getSchemaRefObjs";
2833
import { ZodSchema } from "./zod/ZodSchema.class";
34+
import { resolveExtractedEnumZodSchemaNames } from "./zod/enumExtraction/resolveExtractedEnumZodSchemaNames";
35+
import { resolveExtractedEnumZodSchemaTags } from "./zod/enumExtraction/resolveExtractedEnumZodSchemaTags";
36+
import { updateExtractedEnumZodSchemaData } from "./zod/enumExtraction/updateExtractedEnumZodSchemaData";
2937
import { getEnumZodSchemasFromOpenAPIDoc } from "./zod/getZodSchemasFromOpenAPIDoc";
30-
import {
31-
resolveExtractedEnumZodSchemaNames,
32-
resolveExtractedEnumZodSchemaTags,
33-
updateExtractedEnumZodSchemaData,
34-
} from "./zod/updateExtractedEnumZodSchemaData";
3538

3639
interface SchemaData {
3740
ref: string;
3841
name: string;
3942
zodSchemaName: string;
4043
tags: string[];
44+
deepRefOperations: OperationObject[];
4145
}
4246

4347
interface ZodSchemaData {
@@ -110,6 +114,10 @@ export class SchemaResolver {
110114
return this.docSchemas[getSchemaNameByRef(ref)] as OpenAPIV3.SchemaObject;
111115
}
112116

117+
getSchemaDataByName(name: string) {
118+
return this.schemaData.find((data) => data.name === name);
119+
}
120+
113121
getZodSchemaNameByRef(ref: string) {
114122
const zodSchemaName =
115123
this.getSchemaDataByRef(autocorrectRef(ref))?.zodSchemaName ??
@@ -284,17 +292,20 @@ export class SchemaResolver {
284292
const correctRef = autocorrectRef(ref);
285293
const name = getSchemaNameByRef(correctRef);
286294
const zodSchemaName = getZodSchemaName(name, this.options.schemaSuffix);
287-
this.schemaData.push({ ref: correctRef, name, zodSchemaName, tags: [] });
295+
this.schemaData.push({ ref: correctRef, name, zodSchemaName, tags: [], deepRefOperations: [] });
288296
});
289297

290298
for (const path in this.openApiDoc.paths) {
299+
if (isPathExcluded(path, this.options)) {
300+
continue;
301+
}
302+
291303
const pathItemObj = this.openApiDoc.paths[path] as OpenAPIV3.PathItemObject;
292304

293305
const pathItem = pick(pathItemObj, ALLOWED_METHODS);
294306
for (const method in pathItem) {
295307
const operation = pathItem[method as keyof typeof pathItem] as OperationObject | undefined;
296-
297-
if (!operation || (operation.deprecated && !this.options.withDeprecatedEndpoints)) {
308+
if (!operation || isOperationExcluded(operation, this.options)) {
298309
continue;
299310
}
300311

@@ -380,6 +391,7 @@ export class SchemaResolver {
380391
const schemaData = this.getSchemaDataByRef(schemaRef);
381392
if (schemaData) {
382393
schemaData.tags.push(tag);
394+
schemaData.deepRefOperations.push(operation);
383395
}
384396
});
385397
}

src/generators/core/endpoints/getEndpointsFromOpenAPIDoc.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import SwaggerParser from "@apidevtools/swagger-parser";
22
import { OpenAPIV3 } from "openapi-types";
33
import { describe, expect, test } from "vitest";
4+
import { DEFAULT_GENERATE_OPTIONS } from "../../const/options.const";
45
import { GenerateOptions } from "../../types/options";
56
import { SchemaResolver } from "../SchemaResolver.class";
67
import { getEndpointsFromOpenAPIDoc } from "./getEndpointsFromOpenAPIDoc";
@@ -83,8 +84,10 @@ const schemas = {
8384
} as const;
8485

8586
const generateOptions = {
87+
...DEFAULT_GENERATE_OPTIONS,
88+
tsNamespaces: false,
89+
extractEnums: false,
8690
schemaSuffix: "",
87-
defaultTag: "",
8891
} as GenerateOptions;
8992

9093
describe("getEndpointsFromOpenAPIDoc", () => {

src/generators/core/endpoints/getEndpointsFromOpenAPIDoc.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { STRING_SCHEMA, VOID_SCHEMA } from "src/generators/const/zod.const";
44
import { OperationObject } from "src/generators/types/openapi";
55
import { invalidVariableNameCharactersToCamel } from "src/generators/utils/js.utils";
66
import { isReferenceObject } from "src/generators/utils/openapi-schema.utils";
7+
import { getUniqueOperationName, isOperationExcluded } from "src/generators/utils/operation.utils";
78
import { formatTag, getOperationTag } from "src/generators/utils/tag.utils";
89
import {
910
getInvalidOperationIdError,
@@ -15,10 +16,10 @@ import { getResponseZodSchemaName } from "src/generators/utils/zod-schema.utils"
1516
import { Endpoint, EndpointParameter } from "../../types/endpoint";
1617
import { pick } from "../../utils/object.utils";
1718
import {
18-
getUniqueOperationName,
1919
isErrorStatus,
2020
isMainResponseStatus,
2121
isMediaTypeAllowed,
22+
isPathExcluded,
2223
replaceHyphenatedPath,
2324
} from "../../utils/openapi.utils";
2425
import { SchemaResolver } from "../SchemaResolver.class";
@@ -33,13 +34,17 @@ export function getEndpointsFromOpenAPIDoc(resolver: SchemaResolver) {
3334
const endpoints = [];
3435

3536
for (const path in resolver.openApiDoc.paths) {
37+
if (isPathExcluded(path, resolver.options)) {
38+
continue;
39+
}
40+
3641
const pathItemObj = resolver.openApiDoc.paths[path] as OpenAPIV3.PathItemObject;
3742
const pathItem = pick(pathItemObj, ALLOWED_METHODS);
3843
const pathParameters = getParameters(pathItemObj.parameters ?? []);
3944

4045
for (const method in pathItem) {
4146
const operation = pathItem[method as keyof typeof pathItem] as OperationObject | undefined;
42-
if (!operation || (operation.deprecated && !resolver.options.withDeprecatedEndpoints)) {
47+
if (!operation || isOperationExcluded(operation, resolver.options)) {
4348
continue;
4449
}
4550

src/generators/core/getMetadataFromOpenAPIDoc.test.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,15 +404,22 @@ describe("getMetadataFromOpenAPIDoc", async () => {
404404
];
405405

406406
test("getMetadataFromOpenAPIDoc", async () => {
407-
const metadata = await getMetadataFromOpenAPIDoc(openApiDoc, DEFAULT_GENERATE_OPTIONS);
407+
const metadata = await getMetadataFromOpenAPIDoc(openApiDoc, {
408+
...DEFAULT_GENERATE_OPTIONS,
409+
excludeRedundantZodSchemas: false,
410+
});
408411

409412
expect(metadata.models).toEqual(models());
410413
expect(metadata.queries).toEqual(queries);
411414
});
412415

413416
test("getMetadataFromOpenAPIDoc", async () => {
414417
const extractEnums = false;
415-
const metadata = await getMetadataFromOpenAPIDoc(openApiDoc, { ...DEFAULT_GENERATE_OPTIONS, extractEnums });
418+
const metadata = await getMetadataFromOpenAPIDoc(openApiDoc, {
419+
...DEFAULT_GENERATE_OPTIONS,
420+
excludeRedundantZodSchemas: false,
421+
extractEnums,
422+
});
416423

417424
expect(metadata.models).toEqual(models(extractEnums));
418425
expect(metadata.queries).toEqual(queries);

0 commit comments

Comments
 (0)