Skip to content

Commit

Permalink
feat(client): add useLegacyEnums option to generate typescript enums
Browse files Browse the repository at this point in the history
  • Loading branch information
jordanshatford committed Mar 26, 2024
1 parent 93f5481 commit 3cc372e
Show file tree
Hide file tree
Showing 238 changed files with 4,150 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/nice-pots-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hey-api/openapi-ts": minor
---

Add useLegacyEnums options to generate TypeScript enums
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ $ openapi-ts --help
--postfixModels Model name postfix
--request <value> Path to custom request file
--useDateType <value> Output Date instead of string for the format "date-time" in the models (default: false)
--useLegacyEnums Generate Typescript enum definitions (default: false)
-h, --help display help for command
```
Expand Down
1 change: 1 addition & 0 deletions bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const params = program
.option('--request <value>', 'Path to custom request file')
.option('--write', 'Write files to disk? (used for testing)')
.option('--no-write', 'Skip writing files to disk (used for testing)')
.option('--useLegacyEnums', 'Generate Typescript enum definitions')
.parse(process.argv)
.opts();

Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ const getConfig = async (userConfig: UserConfig, dependencies: Dependencies) =>
serviceResponse = 'body',
useDateType = false,
useOptions = true,
useLegacyEnums = false,
write = true,
} = userConfig;

Expand Down Expand Up @@ -158,6 +159,7 @@ const getConfig = async (userConfig: UserConfig, dependencies: Dependencies) =>
serviceResponse,
useDateType,
useOptions,
useLegacyEnums,
write,
};

Expand Down
1 change: 1 addition & 0 deletions src/openApi/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe('parse', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: true,
useLegacyEnums: false,
write: false,
};

Expand Down
1 change: 1 addition & 0 deletions src/openApi/v2/parser/__tests__/getServices.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('getServices', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: true,
useLegacyEnums: false,
write: false,
};
const services = getServices(
Expand Down
1 change: 1 addition & 0 deletions src/openApi/v3/parser/__tests__/getServices.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('getServices', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: true,
useLegacyEnums: false,
write: false,
};
const services = getServices(
Expand Down
17 changes: 17 additions & 0 deletions src/templates/partials/exportEnum.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,25 @@
{{/if}}
*/
{{/ifdef}}
{{#if @root.$config.useLegacyEnums}}
export enum {{{name}}} {
{{#each enum}}
{{#if x-enum-description}}
/**
* {{{escapeComment x-enum-description}}}
*/
{{else if description}}
/**
* {{{escapeComment description}}}
*/
{{/if}}
{{{enumKey value x-enum-varname}}} = {{{enumValue value}}},
{{/each}}
}
{{else}}
export type {{{name}}} = {{{enumUnionType enum}}};
{{/if}}
{{/if}}

{{#if @root.$config.enums}}
export const {{{enumName name }}} = {
Expand Down
5 changes: 5 additions & 0 deletions src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ export interface UserConfig {
* @default true
*/
useOptions?: boolean;
/**
* Generate Typescript enum definitions
* @default false
*/
useLegacyEnums?: boolean;
/**
* Write the files to disk (true or false)
* @default true
Expand Down
2 changes: 2 additions & 0 deletions src/utils/__tests__/handlebars.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('registerHandlebarHelpers', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: false,
useLegacyEnums: false,
write: true,
},
{
Expand Down Expand Up @@ -76,6 +77,7 @@ describe('registerHandlebarTemplates', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: false,
useLegacyEnums: false,
write: true,
},
{
Expand Down
1 change: 1 addition & 0 deletions src/utils/write/__tests__/class.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('writeClientClass', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: true,
useLegacyEnums: false,
write: true,
});

Expand Down
1 change: 1 addition & 0 deletions src/utils/write/__tests__/client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ describe('writeClient', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: false,
useLegacyEnums: false,
write: true,
});

Expand Down
3 changes: 3 additions & 0 deletions src/utils/write/__tests__/core.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ describe('writeClientCore', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: true,
useLegacyEnums: false,
write: true,
};

Expand Down Expand Up @@ -80,6 +81,7 @@ describe('writeClientCore', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: true,
useLegacyEnums: false,
write: true,
};

Expand Down Expand Up @@ -120,6 +122,7 @@ describe('writeClientCore', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: true,
useLegacyEnums: false,
write: true,
};

Expand Down
1 change: 1 addition & 0 deletions src/utils/write/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('writeClientIndex', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: true,
useLegacyEnums: false,
write: true,
});

Expand Down
1 change: 1 addition & 0 deletions src/utils/write/__tests__/models.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe('writeClientModels', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: true,
useLegacyEnums: false,
write: true,
});

Expand Down
1 change: 1 addition & 0 deletions src/utils/write/__tests__/schemas.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe('writeClientSchemas', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: true,
useLegacyEnums: false,
write: true,
});

Expand Down
1 change: 1 addition & 0 deletions src/utils/write/__tests__/services.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe('writeClientServices', () => {
serviceResponse: 'body',
useDateType: false,
useOptions: false,
useLegacyEnums: false,
write: true,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { ApiRequestOptions } from './ApiRequestOptions';
import type { ApiResult } from './ApiResult';

export class ApiError extends Error {
public readonly url: string;
public readonly status: number;
public readonly statusText: string;
public readonly body: unknown;
public readonly request: ApiRequestOptions;

constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
super(message);

this.name = 'ApiError';
this.url = response.url;
this.status = response.status;
this.statusText = response.statusText;
this.body = response.body;
this.request = request;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export type ApiRequestOptions = {
readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
readonly url: string;
readonly path?: Record<string, unknown>;
readonly cookies?: Record<string, unknown>;
readonly headers?: Record<string, unknown>;
readonly query?: Record<string, unknown>;
readonly formData?: Record<string, unknown>;
readonly body?: any;
readonly mediaType?: string;
readonly responseHeader?: string;
readonly errors?: Record<number, string>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type ApiResult<TData = any> = {
readonly body: TData;
readonly ok: boolean;
readonly status: number;
readonly statusText: string;
readonly url: string;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
export class CancelError extends Error {
constructor(message: string) {
super(message);
this.name = 'CancelError';
}

public get isCancelled(): boolean {
return true;
}
}

export interface OnCancel {
readonly isResolved: boolean;
readonly isRejected: boolean;
readonly isCancelled: boolean;

(cancelHandler: () => void): void;
}

export class CancelablePromise<T> implements Promise<T> {
#isResolved: boolean;
#isRejected: boolean;
#isCancelled: boolean;
readonly #cancelHandlers: (() => void)[];
readonly #promise: Promise<T>;
#resolve?: (value: T | PromiseLike<T>) => void;
#reject?: (reason?: unknown) => void;

constructor(
executor: (
resolve: (value: T | PromiseLike<T>) => void,
reject: (reason?: unknown) => void,
onCancel: OnCancel
) => void
) {
this.#isResolved = false;
this.#isRejected = false;
this.#isCancelled = false;
this.#cancelHandlers = [];
this.#promise = new Promise<T>((resolve, reject) => {
this.#resolve = resolve;
this.#reject = reject;

const onResolve = (value: T | PromiseLike<T>): void => {
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
return;
}
this.#isResolved = true;
if (this.#resolve) this.#resolve(value);
};

const onReject = (reason?: unknown): void => {
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
return;
}
this.#isRejected = true;
if (this.#reject) this.#reject(reason);
};

const onCancel = (cancelHandler: () => void): void => {
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
return;
}
this.#cancelHandlers.push(cancelHandler);
};

Object.defineProperty(onCancel, 'isResolved', {
get: (): boolean => this.#isResolved,
});

Object.defineProperty(onCancel, 'isRejected', {
get: (): boolean => this.#isRejected,
});

Object.defineProperty(onCancel, 'isCancelled', {
get: (): boolean => this.#isCancelled,
});

return executor(onResolve, onReject, onCancel as OnCancel);
});
}

get [Symbol.toStringTag]() {
return 'Cancellable Promise';
}

public then<TResult1 = T, TResult2 = never>(
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null
): Promise<TResult1 | TResult2> {
return this.#promise.then(onFulfilled, onRejected);
}

public catch<TResult = never>(
onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null
): Promise<T | TResult> {
return this.#promise.catch(onRejected);
}

public finally(onFinally?: (() => void) | null): Promise<T> {
return this.#promise.finally(onFinally);
}

public cancel(): void {
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
return;
}
this.#isCancelled = true;
if (this.#cancelHandlers.length) {
try {
for (const cancelHandler of this.#cancelHandlers) {
cancelHandler();
}
} catch (error) {
console.warn('Cancellation threw an error', error);
return;
}
}
this.#cancelHandlers.length = 0;
if (this.#reject) this.#reject(new CancelError('Request aborted'));
}

public get isCancelled(): boolean {
return this.#isCancelled;
}
}
Loading

0 comments on commit 3cc372e

Please sign in to comment.