Skip to content

Commit 697f7fe

Browse files
authored
Keep casing for schema types exported to root (#2524)
* Keep DTO as suffix when creating pascalCase * Add flag to specify to keep casing of the root exports * Added changeset
1 parent e3c05a4 commit 697f7fe

File tree

8 files changed

+64
-3
lines changed

8 files changed

+64
-3
lines changed

.changeset/few-moments-laugh.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"openapi-typescript": minor
3+
---
4+
5+
Added flag to keep casing for exported root types

docs/cli.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ The following flags are supported in the CLI:
122122
| `--path-params-as-types` | | `false` | Allow dynamic string lookups on the `paths` object |
123123
| `--root-types` | | `false` | Exports types from `components` as root level type aliases |
124124
| `--root-types-no-schema-prefix` | | `false` | Do not add "Schema" prefix to types at the root level (should only be used with --root-types) |
125+
| `--root-types-keep-casing` | | `false` | Do not convert root type names to pascal case |
125126
| `--make-paths-enum` | | `false` | Generate ApiPaths enum for all paths |
126127
| `--generate-path-params` | | `false` | Generate path parameters for all paths where they are undefined by schema |
127128

packages/openapi-typescript/bin/cli.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Options
3434
--root-types (optional) Export schemas types at root level
3535
--root-types-no-schema-prefix (optional)
3636
Do not add "Schema" prefix to types at the root level (should only be used with --root-types)
37+
--root-types-keep-casing Keep casing of root types (should only be used with --root-types)
3738
--make-paths-enum Generate ApiPaths enum for all paths
3839
`;
3940

@@ -64,6 +65,10 @@ if (args.includes("--root-types-no-schema-prefix") && !args.includes("--root-typ
6465
// biome-ignore lint/suspicious/noConsole: this is a CLI
6566
console.warn("--root-types-no-schema-prefix has no effect without --root-types flag");
6667
}
68+
if (args.includes("--root-types-keep-casing") && !args.includes("--root-types")) {
69+
// biome-ignore lint/suspicious/noConsole: this is a CLI
70+
console.warn("--root-types-keep-casing has no effect without --root-types flag");
71+
}
6772

6873
const flags = parser(args, {
6974
boolean: [
@@ -86,6 +91,7 @@ const flags = parser(args, {
8691
"pathParamsAsTypes",
8792
"rootTypes",
8893
"rootTypesNoSchemaPrefix",
94+
"rootTypesKeepCasing",
8995
"makePathsEnum",
9096
"generatePathParams",
9197
],
@@ -150,6 +156,7 @@ async function generateSchema(schema, { redocly, silent = false }) {
150156
pathParamsAsTypes: flags.pathParamsAsTypes,
151157
rootTypes: flags.rootTypes,
152158
rootTypesNoSchemaPrefix: flags.rootTypesNoSchemaPrefix,
159+
rootTypesKeepCasing: flags.rootTypesKeepCasing,
153160
makePathsEnum: flags.makePathsEnum,
154161
generatePathParams: flags.generatePathParams,
155162
redocly,

packages/openapi-typescript/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export default async function openapiTS(
8282
immutable: options.immutable ?? false,
8383
rootTypes: options.rootTypes ?? false,
8484
rootTypesNoSchemaPrefix: options.rootTypesNoSchemaPrefix ?? false,
85+
rootTypesKeepCasing: options.rootTypesKeepCasing ?? false,
8586
injectFooter: [],
8687
pathParamsAsTypes: options.pathParamsAsTypes ?? false,
8788
postTransform: typeof options.postTransform === "function" ? options.postTransform : undefined,

packages/openapi-typescript/src/transform/components-object.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,15 @@ export default function transformComponentsObject(componentsObject: ComponentsOb
101101

102102
if (!shouldSkipEnumSchema) {
103103
const componentKey = changeCase.pascalCase(singularizeComponentKey(key));
104-
let aliasName = `${componentKey}${changeCase.pascalCase(name)}`;
104+
const componentName = ctx.rootTypesKeepCasing && key === "schemas" ? name : changeCase.pascalCase(name);
105+
let aliasName = `${componentKey}${componentName}`;
105106

106107
// Add counter suffix (e.g. "_2") if conflict in name
107108
let conflictCounter = 1;
108109

109110
while (rootTypeAliases[aliasName] !== undefined) {
110111
conflictCounter++;
111-
aliasName = `${componentKey}${changeCase.pascalCase(name)}_${conflictCounter}`;
112+
aliasName = `${componentKey}${componentName}_${conflictCounter}`;
112113
}
113114
const ref = ts.factory.createTypeReferenceNode(`components['${key}']['${name}']`);
114115
if (ctx.rootTypesNoSchemaPrefix && key === "schemas") {

packages/openapi-typescript/src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,8 @@ export interface OpenAPITSOptions {
670670
rootTypes?: boolean;
671671
/** (optional) Do not add Schema prefix to types at root level */
672672
rootTypesNoSchemaPrefix?: boolean;
673+
/** (optional) Keep casing of root types */
674+
rootTypesKeepCasing?: boolean;
673675
/**
674676
* Configure Redocly for validation, schema fetching, and bundling
675677
* @see https://redocly.com/docs/cli/configuration/
@@ -708,6 +710,7 @@ export interface GlobalContext {
708710
propertiesRequiredByDefault: boolean;
709711
rootTypes: boolean;
710712
rootTypesNoSchemaPrefix: boolean;
713+
rootTypesKeepCasing: boolean;
711714
redoc: RedoclyConfig;
712715
silent: boolean;
713716
transform: OpenAPITSOptions["transform"];

packages/openapi-typescript/test/test-helpers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export const DEFAULT_CTX: GlobalContext = {
2626
propertiesRequiredByDefault: false,
2727
rootTypes: false,
2828
rootTypesNoSchemaPrefix: false,
29+
rootTypesKeepCasing: false,
2930
redoc: await createConfig({}, { extends: ["minimal"] }),
3031
resolve($ref) {
3132
return resolveRef({}, $ref, { silent: false });

packages/openapi-typescript/test/transform/components-object.test.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,49 @@ export type Item = components['schemas']['Item'];
757757
export type Document = components['schemas']['Document'];
758758
export type Error = components['schemas']['Error'];
759759
`,
760-
options: { ...DEFAULT_OPTIONS, rootTypes: true, rootTypesNoSchemaPrefix: true },
760+
options: {
761+
...DEFAULT_OPTIONS,
762+
rootTypes: true,
763+
rootTypesNoSchemaPrefix: true,
764+
},
765+
},
766+
],
767+
[
768+
"options > rootTypes: true but keep casing",
769+
{
770+
given: {
771+
schemas: {
772+
ItemDTO: {
773+
type: "object",
774+
required: ["name", "url"],
775+
properties: {
776+
name: { type: "string" },
777+
url: { type: "string" },
778+
},
779+
},
780+
},
781+
},
782+
want: `{
783+
schemas: {
784+
ItemDTO: {
785+
name: string;
786+
url: string;
787+
};
788+
};
789+
responses: never;
790+
parameters: never;
791+
requestBodies: never;
792+
headers: never;
793+
pathItems: never;
794+
}
795+
export type ItemDTO = components['schemas']['ItemDTO'];
796+
`,
797+
options: {
798+
...DEFAULT_OPTIONS,
799+
rootTypes: true,
800+
rootTypesNoSchemaPrefix: true,
801+
rootTypesKeepCasing: true,
802+
},
761803
},
762804
],
763805
[

0 commit comments

Comments
 (0)