Comparing version 0.0.1 to 0.0.22
221
index.js
@@ -0,12 +1,217 @@ | ||
var when = require('when') | ||
, sequence = require('when/sequence') | ||
, extend = require('util')._extend; | ||
var Tools = function(usage, flags) { | ||
var self = this; | ||
self.optimist = require('optimist'); | ||
self.tools = require('./tools'); | ||
self.toolsSync = require('./toolsSync'); | ||
//------------------------------------------------------------------------- | ||
// Setup Optimist | ||
//------------------------------------------------------------------------- | ||
self.optimist.usage(usage); | ||
if (flags) { | ||
for (var key in flags) { | ||
self.optimist.options(key, flags[key]); | ||
} | ||
} | ||
// check for any errors & reset argv | ||
self.optimist.argv = self.optimist.parse(process.argv); | ||
// remove node/script path from pos args | ||
self.optimist.argv._.splice(0, 2); | ||
// set `self.flag`s to the values parsed (skip over $0 / "_" / others) | ||
self.flags = {}; | ||
if (flags) { | ||
for (var argvKey in self.optimist.argv) { | ||
if (typeof flags[argvKey] !== 'undefined') { | ||
self.flags[argvKey] = self.optimist.argv[argvKey]; | ||
} | ||
} | ||
} | ||
self.help = self.optimist.help(); // pull out the static help message | ||
self.args = self.optimist.argv._; // pull out the positional arguments | ||
//------------------------------------------------------------------------- | ||
// Setup Tool wrappers | ||
//------------------------------------------------------------------------- | ||
function returnSelf(fn) { | ||
return function() { | ||
fn.apply(null, arguments); | ||
return self; | ||
}; | ||
} | ||
/* | ||
Sync Tools | ||
*/ | ||
// Non Chainable | ||
self.str = self.toolsSync.str; | ||
self.mktemp = self.toolsSync.mktemp; | ||
self.exit = self.toolsSync.exit; | ||
self.walkSync = self.toolsSync.walk; | ||
self.readSync = self.toolsSync.read; | ||
self.readStream = self.toolsSync.readStream; | ||
self.writeStream = self.toolsSync.writeStream; | ||
self.stdout = self.toolsSync.stdout; | ||
self.stderr = self.toolsSync.stderr; | ||
// Chainable | ||
self.out = returnSelf(self.toolsSync.out); | ||
self.cout = returnSelf(self.toolsSync.cout); | ||
self.err = returnSelf(self.toolsSync.err); | ||
self.cerr = returnSelf(self.toolsSync.cerr); | ||
self.writeSync = returnSelf(self.toolsSync.write); | ||
self.appendSync = returnSelf(self.toolsSync.append); | ||
self.existsSync = returnSelf(self.toolsSync.exists); | ||
self.mkdirSync = returnSelf(self.toolsSync.mkdir); | ||
self.touchSync = returnSelf(self.toolsSync.touch); | ||
self.rmSync = returnSelf(self.toolsSync.rm); | ||
/* | ||
Async Tools | ||
*/ | ||
self.exists = self.tools.exists; | ||
self.mkdir = self.tools.mkdir; | ||
self.write = self.tools.write; | ||
self.touch = self.tools.touch; | ||
self.append = self.tools.append; | ||
self.read = self.tools.read; | ||
self.walk = self.tools.walk; | ||
self.rm = self.tools.rm; | ||
// Helpers for Async Tools | ||
self.when = self.tools.when; | ||
self.sequence = self.tools.sequence; | ||
self.parallel = self.tools.parallel; | ||
self.pipeline = self.tools.pipeline; | ||
self.chain = self.tools.chain; | ||
//------------------------------------------------------------------------- | ||
// Class specific functions | ||
//------------------------------------------------------------------------- | ||
self.assert = { | ||
// Positional Argument Asserts | ||
// --------------------------- | ||
argsLen: function(length) { | ||
if (self.args.length !== length) { | ||
self.cerr(self.help) | ||
.cerr('Expected ' + length + ' positional arguments') | ||
.cerr('Received ' + self.str(self.args)).exit(1); | ||
} | ||
return self; | ||
}, | ||
argsLenGe: function(length) { | ||
if (self.args.length < length) { | ||
self.cerr(self.help) | ||
.cerr('Expected at least ' + length + ' positional arguments') | ||
.cerr('Received ' + self.str(self.args)).exit(1); | ||
} | ||
return self; | ||
}, | ||
argsLenGt: function(length) { | ||
if (self.args.length <= length) { | ||
self.cerr(self.help) | ||
.cerr('Expected more than ' + length + ' positional arguments') | ||
.cerr('Received ' + self.str(self.args)).exit(1); | ||
} | ||
return self; | ||
}, | ||
argsLenLe: function(length) { | ||
if (self.args.length > length) { | ||
self.cerr(self.help) | ||
.cerr('Expected ' + length + ' or less positional arguments') | ||
.cerr('Received ' + self.str(self.args)).exit(1); | ||
} | ||
return self; | ||
}, | ||
argsLenLt: function(length) { | ||
if (self.args.length >= length) { | ||
self.cerr(self.help) | ||
.cerr('Expected less than ' + length + ' positional arguments') | ||
.cerr('Received ' + self.str(self.args)).exit(1); | ||
} | ||
return self; | ||
} | ||
// Other asserts to come | ||
// --------------------- | ||
}; | ||
/* | ||
Retrieves the value of a positional argument or a flag based on if a number | ||
or a string is provided. | ||
@param {string|number} positionOrFlag | ||
*/ | ||
self.getValue = function(positionOrFlag) { | ||
if (typeof positionOrFlag === 'string') { | ||
return self.optimist.argv[positionOrFlag] | ||
} | ||
if (typeof positionOrFlag === 'number') { | ||
return self.optimist.argv._[positionOrFlag]; | ||
} | ||
throw new Error('provide a position (number) or a flag (string)'); | ||
}; | ||
return extend(self.getValue, self); | ||
}; | ||
var Main = function(currentModule) { | ||
// ran directly? e.g. node script & not required | ||
var ranDirectly = !currentModule.parent | ||
, usage | ||
, flags; | ||
// sets the usage | ||
this.usage = function() { | ||
var newUsage = Array.prototype.slice.call(arguments).join('\n'); | ||
if (ranDirectly) { | ||
usage = newUsage; | ||
} | ||
return this; | ||
}; | ||
// sets the flags | ||
this.flags = function(newFlags) { | ||
if (ranDirectly && newFlags) { | ||
flags = newFlags; | ||
} | ||
return this; | ||
}; | ||
// Run the main function if we're running this script directly | ||
this.run = function(mainFn) { | ||
if (ranDirectly) { | ||
mainFn(new Tools(usage, flags)); | ||
} | ||
}; | ||
}; | ||
/* | ||
@param {function} mainFn - Function that takes optimist argv | ||
Invoked from another script as: | ||
require('main')(module) | ||
`module` isn't a variable... it's a global that exists in every node script. | ||
@param {object} currentModule - NodeJS module that we will use | ||
*/ | ||
module.exports = function(mainFn) { | ||
// If the script that required US does not have a parent, then | ||
// we assume it was run the the CLI (and not required!) | ||
if (!module.parent.parent) { | ||
var argv = require('optimist').argv; | ||
mainFn(argv); | ||
} | ||
module.exports = function(currentModule) { | ||
return new Main(currentModule); | ||
}; |
{ | ||
"name": "main", | ||
"version": "0.0.1", | ||
"version": "0.0.22", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"description": "Provides useful tools for writing command line scripts", | ||
"repository": { | ||
@@ -16,4 +14,9 @@ "type": "git", | ||
"dependencies": { | ||
"optimist": "~0.6.0" | ||
"optimist": "~0.6.0", | ||
"temp": "~0.6.0", | ||
"when": "~2.5.1" | ||
}, | ||
"scripts": { | ||
"test": "node_modules/.bin/mocha test" | ||
}, | ||
"author": "Trevor Senior <trevor@tsenior.com> (http://tsenior.com/)", | ||
@@ -23,3 +26,9 @@ "license": "MIT", | ||
"url": "https://github.com/trevorsenior/node-main/issues" | ||
}, | ||
"devDependencies": { | ||
"chai": "~1.8.1", | ||
"chai-as-promised": "~4.1.0", | ||
"mocha": "~1.17.0", | ||
"mocha-as-promised": "~2.0.0" | ||
} | ||
} |
162
README.md
@@ -1,39 +0,161 @@ | ||
node-main | ||
========= | ||
# node-main | ||
A *very* basic module that only runs code if the script is ran directly (And not required). It utilizes [optimist](https://github.com/substack/node-optimist) for argument parsing. See [this SO question](http://stackoverflow.com/q/4981891/586621) for more information on how it works. | ||
At the most basic level, `node-main` provides an easy way to specify an [entry point](http://en.wikipedia.org/wiki/Entry_point) for a script that is ran on the command line. It allows us to call a block of code *only* when the script is executed from the command line. If the script is `require`'d into another node module, it will not execute the main block. | ||
## Usage | ||
`node-main` also supports automatic usage generation, and a whole plethora of tools that serve as shortcuts while writing command line scripts. | ||
```javascript | ||
require('main')(function(argv) { | ||
// The code in here will only be executed if running from the CLI | ||
// | ||
// argv is the argv object that optimist produces, e.g. | ||
// require('optimist').argv; | ||
// Code out here will always run, as it is outside the main block | ||
console.log('Hello'); | ||
require('main')(module) | ||
.usage('./someScript [flags] <firstName> <lastName> <filePath>', | ||
'', | ||
'This simple script will write your first and last name', | ||
'to a file. The middle initial is optional as a flag.') | ||
.flags({ | ||
middleInitial: { | ||
alias: 'm', default: '', describe: 'Middle Initial' | ||
} | ||
}) | ||
.run(function($) { | ||
// code in this block only runs when called directly from the CLI | ||
$.assert.argsLen(2); | ||
var fullName = $(0) + $('middleInitial') + $(1); | ||
var filePath = $(2); | ||
$.writeSync(filePath, fullName).cout('done!'); | ||
}); | ||
``` | ||
## Example | ||
Of course, this is only the tip of what's available. Visit the example directories and the documentation for more information | ||
test.js | ||
## Installation | ||
npm install --save main | ||
Don't forget the `--save` flag. It will preserve the version you are using as `node-main` is still going through many changes. | ||
## Usage | ||
For more advanced usage information that covers everything, please visit the [full documentation (WIP)](#). | ||
### The Basics | ||
You're going to need to require the main module in your script. This is the minumum boilerplate to use `node-main`: | ||
```javascript | ||
exports.foo = function() { return 'bar'; } | ||
require('main')(module).run(function() { | ||
// code here will be executed when ran from the CLI | ||
}); | ||
``` | ||
require('main')(function(argv) { | ||
console.log(exports.foo()); | ||
If you would like to set up usage information and flags, simply add those two before calling run like so: | ||
```javascript | ||
require('main')(module) | ||
.usage( | ||
'./someScript [options] <arg1> <arg2>', | ||
'', | ||
'We can expand this usage information to multiple', | ||
'lines if we want to go into detail of how a ', | ||
'script is to be used.') | ||
.flags({ | ||
flagOne: { alias: 'o' }, | ||
flagTwo: { boolean: true }, | ||
flagThree: { demand: true, description: 'third flag' } | ||
}) | ||
.run(function(scriptTools) { // SCRIPT TOOLS | ||
// code here will be executed when ran from the CLI | ||
}); | ||
``` | ||
Running from the terminal: | ||
The flags use [`node-optimist`](https://github.com/substack/node-optimist) - so options that are valid there, will be valid in `node-main` (such as demand, boolean, alias, etc.). | ||
$ node test.js | ||
bar | ||
Also note that I have included a `scriptTools` variable inside of the run function. This allows us to access the tools that help in writing CLI scripts. For the remainder of *The Basics*, I'll refer to the `scriptTools` variable as "`$`" | ||
Using the module from another script will not execute the code in main: | ||
```javascript | ||
// the "scriptTools" can be named anything, even $ | ||
require('main')(module).run(function($) { /* ... */ }); | ||
``` | ||
#### Script Tools | ||
Script tools provide shortcuts for repetitive things that come up frequently when writing CLI scripts. | ||
Visit the [full documentation (WIP)](#) for an exhaustive list of the tools available for use. Visit the example directory to see some in use. | ||
**argument/flag fetching** | ||
If you wanted to fetch the value for flag 'foo' and get the first positional argument: | ||
```javascript | ||
var test = require('./test'); | ||
console.log(test.foo()); // 'bar' | ||
$('foo'); // value of the flag foo | ||
$(0); // the first positional argument | ||
``` | ||
And array of received arguments can be accessed with `$.args` and an object containing all the flag values can be accessed with `$.flags`. | ||
**printing to the console** | ||
```javascript | ||
$.cout('hello world'); //same as console.log | ||
``` | ||
Others include `$.cerr` (console.error), `$.out` (process.stdout.write), `$.err` (process.stderr.write). | ||
**files** | ||
There are synchronous and asynchronous versions of all file operations. The sync ones are chainable and can be used in the following fashion: | ||
```javascript | ||
var file = '/tmp/README.md'; | ||
$.cout( | ||
$.writeSync(file, '# Hello World') | ||
.appendSync(file, 'foo bar') | ||
.readSync(file) | ||
); | ||
``` | ||
The async versions use a promise based implementation. The above can be re-written using asynchronously using promises as such: | ||
```javascript | ||
var file = '/tmp/README.md'; | ||
$.write(file, '# Hello World').then(function() { | ||
return $.append(file, 'foo bar'); | ||
}).then(function() { | ||
return $.read(file); | ||
}).then($.cout); | ||
``` | ||
A non-exhaustive list of some other file functions (all have async counterparts) | ||
```javascript | ||
$.walkSync | ||
$.existsSync | ||
$.mkdirSync | ||
$.touchSync | ||
$.rmSync | ||
``` | ||
There is also a `$.mktemp` function to generate a random file name in the operating systems temporary directory. It doesn't create the file - that's up to you. | ||
**async helpers** | ||
There are async helpers such as `$.chain`, `$.sequence`, and many more to help streamline further, but these are well above the basics. Using `$.chain`, the async file example above can be reduced to: | ||
```javascript | ||
$.chain( | ||
[ $.write, file, '# Hello World' ], | ||
[ $.append, file, 'foo bar' ], | ||
[ $.read, file ], | ||
[ $.cout ] | ||
); | ||
``` | ||
**that's all for now** | ||
That should be enough to get you started. | ||
## Contributing | ||
Feel free to add in new features as long as they are accompanied with test cases. Running `npm test` should get you started. |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
33137
23
817
2
161
3
4
2
1
+ Addedtemp@~0.6.0
+ Addedwhen@~2.5.1
+ Addedgraceful-fs@1.2.3(transitive)
+ Addedosenv@0.0.3(transitive)
+ Addedrimraf@2.1.4(transitive)
+ Addedtemp@0.6.0(transitive)
+ Addedwhen@2.5.1(transitive)