Skip to content

Commit e1b95d8

Browse files
committedNov 30, 2016
Refactor arguments and options parsing
Now use minimist to match yo and fix type parsing. Fix #821 Fix #813
1 parent a3cab64 commit e1b95d8

File tree

4 files changed

+76
-58
lines changed

4 files changed

+76
-58
lines changed
 

‎lib/actions/help.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ help.help = function help() {
5151
return out.join('\n');
5252
};
5353

54-
function formatArg(argItem) {
55-
var arg = '<' + argItem.name + '>';
54+
function formatArg(config) {
55+
var arg = '<' + config.name + '>';
5656

57-
if (!argItem.config.required) {
57+
if (!config.required) {
5858
arg = '[' + arg + ']';
5959
}
6060

@@ -101,12 +101,11 @@ help.desc = function desc(description) {
101101
* @returns {String} Text of options in formatted table
102102
*/
103103
help.argumentsHelp = function argumentsHelp() {
104-
var rows = this._arguments.map(function (arg) {
105-
var config = arg.config;
104+
var rows = this._arguments.map(function (config) {
106105

107106
return [
108107
'',
109-
arg.name ? arg.name : '',
108+
config.name ? config.name : '',
110109
config.desc ? '# ' + config.desc : '',
111110
config.type ? 'Type: ' + config.type.name : '',
112111
'Required: ' + config.required

‎lib/index.js

+61-40
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ var findUp = require('find-up');
88
var readPkgUp = require('read-pkg-up');
99
var chalk = require('chalk');
1010
var mkdirp = require('mkdirp');
11-
var nopt = require('nopt');
11+
var minimist = require('minimist');
1212
var runAsync = require('run-async');
1313
var through = require('through2');
1414
var userHome = require('user-home');
@@ -229,14 +229,6 @@ Base.prototype.option = function option(name, config) {
229229
this._options[name] = config;
230230
}
231231

232-
if (this.options[name] == null && config.alias) {
233-
this.options[name] = this.options[config.alias];
234-
}
235-
236-
if (this.options[name] == null) {
237-
this.options[name] = config.default;
238-
}
239-
240232
this.parseOptions();
241233
return this;
242234
};
@@ -275,47 +267,76 @@ Base.prototype.argument = function argument(name, config) {
275267
type: String
276268
});
277269

270+
this._arguments.push(config);
278271

279-
var position = this._arguments.length;
280-
this._arguments.push({
281-
name: name,
282-
config: config
283-
});
284-
285-
Object.defineProperty(this, name, {
286-
configurable: true,
287-
enumerable: true,
288-
get: function () {
289-
// a bit of coercion and type handling, to be improved
290-
// just dealing with Array/String, default is assumed to be String
291-
var value = config.type === Array ? this.args.slice(position) : this.args[position];
292-
return position >= this.args.length ? config.default : value;
293-
},
294-
set: function (value) {
295-
this.args[position] = value;
296-
}
297-
});
298-
299-
this.checkRequiredArgs();
272+
this.parseOptions();
300273
return this;
301274
};
302275

303276
Base.prototype.parseOptions = function () {
304-
var opts = {};
305-
var shortOpts = {};
277+
var minimistDef = {
278+
string: [],
279+
boolean: [],
280+
alias: {},
281+
default: {}
282+
};
306283

307284
_.each(this._options, function (option) {
308-
opts[option.name] = option.type;
285+
if (option.type === Boolean) {
286+
minimistDef.boolean.push(option.name);
287+
} else {
288+
minimistDef.string.push(option.name);
289+
}
309290

310291
if (option.alias) {
311-
shortOpts[option.alias] = '--' + option.name;
292+
minimistDef.alias[option.alias] = option.name;
312293
}
294+
295+
// Only apply default values if we don't already have a value injected from
296+
// the runner
297+
if (option.name in this.options) {
298+
minimistDef.default[option.name] = this.options[option.name];
299+
} else if (option.alias && option.alias in this.options) {
300+
minimistDef.default[option.alias] = this.options[option.alias];
301+
} else if ('default' in option) {
302+
minimistDef.default[option.name] = option.default;
303+
}
304+
}.bind(this));
305+
306+
var parsedOpts = minimist(this._args, minimistDef);
307+
308+
// Parse options to the desired type
309+
_.each(parsedOpts, function (option, name) {
310+
if (this._options[name] && option !== undefined) {
311+
parsedOpts[name] = this._options[name].type(option);
312+
}
313+
}.bind(this));
314+
315+
// Parse positional arguments to valid options
316+
this._arguments.forEach(function (config, index) {
317+
var value;
318+
if (index >= parsedOpts._.length) {
319+
if ('default' in config) {
320+
value = config.default;
321+
} else {
322+
return;
323+
}
324+
} else {
325+
if (config.type === Array) {
326+
value = parsedOpts._.slice(index, parsedOpts._.length);
327+
} else {
328+
value = config.type(parsedOpts._[index]);
329+
}
330+
}
331+
332+
parsedOpts[config.name] = value;
313333
});
314334

315-
opts = nopt(opts, shortOpts, this._args, 0);
316-
_.extend(this.options, opts);
335+
// Make the parsed options available to the instance
336+
_.extend(this.options, parsedOpts);
337+
this.args = this.arguments = parsedOpts._;
317338

318-
this.args = this.arguments = opts.argv.remain;
339+
// Make sure required args are all present
319340
this.checkRequiredArgs();
320341
};
321342

@@ -331,11 +352,11 @@ Base.prototype.checkRequiredArgs = function () {
331352
return;
332353
}
333354

334-
this._arguments.forEach(function (arg, position) {
355+
this._arguments.forEach(function (config, position) {
335356
// If the help option was not provided, check whether the argument was
336357
// required, and whether a value was provided.
337-
if (arg.config.required && position >= this.args.length) {
338-
return this.emit('error', new Error('Did not provide required argument ' + chalk.bold(arg.name) + '!'));
358+
if (config.required && position >= this.args.length) {
359+
return this.emit('error', new Error('Did not provide required argument ' + chalk.bold(config.name) + '!'));
339360
}
340361
}, this);
341362
};

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
"istextorbinary": "^2.1.0",
5050
"lodash": "^4.11.1",
5151
"mem-fs-editor": "^2.0.0",
52+
"minimist": "^1.2.0",
5253
"mkdirp": "^0.5.0",
53-
"nopt": "^3.0.0",
5454
"path-exists": "^2.0.0",
5555
"path-is-absolute": "^1.0.0",
5656
"pretty-bytes": "^3.0.1",

‎test/base.js

+9-11
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ describe('Base', function () {
9393
resolved: 'test'
9494
});
9595

96+
generator.option('foo');
9697
assert.equal(generator.options.foo, true);
98+
assert.deepEqual(generator.args, ['bar']);
9799
});
98100

99101
it('set arguments based on nopt arguments', function () {
@@ -102,8 +104,9 @@ describe('Base', function () {
102104
resolved: 'test'
103105
});
104106

107+
generator.option('foo');
105108
generator.argument('baz');
106-
assert.equal(generator.baz, 'bar');
109+
assert.equal(generator.options.baz, 'bar');
107110
});
108111

109112
it('set options with false values', function (done) {
@@ -546,18 +549,12 @@ describe('Base', function () {
546549

547550
it('create the property specified with value from positional args', function () {
548551
this.dummy.argument('foo');
549-
assert.equal(this.dummy.foo, 'bar');
550-
});
551-
552-
it('can still be set as a property once defined', function () {
553-
this.dummy.argument('foo');
554-
this.dummy.foo = 'barbar';
555-
assert.equal(this.dummy.foo, 'barbar');
552+
assert.equal(this.dummy.options.foo, 'bar');
556553
});
557554

558555
it('slice positional arguments when config.type is Array', function () {
559556
this.dummy.argument('bar', { type: Array });
560-
assert.deepEqual(this.dummy.bar, ['bar', 'baz', 'bom']);
557+
assert.deepEqual(this.dummy.options.bar, ['bar', 'baz', 'bom']);
561558
});
562559

563560
it('raise an error if required arguments are not provided', function (done) {
@@ -593,7 +590,7 @@ describe('Base', function () {
593590
dummy.argument('baz');
594591
dummy.option('foo', { type: String });
595592

596-
assert.equal(dummy.baz, 'baz');
593+
assert.equal(dummy.options.baz, 'baz');
597594
});
598595
});
599596

@@ -621,7 +618,8 @@ describe('Base', function () {
621618
Base.apply(this, arguments);
622619

623620
this.option('long-name', {
624-
alias: 'short-name'
621+
alias: 'short-name',
622+
type: String
625623
});
626624
}
627625
});

0 commit comments

Comments
 (0)
Please sign in to comment.