wormajs 0.0.2-beta.3 → 0.2.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.
- package/dist/constant.js +7 -10
- package/dist/core/parser/openApiParser/helper.js +91 -69
- package/dist/helper/config/GeneratorHelper.js +20 -18
- package/dist/helper/config/zType.js +2 -2
- package/dist/helper/progress.js +2 -2
- package/dist/plugins/index.js +6 -2
- package/dist/plugins/presets/payloadModifier/hepler.js +29 -33
- package/dist/plugins/presets/payloadModifier/index.js +8 -8
- package/dist/plugins/presets/platform/fastapi.js +22 -0
- package/dist/plugins/presets/platform/index.js +15 -0
- package/dist/plugins/presets/platform/knife4j.js +24 -0
- package/dist/plugins/presets/platform/shared.js +45 -0
- package/dist/plugins/presets/platform/swagger.js +32 -0
- package/dist/plugins/presets/platform/yapi.js +75 -0
- package/dist/plugins/presets/rename.js +154 -21
- package/dist/plugins/presets/tagModifier.js +6 -2
- package/dist/plugins/presets/utils.js +11 -10
- package/dist/template/index.js +0 -34
- package/dist/template/presets/alova/partials/dts-extra-config.handlebars +3 -3
- package/dist/template/presets/alova/partials/dts-fn-declare.handlebars +1 -1
- package/dist/template/presets/alova/typescript/services/{tag}.ts.handlebars +5 -5
- package/dist/template/presets/axios/partials/dts-types.handlebars +4 -4
- package/dist/template/presets/axios/typescript/services/{tag}.ts.handlebars +4 -4
- package/dist/template/presets/config/common/worma.config.js.handlebars +1 -1
- package/dist/template/presets/config/module/worma.config.js.handlebars +1 -1
- package/dist/template/presets/config/partials/generator-content.handlebars +45 -45
- package/dist/template/presets/config/typescript/worma.config.ts.handlebars +1 -1
- package/dist/template/presets/fetch/partials/dts-types.handlebars +4 -4
- package/dist/template/presets/fetch/typescript/services/{tag}.ts.handlebars +4 -4
- package/dist/template/presets/ky/partials/dts-types.handlebars +4 -4
- package/dist/template/presets/ky/typescript/services/{tag}.ts.handlebars +4 -4
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/template.js +23 -0
- package/package.json +1 -1
- package/typings/index.d.ts +9 -5
- package/typings/plugins.d.ts +134 -48
- package/dist/plugins/presets/platform.js +0 -73
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeBase = normalizeBase;
|
|
4
|
+
exports.defineUrlPlatformPlugin = defineUrlPlatformPlugin;
|
|
5
|
+
exports.withCookie = withCookie;
|
|
6
|
+
/** Remove trailing slashes from a base URL */
|
|
7
|
+
function normalizeBase(baseUrl) {
|
|
8
|
+
while (baseUrl.endsWith('/')) {
|
|
9
|
+
baseUrl = baseUrl.slice(0, -1);
|
|
10
|
+
}
|
|
11
|
+
return baseUrl;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Factory for "URL-constructing" platform plugins.
|
|
15
|
+
*
|
|
16
|
+
* Unlike the old `platform()` helper, the platform base URL(s) are supplied as
|
|
17
|
+
* the plugin's own argument (e.g. `swagger('https://petstore.swagger.io')`)
|
|
18
|
+
* rather than being read from `config.input`. The plugin writes the resolved
|
|
19
|
+
* candidate URLs into `config.input` inside its `config` hook, so downstream
|
|
20
|
+
* fetching (which tries each URL in order and keeps the first success) works
|
|
21
|
+
* unchanged.
|
|
22
|
+
*/
|
|
23
|
+
function defineUrlPlatformPlugin(name, buildUrls) {
|
|
24
|
+
return (input) => {
|
|
25
|
+
const inputs = Array.isArray(input) ? [...new Set(input)] : [input];
|
|
26
|
+
return {
|
|
27
|
+
name,
|
|
28
|
+
config({ config }) {
|
|
29
|
+
config.input = inputs.flatMap(url => buildUrls(normalizeBase(String(url))));
|
|
30
|
+
return config;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Merge a cookie into config.fetchOptions.headers */
|
|
36
|
+
function withCookie(config, cookie) {
|
|
37
|
+
config.fetchOptions = {
|
|
38
|
+
...config.fetchOptions,
|
|
39
|
+
headers: {
|
|
40
|
+
...config.fetchOptions?.headers,
|
|
41
|
+
cookie,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
return config;
|
|
45
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.swagger = void 0;
|
|
4
|
+
const constant_1 = require("../../../constant");
|
|
5
|
+
const shared_1 = require("./shared");
|
|
6
|
+
/**
|
|
7
|
+
* Swagger platform plugin.
|
|
8
|
+
*
|
|
9
|
+
* Pass the base URL of your Swagger UI / server; the plugin will try several
|
|
10
|
+
* common OpenAPI document endpoints (OAS3 first, then Swagger2, then the bare
|
|
11
|
+
* base URL) and let the framework pick the first one that responds.
|
|
12
|
+
*
|
|
13
|
+
* @param input - base URL string, or an array of base URLs
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { swagger, alovaGlobals } from 'wormajs/plugin';
|
|
18
|
+
*
|
|
19
|
+
* defineConfig({
|
|
20
|
+
* generator: [{
|
|
21
|
+
* plugins: [swagger('https://petstore3.swagger.io'), alovaGlobals()],
|
|
22
|
+
* output: './src/api',
|
|
23
|
+
* }]
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
exports.swagger = (0, shared_1.defineUrlPlatformPlugin)(constant_1.PluginName.SWAGGER, base => [
|
|
28
|
+
`${base}/openapi.json`,
|
|
29
|
+
`${base}/v2/swagger.json`,
|
|
30
|
+
`${base}/api/v3/openapi.json`,
|
|
31
|
+
base,
|
|
32
|
+
]);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.yapi = yapi;
|
|
4
|
+
const constant_1 = require("../../../constant");
|
|
5
|
+
const shared_1 = require("./shared");
|
|
6
|
+
/**
|
|
7
|
+
* YApi platform plugin.
|
|
8
|
+
*
|
|
9
|
+
* YApi projects are private, so the OpenAPI document must be exported through
|
|
10
|
+
* YApi's own export endpoint, authenticated with your login cookie. The plugin
|
|
11
|
+
* builds the export URL from the server base URL (`url`) plus the required
|
|
12
|
+
* `pid` and the optional `type` / `status` / `isWiki` query params (which
|
|
13
|
+
* default to `OpenAPIV2`, `all`, and `true` respectively).
|
|
14
|
+
*
|
|
15
|
+
* `url`, `pid` and `cookie` are required — the plugin throws a clear error when
|
|
16
|
+
* any is missing.
|
|
17
|
+
*
|
|
18
|
+
* @param options - `{ url, pid, cookie?, type?, status?, isWiki?, timeout? }`
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { yapi, alovaGlobals } from 'wormajs/plugin';
|
|
23
|
+
*
|
|
24
|
+
* defineConfig({
|
|
25
|
+
* generator: [{
|
|
26
|
+
* plugins: [
|
|
27
|
+
* yapi({
|
|
28
|
+
* url: 'https://yapi.xxx.com',
|
|
29
|
+
* pid: 123,
|
|
30
|
+
* cookie: '_yapi_token=xxx; _yapi_uid=yyy',
|
|
31
|
+
* }),
|
|
32
|
+
* alovaGlobals(),
|
|
33
|
+
* ],
|
|
34
|
+
* output: './src/api',
|
|
35
|
+
* }]
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
function yapi(options) {
|
|
40
|
+
return {
|
|
41
|
+
name: constant_1.PluginName.YAPI,
|
|
42
|
+
async config({ config }) {
|
|
43
|
+
const baseUrl = options.url;
|
|
44
|
+
if (!baseUrl) {
|
|
45
|
+
throw new Error('[yapi] `url` is required — the YApi server base URL '
|
|
46
|
+
+ '(e.g. yapi({ url: "https://yapi.xxx.com", pid: 123, cookie: "..." })).');
|
|
47
|
+
}
|
|
48
|
+
const pid = options.pid;
|
|
49
|
+
if (pid == null) {
|
|
50
|
+
throw new Error('[yapi] `pid` is required — the YApi project id used to build the export URL '
|
|
51
|
+
+ '(e.g. yapi({ url: "https://yapi.xxx.com", pid: 123, cookie: "..." })).');
|
|
52
|
+
}
|
|
53
|
+
const cookie = options.cookie ?? config.fetchOptions?.headers?.cookie;
|
|
54
|
+
if (!cookie) {
|
|
55
|
+
throw new Error('[yapi] `cookie` is required. YApi projects are private, so the export '
|
|
56
|
+
+ 'endpoint needs your login cookie to fetch the OpenAPI document.\n'
|
|
57
|
+
+ ' e.g. yapi({ url: "https://yapi.xxx.com", pid: 123, cookie: "_yapi_token=xxx; ..." })');
|
|
58
|
+
}
|
|
59
|
+
const type = options.type ?? 'OpenAPIV2';
|
|
60
|
+
const status = options.status ?? 'all';
|
|
61
|
+
const isWiki = options.isWiki ?? true;
|
|
62
|
+
const base = (0, shared_1.normalizeBase)(baseUrl);
|
|
63
|
+
config.input = `${base}/api/plugin/exportSwagger`
|
|
64
|
+
+ `?type=${encodeURIComponent(type)}`
|
|
65
|
+
+ `&pid=${encodeURIComponent(String(pid))}`
|
|
66
|
+
+ `&status=${encodeURIComponent(status)}`
|
|
67
|
+
+ `&isWiki=${isWiki}`;
|
|
68
|
+
(0, shared_1.withCookie)(config, cookie);
|
|
69
|
+
if (options.timeout != null) {
|
|
70
|
+
config.fetchOptions = { ...config.fetchOptions, timeout: options.timeout };
|
|
71
|
+
}
|
|
72
|
+
return config;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -27,8 +27,12 @@ function toPascalCase(str) {
|
|
|
27
27
|
* Applies renaming rules to the specified value
|
|
28
28
|
* @returns The renamed value, or original value if not matched
|
|
29
29
|
*/
|
|
30
|
-
function applyRenameRule(value, config, apiDescriptor) {
|
|
31
|
-
|
|
30
|
+
function applyRenameRule(value, config, apiDescriptor, level = 0) {
|
|
31
|
+
// Skip non-string values (e.g. a missing or empty operationId) to avoid runtime errors
|
|
32
|
+
if (typeof value !== 'string') {
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
if (!(0, utils_1.isMatch)(value, config.match, level)) {
|
|
32
36
|
return value;
|
|
33
37
|
}
|
|
34
38
|
if (config.transform) {
|
|
@@ -38,7 +42,7 @@ function applyRenameRule(value, config, apiDescriptor) {
|
|
|
38
42
|
return value;
|
|
39
43
|
}
|
|
40
44
|
if (config.style === 'kebabCase' && config.scope === 'refName') {
|
|
41
|
-
throw new Error(`Invalid rename style: ${config.style}
|
|
45
|
+
throw new Error(`Invalid rename style: ${config.style}, ${config.scope}`);
|
|
42
46
|
}
|
|
43
47
|
switch (config.style) {
|
|
44
48
|
case 'camelCase':
|
|
@@ -53,6 +57,59 @@ function applyRenameRule(value, config, apiDescriptor) {
|
|
|
53
57
|
throw new Error(`Invalid rename style: ${config.style}`);
|
|
54
58
|
}
|
|
55
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Validates that no duplicate names exist after renaming.
|
|
62
|
+
* Throws an error with details if duplicates are found within the same scope.
|
|
63
|
+
*
|
|
64
|
+
* @param names the renamed names to check for duplicates
|
|
65
|
+
* @param scopeLabel used to identify the scope in the error message
|
|
66
|
+
* @param apiDescriptor the API descriptor the names belong to
|
|
67
|
+
* @param originalNames optional, the original keys before renaming (same order
|
|
68
|
+
* as `names`). When provided, the error message also lists which original keys
|
|
69
|
+
* were mapped to each duplicated name, making the conflict easier to locate.
|
|
70
|
+
*/
|
|
71
|
+
function assertNoDuplicates(names, scopeLabel, apiDescriptor, originalNames) {
|
|
72
|
+
if (!names || names.length === 0)
|
|
73
|
+
return;
|
|
74
|
+
const counts = new Map();
|
|
75
|
+
for (const name of names) {
|
|
76
|
+
counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
77
|
+
}
|
|
78
|
+
const duplicates = [...counts.entries()]
|
|
79
|
+
.filter(([, count]) => count > 1)
|
|
80
|
+
.map(([name]) => name);
|
|
81
|
+
if (duplicates.length === 0)
|
|
82
|
+
return;
|
|
83
|
+
// When original keys are available, group them by the duplicated (renamed) name
|
|
84
|
+
// so users can see exactly which keys collided.
|
|
85
|
+
let detail = '';
|
|
86
|
+
if (originalNames && originalNames.length === names.length) {
|
|
87
|
+
const grouped = new Map();
|
|
88
|
+
for (let i = 0; i < names.length; i++) {
|
|
89
|
+
if (duplicates.includes(names[i])) {
|
|
90
|
+
const list = grouped.get(names[i]) ?? [];
|
|
91
|
+
list.push(originalNames[i]);
|
|
92
|
+
grouped.set(names[i], list);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
detail = [...grouped.entries()]
|
|
96
|
+
.map(([renamed, originals]) => ` ${renamed} <- [${originals.join(', ')}]`)
|
|
97
|
+
.join('\n');
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
detail = ` ${duplicates.join(', ')}`;
|
|
101
|
+
}
|
|
102
|
+
const method = apiDescriptor.method ? String(apiDescriptor.method).toUpperCase() : '';
|
|
103
|
+
const url = apiDescriptor.url ?? '';
|
|
104
|
+
throw new Error(`[rename] Duplicate names found after renaming (scope=${scopeLabel}):\n`
|
|
105
|
+
+ ` Conflicting (renamed) keys and their original keys:\n`
|
|
106
|
+
+ `${detail}\n`
|
|
107
|
+
+ ` Reason: different original names were mapped to the same name after `
|
|
108
|
+
+ `match/transform/style processing, which would cause naming conflicts or `
|
|
109
|
+
+ `missing references in the generated code.\n`
|
|
110
|
+
+ ` Please adjust the rename config to avoid mapping multiple names to the same result.\n`
|
|
111
|
+
+ ` API: ${method} ${url}`);
|
|
112
|
+
}
|
|
56
113
|
/**
|
|
57
114
|
* renames URL path by processing each segment individually
|
|
58
115
|
* while keeping path parameter placeholders
|
|
@@ -65,27 +122,77 @@ function renameUrl(url, config, apiDescriptor) {
|
|
|
65
122
|
if ((segment.startsWith('{') && segment.endsWith('}')) || !segment) {
|
|
66
123
|
return segment;
|
|
67
124
|
}
|
|
68
|
-
return applyRenameRule(segment, config, apiDescriptor);
|
|
125
|
+
return applyRenameRule(segment, config, apiDescriptor, 0);
|
|
69
126
|
})
|
|
70
127
|
.join('/')
|
|
71
128
|
.replace(/^\/{2,}/g, '/');
|
|
72
129
|
}
|
|
73
130
|
/**
|
|
74
|
-
*
|
|
131
|
+
* Recursively processes a schema (supports nested objects and arrays),
|
|
132
|
+
* applying property renaming to nested objects.
|
|
133
|
+
* @param schema current schema
|
|
134
|
+
* @param config rename configuration
|
|
135
|
+
* @param apiDescriptor API descriptor
|
|
136
|
+
* @param level current nesting level (starts from 0)
|
|
137
|
+
* @param scopeLabel used to identify the scope in duplicate-name errors
|
|
138
|
+
*/
|
|
139
|
+
function transformSchema(schema, config, apiDescriptor, level, scopeLabel) {
|
|
140
|
+
if (!schema || typeof schema !== 'object')
|
|
141
|
+
return schema;
|
|
142
|
+
// Nested object: recursively process its properties
|
|
143
|
+
if ('properties' in schema && schema.properties) {
|
|
144
|
+
return transformProperties(schema, config, apiDescriptor, level, scopeLabel);
|
|
145
|
+
}
|
|
146
|
+
// Array: recursively process items (supports a single schema or an array of schemas)
|
|
147
|
+
if (schema.items && typeof schema.items === 'object') {
|
|
148
|
+
if (Array.isArray(schema.items)) {
|
|
149
|
+
return {
|
|
150
|
+
...schema,
|
|
151
|
+
items: schema.items.map((item) => transformSchema(item, config, apiDescriptor, level, scopeLabel)),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
...schema,
|
|
156
|
+
items: transformSchema(schema.items, config, apiDescriptor, level, scopeLabel),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return schema;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Transforms object properties using the renaming rules (supports deep recursive renaming)
|
|
163
|
+
* @param obj object whose properties to transform
|
|
164
|
+
* @param config rename configuration
|
|
165
|
+
* @param apiDescriptor API descriptor
|
|
166
|
+
* @param level current nesting level (starts from 0), passed through to the match function
|
|
167
|
+
* @param scopeLabel used to identify the scope in duplicate-name errors
|
|
75
168
|
*/
|
|
76
|
-
function transformProperties(obj, config, apiDescriptor) {
|
|
77
|
-
if (!obj || typeof obj !== 'object' || !('properties' in obj)) {
|
|
169
|
+
function transformProperties(obj, config, apiDescriptor, level = 0, scopeLabel = config.scope ?? 'data') {
|
|
170
|
+
if (!obj || typeof obj !== 'object' || !('properties' in obj) || !obj.properties) {
|
|
78
171
|
return obj;
|
|
79
172
|
}
|
|
80
|
-
const properties =
|
|
173
|
+
const properties = obj.properties;
|
|
81
174
|
const newProperties = {};
|
|
175
|
+
const newKeys = [];
|
|
176
|
+
// Record the oldKey -> newKey mapping, used to keep the same-level required array in sync
|
|
177
|
+
const keyMap = new Map();
|
|
82
178
|
for (const key in properties) {
|
|
83
|
-
const newKey = applyRenameRule(key, config, apiDescriptor);
|
|
84
|
-
|
|
179
|
+
const newKey = applyRenameRule(key, config, apiDescriptor, level);
|
|
180
|
+
newKeys.push(newKey);
|
|
181
|
+
keyMap.set(key, newKey);
|
|
182
|
+
newProperties[newKey] = transformSchema(properties[key], config, apiDescriptor, level + 1, scopeLabel);
|
|
183
|
+
}
|
|
184
|
+
// Duplicate property names within the same object level would overwrite each other, so report an error
|
|
185
|
+
assertNoDuplicates(newKeys, scopeLabel, apiDescriptor, Object.keys(properties));
|
|
186
|
+
// Keep the required array of the current level in sync with the renamed property names;
|
|
187
|
+
// otherwise the generator would crash during normalization because required references a non-existent property
|
|
188
|
+
let newRequired = obj.required;
|
|
189
|
+
if (Array.isArray(newRequired)) {
|
|
190
|
+
newRequired = newRequired.map((r) => keyMap.get(r) ?? r);
|
|
85
191
|
}
|
|
86
192
|
return {
|
|
87
193
|
...obj,
|
|
88
194
|
properties: newProperties,
|
|
195
|
+
...(newRequired !== obj.required ? { required: newRequired } : {}),
|
|
89
196
|
};
|
|
90
197
|
}
|
|
91
198
|
/**
|
|
@@ -99,7 +206,7 @@ function transformParameters(parameters, type, config, apiDescriptor) {
|
|
|
99
206
|
if (param.in === type) {
|
|
100
207
|
return {
|
|
101
208
|
...param,
|
|
102
|
-
name: applyRenameRule(param.name, config, apiDescriptor),
|
|
209
|
+
name: applyRenameRule(param.name, config, apiDescriptor, 0),
|
|
103
210
|
};
|
|
104
211
|
}
|
|
105
212
|
return param;
|
|
@@ -110,10 +217,14 @@ function transformRefNameMap(refNameMap, config, apiDescriptor) {
|
|
|
110
217
|
return refNameMap;
|
|
111
218
|
}
|
|
112
219
|
const newRefNameMap = {};
|
|
220
|
+
const newValues = [];
|
|
113
221
|
for (const key in refNameMap) {
|
|
114
|
-
const newValue = applyRenameRule(refNameMap[key], config, apiDescriptor);
|
|
222
|
+
const newValue = applyRenameRule(refNameMap[key], config, apiDescriptor, 0);
|
|
223
|
+
newValues.push(newValue);
|
|
115
224
|
newRefNameMap[key] = newValue;
|
|
116
225
|
}
|
|
226
|
+
// Different $refs mapping to the same type name would generate duplicate interfaces, so report an error
|
|
227
|
+
assertNoDuplicates(newValues, 'refName', apiDescriptor, Object.keys(refNameMap));
|
|
117
228
|
return newRefNameMap;
|
|
118
229
|
}
|
|
119
230
|
/**
|
|
@@ -125,6 +236,7 @@ function transformRefNameMap(refNameMap, config, apiDescriptor) {
|
|
|
125
236
|
* - pathParams: Renames path parameters and their placeholders in URL
|
|
126
237
|
* - data: Renames request body properties
|
|
127
238
|
* - response: Renames response body properties
|
|
239
|
+
* - name: Renames the generated API function name (operationId)
|
|
128
240
|
*/
|
|
129
241
|
function renameApiDescriptor(apiDescriptor, config) {
|
|
130
242
|
if (!apiDescriptor)
|
|
@@ -134,33 +246,49 @@ function renameApiDescriptor(apiDescriptor, config) {
|
|
|
134
246
|
switch (scope) {
|
|
135
247
|
case constant_1.RenameScope.PARAMS:
|
|
136
248
|
if (newDescriptor.parameters) {
|
|
249
|
+
const originalParams = newDescriptor.parameters.filter(p => p.in === constant_1.ParameterIn.QUERY).map(p => p.name);
|
|
137
250
|
newDescriptor.parameters = transformParameters(newDescriptor.parameters, constant_1.ParameterIn.QUERY, config, apiDescriptor);
|
|
251
|
+
// Duplicate query parameter names would be indistinguishable when calling the API, so report an error
|
|
252
|
+
assertNoDuplicates(newDescriptor.parameters.filter(p => p.in === constant_1.ParameterIn.QUERY).map(p => p.name), 'params', apiDescriptor, originalParams);
|
|
138
253
|
}
|
|
139
254
|
break;
|
|
140
255
|
case constant_1.RenameScope.PATH_PARAMS:
|
|
141
256
|
if (newDescriptor.parameters) {
|
|
257
|
+
const originalParams = newDescriptor.parameters.filter(p => p.in === constant_1.ParameterIn.PATH).map(p => p.name);
|
|
142
258
|
newDescriptor.parameters = transformParameters(newDescriptor.parameters, constant_1.ParameterIn.PATH, config, apiDescriptor);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
259
|
+
if (newDescriptor.url) {
|
|
260
|
+
newDescriptor.url = newDescriptor.url.replace(/\{([^}]+)\}/g, (match, paramName) => {
|
|
261
|
+
const newName = applyRenameRule(paramName, config, apiDescriptor, 0);
|
|
262
|
+
return `{${newName}}`;
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
// Duplicate path parameter names would make URL placeholders no longer match the parameters, so report an error
|
|
266
|
+
assertNoDuplicates((newDescriptor.parameters ?? []).filter(p => p.in === constant_1.ParameterIn.PATH).map(p => p.name), 'pathParams', apiDescriptor, originalParams);
|
|
149
267
|
}
|
|
150
268
|
break;
|
|
151
269
|
case constant_1.RenameScope.DATA:
|
|
152
270
|
if (newDescriptor.requestBody) {
|
|
153
|
-
newDescriptor.requestBody = transformProperties(newDescriptor.requestBody, config, apiDescriptor);
|
|
271
|
+
newDescriptor.requestBody = transformProperties(newDescriptor.requestBody, config, apiDescriptor, 0, 'data');
|
|
154
272
|
}
|
|
155
273
|
break;
|
|
156
274
|
case constant_1.RenameScope.RESPONSE:
|
|
157
275
|
if (newDescriptor.responses) {
|
|
158
|
-
newDescriptor.responses = transformProperties(newDescriptor.responses, config, apiDescriptor);
|
|
276
|
+
newDescriptor.responses = transformProperties(newDescriptor.responses, config, apiDescriptor, 0, 'response');
|
|
159
277
|
}
|
|
160
278
|
break;
|
|
161
279
|
case constant_1.RenameScope.URL:
|
|
162
280
|
if (newDescriptor.url) {
|
|
281
|
+
const originalUrlNames = newDescriptor.url
|
|
282
|
+
.split('/')
|
|
283
|
+
.filter(Boolean)
|
|
284
|
+
.map(segment => (segment.startsWith('{') && segment.endsWith('}') ? segment.slice(1, -1) : segment));
|
|
163
285
|
newDescriptor.url = renameUrl(newDescriptor.url, config, apiDescriptor);
|
|
286
|
+
// Duplicate path segments would make the URL unable to distinguish different resources, so report an error
|
|
287
|
+
const urlNames = newDescriptor.url
|
|
288
|
+
.split('/')
|
|
289
|
+
.filter(Boolean)
|
|
290
|
+
.map(segment => (segment.startsWith('{') && segment.endsWith('}') ? segment.slice(1, -1) : segment));
|
|
291
|
+
assertNoDuplicates(urlNames, 'url', apiDescriptor, originalUrlNames);
|
|
164
292
|
}
|
|
165
293
|
break;
|
|
166
294
|
case constant_1.RenameScope.REF_NAME:
|
|
@@ -168,6 +296,11 @@ function renameApiDescriptor(apiDescriptor, config) {
|
|
|
168
296
|
newDescriptor.refNameMap = transformRefNameMap(newDescriptor.refNameMap, config, apiDescriptor);
|
|
169
297
|
}
|
|
170
298
|
break;
|
|
299
|
+
case constant_1.RenameScope.NAME:
|
|
300
|
+
if (newDescriptor.operationId != null) {
|
|
301
|
+
newDescriptor.operationId = applyRenameRule(newDescriptor.operationId, config, apiDescriptor, 0);
|
|
302
|
+
}
|
|
303
|
+
break;
|
|
171
304
|
default:
|
|
172
305
|
// No action needed, keep original descriptor
|
|
173
306
|
break;
|
|
@@ -185,7 +318,7 @@ function rename(config) {
|
|
|
185
318
|
throw new Error('at least one of `style` or `transform` is required');
|
|
186
319
|
}
|
|
187
320
|
if (conf.style === 'kebabCase' && conf.scope === 'refName') {
|
|
188
|
-
throw new Error(`Invalid rename style: ${conf.style}
|
|
321
|
+
throw new Error(`Invalid rename style: ${conf.style}, ${conf.scope}`);
|
|
189
322
|
}
|
|
190
323
|
}
|
|
191
324
|
return {
|
|
@@ -38,6 +38,10 @@ function processApiTags(apiDescriptor, handler) {
|
|
|
38
38
|
// Process each tag and filter out null/undefined results
|
|
39
39
|
newDescriptor.tags = newDescriptor.tags
|
|
40
40
|
.map((tag) => {
|
|
41
|
+
// Resolve the tag to keep when the modification cannot be applied:
|
|
42
|
+
// keep the original tag only if it is itself valid, otherwise drop it.
|
|
43
|
+
// This guarantees that the final output never contains an invalid tag.
|
|
44
|
+
const keepOriginalOrDrop = () => isValidTagName(tag) ? tag : null;
|
|
41
45
|
try {
|
|
42
46
|
// Call user provided handler function
|
|
43
47
|
const modifiedTag = handler(tag);
|
|
@@ -47,12 +51,12 @@ function processApiTags(apiDescriptor, handler) {
|
|
|
47
51
|
}
|
|
48
52
|
// Validate if modified tag follows naming conventions
|
|
49
53
|
if (!isValidTagName(modifiedTag)) {
|
|
50
|
-
return
|
|
54
|
+
return keepOriginalOrDrop(); // Keep original tag if valid, otherwise drop
|
|
51
55
|
}
|
|
52
56
|
return modifiedTag.trim(); // Return trimmed modified tag
|
|
53
57
|
}
|
|
54
58
|
catch {
|
|
55
|
-
return
|
|
59
|
+
return keepOriginalOrDrop(); // Keep original tag if valid, otherwise drop on error
|
|
56
60
|
}
|
|
57
61
|
})
|
|
58
62
|
.filter((tag) => tag != null); // Filter out null/undefined values
|
|
@@ -4,23 +4,24 @@ exports.extend = extend;
|
|
|
4
4
|
exports.isMatch = isMatch;
|
|
5
5
|
const prepareConfig_1 = require("../../functions/prepareConfig");
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* @param config
|
|
11
|
-
* @param newConfig
|
|
12
|
-
* @returns
|
|
7
|
+
* Extends the generator configuration:
|
|
8
|
+
* merges the provided newConfig (object or function) with the base config to produce the merged config.
|
|
9
|
+
* When newConfig is not provided, falls back to the original config.
|
|
10
|
+
* @param config base configuration
|
|
11
|
+
* @param newConfig config to merge, or a function that produces the merged config
|
|
12
|
+
* @returns the merged and normalized configuration
|
|
13
13
|
*/
|
|
14
14
|
function extend(config, newConfig) {
|
|
15
|
-
//
|
|
15
|
+
// Compute the extension config based on newConfig's type: call it if it's a function,
|
|
16
|
+
// otherwise use it directly; fall back to the original config if not provided
|
|
16
17
|
const pluginExtendsConfig = typeof newConfig === 'function' ? newConfig(config) : (newConfig ?? config);
|
|
17
|
-
//
|
|
18
|
+
// Use extendsConfig to merge and normalize, ensuring the final structure meets the generator's requirements
|
|
18
19
|
return (0, prepareConfig_1.extendsConfig)(config, pluginExtendsConfig);
|
|
19
20
|
}
|
|
20
21
|
/**
|
|
21
22
|
* Tests if value matches the specified rule
|
|
22
23
|
*/
|
|
23
|
-
function isMatch(value, match) {
|
|
24
|
+
function isMatch(value, match, level = 0) {
|
|
24
25
|
if (!match)
|
|
25
26
|
return true;
|
|
26
27
|
if (typeof match === 'string') {
|
|
@@ -30,7 +31,7 @@ function isMatch(value, match) {
|
|
|
30
31
|
return match.test(value);
|
|
31
32
|
}
|
|
32
33
|
if (typeof match === 'function') {
|
|
33
|
-
return match(value);
|
|
34
|
+
return match(value, level);
|
|
34
35
|
}
|
|
35
36
|
return false;
|
|
36
37
|
}
|
package/dist/template/index.js
CHANGED
|
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getPresetTemplatePath = getPresetTemplatePath;
|
|
7
|
-
exports.registerProcessTypeHelper = registerProcessTypeHelper;
|
|
8
7
|
exports.config = config;
|
|
9
8
|
exports.alovaGlobals = alovaGlobals;
|
|
10
9
|
exports.alova = alova;
|
|
@@ -21,27 +20,6 @@ function getPresetTemplatePath(presetName) {
|
|
|
21
20
|
// 编译后在 dist/template/presets 目录
|
|
22
21
|
return node_path_1.default.join(__dirname, 'presets', presetName);
|
|
23
22
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Register processType helper on an hbs instance.
|
|
26
|
-
*
|
|
27
|
-
* Scans the type string for every PascalCase identifier that is in componentNames
|
|
28
|
-
* (and not already prefixed with "ComponentTypes.") and prefixes it.
|
|
29
|
-
* Works uniformly for top-level names, generics, object literals, and arrays.
|
|
30
|
-
*/
|
|
31
|
-
function registerProcessTypeHelper(hbs) {
|
|
32
|
-
hbs.registerHelper('processType', (_typeStr, _componentNames) => {
|
|
33
|
-
const typeStr = _typeStr;
|
|
34
|
-
const componentNames = _componentNames;
|
|
35
|
-
if (!typeStr || !Array.isArray(componentNames) || componentNames.length === 0) {
|
|
36
|
-
return new hbs.SafeString(typeStr || 'unknown');
|
|
37
|
-
}
|
|
38
|
-
const componentSet = new Set(componentNames);
|
|
39
|
-
const result = typeStr.replace(/(?<!ComponentTypes\.)(\b[A-Z]\w*\b)/g, (match) => {
|
|
40
|
-
return componentSet.has(match) ? `ComponentTypes.${match}` : match;
|
|
41
|
-
});
|
|
42
|
-
return new hbs.SafeString(result);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
23
|
// ========== Template Preset Plugins ==========
|
|
46
24
|
/**
|
|
47
25
|
* worma.config 模板预设 - plugin mode
|
|
@@ -100,9 +78,6 @@ function alova(opts) {
|
|
|
100
78
|
getTemplate() {
|
|
101
79
|
return { path: getPresetTemplatePath(constant_1.PresetTemplateName.ALOVA) };
|
|
102
80
|
},
|
|
103
|
-
onHandlebarsCreated({ hbs }) {
|
|
104
|
-
registerProcessTypeHelper(hbs);
|
|
105
|
-
},
|
|
106
81
|
beforeCodeGenerate({ data }) {
|
|
107
82
|
data.config = {
|
|
108
83
|
...data.config,
|
|
@@ -122,9 +97,6 @@ function axios(opts) {
|
|
|
122
97
|
getTemplate() {
|
|
123
98
|
return { path: getPresetTemplatePath(constant_1.PresetTemplateName.AXIOS) };
|
|
124
99
|
},
|
|
125
|
-
onHandlebarsCreated({ hbs }) {
|
|
126
|
-
registerProcessTypeHelper(hbs);
|
|
127
|
-
},
|
|
128
100
|
beforeCodeGenerate({ data }) {
|
|
129
101
|
data.config = {
|
|
130
102
|
...data.config,
|
|
@@ -144,9 +116,6 @@ function fetch(opts) {
|
|
|
144
116
|
getTemplate() {
|
|
145
117
|
return { path: getPresetTemplatePath(constant_1.PresetTemplateName.FETCH) };
|
|
146
118
|
},
|
|
147
|
-
onHandlebarsCreated({ hbs }) {
|
|
148
|
-
registerProcessTypeHelper(hbs);
|
|
149
|
-
},
|
|
150
119
|
beforeCodeGenerate({ data }) {
|
|
151
120
|
data.config = {
|
|
152
121
|
...data.config,
|
|
@@ -166,9 +135,6 @@ function ky(opts) {
|
|
|
166
135
|
getTemplate() {
|
|
167
136
|
return { path: getPresetTemplatePath(constant_1.PresetTemplateName.KY) };
|
|
168
137
|
},
|
|
169
|
-
onHandlebarsCreated({ hbs }) {
|
|
170
|
-
registerProcessTypeHelper(hbs);
|
|
171
|
-
},
|
|
172
138
|
beforeCodeGenerate({ data }) {
|
|
173
139
|
data.config = {
|
|
174
140
|
...data.config,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{{#or pathParameters queryParameters requestBody}}
|
|
2
2
|
type {{{name}}}ExtraConfig = {
|
|
3
|
-
{{#if pathParameters}}pathParams: {{
|
|
4
|
-
{{/if}}{{#if queryParameters}}params: {{
|
|
5
|
-
{{/if}}{{#if requestBody}}data: {{
|
|
3
|
+
{{#if pathParameters}}pathParams: {{addNamespace pathParameters @root.componentNames}};
|
|
4
|
+
{{/if}}{{#if queryParameters}}params: {{addNamespace queryParameters @root.componentNames}};
|
|
5
|
+
{{/if}}{{#if requestBody}}data: {{addNamespace requestBody @root.componentNames}};
|
|
6
6
|
{{/if}}
|
|
7
7
|
};
|
|
8
8
|
{{/or}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function {{{name}}}<Config extends Alova2MethodConfig<{{
|
|
1
|
+
export declare function {{{name}}}<Config extends Alova2MethodConfig<{{addNamespace response @root.componentNames}}> & {{{name}}}ExtraConfig>(config: Config): Alova2Method<ExtractResponded<Config, '{{{name}}}', {{addNamespace response @root.componentNames}}>>;
|
|
@@ -12,15 +12,15 @@ import type * as ComponentTypes from '../components';
|
|
|
12
12
|
{{#each apis}}
|
|
13
13
|
|
|
14
14
|
interface {{{name}}}ExtraConfig {
|
|
15
|
-
{{#if pathParameters}}pathParams: {{
|
|
16
|
-
{{/if}}{{#if queryParameters}}params: {{
|
|
17
|
-
{{/if}}{{#if requestBody}}data: {{
|
|
15
|
+
{{#if pathParameters}}pathParams: {{addNamespace pathParameters @root.componentNames}};
|
|
16
|
+
{{/if}}{{#if queryParameters}}params: {{addNamespace queryParameters @root.componentNames}};
|
|
17
|
+
{{/if}}{{#if requestBody}}data: {{addNamespace requestBody @root.componentNames}};
|
|
18
18
|
{{/if}}
|
|
19
19
|
}
|
|
20
20
|
{{> api-jsdoc}}
|
|
21
|
-
export function {{{name}}}<Config extends Alova2MethodConfig<{{
|
|
21
|
+
export function {{{name}}}<Config extends Alova2MethodConfig<{{addNamespace response @root.componentNames}}> & {{{name}}}ExtraConfig>(config: Config): Alova2Method<ExtractResponded<Config, '{{{name}}}', {{addNamespace response @root.componentNames}}>> {
|
|
22
22
|
const { url, data, mergedConfig } = buildPayload('{{{path}}}', {{{tag}}}DefaultConfig, {{{name}}}.name, config);
|
|
23
|
-
return alovaInstance.Request<ExtractResponded<Config, '{{{name}}}', {{
|
|
23
|
+
return alovaInstance.Request<ExtractResponded<Config, '{{{name}}}', {{addNamespace response @root.componentNames}}>>({
|
|
24
24
|
...mergedConfig,
|
|
25
25
|
url,
|
|
26
26
|
data,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
type {{{name}}}Response = {{
|
|
1
|
+
type {{{name}}}Response = {{addNamespace response @root.componentNames}};
|
|
2
2
|
type {{{name}}}ExtraConfig = Omit<AxiosRequestConfig, 'data' | 'params'> & {
|
|
3
|
-
{{#if pathParameters}}pathParams: {{
|
|
4
|
-
{{/if}}{{#if queryParameters}}params: {{
|
|
5
|
-
{{/if}}{{#if requestBody}}data: {{
|
|
3
|
+
{{#if pathParameters}}pathParams: {{addNamespace pathParameters @root.componentNames}};
|
|
4
|
+
{{/if}}{{#if queryParameters}}params: {{addNamespace queryParameters @root.componentNames}};
|
|
5
|
+
{{/if}}{{#if requestBody}}data: {{addNamespace requestBody @root.componentNames}};
|
|
6
6
|
{{/if}}
|
|
7
7
|
};
|
|
@@ -9,11 +9,11 @@ import { {{{tagName}}}DefaultConfig } from '.';
|
|
|
9
9
|
|
|
10
10
|
{{#each tagedApis}}
|
|
11
11
|
{{#each apis}}
|
|
12
|
-
type {{{name}}}Response = {{
|
|
12
|
+
type {{{name}}}Response = {{addNamespace response @root.componentNames}};
|
|
13
13
|
type {{{name}}}ExtraConfig = Omit<AxiosRequestConfig, 'data' | 'params'> & {
|
|
14
|
-
{{#if pathParameters}}pathParams: {{
|
|
15
|
-
{{/if}}{{#if queryParameters}}params: {{
|
|
16
|
-
{{/if}}{{#if requestBody}}data: {{
|
|
14
|
+
{{#if pathParameters}}pathParams: {{addNamespace pathParameters @root.componentNames}};
|
|
15
|
+
{{/if}}{{#if queryParameters}}params: {{addNamespace queryParameters @root.componentNames}};
|
|
16
|
+
{{/if}}{{#if requestBody}}data: {{addNamespace requestBody @root.componentNames}};
|
|
17
17
|
{{/if}}
|
|
18
18
|
};
|
|
19
19
|
{{> api-jsdoc}}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { defineConfig } = require('wormajs');
|
|
2
|
-
const {
|
|
2
|
+
const { swagger, aiDoc{{#if templateImport}}, {{{templateImport}}}{{/if}} } = require('wormajs/plugin');
|
|
3
3
|
|
|
4
4
|
// For more config detailed visit:
|
|
5
5
|
// https://github.com/alovajs/devtools
|