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 |
|
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 |
|
206 |
|
207 | module.exports.rebase = rebase;
|
208 | function 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 | }
|