1 | module.exports = function (args, opts) {
|
2 | if (!opts) opts = {};
|
3 |
|
4 | var flags = { bools : {}, strings : {} };
|
5 |
|
6 | [].concat(opts['boolean']).filter(Boolean).forEach(function (key) {
|
7 | flags.bools[key] = true;
|
8 | });
|
9 |
|
10 | [].concat(opts.string).filter(Boolean).forEach(function (key) {
|
11 | flags.strings[key] = true;
|
12 | });
|
13 |
|
14 | var aliases = {};
|
15 | Object.keys(opts.alias || {}).forEach(function (key) {
|
16 | aliases[key] = [].concat(opts.alias[key]);
|
17 | aliases[key].forEach(function (x) {
|
18 | aliases[x] = [key].concat(aliases[key].filter(function (y) {
|
19 | return x !== y;
|
20 | }));
|
21 | });
|
22 | });
|
23 |
|
24 | var defaults = opts['default'] || {};
|
25 |
|
26 | var argv = { _ : [] };
|
27 | Object.keys(flags.bools).forEach(function (key) {
|
28 | setArg(key, defaults[key] === undefined ? false : defaults[key]);
|
29 | });
|
30 |
|
31 | var notFlags = [];
|
32 |
|
33 | if (args.indexOf('--') !== -1) {
|
34 | notFlags = args.slice(args.indexOf('--')+1);
|
35 | args = args.slice(0, args.indexOf('--'));
|
36 | }
|
37 |
|
38 | function setArg (key, val) {
|
39 | var value = !flags.strings[key] && isNumber(val)
|
40 | ? Number(val) : val
|
41 | ;
|
42 | setKey(argv, key.split('.'), value);
|
43 |
|
44 | (aliases[key] || []).forEach(function (x) {
|
45 | setKey(argv, x.split('.'), value);
|
46 | });
|
47 | }
|
48 |
|
49 | for (var i = 0; i < args.length; i++) {
|
50 | var arg = args[i];
|
51 |
|
52 | if (/^--.+=/.test(arg)) {
|
53 |
|
54 |
|
55 |
|
56 | var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
|
57 | setArg(m[1], m[2]);
|
58 | }
|
59 | else if (/^--no-.+/.test(arg)) {
|
60 | var key = arg.match(/^--no-(.+)/)[1];
|
61 | setArg(key, false);
|
62 | }
|
63 | else if (/^--.+/.test(arg)) {
|
64 | var key = arg.match(/^--(.+)/)[1];
|
65 | var next = args[i + 1];
|
66 | if (next !== undefined && !/^-/.test(next)
|
67 | && !flags.bools[key]
|
68 | && (aliases[key] ? !flags.bools[aliases[key]] : true)) {
|
69 | setArg(key, next);
|
70 | i++;
|
71 | }
|
72 | else if (/^(true|false)$/.test(next)) {
|
73 | setArg(key, next === 'true');
|
74 | i++;
|
75 | }
|
76 | else {
|
77 | setArg(key, flags.strings[key] ? '' : true);
|
78 | }
|
79 | }
|
80 | else if (/^-[^-]+/.test(arg)) {
|
81 | var letters = arg.slice(1,-1).split('');
|
82 |
|
83 | var broken = false;
|
84 | for (var j = 0; j < letters.length; j++) {
|
85 | var next = arg.slice(j+2);
|
86 |
|
87 | if (next === '-') {
|
88 | setArg(letters[j], next)
|
89 | continue;
|
90 | }
|
91 |
|
92 | if (/[A-Za-z]/.test(letters[j])
|
93 | && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
|
94 | setArg(letters[j], next);
|
95 | broken = true;
|
96 | break;
|
97 | }
|
98 |
|
99 | if (letters[j+1] && letters[j+1].match(/\W/)) {
|
100 | setArg(letters[j], arg.slice(j+2));
|
101 | broken = true;
|
102 | break;
|
103 | }
|
104 | else {
|
105 | setArg(letters[j], flags.strings[letters[j]] ? '' : true);
|
106 | }
|
107 | }
|
108 |
|
109 | var key = arg.slice(-1)[0];
|
110 | if (!broken && key !== '-') {
|
111 | if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])
|
112 | && !flags.bools[key]
|
113 | && (aliases[key] ? !flags.bools[aliases[key]] : true)) {
|
114 | setArg(key, args[i+1]);
|
115 | i++;
|
116 | }
|
117 | else if (args[i+1] && /true|false/.test(args[i+1])) {
|
118 | setArg(key, args[i+1] === 'true');
|
119 | i++;
|
120 | }
|
121 | else {
|
122 | setArg(key, flags.strings[key] ? '' : true);
|
123 | }
|
124 | }
|
125 | }
|
126 | else {
|
127 | argv._.push(
|
128 | flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
|
129 | );
|
130 | }
|
131 | }
|
132 |
|
133 | Object.keys(defaults).forEach(function (key) {
|
134 | if (!hasKey(argv, key.split('.'))) {
|
135 | setKey(argv, key.split('.'), defaults[key]);
|
136 |
|
137 | (aliases[key] || []).forEach(function (x) {
|
138 | setKey(argv, x.split('.'), defaults[key]);
|
139 | });
|
140 | }
|
141 | });
|
142 |
|
143 | notFlags.forEach(function(key) {
|
144 | argv._.push(key);
|
145 | });
|
146 |
|
147 | return argv;
|
148 | };
|
149 |
|
150 | function hasKey (obj, keys) {
|
151 | var o = obj;
|
152 | keys.slice(0,-1).forEach(function (key) {
|
153 | o = (o[key] || {});
|
154 | });
|
155 |
|
156 | var key = keys[keys.length - 1];
|
157 | return key in o;
|
158 | }
|
159 |
|
160 | function setKey (obj, keys, value) {
|
161 | var o = obj;
|
162 | keys.slice(0,-1).forEach(function (key) {
|
163 | if (o[key] === undefined) o[key] = {};
|
164 | o = o[key];
|
165 | });
|
166 |
|
167 | var key = keys[keys.length - 1];
|
168 | if (o[key] === undefined || typeof o[key] === 'boolean') {
|
169 | o[key] = value;
|
170 | }
|
171 | else if (Array.isArray(o[key])) {
|
172 | o[key].push(value);
|
173 | }
|
174 | else {
|
175 | o[key] = [ o[key], value ];
|
176 | }
|
177 | }
|
178 |
|
179 | function isNumber (x) {
|
180 | if (typeof x === 'number') return true;
|
181 | if (/^0x[0-9a-f]+$/i.test(x)) return true;
|
182 | return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
|
183 | }
|
184 |
|
185 | function longest (xs) {
|
186 | return Math.max.apply(null, xs.map(function (x) { return x.length }));
|
187 | }
|