Skip to content

Commit 04bf311

Browse files
committed
v3.2.1-beta :: fix null bug of god()
1 parent 03f7606 commit 04bf311

File tree

3 files changed

+85
-26
lines changed

3 files changed

+85
-26
lines changed

README.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ parse(argv, i, req, res, err)
112112
```
113113

114114
- **argv**: Array of command line arguments (usually `process.argv`)
115-
- **i**: Starting index for parsing (usually 2)
115+
- **i**: Starting index for parsing (usually `2`)
116116
- **req**: Configuration object defining your command structure
117117
- **res**: Object that will be populated with parsed results
118118
- **err**: Error handler function, receives `{msg, i, opt, key, val}` and returns boolean
@@ -142,10 +142,12 @@ The function returns either:
142142
}
143143
```
144144

145-
Option definitions can be:
146-
- A string: `'--option'`
147-
- An array of strings: `['--option', '-o']`
148-
- Include `null` in an array to use the variable name as an option: `[null, '-o']`
145+
Option definitions can be:
146+
- A string: `'--option'`
147+
- An array: `['--option', '-o']`
148+
- `null` (refers to the variable name)
149+
150+
Note: `undefined` is not supported. While it may work like `null` in `OptKit`, this is unintended. At `ExitKit`, `undefined` behaves differently from `null`.
149151

150152
## ⚡ Powerful Features
151153

@@ -178,8 +180,7 @@ files: {
178180
set: ['-i', '--input'] // -i 1.txt --input 2.txt -i3.txt --input=4.txt
179181
}
180182

