Comparing version 0.0.25 to 1.0.0
#!/usr/bin/env node | ||
require('../index')(module).run(function($) { | ||
$.cout('foo').exit(); // print with console.out, exit 0 | ||
"use strict"; | ||
// Run the following file with a "name" flag, --name World | ||
require('../main').run(module, function($) { | ||
// Prints out hello, with the value of the flag name. | ||
// You can optionally attach "exit" at the end with an exit | ||
// code. It defaults to 0. | ||
$.cout('Hello ' + $('name')).exit(); | ||
}); |
{ | ||
"name": "main", | ||
"version": "0.0.25", | ||
"main": "index.js", | ||
"description": "Provides useful tools for writing command line scripts", | ||
"version": "1.0.0", | ||
"main": "main.js", | ||
"description": "Document based option parsing.", | ||
"repository": { | ||
@@ -11,8 +11,6 @@ "type": "git", | ||
"keywords": [ | ||
"main" | ||
"main", "man", "man page", "options", "flags", "argparse" | ||
], | ||
"dependencies": { | ||
"optimist": "~0.6.1", | ||
"when": "~2.8.0", | ||
"temp": "~0.6.0" | ||
"minimist": "0.0.8" | ||
}, | ||
@@ -29,6 +27,4 @@ "scripts": { | ||
"chai": "~1.8.1", | ||
"chai-as-promised": "~4.1.0", | ||
"mocha": "~1.17.0", | ||
"mocha-as-promised": "~2.0.0" | ||
"mocha": "~1.17.0" | ||
} | ||
} |
216
README.md
@@ -1,161 +0,157 @@ | ||
# node-main | ||
# main | ||
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. | ||
Document based option parsing. | ||
`node-main` also supports automatic usage generation, and a whole plethora of tools that serve as shortcuts while writing command line scripts. | ||
## Main entry point | ||
The contents of the main function will only be ran when called directly from the command line: | ||
```javascript | ||
// Code out here will always run, as it is outside the main block | ||
console.log('Hello'); | ||
// demo.js | ||
exports.foo = function() { return 'bar'; } | ||
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!'); | ||
require('main').run(module, function($) { | ||
console.log('Hello ' + $('name')); | ||
}); | ||
``` | ||
Of course, this is only the tip of what's available. Visit the example directories and the documentation for more information | ||
<sub>Note: The [variable `module`](http://nodejs.org/api/modules.html) is required.</sub> | ||
## Installation | ||
## Argument Parsing | ||
npm install --save main | ||
### With no configuration | ||
Don't forget the `--save` flag. It will preserve the version you are using as `node-main` is still going through many changes. | ||
Out of the box, we can get arguments with no configuration. In our first example, `demo.js`, if we were to call it with | ||
## Usage | ||
```bash | ||
./demo.js --name World foo bar baz | ||
``` | ||
For more advanced usage information that covers everything, please visit the [full documentation (WIP)](#). | ||
it would print out `"Hello World"`. Positional arguments can be fetched by their position: | ||
### 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 | ||
require('main')(module).run(function() { | ||
// code here will be executed when ran from the CLI | ||
}); | ||
$(0) // "foo" | ||
$(2) // "baz" | ||
``` | ||
If you would like to set up usage information and flags, simply add those two before calling run like so: | ||
### With configuration | ||
```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 | ||
}); | ||
``` | ||
There will be no JavaScript options for configuration. Markdown is used instead. | ||
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.). | ||
See the [Documentation](#documentation) section for more information on why. The following document will go over the options that you can use in your application: | ||
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 "`$`" | ||
```javascript | ||
// the "scriptTools" can be named anything, even $ | ||
require('main')(module).run(function($) { /* ... */ }); | ||
``` | ||
**demo.md** | ||
#### Script Tools | ||
```markdown | ||
# demo -- A demonstration of the features & formatting | ||
Script tools provide shortcuts for repetitive things that come up frequently when writing CLI scripts. | ||
## SYNOPSIS | ||
Visit the [full documentation (WIP)](#) for an exhaustive list of the tools available for use. Visit the example directory to see some in use. | ||
sample [flags] `<first>` `<last>` `[pet]` | ||
**argument/flag fetching** | ||
## OPTIONS | ||
If you wanted to fetch the value for flag 'foo' and get the first positional argument: | ||
### -f, --flag | ||
This is a boolean flag as there is no values that follow the flag. | ||
It can be accessed with $('f') or $('flag') | ||
```javascript | ||
$('foo'); // value of the flag foo | ||
$(0); // the first positional argument | ||
``` | ||
### --anything ANYTHING | ||
This flag expects a value to come after it. It can be a number, a string, | ||
etc. The type will be auto detected and the value of $('anything') will | ||
be that value. | ||
And array of received arguments can be accessed with `$.args` and an object containing all the flag values can be accessed with `$.flags`. | ||
### -s "VALUE" --string "VALUE" | ||
Same as above, except that the value placeholder is in quotes meaning | ||
that no type detection is performed, and it is kept as a string. Give | ||
it `000123` and it will remain `000123` vs. converting it to a number | ||
resulting in `123`. | ||
**printing to the console** | ||
### --default=SOMETHING -d SOMETHING (default=foo) | ||
It is also possible to set default values. | ||
```javascript | ||
$.cout('hello world'); //same as console.log | ||
``` | ||
### --home (default=$HOME) | ||
And use environment variables to set those defaults. Any default value | ||
beginning with a `$` will be treated as an environment variable. | ||
Others include `$.cerr` (console.error), `$.out` (process.stdout.write), `$.err` (process.stderr.write). | ||
### --demand (required) | ||
We can also demand that a flag should be set. | ||
**files** | ||
### --pghost (required, default=$PGHOST) | ||
Combining required and using environment variables as defaults is a | ||
good way to ensure that the value will be set one way or another. | ||
There are synchronous and asynchronous versions of all file operations. The sync ones are chainable and can be used in the following fashion: | ||
## AUTHORS | ||
... Another section | ||
```javascript | ||
var file = '/tmp/README.md'; | ||
$.cout( | ||
$.writeSync(file, '# Hello World') | ||
.appendSync(file, 'foo bar') | ||
.readSync(file) | ||
); | ||
## BUGS | ||
... Another section. Add as many sections as you want. | ||
``` | ||
The async versions use a promise based implementation. The above can be re-written using asynchronously using promises as such: | ||
To use it, specify the path to the markdown document: | ||
```javascript | ||
var file = '/tmp/README.md'; | ||
$.write(file, '# Hello World').then(function() { | ||
return $.append(file, 'foo bar'); | ||
}).then(function() { | ||
return $.read(file); | ||
}).then($.cout); | ||
require('main').run(module, './demo.md', function($) { | ||
console.log($('default') + ' ' + $('d')); // prints "foo foo" | ||
}); | ||
``` | ||
A non-exhaustive list of some other file functions (all have async counterparts) | ||
```javascript | ||
$.walkSync | ||
$.existsSync | ||
$.mkdirSync | ||
$.touchSync | ||
$.rmSync | ||
``` | ||
<a name="documentation"></a> | ||
## Documentation | ||
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. | ||
The reason we don't put our flags within our script is so we can use [marked-man](https://github.com/kapouer/marked-man) for man pages & HTML documentation, and have [npm install our man pages](https://www.npmjs.org/doc/json.html#man) automatically. | ||
**async helpers** | ||
In a nutshell, with our markdown: | ||
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: | ||
```bash | ||
marked-man demo.md > man/demo.1 | ||
``` | ||
And then in your `package.json`, reference the man page that was created: | ||
```javascript | ||
$.chain( | ||
[ $.write, file, '# Hello World' ], | ||
[ $.append, file, 'foo bar' ], | ||
[ $.read, file ], | ||
[ $.cout ] | ||
); | ||
{ | ||
"name" : "demo", | ||
"version" : "1.2.3", | ||
"description" : "A demo package", | ||
"main" : "demo.js", | ||
"bin": { | ||
"demo": "demo.js" | ||
} | ||
"man" : "./man/doc.1" | ||
} | ||
``` | ||
**that's all for now** | ||
When your package is installed globally, the man page will also be installed. | ||
That should be enough to get you started. | ||
### Windows | ||
## Contributing | ||
Use `marked-man --format=html` for html based documentation. | ||
Feel free to add in new features as long as they are accompanied with test cases. Running `npm test` should get you started. | ||
## Extras | ||
In addition to the argument fetching, a very minimal set of functions & getters have been attached to the `$` object. Some of them are chainable and will be indicated as such. View the example directory for more more information. | ||
### Arguments | ||
- `$.all` - An object containing all the arguments given. | ||
- `$.pos` - An array containing all the positional arguments given. | ||
### IO | ||
- `$.cout()` - Alias for `console.log`, chainable. | ||
- `$.cerr()` - Alias for `console.error`, chainable. | ||
- `$.out` - Alias for `process.stdout` | ||
- `$.err` - Alias for `process.stderr` | ||
## Assert | ||
- `$.assert` | ||
Exports Node's assert library to this variable. Useful for argument checking, argument lengths, etc. | ||
## Misc. | ||
- `$.exit()` - Alias for `process.exit` |
185
tools.js
"use strict"; | ||
var extend = require('util')._extend | ||
, path = require('path') | ||
, fs = require('fs') | ||
, when = require('when') | ||
, sequence = require('when/sequence') | ||
, parallel = require('when/parallel') | ||
, pipeline = require('when/pipeline') | ||
, nodefn = require('when/node/function') | ||
, callbacks = require('when/callbacks'); | ||
var extend = require('util')._extend; | ||
// Async Tools | ||
//---------------------------- | ||
exports.load = function(argv, markdown) { | ||
var self = {}; | ||
exports.exists = function(filePath) { | ||
return callbacks.call(fs.exists, filePath); | ||
}; | ||
exports.mkdir = function(directoryPath) { | ||
var split = directoryPath.split(path.sep); | ||
// Handle cases where user specifies root, e.g. /home/nolan/... | ||
if (split[0] == '') { | ||
split.shift(); | ||
split[0] = path.sep + split[0]; | ||
// Call a function and return self for chaining purposes | ||
function returnSelf(fn) { | ||
return function() { | ||
fn.apply(null, arguments); | ||
return self; | ||
}; | ||
} | ||
return when.reduce(split, function(previousPath, itemInPath) { | ||
var newPath = path.join(previousPath, itemInPath); | ||
// Options | ||
var optparser = require('./optparser').parse(argv, markdown); | ||
self.all = optparser.options; | ||
self.pos = optparser.options._; | ||
return exports.exists(newPath).then(function(exists) { | ||
var mkdir = exists | ||
? when.resolve() | ||
: nodefn.call(fs.mkdir, newPath); | ||
return mkdir.then(function() { return newPath; }); | ||
}); | ||
}); | ||
}; | ||
// Tools | ||
self.exit = process.exit; | ||
self.out = process.stdout; | ||
self.cout = returnSelf(console.log); | ||
self.err = process.stderr; | ||
self.cerr = returnSelf(console.error); | ||
exports.write = function(filePath, data) { | ||
return nodefn.call(fs.writeFile, filePath, data); | ||
}; | ||
// Asserts | ||
self.assert = require('assert'); // standard Node.js assert library | ||
exports.touch = function(filePath) { | ||
return exports.write(filePath, ''); | ||
// Extend the option parser's getValue $('') functionality with these tools | ||
return extend(optparser.getValue, self); | ||
}; | ||
exports.append = function(filePath, data) { | ||
return nodefn.call(fs.appendFile, filePath, data); | ||
}; | ||
exports.read = function(filePath) { | ||
return nodefn.call(fs.readFile, filePath, { encoding: 'utf8' }); | ||
}; | ||
exports.walk = function(directory, includeDir) { | ||
var results = []; | ||
return when.map(nodefn.call(fs.readdir, directory), function(file) { | ||
file = path.join(directory, file); | ||
return nodefn.call(fs.stat, file).then(function(stat) { | ||
if (stat.isFile()) { return results.push(file); } | ||
if (includeDir) { results.push(file + path.sep); } | ||
return exports.walk(file, includeDir).then(function(fileInDir) { | ||
results = results.concat(fileInDir); | ||
}); | ||
}); | ||
}).then(function() { | ||
return results; | ||
}); | ||
}; | ||
exports.rm = function(filePath) { | ||
return nodefn.call(fs.stat, filePath).then(function(stat) { | ||
if (stat.isFile()) { return nodefn.call(fs.unlink, filePath); } | ||
return exports.walk(filePath, true).then(function(files) { | ||
return when.map(files.reverse(), function(file) { | ||
return nodefn.call(fs.stat, file).then(function(stat) { | ||
return stat.isDirectory() | ||
? nodefn.call(fs.rmdir, file) | ||
: nodefn.call(fs.unlink, file); | ||
}); | ||
}).then(function() { | ||
return nodefn.call(fs.rmdir, filePath); | ||
}); | ||
}); | ||
}); | ||
}; | ||
exports.when = when; | ||
exports.sequence = function() { | ||
var fns = Array.prototype.slice.call(arguments); | ||
return sequence(fns); | ||
}; | ||
exports.parallel = function() { | ||
var fns = Array.prototype.slice.call(arguments); | ||
return parallel(fns); | ||
}; | ||
exports.pipeline = function() { | ||
var fns = Array.prototype.slice.call(arguments); | ||
return pipeline(fns); | ||
}; | ||
/* | ||
called in the form | ||
$.chain( | ||
[ <function name>, <arg 1>, <arg2>, ... ], | ||
[ <function name>, <arg 1>, <arg2>, ... ], | ||
...); | ||
The previous functions value will be passed in to the next function | ||
as the final parameter. This allows us to do things like this: | ||
$.chain( | ||
[ $.read, '/tmp/README.md' ], | ||
[ $.cout ]); | ||
In this example we are reading from a file and printing it's contents to | ||
standard output. | ||
*/ | ||
exports.chain = function() { | ||
var fnCallArray = Array.prototype.slice.call(arguments) | ||
, fnCall | ||
, i = fnCallArray.length | ||
, valid; | ||
if (typeof fnCallArray === 'undefined' || i === 0) { | ||
return when.resolve(); | ||
} | ||
// verify that things are OK with the passed in array... it is a funky | ||
// mechanism that some may get wrong upon first glance | ||
while (i--) { | ||
fnCall = fnCallArray[i]; | ||
valid = typeof fnCall !== 'undefined' && | ||
fnCall instanceof Array && | ||
fnCall.length > 0; | ||
if (!valid) { | ||
return when.reject(new Error( | ||
'Invalid format provided to chain! check out the docs')); | ||
} | ||
} | ||
// create the functions that will be handled by when/pipeline | ||
var pipeFns = fnCallArray.map(function(fnCall) { | ||
// pull out the function & any arguments provided | ||
var fn = fnCall[0] | ||
, providedArgs = fnCall.slice(1); | ||
return function() { | ||
// Get the value from the previous promise and append it to the | ||
// provided arguments | ||
var priorValue = Array.prototype.slice.call(arguments)[0]; | ||
if (typeof priorValue !== 'undefined') { | ||
providedArgs.push(priorValue); | ||
} | ||
return when(fn.apply(undefined, providedArgs)); | ||
}; | ||
}); | ||
return pipeline(pipeFns); | ||
}; |
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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances 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 v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1
2
1
27868
16
737
158
6
1
+ Addedminimist@0.0.8
+ Addedminimist@0.0.8(transitive)
- Removedoptimist@~0.6.1
- Removedtemp@~0.6.0
- Removedwhen@~2.8.0
- Removedgraceful-fs@1.2.3(transitive)
- Removedminimist@0.0.10(transitive)
- Removedoptimist@0.6.1(transitive)
- Removedosenv@0.0.3(transitive)
- Removedrimraf@2.1.4(transitive)
- Removedtemp@0.6.0(transitive)
- Removedwhen@2.8.0(transitive)
- Removedwordwrap@0.0.3(transitive)