Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80635 views
1
var path = require('path');
2
var minimist = require('minimist');
3
var wordwrap = require('wordwrap');
4
5
/* Hack an instance of Argv with process.argv into Argv
6
so people can do
7
require('optimist')(['--beeble=1','-z','zizzle']).argv
8
to parse a list of args and
9
require('optimist').argv
10
to get a parsed version of process.argv.
11
*/
12
13
var inst = Argv(process.argv.slice(2));
14
Object.keys(inst).forEach(function (key) {
15
Argv[key] = typeof inst[key] == 'function'
16
? inst[key].bind(inst)
17
: inst[key];
18
});
19
20
var exports = module.exports = Argv;
21
function Argv (processArgs, cwd) {
22
var self = {};
23
if (!cwd) cwd = process.cwd();
24
25
self.$0 = process.argv
26
.slice(0,2)
27
.map(function (x) {
28
var b = rebase(cwd, x);
29
return x.match(/^\//) && b.length < x.length
30
? b : x
31
})
32
.join(' ')
33
;
34
35
if (process.env._ != undefined && process.argv[1] == process.env._) {
36
self.$0 = process.env._.replace(
37
path.dirname(process.execPath) + '/', ''
38
);
39
}
40
41
var options = {
42
boolean: [],
43
string: [],
44
alias: {},
45
default: []
46
};
47
48
self.boolean = function (bools) {
49
options.boolean.push.apply(options.boolean, [].concat(bools));
50
return self;
51
};
52
53
self.string = function (strings) {
54
options.string.push.apply(options.string, [].concat(strings));
55
return self;
56
};
57
58
self.default = function (key, value) {
59
if (typeof key === 'object') {
60
Object.keys(key).forEach(function (k) {
61
self.default(k, key[k]);
62
});
63
}
64
else {
65
options.default[key] = value;
66
}
67
return self;
68
};
69
70
self.alias = function (x, y) {
71
if (typeof x === 'object') {
72
Object.keys(x).forEach(function (key) {
73
self.alias(key, x[key]);
74
});
75
}
76
else {
77
options.alias[x] = (options.alias[x] || []).concat(y);
78
}
79
return self;
80
};
81
82
var demanded = {};
83
self.demand = function (keys) {
84
if (typeof keys == 'number') {
85
if (!demanded._) demanded._ = 0;
86
demanded._ += keys;
87
}
88
else if (Array.isArray(keys)) {
89
keys.forEach(function (key) {
90
self.demand(key);
91
});
92
}
93
else {
94
demanded[keys] = true;
95
}
96
97
return self;
98
};
99
100
var usage;
101
self.usage = function (msg, opts) {
102
if (!opts && typeof msg === 'object') {
103
opts = msg;
104
msg = null;
105
}
106
107
usage = msg;
108
109
if (opts) self.options(opts);
110
111
return self;
112
};
113
114
function fail (msg) {
115
self.showHelp();
116
if (msg) console.error(msg);
117
process.exit(1);
118
}
119
120
var checks = [];
121
self.check = function (f) {
122
checks.push(f);
123
return self;
124
};
125
126
var descriptions = {};
127
self.describe = function (key, desc) {
128
if (typeof key === 'object') {
129
Object.keys(key).forEach(function (k) {
130
self.describe(k, key[k]);
131
});
132
}
133
else {
134
descriptions[key] = desc;
135
}
136
return self;
137
};
138
139
self.parse = function (args) {
140
return parseArgs(args);
141
};
142
143
self.option = self.options = function (key, opt) {
144
if (typeof key === 'object') {
145
Object.keys(key).forEach(function (k) {
146
self.options(k, key[k]);
147
});
148
}
149
else {
150
if (opt.alias) self.alias(key, opt.alias);
151
if (opt.demand) self.demand(key);
152
if (typeof opt.default !== 'undefined') {
153
self.default(key, opt.default);
154
}
155
156
if (opt.boolean || opt.type === 'boolean') {
157
self.boolean(key);
158
}
159
if (opt.string || opt.type === 'string') {
160
self.string(key);
161
}
162
163
var desc = opt.describe || opt.description || opt.desc;
164
if (desc) {
165
self.describe(key, desc);
166
}
167
}
168
169
return self;
170
};
171
172
var wrap = null;
173
self.wrap = function (cols) {
174
wrap = cols;
175
return self;
176
};
177
178
self.showHelp = function (fn) {
179
if (!fn) fn = console.error;
180
fn(self.help());
181
};
182
183
self.help = function () {
184
var keys = Object.keys(
185
Object.keys(descriptions)
186
.concat(Object.keys(demanded))
187
.concat(Object.keys(options.default))
188
.reduce(function (acc, key) {
189
if (key !== '_') acc[key] = true;
190
return acc;
191
}, {})
192
);
193
194
var help = keys.length ? [ 'Options:' ] : [];
195
196
if (usage) {
197
help.unshift(usage.replace(/\$0/g, self.$0), '');
198
}
199
200
var switches = keys.reduce(function (acc, key) {
201
acc[key] = [ key ].concat(options.alias[key] || [])
202
.map(function (sw) {
203
return (sw.length > 1 ? '--' : '-') + sw
204
})
205
.join(', ')
206
;
207
return acc;
208
}, {});
209
210
var switchlen = longest(Object.keys(switches).map(function (s) {
211
return switches[s] || '';
212
}));
213
214
var desclen = longest(Object.keys(descriptions).map(function (d) {
215
return descriptions[d] || '';
216
}));
217
218
keys.forEach(function (key) {
219
var kswitch = switches[key];
220
var desc = descriptions[key] || '';
221
222
if (wrap) {
223
desc = wordwrap(switchlen + 4, wrap)(desc)
224
.slice(switchlen + 4)
225
;
226
}
227
228
var spadding = new Array(
229
Math.max(switchlen - kswitch.length + 3, 0)
230
).join(' ');
231
232
var dpadding = new Array(
233
Math.max(desclen - desc.length + 1, 0)
234
).join(' ');
235
236
var type = null;
237
238
if (options.boolean[key]) type = '[boolean]';
239
if (options.string[key]) type = '[string]';
240
241
if (!wrap && dpadding.length > 0) {
242
desc += dpadding;
243
}
244
245
var prelude = ' ' + kswitch + spadding;
246
var extra = [
247
type,
248
demanded[key]
249
? '[required]'
250
: null
251
,
252
options.default[key] !== undefined
253
? '[default: ' + JSON.stringify(options.default[key]) + ']'
254
: null
255
,
256
].filter(Boolean).join(' ');
257
258
var body = [ desc, extra ].filter(Boolean).join(' ');
259
260
if (wrap) {
261
var dlines = desc.split('\n');
262
var dlen = dlines.slice(-1)[0].length
263
+ (dlines.length === 1 ? prelude.length : 0)
264
265
body = desc + (dlen + extra.length > wrap - 2
266
? '\n'
267
+ new Array(wrap - extra.length + 1).join(' ')
268
+ extra
269
: new Array(wrap - extra.length - dlen + 1).join(' ')
270
+ extra
271
);
272
}
273
274
help.push(prelude + body);
275
});
276
277
help.push('');
278
return help.join('\n');
279
};
280
281
Object.defineProperty(self, 'argv', {
282
get : function () { return parseArgs(processArgs) },
283
enumerable : true,
284
});
285
286
function parseArgs (args) {
287
var argv = minimist(args, options);
288
argv.$0 = self.$0;
289
290
if (demanded._ && argv._.length < demanded._) {
291
fail('Not enough non-option arguments: got '
292
+ argv._.length + ', need at least ' + demanded._
293
);
294
}
295
296
var missing = [];
297
Object.keys(demanded).forEach(function (key) {
298
if (!argv[key]) missing.push(key);
299
});
300
301
if (missing.length) {
302
fail('Missing required arguments: ' + missing.join(', '));
303
}
304
305
checks.forEach(function (f) {
306
try {
307
if (f(argv) === false) {
308
fail('Argument check failed: ' + f.toString());
309
}
310
}
311
catch (err) {
312
fail(err)
313
}
314
});
315
316
return argv;
317
}
318
319
function longest (xs) {
320
return Math.max.apply(
321
null,
322
xs.map(function (x) { return x.length })
323
);
324
}
325
326
return self;
327
};
328
329
// rebase an absolute path to a relative one with respect to a base directory
330
// exported for tests
331
exports.rebase = rebase;
332
function rebase (base, dir) {
333
var ds = path.normalize(dir).split('/').slice(1);
334
var bs = path.normalize(base).split('/').slice(1);
335
336
for (var i = 0; ds[i] && ds[i] == bs[i]; i++);
337
ds.splice(0, i); bs.splice(0, i);
338
339
var p = path.normalize(
340
bs.map(function () { return '..' }).concat(ds).join('/')
341
).replace(/\/$/,'').replace(/^$/, '.');
342
return p.match(/^[.\/]/) ? p : './' + p;
343
};
344
345