dragonfly_chrome_headless 0.2.0
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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +12 -0
- data/Gemfile +4 -0
- data/README.md +53 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/dragonfly_chrome_headless.gemspec +27 -0
- data/lib/dragonfly_chrome_headless.rb +9 -0
- data/lib/dragonfly_chrome_headless/plugin.rb +17 -0
- data/lib/dragonfly_chrome_headless/processors/rasterize.rb +33 -0
- data/lib/dragonfly_chrome_headless/version.rb +3 -0
- data/node_modules/.bin/chrome-remote-interface +1 -0
- data/node_modules/.bin/mkdirp +1 -0
- data/node_modules/.bin/rimraf +1 -0
- data/node_modules/@types/core-js/LICENSE +21 -0
- data/node_modules/@types/core-js/README.md +16 -0
- data/node_modules/@types/core-js/index.d.ts +2452 -0
- data/node_modules/@types/core-js/package.json +85 -0
- data/node_modules/@types/mkdirp/README.md +18 -0
- data/node_modules/@types/mkdirp/index.d.ts +14 -0
- data/node_modules/@types/mkdirp/package.json +77 -0
- data/node_modules/@types/mkdirp/types-metadata.json +25 -0
- data/node_modules/@types/node/README.md +16 -0
- data/node_modules/@types/node/index.d.ts +4132 -0
- data/node_modules/@types/node/package.json +84 -0
- data/node_modules/balanced-match/.npmignore +5 -0
- data/node_modules/balanced-match/LICENSE.md +21 -0
- data/node_modules/balanced-match/README.md +91 -0
- data/node_modules/balanced-match/index.js +59 -0
- data/node_modules/balanced-match/package.json +112 -0
- data/node_modules/brace-expansion/README.md +123 -0
- data/node_modules/brace-expansion/index.js +201 -0
- data/node_modules/brace-expansion/package.json +114 -0
- data/node_modules/chrome-launcher/.clang-format +6 -0
- data/node_modules/chrome-launcher/.npmignore +11 -0
- data/node_modules/chrome-launcher/README.md +123 -0
- data/node_modules/chrome-launcher/ask.js +32 -0
- data/node_modules/chrome-launcher/ask.ts +35 -0
- data/node_modules/chrome-launcher/chrome-finder.js +157 -0
- data/node_modules/chrome-launcher/chrome-finder.ts +186 -0
- data/node_modules/chrome-launcher/chrome-launcher.js +245 -0
- data/node_modules/chrome-launcher/chrome-launcher.ts +312 -0
- data/node_modules/chrome-launcher/compiled-check.js +14 -0
- data/node_modules/chrome-launcher/flags.js +27 -0
- data/node_modules/chrome-launcher/flags.ts +26 -0
- data/node_modules/chrome-launcher/index.js +7 -0
- data/node_modules/chrome-launcher/index.ts +1 -0
- data/node_modules/chrome-launcher/manual-chrome-launcher.js +30 -0
- data/node_modules/chrome-launcher/package.json +116 -0
- data/node_modules/chrome-launcher/random-port.js +24 -0
- data/node_modules/chrome-launcher/random-port.ts +23 -0
- data/node_modules/chrome-launcher/tsconfig.json +19 -0
- data/node_modules/chrome-launcher/utils.js +52 -0
- data/node_modules/chrome-launcher/utils.ts +44 -0
- data/node_modules/chrome-launcher/yarn.lock +1486 -0
- data/node_modules/chrome-remote-interface/LICENSE +18 -0
- data/node_modules/chrome-remote-interface/README.md +849 -0
- data/node_modules/chrome-remote-interface/bin/client.js +337 -0
- data/node_modules/chrome-remote-interface/chrome-remote-interface.js +11 -0
- data/node_modules/chrome-remote-interface/index.js +39 -0
- data/node_modules/chrome-remote-interface/lib/api.js +84 -0
- data/node_modules/chrome-remote-interface/lib/chrome.js +307 -0
- data/node_modules/chrome-remote-interface/lib/defaults.js +4 -0
- data/node_modules/chrome-remote-interface/lib/devtools.js +245 -0
- data/node_modules/chrome-remote-interface/lib/external-request.js +28 -0
- data/node_modules/chrome-remote-interface/lib/protocol.json +13780 -0
- data/node_modules/chrome-remote-interface/lib/websocket-wrapper.js +32 -0
- data/node_modules/chrome-remote-interface/package.json +128 -0
- data/node_modules/chrome-remote-interface/webpack.config.js +55 -0
- data/node_modules/commander/Readme.md +195 -0
- data/node_modules/commander/index.js +851 -0
- data/node_modules/commander/package.json +92 -0
- data/node_modules/concat-map/.travis.yml +4 -0
- data/node_modules/concat-map/LICENSE +18 -0
- data/node_modules/concat-map/README.markdown +62 -0
- data/node_modules/concat-map/example/map.js +6 -0
- data/node_modules/concat-map/index.js +13 -0
- data/node_modules/concat-map/package.json +117 -0
- data/node_modules/concat-map/test/map.js +39 -0
- data/node_modules/debug/.coveralls.yml +1 -0
- data/node_modules/debug/.eslintrc +11 -0
- data/node_modules/debug/.npmignore +9 -0
- data/node_modules/debug/.travis.yml +14 -0
- data/node_modules/debug/CHANGELOG.md +357 -0
- data/node_modules/debug/LICENSE +19 -0
- data/node_modules/debug/Makefile +50 -0
- data/node_modules/debug/Readme.md +312 -0
- data/node_modules/debug/component.json +19 -0
- data/node_modules/debug/karma.conf.js +70 -0
- data/node_modules/debug/node.js +1 -0
- data/node_modules/debug/package.json +124 -0
- data/node_modules/debug/src/browser.js +185 -0
- data/node_modules/debug/src/debug.js +202 -0
- data/node_modules/debug/src/index.js +10 -0
- data/node_modules/debug/src/node.js +246 -0
- data/node_modules/fs.realpath/LICENSE +43 -0
- data/node_modules/fs.realpath/README.md +33 -0
- data/node_modules/fs.realpath/index.js +66 -0
- data/node_modules/fs.realpath/old.js +303 -0
- data/node_modules/fs.realpath/package.json +94 -0
- data/node_modules/glob/LICENSE +15 -0
- data/node_modules/glob/README.md +368 -0
- data/node_modules/glob/changelog.md +67 -0
- data/node_modules/glob/common.js +240 -0
- data/node_modules/glob/glob.js +790 -0
- data/node_modules/glob/package.json +112 -0
- data/node_modules/glob/sync.js +486 -0
- data/node_modules/html-pdf-chrome/.npmignore +9 -0
- data/node_modules/html-pdf-chrome/LICENSE +21 -0
- data/node_modules/html-pdf-chrome/README.md +165 -0
- data/node_modules/html-pdf-chrome/lib/src/ChromePrintOptions.d.ts +87 -0
- data/node_modules/html-pdf-chrome/lib/src/ChromePrintOptions.js +4 -0
- data/node_modules/html-pdf-chrome/lib/src/ChromePrintOptions.js.map +1 -0
- data/node_modules/html-pdf-chrome/lib/src/CompletionTrigger.d.ts +120 -0
- data/node_modules/html-pdf-chrome/lib/src/CompletionTrigger.js +206 -0
- data/node_modules/html-pdf-chrome/lib/src/CompletionTrigger.js.map +1 -0
- data/node_modules/html-pdf-chrome/lib/src/CreateResult.d.ts +70 -0
- data/node_modules/html-pdf-chrome/lib/src/CreateResult.js +98 -0
- data/node_modules/html-pdf-chrome/lib/src/CreateResult.js.map +1 -0
- data/node_modules/html-pdf-chrome/lib/src/index.d.ts +72 -0
- data/node_modules/html-pdf-chrome/lib/src/index.js +123 -0
- data/node_modules/html-pdf-chrome/lib/src/index.js.map +1 -0
- data/node_modules/html-pdf-chrome/package.json +133 -0
- data/node_modules/html-pdf-chrome/src/ChromePrintOptions.ts +99 -0
- data/node_modules/html-pdf-chrome/src/CompletionTrigger.ts +201 -0
- data/node_modules/html-pdf-chrome/src/CreateResult.ts +100 -0
- data/node_modules/html-pdf-chrome/src/index.ts +179 -0
- data/node_modules/inflight/LICENSE +15 -0
- data/node_modules/inflight/README.md +37 -0
- data/node_modules/inflight/inflight.js +54 -0
- data/node_modules/inflight/package.json +105 -0
- data/node_modules/inherits/LICENSE +16 -0
- data/node_modules/inherits/README.md +42 -0
- data/node_modules/inherits/inherits.js +7 -0
- data/node_modules/inherits/inherits_browser.js +23 -0
- data/node_modules/inherits/package.json +97 -0
- data/node_modules/lighthouse-logger/README.md +4 -0
- data/node_modules/lighthouse-logger/index.js +212 -0
- data/node_modules/lighthouse-logger/package.json +69 -0
- data/node_modules/lighthouse-logger/yarn.lock +13 -0
- data/node_modules/minimatch/LICENSE +15 -0
- data/node_modules/minimatch/README.md +209 -0
- data/node_modules/minimatch/minimatch.js +923 -0
- data/node_modules/minimatch/package.json +99 -0
- data/node_modules/minimist/.travis.yml +4 -0
- data/node_modules/minimist/LICENSE +18 -0
- data/node_modules/minimist/example/parse.js +2 -0
- data/node_modules/minimist/index.js +187 -0
- data/node_modules/minimist/package.json +101 -0
- data/node_modules/minimist/readme.markdown +73 -0
- data/node_modules/minimist/test/dash.js +24 -0
- data/node_modules/minimist/test/default_bool.js +20 -0
- data/node_modules/minimist/test/dotted.js +16 -0
- data/node_modules/minimist/test/long.js +31 -0
- data/node_modules/minimist/test/parse.js +318 -0
- data/node_modules/minimist/test/parse_modified.js +9 -0
- data/node_modules/minimist/test/short.js +67 -0
- data/node_modules/minimist/test/whitespace.js +8 -0
- data/node_modules/mkdirp/.travis.yml +8 -0
- data/node_modules/mkdirp/LICENSE +21 -0
- data/node_modules/mkdirp/bin/cmd.js +33 -0
- data/node_modules/mkdirp/bin/usage.txt +12 -0
- data/node_modules/mkdirp/examples/pow.js +6 -0
- data/node_modules/mkdirp/index.js +98 -0
- data/node_modules/mkdirp/package.json +93 -0
- data/node_modules/mkdirp/readme.markdown +100 -0
- data/node_modules/mkdirp/test/chmod.js +41 -0
- data/node_modules/mkdirp/test/clobber.js +38 -0
- data/node_modules/mkdirp/test/mkdirp.js +28 -0
- data/node_modules/mkdirp/test/opts_fs.js +29 -0
- data/node_modules/mkdirp/test/opts_fs_sync.js +27 -0
- data/node_modules/mkdirp/test/perm.js +32 -0
- data/node_modules/mkdirp/test/perm_sync.js +36 -0
- data/node_modules/mkdirp/test/race.js +37 -0
- data/node_modules/mkdirp/test/rel.js +32 -0
- data/node_modules/mkdirp/test/return.js +25 -0
- data/node_modules/mkdirp/test/return_sync.js +24 -0
- data/node_modules/mkdirp/test/root.js +19 -0
- data/node_modules/mkdirp/test/sync.js +32 -0
- data/node_modules/mkdirp/test/umask.js +28 -0
- data/node_modules/mkdirp/test/umask_sync.js +32 -0
- data/node_modules/ms/README.md +51 -0
- data/node_modules/ms/index.js +152 -0
- data/node_modules/ms/license.md +21 -0
- data/node_modules/ms/package.json +109 -0
- data/node_modules/once/LICENSE +15 -0
- data/node_modules/once/README.md +79 -0
- data/node_modules/once/once.js +42 -0
- data/node_modules/once/package.json +101 -0
- data/node_modules/path-is-absolute/index.js +20 -0
- data/node_modules/path-is-absolute/license +21 -0
- data/node_modules/path-is-absolute/package.json +111 -0
- data/node_modules/path-is-absolute/readme.md +59 -0
- data/node_modules/rimraf/LICENSE +15 -0
- data/node_modules/rimraf/README.md +101 -0
- data/node_modules/rimraf/bin.js +50 -0
- data/node_modules/rimraf/package.json +99 -0
- data/node_modules/rimraf/rimraf.js +363 -0
- data/node_modules/ultron/LICENSE +22 -0
- data/node_modules/ultron/index.js +138 -0
- data/node_modules/ultron/package.json +112 -0
- data/node_modules/wrappy/LICENSE +15 -0
- data/node_modules/wrappy/README.md +36 -0
- data/node_modules/wrappy/package.json +97 -0
- data/node_modules/wrappy/wrappy.js +33 -0
- data/node_modules/ws/LICENSE +21 -0
- data/node_modules/ws/README.md +259 -0
- data/node_modules/ws/SECURITY.md +33 -0
- data/node_modules/ws/index.js +15 -0
- data/node_modules/ws/lib/BufferUtil.fallback.js +56 -0
- data/node_modules/ws/lib/BufferUtil.js +15 -0
- data/node_modules/ws/lib/ErrorCodes.js +28 -0
- data/node_modules/ws/lib/EventTarget.js +158 -0
- data/node_modules/ws/lib/Extensions.js +69 -0
- data/node_modules/ws/lib/PerMessageDeflate.js +339 -0
- data/node_modules/ws/lib/Receiver.js +520 -0
- data/node_modules/ws/lib/Sender.js +438 -0
- data/node_modules/ws/lib/Validation.fallback.js +9 -0
- data/node_modules/ws/lib/Validation.js +17 -0
- data/node_modules/ws/lib/WebSocket.js +705 -0
- data/node_modules/ws/lib/WebSocketServer.js +336 -0
- data/node_modules/ws/package.json +122 -0
- data/package.json +26 -0
- data/samples/sample.html +13 -0
- data/script/rasterize.js +18 -0
- metadata +325 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
const EventEmitter = require('events');
|
2
|
+
|
3
|
+
// wrapper around the Node.js ws module
|
4
|
+
// for use in browsers
|
5
|
+
class WebSocketWrapper extends EventEmitter {
|
6
|
+
constructor(url) {
|
7
|
+
super();
|
8
|
+
this._ws = new WebSocket(url);
|
9
|
+
this._ws.onopen = () => {
|
10
|
+
this.emit('open');
|
11
|
+
};
|
12
|
+
this._ws.onclose = () => {
|
13
|
+
this.emit('close');
|
14
|
+
};
|
15
|
+
this._ws.onmessage = (event) => {
|
16
|
+
this.emit('message', event.data);
|
17
|
+
};
|
18
|
+
this._ws.onerror = () => {
|
19
|
+
this.emit('error', new Error('WebSocket error'));
|
20
|
+
};
|
21
|
+
}
|
22
|
+
|
23
|
+
close() {
|
24
|
+
this._ws.close();
|
25
|
+
}
|
26
|
+
|
27
|
+
send(data) {
|
28
|
+
this._ws.send(data);
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
module.exports = WebSocketWrapper;
|
@@ -0,0 +1,128 @@
|
|
1
|
+
{
|
2
|
+
"_args": [
|
3
|
+
[
|
4
|
+
{
|
5
|
+
"raw": "chrome-remote-interface@^0.24.1",
|
6
|
+
"scope": null,
|
7
|
+
"escapedName": "chrome-remote-interface",
|
8
|
+
"name": "chrome-remote-interface",
|
9
|
+
"rawSpec": "^0.24.1",
|
10
|
+
"spec": ">=0.24.1 <0.25.0",
|
11
|
+
"type": "range"
|
12
|
+
},
|
13
|
+
"/Users/tomascelizna/Devel/dragonfly_chrome_headless/node_modules/html-pdf-chrome"
|
14
|
+
]
|
15
|
+
],
|
16
|
+
"_from": "chrome-remote-interface@>=0.24.1 <0.25.0",
|
17
|
+
"_id": "chrome-remote-interface@0.24.2",
|
18
|
+
"_inCache": true,
|
19
|
+
"_location": "/chrome-remote-interface",
|
20
|
+
"_nodeVersion": "8.1.4",
|
21
|
+
"_npmOperationalInternal": {
|
22
|
+
"host": "s3://npm-registry-packages",
|
23
|
+
"tmp": "tmp/chrome-remote-interface-0.24.2.tgz_1500028321222_0.3025570046156645"
|
24
|
+
},
|
25
|
+
"_npmUser": {
|
26
|
+
"name": "cyrus-and",
|
27
|
+
"email": "cyrus.and@gmail.com"
|
28
|
+
},
|
29
|
+
"_npmVersion": "5.0.3",
|
30
|
+
"_phantomChildren": {},
|
31
|
+
"_requested": {
|
32
|
+
"raw": "chrome-remote-interface@^0.24.1",
|
33
|
+
"scope": null,
|
34
|
+
"escapedName": "chrome-remote-interface",
|
35
|
+
"name": "chrome-remote-interface",
|
36
|
+
"rawSpec": "^0.24.1",
|
37
|
+
"spec": ">=0.24.1 <0.25.0",
|
38
|
+
"type": "range"
|
39
|
+
},
|
40
|
+
"_requiredBy": [
|
41
|
+
"/html-pdf-chrome"
|
42
|
+
],
|
43
|
+
"_resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.24.2.tgz",
|
44
|
+
"_shasum": "43a05440a1fa60b73769e72f3e7892ac11d66eba",
|
45
|
+
"_shrinkwrap": null,
|
46
|
+
"_spec": "chrome-remote-interface@^0.24.1",
|
47
|
+
"_where": "/Users/tomascelizna/Devel/dragonfly_chrome_headless/node_modules/html-pdf-chrome",
|
48
|
+
"author": {
|
49
|
+
"name": "Andrea Cardaci",
|
50
|
+
"email": "cyrus.and@gmail.com"
|
51
|
+
},
|
52
|
+
"bin": {
|
53
|
+
"chrome-remote-interface": "./bin/client.js"
|
54
|
+
},
|
55
|
+
"browser": "chrome-remote-interface.js",
|
56
|
+
"bugs": {
|
57
|
+
"url": "http://github.com/cyrus-and/chrome-remote-interface/issues"
|
58
|
+
},
|
59
|
+
"contributors": [
|
60
|
+
{
|
61
|
+
"name": "Andrey Sidorov",
|
62
|
+
"email": "sidoares@yandex.ru"
|
63
|
+
},
|
64
|
+
{
|
65
|
+
"name": "Greg Cochard",
|
66
|
+
"email": "greg@gregcochard.com"
|
67
|
+
}
|
68
|
+
],
|
69
|
+
"dependencies": {
|
70
|
+
"commander": "2.1.x",
|
71
|
+
"ws": "2.0.x"
|
72
|
+
},
|
73
|
+
"description": "Chrome Debugging Protocol interface",
|
74
|
+
"devDependencies": {
|
75
|
+
"babel-core": "^6.18.2",
|
76
|
+
"babel-loader": "^6.2.8",
|
77
|
+
"babel-preset-es2015": "^6.18.0",
|
78
|
+
"eslint": "3.12.x",
|
79
|
+
"json-loader": "^0.5.4",
|
80
|
+
"mocha": "3.1.x",
|
81
|
+
"webpack": "^1.13.3"
|
82
|
+
},
|
83
|
+
"directories": {},
|
84
|
+
"dist": {
|
85
|
+
"integrity": "sha512-KmG2wHqlnTEPwdmc0baW4qpSEldN6zAHrkwCquo6wsFnOFkmvYZRv4Kbko7GylFyrLjbyUQzUCMwfxNIftIhvA==",
|
86
|
+
"shasum": "43a05440a1fa60b73769e72f3e7892ac11d66eba",
|
87
|
+
"tarball": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.24.2.tgz"
|
88
|
+
},
|
89
|
+
"engines": {
|
90
|
+
"node": ">=4"
|
91
|
+
},
|
92
|
+
"files": [
|
93
|
+
"lib",
|
94
|
+
"bin",
|
95
|
+
"index.js",
|
96
|
+
"chrome-remote-interface.js",
|
97
|
+
"webpack.config.js"
|
98
|
+
],
|
99
|
+
"gitHead": "217b18986be0f836dee40cfb68401991dfa35634",
|
100
|
+
"homepage": "https://github.com/cyrus-and/chrome-remote-interface",
|
101
|
+
"keywords": [
|
102
|
+
"chrome",
|
103
|
+
"debug",
|
104
|
+
"protocol",
|
105
|
+
"remote",
|
106
|
+
"interface"
|
107
|
+
],
|
108
|
+
"license": "MIT",
|
109
|
+
"maintainers": [
|
110
|
+
{
|
111
|
+
"name": "cyrus-and",
|
112
|
+
"email": "cyrus.and@gmail.com"
|
113
|
+
}
|
114
|
+
],
|
115
|
+
"name": "chrome-remote-interface",
|
116
|
+
"optionalDependencies": {},
|
117
|
+
"readme": "ERROR: No README data found!",
|
118
|
+
"repository": {
|
119
|
+
"type": "git",
|
120
|
+
"url": "git://github.com/cyrus-and/chrome-remote-interface.git"
|
121
|
+
},
|
122
|
+
"scripts": {
|
123
|
+
"prepublish": "webpack",
|
124
|
+
"test": "./scripts/run-tests.sh",
|
125
|
+
"webpack": "webpack"
|
126
|
+
},
|
127
|
+
"version": "0.24.2"
|
128
|
+
}
|
@@ -0,0 +1,55 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const webpack = require('webpack');
|
4
|
+
|
5
|
+
function criWrapper(_, options, callback) {
|
6
|
+
window.criRequest(options, callback);
|
7
|
+
}
|
8
|
+
|
9
|
+
const webpackConfig = {
|
10
|
+
resolve: {
|
11
|
+
alias: {
|
12
|
+
'ws': './websocket-wrapper.js'
|
13
|
+
}
|
14
|
+
},
|
15
|
+
externals: [
|
16
|
+
{
|
17
|
+
'./external-request.js': `var (${criWrapper})`
|
18
|
+
}
|
19
|
+
],
|
20
|
+
module: {
|
21
|
+
loaders: [
|
22
|
+
{
|
23
|
+
test: /\.js$/,
|
24
|
+
exclude: /node_modules/,
|
25
|
+
loader: 'babel-loader'
|
26
|
+
},
|
27
|
+
{
|
28
|
+
test: /\.json$/,
|
29
|
+
loader: 'json'
|
30
|
+
}
|
31
|
+
]
|
32
|
+
},
|
33
|
+
plugins: [
|
34
|
+
],
|
35
|
+
entry: './index.js',
|
36
|
+
output: {
|
37
|
+
libraryTarget: process.env.TARGET || 'commonjs2',
|
38
|
+
library: 'CDP',
|
39
|
+
filename: 'chrome-remote-interface.js'
|
40
|
+
}
|
41
|
+
};
|
42
|
+
|
43
|
+
if (process.env.DEBUG !== 'true') {
|
44
|
+
webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin({
|
45
|
+
mangle: true,
|
46
|
+
compress: {
|
47
|
+
warnings: false
|
48
|
+
},
|
49
|
+
output: {
|
50
|
+
comments: false
|
51
|
+
}
|
52
|
+
}));
|
53
|
+
}
|
54
|
+
|
55
|
+
module.exports = webpackConfig;
|
@@ -0,0 +1,195 @@
|
|
1
|
+
# Commander.js
|
2
|
+
|
3
|
+
The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander).
|
4
|
+
|
5
|
+
[](http://travis-ci.org/visionmedia/commander.js)
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
$ npm install commander
|
10
|
+
|
11
|
+
## Option parsing
|
12
|
+
|
13
|
+
Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options.
|
14
|
+
|
15
|
+
```js
|
16
|
+
#!/usr/bin/env node
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Module dependencies.
|
20
|
+
*/
|
21
|
+
|
22
|
+
var program = require('commander');
|
23
|
+
|
24
|
+
program
|
25
|
+
.version('0.0.1')
|
26
|
+
.option('-p, --peppers', 'Add peppers')
|
27
|
+
.option('-P, --pineapple', 'Add pineapple')
|
28
|
+
.option('-b, --bbq', 'Add bbq sauce')
|
29
|
+
.option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')
|
30
|
+
.parse(process.argv);
|
31
|
+
|
32
|
+
console.log('you ordered a pizza with:');
|
33
|
+
if (program.peppers) console.log(' - peppers');
|
34
|
+
if (program.pineapple) console.log(' - pineapple');
|
35
|
+
if (program.bbq) console.log(' - bbq');
|
36
|
+
console.log(' - %s cheese', program.cheese);
|
37
|
+
```
|
38
|
+
|
39
|
+
Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc.
|
40
|
+
|
41
|
+
## Automated --help
|
42
|
+
|
43
|
+
The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free:
|
44
|
+
|
45
|
+
```
|
46
|
+
$ ./examples/pizza --help
|
47
|
+
|
48
|
+
Usage: pizza [options]
|
49
|
+
|
50
|
+
Options:
|
51
|
+
|
52
|
+
-V, --version output the version number
|
53
|
+
-p, --peppers Add peppers
|
54
|
+
-P, --pineapple Add pineapple
|
55
|
+
-b, --bbq Add bbq sauce
|
56
|
+
-c, --cheese <type> Add the specified type of cheese [marble]
|
57
|
+
-h, --help output usage information
|
58
|
+
|
59
|
+
```
|
60
|
+
|
61
|
+
## Coercion
|
62
|
+
|
63
|
+
```js
|
64
|
+
function range(val) {
|
65
|
+
return val.split('..').map(Number);
|
66
|
+
}
|
67
|
+
|
68
|
+
function list(val) {
|
69
|
+
return val.split(',');
|
70
|
+
}
|
71
|
+
|
72
|
+
program
|
73
|
+
.version('0.0.1')
|
74
|
+
.usage('[options] <file ...>')
|
75
|
+
.option('-i, --integer <n>', 'An integer argument', parseInt)
|
76
|
+
.option('-f, --float <n>', 'A float argument', parseFloat)
|
77
|
+
.option('-r, --range <a>..<b>', 'A range', range)
|
78
|
+
.option('-l, --list <items>', 'A list', list)
|
79
|
+
.option('-o, --optional [value]', 'An optional value')
|
80
|
+
.parse(process.argv);
|
81
|
+
|
82
|
+
console.log(' int: %j', program.integer);
|
83
|
+
console.log(' float: %j', program.float);
|
84
|
+
console.log(' optional: %j', program.optional);
|
85
|
+
program.range = program.range || [];
|
86
|
+
console.log(' range: %j..%j', program.range[0], program.range[1]);
|
87
|
+
console.log(' list: %j', program.list);
|
88
|
+
console.log(' args: %j', program.args);
|
89
|
+
```
|
90
|
+
|
91
|
+
## Custom help
|
92
|
+
|
93
|
+
You can display arbitrary `-h, --help` information
|
94
|
+
by listening for "--help". Commander will automatically
|
95
|
+
exit once you are done so that the remainder of your program
|
96
|
+
does not execute causing undesired behaviours, for example
|
97
|
+
in the following executable "stuff" will not output when
|
98
|
+
`--help` is used.
|
99
|
+
|
100
|
+
```js
|
101
|
+
#!/usr/bin/env node
|
102
|
+
|
103
|
+
/**
|
104
|
+
* Module dependencies.
|
105
|
+
*/
|
106
|
+
|
107
|
+
var program = require('../');
|
108
|
+
|
109
|
+
function list(val) {
|
110
|
+
return val.split(',').map(Number);
|
111
|
+
}
|
112
|
+
|
113
|
+
program
|
114
|
+
.version('0.0.1')
|
115
|
+
.option('-f, --foo', 'enable some foo')
|
116
|
+
.option('-b, --bar', 'enable some bar')
|
117
|
+
.option('-B, --baz', 'enable some baz');
|
118
|
+
|
119
|
+
// must be before .parse() since
|
120
|
+
// node's emit() is immediate
|
121
|
+
|
122
|
+
program.on('--help', function(){
|
123
|
+
console.log(' Examples:');
|
124
|
+
console.log('');
|
125
|
+
console.log(' $ custom-help --help');
|
126
|
+
console.log(' $ custom-help -h');
|
127
|
+
console.log('');
|
128
|
+
});
|
129
|
+
|
130
|
+
program.parse(process.argv);
|
131
|
+
|
132
|
+
console.log('stuff');
|
133
|
+
```
|
134
|
+
|
135
|
+
yielding the following help output:
|
136
|
+
|
137
|
+
```
|
138
|
+
|
139
|
+
Usage: custom-help [options]
|
140
|
+
|
141
|
+
Options:
|
142
|
+
|
143
|
+
-h, --help output usage information
|
144
|
+
-V, --version output the version number
|
145
|
+
-f, --foo enable some foo
|
146
|
+
-b, --bar enable some bar
|
147
|
+
-B, --baz enable some baz
|
148
|
+
|
149
|
+
Examples:
|
150
|
+
|
151
|
+
$ custom-help --help
|
152
|
+
$ custom-help -h
|
153
|
+
|
154
|
+
```
|
155
|
+
|
156
|
+
## .outputHelp()
|
157
|
+
|
158
|
+
Output help information without exiting.
|
159
|
+
|
160
|
+
## .help()
|
161
|
+
|
162
|
+
Output help information and exit immediately.
|
163
|
+
|
164
|
+
## Links
|
165
|
+
|
166
|
+
- [API documentation](http://visionmedia.github.com/commander.js/)
|
167
|
+
- [ascii tables](https://github.com/LearnBoost/cli-table)
|
168
|
+
- [progress bars](https://github.com/visionmedia/node-progress)
|
169
|
+
- [more progress bars](https://github.com/substack/node-multimeter)
|
170
|
+
- [examples](https://github.com/visionmedia/commander.js/tree/master/examples)
|
171
|
+
|
172
|
+
## License
|
173
|
+
|
174
|
+
(The MIT License)
|
175
|
+
|
176
|
+
Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
|
177
|
+
|
178
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
179
|
+
a copy of this software and associated documentation files (the
|
180
|
+
'Software'), to deal in the Software without restriction, including
|
181
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
182
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
183
|
+
permit persons to whom the Software is furnished to do so, subject to
|
184
|
+
the following conditions:
|
185
|
+
|
186
|
+
The above copyright notice and this permission notice shall be
|
187
|
+
included in all copies or substantial portions of the Software.
|
188
|
+
|
189
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
190
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
191
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
192
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
193
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
194
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
195
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,851 @@
|
|
1
|
+
|
2
|
+
/**
|
3
|
+
* Module dependencies.
|
4
|
+
*/
|
5
|
+
|
6
|
+
var EventEmitter = require('events').EventEmitter;
|
7
|
+
var spawn = require('child_process').spawn;
|
8
|
+
var fs = require('fs');
|
9
|
+
var exists = fs.existsSync;
|
10
|
+
var path = require('path');
|
11
|
+
var dirname = path.dirname;
|
12
|
+
var basename = path.basename;
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Expose the root command.
|
16
|
+
*/
|
17
|
+
|
18
|
+
exports = module.exports = new Command;
|
19
|
+
|
20
|
+
/**
|
21
|
+
* Expose `Command`.
|
22
|
+
*/
|
23
|
+
|
24
|
+
exports.Command = Command;
|
25
|
+
|
26
|
+
/**
|
27
|
+
* Expose `Option`.
|
28
|
+
*/
|
29
|
+
|
30
|
+
exports.Option = Option;
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Initialize a new `Option` with the given `flags` and `description`.
|
34
|
+
*
|
35
|
+
* @param {String} flags
|
36
|
+
* @param {String} description
|
37
|
+
* @api public
|
38
|
+
*/
|
39
|
+
|
40
|
+
function Option(flags, description) {
|
41
|
+
this.flags = flags;
|
42
|
+
this.required = ~flags.indexOf('<');
|
43
|
+
this.optional = ~flags.indexOf('[');
|
44
|
+
this.bool = !~flags.indexOf('-no-');
|
45
|
+
flags = flags.split(/[ ,|]+/);
|
46
|
+
if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift();
|
47
|
+
this.long = flags.shift();
|
48
|
+
this.description = description || '';
|
49
|
+
}
|
50
|
+
|
51
|
+
/**
|
52
|
+
* Return option name.
|
53
|
+
*
|
54
|
+
* @return {String}
|
55
|
+
* @api private
|
56
|
+
*/
|
57
|
+
|
58
|
+
Option.prototype.name = function(){
|
59
|
+
return this.long
|
60
|
+
.replace('--', '')
|
61
|
+
.replace('no-', '');
|
62
|
+
};
|
63
|
+
|
64
|
+
/**
|
65
|
+
* Check if `arg` matches the short or long flag.
|
66
|
+
*
|
67
|
+
* @param {String} arg
|
68
|
+
* @return {Boolean}
|
69
|
+
* @api private
|
70
|
+
*/
|
71
|
+
|
72
|
+
Option.prototype.is = function(arg){
|
73
|
+
return arg == this.short
|
74
|
+
|| arg == this.long;
|
75
|
+
};
|
76
|
+
|
77
|
+
/**
|
78
|
+
* Initialize a new `Command`.
|
79
|
+
*
|
80
|
+
* @param {String} name
|
81
|
+
* @api public
|
82
|
+
*/
|
83
|
+
|
84
|
+
function Command(name) {
|
85
|
+
this.commands = [];
|
86
|
+
this.options = [];
|
87
|
+
this._execs = [];
|
88
|
+
this._args = [];
|
89
|
+
this._name = name;
|
90
|
+
}
|
91
|
+
|
92
|
+
/**
|
93
|
+
* Inherit from `EventEmitter.prototype`.
|
94
|
+
*/
|
95
|
+
|
96
|
+
Command.prototype.__proto__ = EventEmitter.prototype;
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Add command `name`.
|
100
|
+
*
|
101
|
+
* The `.action()` callback is invoked when the
|
102
|
+
* command `name` is specified via __ARGV__,
|
103
|
+
* and the remaining arguments are applied to the
|
104
|
+
* function for access.
|
105
|
+
*
|
106
|
+
* When the `name` is "*" an un-matched command
|
107
|
+
* will be passed as the first arg, followed by
|
108
|
+
* the rest of __ARGV__ remaining.
|
109
|
+
*
|
110
|
+
* Examples:
|
111
|
+
*
|
112
|
+
* program
|
113
|
+
* .version('0.0.1')
|
114
|
+
* .option('-C, --chdir <path>', 'change the working directory')
|
115
|
+
* .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
|
116
|
+
* .option('-T, --no-tests', 'ignore test hook')
|
117
|
+
*
|
118
|
+
* program
|
119
|
+
* .command('setup')
|
120
|
+
* .description('run remote setup commands')
|
121
|
+
* .action(function(){
|
122
|
+
* console.log('setup');
|
123
|
+
* });
|
124
|
+
*
|
125
|
+
* program
|
126
|
+
* .command('exec <cmd>')
|
127
|
+
* .description('run the given remote command')
|
128
|
+
* .action(function(cmd){
|
129
|
+
* console.log('exec "%s"', cmd);
|
130
|
+
* });
|
131
|
+
*
|
132
|
+
* program
|
133
|
+
* .command('*')
|
134
|
+
* .description('deploy the given env')
|
135
|
+
* .action(function(env){
|
136
|
+
* console.log('deploying "%s"', env);
|
137
|
+
* });
|
138
|
+
*
|
139
|
+
* program.parse(process.argv);
|
140
|
+
*
|
141
|
+
* @param {String} name
|
142
|
+
* @param {String} [desc]
|
143
|
+
* @return {Command} the new command
|
144
|
+
* @api public
|
145
|
+
*/
|
146
|
+
|
147
|
+
Command.prototype.command = function(name, desc){
|
148
|
+
var args = name.split(/ +/);
|
149
|
+
var cmd = new Command(args.shift());
|
150
|
+
if (desc) cmd.description(desc);
|
151
|
+
if (desc) this.executables = true;
|
152
|
+
if (desc) this._execs[cmd._name] = true;
|
153
|
+
this.commands.push(cmd);
|
154
|
+
cmd.parseExpectedArgs(args);
|
155
|
+
cmd.parent = this;
|
156
|
+
if (desc) return this;
|
157
|
+
return cmd;
|
158
|
+
};
|
159
|
+
|
160
|
+
/**
|
161
|
+
* Add an implicit `help [cmd]` subcommand
|
162
|
+
* which invokes `--help` for the given command.
|
163
|
+
*
|
164
|
+
* @api private
|
165
|
+
*/
|
166
|
+
|
167
|
+
Command.prototype.addImplicitHelpCommand = function() {
|
168
|
+
this.command('help [cmd]', 'display help for [cmd]');
|
169
|
+
};
|
170
|
+
|
171
|
+
/**
|
172
|
+
* Parse expected `args`.
|
173
|
+
*
|
174
|
+
* For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
|
175
|
+
*
|
176
|
+
* @param {Array} args
|
177
|
+
* @return {Command} for chaining
|
178
|
+
* @api public
|
179
|
+
*/
|
180
|
+
|
181
|
+
Command.prototype.parseExpectedArgs = function(args){
|
182
|
+
if (!args.length) return;
|
183
|
+
var self = this;
|
184
|
+
args.forEach(function(arg){
|
185
|
+
switch (arg[0]) {
|
186
|
+
case '<':
|
187
|
+
self._args.push({ required: true, name: arg.slice(1, -1) });
|
188
|
+
break;
|
189
|
+
case '[':
|
190
|
+
self._args.push({ required: false, name: arg.slice(1, -1) });
|
191
|
+
break;
|
192
|
+
}
|
193
|
+
});
|
194
|
+
return this;
|
195
|
+
};
|
196
|
+
|
197
|
+
/**
|
198
|
+
* Register callback `fn` for the command.
|
199
|
+
*
|
200
|
+
* Examples:
|
201
|
+
*
|
202
|
+
* program
|
203
|
+
* .command('help')
|
204
|
+
* .description('display verbose help')
|
205
|
+
* .action(function(){
|
206
|
+
* // output help here
|
207
|
+
* });
|
208
|
+
*
|
209
|
+
* @param {Function} fn
|
210
|
+
* @return {Command} for chaining
|
211
|
+
* @api public
|
212
|
+
*/
|
213
|
+
|
214
|
+
Command.prototype.action = function(fn){
|
215
|
+
var self = this;
|
216
|
+
this.parent.on(this._name, function(args, unknown){
|
217
|
+
// Parse any so-far unknown options
|
218
|
+
unknown = unknown || [];
|
219
|
+
var parsed = self.parseOptions(unknown);
|
220
|
+
|
221
|
+
// Output help if necessary
|
222
|
+
outputHelpIfNecessary(self, parsed.unknown);
|
223
|
+
|
224
|
+
// If there are still any unknown options, then we simply
|
225
|
+
// die, unless someone asked for help, in which case we give it
|
226
|
+
// to them, and then we die.
|
227
|
+
if (parsed.unknown.length > 0) {
|
228
|
+
self.unknownOption(parsed.unknown[0]);
|
229
|
+
}
|
230
|
+
|
231
|
+
// Leftover arguments need to be pushed back. Fixes issue #56
|
232
|
+
if (parsed.args.length) args = parsed.args.concat(args);
|
233
|
+
|
234
|
+
self._args.forEach(function(arg, i){
|
235
|
+
if (arg.required && null == args[i]) {
|
236
|
+
self.missingArgument(arg.name);
|
237
|
+
}
|
238
|
+
});
|
239
|
+
|
240
|
+
// Always append ourselves to the end of the arguments,
|
241
|
+
// to make sure we match the number of arguments the user
|
242
|
+
// expects
|
243
|
+
if (self._args.length) {
|
244
|
+
args[self._args.length] = self;
|
245
|
+
} else {
|
246
|
+
args.push(self);
|
247
|
+
}
|
248
|
+
|
249
|
+
fn.apply(this, args);
|
250
|
+
});
|
251
|
+
return this;
|
252
|
+
};
|
253
|
+
|
254
|
+
/**
|
255
|
+
* Define option with `flags`, `description` and optional
|
256
|
+
* coercion `fn`.
|
257
|
+
*
|
258
|
+
* The `flags` string should contain both the short and long flags,
|
259
|
+
* separated by comma, a pipe or space. The following are all valid
|
260
|
+
* all will output this way when `--help` is used.
|
261
|
+
*
|
262
|
+
* "-p, --pepper"
|
263
|
+
* "-p|--pepper"
|
264
|
+
* "-p --pepper"
|
265
|
+
*
|
266
|
+
* Examples:
|
267
|
+
*
|
268
|
+
* // simple boolean defaulting to false
|
269
|
+
* program.option('-p, --pepper', 'add pepper');
|
270
|
+
*
|
271
|
+
* --pepper
|
272
|
+
* program.pepper
|
273
|
+
* // => Boolean
|
274
|
+
*
|
275
|
+
* // simple boolean defaulting to false
|
276
|
+
* program.option('-C, --no-cheese', 'remove cheese');
|
277
|
+
*
|
278
|
+
* program.cheese
|
279
|
+
* // => true
|
280
|
+
*
|
281
|
+
* --no-cheese
|
282
|
+
* program.cheese
|
283
|
+
* // => true
|
284
|
+
*
|
285
|
+
* // required argument
|
286
|
+
* program.option('-C, --chdir <path>', 'change the working directory');
|
287
|
+
*
|
288
|
+
* --chdir /tmp
|
289
|
+
* program.chdir
|
290
|
+
* // => "/tmp"
|
291
|
+
*
|
292
|
+
* // optional argument
|
293
|
+
* program.option('-c, --cheese [type]', 'add cheese [marble]');
|
294
|
+
*
|
295
|
+
* @param {String} flags
|
296
|
+
* @param {String} description
|
297
|
+
* @param {Function|Mixed} fn or default
|
298
|
+
* @param {Mixed} defaultValue
|
299
|
+
* @return {Command} for chaining
|
300
|
+
* @api public
|
301
|
+
*/
|
302
|
+
|
303
|
+
Command.prototype.option = function(flags, description, fn, defaultValue){
|
304
|
+
var self = this
|
305
|
+
, option = new Option(flags, description)
|
306
|
+
, oname = option.name()
|
307
|
+
, name = camelcase(oname);
|
308
|
+
|
309
|
+
// default as 3rd arg
|
310
|
+
if ('function' != typeof fn) defaultValue = fn, fn = null;
|
311
|
+
|
312
|
+
// preassign default value only for --no-*, [optional], or <required>
|
313
|
+
if (false == option.bool || option.optional || option.required) {
|
314
|
+
// when --no-* we make sure default is true
|
315
|
+
if (false == option.bool) defaultValue = true;
|
316
|
+
// preassign only if we have a default
|
317
|
+
if (undefined !== defaultValue) self[name] = defaultValue;
|
318
|
+
}
|
319
|
+
|
320
|
+
// register the option
|
321
|
+
this.options.push(option);
|
322
|
+
|
323
|
+
// when it's passed assign the value
|
324
|
+
// and conditionally invoke the callback
|
325
|
+
this.on(oname, function(val){
|
326
|
+
// coercion
|
327
|
+
if (null != val && fn) val = fn(val);
|
328
|
+
|
329
|
+
// unassigned or bool
|
330
|
+
if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) {
|
331
|
+
// if no value, bool true, and we have a default, then use it!
|
332
|
+
if (null == val) {
|
333
|
+
self[name] = option.bool
|
334
|
+
? defaultValue || true
|
335
|
+
: false;
|
336
|
+
} else {
|
337
|
+
self[name] = val;
|
338
|
+
}
|
339
|
+
} else if (null !== val) {
|
340
|
+
// reassign
|
341
|
+
self[name] = val;
|
342
|
+
}
|
343
|
+
});
|
344
|
+
|
345
|
+
return this;
|
346
|
+
};
|
347
|
+
|
348
|
+
/**
|
349
|
+
* Parse `argv`, settings options and invoking commands when defined.
|
350
|
+
*
|
351
|
+
* @param {Array} argv
|
352
|
+
* @return {Command} for chaining
|
353
|
+
* @api public
|
354
|
+
*/
|
355
|
+
|
356
|
+
Command.prototype.parse = function(argv){
|
357
|
+
// implicit help
|
358
|
+
if (this.executables) this.addImplicitHelpCommand();
|
359
|
+
|
360
|
+
// store raw args
|
361
|
+
this.rawArgs = argv;
|
362
|
+
|
363
|
+
// guess name
|
364
|
+
this._name = this._name || basename(argv[1]);
|
365
|
+
|
366
|
+
// process argv
|
367
|
+
var parsed = this.parseOptions(this.normalize(argv.slice(2)));
|
368
|
+
var args = this.args = parsed.args;
|
369
|
+
|
370
|
+
var result = this.parseArgs(this.args, parsed.unknown);
|
371
|
+
|
372
|
+
// executable sub-commands
|
373
|
+
var name = result.args[0];
|
374
|
+
if (this._execs[name]) return this.executeSubCommand(argv, args, parsed.unknown);
|
375
|
+
|
376
|
+
return result;
|
377
|
+
};
|
378
|
+
|
379
|
+
/**
|
380
|
+
* Execute a sub-command executable.
|
381
|
+
*
|
382
|
+
* @param {Array} argv
|
383
|
+
* @param {Array} args
|
384
|
+
* @param {Array} unknown
|
385
|
+
* @api private
|
386
|
+
*/
|
387
|
+
|
388
|
+
Command.prototype.executeSubCommand = function(argv, args, unknown) {
|
389
|
+
args = args.concat(unknown);
|
390
|
+
|
391
|
+
if (!args.length) this.help();
|
392
|
+
if ('help' == args[0] && 1 == args.length) this.help();
|
393
|
+
|
394
|
+
// <cmd> --help
|
395
|
+
if ('help' == args[0]) {
|
396
|
+
args[0] = args[1];
|
397
|
+
args[1] = '--help';
|
398
|
+
}
|
399
|
+
|
400
|
+
// executable
|
401
|
+
var dir = dirname(argv[1]);
|
402
|
+
var bin = basename(argv[1]) + '-' + args[0];
|
403
|
+
|
404
|
+
// check for ./<bin> first
|
405
|
+
var local = path.join(dir, bin);
|
406
|
+
|
407
|
+
// run it
|
408
|
+
args = args.slice(1);
|
409
|
+
var proc = spawn(local, args, { stdio: 'inherit', customFds: [0, 1, 2] });
|
410
|
+
proc.on('error', function(err){
|
411
|
+
if (err.code == "ENOENT") {
|
412
|
+
console.error('\n %s(1) does not exist, try --help\n', bin);
|
413
|
+
} else if (err.code == "EACCES") {
|
414
|
+
console.error('\n %s(1) not executable. try chmod or run with root\n', bin);
|
415
|
+
}
|
416
|
+
});
|
417
|
+
|
418
|
+
this.runningCommand = proc;
|
419
|
+
};
|
420
|
+
|
421
|
+
/**
|
422
|
+
* Normalize `args`, splitting joined short flags. For example
|
423
|
+
* the arg "-abc" is equivalent to "-a -b -c".
|
424
|
+
* This also normalizes equal sign and splits "--abc=def" into "--abc def".
|
425
|
+
*
|
426
|
+
* @param {Array} args
|
427
|
+
* @return {Array}
|
428
|
+
* @api private
|
429
|
+
*/
|
430
|
+
|
431
|
+
Command.prototype.normalize = function(args){
|
432
|
+
var ret = []
|
433
|
+
, arg
|
434
|
+
, lastOpt
|
435
|
+
, index;
|
436
|
+
|
437
|
+
for (var i = 0, len = args.length; i < len; ++i) {
|
438
|
+
arg = args[i];
|
439
|
+
i > 0 && (lastOpt = this.optionFor(args[i-1]));
|
440
|
+
|
441
|
+
if (lastOpt && lastOpt.required) {
|
442
|
+
ret.push(arg);
|
443
|
+
} else if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {
|
444
|
+
arg.slice(1).split('').forEach(function(c){
|
445
|
+
ret.push('-' + c);
|
446
|
+
});
|
447
|
+
} else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) {
|
448
|
+
ret.push(arg.slice(0, index), arg.slice(index + 1));
|
449
|
+
} else {
|
450
|
+
ret.push(arg);
|
451
|
+
}
|
452
|
+
}
|
453
|
+
|
454
|
+
return ret;
|
455
|
+
};
|
456
|
+
|
457
|
+
/**
|
458
|
+
* Parse command `args`.
|
459
|
+
*
|
460
|
+
* When listener(s) are available those
|
461
|
+
* callbacks are invoked, otherwise the "*"
|
462
|
+
* event is emitted and those actions are invoked.
|
463
|
+
*
|
464
|
+
* @param {Array} args
|
465
|
+
* @return {Command} for chaining
|
466
|
+
* @api private
|
467
|
+
*/
|
468
|
+
|
469
|
+
Command.prototype.parseArgs = function(args, unknown){
|
470
|
+
var cmds = this.commands
|
471
|
+
, len = cmds.length
|
472
|
+
, name;
|
473
|
+
|
474
|
+
if (args.length) {
|
475
|
+
name = args[0];
|
476
|
+
if (this.listeners(name).length) {
|
477
|
+
this.emit(args.shift(), args, unknown);
|
478
|
+
} else {
|
479
|
+
this.emit('*', args);
|
480
|
+
}
|
481
|
+
} else {
|
482
|
+
outputHelpIfNecessary(this, unknown);
|
483
|
+
|
484
|
+
// If there were no args and we have unknown options,
|
485
|
+
// then they are extraneous and we need to error.
|
486
|
+
if (unknown.length > 0) {
|
487
|
+
this.unknownOption(unknown[0]);
|
488
|
+
}
|
489
|
+
}
|
490
|
+
|
491
|
+
return this;
|
492
|
+
};
|
493
|
+
|
494
|
+
/**
|
495
|
+
* Return an option matching `arg` if any.
|
496
|
+
*
|
497
|
+
* @param {String} arg
|
498
|
+
* @return {Option}
|
499
|
+
* @api private
|
500
|
+
*/
|
501
|
+
|
502
|
+
Command.prototype.optionFor = function(arg){
|
503
|
+
for (var i = 0, len = this.options.length; i < len; ++i) {
|
504
|
+
if (this.options[i].is(arg)) {
|
505
|
+
return this.options[i];
|
506
|
+
}
|
507
|
+
}
|
508
|
+
};
|
509
|
+
|
510
|
+
/**
|
511
|
+
* Parse options from `argv` returning `argv`
|
512
|
+
* void of these options.
|
513
|
+
*
|
514
|
+
* @param {Array} argv
|
515
|
+
* @return {Array}
|
516
|
+
* @api public
|
517
|
+
*/
|
518
|
+
|
519
|
+
Command.prototype.parseOptions = function(argv){
|
520
|
+
var args = []
|
521
|
+
, len = argv.length
|
522
|
+
, literal
|
523
|
+
, option
|
524
|
+
, arg;
|
525
|
+
|
526
|
+
var unknownOptions = [];
|
527
|
+
|
528
|
+
// parse options
|
529
|
+
for (var i = 0; i < len; ++i) {
|
530
|
+
arg = argv[i];
|
531
|
+
|
532
|
+
// literal args after --
|
533
|
+
if ('--' == arg) {
|
534
|
+
literal = true;
|
535
|
+
continue;
|
536
|
+
}
|
537
|
+
|
538
|
+
if (literal) {
|
539
|
+
args.push(arg);
|
540
|
+
continue;
|
541
|
+
}
|
542
|
+
|
543
|
+
// find matching Option
|
544
|
+
option = this.optionFor(arg);
|
545
|
+
|
546
|
+
// option is defined
|
547
|
+
if (option) {
|
548
|
+
// requires arg
|
549
|
+
if (option.required) {
|
550
|
+
arg = argv[++i];
|
551
|
+
if (null == arg) return this.optionMissingArgument(option);
|
552
|
+
this.emit(option.name(), arg);
|
553
|
+
// optional arg
|
554
|
+
} else if (option.optional) {
|
555
|
+
arg = argv[i+1];
|
556
|
+
if (null == arg || ('-' == arg[0] && '-' != arg)) {
|
557
|
+
arg = null;
|
558
|
+
} else {
|
559
|
+
++i;
|
560
|
+
}
|
561
|
+
this.emit(option.name(), arg);
|
562
|
+
// bool
|
563
|
+
} else {
|
564
|
+
this.emit(option.name());
|
565
|
+
}
|
566
|
+
continue;
|
567
|
+
}
|
568
|
+
|
569
|
+
// looks like an option
|
570
|
+
if (arg.length > 1 && '-' == arg[0]) {
|
571
|
+
unknownOptions.push(arg);
|
572
|
+
|
573
|
+
// If the next argument looks like it might be
|
574
|
+
// an argument for this option, we pass it on.
|
575
|
+
// If it isn't, then it'll simply be ignored
|
576
|
+
if (argv[i+1] && '-' != argv[i+1][0]) {
|
577
|
+
unknownOptions.push(argv[++i]);
|
578
|
+
}
|
579
|
+
continue;
|
580
|
+
}
|
581
|
+
|
582
|
+
// arg
|
583
|
+
args.push(arg);
|
584
|
+
}
|
585
|
+
|
586
|
+
return { args: args, unknown: unknownOptions };
|
587
|
+
};
|
588
|
+
|
589
|
+
/**
|
590
|
+
* Argument `name` is missing.
|
591
|
+
*
|
592
|
+
* @param {String} name
|
593
|
+
* @api private
|
594
|
+
*/
|
595
|
+
|
596
|
+
Command.prototype.missingArgument = function(name){
|
597
|
+
console.error();
|
598
|
+
console.error(" error: missing required argument `%s'", name);
|
599
|
+
console.error();
|
600
|
+
process.exit(1);
|
601
|
+
};
|
602
|
+
|
603
|
+
/**
|
604
|
+
* `Option` is missing an argument, but received `flag` or nothing.
|
605
|
+
*
|
606
|
+
* @param {String} option
|
607
|
+
* @param {String} flag
|
608
|
+
* @api private
|
609
|
+
*/
|
610
|
+
|
611
|
+
Command.prototype.optionMissingArgument = function(option, flag){
|
612
|
+
console.error();
|
613
|
+
if (flag) {
|
614
|
+
console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag);
|
615
|
+
} else {
|
616
|
+
console.error(" error: option `%s' argument missing", option.flags);
|
617
|
+
}
|
618
|
+
console.error();
|
619
|
+
process.exit(1);
|
620
|
+
};
|
621
|
+
|
622
|
+
/**
|
623
|
+
* Unknown option `flag`.
|
624
|
+
*
|
625
|
+
* @param {String} flag
|
626
|
+
* @api private
|
627
|
+
*/
|
628
|
+
|
629
|
+
Command.prototype.unknownOption = function(flag){
|
630
|
+
console.error();
|
631
|
+
console.error(" error: unknown option `%s'", flag);
|
632
|
+
console.error();
|
633
|
+
process.exit(1);
|
634
|
+
};
|
635
|
+
|
636
|
+
|
637
|
+
/**
|
638
|
+
* Set the program version to `str`.
|
639
|
+
*
|
640
|
+
* This method auto-registers the "-V, --version" flag
|
641
|
+
* which will print the version number when passed.
|
642
|
+
*
|
643
|
+
* @param {String} str
|
644
|
+
* @param {String} flags
|
645
|
+
* @return {Command} for chaining
|
646
|
+
* @api public
|
647
|
+
*/
|
648
|
+
|
649
|
+
Command.prototype.version = function(str, flags){
|
650
|
+
if (0 == arguments.length) return this._version;
|
651
|
+
this._version = str;
|
652
|
+
flags = flags || '-V, --version';
|
653
|
+
this.option(flags, 'output the version number');
|
654
|
+
this.on('version', function(){
|
655
|
+
console.log(str);
|
656
|
+
process.exit(0);
|
657
|
+
});
|
658
|
+
return this;
|
659
|
+
};
|
660
|
+
|
661
|
+
/**
|
662
|
+
* Set the description `str`.
|
663
|
+
*
|
664
|
+
* @param {String} str
|
665
|
+
* @return {String|Command}
|
666
|
+
* @api public
|
667
|
+
*/
|
668
|
+
|
669
|
+
Command.prototype.description = function(str){
|
670
|
+
if (0 == arguments.length) return this._description;
|
671
|
+
this._description = str;
|
672
|
+
return this;
|
673
|
+
};
|
674
|
+
|
675
|
+
/**
|
676
|
+
* Set / get the command usage `str`.
|
677
|
+
*
|
678
|
+
* @param {String} str
|
679
|
+
* @return {String|Command}
|
680
|
+
* @api public
|
681
|
+
*/
|
682
|
+
|
683
|
+
Command.prototype.usage = function(str){
|
684
|
+
var args = this._args.map(function(arg){
|
685
|
+
return arg.required
|
686
|
+
? '<' + arg.name + '>'
|
687
|
+
: '[' + arg.name + ']';
|
688
|
+
});
|
689
|
+
|
690
|
+
var usage = '[options'
|
691
|
+
+ (this.commands.length ? '] [command' : '')
|
692
|
+
+ ']'
|
693
|
+
+ (this._args.length ? ' ' + args : '');
|
694
|
+
|
695
|
+
if (0 == arguments.length) return this._usage || usage;
|
696
|
+
this._usage = str;
|
697
|
+
|
698
|
+
return this;
|
699
|
+
};
|
700
|
+
|
701
|
+
/**
|
702
|
+
* Return the largest option length.
|
703
|
+
*
|
704
|
+
* @return {Number}
|
705
|
+
* @api private
|
706
|
+
*/
|
707
|
+
|
708
|
+
Command.prototype.largestOptionLength = function(){
|
709
|
+
return this.options.reduce(function(max, option){
|
710
|
+
return Math.max(max, option.flags.length);
|
711
|
+
}, 0);
|
712
|
+
};
|
713
|
+
|
714
|
+
/**
|
715
|
+
* Return help for options.
|
716
|
+
*
|
717
|
+
* @return {String}
|
718
|
+
* @api private
|
719
|
+
*/
|
720
|
+
|
721
|
+
Command.prototype.optionHelp = function(){
|
722
|
+
var width = this.largestOptionLength();
|
723
|
+
|
724
|
+
// Prepend the help information
|
725
|
+
return [pad('-h, --help', width) + ' ' + 'output usage information']
|
726
|
+
.concat(this.options.map(function(option){
|
727
|
+
return pad(option.flags, width)
|
728
|
+
+ ' ' + option.description;
|
729
|
+
}))
|
730
|
+
.join('\n');
|
731
|
+
};
|
732
|
+
|
733
|
+
/**
|
734
|
+
* Return command help documentation.
|
735
|
+
*
|
736
|
+
* @return {String}
|
737
|
+
* @api private
|
738
|
+
*/
|
739
|
+
|
740
|
+
Command.prototype.commandHelp = function(){
|
741
|
+
if (!this.commands.length) return '';
|
742
|
+
return [
|
743
|
+
''
|
744
|
+
, ' Commands:'
|
745
|
+
, ''
|
746
|
+
, this.commands.map(function(cmd){
|
747
|
+
var args = cmd._args.map(function(arg){
|
748
|
+
return arg.required
|
749
|
+
? '<' + arg.name + '>'
|
750
|
+
: '[' + arg.name + ']';
|
751
|
+
}).join(' ');
|
752
|
+
|
753
|
+
return pad(cmd._name
|
754
|
+
+ (cmd.options.length
|
755
|
+
? ' [options]'
|
756
|
+
: '') + ' ' + args, 22)
|
757
|
+
+ (cmd.description()
|
758
|
+
? ' ' + cmd.description()
|
759
|
+
: '');
|
760
|
+
}).join('\n').replace(/^/gm, ' ')
|
761
|
+
, ''
|
762
|
+
].join('\n');
|
763
|
+
};
|
764
|
+
|
765
|
+
/**
|
766
|
+
* Return program help documentation.
|
767
|
+
*
|
768
|
+
* @return {String}
|
769
|
+
* @api private
|
770
|
+
*/
|
771
|
+
|
772
|
+
Command.prototype.helpInformation = function(){
|
773
|
+
return [
|
774
|
+
''
|
775
|
+
, ' Usage: ' + this._name + ' ' + this.usage()
|
776
|
+
, '' + this.commandHelp()
|
777
|
+
, ' Options:'
|
778
|
+
, ''
|
779
|
+
, '' + this.optionHelp().replace(/^/gm, ' ')
|
780
|
+
, ''
|
781
|
+
, ''
|
782
|
+
].join('\n');
|
783
|
+
};
|
784
|
+
|
785
|
+
/**
|
786
|
+
* Output help information for this command
|
787
|
+
*
|
788
|
+
* @api public
|
789
|
+
*/
|
790
|
+
|
791
|
+
Command.prototype.outputHelp = function(){
|
792
|
+
process.stdout.write(this.helpInformation());
|
793
|
+
this.emit('--help');
|
794
|
+
};
|
795
|
+
|
796
|
+
/**
|
797
|
+
* Output help information and exit.
|
798
|
+
*
|
799
|
+
* @api public
|
800
|
+
*/
|
801
|
+
|
802
|
+
Command.prototype.help = function(){
|
803
|
+
this.outputHelp();
|
804
|
+
process.exit();
|
805
|
+
};
|
806
|
+
|
807
|
+
/**
|
808
|
+
* Camel-case the given `flag`
|
809
|
+
*
|
810
|
+
* @param {String} flag
|
811
|
+
* @return {String}
|
812
|
+
* @api private
|
813
|
+
*/
|
814
|
+
|
815
|
+
function camelcase(flag) {
|
816
|
+
return flag.split('-').reduce(function(str, word){
|
817
|
+
return str + word[0].toUpperCase() + word.slice(1);
|
818
|
+
});
|
819
|
+
}
|
820
|
+
|
821
|
+
/**
|
822
|
+
* Pad `str` to `width`.
|
823
|
+
*
|
824
|
+
* @param {String} str
|
825
|
+
* @param {Number} width
|
826
|
+
* @return {String}
|
827
|
+
* @api private
|
828
|
+
*/
|
829
|
+
|
830
|
+
function pad(str, width) {
|
831
|
+
var len = Math.max(0, width - str.length);
|
832
|
+
return str + Array(len + 1).join(' ');
|
833
|
+
}
|
834
|
+
|
835
|
+
/**
|
836
|
+
* Output help information if necessary
|
837
|
+
*
|
838
|
+
* @param {Command} command to output help for
|
839
|
+
* @param {Array} array of options to search for -h or --help
|
840
|
+
* @api private
|
841
|
+
*/
|
842
|
+
|
843
|
+
function outputHelpIfNecessary(cmd, options) {
|
844
|
+
options = options || [];
|
845
|
+
for (var i = 0; i < options.length; i++) {
|
846
|
+
if (options[i] == '--help' || options[i] == '-h') {
|
847
|
+
cmd.outputHelp();
|
848
|
+
process.exit(0);
|
849
|
+
}
|
850
|
+
}
|
851
|
+
}
|