npm package: @lhy-meta-web/open-api
Description: > 根据open api规范文档生成typescript接口文件
Author: chen
Latest version: 0.1.10

# @lhy-meta-web/open-api

> 根据open api规范文档生成typescript接口文件

<br />

## 快速开始

安装

```typescript
  npm install -g @lhy-meta-web/open-api

  // 或者
  yarn install -g @lhy-meta-web/open-api

  // 或者
  pnpm install -g @lhy-meta-web/open-api
```

使用

```typescript
// typescript
import { GeneratorV2, GeneratorV3 } from '@lhy-meta-web/open-api'
// javascript
const { GeneratorV2, GeneratorV3 } = require('@lhy-meta-web/open-api')

const generator = new GeneratorV2({
  api: 'open api v2的访问地址，可在swagger文档的http请求中找到',
  template: {
    outputDir: './dist-api',
    clearOutputDir: true,
  },
})

generator.build()
```

<br />

## 配置

`GeneratorConfig`

| 属性                         | 类型                                                           | 描述                                                                                                         |
| ---------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `api`                        | `string \| OpenAPI.Document \| string[] \| OpenAPI.Document[]` | swagger.json的url地址或json                                                                                  |
| `template`                   | `TemplateConfig`                                               | 模板配置                                                                                                     |
| `commonUrl`                  | `string`                                                       | 公共的url前缀，生成器会自动计算一个最长公共子路径，commonUrl必须是最长公共子路径的子集。通常不需要使用该配置 |
| `filterResourceDependencies` | `boolean`                                                      | 是否对依赖做过滤处理，只会保留有存在依赖关系的模型和枚举，默认true                                           |
| `formatter`                  | `GeneratorFormatter`                                           | 格式化配置                                                                                                   |
| `package`                    | `GeneratorPackage`                                             | 包路径配置                                                                                                   |
| `logger`                     | `GeneratorLogger`                                              | 日志配置                                                                                                     |

<br />

`GeneratorV2Config extends GeneratorConfig`

| 属性               | 类型                            | 描述                                            |
| ------------------ | ------------------------------- | ----------------------------------------------- |
| `makeEnum`         | `(...) => ResourceEnum`         | 生成枚举，可用于自定义枚举解析，具体定义详见dts |
| `makeEnumProperty` | `(...) => ResourceEnumProperty` | 生成枚举属性，具体定义详见dts                   |
| `pathFilter`       | `(...) => boolean`              | 路径过滤，返回true才执行生成，具体定义详见dts   |


<br />

`GeneratorV3Config extends GeneratorConfig`

| 属性               | 类型                            | 描述                                            |
| ------------------ | ------------------------------- | ----------------------------------------------- |
| `makeEnum`         | `(...) => ResourceEnum`         | 生成枚举，可用于自定义枚举解析，具体定义详见dts |
| `makeEnumProperty` | `(...) => ResourceEnumProperty` | 生成枚举属性，具体定义详见dts                   |
| `pathFilter`       | `(...) => boolean`              | 路径过滤，返回true才执行生成，具体定义详见dts   |


<br />

`TemplateConfig` 模板配置

| 属性                         | 类型                                                                             | 描述                                                                      |
| ---------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `groupName`                  | `string `                                                                        | 模板组名称，默认 `default`                                                |
| `outputDir`                  | `string`                                                                         | 输出目录                                                                  |
| `outputFileName`             | `(filename: string) => string`                                                   | 输出文件名，默认：`${filename}.ts `                                       |
| `outputFile`                 | `(outputDir: string, packagePath: string, fileName: string) => string`           | 输出文件绝对路径，默认：`path.resolve(outputDir, packagePath, fileName) ` |
| `clearOutputDir`             | `boolean`                                                                        | 重新编译前清空输出目录                                                    |
| `buildModelContext`          | `(model: ResourceModel, resource: Resource) => Record<string, unknown>`          | 生成Model编译上下文，可在模板中使用                                       |
| `buildEnumContext`           | `(enumModel: ResourceEnum, resource: Resource) => Record<string, unknown>`       | 生成Enum编译上下文，可在模板中使用                                        |
| `buildParameterModelContext` | `(parameterModel: ResourceModel, resource: Resource) => Record<string, unknown>` | 生成ParameterModel编译上下文，可在模板中使用                              |
| `buildClassContext`          | `(clazz: ResourceClass, resource: Resource) => Record<string, unknown>`          | 生成Class编译上下文，可在模板中使用                                       |

