1 | module.exports = Argv;
|
2 | var path = require('path');
|
3 |
|
4 |
|
5 | if (process.argv.length < 2
|
6 | || !process.argv[0].match(/(?:^|\/)node(?:-?\d+(?:\.\d+)*)?$/)) {
|
7 | process.argv = ['coffee', ''].concat(process.argv);
|
8 | }
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | var inst = Argv(process.argv.slice(2));
|
18 | Object.keys(inst).forEach(function (key) {
|
19 | Argv[key] = typeof inst[key] == 'function'
|
20 | ? inst[key].bind(inst)
|
21 | : inst[key];
|
22 | });
|
23 |
|
24 | function 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 |
|
221 |
|
222 | module.exports.rebase = rebase;
|
223 | function 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 | }
|