UNPKG

6.04 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
48 if (key in self.argv) {
49 if (!Array.isArray(self.argv[key])) {
50 self.argv[key] = [ self.argv[key] ];
51 }
52 self.argv[key].push(value);
53 }
54 else {
55 self.argv[key] = value;
56 }
57 }
58
59 var flags = { bools : {} };
60
61 self.boolean = function (bools) {
62 if (!Array.isArray(bools)) {
63 bools = [].slice.call(arguments);
64 }
65
66 bools.forEach(function (name) {
67 flags.bools[name] = true;
68 });
69
70 rescan();
71
72 bools.forEach(function (name) {
73 if (!self.argv[name]) {
74 self.argv[name] = false;
75 }
76 });
77
78 return self;
79 };
80
81 function rescan () {
82 self.argv = { _ : [], $0 : self.$0 };
83
84 for (var i = 0; i < args.length; i++) {
85 var arg = args[i];
86
87 if (arg == '--') {
88 self.argv._.push.apply(self.argv._, args.slice(i + 1));
89 break;
90 }
91 else if (arg.match(/^--.+=/)) {
92 var m = arg.match(/^--([^=]+)=(.*)/);
93 set(m[1], m[2]);
94 }
95 else if (arg.match(/^--no-.+/)) {
96 var key = arg.match(/^--no-(.+)/)[1];
97 set(key, false);
98 }
99 else if (arg.match(/^--.+/)) {
100 var key = arg.match(/^--(.+)/)[1];
101 var next = args[i + 1];
102 if (next !== undefined && !next.match(/^-/)
103 && !flags.bools[key]) {
104 set(key, next);
105 i++;
106 }
107 else {
108 set(key, true);
109 }
110 }
111 else if (arg.match(/^-[^-]+/)) {
112 arg.slice(1,-1).split('').forEach(function (letter) {
113 set(letter, true);
114 });
115
116 var key = arg.slice(-1)[0];
117
118 if (args[i+1] && !args[i+1].match(/^-/)
119 && !flags.bools[key]) {
120 set(key, args[i+1]);
121 i++;
122 }
123 else {
124 set(key, true);
125 }
126 }
127 else {
128 var n = Number(arg);
129 self.argv._.push(isNaN(n) ? arg : n);
130 }
131 }
132 }
133 rescan();
134
135 var usage;
136 self.usage = function (msg) {
137 usage = msg;
138 return self;
139 };
140
141 function fail (msg) {
142 if (usage) console.error(usage.replace(/\$0/g, self.$0))
143 console.error(msg);
144 process.exit();
145 }
146
147 self.check = function (f) {
148 try {
149 if (f(self.argv) === false) fail(
150 'Argument check failed: ' + f.toString()
151 );
152 }
153 catch (err) { fail(err) }
154
155 return self;
156 };
157
158 self.demand = function (keys, cb) {
159 if (typeof keys == 'number') {
160 return self.demandCount(keys, cb);
161 }
162
163 var missing = [];
164 keys.forEach(function (key) {
165 if (!(key in self.argv)) missing.push(key);
166 });
167
168 if (missing.length > 0) {
169 if (cb) cb(missing);
170 else fail('Missing arguments: ' + missing.join(' '));
171 }
172 return self;
173 };
174
175 self.demandCount = function (count, cb) {
176 if (self.argv._.length < count) {
177 if (cb) cb(self.argv._.length);
178 else fail('Not enough arguments, expected '
179 + count + ', but only found ' + self.argv._.length);
180 }
181 return self;
182 };
183
184 self.default = function (key, value) {
185 if (typeof key === 'object') {
186 Object.keys(key).forEach(function (k) {
187 self.default(k, key[k]);
188 });
189 }
190 else {
191 if (self.argv[key] === undefined) {
192 self.argv[key] = value;
193 }
194 }
195 return self;
196 };
197
198 self.parse = function (args) {
199 return Argv(args).argv;
200 };
201
202 return self;
203};
204
205// rebase an absolute path to a relative one with respect to a base directory
206// exported for tests
207module.exports.rebase = rebase;
208function rebase (base, dir) {
209 var ds = path.normalize(dir).split('/').slice(1);
210 var bs = path.normalize(base).split('/').slice(1);
211
212 for (var i = 0; ds[i] && ds[i] == bs[i]; i++);
213 ds.splice(0, i); bs.splice(0, i);
214
215 var p = path.normalize(
216 bs.map(function () { return '..' }).concat(ds).join('/')
217 ).replace(/\/$/,'').replace(/^$/, '.');
218 return p.match(/^[.\/]/) ? p : './' + p;
219}