Skip to content

Commit fdc545a

Browse files
committed
Initial commit
1 parent f3c384d commit fdc545a

22 files changed

+226
-31
lines changed

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1+
This is a fork of the original OpenAPI Typescript Codegen library. It is only meant as a temprorary npm package to use until these feature requests are merged into the original repo:
2+
- https://github.com/ferdikoomen/openapi-typescript-codegen/pull/1465
3+
- https://github.com/ferdikoomen/openapi-typescript-codegen/pull/1145
4+
5+
To install this fork: `npm install @oskarasplin/openapi-typescript-codegen`
6+
7+
At the time of writing the `feature/omit-read-only` branch is published as the latest npm version
8+
9+
Original ReadMe below
10+
111
# Important announcement
212

3-
> [!IMPORTANT]
13+
> [!IMPORTANT]
414
> Please migrate your projects to use [@hey-api/openapi-ts](https://github.com/hey-api/openapi-ts)
515
616
Due to time limitations on my end, this project has been unmaintained for a while now. The `@hey-api/openapi-ts`
@@ -65,6 +75,7 @@ $ openapi --help
6575
--postfixServices Service name postfix (default: "Service")
6676
--postfixModels Model name postfix
6777
--request <value> Path to custom request file
78+
--transformCase <value> Transforms field names to specified case [camel, snake] (default: none)
6879
-h, --help display help for command
6980
7081
Examples

bin/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const params = program
2323
.option('--indent <value>', 'Indentation options [4, 2, tabs]', '4')
2424
.option('--postfixServices <value>', 'Service name postfix', 'Service')
2525
.option('--postfixModels <value>', 'Model name postfix')
26+
.option('--transformCase <value>', 'Transforms field names to specified case [camel, snake]', 'none')
2627
.option('--request <value>', 'Path to custom request file')
2728
.parse(process.argv)
2829
.opts();
@@ -44,6 +45,7 @@ if (OpenAPI) {
4445
indent: params.indent,
4546
postfixServices: params.postfixServices,
4647
postfixModels: params.postfixModels,
48+
transformCase: params.transformCase,
4749
request: params.request,
4850
})
4951
.then(() => {

package-lock.json

Lines changed: 4 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"name": "openapi-typescript-codegen",
3-
"version": "0.29.0",
2+
"name": "@cloudfactory/openapi-typescript-codegen",
3+
"version": "1.0.0",
44
"description": "Library that generates Typescript clients based on the OpenAPI specification.",
55
"author": "Ferdi Koomen",
66
"homepage": "https://github.com/ferdikoomen/openapi-typescript-codegen",

src/Case.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Enum } from './client/interfaces/Enum';
2+
import { Model } from './client/interfaces/Model';
3+
import { OperationResponse } from './client/interfaces/OperationResponse';
4+
import { Service } from './client/interfaces/Service';
5+
6+
export enum Case {
7+
NONE = 'none',
8+
CAMEL = 'camel',
9+
SNAKE = 'snake',
10+
}
11+
// Convert a string from snake case to camel case.
12+
const toCamelCase = (str: string): string => {
13+
return str.replace(/_([a-z0-9])/g, match => match[1].toUpperCase());
14+
};
15+
16+
// Convert a string from camel case or pascal case to snake case.
17+
const toSnakeCase = (str: string): string => {
18+
return str.replace(/([A-Z])/g, match => `_${match.toLowerCase()}`);
19+
};
20+
21+
const transforms = {
22+
[Case.CAMEL]: toCamelCase,
23+
[Case.SNAKE]: toSnakeCase,
24+
};
25+
26+
// A recursive function that looks at the models and their properties and
27+
// converts each property name using the provided transform function.
28+
export const convertModelCase = <T extends Model | OperationResponse>(model: T, type: Exclude<Case, Case.NONE>): T => {
29+
return {
30+
...model,
31+
name: transforms[type](model.name),
32+
link: model.link ? convertModelCase(model.link, type) : null,
33+
enum: model.enum.map(modelEnum => convertEnumCase(modelEnum, type)),
34+
enums: model.enums.map(property => convertModelCase(property, type)),
35+
properties: model.properties.map(property => convertModelCase(property, type)),
36+
};
37+
};
38+
39+
const convertEnumCase = (modelEnum: Enum, type: Exclude<Case, Case.NONE>): Enum => {
40+
return {
41+
...modelEnum,
42+
name: transforms[type](modelEnum.name),
43+
};
44+
};
45+
46+
export const convertServiceCase = (service: Service, type: Exclude<Case, Case.NONE>): Service => {
47+
return {
48+
...service,
49+
operations: service.operations.map(op => ({
50+
...op,
51+
parameters: op.parameters.map(opParameter => convertModelCase(opParameter, type)),
52+
results: op.results.map(results => convertModelCase(results, type)),
53+
})),
54+
};
55+
};

src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Case } from './Case';
12
import { HttpClient } from './HttpClient';
23
import { Indent } from './Indent';
34
import { parse as parseV2 } from './openApi/v2';
@@ -26,6 +27,7 @@ export type Options = {
2627
indent?: Indent;
2728
postfixServices?: string;
2829
postfixModels?: string;
30+
transformCase?: Case;
2931
request?: string;
3032
write?: boolean;
3133
};
@@ -47,6 +49,7 @@ export type Options = {
4749
* @param indent Indentation options (4, 2 or tab)
4850
* @param postfixServices Service name postfix
4951
* @param postfixModels Model name postfix
52+
* @param transformCase Transform case (camel, snake)
5053
* @param request Path to custom request file
5154
* @param write Write the files to disk (true or false)
5255
*/
@@ -64,6 +67,7 @@ export const generate = async ({
6467
indent = Indent.SPACE_4,
6568
postfixServices = 'Service',
6669
postfixModels = '',
70+
transformCase = Case.NONE,
6771
request,
6872
write = true,
6973
}: Options): Promise<void> => {
@@ -94,6 +98,7 @@ export const generate = async ({
9498
indent,
9599
postfixServices,
96100
postfixModels,
101+
transformCase,
97102
clientName,
98103
request
99104
);
@@ -118,6 +123,7 @@ export const generate = async ({
118123
indent,
119124
postfixServices,
120125
postfixModels,
126+
transformCase,
121127
clientName,
122128
request
123129
);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* The contents of this file are inspired from https://stackoverflow.com/a/62362197/10326226
3+
* and https://stackoverflow.com/a/75587790/14166396
4+
* */
5+
6+
type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends <
7+
T,
8+
>() => T extends Y ? 1 : 2
9+
? A
10+
: B;
11+
12+
type WritableKeys<T> = {
13+
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>;
14+
}[keyof T];
15+
16+
type Primitive = undefined | null | boolean | string | number | Function;
17+
18+
type OmitReadonlyArray<T> = Array<OmitReadonly<T>>;
19+
type OmitReadonlyMap<K, V> = Map<K, OmitReadonly<V>>;
20+
type DeepWriableSet<T> = Set<OmitReadonly<T>>;
21+
22+
type OmitReadonlyObject<T> = MakeUndefinedFieldsOptional<{
23+
[K in WritableKeys<T>]: OmitReadonly<T[K]>;
24+
}>;
25+
26+
type UndefinedProps<T extends object> = {
27+
[K in keyof T as undefined extends T[K] ? K : never]?: T[K];
28+
};
29+
30+
type MakeUndefinedFieldsOptional<T extends object> = UndefinedProps<T> &
31+
Omit<T, keyof UndefinedProps<T>>;
32+
33+
export declare type OmitReadonly<T> = T extends Primitive
34+
? T
35+
: T extends Array<infer U>
36+
? OmitReadonlyArray<U>
37+
: T extends Map<infer K, infer V>
38+
? OmitReadonlyMap<K, V>
39+
: T extends Set<infer T>
40+
? DeepWriableSet<T>
41+
: OmitReadonlyObject<T>;

src/templates/exportService.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import type { BaseHttpRequest } from '../core/BaseHttpRequest';
3030
import { OpenAPI } from '../core/OpenAPI';
3131
import { request as __request } from '../core/request';
3232
{{/if}}
33+
import type { OmitReadonly } from '../core/utils/OmitReadonly';
3334

3435
{{#equals @root.httpClient 'angular'}}
3536
@Injectable({

src/templates/partials/parameters.hbs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
{{/if}}
1717
*/
1818
{{/ifdef}}
19-
{{{name}}}{{>isRequired}}: {{>type}},
19+
{{{name}}}{{>isRequired}}: {{>typeWithOmitReadOnly httpMethod=../method}},
2020
{{/each}}
2121
}
2222
{{~else}}
2323

2424
{{#each parameters}}
25-
{{{name}}}{{>isRequired}}: {{>type}}{{#if default}} = {{{default}}}{{/if}},
25+
{{{name}}}{{>isRequired}}: {{>typeWithOmitReadOnly httpMethod=../method}}{{#if default}} = {{{default}}}{{/if}},
2626
{{/each}}
2727
{{/if}}
2828
{{/if}}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{{#equals export "reference"}}
2+
{{#equals httpMethod "POST"~}}
3+
OmitReadonly<{{>type}}>
4+
{{~else equals httpMethod "PUT"~}}
5+
OmitReadonly<{{>type}}>
6+
{{~else equals httpMethod "PATCH"~}}
7+
OmitReadonly<{{>type}}>
8+
{{else}}
9+
{{~>type}}
10+
{{~/equals}}
11+
{{~else}}
12+
{{~>type}}
13+
{{~/equals}}

0 commit comments

Comments
 (0)