UNPKG

6.41 kBJavaScriptView Raw
1module.exports = Argv;
2var path = require('path');
3
4// Hack to work when loaded with CoffeeScript
5if (process.argv.length < 2
6|| !process.argv[0].match(/(?:^|\/)node(?:-?\d+(?:\.\d+)*)?$/)) {
7 process.argv = ['coffee', ''].concat(process.argv);
8}
9
10/* Hack an instance of Argv with process.argv into Argv
11 so people an do
12 require('optimist')(['--beeble=1','-z','zizzle']).argv
13 to parse a list of args and
14 require('optimist').argv
15 to get a parsed version of process.argv.
16*/
17var inst = Argv(process.argv.slice(2));
18Object.keys(inst).forEach(function (key) {
19 Argv[key] = typeof inst[key] == 'function'
20 ? inst[key].bind(inst)
21 : inst[key];
22});
23
24function Argv (args, cwd) {
25 var self = {};
26 if (!cwd) cwd = process.cwd();
27
28 self.$0 = process.argv
29 .slice(0,2)
30 .map(function (x) {
31 var b = rebase(cwd, x);
32 return x.match(/^\//) && b.length < x.length
33 ? b : x
34 })
35 .join(' ')
36 ;
37
38 if (process.argv[1] == process.env._) {
39 self.$0 = process.env._.replace(
40 path.dirname(process.execPath) + '/', ''
41 );
42 }
43
44 function set (key, val) {
45 var num = Number(val);
46 var value = typeof val !== 'string' || isNaN(num) ? val : num;
47 if (flags.strings[key]) value = val;
48
49 if (key in self.argv) {
50 if (!Array.isArray(self.argv[key])) {
51 self.argv[key] = [ self.argv[key] ];
52 }
53 self.argv[key].push(value);
54 }
55 else {
56 self.argv[key] = value;
57 }
58 }
59
60 var flags = { bools : {}, strings : {} };
61
62 self.boolean = function (bools) {
63 if (!Array.isArray(bools)) {
64 bools = [].slice.call(arguments);
65 }
66
67 bools.forEach(function (name) {
68 flags.bools[name] = true;
69 });
70
71 rescan();
72
73 bools.forEach(function (name) {
74 if (!self.argv[name]) {
75 self.argv[name] = false;
76 }
77 });
78
79 return self;
80 };
81
82 self.string = function (strings) {
83 if (!Array.isArray(strings)) {
84 strings = [].slice.call(arguments);
85 }
86
87 strings.forEach(function (name) {
88 flags.strings[name] = true;
89 });
90
91 rescan();
92
93 return self;
94 };
95
96 function rescan () {
97 self.argv = { _ : [], $0 : self.$0 };
98
99 for (var i = 0; i < args.length; i++) {
100 var arg = args[i];
101
102 if (arg == '--') {
103 self.argv._.push.apply(self.argv._, args.slice(i + 1));
104 break;
105 }
106 else if (arg.match(/^--.+=/)) {
107 var m = arg.match(/^--([^=]+)=(.*)/);
108 set(m[1], m[2]);
109 }
110 else if (arg.match(/^--no-.+/)) {
111 var key = arg.match(/^--no-(.+)/)[1];
112 set(key, false);
113 }
114 else if (arg.match(/^--.+/)) {
115 var key = arg.match(/^--(.+)/)[1];
116 var next = args[i + 1];
117 if (next !== undefined && !next.match(/^-/)
118 && !flags.bools[key]) {
119 set(key, next);
120 i++;
121 }
122 else {
123 set(key, true);
124 }
125 }
126 else if (arg.match(/^-[^-]+/)) {
127 arg.slice(1,-1).split('').forEach(function (letter) {
128 set(letter, true);
129 });
130
131 var key = arg.slice(-1)[0];
132
133 if (args[i+1] && !args[i+1].match(/^-/)
134 && !flags.bools[key]) {
135 set(key, args[i+1]);
136 i++;
137 }
138 else {
139 set(key, true);
140 }
141 }
142 else {
143 var n = Number(arg);
144 self.argv._.push(isNaN(n) ? arg : n);
145 }
146 }
147 }
148 rescan();
149
150 var usage;
151 self.usage = function (msg) {
152 usage = msg;
153 return self;
154 };
155
156 function fail (msg) {
157 if (usage) console.error(usage.replace(/\$0/g, self.$0))
158 console.error(msg);
159 process.exit();
160 }
161
162 self.check = function (f) {
163 try {
164 if (f(self.argv) === false) fail(
165 'Argument check failed: ' + f.toString()
166 );
167 }
168 catch (err) { fail(err) }
169
170 return self;
171 };
172
173 self.demand = function (keys, cb) {
174 if (typeof keys == 'number') {
175 return self.demandCount(keys, cb);
176 }
177
178 var missing = [];
179 keys.forEach(function (key) {
180 if (!(key in self.argv)) missing.push(key);
181 });
182
183 if (missing.length > 0) {
184 if (cb) cb(missing);
185 else fail('Missing arguments: ' + missing.join(' '));
186 }
187 return self;
188 };
189
190 self.demandCount = function (count, cb) {
191 if (self.argv._.length < count) {
192 if (cb) cb(self.argv._.length);
193 else fail('Not enough arguments, expected '
194 + count + ', but only found ' + self.argv._.length);
195 }
196 return self;
197 };
198
199 self.default = function (key, value) {
200 if (typeof key === 'object') {
201 Object.keys(key).forEach(function (k) {
202 self.default(k, key[k]);
203 });
204 }
205 else {
206 if (self.argv[key] === undefined) {
207 self.argv[key] = value;
208 }
209 }
210 return self;
211 };
212
213 self.parse = function (args) {
214 return Argv(args).argv;
215 };
216
217 return self;
218};
219
220// rebase an absolute path to a relative one with respect to a base directory
221// exported for tests
222module.exports.rebase = rebase;
223function rebase (base, dir) {
224 var ds = path.normalize(dir).split('/').slice(1);
225 var bs = path.normalize(base).split('/').slice(1);
226
227 for (var i = 0; ds[i] && ds[i] == bs[i]; i++);
228 ds.splice(0, i); bs.splice(0, i);
229
230 var p = path.normalize(
231 bs.map(function () { return '..' }).concat(ds).join('/')
232 ).replace(/\/$/,'').replace(/^$/, '.');
233 return p.match(/^[.\/]/) ? p : './' + p;
234}