Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eight-donuts-rush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openapi-ts-request': minor
---

feat: add supportParseEnumDescByReg func
2 changes: 2 additions & 0 deletions README-en_US.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ $ openapi --help
--isOnlyGenTypeScriptType <boolean> only generate typescript type (default: false)
--isCamelCase <boolean> camelCase naming of controller files and request client (default: true)
--isSupportParseEnumDesc <boolean> parse enum description to generate enum label (default: false)
--supportParseEnumDescByReg <string> custom regex for parsing enum description
-h, --help display help for command
```

Expand Down Expand Up @@ -251,6 +252,7 @@ openapi -i ./spec.json -o ./apis
| isOnlyGenTypeScriptType | no | boolean | false | only generate typescript type |
| isCamelCase | no | boolean | true | camelCase naming of controller files and request client |
| isSupportParseEnumDesc | no | boolean | false | parse enum description to generate enum label, format example: `UserRole:User(Normal User)=0,Agent(Agent)=1,Admin(Administrator)=2` |
| supportParseEnumDescByReg | no | string \| RegExp | - | custom regex for parsing enum descriptions. If set, replaces default parseDescriptionEnum method. Example: `/([^\s=<>/&;]+(?:\s+[^\s=<>/&;]+)*)\s*=\s*(\d+)/g` matches "Normal = 0" or "SampleMaker = 1" |
| binaryMediaTypes | no | string[] | - | custom binary media types list, default includes: ['application/octet-stream', 'application/pdf', 'image/*', 'video/*', 'audio/*'] |
| hook | no | [Custom Hook](#Custom-Hook) | - | custom hook |

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ $ openapi --help
--isOnlyGenTypeScriptType <boolean> only generate typescript type (default: false)
--isCamelCase <boolean> camelCase naming of controller files and request client (default: true)
--isSupportParseEnumDesc <boolean> parse enum description to generate enum label (default: false)
--supportParseEnumDescByReg <string> custom regex for parsing enum description
-h, --help display help for command
```

