Skip to content

Commit

Permalink
feat: add optional configuration parameter to generate native Typescr…
Browse files Browse the repository at this point in the history
…ipt enums (#1058)

* feat: add optional configuration parameter to generate native Typescript enums

* fix: move enum type decision to getEnum function
  • Loading branch information
dsthode authored Nov 20, 2023
1 parent d26c2a8 commit 76efc85
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 2 deletions.
22 changes: 22 additions & 0 deletions docs/src/pages/reference/configuration/output.md
Original file line number Diff line number Diff line change
Expand Up @@ -1303,3 +1303,25 @@ module.exports = {
},
};
```

#### useNativeEnums

Type: `Boolean`

Valid values: true or false. Defaults to false.

Use this property to generate native Typescript `enum` instead of `type` and `const` combo.

Example:

```js
module.exports = {
petstore: {
output: {
override: {
useNativeEnums: true,
},
},
},
};
```
1 change: 1 addition & 0 deletions packages/core/src/generators/schema-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const generateSchemasDefinition = (
resolvedValue.value,
schemaName,
resolvedValue.originalSchema?.['x-enumNames'],
context.override.useNativeEnums,
);
} else if (schemaName === resolvedValue.value && resolvedValue.isRef) {
// Don't add type if schema has same name and the referred schema will be an interface
Expand Down
61 changes: 60 additions & 1 deletion packages/core/src/getters/enum.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import { keyword } from 'esutils';
import { isNumeric, sanitize } from '../utils';

export const getEnum = (value: string, enumName: string, names?: string[]) => {
export const getEnum = (
value: string,
enumName: string,
names?: string[],
useNativeEnums?: boolean,
) => {
const enumValue = useNativeEnums
? getNativeEnum(value, enumName, names)
: getTypeConstEnum(value, enumName, names);
return enumValue;
};

const getTypeConstEnum = (
value: string,
enumName: string,
names?: string[],
) => {
let enumValue = `export type ${enumName} = typeof ${enumName}[keyof typeof ${enumName}]`;

if (value.endsWith(' | null')) {
Expand Down Expand Up @@ -59,6 +75,49 @@ export const getEnumImplementation = (value: string, names?: string[]) => {
}, '');
};

const getNativeEnum = (value: string, enumName: string, names?: string[]) => {
const enumItems = getNativeEnumItems(value, names);
const enumValue = `export enum ${enumName} {\n${enumItems}\n}`;

return enumValue;
};

const getNativeEnumItems = (value: string, names?: string[]) => {
if (value === '') return '';

return [...new Set(value.split(' | '))].reduce((acc, val, index) => {
const name = names?.[index];
if (name) {
return (
acc +
` ${keyword.isIdentifierNameES5(name) ? name : `'${name}'`}: ${val},\n`
);
}

let key = val.startsWith("'") ? val.slice(1, -1) : val;

const isNumber = isNumeric(key);

if (isNumber) {
key = toNumberKey(key);
}

if (key.length > 1) {
key = sanitize(key, {
whitespace: '_',
underscore: true,
dash: true,
special: true,
});
}

return (
acc +
` ${keyword.isIdentifierNameES5(key) ? key : `'${key}'`}= ${val},\n`
);
}, '');
};

const toNumberKey = (value: string) => {
if (value[0] === '-') {
return `NUMBER_MINUS_${value.slice(1)}`;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/getters/query-params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ const getQueryParamsTypes = (

if (resolvedValue.isEnum && !resolvedValue.isRef) {
const enumName = queryName;

const enumValue = getEnum(
resolvedValue.value,
enumName,
resolvedValue.originalSchema?.['x-enumNames'],
context.override.useNativeEnums,
);

return {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/resolvers/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const resolveObject = ({
resolvedValue.value,
propName,
resolvedValue.originalSchema?.['x-enumNames'],
context.override.useNativeEnums,
);

return {
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export type NormalizedOverrideOutput = {
useDeprecatedOperations?: boolean;
useBigInt?: boolean;
useNamedParameters?: boolean;
useNativeEnums?: boolean;
};

export type NormalizedMutator = {
Expand Down Expand Up @@ -303,6 +304,7 @@ export type OverrideOutput = {
useDeprecatedOperations?: boolean;
useBigInt?: boolean;
useNamedParameters?: boolean;
useNativeEnums?: boolean;
};

export type OverrideOutputContentType = {
Expand Down
1 change: 1 addition & 0 deletions packages/orval/src/utils/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ export const normalizeOptions = async (
useDates: outputOptions.override?.useDates || false,
useDeprecatedOperations:
outputOptions.override?.useDeprecatedOperations ?? true,
useNativeEnums: outputOptions.override?.useNativeEnums ?? false,
},
},
hooks: options.hooks ? normalizeHooks(options.hooks) : {},
Expand Down

0 comments on commit 76efc85

Please sign in to comment.