@jayfong/x-server 2.31.0 → 2.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -42,13 +42,14 @@ class ApiGenerator {
42
42
  const program = moProject.getProgram().compilerObject;
43
43
  const checker = program.getTypeChecker();
44
44
  this.checker = checker;
45
- const moduleSymbol = checker.getSymbolAtLocation(program.getSourceFile(entryFile));
45
+ const entrySourceFile = program.getSourceFile(entryFile);
46
+ const moduleSymbol = checker.getSymbolAtLocation(entrySourceFile);
46
47
  const categorySymbols = checker.getExportsOfModule(moduleSymbol);
47
48
  const apiData = [];
48
49
  for (const categorySymbol of categorySymbols) {
49
50
  var _ref, _categoryUrlMatch$;
50
51
  // 分类的类型
51
- const categoryType = checker.getTypeOfSymbolAtLocation(categorySymbol, categorySymbol.getDeclarations()[0]);
52
+ const categoryType = checker.getTypeOfSymbol(categorySymbol);
52
53
 
53
54
  // 处理器列表
54
55
  const handlerSymbols = categoryType.getProperties();
@@ -64,11 +65,39 @@ class ApiGenerator {
64
65
  let categoryUrl = (_ref = (_categoryUrlMatch$ = categoryUrlMatch[1]) != null ? _categoryUrlMatch$ : categoryUrlMatch[2]) != null ? _ref : '';
65
66
  categoryUrl = categoryUrl === '' ? '/' : `/${categoryUrl}/`;
66
67
  for (const handlerSymbol of handlerSymbols) {
67
- // 处理器名称
68
- const handlerName = handlerSymbol.getName();
68
+ // 处理器路径
69
+ let handlerPath = [handlerSymbol.getName()];
70
+ const handlerNode = handlerSymbol.getDeclarations()[0];
71
+
72
+ // 支持自定义 requestPath
73
+ if (
74
+ // const xx = defineHandler
75
+ _tsMorph.ts.isVariableDeclaration(handlerNode) && handlerNode.initializer &&
76
+ // defineHandler()
77
+ _tsMorph.ts.isCallExpression(handlerNode.initializer) &&
78
+ // defineHandler({})
79
+ _tsMorph.ts.isObjectLiteralExpression(handlerNode.initializer.arguments[0])) {
80
+ const props = handlerNode.initializer.arguments[0].properties;
81
+ const requestPathNode = props.find(item => {
82
+ var _item$name;
83
+ return ((_item$name = item.name) == null ? void 0 : _item$name.getText()) === 'requestPath';
84
+ });
85
+ if (requestPathNode &&
86
+ // requestPath: ''
87
+ _tsMorph.ts.isPropertyAssignment(requestPathNode)) {
88
+ const customHandlerPath =
89
+ // requestPath: 'xx'
90
+ _tsMorph.ts.isStringLiteral(requestPathNode.initializer) ? requestPathNode.initializer.text :
91
+ // requestPath: ['xx']
92
+ _tsMorph.ts.isArrayLiteralExpression(requestPathNode.initializer) ? requestPathNode.initializer.elements.filter(item => _tsMorph.ts.isStringLiteral(item)).map(item => item.text) : '';
93
+ if (customHandlerPath.length) {
94
+ handlerPath = (0, _vtils.castArray)(customHandlerPath);
95
+ }
96
+ }
97
+ }
69
98
 
70
99
  // 处理器 URL
71
- const handlerUrl = `${categoryUrl}${handlerName}`;
100
+ const handlerUrl = handlerPath.map(path => `${categoryUrl}${path}`);
72
101
 
73
102
  // 处理器注释
74
103
  const handlerComment = this.getComment(handlerSymbol);
@@ -77,11 +106,10 @@ class ApiGenerator {
77
106
  continue;
78
107
  }
79
108
  this.debug('生成接口: %s ...', handlerUrl);
80
- const handlerNode = handlerSymbol.getDeclarations()[0];
81
109
  const handlerType = checker.getTypeAtLocation(handlerNode);
82
110
  const [requestType, responseType, methodType] = checker.getTypeArguments(handlerType);
83
111
  const handlerCategory = (0, _vtils.pascalCase)(categoryUrl) || 'Index';
84
- const handlerDescription = handlerComment.description || handlerUrl;
112
+ const handlerDescription = handlerComment.description || handlerUrl.join(', ');
85
113
  const handlerMethod = methodType.value;
86
114
  const httpMethod = _http_method.HandlerMethodToHttpMethod[handlerMethod];
87
115
  const requestData = this.getApiDto({
@@ -256,57 +284,59 @@ class ApiGenerator {
256
284
  genYApiData(handles) {
257
285
  const data = {};
258
286
  for (const handle of handles) {
259
- data[handle.category] = data[handle.category] || [];
260
- data[handle.category].push(handle.handlerMethod === 'GET' ? {
261
- method: handle.method.toUpperCase(),
262
- title: handle.name,
263
- path: handle.path,
264
- res_body_type: 'json',
265
- req_body_is_json_schema: false,
266
- res_body_is_json_schema: true,
267
- req_params: [],
268
- req_query: handle.requestData.children.map(item => ({
269
- required: item.required ? 1 : 0,
270
- name: item.name,
271
- desc: item.desc,
272
- type: 'string'
273
- })),
274
- req_headers: [],
275
- req_body_form: [],
276
- res_body: JSON.stringify(handle.responseDataJsonSchema)
277
- } : handle.handlerMethod === 'FILE' ? {
278
- method: handle.method.toUpperCase(),
279
- title: handle.name,
280
- path: handle.path,
281
- req_body_type: 'form',
282
- res_body_type: 'json',
283
- req_body_is_json_schema: false,
284
- res_body_is_json_schema: true,
285
- req_params: [],
286
- req_query: [],
287
- req_headers: [],
288
- req_body_form: handle.requestData.children.map(item => ({
289
- required: item.required ? 1 : 0,
290
- name: item.name,
291
- desc: item.desc,
292
- type: item.type === 'file' ? 'file' : 'text'
293
- })),
294
- res_body: JSON.stringify(handle.responseDataJsonSchema)
295
- } : {
296
- method: handle.method.toUpperCase(),
297
- title: handle.name,
298
- path: handle.path,
299
- req_body_type: 'json',
300
- res_body_type: 'json',
301
- req_body_is_json_schema: true,
302
- res_body_is_json_schema: true,
303
- req_params: [],
304
- req_query: [],
305
- req_headers: [],
306
- req_body_form: [],
307
- req_body_other: JSON.stringify(handle.requestDataJsonSchema),
308
- res_body: JSON.stringify(handle.responseDataJsonSchema)
309
- });
287
+ for (const path of handle.path) {
288
+ data[handle.category] = data[handle.category] || [];
289
+ data[handle.category].push(handle.handlerMethod === 'GET' ? {
290
+ method: handle.method.toUpperCase(),
291
+ title: handle.name,
292
+ path: path,
293
+ res_body_type: 'json',
294
+ req_body_is_json_schema: false,
295
+ res_body_is_json_schema: true,
296
+ req_params: [],
297
+ req_query: handle.requestData.children.map(item => ({
298
+ required: item.required ? 1 : 0,
299
+ name: item.name,
300
+ desc: item.desc,
301
+ type: 'string'
302
+ })),
303
+ req_headers: [],
304
+ req_body_form: [],
305
+ res_body: JSON.stringify(handle.responseDataJsonSchema)
306
+ } : handle.handlerMethod === 'FILE' ? {
307
+ method: handle.method.toUpperCase(),
308
+ title: handle.name,
309
+ path: path,
310
+ req_body_type: 'form',
311
+ res_body_type: 'json',
312
+ req_body_is_json_schema: false,
313
+ res_body_is_json_schema: true,
314
+ req_params: [],
315
+ req_query: [],
316
+ req_headers: [],
317
+ req_body_form: handle.requestData.children.map(item => ({
318
+ required: item.required ? 1 : 0,
319
+ name: item.name,
320
+ desc: item.desc,
321
+ type: item.type === 'file' ? 'file' : 'text'
322
+ })),
323
+ res_body: JSON.stringify(handle.responseDataJsonSchema)
324
+ } : {
325
+ method: handle.method.toUpperCase(),
326
+ title: handle.name,
327
+ path: path,
328
+ req_body_type: 'json',
329
+ res_body_type: 'json',
330
+ req_body_is_json_schema: true,
331
+ res_body_is_json_schema: true,
332
+ req_params: [],
333
+ req_query: [],
334
+ req_headers: [],
335
+ req_body_form: [],
336
+ req_body_other: JSON.stringify(handle.requestDataJsonSchema),
337
+ res_body: JSON.stringify(handle.responseDataJsonSchema)
338
+ });
339
+ }
310
340
  }
311
341
  return Object.keys(data).map(cat => ({
312
342
  name: cat,
@@ -22,7 +22,7 @@ type ApiDto = {
22
22
  type ApiData = {
23
23
  category: string;
24
24
  name: string;
25
- path: string;
25
+ path: string[];
26
26
  handlerMethod: XHandler.Method;
27
27
  method: string;
28
28
  requestData: ApiDto;
@@ -2,7 +2,7 @@ import path from 'path';
2
2
  import createDebug from 'debug';
3
3
  import fs from 'fs-extra';
4
4
  import mo, { ts } from 'ts-morph';
5
- import { ii, pascalCase } from 'vtils';
5
+ import { castArray, ii, pascalCase } from 'vtils';
6
6
  import { HandlerMethodToHttpMethod } from "../core/http_method";
7
7
 
8
8
  /** 注释 */
@@ -37,13 +37,14 @@ export class ApiGenerator {
37
37
  const program = moProject.getProgram().compilerObject;
38
38
  const checker = program.getTypeChecker();
39
39
  this.checker = checker;
40
- const moduleSymbol = checker.getSymbolAtLocation(program.getSourceFile(entryFile));
40
+ const entrySourceFile = program.getSourceFile(entryFile);
41
+ const moduleSymbol = checker.getSymbolAtLocation(entrySourceFile);
41
42
  const categorySymbols = checker.getExportsOfModule(moduleSymbol);
42
43
  const apiData = [];
43
44
  for (const categorySymbol of categorySymbols) {
44
45
  var _ref, _categoryUrlMatch$;
45
46
  // 分类的类型
46
- const categoryType = checker.getTypeOfSymbolAtLocation(categorySymbol, categorySymbol.getDeclarations()[0]);
47
+ const categoryType = checker.getTypeOfSymbol(categorySymbol);
47
48
 
48
49
  // 处理器列表
49
50
  const handlerSymbols = categoryType.getProperties();
@@ -59,11 +60,39 @@ export class ApiGenerator {
59
60
  let categoryUrl = (_ref = (_categoryUrlMatch$ = categoryUrlMatch[1]) != null ? _categoryUrlMatch$ : categoryUrlMatch[2]) != null ? _ref : '';
60
61
  categoryUrl = categoryUrl === '' ? '/' : `/${categoryUrl}/`;
61
62
  for (const handlerSymbol of handlerSymbols) {
62
- // 处理器名称
63
- const handlerName = handlerSymbol.getName();
63
+ // 处理器路径
64
+ let handlerPath = [handlerSymbol.getName()];
65
+ const handlerNode = handlerSymbol.getDeclarations()[0];
66
+
67
+ // 支持自定义 requestPath
68
+ if (
69
+ // const xx = defineHandler
70
+ ts.isVariableDeclaration(handlerNode) && handlerNode.initializer &&
71
+ // defineHandler()
72
+ ts.isCallExpression(handlerNode.initializer) &&
73
+ // defineHandler({})
74
+ ts.isObjectLiteralExpression(handlerNode.initializer.arguments[0])) {
75
+ const props = handlerNode.initializer.arguments[0].properties;
76
+ const requestPathNode = props.find(item => {
77
+ var _item$name;
78
+ return ((_item$name = item.name) == null ? void 0 : _item$name.getText()) === 'requestPath';
79
+ });
80
+ if (requestPathNode &&
81
+ // requestPath: ''
82
+ ts.isPropertyAssignment(requestPathNode)) {
83
+ const customHandlerPath =
84
+ // requestPath: 'xx'
85
+ ts.isStringLiteral(requestPathNode.initializer) ? requestPathNode.initializer.text :
86
+ // requestPath: ['xx']
87
+ ts.isArrayLiteralExpression(requestPathNode.initializer) ? requestPathNode.initializer.elements.filter(item => ts.isStringLiteral(item)).map(item => item.text) : '';
88
+ if (customHandlerPath.length) {
89
+ handlerPath = castArray(customHandlerPath);
90
+ }
91
+ }
92
+ }
64
93
 
65
94
  // 处理器 URL
66
- const handlerUrl = `${categoryUrl}${handlerName}`;
95
+ const handlerUrl = handlerPath.map(path => `${categoryUrl}${path}`);
67
96
 
68
97
  // 处理器注释
69
98
  const handlerComment = this.getComment(handlerSymbol);
@@ -72,11 +101,10 @@ export class ApiGenerator {
72
101
  continue;
73
102
  }
74
103
  this.debug('生成接口: %s ...', handlerUrl);
75
- const handlerNode = handlerSymbol.getDeclarations()[0];
76
104
  const handlerType = checker.getTypeAtLocation(handlerNode);
77
105
  const [requestType, responseType, methodType] = checker.getTypeArguments(handlerType);
78
106
  const handlerCategory = pascalCase(categoryUrl) || 'Index';
79
- const handlerDescription = handlerComment.description || handlerUrl;
107
+ const handlerDescription = handlerComment.description || handlerUrl.join(', ');
80
108
  const handlerMethod = methodType.value;
81
109
  const httpMethod = HandlerMethodToHttpMethod[handlerMethod];
82
110
  const requestData = this.getApiDto({
@@ -251,57 +279,59 @@ export class ApiGenerator {
251
279
  genYApiData(handles) {
252
280
  const data = {};
253
281
  for (const handle of handles) {
254
- data[handle.category] = data[handle.category] || [];
255
- data[handle.category].push(handle.handlerMethod === 'GET' ? {
256
- method: handle.method.toUpperCase(),
257
- title: handle.name,
258
- path: handle.path,
259
- res_body_type: 'json',
260
- req_body_is_json_schema: false,
261
- res_body_is_json_schema: true,
262
- req_params: [],
263
- req_query: handle.requestData.children.map(item => ({
264
- required: item.required ? 1 : 0,
265
- name: item.name,
266
- desc: item.desc,
267
- type: 'string'
268
- })),
269
- req_headers: [],
270
- req_body_form: [],
271
- res_body: JSON.stringify(handle.responseDataJsonSchema)
272
- } : handle.handlerMethod === 'FILE' ? {
273
- method: handle.method.toUpperCase(),
274
- title: handle.name,
275
- path: handle.path,
276
- req_body_type: 'form',
277
- res_body_type: 'json',
278
- req_body_is_json_schema: false,
279
- res_body_is_json_schema: true,
280
- req_params: [],
281
- req_query: [],
282
- req_headers: [],
283
- req_body_form: handle.requestData.children.map(item => ({
284
- required: item.required ? 1 : 0,
285
- name: item.name,
286
- desc: item.desc,
287
- type: item.type === 'file' ? 'file' : 'text'
288
- })),
289
- res_body: JSON.stringify(handle.responseDataJsonSchema)
290
- } : {
291
- method: handle.method.toUpperCase(),
292
- title: handle.name,
293
- path: handle.path,
294
- req_body_type: 'json',
295
- res_body_type: 'json',
296
- req_body_is_json_schema: true,
297
- res_body_is_json_schema: true,
298
- req_params: [],
299
- req_query: [],
300
- req_headers: [],
301
- req_body_form: [],
302
- req_body_other: JSON.stringify(handle.requestDataJsonSchema),
303
- res_body: JSON.stringify(handle.responseDataJsonSchema)
304
- });
282
+ for (const path of handle.path) {
283
+ data[handle.category] = data[handle.category] || [];
284
+ data[handle.category].push(handle.handlerMethod === 'GET' ? {
285
+ method: handle.method.toUpperCase(),
286
+ title: handle.name,
287
+ path: path,
288
+ res_body_type: 'json',
289
+ req_body_is_json_schema: false,
290
+ res_body_is_json_schema: true,
291
+ req_params: [],
292
+ req_query: handle.requestData.children.map(item => ({
293
+ required: item.required ? 1 : 0,
294
+ name: item.name,
295
+ desc: item.desc,
296
+ type: 'string'
297
+ })),
298
+ req_headers: [],
299
+ req_body_form: [],
300
+ res_body: JSON.stringify(handle.responseDataJsonSchema)
301
+ } : handle.handlerMethod === 'FILE' ? {
302
+ method: handle.method.toUpperCase(),
303
+ title: handle.name,
304
+ path: path,
305
+ req_body_type: 'form',
306
+ res_body_type: 'json',
307
+ req_body_is_json_schema: false,
308
+ res_body_is_json_schema: true,
309
+ req_params: [],
310
+ req_query: [],
311
+ req_headers: [],
312
+ req_body_form: handle.requestData.children.map(item => ({
313
+ required: item.required ? 1 : 0,
314
+ name: item.name,
315
+ desc: item.desc,
316
+ type: item.type === 'file' ? 'file' : 'text'
317
+ })),
318
+ res_body: JSON.stringify(handle.responseDataJsonSchema)
319
+ } : {
320
+ method: handle.method.toUpperCase(),
321
+ title: handle.name,
322
+ path: path,
323
+ req_body_type: 'json',
324
+ res_body_type: 'json',
325
+ req_body_is_json_schema: true,
326
+ res_body_is_json_schema: true,
327
+ req_params: [],
328
+ req_query: [],
329
+ req_headers: [],
330
+ req_body_form: [],
331
+ req_body_other: JSON.stringify(handle.requestDataJsonSchema),
332
+ res_body: JSON.stringify(handle.responseDataJsonSchema)
333
+ });
334
+ }
305
335
  }
306
336
  return Object.keys(data).map(cat => ({
307
337
  name: cat,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jayfong/x-server",
3
- "version": "2.31.0",
3
+ "version": "2.32.0",
4
4
  "license": "ISC",
5
5
  "sideEffects": false,
6
6
  "main": "lib/_cjs/index.js",