Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80538 views
1
module.exports = function (args, opts) {
2
if (!opts) opts = {};
3
4
var flags = { bools : {}, strings : {}, unknownFn: null };
5
6
if (typeof opts['unknown'] === 'function') {
7
flags.unknownFn = opts['unknown'];
8
}
9
10
if (typeof opts['boolean'] === 'boolean' && opts['boolean']) {
11
flags.allBools = true;
12
} else {
13
[].concat(opts['boolean']).filter(Boolean).forEach(function (key) {
14
flags.bools[key] = true;
15
});
16
}
17
18
var aliases = {};
19
Object.keys(opts.alias || {}).forEach(function (key) {
20
aliases[key] = [].concat(opts.alias[key]);
21
aliases[key].forEach(function (x) {
22
aliases[x] = [key].concat(aliases[key].filter(function (y) {
23
return x !== y;
24
}));
25
});
26
});
27
28
[].concat(opts.string).filter(Boolean).forEach(function (key) {
29
flags.strings[key] = true;
30
if (aliases[key]) {
31
flags.strings[aliases[key]] = true;
32
}
33
});
34
35
var defaults = opts['default'] || {};
36
37
var argv = { _ : [] };
38
Object.keys(flags.bools).forEach(function (key) {
39
setArg(key, defaults[key] === undefined ? false : defaults[key]);
40
});
41
42
var notFlags = [];
43
44
if (args.indexOf('--') !== -1) {
45
notFlags = args.slice(args.indexOf('--')+1);
46
args = args.slice(0, args.indexOf('--'));
47
}
48
49
function argDefined(key, arg) {
50
return (flags.allBools && /^--[^=]+$/.test(arg)) ||
51
flags.strings[key] || flags.bools[key] || aliases[key];
52
}
53
54
function setArg (key, val, arg) {
55
if (arg && flags.unknownFn && !argDefined(key, arg)) {
56
if (flags.unknownFn(arg) === false) return;
57
}
58
59
var value = !flags.strings[key] && isNumber(val)
60
? Number(val) : val
61
;
62
setKey(argv, key.split('.'), value);
63
64
(aliases[key] || []).forEach(function (x) {
65
setKey(argv, x.split('.'), value);
66
});
67
}
68
69
function setKey (obj, keys, value) {
70
var o = obj;
71
keys.slice(0,-1).forEach(function (key) {
72
if (o[key] === undefined) o[key] = {};
73
o = o[key];
74
});
75
76
var key = keys[keys.length - 1];
77
if (o[key] === undefined || flags.bools[key] || typeof o[key] === 'boolean') {
78
o[key] = value;
79
}
80
else if (Array.isArray(o[key])) {
81
o[key].push(value);
82
}
83
else {
84
o[key] = [ o[key], value ];
85
}
86
}
87
88
for (var i = 0; i < args.length; i++) {
89
var arg = args[i];
90
91
if (/^--.+=/.test(arg)) {
92
// Using [\s\S] instead of . because js doesn't support the
93
// 'dotall' regex modifier. See:
94
// http://stackoverflow.com/a/1068308/13216
95
var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
96
setArg(m[1], m[2], arg);
97
}
98
else if (/^--no-.+/.test(arg)) {
99
var key = arg.match(/^--no-(.+)/)[1];
100
setArg(key, false, arg);
101
}
102
else if (/^--.+/.test(arg)) {
103
var key = arg.match(/^--(.+)/)[1];
104
var next = args[i + 1];
105
if (next !== undefined && !/^-/.test(next)
106
&& !flags.bools[key]
107
&& !flags.allBools
108
&& (aliases[key] ? !flags.bools[aliases[key]] : true)) {
109
setArg(key, next, arg);
110
i++;
111
}
112
else if (/^(true|false)$/.test(next)) {
113
setArg(key, next === 'true', arg);
114
i++;
115
}
116
else {
117
setArg(key, flags.strings[key] ? '' : true, arg);
118
}
119
}
120
else if (/^-[^-]+/.test(arg)) {
121
var letters = arg.slice(1,-1).split('');
122
123
var broken = false;
124
for (var j = 0; j < letters.length; j++) {
125
var next = arg.slice(j+2);
126
127
if (next === '-') {
128
setArg(letters[j], next, arg)
129
continue;
130
}
131
132
if (/[A-Za-z]/.test(letters[j])
133
&& /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
134
setArg(letters[j], next, arg);
135
broken = true;
136
break;
137
}
138
139
if (letters[j+1] && letters[j+1].match(/\W/)) {
140
setArg(letters[j], arg.slice(j+2), arg);
141
broken = true;
142
break;
143
}
144
else {
145
setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg);
146
}
147
}
148
149
var key = arg.slice(-1)[0];
150
if (!broken && key !== '-') {
151
if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])
152
&& !flags.bools[key]
153
&& (aliases[key] ? !flags.bools[aliases[key]] : true)) {
154
setArg(key, args[i+1], arg);
155
i++;
156
}
157
else if (args[i+1] && /true|false/.test(args[i+1])) {
158
setArg(key, args[i+1] === 'true', arg);
159
i++;
160
}
161
else {
162
setArg(key, flags.strings[key] ? '' : true, arg);
163
}
164
}
165
}
166
else {
167
if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
168
argv._.push(
169
flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
170
);
171
}
172
if (opts.stopEarly) {
173
argv._.push.apply(argv._, args.slice(i + 1));
174
break;
175
}
176
}
177
}
178
179
Object.keys(defaults).forEach(function (key) {
180
if (!hasKey(argv, key.split('.'))) {
181
setKey(argv, key.split('.'), defaults[key]);
182
183
(aliases[key] || []).forEach(function (x) {
184
setKey(argv, x.split('.'), defaults[key]);
185
});
186
}
187
});
188
189
if (opts['--']) {
190
argv['--'] = new Array();
191
notFlags.forEach(function(key) {
192
argv['--'].push(key);
193
});
194
}
195
else {
196
notFlags.forEach(function(key) {
197
argv._.push(key);
198
});
199
}
200
201
return argv;
202
};
203
204
function hasKey (obj, keys) {
205
var o = obj;
206
keys.slice(0,-1).forEach(function (key) {
207
o = (o[key] || {});
208
});
209
210
var key = keys[keys.length - 1];
211
return key in o;
212
}
213
214
function isNumber (x) {
215
if (typeof x === 'number') return true;
216
if (/^0x[0-9a-f]+$/i.test(x)) return true;
217
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
218
}
219
220
221