Expand Down Expand Up @@ -251,6 +252,7 @@ openapi --i ./spec.json --o ./apis
| isOnlyGenTypeScriptType | 否 | boolean | false | 仅生成 typescript 类型 |
| isCamelCase | 否 | boolean | true | 小驼峰命名文件和请求函数 |
| isSupportParseEnumDesc | 否 | boolean | false | 解析枚举描述生成枚举标签,格式参考:`系统用户角色:User(普通用户)=0,Agent(经纪人)=1,Admin(管理员)=2` |
| supportParseEnumDescByReg | 否 | string \| RegExp | - | 自定义正则表达式,用于解析 description 中的枚举定义。如果设置了此参数,将使用此正则表达式替代默认的 parseDescriptionEnum 方法。例如:`/([^\s=<>/&;]+(?:\s+[^\s=<>/&;]+)*)\s*=\s*(\d+)/g` 可以匹配 "普通 = 0" 或 "SampleMaker = 1" 这样的格式 |
| binaryMediaTypes | 否 | string[] | - | 自定义二进制媒体类型列表,默认包含:['application/octet-stream', 'application/pdf', 'image/*', 'video/*', 'audio/*'] |
| hook | 否 | [Custom Hook](#Custom-Hook) | - | 自定义 hook |

Expand Down
4 changes: 4 additions & 0 deletions agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ else if (isNodeProject) {
| `isOnlyGenTypeScriptType` | boolean | `false` | Only types, no request functions | Type-only needs |
| `isCamelCase` | boolean | `true` | Use camelCase naming | Consistent naming |
| `isSupportParseEnumDesc` | boolean | `false` | Parse enum descriptions | Labeled enums |
| `supportParseEnumDescByReg` | `string \| RegExp` | - | Custom regex for parsing enum descriptions. If set, replaces default parseDescriptionEnum method. Example: `/([^\s=<>/&;]+(?:\s+[^\s=<>/&;]+)*)\s*=\s*(\d+)/g` matches "普通 = 0" or "SampleMaker = 1" | Custom enum description formats |

#### Request Customization

Expand Down Expand Up @@ -118,6 +119,7 @@ else if (isNodeProject) {
| "支持文件上传" | Check OpenAPI for multipart | Auto-detects file uploads |
| "翻译中文标签" | `isTranslateToEnglishTag: true` | Chinese to English tags |
| "解析枚举描述" | `isSupportParseEnumDesc: true` | Enum with descriptions |
| "自定义枚举解析正则" | `supportParseEnumDescByReg: /pattern/g` | Custom enum description parsing |
| "生成JSON验证" | `isGenJsonSchemas: true` | Schema validation |
| "前缀API路径" | `apiPrefix: "'api'"` | Add prefix to paths |

Expand Down Expand Up @@ -205,6 +207,8 @@ else if (isNodeProject) {
isTranslateToEnglishTag: true,
isDisplayTypeLabel: true,
isSupportParseEnumDesc: true,
// Custom regex for parsing enum descriptions like "普通 = 0" or "SampleMaker = 1"
supportParseEnumDescByReg: /([^\s=<>/&;]+(?:\s+[^\s=<>/&;]+)*)\s*=\s*(\d+)/g,
isCamelCase: true,
filterCaseInsensitive: true,
}
Expand Down
7 changes: 7 additions & 0 deletions src/bin/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ const params = program
'parse enum description to generate enum label',
false
)
.option(
'--supportParseEnumDescByReg <string>',
'custom regex for parsing enum description'
)
.parse(process.argv)
.opts();

Expand Down Expand Up @@ -144,6 +148,9 @@ const baseGenerate = (_params_: OptionValues): GenerateServiceProps => {
isCamelCase: JSON.parse(_params_.isCamelCase as string) === true,
isSupportParseEnumDesc:
JSON.parse(_params_.isSupportParseEnumDesc as string) === true,
supportParseEnumDescByReg: _params_.supportParseEnumDescByReg as
| string
| RegExp,
};

return options;
Expand Down
14 changes: 12 additions & 2 deletions src/generator/serviceGenarator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ import {
isSchemaObject,
markAllowedSchema,
parseDescriptionEnum,
parseDescriptionEnumByReg,
replaceDot,
resolveRefs,
resolveTypeName,
Expand Down Expand Up @@ -1551,8 +1552,17 @@ export default class ServiceGenerator {
}).join(',')}}`;
} else {
if (numberEnum.includes(schemaObject.type) || isAllNumber(enumArray)) {
if (this.config.isSupportParseEnumDesc && schemaObject.description) {
const enumMap = parseDescriptionEnum(schemaObject.description);
if (
(this.config.isSupportParseEnumDesc ||
this.config.supportParseEnumDescByReg) &&
schemaObject.description
) {
const enumMap = this.config.isSupportParseEnumDesc
? parseDescriptionEnum(schemaObject.description)
: parseDescriptionEnumByReg(
schemaObject.description,
this.config.supportParseEnumDescByReg
);
enumLabelTypeStr = `{${map(enumArray, (value) => {
const enumLabel = enumMap.get(Number(value));
return `${Number(value)}:"${enumLabel}"`;
Expand Down
51 changes: 51 additions & 0 deletions src/generator/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,57 @@ export const parseDescriptionEnum = (
return enumMap;
};

/**
* 通过自定义正则表达式解析 description 中的枚举翻译
* @param description 描述文本
* @param regex 自定义正则表达式,用于匹配枚举项,例如:(\S+)\s*=\s*(\S+)
* @returns Map<number, string> 枚举值到标签的映射
*/
export const parseDescriptionEnumByReg = (
description: string,
regex: string | RegExp
): Map<number, string> => {
const enumMap = new Map<number, string>();
if (!description) return enumMap;

// 将字符串正则转换为 RegExp 对象,确保有全局标志
let regExp: RegExp;
if (typeof regex === 'string') {
const flagsMatch = regex.match(/\/([gimsuy]*)$/);
if (flagsMatch) {
const parts = regex.split('/');
const pattern = parts.slice(1, -1).join('/');
const flags = flagsMatch[1] || '';
regExp = new RegExp(pattern, flags.includes('g') ? flags : flags + 'g');
} else {
regExp = new RegExp(regex, 'g');
}
} else {
const flags = regex.flags;
regExp = flags.includes('g')
? regex
: new RegExp(regex.source, flags + 'g');
}

// 匹配所有结果,然后对每个匹配结果按 = 拆分
let match: RegExpExecArray | null;
while ((match = regExp.exec(description)) !== null) {
const fullMatch = match[0].trim();
// 按 = 拆分,左边是标签,右边是值
const parts = fullMatch.split(/\s*=\s*/);
if (parts.length >= 2) {
const label = parts[0].trim();
const valueStr = parts[1].trim();
const numValue = Number(valueStr);
if (label && valueStr && !isNaN(numValue)) {
enumMap.set(numValue, label);
}
}
}

return enumMap;
};

/**
* 获取默认的二进制媒体类型列表
*/
Expand Down
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ export type GenerateServiceProps = {
* 是否使用 description 中的枚举定义
*/
isSupportParseEnumDesc?: boolean;
/**
* 自定义正则表达式,用于解析 description 中的枚举定义
* 如果设置了此参数,将使用此正则表达式替代默认的 parseDescriptionEnum 方法
* 例如:/([^\s=<>/&;]+(?:\s+[^\s=<>/&;]+)*)\s*=\s*(\d+)/g 可以匹配 "普通 = 0" 或 "SampleMaker = 1" 这样的格式
*/
supportParseEnumDescByReg?: string | RegExp;
/**
* 命名空间名称,默认为API,不需要关注
*/
Expand Down