electron 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.npmignore CHANGED
@@ -1,4 +1,5 @@
1
1
  .git*
2
+ docs/
2
3
  support/
3
4
  test/
4
5
  .DS_Store
package/History.md CHANGED
@@ -1,4 +1,59 @@
1
1
 
2
+ 0.2.1 / 2012-06-11
3
+ ==================
4
+
5
+ * fix doc asset links
6
+ * generate docs
7
+ * comment typos
8
+ * doc website header
9
+ * readme
10
+ * argv export default to using process.argv
11
+ * added travis support
12
+ * commentt program header
13
+ * fix bug that caused error on multiple calls to `program.colorize`
14
+
15
+ 0.2.0 / 2012-06-10
16
+ ==================
17
+
18
+ * sep update drip 0.3.x
19
+ * docs
20
+ * comment adjustments for docs
21
+ * rearrange examples
22
+ * update examples, change default to use `simple` theme
23
+ * fix copyright headers
24
+ * add support for program description
25
+ * add simple theme and theme comments like what
26
+ * command comments like what
27
+ * typos in program comments
28
+ * comments like what for program
29
+ * improve support for wildcard events using drip 0.3.0
30
+ * improve test coverage for wildcard commands
31
+ * clean theme supports refactored command option parsing
32
+ * tests for program and command
33
+ * refactor command option parsing to support default value
34
+ * program has theme support
35
+ * examples update to include default and absetn
36
+ * program support for `default` and `absent` commands
37
+ * refactor Cli as Program, add istty detection and `useColor` option
38
+ * Args commented for doc generation
39
+ * test coverage support
40
+ * add tests for argv parser
41
+ * expose `electron.argv` as argv parser factory on module main export
42
+ * refactor arg parser to user `commands` instead of `args` and filter helper works for all three types
43
+ * add cli colorize helper
44
+
45
+ 0.1.2 / 2012-05-23
46
+ ==================
47
+
48
+ * refactor parse to return `this`
49
+
50
+ 0.1.1 / 2012-05-22
51
+ ==================
52
+
53
+ * parse args now moved to Args constructor
54
+ * example update
55
+ * argument lookups
56
+
2
57
  0.1.0 / 2012-05-18
3
58
  ==================
4
59
 
