topbit 3.1.1 → 3.1.3
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/README.cn.md +0 -9
- package/README.md +0 -8
- package/package.json +1 -1
- package/src/lib/npargv.js +224 -280
package/README.cn.md
CHANGED
|
@@ -1579,15 +1579,6 @@ app.run(1235)
|
|
|
1579
1579
|
|
|
1580
1580
|
---
|
|
1581
1581
|
|
|
1582
|
-
以下是根据你提供的 `titbit-toolkit` 原始文档内容,按照新的列表顺序重新整理的文档。
|
|
1583
|
-
|
|
1584
|
-
**注意**:
|
|
1585
|
-
* 根据指示,文档已针对新的 **Topbit** 框架语境进行了调整(即不再强调独立安装扩展)。
|
|
1586
|
-
* 列表中未包含的组件(如 `Timing`, `RealIP`, `MixLogger`, `SendType`, `SetFinal`, `Http2Limit`)已被移除。
|
|
1587
|
-
* 列表中包含但原文档未提供详细说明的组件(如 `SNI`),仅保留了描述占位符。
|
|
1588
|
-
|
|
1589
|
-
---
|
|
1590
|
-
|
|
1591
1582
|
### 1. Cors (跨域支持)
|
|
1592
1583
|
|
|
1593
1584
|
支持跨域请求的中间件。尽管是对跨域场景的支持,但在具备 `origin` 消息头的跨域通信和 `referer` 资源引入场景都做了支持,并且可以灵活选择和配置。
|
package/README.md
CHANGED
|
@@ -1543,14 +1543,6 @@ app.run(1235)
|
|
|
1543
1543
|
|
|
1544
1544
|
---
|
|
1545
1545
|
|
|
1546
|
-
The following documentation is reorganized based on the original `titbit-toolkit` documentation you provided, adapted for the new list order.
|
|
1547
|
-
|
|
1548
|
-
**Note**:
|
|
1549
|
-
* The documentation has been adjusted for the **Topbit** framework context (no longer emphasizing standalone installation of extensions).
|
|
1550
|
-
* Components not included in the list above have been removed.
|
|
1551
|
-
|
|
1552
|
-
---
|
|
1553
|
-
|
|
1554
1546
|
### 1. Cors (Cross-Origin Support)
|
|
1555
1547
|
|
|
1556
1548
|
Middleware that supports cross-origin requests. It supports cross-origin communication via the `origin` header as well as resource inclusion via `referer`, allowing for flexible selection and configuration.
|
package/package.json
CHANGED
package/src/lib/npargv.js
CHANGED
|
@@ -1,354 +1,298 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* '-a' : {
|
|
6
|
-
* name: 'a',
|
|
7
|
-
* type: 'number|string|int|float|bool',
|
|
8
|
-
* min: 0,
|
|
9
|
-
* max: 100,
|
|
10
|
-
* match: RegExp,
|
|
11
|
-
* default : any
|
|
12
|
-
* },
|
|
13
|
-
*
|
|
14
|
-
* '$2': {
|
|
15
|
-
*
|
|
16
|
-
* },
|
|
17
|
-
* {
|
|
18
|
-
* '--port=' : {
|
|
19
|
-
* name : 'port',
|
|
20
|
-
* type : 'int',
|
|
21
|
-
* min: 2000,
|
|
22
|
-
* max: 5000,
|
|
23
|
-
* default: 3456
|
|
24
|
-
* }
|
|
25
|
-
* }
|
|
26
|
-
* }
|
|
27
|
-
*
|
|
4
|
+
* 验证并转换数值
|
|
28
5
|
*/
|
|
6
|
+
function validateAndConvert(name, rule, value) {
|
|
7
|
+
let val = value
|
|
29
8
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
ok: false,
|
|
36
|
-
message: `${a} 类型错误,要求参数必须是数字类型:${next}`
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (ainfo.type === 'int' || ainfo.type === 'number') {
|
|
41
|
-
next = parseInt(next)
|
|
42
|
-
} else {
|
|
43
|
-
next = parseFloat(next)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (ainfo.min !== undefined) {
|
|
48
|
-
if (next < ainfo.min) {
|
|
49
|
-
return {
|
|
50
|
-
ok: false,
|
|
51
|
-
message: `${a} 数值不能低于 ${ainfo.min}`
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (ainfo.max !== undefined) {
|
|
57
|
-
if (next > ainfo.max) {
|
|
58
|
-
return {
|
|
59
|
-
ok: false,
|
|
60
|
-
message: `${a} 数值不能大于 ${ainfo.max}`
|
|
61
|
-
}
|
|
9
|
+
// 1. 类型检查与转换
|
|
10
|
+
if (['int', 'float', 'number'].includes(rule.type)) {
|
|
11
|
+
if (isNaN(val)) {
|
|
12
|
+
throw new Error(`${name} 类型错误,要求参数必须是数字类型,当前值为:${val}`)
|
|
62
13
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
ok: false,
|
|
69
|
-
message: `${a} 数值无法匹配 ${next}`
|
|
70
|
-
}
|
|
14
|
+
if (rule.type === 'int') {
|
|
15
|
+
val = parseInt(val, 10)
|
|
16
|
+
} else {
|
|
17
|
+
val = parseFloat(val)
|
|
71
18
|
}
|
|
72
19
|
}
|
|
73
20
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
val = ainfo.callback(next)
|
|
78
|
-
if (val === undefined) val = next
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
obj[ ainfo.name || a ] = val
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
ok: true,
|
|
85
|
-
message: ''
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function checkAndSet (a, ainfo, obj, next) {
|
|
91
|
-
|
|
92
|
-
if (typeof ainfo === 'boolean') {
|
|
93
|
-
obj[a] = true
|
|
94
|
-
return {
|
|
95
|
-
ok: true,
|
|
96
|
-
message: '',
|
|
97
|
-
op: 'none'
|
|
98
|
-
}
|
|
21
|
+
// 2. 范围检查
|
|
22
|
+
if (rule.min !== undefined && val < rule.min) {
|
|
23
|
+
throw new Error(`${name} 数值不能低于 ${rule.min}`)
|
|
99
24
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
obj[ ainfo.name || a ] = true
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
ok: true,
|
|
106
|
-
message: '',
|
|
107
|
-
op: 'none'
|
|
108
|
-
}
|
|
25
|
+
if (rule.max !== undefined && val > rule.max) {
|
|
26
|
+
throw new Error(`${name} 数值不能大于 ${rule.max}`)
|
|
109
27
|
}
|
|
110
28
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
29
|
+
// 3. 正则匹配
|
|
30
|
+
if (rule.match && rule.match instanceof RegExp) {
|
|
31
|
+
if (!rule.match.test(String(value))) { // 确保用原始字符串测试
|
|
32
|
+
throw new Error(`${name} 格式不匹配`)
|
|
115
33
|
}
|
|
116
34
|
}
|
|
117
35
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (r.ok) {
|
|
125
|
-
r.op = 'next'
|
|
126
|
-
|
|
127
|
-
if (a[a.length - 1] === '=') {
|
|
128
|
-
r.op = 'none'
|
|
129
|
-
}
|
|
36
|
+
// 4. 回调处理
|
|
37
|
+
if (typeof rule.callback === 'function') {
|
|
38
|
+
const callbackVal = rule.callback(val)
|
|
39
|
+
if (callbackVal !== undefined) val = callbackVal
|
|
130
40
|
}
|
|
131
41
|
|
|
132
|
-
return
|
|
42
|
+
return val
|
|
133
43
|
}
|
|
134
44
|
|
|
135
|
-
|
|
136
|
-
|
|
45
|
+
/**
|
|
46
|
+
* 自动填充默认值配置
|
|
47
|
+
*/
|
|
48
|
+
function applyAutoDefault(rule) {
|
|
49
|
+
if (rule.default !== undefined) return
|
|
50
|
+
|
|
51
|
+
switch (rule.type) {
|
|
137
52
|
case 'number':
|
|
138
53
|
case 'int':
|
|
139
54
|
case 'float':
|
|
140
|
-
|
|
141
|
-
if (opts[k].min)
|
|
142
|
-
opts[k].default = opts[k].min;
|
|
55
|
+
rule.default = rule.min !== undefined ? rule.min : 0
|
|
143
56
|
break
|
|
144
|
-
|
|
145
57
|
case 'string':
|
|
146
|
-
|
|
58
|
+
rule.default = ''
|
|
147
59
|
break
|
|
148
|
-
|
|
149
60
|
case 'bool':
|
|
150
61
|
case 'boolean':
|
|
151
|
-
|
|
62
|
+
rule.default = false
|
|
152
63
|
break
|
|
153
64
|
}
|
|
154
65
|
}
|
|
155
66
|
|
|
156
|
-
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
67
|
+
/**
|
|
68
|
+
* 规范化 Schema 定义
|
|
69
|
+
* 将简写转换为完整对象,处理自动默认值
|
|
70
|
+
*/
|
|
71
|
+
function normalizeSchema(schema, autoDefault) {
|
|
72
|
+
const normalized = {}
|
|
73
|
+
|
|
74
|
+
for (let key in schema) {
|
|
75
|
+
let rule = schema[key]
|
|
76
|
+
|
|
77
|
+
// 1. 字符串简写处理: {'-v': 'verbose'} -> {'-v': { name: 'verbose', type: 'boolean' }}
|
|
78
|
+
if (typeof rule === 'string' && rule.trim().length > 0) {
|
|
79
|
+
rule = { name: rule.trim(), type: 'boolean', default: false }
|
|
80
|
+
}
|
|
81
|
+
else if (typeof rule !== 'object' || rule === null) {
|
|
82
|
+
rule = { name: key, type: 'boolean' }
|
|
83
|
+
}
|
|
173
84
|
|
|
174
|
-
|
|
85
|
+
// 3. 自动推断 Type
|
|
86
|
+
if (!rule.type) {
|
|
87
|
+
if (key.includes('=')) {
|
|
88
|
+
rule.type = 'string'
|
|
89
|
+
} else if (rule.match || rule.callback) {
|
|
90
|
+
rule.type = 'string'
|
|
91
|
+
} else if (rule.min !== undefined || rule.max !== undefined) {
|
|
92
|
+
rule.type = 'int'
|
|
93
|
+
} else if (rule.default !== undefined) {
|
|
94
|
+
const defaultType = typeof rule.default
|
|
95
|
+
rule.type = ['number', 'boolean', 'string'].includes(defaultType) ? defaultType : 'string'
|
|
96
|
+
} else {
|
|
97
|
+
rule.type = 'boolean'
|
|
98
|
+
}
|
|
99
|
+
}
|
|
175
100
|
|
|
176
|
-
|
|
177
|
-
autoDefault
|
|
178
|
-
|
|
179
|
-
|
|
101
|
+
// 4. 应用自动默认值
|
|
102
|
+
if (autoDefault) {
|
|
103
|
+
applyAutoDefault(rule)
|
|
104
|
+
}
|
|
180
105
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (typeof
|
|
184
|
-
|
|
185
|
-
commands.push(a.trim())
|
|
186
|
-
})
|
|
187
|
-
} else if (Array.isArray(opts['@command'])) {
|
|
188
|
-
commands = opts['@command'];
|
|
106
|
+
// 5. 处理别名 (Alias)
|
|
107
|
+
normalized[key] = rule
|
|
108
|
+
if (rule.alias && typeof rule.alias === 'string') {
|
|
109
|
+
normalized[rule.alias] = rule
|
|
189
110
|
}
|
|
190
|
-
delete opts['@command'];
|
|
191
111
|
}
|
|
112
|
+
|
|
113
|
+
return normalized
|
|
114
|
+
}
|
|
192
115
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
116
|
+
/**
|
|
117
|
+
* 主解析函数
|
|
118
|
+
*
|
|
119
|
+
* @param {Object} schema 参数定义
|
|
120
|
+
* @param {Object} options 配置项 { strict: boolean, autoDefault: boolean, commands: [], defaultCommand: string, argv: [] }
|
|
121
|
+
*/
|
|
122
|
+
function parseArgv(schema = {}, options = {}) {
|
|
123
|
+
// --- 1. 配置初始化 ---
|
|
124
|
+
const config = {
|
|
125
|
+
strict: true, // 默认为严格模式,报错即停
|
|
126
|
+
autoDefault: true, // 默认自动生成 default 值
|
|
127
|
+
commands: options.commands && Array.isArray(options.commands) ? options.commands : [],
|
|
128
|
+
defaultCommand: options.defaultCommand || '', // 默认子命令
|
|
129
|
+
argv: process.argv, // 允许传入自定义 argv 用于测试
|
|
130
|
+
...options
|
|
197
131
|
}
|
|
198
132
|
|
|
133
|
+
if (config.commands && config.commands.length > 0 && !options.defaultCommand) {
|
|
134
|
+
config.defaultCommand = config.commands[0]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// --- 2. 预处理 ---
|
|
138
|
+
const rules = normalizeSchema(schema, config.autoDefault)
|
|
139
|
+
const result = {}
|
|
140
|
+
const errors = []
|
|
141
|
+
const unknownList = []
|
|
199
142
|
let userCommand = null
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
message: '请使用子命令:' + commands.join('|'),
|
|
208
|
-
args: obj
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
} else if (commands.indexOf(process.argv[2]) < 0) {
|
|
212
|
-
if (defaultCommand) userCommand = defaultCommand;
|
|
213
|
-
else {
|
|
214
|
-
return {
|
|
215
|
-
ok: false,
|
|
216
|
-
message: '不支持的子命令',
|
|
217
|
-
args: obj
|
|
218
|
-
}
|
|
219
|
-
}
|
|
143
|
+
|
|
144
|
+
// 初始化结果对象中的默认值
|
|
145
|
+
for (let key in rules) {
|
|
146
|
+
const rule = rules[key]
|
|
147
|
+
const name = rule.name || key
|
|
148
|
+
if (rule.type === 'bool' || rule.type === 'boolean') {
|
|
149
|
+
if (result[name] === undefined) result[name] = rule.default !== undefined ? rule.default : false
|
|
220
150
|
} else {
|
|
221
|
-
|
|
222
|
-
userCommand = process.argv[2]
|
|
151
|
+
if (result[name] === undefined && rule.default !== undefined) result[name] = rule.default
|
|
223
152
|
}
|
|
224
153
|
}
|
|
225
154
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
type: 'boolean',
|
|
233
|
-
default: false
|
|
234
|
-
}
|
|
235
|
-
}
|
|
155
|
+
// --- 3. 解析子命令 ---
|
|
156
|
+
let index = 2
|
|
157
|
+
if (config.argv[0].endsWith('node')) {
|
|
158
|
+
// 标准 node xxx.js 格式,从下标2开始
|
|
159
|
+
// 如果是 compiled binary 或者是 electron 等环境,可能需要自行调整 config.argv
|
|
160
|
+
}
|
|
236
161
|
|
|
237
|
-
|
|
162
|
+
if (config.commands.length > 0) {
|
|
163
|
+
const inputCmd = config.argv[index]
|
|
238
164
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
165
|
+
if (!inputCmd || inputCmd.startsWith('-')) {
|
|
166
|
+
// 没有提供命令,或者直接开始了参数
|
|
167
|
+
if (config.defaultCommand) {
|
|
168
|
+
userCommand = config.defaultCommand
|
|
169
|
+
} else {
|
|
170
|
+
const msg = `缺少子命令,可用命令: ${config.commands.join('|')}`
|
|
171
|
+
if (config.strict) return { ok: false, message: msg, args: result }
|
|
172
|
+
errors.push(msg)
|
|
243
173
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
174
|
+
} else if (config.commands.includes(inputCmd)) {
|
|
175
|
+
userCommand = inputCmd
|
|
176
|
+
index++ // 消耗掉命令参数
|
|
177
|
+
} else {
|
|
178
|
+
// 提供了命令但不在列表中
|
|
179
|
+
if (config.defaultCommand) {
|
|
180
|
+
userCommand = config.defaultCommand
|
|
181
|
+
// 注意:这里不 index++,因为当前这个 unknown command 可能是个普通参数?
|
|
182
|
+
// 根据惯例,如果命令不对,通常视为错误,或者回退到 defaultCommand 但把当前词作为参数解析
|
|
251
183
|
} else {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
tmp_val = typeof opts[k].default
|
|
256
|
-
if (tmp_val === 'number' || tmp_val === 'boolean' || tmp_val === 'string') {
|
|
257
|
-
opts[k].type = tmp_val
|
|
258
|
-
} else {
|
|
259
|
-
opts[k].type = 'string'
|
|
260
|
-
}
|
|
261
|
-
} else {
|
|
262
|
-
opts[k].type = 'bool'
|
|
263
|
-
}
|
|
184
|
+
const msg = `不支持的子命令: ${inputCmd}`
|
|
185
|
+
if (config.strict) return { ok: false, message: msg, args: result }
|
|
186
|
+
errors.push(msg)
|
|
264
187
|
}
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
autoDefault && opts[k].default === undefined && setAutoDefault(opts, k)
|
|
269
|
-
|
|
270
|
-
if (opts[k].type === 'bool' || opts[k].type === 'boolean') {
|
|
271
|
-
obj[ opts[k].name || k ] = false
|
|
272
|
-
} else if (opts[k].default !== undefined) {
|
|
273
|
-
obj[ opts[k].name || k ] = opts[k].default
|
|
274
188
|
}
|
|
275
|
-
|
|
276
189
|
}
|
|
277
190
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
}
|
|
191
|
+
// --- 4. 遍历参数 ---
|
|
192
|
+
// 计算位置参数的偏移量 (如果有子命令,$1 应该是子命令后的第一个参数)
|
|
193
|
+
const posOffset = index - 1
|
|
283
194
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
let aList = []
|
|
298
|
-
|
|
299
|
-
while (i < process.argv.length) {
|
|
300
|
-
|
|
301
|
-
//先检测是否存在对位置的引用
|
|
302
|
-
pos_key = '$' + `${i-offset}`
|
|
303
|
-
if (opts[pos_key]) {
|
|
304
|
-
r = checkAndSet(pos_key, opts[pos_key], obj, process.argv[i])
|
|
305
|
-
if (!r.ok) {
|
|
306
|
-
r.args = obj
|
|
307
|
-
return r
|
|
195
|
+
while (index < config.argv.length) {
|
|
196
|
+
let rawArg = config.argv[index]
|
|
197
|
+
|
|
198
|
+
// 4.1 处理位置参数引用 ($1, $2...)
|
|
199
|
+
const posKey = `$${index - posOffset}`
|
|
200
|
+
if (rules[posKey]) {
|
|
201
|
+
const rule = rules[posKey]
|
|
202
|
+
const name = rule.name || posKey
|
|
203
|
+
try {
|
|
204
|
+
result[name] = validateAndConvert(name, rule, rawArg)
|
|
205
|
+
} catch (e) {
|
|
206
|
+
if (config.strict) return { ok: false, message: e.message, args: result }
|
|
207
|
+
errors.push(e.message)
|
|
308
208
|
}
|
|
309
|
-
|
|
209
|
+
index++
|
|
310
210
|
continue
|
|
311
211
|
}
|
|
312
212
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
213
|
+
// 4.2 解析 Key-Value (处理 --port=8080 这种情况)
|
|
214
|
+
let nextArg = (index + 1 < config.argv.length) ? config.argv[index + 1] : null
|
|
215
|
+
let key = rawArg
|
|
216
|
+
let valStr = null
|
|
217
|
+
let consumeNext = false // 是否消耗了下一个参数
|
|
218
|
+
|
|
219
|
+
const equalIndex = rawArg.indexOf('=')
|
|
220
|
+
if (equalIndex > 0) {
|
|
221
|
+
key = rawArg.substring(0, equalIndex + 1) // 保留 = 号以匹配定义
|
|
222
|
+
if (!rules[key]) {
|
|
223
|
+
key = rawArg.substring(0, equalIndex) // 尝试不带 = 匹配
|
|
224
|
+
}
|
|
225
|
+
valStr = rawArg.substring(equalIndex + 1)
|
|
226
|
+
}
|
|
316
227
|
|
|
317
|
-
|
|
228
|
+
// 4.3 匹配定义
|
|
229
|
+
if (rules[key]) {
|
|
230
|
+
const rule = rules[key]
|
|
231
|
+
const name = rule.name || key
|
|
318
232
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
233
|
+
// 布尔类型不需要下一个参数值 (除非显式赋值,暂不支持 --bool=false 写法,按原逻辑处理)
|
|
234
|
+
if (rule.type === 'bool' || rule.type === 'boolean') {
|
|
235
|
+
result[name] = true
|
|
236
|
+
} else {
|
|
237
|
+
// 取值
|
|
238
|
+
let valToProcess = (valStr !== null) ? valStr : nextArg
|
|
239
|
+
|
|
240
|
+
// 如果是从 nextArg 取值的,标记需要跳过下一个循环
|
|
241
|
+
if (valStr === null) {
|
|
242
|
+
if (valToProcess === null) {
|
|
243
|
+
// 已经是最后一个了,却需要参数
|
|
244
|
+
const msg = `${key} 必须携带参数`
|
|
245
|
+
if (config.strict) return { ok: false, message: msg, args: result }
|
|
246
|
+
errors.push(msg)
|
|
247
|
+
}
|
|
248
|
+
consumeNext = true
|
|
249
|
+
}
|
|
323
250
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
251
|
+
if (valToProcess !== null) {
|
|
252
|
+
try {
|
|
253
|
+
result[name] = validateAndConvert(name, rule, valToProcess)
|
|
254
|
+
} catch (e) {
|
|
255
|
+
if (config.strict) return { ok: false, message: e.message, args: result }
|
|
256
|
+
errors.push(e.message)
|
|
257
|
+
// 出错时,非 strict 模式下保留默认值
|
|
258
|
+
}
|
|
259
|
+
}
|
|
329
260
|
}
|
|
330
261
|
|
|
331
|
-
if (
|
|
332
|
-
|
|
333
|
-
|
|
262
|
+
if (consumeNext) index++
|
|
263
|
+
} else {
|
|
264
|
+
// 1. 处理转义字符 (例如输入 \-name,实际意图是文件名 -name)
|
|
265
|
+
if (rawArg.startsWith('\\')) {
|
|
266
|
+
// 去掉开头的反斜杠,将其余部分作为普通参数存入 list
|
|
267
|
+
unknownList.push(rawArg.substring(1))
|
|
334
268
|
}
|
|
335
269
|
|
|
336
|
-
|
|
337
|
-
if (
|
|
338
|
-
|
|
270
|
+
// 2. 处理普通参数 (文件名、纯字符串等,不以 - 开头)
|
|
271
|
+
else if (!rawArg.startsWith('-')) {
|
|
272
|
+
unknownList.push(rawArg)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// 3. 剩下的即为以 - 开头但未定义的未知 Flag
|
|
276
|
+
else {
|
|
277
|
+
//unknownList.push(rawArg)
|
|
278
|
+
// 这一块可以根据你的需求决定:
|
|
279
|
+
// 选项 A: 忽略 (保持当前逻辑)
|
|
280
|
+
// 选项 B: 报错 (如果 config.strict 为 true)
|
|
281
|
+
// 选项 C: 也放入 list (通常不建议,因为可能是用户输错的参数)
|
|
339
282
|
}
|
|
340
283
|
}
|
|
341
284
|
|
|
342
|
-
|
|
285
|
+
index++
|
|
343
286
|
}
|
|
344
287
|
|
|
345
288
|
return {
|
|
346
|
-
ok:
|
|
347
|
-
message: '',
|
|
348
|
-
|
|
349
|
-
|
|
289
|
+
ok: errors.length === 0,
|
|
290
|
+
message: errors.length > 0 ? errors[0] : '', // 向下兼容,返回第一个错误
|
|
291
|
+
errors: errors, // 新增:返回所有错误
|
|
292
|
+
args: result,
|
|
293
|
+
list: unknownList,
|
|
350
294
|
command: userCommand
|
|
351
295
|
}
|
|
352
296
|
}
|
|
353
297
|
|
|
354
|
-
module.exports = parseArgv
|
|
298
|
+
module.exports = parseArgv
|