-
Notifications
You must be signed in to change notification settings - Fork 31
/
index.ts
203 lines (192 loc) · 6.81 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/* eslint-disable @typescript-eslint/no-var-requires */
import * as path from 'path';
import * as fs from 'fs';
import * as request from 'request';
import chalk from 'chalk';
import { Json2Service, SwaggerParser, RemoteUrlReg, ProjectDir, SwaggerJson } from './consts';
import swagger2ts from './swagger2ts';
import serve from './yapi/serve';
import { pluginsPath, SmTmpDir, basePathToFileName, DefaultBasePath } from './init';
import { operationIdGuard, strictModeGuard } from './guard';
import { serveDiff } from './diff/serve';
import pathsFilter from './utils/pathsFilter';
const defaultParseConfig: Partial<SwaggerParser> = {
'-l': 'typescript-angularjs',
'-t': path.join(pluginsPath, 'typescript-tkit'),
'-o': path.join(process.cwd(), 'src', 'services')
};
/** CLI入口函数 */
export default async function gen(
config: Json2Service,
options: {
// 自动全量使用远程文档,不显示 diff & merge 页面
quiet?: boolean;
/** 是否清空上次生成目录 */
clear?: boolean;
/** 指定是否生成 models,false表示不生成,true表示一定生成,默认受 apis 的值影响 */
models?: boolean;
/** 指定是否生成 apis,false表示不生成,true表示一定生成,默认受 models 的值影响 */
apis?: boolean;
/** 生成typeScriptDataFile,可以指定文件名 */
typeScriptDataFile?: string | boolean;
}
): Promise<number> {
const {
url,
remoteUrl,
type = 'swagger',
swaggerParser,
requestConfig = {},
swaggerConfig = {}
} = config;
if (!url || url.match(RemoteUrlReg)) {
console.log(chalk.red(`[ERROR]: 自 @3.1.* url 必须是本地地址`));
throw 1;
}
/** 当前版本 */
const localSwaggerUrl = path.join(ProjectDir, url);
/** 远程或本地新版本 */
let remoteSwaggerUrl = (requestConfig.url = requestConfig.url || remoteUrl || '');
if (remoteSwaggerUrl) {
if (!remoteSwaggerUrl.match(RemoteUrlReg)) {
remoteSwaggerUrl = path.join(ProjectDir, remoteSwaggerUrl);
if (!fs.existsSync(remoteSwaggerUrl)) {
console.log(chalk.red(`[ERROR]: remoteUrl 指定的文件 ${remoteUrl} 不存在`));
throw 1;
}
}
}
// IMP: yapi => swagger
if (type === 'yapi') {
const yapiTMP = await serve(remoteSwaggerUrl, config.yapiConfig);
if ('result' in yapiTMP && yapiTMP.result && !yapiTMP.code) {
remoteSwaggerUrl = yapiTMP.result;
} else {
console.log(chalk.red(`[ERROR]: 基于 YAPI 生成失败: ${yapiTMP.message}`));
throw 1;
}
}
/** 写入本地版本 */
const updateLocalSwagger = (data: SwaggerJson) => {
fs.writeFileSync(localSwaggerUrl, data ? JSON.stringify(data, null, 2) : data, {
encoding: 'utf8'
});
};
const swagger2tsConfig = { ...defaultParseConfig, ...swaggerParser };
if (
swagger2tsConfig['-t'] === 'plugins/types-only' ||
swagger2tsConfig['-t'] === 'plugins/typescript-tkit-autos'
) {
swagger2tsConfig['-t'] = path.join(pluginsPath, '..', swagger2tsConfig['-t']);
}
const servicesPath = swagger2tsConfig['-o'] || '';
// IMP: 加载新版
const code: number = await new Promise(rs => {
const loader = (cb: (err: any, res: { body?: SwaggerJson }) => any) => {
remoteSwaggerUrl
? remoteSwaggerUrl.match(RemoteUrlReg)
? request.get(
{
...requestConfig,
url: remoteSwaggerUrl
},
(err, res) => cb(err, { body: JSON.parse(res.body) })
)
: cb(undefined, { body: require(remoteSwaggerUrl) as SwaggerJson })
: cb(undefined, {});
};
loader(async (err, { body: newSwagger }) => {
if (err) {
console.log(chalk.red(`[ERROR]: 下载 Swagger JSON 失败: ${err}`));
rs(1);
} else {
if (!fs.existsSync(servicesPath)) {
fs.mkdirSync(servicesPath);
}
if (newSwagger) {
// TODO: exclude 最终还是决定放到 diff 之后,生成之前
// newSwagger = pathsFilter(newSwagger, swaggerConfig);
const { modifier } = swaggerConfig;
if (modifier) {
newSwagger = modifier(newSwagger, config);
}
if (fs.existsSync(localSwaggerUrl) && !options.quiet) {
// diff and patch
const localSwagger: SwaggerJson = require(localSwaggerUrl);
const merged = await serveDiff<SwaggerJson>(localSwagger, newSwagger);
merged && updateLocalSwagger(merged);
} else {
updateLocalSwagger(newSwagger);
}
}
rs(0);
}
});
});
if (code) {
throw code;
}
// 删除缓存
delete require.cache[require.resolve(localSwaggerUrl)];
// eslint-disable-next-line @typescript-eslint/no-var-requires
const swaggerData: SwaggerJson = require(localSwaggerUrl);
const guardConfig = config.guardConfig || {};
// IMP: tags 校验
const warnings = Array<string>();
{
const { warnings: w, errors } = await strictModeGuard(swaggerData, guardConfig);
warnings.push(...w);
if (errors.length) {
throw errors.join('\n');
}
}
// IMP: 风险校验
const { errors, warnings: w, suggestions } = await operationIdGuard(swaggerData, guardConfig);
warnings.push(...w);
if (warnings.length) {
console.log(chalk.yellow(warnings.join('\n')));
}
// IMP: 校正后的文件写入临时文件
const swaggerFileName = basePathToFileName(`${swaggerData.basePath || DefaultBasePath}.json`);
const swaggerPath = path.join(SmTmpDir, swaggerFileName);
// IMP: exclude 在 diff 之后,生成之前
fs.writeFileSync(swaggerPath, JSON.stringify(pathsFilter(swaggerData, swaggerConfig)), {
encoding: 'utf8'
});
if (errors.length) {
if (suggestions.length) {
console.log(chalk.green(JSON.stringify(suggestions, undefined, 2)));
}
throw errors.join('\n');
} else {
if (suggestions.length) {
console.log(chalk.green(JSON.stringify(suggestions, undefined, 2)));
}
}
const envs: string[] = [];
const { apis, models, clear, typeScriptDataFile } = options;
if (typeScriptDataFile !== undefined && typeScriptDataFile !== false) {
envs.push(
typeScriptDataFile === true
? 'typeScriptDataFile'
: `typeScriptDataFile=${typeScriptDataFile}`,
'apis',
'models'
);
} else {
if (apis !== undefined) {
envs.push(apis === true ? 'apis' : `apis=${options.apis}`);
}
if (models !== undefined) {
envs.push(models === true ? 'models' : `models=${models}`);
}
}
const res = await swagger2ts({ ...swagger2tsConfig, '-i': swaggerPath }, clear, envs);
if (res.code) {
throw `[ERROR]: gen failed with: ${res.message}`;
} else {
console.log(chalk.green(`[INFO]: gen success with: ${localSwaggerUrl}`));
console.log(chalk.yellowBright(`[IMP] 请将文件 ${localSwaggerUrl} 添加到版本仓库`));
}
return 0;
}