package/README.md CHANGED
@@ -1,4 +1,151 @@
1
- electron
2
- ========
1
+ [![Build Status](https://secure.travis-ci.org/logicalparadox/electron.png?branch=master)](http://travis-ci.org/logicalparadox/electron)
3
2
 
4
- Event based CLI framework.
3
+ # Electron
4
+
5
+ > A simple command-line interface framework for [node.js](http://nodejs.org).
6
+
7
+ #### Features
8
+
9
+ - reimagined `process.argv` parsing utility
10
+ - framework for single or multiple command programs
11
+ - automatic `--help` command generation with multiple theming options
12
+ - built in cli coloring
13
+ - chainable api
14
+
15
+ ## Quick Start Guide
16
+
17
+ This "Quick Start Guide" and the full API reference can be found
18
+ on [electron's documentation website](http://alogicalparadox.com/electron).
19
+
20
+ #### Installation
21
+
22
+ The `electron` package is available through [npm](http://npmjs.org). It is recommended
23
+ that you add it to your project's `package.json`.
24
+
25
+ ```bash
26
+ npm install electron
27
+ ```
28
+
29
+ #### Parsing Arguments
30
+
31
+ The argument parsing utility can be used independently of the program
32
+ framework. Just pass the `process.argv` from any node modules and your
33
+ ready to go.
34
+
35
+ The following command execution...
36
+
37
+ ```bash
38
+ $ node cli.js build --minify --out saved.min.js
39
+ ```
40
+
41
+ Could be captured as so...
42
+
43
+ ```javascript
44
+ var argv = require('electron').argv();
45
+
46
+ // objects
47
+ argv.commands; // [ 'build' ]
48
+ argv.modes; // [ 'minify' ]
49
+ argv.params; // { out: 'saved.min.js' }
50
+
51
+ // helpers
52
+ argv.command('build'); // true
53
+ argv.mode('m', 'minify'); // true
54
+ argv.param('o', 'out'); // 'saved.min.js'
55
+ ```
56
+
57
+ Recommend reading the "Argument Parsing Utility" section of the
58
+ [documentation](http://alogicalpardox.com/electron)
59
+ to learn about the methodologies and specifics of each of the helpers.
60
+
61
+ #### Your First Program
62
+
63
+ To construct your first program, simply execute the electron export
64
+ with a parameter of the namespace you wish to use for your program.
65
+ Then proceed to define your settings and commands.
66
+
67
+ ```javascript
68
+ var myApp = require('../lib/myapp')
69
+ , program = require('electron')('myapp');
70
+
71
+ /**
72
+ * Define your program settings
73
+ */
74
+
75
+ program
76
+ .name('My Cool App')
77
+ .desc('http://docs.mycoolapp.com')
78
+ .version(myApp.version);
79
+
80
+ /**
81
+ * Define your first command
82
+ */
83
+
84
+ program
85
+ .command('build')
86
+ .desc('start a build task')
87
+ .option('-m, --minify', 'flag to set enable minification')
88
+ .option('-o, --out [file.js]', 'name of output file')
89
+ .action(function (argv) {
90
+ var minify = argv.mode('m', 'minify')
91
+ , savefile = argv.param('o', 'out')
92
+ , cwd = argv.cwd;
93
+
94
+ program.colorize();
95
+ console.log('Welcome to myApp'.gray + myApp.version);
96
+ console.log('It works if it ends with '.gray + 'myApp ' + 'ok'.green);
97
+ // etc...
98
+ });
99
+
100
+ /**
101
+ * Parse argv and execute respective command
102
+ */
103
+
104
+ program.parse();
105
+ ```
106
+
107
+ Your `-h, --help` and `-v, --version` will be generated for you automatically.
108
+
109
+ Recommend reading the "Program Framework" and "Constructing Commands" sections
110
+ of the [documentation](http://alogicalpardox.com/electron)
111
+ to learn about all of the available chainable commands and theming options
112
+ available to construct your programs.
113
+
114
+ ## Tests
115
+
116
+ Tests are writting in [Mocha](http://github.com/visionmedia/mocha) using
117
+ the [Chai](http://chaijs.com) `should` BDD assertion library. To make sure you
118
+ have that installed, clone this repo, install dependacies using `npm install`.
119
+
120
+ $ npm test
121
+
122
+ ## Contributors
123
+
124
+ Interested in contributing? Fork to get started. Contact [@logicalparadox](http://github.com/logicalparadox)
125
+ if you are interested in being regular contributor.
126
+
127
+ * Jake Luer ([@logicalparadox](http://github.com/logicalparadox))
128
+
129
+ ## License
130
+
131
+ (The MIT License)
132
+
133
+ Copyright (c) 2012 Jake Luer <jake@alogicalparadox.com>
134
+
135
+ Permission is hereby granted, free of charge, to any person obtaining a copy
136
+ of this software and associated documentation files (the "Software"), to deal
137
+ in the Software without restriction, including without limitation the rights
138
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
139
+ copies of the Software, and to permit persons to whom the Software is
140
+ furnished to do so, subject to the following conditions:
141
+
142
+ The above copyright notice and this permission notice shall be included in
143
+ all copies or substantial portions of the Software.
144
+
145
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
146
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
147
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
148
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
149
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
150
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
151
+ THE SOFTWARE.
@@ -0,0 +1,38 @@
1
+ var electron = require('..')
2
+ , program = electron('node clean.js');
3
+
4
+ program
5
+ .name('Electron Clean Theme')
6
+ .desc('https://github.com/logicalparadox/electron')
7
+ .version('0.2.x');
8
+
9
+ program
10
+ .command('start simple')
11
+ .description('Start a simple task')
12
+ .option('-p, --port [6000]', 'Set the simple port')
13
+ .option('-f, --file [path]', 'Use a specific file for this task', true)
14
+ .option('-l, --local', 'Flag as local task')
15
+ .action(function (args) {
16
+ console.log(args.mode('l', 'local'));
17
+ console.log(args.param('p', 'port'));
18
+ });
19
+
20
+ program
21
+ .command('start complex')
22
+ .description('Start a complex task')
23
+ .option('-p, --port [6000]', 'Set the comples port')
24
+ .option('-f, --file [path]', 'Use a specific file for this task', true)
25
+ .option('-l, --local', 'Flag as local task')
26
+ .action(function (args) {
27
+ console.log(args.mode('l', 'local'));
28
+ console.log(args.param('p', 'port'));
29
+ });
30
+
31
+ program
32
+ .command('absent')
33
+ .action(function (args) {
34
+ var cmd = args.commands.join(' ');
35
+ console.log(cmd + ' is not a valid command. Try --help for a list.');
36
+ });
37
+
38
+ program.parse();
@@ -0,0 +1,24 @@
1
+ var electron = require('..');
2
+
3
+ var program = electron('node simple.js');
4
+
5
+ program
6
+ .name('Electron Simple Theme')
7
+ .desc('https://github.com/logicalparadox/electron')
8
+ .theme('simple')
9
+ .version('0.2.x');
10
+
11
+ program
12
+ .command('default')
13
+ .desc('This is sample cli to show off the `simple` theme.')
14
+ .option('-p, --port [6000]', 'Set the simple port')
15
+ .option('-f, --file [path]', 'Use a specific file for this task', true)
16
+ .option('-l, --local', 'Flag as local task')
17
+ .option('-t, --test', 'Run the tests first')
18
+ .option('-w, --watch [dir]', 'Watch directory for changes')
19
+ .action(function (args) {
20
+ console.log('file: ' + args.param('f', 'file'));
21
+ console.log('local: ' + args.mode('l', 'local'));
22
+ });
23
+
24
+ program.parse();
@@ -0,0 +1,235 @@
1
+ /*!
2
+ * Electron - process.argv parsing
3
+ * Copyright (c) 2012 Jake Luer <jake@alogicalparadox.com>
4
+ * MIT Licensed
5
+ */
6
+
7
+ /*!
8
+ * Module export
9
+ */
10
+
11
+ module.exports = Args;
12
+
13
+ /**
14
+ * ## Argument Parsing Utility
15
+ *
16
+ * The electron argument parser takes the node.js standard
17
+ * `process.argv` array and constructs an object with helpers
18
+ * that can easily be queried. This helper is publicly exposed
19
+ * so it can be used independant of the cli framework.
20
+ *
21
+ * var electron = require('electron')
22
+ * , argv = electron.argv(process.argv);
23
+ *
24
+ * When constructed, the electron argv parser recognizes three
25
+ * types command line arguments: _commands_, _modes_, and _parameters_.
26
+ *
27
+ * Each of these types also has a helper that will provide quick access
28
+ * to whether a _command_ or _mode_ is present, or the value of a _parameter_.
29
+ *
30
+ * ##### Commands
31
+ *
32
+ * Commands are the simplest of arguments. They are any arguments
33
+ * that are listed to that do not start with the `-` or `--` prefix.
34
+ * Essentially, they are a list of keys.
35
+ *
36
+ * // $ node cli.js hello universe
37
+ * argv.commands === [ 'hello', 'universe' ];
38
+ *
39
+ * ##### Modes
40
+ *
41
+ * Modes are also a non-value list of keys, but they can be expressed
42
+ * differently by using the `-` or `--` prefix. When using modes, if
43
+ * it begins with a single `-`, each letter will be parsed as its own mode.
44
+ *
45
+ * // $ node cli.js --universe -abc
46
+ * argv.modes === [ 'universe', 'a', 'b', 'c' ];
47
+ *
48
+ * ##### Parameters
49
+ *
50
+ * Paremeters are key:value pairs that are declared in a similiar manner
51
+ * as modes. They can be declared in any of the following ways.
52
+ *
53
+ * // $ node cli.js --noun unverse -v say --topic=hello -w=now
54
+ * argv.params === {
55
+ * noun: 'universe'
56
+ * , v: 'say'
57
+ * , topic: 'hello'
58
+ * , w: 'now'
59
+ * };
60
+ *
61
+ * @header Argument Parsing Utility
62
+ */
63
+
64
+ function Args (args) {
65
+ /*!
66
+ * @param {Array} node.js compatible process.argv
67
+ */
68
+
69
+ this._raw = args;
70
+ this.commands = [];
71
+ this.modes = [];
72
+ this.params = {};
73
+ processArgs.call(this, args);
74
+ }
75
+
76
+ /**
77
+ * ### .command (cmd, [cmd], [...])
78
+ *
79
+ * The `command` helper takes a list of commands and will
80
+ * return `true` if any of them exist in the _commands_ list.
81
+ *
82
+ * // node cli.js hello universe
83
+ * var greeting = argv.command('hi', 'hello') // true
84
+ * , world = argv.command('world', 'earth'); // false
85
+ *
86
+ * @param {String} command(s) to check
87
+ * @returns {Boolean} exists
88
+ * @name command
89
+ * @api public
90
+ */
91
+
92
+ Args.prototype.command = filter('commands');
93
+
94
+ /**
95
+ * ### .mode (mode, [mode], [...])
96
+ *
97
+ * The `mode` helper takes a list of modes and will
98
+ * return `true` if any of them exist in the _modes_ list.
99
+ *
100
+ * // node cli.js --hello -abc
101
+ * var greeting = argv.mode('h', 'hello') // true
102
+ * , world = argv.mode('w', 'world'); // false
103
+ *
104
+ * @param {String} mode(s) to check
105
+ * @returns {Boolean} exists
106
+ * @name mode
107
+ * @api public
108
+ */
109
+
110
+ Args.prototype.mode = filter('modes');
111
+
112
+ /**
113
+ * ### .param (param, [param], [...])
114
+ *
115
+ * The `param` helper takes a list of parameters and will
116
+ * return the value of the first parameter that matches, or
117
+ * `null` if none of the parameters exist in the _params_ list.
118
+ *
119
+ * // node cli.js --hello universe
120
+ * var greeting = argv.param('h', 'hello') // 'universe'
121
+ * , world = argv.param('w', 'world'); // null
122
+ *
123
+ * @param {String} mode(s) to check
124
+ * @returns {String|null} value of first matching parameter
125
+ * @name param
126
+ * @api public
127
+ */
128
+
129
+ Args.prototype.param = filter('params');
130
+
131
+ /*!
132
+ * ### processArgs (args)
133
+ *
134
+ * Take the raw node.js args array and parse out
135
+ * commands, modes, and parameters. Per node standard,
136
+ * the first two elements are considered to be the executor
137
+ * and file and irrelevant.
138
+ *
139
+ * @param {Array} process.argv
140
+ * @ctx Args
141
+ * @api private
142
+ */
143
+
144
+ function processArgs (args) {
145
+ var param_key = null
146
+ , parts = args.slice(2)
147
+ , input = this;
148
+
149
+ function checkParamKey () {
150
+ if (param_key !== null) {
151
+ input.modes.push(param_key);
152
+ param_key = null;
153
+ }
154
+ }
155
+
156
+ parts.forEach(function (part) {
157
+ if (part.substr(0, 2) === '--') {
158
+ checkParamKey();
159
+ if (part.indexOf('=') !== -1) {
160
+ part = part.substr(2).split('=', 2);
161
+ return input.params[part[0]] = part[1]
162
+ }
163
+
164
+ return param_key = part.substr(2);
165
+ }
166
+
167
+ if (part[0] === '-') {
168
+ checkParamKey();
169
+ var sstr = part.substr(1);
170
+ if (sstr.length > 1) {
171
+ if (part.indexOf('=') !== -1) {
172
+ part = part.substr(1).split('=', 2);
173
+ return input.params[part[0]] = part[1]
174
+ }
175
+ for (var i = 0; i < sstr.length; i++)
176
+ input.modes.push(sstr[i]);
177
+ return;
178
+ } else {
179
+ return param_key = part.substr(1);
180
+ }
181
+ }
182
+
183
+ part = Number(part) || part
184
+
185
+ if (param_key !== null) {
186
+ input.params[param_key] = part;
187
+ param_key = null;
188
+ } else {
189
+ input.commands.push(part);
190
+ }
191
+ });
192
+
193
+ checkParamKey();
194
+ }
195
+
196
+ /*!
197
+ * ### filter (which)
198
+ *
199
+ * Constructs a helper function for each of the
200
+ * three types of process.argv types. Returns function
201
+ * to be mounted on to the Arg.prototype.
202
+ *
203
+ * @param {String} which argument type
204
+ * @returns {Function}
205
+ * @api private
206
+ */
207
+
208
+ function filter (which) {
209
+ return function () {
210
+ var self = this
211
+ , modes = Array.prototype.slice.call(arguments)
212
+ , res = Array.isArray(this[which])
213
+ ? false
214
+ : null;
215
+
216
+ function check (el) {
217
+ if (Array.isArray(self[which])) {
218
+ return self[which].indexOf(el) > -1
219
+ ? true
220
+ : null;
221
+ } else {
222
+ return 'undefined' !== typeof self[which][el]
223
+ ? self[which][el]
224
+ : null;
225
+ }
226
+ }
227
+
228
+ for (var i = 0; i < modes.length; i++) {
229
+ var val = check(modes[i]);
230
+ if (val && !res) res = val;
231
+ }
232
+
233
+ return res;
234
+ };
235
+ }
@@ -1,6 +1,26 @@
1
+ /*!
2
+ * Electron - command constructor
3
+ * Copyright (c) 2012 Jake Luer <jake@alogicalpardox.com>
4
+ * MIT Licensed
5
+ */
6
+
7
+ /*!
8
+ * Main export
9
+ */
10
+
1
11
  module.exports = Command;
2
12
 
3
- function Command(cmd) {
13
+ /**
14
+ * ## Constructing Commands
15
+ *
16
+ * Once you have decided to construct a command through `program.command`
17
+ * you will be returned a command object that you can manipulate through
18
+ * chainable methods.
19
+ *
20
+ * @header Constructing Commands
21
+ */
22
+
23
+ function Command (cmd) {
4
24
  this.opts = {
5
25
  cmd: cmd
6
26
  , desc: ''
@@ -9,18 +29,65 @@ function Command(cmd) {
9
29
  }
10
30
  }
11
31
 
12
- Command.prototype.description = function (desc) {
32
+ /**
33
+ * ### .desc (description)
34
+ *
35
+ * Provide a description for this command to be used when
36
+ * being display in help.
37
+ *
38
+ * program
39
+ * .command('hello universe')
40
+ * .desc('Say "Hello" to the Universe.');
41
+ *
42
+ * @param {String} description
43
+ * @returns `command` for chaining
44
+ * @name desc
45
+ * @api public
46
+ */
47
+
48
+ Command.prototype.desc = function (desc) {
13
49
  this.opts.desc = desc;
14
50
  return this;
15
51
  };
16
52
 
17
- // ('-p, --port [6000]', 'Set the port'); => not require, default 6000
18
- // ('-p, --port', 'Set the port', true); => required
53
+ /*!
54
+ * legacy support
55
+ */
56
+
57
+ Command.prototype.description = Command.prototype.desc;
58
+
59
+ /**
60
+ * ### .option (opts, description, required)
61
+ *
62
+ * You may define any number of options for
63
+ * each command. The `opts` string expects a comma delimited
64
+ * list of commands with an optional default value or
65
+ * indicator surrounded by brackets. You may
66
+ * also provide a description of the option and whether
67
+ * it is required.
68
+ *
69
+ * This command may be called multiple times to define multiple
70
+ * options.
71
+ *
72
+ * program
73
+ * .command('build')
74
+ * .option('-m, --minify', 'Flag to build minify version')
75
+ * .option('-f, --file [build.js]', 'Save filename', true);
76
+ *
77
+ * @param {String} options to parse
78
+ * @param {String} description
79
+ * @param {Boolean} required. defaults to false
80
+ * @returns `command` for chaining
81
+ * @name option
82
+ * @api public
83
+ */
19
84
 
20
85
  Command.prototype.option = function (opt, desc, required) {
86
+ var opts = prepareOptions(opt);
87
+
21
88
  this.opts.options.push({
22
- opts: opt.split(' ')
23
- , description: desc
89
+ opts: opts
90
+ , desc: desc
24
91
  , required: ('boolean' === typeof required)
25
92
  ? required
26
93
  : false
@@ -29,8 +96,64 @@ Command.prototype.option = function (opt, desc, required) {
29
96
  return this;
30
97
  };
31
98
 
99
+ /**
100
+ * ### .action (function)
101
+ *
102
+ * Provide the action to be used should this command be
103
+ * called. The function will receive one parameter of the
104
+ * parsed process.argv object. Multiple calls to `action` will
105
+ * replace the previous defined action.
106
+ *
107
+ * program
108
+ * .command('build')
109
+ * .action(function (argv) {
110
+ * var minify = argv.mode('m', 'minify')
111
+ * , file = argv.param('f', 'file');
112
+ * // go!
113
+ * });
114
+ *
115
+ * @param {Function} action to perform
116
+ * @returns `command` for chaining
117
+ * @name action
118
+ * @api public
119
+ */
120
+
32
121
  Command.prototype.action = function (fn) {
33
122
  if ('function' === typeof fn)
34
123
  this.opts.action = fn;
35
124
  return this;
36
125
  };
126
+
127
+ /*!
128
+ * prepareOptions (string)
129
+ *
130
+ * Parse the parameter string provide as an option
131
+ * list in `option`. Returns an object that can be
132
+ * explored during help dislay.
133
+ *
134
+ * @param {String} options
135
+ * @returns {Object} parsed
136
+ * @api private
137
+ */
138
+
139
+ function prepareOptions (str) {
140
+ var list = str.split(' ')
141
+ , res = { flags: [], def: null }
142
+ , m;
143
+
144
+ list.forEach(function (line) {
145
+ // remove trailing commas
146
+ if (line[line.length - 1] === ',')
147
+ line = line.substr(0, line.length - 1);
148
+
149
+ // parse out flags and default value
150
+ if (line.substr(0, 2) === '--')
151
+ res.flags.push(line.substr(2));
152
+ else if (line[0] === '-')
153
+ res.flags.push(line.substr(1));
154
+ else if (m = line.match(/[^\[\]]+(?=\])/g))
155
+ if (!res.def) res.def= m[0];
156
+ });
157
+
158
+ return res;
159
+ }