181-
// '--' automatically collects all the anonymous arguments.
182-
// If you write it out, it collects all the rest arguments.
183+
// Special option '--' collects all the anonymous arguments.
183184
allFiles: {
184185
def: [],
185186
set: ['--', '-i'] // -i 1.txt 2.txt -- --this-will-be-collected-too
@@ -211,12 +212,11 @@ files: {
211212
}
212213
```
213214

214-
Note: Inverting a boolean value is not supported now.
215+
Note: Inverting a boolean value is not supported.
215216

216217
### Combined Short Options
217218

218219
```javascript
219-
// -abc is equivalent to -a -b -c
220220
const res = {};
221221
parse(['node', 'app.js', '-abc'], 2, {
222222
a: { def: false, set: '-a' },
@@ -225,6 +225,16 @@ parse(['node', 'app.js', '-abc'], 2, {
225225
}, res, errorHandler);
226226
// res = { a: true, b: true, c: true }
227227
```
228+
```javascript
229+
const res = {};
230+
parse(['node', 'app.js', '-abcd'], 2, {
231+
a: { def: false, set: '-a' },
232+
b: { def: [], set: '-b' },
233+
c: { def: false, set: '-c' },
234+
d: { def: false, set: '-d' }
235+
}, res, errorHandler);
236+
// { a: true, b: [ 'cd' ], c: false, d: false }
237+
```
228238

229239
## 🔄 Subcommand Handling
230240

index.js

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ const isA = Array.isArray;
2121
* @returns {boolean} Whether the parsing should continue (false) or quit (true)
2222
* @typedef {Record<OptStr, VarKey>} OptKeyMap internal type
2323
*/
24-
/** @param {OptKit} ok */
25-
const god = ok => typeof ok === 'string' ? [ok] : isA(ok) ? ok : [];
24+
/** Get OD! @param {OptKit} ok */
25+
const god = ok => ok === undefined ? [] : isA(ok) ? ok : [ok];
2626
/**
2727
* Command line argument parser function
2828
* @param {string[]} argv Command line arguments array, e.g. `process.argv`
@@ -51,8 +51,9 @@ export default function parse(argv, i, req, res, err) {
5151
res[key] = !def;
5252
return false;
5353
} return true;
54-
}, exit = () => ({ i: i + 1, key, opt }),
55-
ask = (msg, val) => err({msg, i, opt, key, val});
54+
}, k = o => o == null ? key : o, // split undefined? really?
55+
ask = (msg, val) => err({msg, i, opt, key, val}),
56+
exit = () => ({ i: i + 1, key, opt });
5657
// prepare
5758
/** @type {OptKeyMap} */
5859
const set_ = {}, rst_ = {}, exit_ = {};
@@ -63,17 +64,17 @@ export default function parse(argv, i, req, res, err) {
6364
X: { let xk; // stricter than god()
6465
switch (typeof vk) {
6566
case 'object':
66-
if (vk === null) xk = [key];
67+
if (vk === null) xk = [vk];
6768
else if (isA(vk)) xk = vk;
6869
else break X; break;
6970
case 'string': xk = [vk]; break;
7071
default: continue; }
71-
for (const o of xk) if (o!=='--') exit_[o||key] = key; else _key = key, _exit = true;
72+
for (const o of xk) if (o!=='--') exit_[k(o)] = key; else _key = key, _exit = true;
7273
continue; }
7374
const def = vk.def;
7475
res[key] = isA(def) ? def.slice() : def;
75-
for (const o of god(vk.set)) if (o!=='--') set_[o||key] = key; else _key = key, _exit = false;
76-
for (const o of god(vk.rst)) if (o!=='--') rst_[o||key] = key;
76+
for (const o of god(vk.set)) if (o!=='--') set_[k(o)] = key; else _key = key, _exit = false;
77+
for (const o of god(vk.rst)) if (o!=='--') rst_[k(o)] = key;
7778
}
7879
// process
7980
let ext = false;
@@ -114,8 +115,8 @@ export default function parse(argv, i, req, res, err) {
114115
}
115116
// --opt
116117
if (s.length > 2) {
117-
const k = s.indexOf('=');
118-
if (k < 0) {
118+
const J = s.indexOf('=');
119+
if (J < 0) {
119120
// --opt ...
120121
if (key = set_[opt = s]) ext = noB();
121122
else if (key = rst_[opt]) rst();
@@ -124,8 +125,8 @@ export default function parse(argv, i, req, res, err) {
124125
continue;
125126
}
126127
// --opt=val
127-
let t; opt = s.slice(0, k);
128-
const v = s.slice(k + 1);
128+
let t; opt = s.slice(0, J);
129+
const v = s.slice(J + 1);
129130
if (key = set_[opt])
130131
switch (t = typeof res[key]) {
131132
case 'boolean': break;

test.js

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,17 +216,65 @@ runTest('Default naming', () => {
216216
assert.strictEqual(ret1.key, 'help');
217217
assert.strictEqual(ret1.opt, 'help');
218218

219-
// Test using null in array for direct naming
219+
// Test using null for direct naming
220220
const req2 = {
221221
verbose: {
222222
def: false,
223-
set: [null] // null in the array means use 'verbose' as option name
223+
set: null // null means use 'verbose' as option name
224224
}
225-
};
226-
227-
const res2 = {};
225+
}, res2 = {};
228226
parse(['node', 'script.js', 'verbose'], 2, req2, res2, errorHandler);
229227
assert.strictEqual(res2.verbose, true);
228+
229+
// Test using null in array for direct naming
230+
const req3 = {
231+
verbose: {
232+
def: false,
233+
set: [null] // null means use 'verbose' as option name
234+
}
235+
}, res3 = {};
236+
parse(['node', 'script.js', 'verbose'], 2, req3, res3, errorHandler);
237+
assert.strictEqual(res3.verbose, true);
238+
239+
const req3a = {
240+
verbose: {
241+
def: false,
242+
set: [null, '-v']
243+
}
244+
}, res3a = {};
245+
parse(['node', 'script.js', '-v'], 2, req3a, res3a, errorHandler);
246+
assert.strictEqual(res3a.verbose, true);
247+
248+
// Test using '' for direct naming
249+
const req4 = {
250+
verbose: {
251+
def: false,
252+
set: '' // '' doesn't mean use 'verbose' as option name
253+
}
254+
}, res4 = {};
255+
parse(['node', 'script.js', ''], 2, req4, res4, errorHandler);
256+
assert.strictEqual(res4.verbose, true);
257+
258+
// Test using '' in array for direct naming
259+
const req5 = {
260+
verbose: {
261+
def: false,
262+
set: [''] // '' doesn't mean use 'verbose' as option name
263+
}
264+
}, res5 = {};
265+
parse(['node', 'script.js', ''], 2, req5, res5, errorHandler);
266+
assert.strictEqual(res5.verbose, true);
267+
268+
const req5a = {
269+
verbose: {
270+
def: false,
271+
set: ['', '-v']
272+
}
273+
}, res5a = {};
274+
parse(['node', 'script.js', '-v'], 2, req5a, res5a, errorHandler);
275+
assert.strictEqual(res5a.verbose, true);
276+
277+
230278
});
231279

232280
// TEST 9: Error handling

0 commit comments

Comments
 (0)