@wx-sab/renkei 1.3.3 → 1.3.4
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.
- package/lib/cli.js +2 -2
- package/lib/commands/api.d.ts +9 -0
- package/lib/commands/api.js +18 -9
- package/lib/commands/api.test.d.ts +1 -0
- package/lib/commands/api.test.js +132 -0
- package/lib/core/constants.test.d.ts +1 -0
- package/lib/core/constants.test.js +31 -0
- package/lib/core/folder-selector.test.d.ts +1 -0
- package/lib/core/folder-selector.test.js +246 -0
- package/lib/core/generate.test.d.ts +1 -0
- package/lib/core/generate.test.js +341 -0
- package/lib/core/type-filter.d.ts +19 -0
- package/lib/core/type-filter.js +26 -10
- package/lib/core/type-filter.test.d.ts +1 -0
- package/lib/core/type-filter.test.js +564 -0
- package/package.json +1 -1
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const vitest_1 = require("vitest");
|
|
13
|
+
const swagger_typescript_api_1 = require("swagger-typescript-api");
|
|
14
|
+
const generate_1 = require("./generate");
|
|
15
|
+
const type_filter_1 = require("./type-filter");
|
|
16
|
+
const config_utils_1 = require("./config-utils");
|
|
17
|
+
vitest_1.vi.mock('swagger-typescript-api', () => ({
|
|
18
|
+
generateApi: vitest_1.vi.fn()
|
|
19
|
+
}));
|
|
20
|
+
vitest_1.vi.mock('cli-progress', () => ({
|
|
21
|
+
default: {
|
|
22
|
+
SingleBar: class {
|
|
23
|
+
constructor() {
|
|
24
|
+
this.start = vitest_1.vi.fn();
|
|
25
|
+
this.increment = vitest_1.vi.fn();
|
|
26
|
+
this.stop = vitest_1.vi.fn();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}));
|
|
31
|
+
vitest_1.vi.mock('./type-filter', () => ({
|
|
32
|
+
filterUnusedTypes: vitest_1.vi.fn()
|
|
33
|
+
}));
|
|
34
|
+
const mockedGenerateApi = vitest_1.vi.mocked(swagger_typescript_api_1.generateApi);
|
|
35
|
+
const mockedFilterUnusedTypes = vitest_1.vi.mocked(type_filter_1.filterUnusedTypes);
|
|
36
|
+
(0, vitest_1.describe)('generate', () => {
|
|
37
|
+
(0, vitest_1.beforeEach)(() => {
|
|
38
|
+
vitest_1.vi.clearAllMocks();
|
|
39
|
+
vitest_1.vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
40
|
+
vitest_1.vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
41
|
+
vitest_1.vi.spyOn(console, 'info').mockImplementation(() => { });
|
|
42
|
+
});
|
|
43
|
+
(0, vitest_1.afterEach)(() => {
|
|
44
|
+
vitest_1.vi.restoreAllMocks();
|
|
45
|
+
});
|
|
46
|
+
(0, vitest_1.describe)('base generate()', () => {
|
|
47
|
+
(0, vitest_1.it)('calls generateApi with correct base config', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
48
|
+
mockedGenerateApi.mockResolvedValueOnce({});
|
|
49
|
+
yield (0, generate_1.generate)({ serviceName: 'test', source: 'http://test.com' });
|
|
50
|
+
(0, vitest_1.expect)(mockedGenerateApi).toHaveBeenCalledTimes(1);
|
|
51
|
+
const callArgs = mockedGenerateApi.mock.calls[0][0];
|
|
52
|
+
(0, vitest_1.expect)(callArgs.silent).toBe(true);
|
|
53
|
+
(0, vitest_1.expect)(callArgs.modular).toBe(false);
|
|
54
|
+
(0, vitest_1.expect)(callArgs.generateClient).toBe(false);
|
|
55
|
+
(0, vitest_1.expect)(callArgs.extractRequestParams).toBe(true);
|
|
56
|
+
(0, vitest_1.expect)(callArgs.cleanOutput).toBe(false);
|
|
57
|
+
}));
|
|
58
|
+
(0, vitest_1.it)('triggers filterUnusedTypes when folders configured and output set', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
59
|
+
mockedGenerateApi.mockResolvedValueOnce({});
|
|
60
|
+
yield (0, generate_1.generate)({
|
|
61
|
+
serviceName: 'myService',
|
|
62
|
+
source: 'http://test.com',
|
|
63
|
+
folders: ['users'],
|
|
64
|
+
output: '/tmp/output'
|
|
65
|
+
});
|
|
66
|
+
(0, vitest_1.expect)(mockedFilterUnusedTypes).toHaveBeenCalledTimes(1);
|
|
67
|
+
(0, vitest_1.expect)(mockedFilterUnusedTypes).toHaveBeenCalledWith('/tmp/output', 'myService', 'MyService', 'Model');
|
|
68
|
+
}));
|
|
69
|
+
(0, vitest_1.it)('does NOT trigger filterUnusedTypes when no folders configured', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
70
|
+
mockedGenerateApi.mockResolvedValueOnce({});
|
|
71
|
+
yield (0, generate_1.generate)({
|
|
72
|
+
serviceName: 'myService',
|
|
73
|
+
source: 'http://test.com',
|
|
74
|
+
output: '/tmp/output'
|
|
75
|
+
});
|
|
76
|
+
(0, vitest_1.expect)(mockedFilterUnusedTypes).not.toHaveBeenCalled();
|
|
77
|
+
}));
|
|
78
|
+
});
|
|
79
|
+
(0, vitest_1.describe)('onCreateRouteName hook', () => {
|
|
80
|
+
(0, vitest_1.it)('skips routes outside configured folders', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
81
|
+
let capturedHooks;
|
|
82
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
83
|
+
capturedHooks = params.hooks;
|
|
84
|
+
return {};
|
|
85
|
+
}));
|
|
86
|
+
yield (0, generate_1.generate)({
|
|
87
|
+
serviceName: 'test',
|
|
88
|
+
source: 'http://test.com',
|
|
89
|
+
folders: ['users']
|
|
90
|
+
});
|
|
91
|
+
const result = capturedHooks.onCreateRouteName({ original: 'getUsers', usage: 'getUsers' }, { route: '/orders', method: 'get', 'x-apifox-folder': 'orders/list' });
|
|
92
|
+
(0, vitest_1.expect)(result).toEqual({
|
|
93
|
+
duplicate: false,
|
|
94
|
+
original: config_utils_1.SKIP_ROUTE_MARKER,
|
|
95
|
+
usage: config_utils_1.SKIP_ROUTE_MARKER
|
|
96
|
+
});
|
|
97
|
+
}));
|
|
98
|
+
(0, vitest_1.it)('allows routes in configured folders', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
99
|
+
let capturedHooks;
|
|
100
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
101
|
+
capturedHooks = params.hooks;
|
|
102
|
+
return {};
|
|
103
|
+
}));
|
|
104
|
+
yield (0, generate_1.generate)({
|
|
105
|
+
serviceName: 'test',
|
|
106
|
+
source: 'http://test.com',
|
|
107
|
+
folders: ['users']
|
|
108
|
+
});
|
|
109
|
+
const result = capturedHooks.onCreateRouteName({ original: 'getUsers', usage: 'getUsers' }, { route: '/api/users', method: 'get', 'x-apifox-folder': 'users/list' });
|
|
110
|
+
(0, vitest_1.expect)(result.original).not.toBe(config_utils_1.SKIP_ROUTE_MARKER);
|
|
111
|
+
(0, vitest_1.expect)(result.usage).not.toBe(config_utils_1.SKIP_ROUTE_MARKER);
|
|
112
|
+
}));
|
|
113
|
+
(0, vitest_1.it)('throws on route with spaces', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
114
|
+
let capturedHooks;
|
|
115
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
116
|
+
capturedHooks = params.hooks;
|
|
117
|
+
return {};
|
|
118
|
+
}));
|
|
119
|
+
yield (0, generate_1.generate)({ serviceName: 'test', source: 'http://test.com' });
|
|
120
|
+
(0, vitest_1.expect)(() => capturedHooks.onCreateRouteName({ original: 'badRoute', usage: 'badRoute' }, { route: '/path with space', method: 'get' })).toThrow(/路径中存在空格/);
|
|
121
|
+
}));
|
|
122
|
+
(0, vitest_1.it)('warns on Chinese characters in route', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
123
|
+
let capturedHooks;
|
|
124
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
125
|
+
capturedHooks = params.hooks;
|
|
126
|
+
return {};
|
|
127
|
+
}));
|
|
128
|
+
yield (0, generate_1.generate)({ serviceName: 'test', source: 'http://test.com' });
|
|
129
|
+
const rawRouteInfo = { route: '/api/中文路径', method: 'get' };
|
|
130
|
+
capturedHooks.onCreateRouteName({ original: 'test', usage: 'test' }, rawRouteInfo);
|
|
131
|
+
(0, vitest_1.expect)(console.warn).toHaveBeenCalledWith(vitest_1.expect.stringContaining('检测到请求路径中存在中文'));
|
|
132
|
+
}));
|
|
133
|
+
(0, vitest_1.it)('applies customRequestUrl when it returns a string', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
134
|
+
let capturedHooks;
|
|
135
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
136
|
+
capturedHooks = params.hooks;
|
|
137
|
+
return {};
|
|
138
|
+
}));
|
|
139
|
+
yield (0, generate_1.generate)({
|
|
140
|
+
serviceName: 'test',
|
|
141
|
+
source: 'http://test.com',
|
|
142
|
+
customRequestUrl: () => '/new-url'
|
|
143
|
+
});
|
|
144
|
+
const rawRouteInfo = { route: '/old-url', method: 'get' };
|
|
145
|
+
capturedHooks.onCreateRouteName({ original: 'test', usage: 'test' }, rawRouteInfo);
|
|
146
|
+
(0, vitest_1.expect)(rawRouteInfo.route).toBe('/new-url');
|
|
147
|
+
}));
|
|
148
|
+
(0, vitest_1.it)('applies customRequestUrl when it returns an object with path and mockUrl', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
149
|
+
let capturedHooks;
|
|
150
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
151
|
+
capturedHooks = params.hooks;
|
|
152
|
+
return {};
|
|
153
|
+
}));
|
|
154
|
+
yield (0, generate_1.generate)({
|
|
155
|
+
serviceName: 'test',
|
|
156
|
+
source: 'http://test.com',
|
|
157
|
+
customRequestUrl: () => ({
|
|
158
|
+
path: '/new-path',
|
|
159
|
+
mockUrl: 'https://mock.example.com/api'
|
|
160
|
+
})
|
|
161
|
+
});
|
|
162
|
+
const rawRouteInfo = { route: '/old-path', method: 'get', mockUrl: 'https://mock.example.com/api' };
|
|
163
|
+
capturedHooks.onCreateRouteName({ original: 'test', usage: 'test' }, rawRouteInfo);
|
|
164
|
+
(0, vitest_1.expect)(rawRouteInfo.route).toBe('/new-path');
|
|
165
|
+
(0, vitest_1.expect)(rawRouteInfo.mockUrl).toBe('https://mock.example.com/api');
|
|
166
|
+
}));
|
|
167
|
+
(0, vitest_1.it)('detects duplicate method names', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
168
|
+
let capturedHooks;
|
|
169
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
170
|
+
capturedHooks = params.hooks;
|
|
171
|
+
return {};
|
|
172
|
+
}));
|
|
173
|
+
yield (0, generate_1.generate)({ serviceName: 'test', source: 'http://test.com' });
|
|
174
|
+
const rawRouteInfo = { route: '/users', method: 'get' };
|
|
175
|
+
const first = capturedHooks.onCreateRouteName({ original: 'test', usage: 'test' }, rawRouteInfo);
|
|
176
|
+
(0, vitest_1.expect)(first.duplicate).toBe(false);
|
|
177
|
+
const second = capturedHooks.onCreateRouteName({ original: 'test', usage: 'test' }, rawRouteInfo);
|
|
178
|
+
(0, vitest_1.expect)(second.duplicate).toBe(true);
|
|
179
|
+
}));
|
|
180
|
+
(0, vitest_1.it)('uses customRouteName when provided', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
181
|
+
let capturedHooks;
|
|
182
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
183
|
+
capturedHooks = params.hooks;
|
|
184
|
+
return {};
|
|
185
|
+
}));
|
|
186
|
+
yield (0, generate_1.generate)({
|
|
187
|
+
serviceName: 'test',
|
|
188
|
+
source: 'http://test.com',
|
|
189
|
+
customRouteName: () => 'customMethodName'
|
|
190
|
+
});
|
|
191
|
+
const result = capturedHooks.onCreateRouteName({ original: 'original', usage: 'original' }, { route: '/users', method: 'get' });
|
|
192
|
+
(0, vitest_1.expect)(result.original).toBe('customMethodName');
|
|
193
|
+
(0, vitest_1.expect)(result.usage).toBe('customMethodName');
|
|
194
|
+
}));
|
|
195
|
+
});
|
|
196
|
+
(0, vitest_1.describe)('onParseSchema hook', () => {
|
|
197
|
+
(0, vitest_1.it)('handles int64 format', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
198
|
+
let capturedHooks;
|
|
199
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
200
|
+
capturedHooks = params.hooks;
|
|
201
|
+
return {};
|
|
202
|
+
}));
|
|
203
|
+
yield (0, generate_1.generate)({ serviceName: 'test', source: 'http://test.com' });
|
|
204
|
+
const result = capturedHooks.onParseSchema({ format: 'int64' }, { content: 'number' });
|
|
205
|
+
(0, vitest_1.expect)(result.content).toBe('number | string');
|
|
206
|
+
}));
|
|
207
|
+
(0, vitest_1.it)('handles long format', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
208
|
+
let capturedHooks;
|
|
209
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
210
|
+
capturedHooks = params.hooks;
|
|
211
|
+
return {};
|
|
212
|
+
}));
|
|
213
|
+
yield (0, generate_1.generate)({ serviceName: 'test', source: 'http://test.com' });
|
|
214
|
+
const result = capturedHooks.onParseSchema({ format: 'long' }, { content: 'number' });
|
|
215
|
+
(0, vitest_1.expect)(result.content).toBe('number | string');
|
|
216
|
+
}));
|
|
217
|
+
(0, vitest_1.it)('handles any type with $ref by resolving to namespaced name', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
218
|
+
let capturedHooks;
|
|
219
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
220
|
+
capturedHooks = params.hooks;
|
|
221
|
+
return {};
|
|
222
|
+
}));
|
|
223
|
+
yield (0, generate_1.generate)({ serviceName: 'test', source: 'http://test.com' });
|
|
224
|
+
const result = capturedHooks.onParseSchema({}, {
|
|
225
|
+
name: 'any',
|
|
226
|
+
content: 'any',
|
|
227
|
+
$ref: '#/definitions/UserVO'
|
|
228
|
+
});
|
|
229
|
+
// getValidName('UserVO') returns 'UserVO', modelNamespace is 'Model', serviceName is 'Test'
|
|
230
|
+
(0, vitest_1.expect)(result.content).toBe('Model.Test.UserVO');
|
|
231
|
+
}));
|
|
232
|
+
(0, vitest_1.it)('passes through non-matching schema unchanged', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
233
|
+
let capturedHooks;
|
|
234
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
235
|
+
capturedHooks = params.hooks;
|
|
236
|
+
return {};
|
|
237
|
+
}));
|
|
238
|
+
yield (0, generate_1.generate)({ serviceName: 'test', source: 'http://test.com' });
|
|
239
|
+
const parsed = { name: 'string', content: 'string' };
|
|
240
|
+
const result = capturedHooks.onParseSchema({ format: 'email' }, parsed);
|
|
241
|
+
(0, vitest_1.expect)(result).toBe(parsed);
|
|
242
|
+
}));
|
|
243
|
+
});
|
|
244
|
+
(0, vitest_1.describe)('onCreateRoute hook', () => {
|
|
245
|
+
(0, vitest_1.it)('replaces path params from {param} to ${param}', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
246
|
+
let capturedHooks;
|
|
247
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
248
|
+
capturedHooks = params.hooks;
|
|
249
|
+
return {};
|
|
250
|
+
}));
|
|
251
|
+
yield (0, generate_1.generate)({ serviceName: 'test', source: 'http://test.com' });
|
|
252
|
+
const routeData = {
|
|
253
|
+
raw: { route: '/users/{userId}/posts/{postId}' },
|
|
254
|
+
request: { path: '/users/{userId}/posts/{postId}' }
|
|
255
|
+
};
|
|
256
|
+
const result = capturedHooks.onCreateRoute(routeData);
|
|
257
|
+
(0, vitest_1.expect)(result.request.path).toBe('/users/${userId}/posts/${postId}');
|
|
258
|
+
}));
|
|
259
|
+
});
|
|
260
|
+
(0, vitest_1.describe)('onPrepareConfig hook', () => {
|
|
261
|
+
(0, vitest_1.it)('injects modelNamespace, serviceName, SKIP_ROUTE_MARKER, and skippedRoutes set', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
262
|
+
let capturedHooks;
|
|
263
|
+
mockedGenerateApi.mockImplementationOnce((params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
264
|
+
capturedHooks = params.hooks;
|
|
265
|
+
return {};
|
|
266
|
+
}));
|
|
267
|
+
yield (0, generate_1.generate)({ serviceName: 'test', source: 'http://test.com' });
|
|
268
|
+
const result = capturedHooks.onPrepareConfig({ someExisting: 'value' });
|
|
269
|
+
(0, vitest_1.expect)(result.modelNamespace).toBe('Model');
|
|
270
|
+
(0, vitest_1.expect)(result.serviceName).toBe('Test');
|
|
271
|
+
(0, vitest_1.expect)(result.SKIP_ROUTE_MARKER).toBe(config_utils_1.SKIP_ROUTE_MARKER);
|
|
272
|
+
(0, vitest_1.expect)(result.skippedRoutes).toBeInstanceOf(Set);
|
|
273
|
+
(0, vitest_1.expect)(result.someExisting).toBe('value');
|
|
274
|
+
}));
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
(0, vitest_1.describe)('generateApis', () => {
|
|
278
|
+
(0, vitest_1.beforeEach)(() => {
|
|
279
|
+
vitest_1.vi.clearAllMocks();
|
|
280
|
+
vitest_1.vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
281
|
+
vitest_1.vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
282
|
+
vitest_1.vi.spyOn(console, 'info').mockImplementation(() => { });
|
|
283
|
+
});
|
|
284
|
+
(0, vitest_1.afterEach)(() => {
|
|
285
|
+
vitest_1.vi.restoreAllMocks();
|
|
286
|
+
});
|
|
287
|
+
(0, vitest_1.it)('generates all configs when services is empty', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
288
|
+
mockedGenerateApi.mockResolvedValue({});
|
|
289
|
+
const configs = [
|
|
290
|
+
{ serviceName: 'service1', source: 'http://s1.com' },
|
|
291
|
+
{ serviceName: 'service2', source: 'http://s2.com' },
|
|
292
|
+
{ serviceName: 'service3', source: 'http://s3.com' }
|
|
293
|
+
];
|
|
294
|
+
yield (0, generate_1.generateApis)(configs, []);
|
|
295
|
+
(0, vitest_1.expect)(mockedGenerateApi).toHaveBeenCalledTimes(3);
|
|
296
|
+
}));
|
|
297
|
+
(0, vitest_1.it)('filters by services list', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
298
|
+
mockedGenerateApi.mockResolvedValue({});
|
|
299
|
+
const configs = [
|
|
300
|
+
{ serviceName: 'service1', source: 'http://s1.com' },
|
|
301
|
+
{ serviceName: 'service2', source: 'http://s2.com' },
|
|
302
|
+
{ serviceName: 'service3', source: 'http://s3.com' }
|
|
303
|
+
];
|
|
304
|
+
yield (0, generate_1.generateApis)(configs, ['service1', 'service3']);
|
|
305
|
+
(0, vitest_1.expect)(mockedGenerateApi).toHaveBeenCalledTimes(2);
|
|
306
|
+
}));
|
|
307
|
+
(0, vitest_1.it)('collects errors from failed services and logs failure info', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
308
|
+
mockedGenerateApi
|
|
309
|
+
.mockResolvedValueOnce({})
|
|
310
|
+
.mockRejectedValueOnce(new Error('generation failed'))
|
|
311
|
+
.mockResolvedValueOnce({});
|
|
312
|
+
const configs = [
|
|
313
|
+
{ serviceName: 'service1', source: 'http://s1.com' },
|
|
314
|
+
{ serviceName: 'service2', source: 'http://s2.com' },
|
|
315
|
+
{ serviceName: 'service3', source: 'http://s3.com' }
|
|
316
|
+
];
|
|
317
|
+
yield (0, generate_1.generateApis)(configs, []);
|
|
318
|
+
(0, vitest_1.expect)(console.error).toHaveBeenCalledWith('generation failed');
|
|
319
|
+
(0, vitest_1.expect)(console.info).toHaveBeenCalledWith(vitest_1.expect.stringContaining('生成失败'));
|
|
320
|
+
(0, vitest_1.expect)(console.info).toHaveBeenCalledWith(vitest_1.expect.stringContaining('service2'));
|
|
321
|
+
}));
|
|
322
|
+
(0, vitest_1.it)('prints success message when all services pass', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
323
|
+
mockedGenerateApi.mockResolvedValue({});
|
|
324
|
+
const configs = [
|
|
325
|
+
{ serviceName: 'service1', source: 'http://s1.com' },
|
|
326
|
+
{ serviceName: 'service2', source: 'http://s2.com' }
|
|
327
|
+
];
|
|
328
|
+
yield (0, generate_1.generateApis)(configs, []);
|
|
329
|
+
(0, vitest_1.expect)(console.info).toHaveBeenCalledWith('✅ 执行完毕');
|
|
330
|
+
}));
|
|
331
|
+
(0, vitest_1.it)('handles non-Error thrown values (string thrown)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
332
|
+
mockedGenerateApi.mockRejectedValueOnce('string error message');
|
|
333
|
+
const configs = [
|
|
334
|
+
{ serviceName: 'service1', source: 'http://s1.com' }
|
|
335
|
+
];
|
|
336
|
+
yield (0, generate_1.generateApis)(configs, []);
|
|
337
|
+
(0, vitest_1.expect)(console.error).toHaveBeenCalledWith('string error message');
|
|
338
|
+
(0, vitest_1.expect)(console.info).toHaveBeenCalledWith(vitest_1.expect.stringContaining('service1'));
|
|
339
|
+
(0, vitest_1.expect)(console.info).toHaveBeenCalledWith(vitest_1.expect.stringContaining('string error message'));
|
|
340
|
+
}));
|
|
341
|
+
});
|
|
@@ -1 +1,20 @@
|
|
|
1
|
+
/** @internal */
|
|
2
|
+
export declare const extractTypeReferences: (content: string, modelNamespace: string) => Map<string, Set<string>>;
|
|
3
|
+
/** @internal */
|
|
4
|
+
export declare const stripComments: (content: string) => string;
|
|
5
|
+
/** @internal */
|
|
6
|
+
export declare const collectTypeDefBody: (lines: string[], startIndex: number, initialLines: string[]) => {
|
|
7
|
+
typeDefLines: string[];
|
|
8
|
+
nextIndex: number;
|
|
9
|
+
};
|
|
10
|
+
/** @internal */
|
|
11
|
+
export declare const extractAllTypeDefinitions: (content: string) => Map<string, string>;
|
|
12
|
+
/** @internal */
|
|
13
|
+
export declare const findShortNameRefs: (codeOnly: string, typeNameSet: Set<string>) => Set<string>;
|
|
14
|
+
/** @internal */
|
|
15
|
+
export declare const findTypeDependencies: (rootTypes: Set<string>, typeDefs: Map<string, string>, modelNamespace: string, serviceName: string) => Set<string>;
|
|
16
|
+
/** @internal */
|
|
17
|
+
export declare const detectNamespaceIndent: (lines: string[], serviceName: string) => string;
|
|
18
|
+
/** @internal */
|
|
19
|
+
export declare const filterModelTypes: (modelContent: string, usedTypes: Set<string>, modelNamespace: string, serviceName: string) => string;
|
|
1
20
|
export declare const filterUnusedTypes: (outputDir: string, serviceDirName: string, serviceTypeName: string, modelNamespace: string) => Promise<void>;
|
package/lib/core/type-filter.js
CHANGED
|
@@ -12,9 +12,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.filterUnusedTypes = void 0;
|
|
15
|
+
exports.filterUnusedTypes = exports.filterModelTypes = exports.detectNamespaceIndent = exports.findTypeDependencies = exports.findShortNameRefs = exports.extractAllTypeDefinitions = exports.collectTypeDefBody = exports.stripComments = exports.extractTypeReferences = void 0;
|
|
16
16
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
|
+
/** @internal */
|
|
18
19
|
const extractTypeReferences = (content, modelNamespace) => {
|
|
19
20
|
const typeUsageMap = new Map();
|
|
20
21
|
const regex = new RegExp(`${modelNamespace}\\.(\\w+)\\.(\\w+)`, 'g');
|
|
@@ -29,6 +30,8 @@ const extractTypeReferences = (content, modelNamespace) => {
|
|
|
29
30
|
}
|
|
30
31
|
return typeUsageMap;
|
|
31
32
|
};
|
|
33
|
+
exports.extractTypeReferences = extractTypeReferences;
|
|
34
|
+
/** @internal */
|
|
32
35
|
const stripComments = (content) => {
|
|
33
36
|
const result = [];
|
|
34
37
|
let i = 0;
|
|
@@ -74,6 +77,8 @@ const stripComments = (content) => {
|
|
|
74
77
|
}
|
|
75
78
|
return result.join('');
|
|
76
79
|
};
|
|
80
|
+
exports.stripComments = stripComments;
|
|
81
|
+
/** @internal */
|
|
77
82
|
const collectTypeDefBody = (lines, startIndex, initialLines) => {
|
|
78
83
|
const firstLine = lines[startIndex].trim();
|
|
79
84
|
const typeDefLines = [...initialLines];
|
|
@@ -92,6 +97,8 @@ const collectTypeDefBody = (lines, startIndex, initialLines) => {
|
|
|
92
97
|
}
|
|
93
98
|
return { typeDefLines, nextIndex: i };
|
|
94
99
|
};
|
|
100
|
+
exports.collectTypeDefBody = collectTypeDefBody;
|
|
101
|
+
/** @internal */
|
|
95
102
|
const extractAllTypeDefinitions = (content) => {
|
|
96
103
|
const typeDefs = new Map();
|
|
97
104
|
const lines = content.split('\n');
|
|
@@ -116,7 +123,7 @@ const extractAllTypeDefinitions = (content) => {
|
|
|
116
123
|
const typeMatch = nextLine.match(/(?:export\s+)?(?:interface|type|enum)\s+(\w+)/);
|
|
117
124
|
if (typeMatch) {
|
|
118
125
|
const typeName = typeMatch[1];
|
|
119
|
-
const { typeDefLines, nextIndex } = collectTypeDefBody(lines, i, commentLines);
|
|
126
|
+
const { typeDefLines, nextIndex } = (0, exports.collectTypeDefBody)(lines, i, commentLines);
|
|
120
127
|
typeDefs.set(typeName, typeDefLines.join('\n'));
|
|
121
128
|
i = nextIndex;
|
|
122
129
|
continue;
|
|
@@ -127,7 +134,7 @@ const extractAllTypeDefinitions = (content) => {
|
|
|
127
134
|
const typeMatch = trimmedLine.match(/(?:export\s+)?(?:interface|type|enum)\s+(\w+)/);
|
|
128
135
|
if (typeMatch) {
|
|
129
136
|
const typeName = typeMatch[1];
|
|
130
|
-
const { typeDefLines, nextIndex } = collectTypeDefBody(lines, i, []);
|
|
137
|
+
const { typeDefLines, nextIndex } = (0, exports.collectTypeDefBody)(lines, i, []);
|
|
131
138
|
typeDefs.set(typeName, typeDefLines.join('\n'));
|
|
132
139
|
i = nextIndex;
|
|
133
140
|
continue;
|
|
@@ -136,7 +143,9 @@ const extractAllTypeDefinitions = (content) => {
|
|
|
136
143
|
}
|
|
137
144
|
return typeDefs;
|
|
138
145
|
};
|
|
146
|
+
exports.extractAllTypeDefinitions = extractAllTypeDefinitions;
|
|
139
147
|
const CHUNK_SIZE = 200;
|
|
148
|
+
/** @internal */
|
|
140
149
|
const findShortNameRefs = (codeOnly, typeNameSet) => {
|
|
141
150
|
const found = new Set();
|
|
142
151
|
const typeNames = Array.from(typeNameSet);
|
|
@@ -151,6 +160,8 @@ const findShortNameRefs = (codeOnly, typeNameSet) => {
|
|
|
151
160
|
}
|
|
152
161
|
return found;
|
|
153
162
|
};
|
|
163
|
+
exports.findShortNameRefs = findShortNameRefs;
|
|
164
|
+
/** @internal */
|
|
154
165
|
const findTypeDependencies = (rootTypes, typeDefs, modelNamespace, serviceName) => {
|
|
155
166
|
const visited = new Set();
|
|
156
167
|
const typeNameSet = new Set(typeDefs.keys());
|
|
@@ -169,8 +180,8 @@ const findTypeDependencies = (rootTypes, typeDefs, modelNamespace, serviceName)
|
|
|
169
180
|
traverse(refTypeName);
|
|
170
181
|
}
|
|
171
182
|
}
|
|
172
|
-
const codeOnly = stripComments(typeDef);
|
|
173
|
-
const shortRefs = findShortNameRefs(codeOnly, typeNameSet);
|
|
183
|
+
const codeOnly = (0, exports.stripComments)(typeDef);
|
|
184
|
+
const shortRefs = (0, exports.findShortNameRefs)(codeOnly, typeNameSet);
|
|
174
185
|
for (const refTypeName of shortRefs) {
|
|
175
186
|
if (refTypeName !== typeName && !visited.has(refTypeName) && typeDefs.has(refTypeName)) {
|
|
176
187
|
traverse(refTypeName);
|
|
@@ -182,6 +193,8 @@ const findTypeDependencies = (rootTypes, typeDefs, modelNamespace, serviceName)
|
|
|
182
193
|
}
|
|
183
194
|
return visited;
|
|
184
195
|
};
|
|
196
|
+
exports.findTypeDependencies = findTypeDependencies;
|
|
197
|
+
/** @internal */
|
|
185
198
|
const detectNamespaceIndent = (lines, serviceName) => {
|
|
186
199
|
var _a;
|
|
187
200
|
for (const line of lines) {
|
|
@@ -193,10 +206,12 @@ const detectNamespaceIndent = (lines, serviceName) => {
|
|
|
193
206
|
}
|
|
194
207
|
return ' ';
|
|
195
208
|
};
|
|
209
|
+
exports.detectNamespaceIndent = detectNamespaceIndent;
|
|
210
|
+
/** @internal */
|
|
196
211
|
const filterModelTypes = (modelContent, usedTypes, modelNamespace, serviceName) => {
|
|
197
212
|
const lines = modelContent.split('\n');
|
|
198
|
-
const allTypeDefs = extractAllTypeDefinitions(modelContent);
|
|
199
|
-
const allUsedTypes = findTypeDependencies(usedTypes, allTypeDefs, modelNamespace, serviceName);
|
|
213
|
+
const allTypeDefs = (0, exports.extractAllTypeDefinitions)(modelContent);
|
|
214
|
+
const allUsedTypes = (0, exports.findTypeDependencies)(usedTypes, allTypeDefs, modelNamespace, serviceName);
|
|
200
215
|
const filteredTypeDefs = [];
|
|
201
216
|
allTypeDefs.forEach((typeDef, name) => {
|
|
202
217
|
if (allUsedTypes.has(name)) {
|
|
@@ -229,7 +244,7 @@ const filterModelTypes = (modelContent, usedTypes, modelNamespace, serviceName)
|
|
|
229
244
|
}
|
|
230
245
|
header.push(line);
|
|
231
246
|
}
|
|
232
|
-
const closingIndent = detectNamespaceIndent(lines, serviceName);
|
|
247
|
+
const closingIndent = (0, exports.detectNamespaceIndent)(lines, serviceName);
|
|
233
248
|
const result = [
|
|
234
249
|
...header,
|
|
235
250
|
...filteredTypeDefs,
|
|
@@ -238,6 +253,7 @@ const filterModelTypes = (modelContent, usedTypes, modelNamespace, serviceName)
|
|
|
238
253
|
];
|
|
239
254
|
return result.join('\n');
|
|
240
255
|
};
|
|
256
|
+
exports.filterModelTypes = filterModelTypes;
|
|
241
257
|
const filterUnusedTypes = (outputDir, serviceDirName, serviceTypeName, modelNamespace) => __awaiter(void 0, void 0, void 0, function* () {
|
|
242
258
|
try {
|
|
243
259
|
const serviceDir = path_1.default.join(outputDir, serviceDirName);
|
|
@@ -245,7 +261,7 @@ const filterUnusedTypes = (outputDir, serviceDirName, serviceTypeName, modelName
|
|
|
245
261
|
const modelFile = path_1.default.join(serviceDir, 'model.d.ts');
|
|
246
262
|
const indexContent = yield promises_1.default.readFile(indexFile, 'utf-8');
|
|
247
263
|
const modelContent = yield promises_1.default.readFile(modelFile, 'utf-8');
|
|
248
|
-
const typeUsageMap = extractTypeReferences(indexContent, modelNamespace);
|
|
264
|
+
const typeUsageMap = (0, exports.extractTypeReferences)(indexContent, modelNamespace);
|
|
249
265
|
if (!typeUsageMap.has(serviceTypeName)) {
|
|
250
266
|
console.log(`没有找到服务 ${serviceTypeName} 的类型引用,跳过过滤`);
|
|
251
267
|
return;
|
|
@@ -255,7 +271,7 @@ const filterUnusedTypes = (outputDir, serviceDirName, serviceTypeName, modelName
|
|
|
255
271
|
console.log(`服务 ${serviceTypeName} 没有使用任何类型,跳过过滤`);
|
|
256
272
|
return;
|
|
257
273
|
}
|
|
258
|
-
const filteredModelContent = filterModelTypes(modelContent, usedTypes, modelNamespace, serviceTypeName);
|
|
274
|
+
const filteredModelContent = (0, exports.filterModelTypes)(modelContent, usedTypes, modelNamespace, serviceTypeName);
|
|
259
275
|
yield promises_1.default.writeFile(modelFile, filteredModelContent, 'utf-8');
|
|
260
276
|
const originalLines = modelContent.split('\n').length;
|
|
261
277
|
const filteredLines = filteredModelContent.split('\n').length;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|