<br />

`GeneratorFormatter` 包路径配置

| 属性                | 类型                         | 描述                  |
| ------------------- | ---------------------------- | --------------------- |
| `className`         | `ResourceCodeNameFormatter ` | 类名，默认 `pascal`   |
| `classPropertyName` | `ResourceCodeNameFormatter`  | 属性名，默认 `hump`   |
| `fileName`          | `ResourceFormatter`          | 文件名，默认 `hyphen` |

<br />

```typescript
export const ResourceFormatter = {
  // 小驼峰
  Hump: 'hump',
  // 大驼峰
  Pascal: 'pascal',
  // 中划线
  Hyphen: 'hyphen',
  // 下划线
  Snake: 'snake',
} as const
export type ResourceFormatter = ConstType<typeof ResourceFormatter>
export type ResourceCodeNameFormatter = Exclude<ResourceFormatter, typeof ResourceFormatter.Hyphen>
```

<br />

`GeneratorPackage` 包路径配置

| 属性             | 类型      | 描述                                                                     |
| ---------------- | --------- | ------------------------------------------------------------------------ |
| `model`          | `string ` | Model包目录，默认 `models`                                               |
| `enum`           | `string ` | Enum包目录，默认 `enums`                                                 |
| `parameterModel` | `string ` | ParameterModel包目录，默认 `parameter-models` (取决于formatter.fileName) |

<br />

`GeneratorLogger` 日志配置

| 属性    | 类型           | 描述                 |
| ------- | -------------- | -------------------- |
| `level` | `LoggerLevel ` | 日志级别，默认`warn` |

<br />

```typescript
export const LoggerLevel = {
  Info: 'info',
  Warn: 'warn',
  Error: 'error',
} as const
export type LoggerLevel = ConstType<typeof LoggerLevel>
```

<br />

## 执行流程

- 从options.api读取Document json
- 将Document json转换为Resource(v2、v3差异在此)
- 根据配置从Template中获取模板组文件(可自定义扩展)
- 根据模板组文件读取对应的模板编译器(可自定义扩展)
- 根据配置项生成模板编译Context上下文(可配置扩展)
- 执行编译器并将编译后的文本写入文件

<br />

## Class拆分

@lhy-meta-web/open-api会自动根据Document json中的所有url计算出最长公共子路径，并与`commonUrl`做对比得出最终公共前缀，然后移除掉该公共路径后的下一级路径作为Class分类。

例：
- v1/api/user/save
- v1/api/user/update
- v1/api/role/update
- v1/api/menu/page

=> 最长公共子路径: v1/api/，得到Class分类
- user
  - save
  - update
- role
  - update
- menu
  - page

<br />

当配置`commonUrl`时需要特别注意，`commonUrl`下一级的接口会全部合并成一个Class类，可能存在同名方法的情况。

=> `commonUrl = 'v1'`，得到的Class分类为
- api
  - save    => user.save
  - update  => user.update
  - update  => role.update，同名了
  - page    => menu.page

## 模板

@lhy-meta-web/open-api采用nunjucks作为模板编译引擎，并内置了一套默认模板组，包含

- class.njk
- enum.njk
- model.njk

### 模板文件类型

- TemplateFileType.Model (open api中定义好的模型)
- TemplateFileType.Enum (枚举，v2中的枚举是每个Model或ParameterModel的属性中[独立生成](https://swagger.org.cn/docs/specification/2-0/enums/)，v3中可使用[重用枚举](https://swagger.org.cn/docs/specification/data-models/enums/))
- TemplateFileType.ParameterModel (针对Path、Query、FormData而新生成的参数模型)
- TemplateFileType.Class (用于多个Api static挂载的Class)

### 自定义模板

```typescript
// 注册模板组
Template.registryGroup('自定义模板组名称', {
  files: [
    {
      type: TemplateFileType.Model,
      path: './default/model.njk',
    },
    {
      type: TemplateFileType.Enum,
      path: './default/enum.njk',
    },
    {
      type: TemplateFileType.ParameterModel,
      path: './default/model.njk',
    },
    {
      type: TemplateFileType.Class,
      path: './default/class.njk',
    },
  ],
})

// 然后在配置中指定使用自己的模板组
const generator = new GeneratorV2({
  api: 'open api v2的访问地址，可在swagger文档的http请求中找到',
  template: {
    groupName: '自定义模板组名称',
    outputDir: './dist-api',
    clearOutputDir: true,
  },
})

```

### 模板编译器

模板编译器是针对**模板文件类型**的编译器

@lhy-meta-web/open-api内置了默认的模板编译器，可自定义覆盖默认编译器

```typescript
import { Template } from '@lhy-meta-web/open-api'
import nunjucks from 'nunjucks'

Template.registryCompiler(
  TemplateFileType.Model,
  (templateContent: string, context: TemplateCompilerContext<typeof TemplateFileType.Model>, resource, config) => {
    return nunjucks.renderString(templateContent, context)
  }
)
```

<br />

## 自定义TS基础类型解析器

```typescript
import { TSTypeResolverV2 } from '@lhy-meta-web/open-api'

// 注意TSTypeResolverV2.mapping是只读属性
console.log(TSTypeResolverV2.mapping)

// 以下是默认值
//   {
//     object: 'Record<string, unknown>',
//     string: 'string',
//     integer: 'number',
//     int: 'number',
//     long: 'number',
//     double: 'number',
//     boolean: 'boolean',
//     Map: 'Record',
//     List: 'Array',
//     Void: 'void',
//     file: ' File',
//   }

Object.assign(TSTypeResolverV2.mapping, {
  // ...编写自己的类型映射
})
```

<br />

## 移除统一目录前缀

```typescript
const generator = new GeneratorV2({
  api: 'open api v2的访问地址，可在swagger文档的http请求中找到',
  template: {
    groupName: '自定义模板组名称',
    outputDir: './dist-api',
    outputFile(outputDir, packagePath, fileName) {
      // 注意移除url中的前缀时要同时移除尾巴的/，否则生成地址时会放置到根目录
      return path.resolve(__dirname, outputDir, packagePath.replace(/^api-prefix\/?/, ''), fileName)
    },
  },
})

```

<br />

## 注意事项

- 接口文档中的泛型对象不能使用多泛型，只能是单泛型结构。如: User\<Integer, String\>是不允许的，User\<Integer\>是允许的
- 所有的泛型必须明确的指定类型，否则会被解析成unknown
- 应避免使用Object、JSON、Map等数据类型，尽可能使用明确的类型，此类均被解析成Record<string, unknown>
- 文件上传接口指定@ApiImplicitParams({@ApiImplicitParam(name = "file", dataType = "MultipartFile")})，在swagger文档上能显示 consumes ["multipart/form-data"]即可
- 返回文件流接口指定produces = MediaType.APPLICATION_OCTET_STREAM_VALUE，在swagger文档上能显示 produces ["application/octet-stream"]即可
- 目前不支持`allof`、`oneof`、`anyof`，泛型的判断依据于名称中包含`/«.*»/`字符，主要适配于java的swagger生成

<br />

## 意见或建议

可发送邮件至邮箱`810422646@qq.com`，标题请明确包含`@lhy-meta-web/open-api`字样