flow like the river

This commit is contained in:
root 2025-11-07 00:06:12 +01:00
commit 013fe673f3
42435 changed files with 5764238 additions and 0 deletions

67
VISUALIZACION/node_modules/postcss-svgo/CHANGELOG.md generated vendored Executable file
View file

@ -0,0 +1,67 @@
# 2.1.6
* Resolves an issue where postcss-svgo would convert colours to hex codes
without URL-encoding the `#`.
# 2.1.5
* Bump svgo to v0.7.x.
# 2.1.4
* Fixes an issue where postcss-svgo would throw with some SVGs that were
not properly URI encoded.
# 2.1.3
* Upgrade is-svg to version 2.
# 2.1.2
* Fixes an issue with processing some malformed URIs (thanks to @TrySound).
# 2.1.1
* Bump SVGO to 0.6.1 (thanks to @shinnn).
# 2.1.0
* Adds `encode` option (thanks to @TrySound).
# 2.0.4
* Updates postcss-value-parser to version 3 (thanks to @TrySound).
# 2.0.3
* Uses postcss-value-parser instead of async-replace to reduce cssnano's
download size (thanks to @TrySound).
# 2.0.2
* Fixed an issue where the module was not handling exceptions from
decoding URLs.
* The module will now convert all SVG wrapping quotes to single quotes, which
is consistent with SVGO's output.
# 2.0.1
* Fixed an issue where the `charset` definition was being removed from the
SVG source, breaking IE compatibility (thanks to @ophyros).
# 2.0.0
* Upgraded to PostCSS 5.0.
# 1.1.0
* Adds support for uri-encoded SVG files, for better compatibility
with postcss-svg.
# 1.0.1
* Push ES5 build to npm.
# 1.0.0
* Initial release.

22
VISUALIZACION/node_modules/postcss-svgo/LICENSE-MIT generated vendored Executable file
View file

@ -0,0 +1,22 @@
Copyright (c) Ben Briggs <beneb.info@gmail.com> (http://beneb.info)
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

130
VISUALIZACION/node_modules/postcss-svgo/README.md generated vendored Executable file
View file

@ -0,0 +1,130 @@
# [postcss][postcss]-svgo [![Build Status](https://travis-ci.org/ben-eb/postcss-svgo.svg?branch=master)][ci] [![NPM version](https://badge.fury.io/js/postcss-svgo.svg)][npm] [![Dependency Status](https://gemnasium.com/ben-eb/postcss-svgo.svg)][deps]
> Optimise inline SVG with PostCSS.
## Install
With [npm](https://npmjs.org/package/postcss-svgo) do:
```
npm install postcss-svgo --save
```
## Example
### Input
```css
h1 {
background: url('data:image/svg+xml;charset=utf-8,<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"><circle cx="50" cy="50" r="40" fill="yellow" /></svg>');
}
```
### Output
```css
h1 {
background: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="40" fill="%23ff0"/></svg>');
}
```
## API
### `svgo([options])`
Note that postcss-svgo is an *asynchronous* processor. It cannot be used
like this:
```js
var result = postcss([ svgo() ]).process(css).css;
console.log(result);
```
Instead make sure your PostCSS runner uses the asynchronous API:
```js
postcss([ svgo() ]).process(css).then(function (result) {
console.log(result.css);
});
```
#### options
##### encode
Type: `boolean`
Default: `undefined`
If `true`, it will encode URL-unsafe characters such as `<`, `>` and `&`;
`false` will decode these characters, and `undefined` will neither encode nor
decode the original input. Note that regardless of this setting, `#` will
always be URL-encoded.
##### plugins
Optionally, you can customise the output by specifying the `plugins` option. You
will need to provide the config in comma separated objects, like the example
below. Note that you can either disable the plugin by setting it to `false`,
or pass different options to change the default behaviour.
```js
var postcss = require('postcss');
var svgo = require('postcss-svgo');
var opts = {
plugins: [{
removeDoctype: false
}, {
removeComments: false
}, {
cleanupNumericValues: {
floatPrecision: 2
}
}, {
convertColors: {
names2hex: false,
rgb2hex: false
}
}]
};
postcss([ svgo(opts) ]).process(css).then(function (result) {
console.log(result.css)
});
```
You can view the [full list of plugins here][plugins].
## Usage
See the [PostCSS documentation](https://github.com/postcss/postcss#usage) for
examples for your environment.
## Contributors
Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
| [<img src="https://avatars.githubusercontent.com/u/1282980?v=3" width="100px;"/><br /><sub>Ben Briggs</sub>](http://beneb.info)<br />[💻](https://github.com/ben-eb/postcss-svgo/commits?author=ben-eb) [📖](https://github.com/ben-eb/postcss-svgo/commits?author=ben-eb) 👀 [⚠️](https://github.com/ben-eb/postcss-svgo/commits?author=ben-eb) | [<img src="https://avatars.githubusercontent.com/u/7263665?v=3" width="100px;"/><br /><sub>Sebastian Misch</sub>](https://sebastian-misch.de)<br />[💻](https://github.com/ben-eb/postcss-svgo/commits?author=sbstnmsch) [⚠️](https://github.com/ben-eb/postcss-svgo/commits?author=sbstnmsch) | [<img src="https://avatars.githubusercontent.com/u/11319202?v=3" width="100px;"/><br /><sub>Вячеслав Ляшенко</sub>](https://github.com/ophyros)<br />[💻](https://github.com/ben-eb/postcss-svgo/commits?author=ophyros) [⚠️](https://github.com/ben-eb/postcss-svgo/commits?author=ophyros) | [<img src="https://avatars.githubusercontent.com/u/1131567?v=3" width="100px;"/><br /><sub>shinnn</sub>](https://shinnn.github.io)<br />[💻](https://github.com/ben-eb/postcss-svgo/commits?author=shinnn) | [<img src="https://avatars.githubusercontent.com/u/45338?v=3" width="100px;"/><br /><sub>Jung-gun Lim</sub>](https://github.com/j6lim)<br />[🐛](https://github.com/ben-eb/postcss-svgo/issues?q=author%3Aj6lim) | [<img src="https://avatars.githubusercontent.com/u/5635476?v=3" width="100px;"/><br /><sub>Bogdan Chadkin</sub>](https://github.com/TrySound)<br />[💻](https://github.com/ben-eb/postcss-svgo/commits?author=TrySound) 👀 [⚠️](https://github.com/ben-eb/postcss-svgo/commits?author=TrySound) | [<img src="https://avatars.githubusercontent.com/u/368561?v=3" width="100px;"/><br /><sub>Piotr Walczyszyn</sub>](http://outof.me)<br />[🐛](https://github.com/ben-eb/postcss-svgo/issues?q=author%3Apwalczyszyn) |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors] specification. Contributions of
any kind welcome!
## License
MIT © [Ben Briggs](http://beneb.info)
[all-contributors]: https://github.com/kentcdodds/all-contributors
[ci]: https://travis-ci.org/ben-eb/postcss-svgo
[deps]: https://gemnasium.com/ben-eb/postcss-svgo
[npm]: http://badge.fury.io/js/postcss-svgo
[postcss]: https://github.com/postcss/postcss
[plugins]: https://github.com/svg/svgo/tree/master/plugins

108
VISUALIZACION/node_modules/postcss-svgo/dist/index.js generated vendored Executable file
View file

@ -0,0 +1,108 @@
'use strict';
exports.__esModule = true;
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _postcss = require('postcss');
var _postcss2 = _interopRequireDefault(_postcss);
var _postcssValueParser = require('postcss-value-parser');
var _postcssValueParser2 = _interopRequireDefault(_postcssValueParser);
var _svgo = require('svgo');
var _svgo2 = _interopRequireDefault(_svgo);
var _isSvg = require('is-svg');
var _isSvg2 = _interopRequireDefault(_isSvg);
var _url = require('./lib/url');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var PLUGIN = 'postcss-svgo';
var dataURI = /data:image\/svg\+xml(;(charset=)?utf-8)?,/;
function minifyPromise(svgo, decl, opts) {
var promises = [];
decl.value = (0, _postcssValueParser2.default)(decl.value).walk(function (node) {
if (node.type !== 'function' || node.value !== 'url' || !node.nodes.length) {
return;
}
var value = node.nodes[0].value;
var decodedUri = void 0,
isUriEncoded = void 0;
try {
decodedUri = (0, _url.decode)(value);
isUriEncoded = decodedUri !== value;
} catch (e) {
// Swallow exception if we cannot decode the value
isUriEncoded = false;
}
if (isUriEncoded) {
value = decodedUri;
}
if (opts.encode !== undefined) {
isUriEncoded = opts.encode;
}
var svg = value.replace(dataURI, '');
if (!(0, _isSvg2.default)(svg)) {
return;
}
promises.push(new Promise(function (resolve, reject) {
return svgo.optimize(svg, function (result) {
if (result.error) {
return reject(PLUGIN + ': ' + result.error);
}
var data = isUriEncoded ? (0, _url.encode)(result.data) : result.data;
// Should always encode # otherwise we yield a broken SVG
// in Firefox (works in Chrome however). See this issue:
// https://github.com/ben-eb/cssnano/issues/245
data = data.replace(/#/g, '%23');
node.nodes[0] = _extends({}, node.nodes[0], {
value: 'data:image/svg+xml;charset=utf-8,' + data,
quote: isUriEncoded ? '"' : '\'',
type: 'string',
before: '',
after: ''
});
return resolve();
});
}));
return false;
});
return Promise.all(promises).then(function () {
return decl.value = decl.value.toString();
});
}
exports.default = _postcss2.default.plugin(PLUGIN, function () {
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var svgo = new _svgo2.default(opts);
return function (css) {
return new Promise(function (resolve, reject) {
var promises = [];
css.walkDecls(function (decl) {
if (dataURI.test(decl.value)) {
promises.push(minifyPromise(svgo, decl, opts));
}
});
return Promise.all(promises).then(resolve, reject);
});
};
});
module.exports = exports['default'];

9
VISUALIZACION/node_modules/postcss-svgo/dist/lib/url.js generated vendored Executable file
View file

@ -0,0 +1,9 @@
'use strict';
exports.__esModule = true;
exports.encode = encode;
function encode(data) {
return data.replace(/"/g, '\'').replace(/%/g, '%25').replace(/</g, '%3C').replace(/>/g, '%3E').replace(/&/g, '%26').replace(/#/g, '%23').replace(/\s+/g, ' ');
};
var decode = exports.decode = decodeURIComponent;

View file

@ -0,0 +1 @@
../csso/bin/csso

View file

@ -0,0 +1 @@
../esprima/bin/esparse.js

View file

@ -0,0 +1 @@
../esprima/bin/esvalidate.js

View file

@ -0,0 +1 @@
../js-yaml/bin/js-yaml.js

View file

@ -0,0 +1 @@
../svgo/bin/svgo

View file

@ -0,0 +1,4 @@
'use strict';
module.exports = function () {
return /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]/g;
};

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,64 @@
{
"name": "ansi-regex",
"version": "2.1.1",
"description": "Regular expression for matching ANSI escape codes",
"license": "MIT",
"repository": "chalk/ansi-regex",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"maintainers": [
"Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)",
"Joshua Appelman <jappelman@xebia.com> (jbnicolai.com)",
"JD Ballard <i.am.qix@gmail.com> (github.com/qix-)"
],
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "xo && ava --verbose",
"view-supported": "node fixtures/view-codes.js"
},
"files": [
"index.js"
],
"keywords": [
"ansi",
"styles",
"color",
"colour",
"colors",
"terminal",
"console",
"cli",
"string",
"tty",
"escape",
"formatting",
"rgb",
"256",
"shell",
"xterm",
"command-line",
"text",
"regex",
"regexp",
"re",
"match",
"test",
"find",
"pattern"
],
"devDependencies": {
"ava": "0.17.0",
"xo": "0.16.0"
},
"xo": {
"rules": {
"guard-for-in": 0,
"no-loop-func": 0
}
}
}

View file

@ -0,0 +1,39 @@
# ansi-regex [![Build Status](https://travis-ci.org/chalk/ansi-regex.svg?branch=master)](https://travis-ci.org/chalk/ansi-regex)
> Regular expression for matching [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
## Install
```
$ npm install --save ansi-regex
```
## Usage
```js
const ansiRegex = require('ansi-regex');
ansiRegex().test('\u001b[4mcake\u001b[0m');
//=> true
ansiRegex().test('cake');
//=> false
'\u001b[4mcake\u001b[0m'.match(ansiRegex());
//=> ['\u001b[4m', '\u001b[0m']
```
## FAQ
### Why do you test for codes not in the ECMA 48 standard?
Some of the codes we run as a test are codes that we acquired finding various lists of non-standard or manufacturer specific codes. If I recall correctly, we test for both standard and non-standard codes, as most of them follow the same or similar format and can be safely matched in strings without the risk of removing actual string content. There are a few non-standard control codes that do not follow the traditional format (i.e. they end in numbers) thus forcing us to exclude them from the test because we cannot reliably match them.
On the historical side, those ECMA standards were established in the early 90's whereas the VT100, for example, was designed in the mid/late 70's. At that point in time, control codes were still pretty ungoverned and engineers used them for a multitude of things, namely to activate hardware ports that may have been proprietary. Somewhere else you see a similar 'anarchy' of codes is in the x86 architecture for processors; there are a ton of "interrupts" that can mean different things on certain brands of processors, most of which have been phased out.
## License
MIT © [Sindre Sorhus](http://sindresorhus.com)

View file

@ -0,0 +1,65 @@
'use strict';
function assembleStyles () {
var styles = {
modifiers: {
reset: [0, 0],
bold: [1, 22], // 21 isn't widely supported and 22 does the same thing
dim: [2, 22],
italic: [3, 23],
underline: [4, 24],
inverse: [7, 27],
hidden: [8, 28],
strikethrough: [9, 29]
},
colors: {
black: [30, 39],
red: [31, 39],
green: [32, 39],
yellow: [33, 39],
blue: [34, 39],
magenta: [35, 39],
cyan: [36, 39],
white: [37, 39],
gray: [90, 39]
},
bgColors: {
bgBlack: [40, 49],
bgRed: [41, 49],
bgGreen: [42, 49],
bgYellow: [43, 49],
bgBlue: [44, 49],
bgMagenta: [45, 49],
bgCyan: [46, 49],
bgWhite: [47, 49]
}
};
// fix humans
styles.colors.grey = styles.colors.gray;
Object.keys(styles).forEach(function (groupName) {
var group = styles[groupName];
Object.keys(group).forEach(function (styleName) {
var style = group[styleName];
styles[styleName] = group[styleName] = {
open: '\u001b[' + style[0] + 'm',
close: '\u001b[' + style[1] + 'm'
};
});
Object.defineProperty(styles, groupName, {
value: group,
enumerable: false
});
});
return styles;
}
Object.defineProperty(module, 'exports', {
enumerable: true,
get: assembleStyles
});

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,50 @@
{
"name": "ansi-styles",
"version": "2.2.1",
"description": "ANSI escape codes for styling strings in the terminal",
"license": "MIT",
"repository": "chalk/ansi-styles",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"maintainers": [
"Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)",
"Joshua Appelman <jappelman@xebia.com> (jbnicolai.com)"
],
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha"
},
"files": [
"index.js"
],
"keywords": [
"ansi",
"styles",
"color",
"colour",
"colors",
"terminal",
"console",
"cli",
"string",
"tty",
"escape",
"formatting",
"rgb",
"256",
"shell",
"xterm",
"log",
"logging",
"command-line",
"text"
],
"devDependencies": {
"mocha": "*"
}
}

View file

@ -0,0 +1,86 @@
# ansi-styles [![Build Status](https://travis-ci.org/chalk/ansi-styles.svg?branch=master)](https://travis-ci.org/chalk/ansi-styles)
> [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal
You probably want the higher-level [chalk](https://github.com/chalk/chalk) module for styling your strings.
![](screenshot.png)
## Install
```
$ npm install --save ansi-styles
```
## Usage
```js
var ansi = require('ansi-styles');
console.log(ansi.green.open + 'Hello world!' + ansi.green.close);
```
## API
Each style has an `open` and `close` property.
## Styles
### Modifiers
- `reset`
- `bold`
- `dim`
- `italic` *(not widely supported)*
- `underline`
- `inverse`
- `hidden`
- `strikethrough` *(not widely supported)*
### Colors
- `black`
- `red`
- `green`
- `yellow`
- `blue`
- `magenta`
- `cyan`
- `white`
- `gray`
### Background colors
- `bgBlack`
- `bgRed`
- `bgGreen`
- `bgYellow`
- `bgBlue`
- `bgMagenta`
- `bgCyan`
- `bgWhite`
## Advanced usage
By default you get a map of styles, but the styles are also available as groups. They are non-enumerable so they don't show up unless you access them explicitly. This makes it easier to expose only a subset in a higher-level module.
- `ansi.modifiers`
- `ansi.colors`
- `ansi.bgColors`
###### Example
```js
console.log(ansi.colors.green.open);
```
## License
MIT © [Sindre Sorhus](http://sindresorhus.com)

View file

@ -0,0 +1,116 @@
'use strict';
var escapeStringRegexp = require('escape-string-regexp');
var ansiStyles = require('ansi-styles');
var stripAnsi = require('strip-ansi');
var hasAnsi = require('has-ansi');
var supportsColor = require('supports-color');
var defineProps = Object.defineProperties;
var isSimpleWindowsTerm = process.platform === 'win32' && !/^xterm/i.test(process.env.TERM);
function Chalk(options) {
// detect mode if not set manually
this.enabled = !options || options.enabled === undefined ? supportsColor : options.enabled;
}
// use bright blue on Windows as the normal blue color is illegible
if (isSimpleWindowsTerm) {
ansiStyles.blue.open = '\u001b[94m';
}
var styles = (function () {
var ret = {};
Object.keys(ansiStyles).forEach(function (key) {
ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g');
ret[key] = {
get: function () {
return build.call(this, this._styles.concat(key));
}
};
});
return ret;
})();
var proto = defineProps(function chalk() {}, styles);
function build(_styles) {
var builder = function () {
return applyStyle.apply(builder, arguments);
};
builder._styles = _styles;
builder.enabled = this.enabled;
// __proto__ is used because we must return a function, but there is
// no way to create a function with a different prototype.
/* eslint-disable no-proto */
builder.__proto__ = proto;
return builder;
}
function applyStyle() {
// support varags, but simply cast to string in case there's only one arg
var args = arguments;
var argsLen = args.length;
var str = argsLen !== 0 && String(arguments[0]);
if (argsLen > 1) {
// don't slice `arguments`, it prevents v8 optimizations
for (var a = 1; a < argsLen; a++) {
str += ' ' + args[a];
}
}
if (!this.enabled || !str) {
return str;
}
var nestedStyles = this._styles;
var i = nestedStyles.length;
// Turns out that on Windows dimmed gray text becomes invisible in cmd.exe,
// see https://github.com/chalk/chalk/issues/58
// If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop.
var originalDim = ansiStyles.dim.open;
if (isSimpleWindowsTerm && (nestedStyles.indexOf('gray') !== -1 || nestedStyles.indexOf('grey') !== -1)) {
ansiStyles.dim.open = '';
}
while (i--) {
var code = ansiStyles[nestedStyles[i]];
// Replace any instances already present with a re-opening code
// otherwise only the part of the string until said closing code
// will be colored, and the rest will simply be 'plain'.
str = code.open + str.replace(code.closeRe, code.open) + code.close;
}
// Reset the original 'dim' if we changed it to work around the Windows dimmed gray issue.
ansiStyles.dim.open = originalDim;
return str;
}
function init() {
var ret = {};
Object.keys(styles).forEach(function (name) {
ret[name] = {
get: function () {
return build.call(this, [name]);
}
};
});
return ret;
}
defineProps(Chalk.prototype, init());
module.exports = new Chalk();
module.exports.styles = ansiStyles;
module.exports.hasColor = hasAnsi;
module.exports.stripColor = stripAnsi;
module.exports.supportsColor = supportsColor;

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,50 @@
'use strict';
var argv = process.argv;
var terminator = argv.indexOf('--');
var hasFlag = function (flag) {
flag = '--' + flag;
var pos = argv.indexOf(flag);
return pos !== -1 && (terminator !== -1 ? pos < terminator : true);
};
module.exports = (function () {
if ('FORCE_COLOR' in process.env) {
return true;
}
if (hasFlag('no-color') ||
hasFlag('no-colors') ||
hasFlag('color=false')) {
return false;
}
if (hasFlag('color') ||
hasFlag('colors') ||
hasFlag('color=true') ||
hasFlag('color=always')) {
return true;
}
if (process.stdout && !process.stdout.isTTY) {
return false;
}
if (process.platform === 'win32') {
return true;
}
if ('COLORTERM' in process.env) {
return true;
}
if (process.env.TERM === 'dumb') {
return false;
}
if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) {
return true;
}
return false;
})();

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,49 @@
{
"name": "supports-color",
"version": "2.0.0",
"description": "Detect whether a terminal supports color",
"license": "MIT",
"repository": "chalk/supports-color",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"maintainers": [
"Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)",
"Joshua Appelman <jappelman@xebia.com> (jbnicolai.com)"
],
"engines": {
"node": ">=0.8.0"
},
"scripts": {
"test": "mocha"
},
"files": [
"index.js"
],
"keywords": [
"color",
"colour",
"colors",
"terminal",
"console",
"cli",
"ansi",
"styles",
"tty",
"rgb",
"256",
"shell",
"xterm",
"command-line",
"support",
"supports",
"capability",
"detect"
],
"devDependencies": {
"mocha": "*",
"require-uncached": "^1.0.2"
}
}

View file

@ -0,0 +1,36 @@
# supports-color [![Build Status](https://travis-ci.org/chalk/supports-color.svg?branch=master)](https://travis-ci.org/chalk/supports-color)
> Detect whether a terminal supports color
## Install
```
$ npm install --save supports-color
```
## Usage
```js
var supportsColor = require('supports-color');
if (supportsColor) {
console.log('Terminal supports color');
}
```
It obeys the `--color` and `--no-color` CLI flags.
For situations where using `--color` is not possible, add an environment variable `FORCE_COLOR` with any value to force color. Trumps `--no-color`.
## Related
- [supports-color-cli](https://github.com/chalk/supports-color-cli) - CLI for this module
- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right
## License
MIT © [Sindre Sorhus](http://sindresorhus.com)

View file

@ -0,0 +1,70 @@
{
"name": "chalk",
"version": "1.1.3",
"description": "Terminal string styling done right. Much color.",
"license": "MIT",
"repository": "chalk/chalk",
"maintainers": [
"Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)",
"Joshua Appelman <jappelman@xebia.com> (jbnicolai.com)",
"JD Ballard <i.am.qix@gmail.com> (github.com/qix-)"
],
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "xo && mocha",
"bench": "matcha benchmark.js",
"coverage": "nyc npm test && nyc report",
"coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls"
},
"files": [
"index.js"
],
"keywords": [
"color",
"colour",
"colors",
"terminal",
"console",
"cli",
"string",
"str",
"ansi",
"style",
"styles",
"tty",
"formatting",
"rgb",
"256",
"shell",
"xterm",
"log",
"logging",
"command-line",
"text"
],
"dependencies": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
},
"devDependencies": {
"coveralls": "^2.11.2",
"matcha": "^0.6.0",
"mocha": "*",
"nyc": "^3.0.0",
"require-uncached": "^1.0.2",
"resolve-from": "^1.0.0",
"semver": "^4.3.3",
"xo": "*"
},
"xo": {
"envs": [
"node",
"mocha"
]
}
}

View file

@ -0,0 +1,213 @@
<h1 align="center">
<br>
<br>
<img width="360" src="https://cdn.rawgit.com/chalk/chalk/19935d6484811c5e468817f846b7b3d417d7bf4a/logo.svg" alt="chalk">
<br>
<br>
<br>
</h1>
> Terminal string styling done right
[![Build Status](https://travis-ci.org/chalk/chalk.svg?branch=master)](https://travis-ci.org/chalk/chalk)
[![Coverage Status](https://coveralls.io/repos/chalk/chalk/badge.svg?branch=master)](https://coveralls.io/r/chalk/chalk?branch=master)
[![](http://img.shields.io/badge/unicorn-approved-ff69b4.svg)](https://www.youtube.com/watch?v=9auOCbH5Ns4)
[colors.js](https://github.com/Marak/colors.js) used to be the most popular string styling module, but it has serious deficiencies like extending `String.prototype` which causes all kinds of [problems](https://github.com/yeoman/yo/issues/68). Although there are other ones, they either do too much or not enough.
**Chalk is a clean and focused alternative.**
![](https://github.com/chalk/ansi-styles/raw/master/screenshot.png)
## Why
- Highly performant
- Doesn't extend `String.prototype`
- Expressive API
- Ability to nest styles
- Clean and focused
- Auto-detects color support
- Actively maintained
- [Used by ~4500 modules](https://www.npmjs.com/browse/depended/chalk) as of July 15, 2015
## Install
```
$ npm install --save chalk
```
## Usage
Chalk comes with an easy to use composable API where you just chain and nest the styles you want.
```js
var chalk = require('chalk');
// style a string
chalk.blue('Hello world!');
// combine styled and normal strings
chalk.blue('Hello') + 'World' + chalk.red('!');
// compose multiple styles using the chainable API
chalk.blue.bgRed.bold('Hello world!');
// pass in multiple arguments
chalk.blue('Hello', 'World!', 'Foo', 'bar', 'biz', 'baz');
// nest styles
chalk.red('Hello', chalk.underline.bgBlue('world') + '!');
// nest styles of the same type even (color, underline, background)
chalk.green(
'I am a green line ' +
chalk.blue.underline.bold('with a blue substring') +
' that becomes green again!'
);
```
Easily define your own themes.
```js
var chalk = require('chalk');
var error = chalk.bold.red;
console.log(error('Error!'));
```
Take advantage of console.log [string substitution](http://nodejs.org/docs/latest/api/console.html#console_console_log_data).
```js
var name = 'Sindre';
console.log(chalk.green('Hello %s'), name);
//=> Hello Sindre
```
## API
### chalk.`<style>[.<style>...](string, [string...])`
Example: `chalk.red.bold.underline('Hello', 'world');`
Chain [styles](#styles) and call the last one as a method with a string argument. Order doesn't matter, and later styles take precedent in case of a conflict. This simply means that `Chalk.red.yellow.green` is equivalent to `Chalk.green`.
Multiple arguments will be separated by space.
### chalk.enabled
Color support is automatically detected, but you can override it by setting the `enabled` property. You should however only do this in your own code as it applies globally to all chalk consumers.
If you need to change this in a reusable module create a new instance:
```js
var ctx = new chalk.constructor({enabled: false});
```
### chalk.supportsColor
Detect whether the terminal [supports color](https://github.com/chalk/supports-color). Used internally and handled for you, but exposed for convenience.
Can be overridden by the user with the flags `--color` and `--no-color`. For situations where using `--color` is not possible, add an environment variable `FORCE_COLOR` with any value to force color. Trumps `--no-color`.
### chalk.styles
Exposes the styles as [ANSI escape codes](https://github.com/chalk/ansi-styles).
Generally not useful, but you might need just the `.open` or `.close` escape code if you're mixing externally styled strings with your own.
```js
var chalk = require('chalk');
console.log(chalk.styles.red);
//=> {open: '\u001b[31m', close: '\u001b[39m'}
console.log(chalk.styles.red.open + 'Hello' + chalk.styles.red.close);
```
### chalk.hasColor(string)
Check whether a string [has color](https://github.com/chalk/has-ansi).
### chalk.stripColor(string)
[Strip color](https://github.com/chalk/strip-ansi) from a string.
Can be useful in combination with `.supportsColor` to strip color on externally styled text when it's not supported.
Example:
```js
var chalk = require('chalk');
var styledString = getText();
if (!chalk.supportsColor) {
styledString = chalk.stripColor(styledString);
}
```
## Styles
### Modifiers
- `reset`
- `bold`
- `dim`
- `italic` *(not widely supported)*
- `underline`
- `inverse`
- `hidden`
- `strikethrough` *(not widely supported)*
### Colors
- `black`
- `red`
- `green`
- `yellow`
- `blue` *(on Windows the bright version is used as normal blue is illegible)*
- `magenta`
- `cyan`
- `white`
- `gray`
### Background colors
- `bgBlack`
- `bgRed`
- `bgGreen`
- `bgYellow`
- `bgBlue`
- `bgMagenta`
- `bgCyan`
- `bgWhite`
## 256-colors
Chalk does not support anything other than the base eight colors, which guarantees it will work on all terminals and systems. Some terminals, specifically `xterm` compliant ones, will support the full range of 8-bit colors. For this the lower level [ansi-256-colors](https://github.com/jbnicolai/ansi-256-colors) package can be used.
## Windows
If you're on Windows, do yourself a favor and use [`cmder`](http://bliker.github.io/cmder/) instead of `cmd.exe`.
## Related
- [chalk-cli](https://github.com/chalk/chalk-cli) - CLI for this module
- [ansi-styles](https://github.com/chalk/ansi-styles/) - ANSI escape codes for styling strings in the terminal
- [supports-color](https://github.com/chalk/supports-color/) - Detect whether a terminal supports color
- [strip-ansi](https://github.com/chalk/strip-ansi) - Strip ANSI escape codes
- [has-ansi](https://github.com/chalk/has-ansi) - Check if a string has ANSI escape codes
- [ansi-regex](https://github.com/chalk/ansi-regex) - Regular expression for matching ANSI escape codes
- [wrap-ansi](https://github.com/chalk/wrap-ansi) - Wordwrap a string with ANSI escape codes
## License
MIT © [Sindre Sorhus](http://sindresorhus.com)

View file

@ -0,0 +1,6 @@
.idea
*.iml
node_modules/
!node_modules/coa*.js
lib-cov/
html-report/

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,9 @@
language: node_js
node_js:
- "0.10"
- "0.11"
matrix:
allow_failures:
- node_js: 0.11

View file

@ -0,0 +1,34 @@
BIN = ./node_modules/.bin
.PHONY: all
all: lib
lib: $(foreach s,$(wildcard src/*.coffee),$(patsubst src/%.coffee,lib/%.js,$s))
lib-cov: clean-coverage lib
$(BIN)/istanbul instrument --output lib-cov --no-compact --variable global.__coverage__ lib
lib/%.js: src/%.coffee
$(BIN)/coffee -cb -o $(@D) $<
.PHONY: test
test: lib
$(BIN)/mocha
.PHONY: coverage
coverage: lib-cov
COVER=1 $(BIN)/mocha --reporter mocha-istanbul
@echo
@echo Open html-report/index.html file in your browser
.PHONY: watch
watch:
$(BIN)/coffee --watch --bare --output lib src/*.coffee
.PHONY: clean
clean: clean-coverage
.PHONY: clean-coverage
clean-coverage:
-rm -rf lib-cov
-rm -rf html-report

View file

@ -0,0 +1,322 @@
# Command-Option-Argument
[![build status](https://secure.travis-ci.org/veged/coa.png)](http://travis-ci.org/veged/coa)
## What is it?
COA is a parser for command line options that aim to get maximum profit from formalization your program API.
Once you write definition in terms of commands, options and arguments you automaticaly get:
* Command line help text
* Program API for use COA-based programs as modules
* Shell completion
### Other features
* Rich types for options and arguments, such as arrays, boolean flags and required
* Commands can be async throught using promising (powered by [Q](https://github.com/kriskowal/q))
* Easy submoduling some existing commands to new top-level one
* Combined validation and complex parsing of values
### TODO
* Localization
* Shell-mode
* Configs
* Aliases
* Defaults
## Examples
````javascript
require('coa').Cmd() // main (top level) command declaration
.name(process.argv[1]) // set top level command name from program name
.title('My awesome command line util') // title for use in text messages
.helpful() // make command "helpful", i.e. options -h --help with usage message
.opt() // add some option
.name('version') // name for use in API
.title('Version') // title for use in text messages
.short('v') // short key: -v
.long('version') // long key: --version
.flag() // for options without value
.act(function(opts) { // add action for option
// return message as result of action
return JSON.parse(require('fs').readFileSync(__dirname + '/package.json'))
.version;
})
.end() // end option chain and return to main command
.cmd().name('subcommand').apply(require('./subcommand').COA).end() // load subcommand from module
.cmd() // inplace subcommand declaration
.name('othercommand').title('Awesome other subcommand').helpful()
.opt()
.name('input').title('input file, required')
.short('i').long('input')
.val(function(v) { // validator function, also for translate simple values
return require('fs').createReadStream(v) })
.req() // make option required
.end() // end option chain and return to command
.end() // end subcommand chain and return to parent command
.run(process.argv.slice(2)); // parse and run on process.argv
````
````javascript
// subcommand.js
exports.COA = function() {
this
.title('Awesome subcommand').helpful()
.opt()
.name('output').title('output file')
.short('o').long('output')
.output() // use default preset for "output" option declaration
.end()
};
````
## API reference
### Cmd
Command is a top level entity. Commands may have options and arguments.
#### Cmd.api
Returns object containing all its subcommands as methods to use from other programs.<br>
**@returns** *{Object}*
#### Cmd.name
Set a canonical command identifier to be used anywhere in the API.<br>
**@param** *String* `_name` command name<br>
**@returns** *COA.Cmd* `this` instance (for chainability)
#### Cmd.title
Set a long description for command to be used anywhere in text messages.<br>
**@param** *String* `_title` command title<br>
**@returns** *COA.Cmd* `this` instance (for chainability)
#### Cmd.cmd
Create new or add existing subcommand for current command.<br>
**@param** *COA.Cmd* `[cmd]` existing command instance<br>
**@returns** *COA.Cmd* new or added subcommand instance
#### Cmd.opt
Create option for current command.<br>
**@returns** *COA.Opt* `new` option instance
#### Cmd.arg
Create argument for current command.<br>
**@returns** *COA.Opt* `new` argument instance
#### Cmd.act
Add (or set) action for current command.<br>
**@param** *Function* `act` action function,
invoked in the context of command instance
and has the parameters:<br>
- *Object* `opts` parsed options<br>
- *Array* `args` parsed arguments<br>
- *Object* `res` actions result accumulator<br>
It can return rejected promise by Cmd.reject (in case of error)
or any other value treated as result.<br>
**@param** *{Boolean}* [force=false] flag for set action instead add to existings<br>
**@returns** *COA.Cmd* `this` instance (for chainability)
#### Cmd.apply
Apply function with arguments in context of command instance.<br>
**@param** *Function* `fn`<br>
**@param** *Array* `args`<br>
**@returns** *COA.Cmd* `this` instance (for chainability)
#### Cmd.comp
Set custom additional completion for current command.<br>
**@param** *Function* `fn` completion generation function,
invoked in the context of command instance.
Accepts parameters:<br>
- *Object* `opts` completion options<br>
It can return promise or any other value treated as result.<br>
**@returns** *COA.Cmd* `this` instance (for chainability)
#### Cmd.helpful
Make command "helpful", i.e. add -h --help flags for print usage.<br>
**@returns** *COA.Cmd* `this` instance (for chainability)
#### Cmd.completable
Adds shell completion to command, adds "completion" subcommand, that makes all the magic.<br>
Must be called only on root command.<br>
**@returns** *COA.Cmd* `this` instance (for chainability)
#### Cmd.usage
Build full usage text for current command instance.<br>
**@returns** *String* `usage` text
#### Cmd.run
Parse arguments from simple format like NodeJS process.argv
and run ahead current program, i.e. call process.exit when all actions done.<br>
**@param** *Array* `argv`<br>
**@returns** *COA.Cmd* `this` instance (for chainability)
#### Cmd.invoke
Invoke specified (or current) command using provided options and arguments.<br>
**@param** *String|Array* `cmds` subcommand to invoke (optional)<br>
**@param** *Object* `opts` command options (optional)<br>
**@param** *Object* `args` command arguments (optional)<br>
**@returns** *Q.Promise*
#### Cmd.reject
Return reject of actions results promise.<br>
Use in .act() for return with error.<br>
**@param** *Object* `reason` reject reason<br>
You can customize toString() method and exitCode property
of reason object.<br>
**@returns** *Q.promise* rejected promise
#### Cmd.end
Finish chain for current subcommand and return parent command instance.<br>
**@returns** *COA.Cmd* `parent` command
### Opt
Option is a named entity. Options may have short and long keys for use from command line.<br>
**@namespace**<br>
**@class** Presents option
#### Opt.name
Set a canonical option identifier to be used anywhere in the API.<br>
**@param** *String* `_name` option name<br>
**@returns** *COA.Opt* `this` instance (for chainability)
#### Opt.title
Set a long description for option to be used anywhere in text messages.<br>
**@param** *String* `_title` option title<br>
**@returns** *COA.Opt* `this` instance (for chainability)
#### Opt.short
Set a short key for option to be used with one hyphen from command line.<br>
**@param** *String* `_short`<br>
**@returns** *COA.Opt* `this` instance (for chainability)
#### Opt.long
Set a short key for option to be used with double hyphens from command line.<br>
**@param** *String* `_long`<br>
**@returns** *COA.Opt* `this` instance (for chainability)
#### Opt.flag
Make an option boolean, i.e. option without value.<br>
**@returns** *COA.Opt* `this` instance (for chainability)
#### Opt.arr
Makes an option accepts multiple values.<br>
Otherwise, the value will be used by the latter passed.<br>
**@returns** *COA.Opt* `this` instance (for chainability)
#### Opt.req
Makes an option req.<br>
**@returns** *COA.Opt* `this` instance (for chainability)
#### Opt.only
Makes an option to act as a command,
i.e. program will exit just after option action.<br>
**@returns** *COA.Opt* `this` instance (for chainability)
#### Opt.val
Set a validation (or value) function for argument.<br>
Value from command line passes through before becoming available from API.<br>
Using for validation and convertion simple types to any values.<br>
**@param** *Function* `_val` validating function,
invoked in the context of option instance
and has one parameter with value from command line<br>
**@returns** *COA.Opt* `this` instance (for chainability)
#### Opt.def
Set a default value for option.
Default value passed through validation function as ordinary value.<br>
**@param** *Object* `_def`<br>
**@returns** *COA.Opt* `this` instance (for chainability)
#### Opt.input
Make option value inputting stream.
It's add useful validation and shortcut for STDIN.
**@returns** *{COA.Opt}* `this` instance (for chainability)
#### Opt.output
Make option value outputing stream.<br>
It's add useful validation and shortcut for STDOUT.<br>
**@returns** *COA.Opt* `this` instance (for chainability)
#### Opt.act
Add action for current option command.
This action is performed if the current option
is present in parsed options (with any value).<br>
**@param** *Function* `act` action function,
invoked in the context of command instance
and has the parameters:<br>
- *Object* `opts` parsed options<br>
- *Array* `args` parsed arguments<br>
- *Object* `res` actions result accumulator<br>
It can return rejected promise by Cmd.reject (in case of error)
or any other value treated as result.<br>
**@returns** *COA.Opt* `this` instance (for chainability)
#### Opt.comp
Set custom additional completion for current option.<br>
**@param** *Function* `fn` completion generation function,
invoked in the context of command instance.
Accepts parameters:<br>
- *Object* `opts` completion options<br>
It can return promise or any other value treated as result.<br>
**@returns** *COA.Opt* `this` instance (for chainability)
#### Opt.end
Finish chain for current option and return parent command instance.<br>
**@returns** *COA.Cmd* `parent` command
### Arg
Argument is a unnamed entity.<br>
From command line arguments passed as list of unnamed values.
#### Arg.name
Set a canonical argument identifier to be used anywhere in text messages.<br>
**@param** *String* `_name` argument name<br>
**@returns** *COA.Arg* `this` instance (for chainability)
#### Arg.title
Set a long description for argument to be used anywhere in text messages.<br>
**@param** *String* `_title` argument title<br>
**@returns** *COA.Arg* `this` instance (for chainability)
#### Arg.arr
Makes an argument accepts multiple values.<br>
Otherwise, the value will be used by the latter passed.<br>
**@returns** *COA.Arg* `this` instance (for chainability)
#### Arg.req
Makes an argument req.<br>
**@returns** *COA.Arg* `this` instance (for chainability)
#### Arg.val
Set a validation (or value) function for argument.<br>
Value from command line passes through before becoming available from API.<br>
Using for validation and convertion simple types to any values.<br>
**@param** *Function* `_val` validating function,
invoked in the context of argument instance
and has one parameter with value from command line<br>
**@returns** *COA.Arg* `this` instance (for chainability)
#### Arg.def
Set a default value for argument.
Default value passed through validation function as ordinary value.<br>
**@param** *Object* `_def`<br>
**@returns** *COA.Arg* `this` instance (for chainability)
#### Arg.output
Make argument value outputing stream.<br>
It's add useful validation and shortcut for STDOUT.<br>
**@returns** *COA.Arg* `this` instance (for chainability)
#### Arg.comp
Set custom additional completion for current argument.<br>
**@param** *Function* `fn` completion generation function,
invoked in the context of command instance.
Accepts parameters:<br>
- *Object* `opts` completion options<br>
It can return promise or any other value treated as result.<br>
**@returns** *COA.Arg* `this` instance (for chainability)
#### Arg.end
Finish chain for current option and return parent command instance.<br>
**@returns** *COA.Cmd* `parent` command

View file

@ -0,0 +1,316 @@
# Command-Option-Argument
[![build status](https://secure.travis-ci.org/veged/coa.png)](http://travis-ci.org/veged/coa)
## Что это?
COA — парсер параметров командной строки, позволяющий извлечь максимум пользы от формального API вашей программы.
Как только вы опишете определение в терминах команд, параметров и аргументов, вы автоматически получите:
* Справку для командной строки
* API для использования программы как модуля в COA-совместимых программах
* Автодополнение для командной строки
### Прочие возможности
* Широкий выбор настроек для параметров и аргументов, включая множественные значения, логические значения и обязательность параметров
* Возможность асинхронного исполнения команд, используя промисы (используется библиотека [Q](https://github.com/kriskowal/q))
* Простота использования существующих команд как подмодулей для новых команд
* Комбинированная валидация и анализ сложных значений
## Примеры
````javascript
require('coa').Cmd() // декларация команды верхнего уровня
.name(process.argv[1]) // имя команды верхнего уровня, берем из имени программы
.title('Жутко полезная утилита для командной строки') // название для использования в справке и сообщениях
.helpful() // добавляем поддержку справки командной строки (-h, --help)
.opt() // добавляем параметр
.name('version') // имя параметра для использования в API
.title('Version') // текст для вывода в сообщениях
.short('v') // короткое имя параметра: -v
.long('version') // длинное имя параметра: --version
.flag() // параметр не требует ввода значения
.act(function(opts) { // действия при вызове аргумента
// результатом является вывод текстового сообщения
return JSON.parse(require('fs').readFileSync(__dirname + '/package.json'))
.version;
})
.end() // завершаем определение параметра и возвращаемся к определению верхнего уровня
.cmd().name('subcommand').apply(require('./subcommand').COA).end() // загрузка подкоманды из модуля
.cmd() // добавляем еще одну подкоманду
.name('othercommand').title('Еще одна полезная подпрограмма').helpful()
.opt()
.name('input').title('input file, required')
.short('i').long('input')
.val(function(v) { // функция-валидатор, также может использоваться для трансформации значений параметров
return require('fs').createReadStream(v) })
.req() // параметр является обязательным
.end() // завершаем определение параметра и возвращаемся к определению команды
.end() // завершаем определение подкоманды и возвращаемся к определению команды верхнего уровня
.run(process.argv.slice(2)); // разбираем process.argv и запускаем
````
````javascript
// subcommand.js
exports.COA = function() {
this
.title('Полезная подпрограмма').helpful()
.opt()
.name('output').title('output file')
.short('o').long('output')
.output() // использовать стандартную настройку для параметра вывода
.end()
};
````
## API
### Cmd
Команда — сущность верхнего уровня. У команды могут быть определены параметры и аргументы.
#### Cmd.api
Возвращает объект, который можно использовать в других программах. Подкоманды являются методами этого объекта.<br>
**@returns** *{Object}*
#### Cmd.name
Определяет канонический идентификатор команды, используемый в вызовах API.<br>
**@param** *String* `_name` имя команды<br>
**@returns** *COA.Cmd* `this` экземпляр команды (для поддержки цепочки методов)
#### Cmd.title
Определяет название команды, используемый в текстовых сообщениях.<br>
**@param** *String* `_title` название команды<br>
**@returns** *COA.Cmd* `this` экземпляр команды (для поддержки цепочки методов)
#### Cmd.cmd
Создает новую подкоманду или добавляет ранее определенную подкоманду к текущей команде.<br>
**@param** *COA.Cmd* `[cmd]` экземпляр ранее определенной подкоманды<br>
**@returns** *COA.Cmd* экземпляр новой или ранее определенной подкоманды
#### Cmd.opt
Создает параметр для текущей команды.<br>
**@returns** *COA.Opt* `new` экземпляр параметра
#### Cmd.arg
Создает аргумент для текущей команды.<br>
**@returns** *COA.Opt* `new` экземпляр аргумента
#### Cmd.act
Добавляет (или создает) действие для текущей команды.<br>
**@param** *Function* `act` функция,
выполняемая в контексте экземпляра текущей команды
и принимающая следующие параметры:<br>
- *Object* `opts` параметры команды<br>
- *Array* `args` аргументы команды<br>
- *Object* `res` объект-аккумулятор результатов<br>
Функция может вернуть проваленный промис из Cmd.reject (в случае ошибки)
или любое другое значение, рассматриваемое как результат.<br>
**@param** *{Boolean}* [force=false] флаг, назначающий немедленное исполнение вместо добавления к списку существующих действий<br>
**@returns** *COA.Cmd* `this` экземпляр команды (для поддержки цепочки методов)
#### Cmd.apply
Исполняет функцию с переданными аргументами в контексте экземпляра текущей команды.<br>
**@param** *Function* `fn`<br>
**@param** *Array* `args`<br>
**@returns** *COA.Cmd* `this` экземпляр команды (для поддержки цепочки методов)
#### Cmd.comp
Назначает кастомную функцию автодополнения для текущей команды.<br>
**@param** *Function* `fn` функция-генератор автодополнения,
исполняемая в контексте текущей команды.
Принимает параметры:<br>
- *Object* `opts` параметры<br>
Может возвращать промис или любое другое значение, рассматриваемое как результат исполнения команды.<br>
**@returns** *COA.Cmd* `this` экземпляр команды (для поддержки цепочки методов)
#### Cmd.helpful
Ставит флаг поддержки справки командной строки, т.е. вызов команды с параметрами -h --help выводит справку по работе с командой.<br>
**@returns** *COA.Cmd* `this` экземпляр команды (для поддержки цепочки методов)
#### Cmd.completable
Добавляет поддержку автодополнения командной строки. Добавляется подкоманда "completion", которая выполняет все необходимые действия.<br>
Может быть добавлен только для главной команды.<br>
**@returns** *COA.Cmd* `this` экземпляр команды (для поддержки цепочки методов)
#### Cmd.usage
Возвращает текст справки по использованию команды для текущего экземпляра.<br>
**@returns** *String* `usage` Текст справки по использованию
#### Cmd.run
Разбирает аргументы из значения, возвращаемого NodeJS process.argv,
и запускает текущую программу, т.е. вызывает process.exit после завершения
всех действий.<br>
**@param** *Array* `argv`<br>
**@returns** *COA.Cmd* `this` экземпляр команды (для поддержки цепочки методов)
#### Cmd.invoke
Исполняет переданную (или текущую) команду с указанными параметрами и аргументами.<br>
**@param** *String|Array* `cmds` подкоманда для исполнения (необязательно)<br>
**@param** *Object* `opts` параметры, передаваемые команде (необязательно)<br>
**@param** *Object* `args` аргументы, передаваемые команде (необязательно)<br>
**@returns** *Q.Promise*
#### Cmd.reject
Проваливает промисы, возращенные в действиях.<br>
Используется в .act() для возврата с ошибкой.<br>
**@param** *Object* `reason` причина провала<br>
Вы можете определить метод toString() и свойство toString()
объекта причины провала.<br>
**@returns** *Q.promise* проваленный промис
#### Cmd.end
Завершает цепочку методов текущей подкоманды и возвращает экземпляр родительской команды.<br>
**@returns** *COA.Cmd* `parent` родительская команда
### Opt
Параметр — именованная сущность. У параметра может быть определено короткое или длинное имя для использования из командной строки.<br>
**@namespace**<br>
**@class** Переданный параметр
#### Opt.name
Определяет канонический идентификатор параметра, используемый в вызовах API.<br>
**@param** *String* `_name` имя параметра<br>
**@returns** *COA.Opt* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.title
Определяет описание для параметра, используемое в текстовых сообщениях.<br>
**@param** *String* `_title` название параметра<br>
**@returns** *COA.Opt* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.short
Назначает ключ для короткого имени параметра, передаваемого из командной строки с одинарным дефисом (например, `-v`).<br>
**@param** *String* `_short`<br>
**@returns** *COA.Opt* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.long
Назначает ключ для длинного имени параметра, передаваемого из командной строки с двойным дефисом (например, `--version`).<br>
**@param** *String* `_long`<br>
**@returns** *COA.Opt* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.flag
Помечает параметр как логический, т.е. параметр не имеющий значения.<br>
**@returns** *COA.Opt* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.arr
Помечает параметр как принимающий множественные значения.<br>
Иначе будет использовано последнее переданное значение параметра.<br>
**@returns** *COA.Opt* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.req
Помечает параметр как обязательный.<br>
**@returns** *COA.Opt* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.only
Интерпретирует параметр как команду,
т.е. программа будет завершена сразу после выполнения параметра.<br>
**@returns** *COA.Opt* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.val
Назначает функцию валидации (или трансформации значения) для значения параметра.<br>
Значение, полученное из командной строки, передается в функцию-валидатор прежде чем оно станет доступно из API.<br>
Используется для валидации и трансформации введенных данных.<br>
**@param** *Function* `_val` функция валидации,
исполняемая в контексте экземпляра параметра
и принимающая в качестве единственного параметра значение, полученное
из командной строки<br>
**@returns** *COA.Opt* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.def
Назначает значение параметра по умолчанию. Это значение также передается
в функцию валидации как обычное значение.<br>
**@param** *Object* `_def`<br>
**@returns** *COA.Opt* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.input
Помечает параметр как принимающий ввод пользователя. <br>
Позволяет использовать валидацию для STDIN.<br>
**@returns** *{COA.Opt}* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.output
Помечает параметр как вывод.<br>
Позволяет использовать валидацию для STDOUT.<br>
**@returns** *COA.Opt* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.act
Добавляет (или создает) действие для текущего параметра команды.
Это действие будет выполнено, если текущий параметр есть
в списке полученных параметров (с любым значением).<br>
**@param** *Function* `act` функция, выполняемая в контексте
экземпляра текущей команды и принимающая следующие параметры:<br>
- *Object* `opts` параметры команды<br>
- *Array* `args` аргументы команды<br>
- *Object* `res` объект-аккумулятор результатов<br>
Функция может вернуть проваленный промис из Cmd.reject (в случае ошибки)
или любое другое значение, рассматриваемое как результат.<br>
**@returns** *COA.Opt* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.comp
Назначает кастомную функцию автодополнения для текущей команды.<br>
**@param** *Function* `fn` функция-генератор автодоплнения, исполняемая в
контексте экземпляра команды.
Принимает параметры:<br>
- *Object* `opts` параметры автодополнения<br>
Может возвращать промис или любое другое значение, рассматриваемое как результат исполнения команды.<br>
**@returns** *COA.Opt* `this` экземпляр параметра (для поддержки цепочки методов)
#### Opt.end
Завершает цепочку методов текущего параметра и возвращает экземпляр родительской команды.<br>
**@returns** *COA.Cmd* `parent` родительская команда
### Arg
Аргумент — неименованная сущность.<br>
Аргументы передаются из командной строки как список неименованных значений.
#### Arg.name
Определяет канонический идентификатор аргумента, используемый в вызовах API.<br>
**@param** *String* `_name` имя аргумента<br>
**@returns** *COA.Arg* `this` экземпляр аргумента (для поддержки цепочки методов)
#### Arg.title
Определяет описание для аргумента, используемое в текстовых сообщениях.<br>
**@param** *String* `_title` описание аргумента<br>
**@returns** *COA.Arg* `this` экземпляр аргумента (для поддержки цепочки методов)
#### Arg.arr
Помечает аргумент как принимающий множественные значения.<br>
Иначе будет использовано последнее переданное значение аргумента.<br>
**@returns** *COA.Arg* `this` экземпляр аргумента (для поддержки цепочки методов)
#### Arg.req
Помечает аргумент как обязательный.<br>
**@returns** *COA.Arg* `this` экземпляр аргумента (для поддержки цепочки методов)
#### Arg.val
Назначает функцию валидации (или трансформации значения) для аргумента.<br>
Значение, полученное из командной строки, передается в функцию-валидатор прежде чем оно станет доступно из API.<br>
Используется для валидации и трансформации введенных данных.<br>
**@param** *Function* `_val` функция валидации,
исполняемая в контексте экземпляра аргумента
и принимающая в качестве единственного параметра значение, полученное
из командной строки<br>
**@returns** *COA.Opt* `this` экземпляр аргумента (для поддержки цепочки методов)
#### Arg.def
Назначает дефолтное значение для аргумента. Дефолтное значение передается
в функцию валидации как обычное значение.<br>
**@param** *Object* `_def`<br>
**@returns** *COA.Arg* `this` экземпляр аргумента (для поддержки цепочки методов)
#### Arg.output
Помечает параметр как вывод.<br>
Позволяет назначить валидацию для STDOUT.<br>
**@returns** *COA.Arg* `this` экземпляр аргумента (для поддержки цепочки методов)
#### Arg.comp
Назначает кастомную функцию автодополнения для текущего аргумента.<br>
**@param** *Function* `fn` функция-генератор автодоплнения,
исполняемая в контексте текущей команды.
Принимает параметры:<br>
- *Object* `opts` параметры
Может возвращать промис или любое другое значение, рассматриваемое как результат исполнения команды.<br>
**@returns** *COA.Arg* `this` экземпляр аргумента (для поддержки цепочки методов)
#### Arg.end
Завершает цепочку методов текущего аргумента и возвращает экземпляр родительской команды.<br>
**@returns** *COA.Cmd* `parent` родительская команда

View file

@ -0,0 +1,212 @@
body, html {
margin:0; padding: 0;
height: 100%;
}
body {
font-family: Helvetica Neue, Helvetica, Arial;
font-size: 14px;
color:#333;
}
.small { font-size: 12px; }
*, *:after, *:before {
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}
h1 { font-size: 20px; margin: 0;}
h2 { font-size: 14px; }
pre {
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
margin: 0;
padding: 0;
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
}
a { color:#0074D9; text-decoration:none; }
a:hover { text-decoration:underline; }
.strong { font-weight: bold; }
.space-top1 { padding: 10px 0 0 0; }
.pad2y { padding: 20px 0; }
.pad1y { padding: 10px 0; }
.pad2x { padding: 0 20px; }
.pad2 { padding: 20px; }
.pad1 { padding: 10px; }
.space-left2 { padding-left:55px; }
.space-right2 { padding-right:20px; }
.center { text-align:center; }
.clearfix { display:block; }
.clearfix:after {
content:'';
display:block;
height:0;
clear:both;
visibility:hidden;
}
.fl { float: left; }
@media only screen and (max-width:640px) {
.col3 { width:100%; max-width:100%; }
.hide-mobile { display:none!important; }
}
.quiet {
color: #7f7f7f;
color: rgba(0,0,0,0.5);
}
.quiet a { opacity: 0.7; }
.fraction {
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
font-size: 10px;
color: #555;
background: #E8E8E8;
padding: 4px 5px;
border-radius: 3px;
vertical-align: middle;
}
div.path a:link, div.path a:visited { color: #333; }
table.coverage {
border-collapse: collapse;
margin: 10px 0 0 0;
padding: 0;
}
table.coverage td {
margin: 0;
padding: 0;
vertical-align: top;
}
table.coverage td.line-count {
text-align: right;
padding: 0 5px 0 20px;
}
table.coverage td.line-coverage {
text-align: right;
padding-right: 10px;
min-width:20px;
}
table.coverage td span.cline-any {
display: inline-block;
padding: 0 5px;
width: 100%;
}
.missing-if-branch {
display: inline-block;
margin-right: 5px;
border-radius: 3px;
position: relative;
padding: 0 4px;
background: #333;
color: yellow;
}
.skip-if-branch {
display: none;
margin-right: 10px;
position: relative;
padding: 0 4px;
background: #ccc;
color: white;
}
.missing-if-branch .typ, .skip-if-branch .typ {
color: inherit !important;
}
.coverage-summary {
border-collapse: collapse;
width: 100%;
}
.coverage-summary tr { border-bottom: 1px solid #bbb; }
.keyline-all { border: 1px solid #ddd; }
.coverage-summary td, .coverage-summary th { padding: 10px; }
.coverage-summary tbody { border: 1px solid #bbb; }
.coverage-summary td { border-right: 1px solid #bbb; }
.coverage-summary td:last-child { border-right: none; }
.coverage-summary th {
text-align: left;
font-weight: normal;
white-space: nowrap;
}
.coverage-summary th.file { border-right: none !important; }
.coverage-summary th.pct { }
.coverage-summary th.pic,
.coverage-summary th.abs,
.coverage-summary td.pct,
.coverage-summary td.abs { text-align: right; }
.coverage-summary td.file { white-space: nowrap; }
.coverage-summary td.pic { min-width: 120px !important; }
.coverage-summary tfoot td { }
.coverage-summary .sorter {
height: 10px;
width: 7px;
display: inline-block;
margin-left: 0.5em;
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
}
.coverage-summary .sorted .sorter {
background-position: 0 -20px;
}
.coverage-summary .sorted-desc .sorter {
background-position: 0 -10px;
}
.status-line { height: 10px; }
/* dark red */
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
.low .chart { border:1px solid #C21F39 }
/* medium red */
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
/* light red */
.low, .cline-no { background:#FCE1E5 }
/* light green */
.high, .cline-yes { background:rgb(230,245,208) }
/* medium green */
.cstat-yes { background:rgb(161,215,106) }
/* dark green */
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
.high .chart { border:1px solid rgb(77,146,33) }
.medium .chart { border:1px solid #666; }
.medium .cover-fill { background: #666; }
.cbranch-no { background: yellow !important; color: #111; }
.cstat-skip { background: #ddd; color: #111; }
.fstat-skip { background: #ddd; color: #111 !important; }
.cbranch-skip { background: #ddd !important; color: #111; }
span.cline-neutral { background: #eaeaea; }
.medium { background: #eaeaea; }
.cover-fill, .cover-empty {
display:inline-block;
height: 12px;
}
.chart {
line-height: 0;
}
.cover-empty {
background: white;
}
.cover-full {
border-right: none !important;
}
pre.prettyprint {
border: none !important;
padding: 0 !important;
margin: 0 !important;
}
.com { color: #999 !important; }
.ignore-none { color: #999; font-weight: normal; }
.wrapper {
min-height: 100%;
height: auto !important;
height: 100%;
margin: 0 auto -48px;
}
.footer, .push {
height: 48px;
}

View file

@ -0,0 +1,93 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for coa</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../prettify.css" />
<link rel="stylesheet" href="../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../index.html">All files</a> coa
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Statements</span>
<span class='fraction'>1/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>1/1</span>
</div>
</div>
</div>
<div class='status-line high'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file high" data-value="index.js"><a href="index.js.html">index.js</a></td>
<td data-value="100" class="pic high"><div class="chart"><div class="cover-fill cover-full" style="width: 100%;"></div><div class="cover-empty" style="width:0%;"></div></div></td>
<td data-value="100" class="pct high">100%</td>
<td data-value="1" class="abs high">1/1</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="0" class="abs high">0/0</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="0" class="abs high">0/0</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="1" class="abs high">1/1</td>
</tr>
</tbody>
</table>
</div><div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Fri Feb 17 2017 22:25:28 GMT+0300 (MSK)
</div>
</div>
<script src="../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../sorter.js"></script>
</body>
</html>

View file

@ -0,0 +1,68 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for coa/index.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../prettify.css" />
<link rel="stylesheet" href="../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../index.html">All files</a> / <a href="index.html">coa</a> index.js
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Statements</span>
<span class='fraction'>1/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>1/1</span>
</div>
</div>
</div>
<div class='status-line high'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet">1
2</td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">module.exports = require('./lib');
&nbsp;</pre></td></tr>
</table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Fri Feb 17 2017 22:25:28 GMT+0300 (MSK)
</div>
</div>
<script src="../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../sorter.js"></script>
</body>
</html>

View file

@ -0,0 +1,239 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for coa/lib/arg.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../../index.html">All files</a> / <a href="index.html">coa/lib</a> arg.js
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Statements</span>
<span class='fraction'>16/16</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">87.5% </span>
<span class="quiet">Branches</span>
<span class='fraction'>7/8</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>6/6</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>16/16</span>
</div>
</div>
</div>
<div class='status-line high'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59</td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">16x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">16x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">31x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">31x</span>
<span class="cline-any cline-yes">31x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">31x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">28x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">'use strict';
&nbsp;
const
CoaParam = require('./coaparam'),
Color = require('./color');
&nbsp;
/**
* Argument
*
* Unnamed entity. From command line arguments passed as list of unnamed values.
*
* @class Arg
* @extends CoaParam
*/
module.exports = class Arg extends CoaParam {
/**
* @constructs
* @param {COA.Cmd} cmd - parent command
*/
constructor(cmd) {
super(cmd);
&nbsp;
this._cmd._args.push(this);
}
&nbsp;
_saveVal(args, val) {
this._val &amp;&amp; (<span class="branch-1 cbranch-no" title="branch not covered" >val = this._val(val))</span>;
&nbsp;
const name = this._name;
this._arr
? (args[name] || (args[name] = [])).push(val)
: (args[name] = val);
&nbsp;
return val;
}
&nbsp;
_parse(arg, args) {
return this._saveVal(args, arg);
}
&nbsp;
_checkParsed(opts, args) {
return !args.hasOwnProperty(this._name);
}
&nbsp;
_usage() {
const res = [];
&nbsp;
res.push(Color('lpurple', this._name.toUpperCase()), ' : ', this._title);
&nbsp;
this._req &amp;&amp; res.push(' ', Color('lred', '(required)'));
&nbsp;
return res.join('');
}
&nbsp;
_requiredText() {
return `Missing required argument:\n ${this._usage()}`;
}
};
&nbsp;</pre></td></tr>
</table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Fri Feb 17 2017 22:25:28 GMT+0300 (MSK)
</div>
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../../sorter.js"></script>
</body>
</html>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,365 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for coa/lib/coaobject.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../../index.html">All files</a> / <a href="index.html">coa/lib</a> coaobject.js
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">75% </span>
<span class="quiet">Statements</span>
<span class='fraction'>12/16</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">50% </span>
<span class="quiet">Branches</span>
<span class='fraction'>1/2</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">71.43% </span>
<span class="quiet">Functions</span>
<span class='fraction'>5/7</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">75% </span>
<span class="quiet">Lines</span>
<span class='fraction'>12/16</span>
</div>
</div>
</div>
<div class='status-line medium'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101</td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">58x</span>
<span class="cline-any cline-yes">58x</span>
<span class="cline-any cline-yes">58x</span>
<span class="cline-any cline-yes">58x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">44x</span>
<span class="cline-any cline-yes">44x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">8x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">38x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">'use strict';
&nbsp;
const Q = require('q');
&nbsp;
/**
* COA Object
*
* Base class for all COA-related objects
*
* --------|-----|-----|-----
* | Cmd | Opt | Arg
* --------|-----|-----|-----
* name | ✓ | ✓ | ✓
* title | ✓ | ✓ | ✓
* comp | ✓ | ✓ | ✓
* reject | ✓ | ✓ | ✓
* end | ✓ | ✓ | ✓
* apply | ✓ | ✓ | ✓
*
* @class CoaObject
*/
module.exports = class CoaObject {
constructor(cmd) {
this._cmd = cmd;
this._name = null;
this._title = null;
this._comp = null;
}
&nbsp;
/**
* Set a canonical identifier to be used anywhere in the API.
*
* @param {String} name - command, option or argument name
* @returns {COA.CoaObject} - this instance (for chainability)
*/
name(name) {
this._name = name;
return this;
}
&nbsp;
/**
* Set a long description to be used anywhere in text messages.
* @param {String} title - human readable entity title
* @returns {COA.CoaObject} - this instance (for chainability)
*/
<span class="fstat-no" title="function not covered" > ti</span>tle(title) {
<span class="cstat-no" title="statement not covered" > this._title = title;</span>
<span class="cstat-no" title="statement not covered" > return this;</span>
}
&nbsp;
/**
* Set custom additional completion for current object.
*
* @param {Function} comp - completion generation function,
* invoked in the context of object instance.
* Accepts parameters:
* - {Object} opts - completion options
* It can return promise or any other value threated as a result.
* @returns {COA.CoaObject} - this instance (for chainability)
*/
<span class="fstat-no" title="function not covered" > co</span>mp(comp) {
<span class="cstat-no" title="statement not covered" > this._comp = comp;</span>
<span class="cstat-no" title="statement not covered" > return this;</span>
}
&nbsp;
/**
* Apply function with arguments in a context of object instance.
*
* @param {Function} fn - body
* @param {Array.&lt;*&gt;} args... - arguments
* @returns {COA.CoaObject} - this instance (for chainability)
*/
apply(fn) {
arguments.length &gt; 1?
<span class="branch-0 cbranch-no" title="branch not covered" > fn.apply(this, [].slice.call(arguments, 1))</span>
: fn.call(this);
&nbsp;
return this;
}
&nbsp;
/**
* Return reject of actions results promise with error code.
* Use in .act() for return with error.
* @param {Object} reason - reject reason
* You can customize toString() method and exitCode property
* of reason object.
* @returns {Q.promise} rejected promise
*/
reject(reason) {
return Q.reject(reason);
}
&nbsp;
/**
* Finish chain for current subcommand and return parent command instance.
* @returns {COA.Cmd} parent command
*/
end() {
return this._cmd;
}
};
&nbsp;</pre></td></tr>
</table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Fri Feb 17 2017 22:25:28 GMT+0300 (MSK)
</div>
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../../sorter.js"></script>
</body>
</html>

View file

@ -0,0 +1,440 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for coa/lib/coaparam.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../../index.html">All files</a> / <a href="index.html">coa/lib</a> coaparam.js
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">51.61% </span>
<span class="quiet">Statements</span>
<span class='fraction'>16/31</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/8</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">55.56% </span>
<span class="quiet">Functions</span>
<span class='fraction'>5/9</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">51.61% </span>
<span class="quiet">Lines</span>
<span class='fraction'>16/31</span>
</div>
</div>
</div>
<div class='status-line medium'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126</td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">32x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">32x</span>
<span class="cline-any cline-yes">32x</span>
<span class="cline-any cline-yes">32x</span>
<span class="cline-any cline-yes">32x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">9x</span>
<span class="cline-any cline-yes">9x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">'use strict';
&nbsp;
const fs = require('fs');
&nbsp;
const CoaObject = require('./coaobject');
&nbsp;
/**
* COA Parameter
*
* Base class for options and arguments
*
* --------|-----|-----|-----
* | Cmd | Opt | Arg
* --------|-----|-----|-----
* arr | | ✓ | ✓
* req | | ✓ | ✓
* val | | ✓ | ✓
* def | | ✓ | ✓
* input | | ✓ | ✓
* output | | ✓ | ✓
*
* @class CoaParam
* @extends CoaObject
*/
module.exports = class CoaParam extends CoaObject {
constructor(cmd) {
super(cmd);
&nbsp;
this._arr = false;
this._req = false;
this._val = undefined;
this._def = undefined;
}
&nbsp;
/**
* Makes a param accepts multiple values.
* Otherwise, the value will be used by the latter passed.
*
* @returns {COA.CoaParam} - this instance (for chainability)
*/
arr() {
this._arr = true;
return this;
}
&nbsp;
/**
* Makes a param required.
*
* @returns {COA.CoaParam} - this instance (for chainability)
*/
req() {
this._req = true;
return this;
}
&nbsp;
/**
* Set a validation (or value) function for param.
* Value from command line passes through before becoming available from API.
* Using for validation and convertion simple types to any values.
*
* @param {Function} val - validating function,
* invoked in the context of option instance
* and has one parameter with value from command line.
* @returns {COA.CoaParam} - this instance (for chainability)
*/
val(val) {
this._val = val;
return this;
}
&nbsp;
/**
* Set a default value for param.
* Default value passed through validation function as ordinary value.
*
* @param {*} def - default value of function generator
* @returns {COA.CoaParam} - this instance (for chainability)
*/
def(def) {
this._def = def;
return this;
}
&nbsp;
/**
* Make option value inputting stream.
* It's add useful validation and shortcut for STDIN.
*
* @returns {COA.CoaParam} - this instance (for chainability)
*/
<span class="fstat-no" title="function not covered" > in</span>put() {
<span class="cstat-no" title="statement not covered" > process.stdin.pause();</span>
<span class="cstat-no" title="statement not covered" > return this</span>
.def(process.stdin)
.val(<span class="fstat-no" title="function not covered" >fu</span>nction(v) {
<span class="cstat-no" title="statement not covered" > if(typeof v !== 'string')</span>
<span class="cstat-no" title="statement not covered" > return v;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if(v === '-')</span>
<span class="cstat-no" title="statement not covered" > return process.stdin;</span>
&nbsp;
const s = <span class="cstat-no" title="statement not covered" >fs.createReadStream(v, { encoding : 'utf8' });</span>
<span class="cstat-no" title="statement not covered" > s.pause();</span>
<span class="cstat-no" title="statement not covered" > return s;</span>
});
}
&nbsp;
/**
* Make option value outputing stream.
* It's add useful validation and shortcut for STDOUT.
*
* @returns {COA.CoaParam} - this instance (for chainability)
*/
<span class="fstat-no" title="function not covered" > ou</span>tput() {
<span class="cstat-no" title="statement not covered" > return this</span>
.def(process.stdout)
.val(<span class="fstat-no" title="function not covered" >fu</span>nction(v) {
<span class="cstat-no" title="statement not covered" > if(typeof v !== 'string')</span>
<span class="cstat-no" title="statement not covered" > return v;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if(v === '-')</span>
<span class="cstat-no" title="statement not covered" > return process.stdout;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > return fs.createWriteStream(v, { encoding : 'utf8' });</span>
});
}
};
&nbsp;</pre></td></tr>
</table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Fri Feb 17 2017 22:25:28 GMT+0300 (MSK)
</div>
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../../sorter.js"></script>
</body>
</html>

View file

@ -0,0 +1,131 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for coa/lib/color.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../../index.html">All files</a> / <a href="index.html">coa/lib</a> color.js
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Statements</span>
<span class='fraction'>3/3</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>1/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>2/2</span>
</div>
</div>
</div>
<div class='status-line high'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23</td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">4x</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">'use strict';
&nbsp;
const colors = {
black : '30',
dgray : '1;30',
red : '31',
lred : '1;31',
green : '32',
lgreen : '1;32',
brown : '33',
yellow : '1;33',
blue : '34',
lblue : '1;34',
purple : '35',
lpurple : '1;35',
cyan : '36',
lcyan : '1;36',
lgray : '37',
white : '1;37'
};
&nbsp;
module.exports = (c, str) =&gt; `\x1B[${colors[c]}m${str}\x1B[m`;
&nbsp;</pre></td></tr>
</table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Fri Feb 17 2017 22:25:28 GMT+0300 (MSK)
</div>
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../../sorter.js"></script>
</body>
</html>

View file

@ -0,0 +1,593 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for coa/lib/completion.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../../index.html">All files</a> / <a href="index.html">coa/lib</a> completion.js
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">11.27% </span>
<span class="quiet">Statements</span>
<span class='fraction'>8/71</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/32</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/14</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">12.5% </span>
<span class="quiet">Lines</span>
<span class='fraction'>8/64</span>
</div>
</div>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177</td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">'use strict';
&nbsp;
const constants = require('constants');
const fs = require('fs');
const path = require('path');
&nbsp;
const Q = require('q');
&nbsp;
const shell = require('./shell');
const escape = shell.escape;
const unescape = shell.unescape;
&nbsp;
/**
* Most of the code adopted from the npm package shell completion code.
* See https://github.com/isaacs/npm/blob/master/lib/completion.js
*
* @returns {COA.CoaObject}
*/
module.exports = function <span class="fstat-no" title="function not covered" >completion(</span>) {
<span class="cstat-no" title="statement not covered" > return this</span>
.title('Shell completion')
.helpful()
.arg()
.name('raw')
.title('Completion words')
.arr()
.end()
.act(<span class="fstat-no" title="function not covered" >(o</span>pts, args) =&gt; {
<span class="cstat-no" title="statement not covered" > if(process.platform === 'win32') {</span>
const e = <span class="cstat-no" title="statement not covered" >new Error('shell completion not supported on windows');</span>
<span class="cstat-no" title="statement not covered" > e.code = 'ENOTSUP';</span>
<span class="cstat-no" title="statement not covered" > e.errno = constants.ENOTSUP;</span>
<span class="cstat-no" title="statement not covered" > return this.reject(e);</span>
}
&nbsp;
// if the COMP_* isn't in the env, then just dump the script
<span class="cstat-no" title="statement not covered" > if((process.env.COMP_CWORD == null)</span>
|| (process.env.COMP_LINE == null)
|| (process.env.COMP_POINT == null)) {
<span class="cstat-no" title="statement not covered" > return dumpScript(this._cmd._name);</span>
}
&nbsp;
<span class="cstat-no" title="statement not covered" > console.error('COMP_LINE: %s', process.env.COMP_LINE);</span>
<span class="cstat-no" title="statement not covered" > console.error('COMP_CWORD: %s', process.env.COMP_CWORD);</span>
<span class="cstat-no" title="statement not covered" > console.error('COMP_POINT: %s', process.env.COMP_POINT);</span>
<span class="cstat-no" title="statement not covered" > console.error('args: %j', args.raw);</span>
&nbsp;
// completion opts
<span class="cstat-no" title="statement not covered" > opts = getOpts(args.raw);</span>
&nbsp;
// cmd
const parsed = <span class="cstat-no" title="statement not covered" >this._cmd._parseCmd(opts.partialWords);</span>
<span class="cstat-no" title="statement not covered" > return Q.when(complete(parsed.cmd, parsed.opts), <span class="fstat-no" title="function not covered" >(c</span>ompls) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > console.error('filtered: %j', compls);</span>
<span class="cstat-no" title="statement not covered" > return console.log(compls.map(escape).join('\n'));</span>
});
});
};
&nbsp;
function <span class="fstat-no" title="function not covered" >dumpScript(</span>name) {
const defer = <span class="cstat-no" title="statement not covered" >Q.defer();</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > fs.readFile(path.resolve(__dirname, 'completion.sh'), 'utf8', <span class="fstat-no" title="function not covered" >fu</span>nction(err, d) {</span>
<span class="cstat-no" title="statement not covered" > if(err) <span class="cstat-no" title="statement not covered" >return defer.reject(err);</span></span>
<span class="cstat-no" title="statement not covered" > d = d.replace(/{{cmd}}/g, path.basename(name)).replace(/^\#\!.*?\n/, '');</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > process.stdout.on('error', onError);</span>
<span class="cstat-no" title="statement not covered" > process.stdout.write(d, <span class="fstat-no" title="function not covered" >()</span> =&gt; <span class="cstat-no" title="statement not covered" >defer.resolve())</span>;</span>
});
&nbsp;
<span class="cstat-no" title="statement not covered" > return defer.promise;</span>
&nbsp;
function <span class="fstat-no" title="function not covered" >onError(</span>err) {
// Darwin is a real dick sometimes.
//
// This is necessary because the "source" or "." program in
// bash on OS X closes its file argument before reading
// from it, meaning that you get exactly 1 write, which will
// work most of the time, and will always raise an EPIPE.
//
// Really, one should not be tossing away EPIPE errors, or any
// errors, so casually. But, without this, `. &lt;(cmd completion)`
// can never ever work on OS X.
<span class="cstat-no" title="statement not covered" > if(err.errno !== constants.EPIPE) <span class="cstat-no" title="statement not covered" >return defer.reject(err);</span></span>
<span class="cstat-no" title="statement not covered" > process.stdout.removeListener('error', onError);</span>
<span class="cstat-no" title="statement not covered" > return defer.resolve();</span>
}
}
&nbsp;
function <span class="fstat-no" title="function not covered" >getOpts(</span>argv) {
// get the partial line and partial word, if the point isn't at the end
// ie, tabbing at: cmd foo b|ar
const line = <span class="cstat-no" title="statement not covered" >process.env.COMP_LINE;</span>
const w = <span class="cstat-no" title="statement not covered" >+process.env.COMP_CWORD;</span>
const point = <span class="cstat-no" title="statement not covered" >+process.env.COMP_POINT;</span>
const words = <span class="cstat-no" title="statement not covered" >argv.map(unescape);</span>
const word = <span class="cstat-no" title="statement not covered" >words[w];</span>
const partialLine = <span class="cstat-no" title="statement not covered" >line.substr(0, point);</span>
const partialWords = <span class="cstat-no" title="statement not covered" >words.slice(0, w);</span>
&nbsp;
// figure out where in that last word the point is
let partialWord = <span class="cstat-no" title="statement not covered" >argv[w] || '';</span>
let i = <span class="cstat-no" title="statement not covered" >partialWord.length;</span>
<span class="cstat-no" title="statement not covered" > while(partialWord.substr(0, i) !== partialLine.substr(-1 * i) &amp;&amp; i &gt; 0) <span class="cstat-no" title="statement not covered" >i--;</span></span>
&nbsp;
<span class="cstat-no" title="statement not covered" > partialWord = unescape(partialWord.substr(0, i));</span>
<span class="cstat-no" title="statement not covered" > partialWord &amp;&amp; partialWords.push(partialWord);</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > return {</span>
line,
w,
point,
words,
word,
partialLine,
partialWords,
partialWord
};
}
&nbsp;
function <span class="fstat-no" title="function not covered" >complete(</span>cmd, opts) {
let optWord, optPrefix,
compls = <span class="cstat-no" title="statement not covered" >[];</span>
&nbsp;
// Complete on cmds
<span class="cstat-no" title="statement not covered" > if(opts.partialWord.indexOf('-'))</span>
<span class="cstat-no" title="statement not covered" > compls = Object.keys(cmd._cmdsByName);</span>
// Complete on required opts without '-' in last partial word
// (if required not already specified)
//
// Commented out because of uselessness:
// -b, --block suggest results in '-' on cmd line;
// next completion suggest all options, because of '-'
//.concat Object.keys(cmd._optsByKey).filter (v) -&gt; cmd._optsByKey[v]._req
else {
// complete on opt values: --opt=| case
const m = <span class="cstat-no" title="statement not covered" >opts.partialWord.match(/^(--\w[\w-_]*)=(.*)$/);</span>
<span class="cstat-no" title="statement not covered" > if(m) {</span>
<span class="cstat-no" title="statement not covered" > optWord = m[1];</span>
<span class="cstat-no" title="statement not covered" > optPrefix = optWord + '=';</span>
} else
// complete on opts
// don't complete on opts in case of --opt=val completion
// TODO: don't complete on opts in case of unknown arg after commands
// TODO: complete only on opts with arr() or not already used
// TODO: complete only on full opts?
<span class="cstat-no" title="statement not covered" > compls = Object.keys(cmd._optsByKey);</span>
}
&nbsp;
// complete on opt values: next arg case
<span class="cstat-no" title="statement not covered" > opts.partialWords[opts.w - 1].indexOf('-') || (optWord = opts.partialWords[opts.w - 1]);</span>
&nbsp;
// complete on opt values: completion
let opt;
<span class="cstat-no" title="statement not covered" > optWord</span>
&amp;&amp; (opt = cmd._optsByKey[optWord])
&amp;&amp; !opt._flag
&amp;&amp; opt._comp
&amp;&amp; (compls = Q.join(compls,
Q.when(opt._comp(opts),
<span class="fstat-no" title="function not covered" > (c</span>, o) =&gt; <span class="cstat-no" title="statement not covered" >c.concat(o.map(<span class="fstat-no" title="function not covered" >v </span>=&gt; <span class="cstat-no" title="statement not covered" >(optPrefix || '') + v)</span>))</span>));
&nbsp;
// TODO: complete on args values (context aware, custom completion?)
&nbsp;
// custom completion on cmds
<span class="cstat-no" title="statement not covered" > cmd._comp &amp;&amp; (compls = Q.join(compls, Q.when(cmd._comp(opts)), <span class="fstat-no" title="function not covered" >(c</span>, o) =&gt; <span class="cstat-no" title="statement not covered" >c.concat(o))</span>);</span>
&nbsp;
// TODO: context aware custom completion on cmds, opts and args
// (can depend on already entered values, especially options)
&nbsp;
<span class="cstat-no" title="statement not covered" > return Q.when(compls, <span class="fstat-no" title="function not covered" >co</span>mplitions =&gt; {</span>
<span class="cstat-no" title="statement not covered" > console.error('partialWord: %s', opts.partialWord);</span>
<span class="cstat-no" title="statement not covered" > console.error('compls: %j', complitions);</span>
<span class="cstat-no" title="statement not covered" > return compls.filter(<span class="fstat-no" title="function not covered" >(c</span>) =&gt; <span class="cstat-no" title="statement not covered" >c.indexOf(opts.partialWord) === 0)</span>;</span>
});
}
&nbsp;</pre></td></tr>
</table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Fri Feb 17 2017 22:25:28 GMT+0300 (MSK)
</div>
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../../sorter.js"></script>
</body>
</html>

View file

@ -0,0 +1,197 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for coa/lib</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../../index.html">All files</a> coa/lib
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">68.17% </span>
<span class="quiet">Statements</span>
<span class='fraction'>257/377</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">59.05% </span>
<span class="quiet">Branches</span>
<span class='fraction'>124/210</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">65.35% </span>
<span class="quiet">Functions</span>
<span class='fraction'>66/101</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">69.8% </span>
<span class="quiet">Lines</span>
<span class='fraction'>245/351</span>
</div>
</div>
</div>
<div class='status-line medium'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file high" data-value="arg.js"><a href="arg.js.html">arg.js</a></td>
<td data-value="100" class="pic high"><div class="chart"><div class="cover-fill cover-full" style="width: 100%;"></div><div class="cover-empty" style="width:0%;"></div></div></td>
<td data-value="100" class="pct high">100%</td>
<td data-value="16" class="abs high">16/16</td>
<td data-value="87.5" class="pct high">87.5%</td>
<td data-value="8" class="abs high">7/8</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="6" class="abs high">6/6</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="16" class="abs high">16/16</td>
</tr>
<tr>
<td class="file high" data-value="cmd.js"><a href="cmd.js.html">cmd.js</a></td>
<td data-value="81.22" class="pic high"><div class="chart"><div class="cover-fill" style="width: 81%;"></div><div class="cover-empty" style="width:19%;"></div></div></td>
<td data-value="81.22" class="pct high">81.22%</td>
<td data-value="181" class="abs high">147/181</td>
<td data-value="71.64" class="pct medium">71.64%</td>
<td data-value="134" class="abs medium">96/134</td>
<td data-value="68.75" class="pct medium">68.75%</td>
<td data-value="48" class="abs medium">33/48</td>
<td data-value="82.53" class="pct high">82.53%</td>
<td data-value="166" class="abs high">137/166</td>
</tr>
<tr>
<td class="file medium" data-value="coaobject.js"><a href="coaobject.js.html">coaobject.js</a></td>
<td data-value="75" class="pic medium"><div class="chart"><div class="cover-fill" style="width: 75%;"></div><div class="cover-empty" style="width:25%;"></div></div></td>
<td data-value="75" class="pct medium">75%</td>
<td data-value="16" class="abs medium">12/16</td>
<td data-value="50" class="pct medium">50%</td>
<td data-value="2" class="abs medium">1/2</td>
<td data-value="71.43" class="pct medium">71.43%</td>
<td data-value="7" class="abs medium">5/7</td>
<td data-value="75" class="pct medium">75%</td>
<td data-value="16" class="abs medium">12/16</td>
</tr>
<tr>
<td class="file medium" data-value="coaparam.js"><a href="coaparam.js.html">coaparam.js</a></td>
<td data-value="51.61" class="pic medium"><div class="chart"><div class="cover-fill" style="width: 51%;"></div><div class="cover-empty" style="width:49%;"></div></div></td>
<td data-value="51.61" class="pct medium">51.61%</td>
<td data-value="31" class="abs medium">16/31</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="8" class="abs low">0/8</td>
<td data-value="55.56" class="pct medium">55.56%</td>
<td data-value="9" class="abs medium">5/9</td>
<td data-value="51.61" class="pct medium">51.61%</td>
<td data-value="31" class="abs medium">16/31</td>
</tr>
<tr>
<td class="file high" data-value="color.js"><a href="color.js.html">color.js</a></td>
<td data-value="100" class="pic high"><div class="chart"><div class="cover-fill cover-full" style="width: 100%;"></div><div class="cover-empty" style="width:0%;"></div></div></td>
<td data-value="100" class="pct high">100%</td>
<td data-value="3" class="abs high">3/3</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="0" class="abs high">0/0</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="1" class="abs high">1/1</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="2" class="abs high">2/2</td>
</tr>
<tr>
<td class="file low" data-value="completion.js"><a href="completion.js.html">completion.js</a></td>
<td data-value="11.27" class="pic low"><div class="chart"><div class="cover-fill" style="width: 11%;"></div><div class="cover-empty" style="width:89%;"></div></div></td>
<td data-value="11.27" class="pct low">11.27%</td>
<td data-value="71" class="abs low">8/71</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="32" class="abs low">0/32</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="14" class="abs low">0/14</td>
<td data-value="12.5" class="pct low">12.5%</td>
<td data-value="64" class="abs low">8/64</td>
</tr>
<tr>
<td class="file high" data-value="index.js"><a href="index.js.html">index.js</a></td>
<td data-value="100" class="pic high"><div class="chart"><div class="cover-fill cover-full" style="width: 100%;"></div><div class="cover-empty" style="width:0%;"></div></div></td>
<td data-value="100" class="pct high">100%</td>
<td data-value="5" class="abs high">5/5</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="0" class="abs high">0/0</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="0" class="abs high">0/0</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="5" class="abs high">5/5</td>
</tr>
<tr>
<td class="file high" data-value="opt.js"><a href="opt.js.html">opt.js</a></td>
<td data-value="91.84" class="pic high"><div class="chart"><div class="cover-fill" style="width: 91%;"></div><div class="cover-empty" style="width:9%;"></div></div></td>
<td data-value="91.84" class="pct high">91.84%</td>
<td data-value="49" class="abs high">45/49</td>
<td data-value="72.73" class="pct medium">72.73%</td>
<td data-value="22" class="abs medium">16/22</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="14" class="abs high">14/14</td>
<td data-value="95.65" class="pct high">95.65%</td>
<td data-value="46" class="abs high">44/46</td>
</tr>
<tr>
<td class="file high" data-value="shell.js"><a href="shell.js.html">shell.js</a></td>
<td data-value="100" class="pic high"><div class="chart"><div class="cover-fill cover-full" style="width: 100%;"></div><div class="cover-empty" style="width:0%;"></div></div></td>
<td data-value="100" class="pct high">100%</td>
<td data-value="5" class="abs high">5/5</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="4" class="abs high">4/4</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="2" class="abs high">2/2</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="5" class="abs high">5/5</td>
</tr>
</tbody>
</table>
</div><div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Fri Feb 17 2017 22:25:28 GMT+0300 (MSK)
</div>
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../../sorter.js"></script>
</body>
</html>

View file

@ -0,0 +1,107 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for coa/lib/index.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../../index.html">All files</a> / <a href="index.html">coa/lib</a> index.js
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Statements</span>
<span class='fraction'>5/5</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>5/5</span>
</div>
</div>
</div>
<div class='status-line high'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15</td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const
Cmd = require('./cmd'),
Opt = require('./opt'),
Arg = require('./arg'),
shell = require('./shell');
&nbsp;
module.exports = {
Cmd : Cmd.create,
Opt : Opt.create,
Arg : Arg.create,
classes : { Cmd, Opt, Arg },
shell,
require
};
&nbsp;</pre></td></tr>
</table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Fri Feb 17 2017 22:25:28 GMT+0300 (MSK)
</div>
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../../sorter.js"></script>
</body>
</html>

View file

@ -0,0 +1,524 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for coa/lib/opt.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../../index.html">All files</a> / <a href="index.html">coa/lib</a> opt.js
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">91.84% </span>
<span class="quiet">Statements</span>
<span class='fraction'>45/49</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">72.73% </span>
<span class="quiet">Branches</span>
<span class='fraction'>16/22</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>14/14</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">95.65% </span>
<span class="quiet">Lines</span>
<span class='fraction'>44/46</span>
</div>
</div>
</div>
<div class='status-line high'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154</td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">16x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">16x</span>
<span class="cline-any cline-yes">16x</span>
<span class="cline-any cline-yes">16x</span>
<span class="cline-any cline-yes">16x</span>
<span class="cline-any cline-yes">16x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">7x</span>
<span class="cline-any cline-yes">7x</span>
<span class="cline-any cline-yes">7x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">9x</span>
<span class="cline-any cline-yes">9x</span>
<span class="cline-any cline-yes">9x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">20x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">20x</span>
<span class="cline-any cline-yes">20x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">20x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">18x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">'use strict';
&nbsp;
const
Q = require('q'),
&nbsp;
CoaParam = require('./coaparam'),
Color = require('./color');
&nbsp;
/**
* Option
*
* Named entity. Options may have short and long keys for use from command line.
*
* @namespace
* @class Opt
* @extends CoaParam
*/
module.exports = class Opt extends CoaParam {
/**
* @constructs
* @param {COA.Cmd} cmd - parent command
*/
constructor(cmd) {
super(cmd);
&nbsp;
this._short = null;
this._long = null;
this._flag = false;
this._only = false;
this._cmd._opts.push(this);
}
&nbsp;
/**
* Set a short key for option to be used with one hyphen from command line.
*
* @param {String} short - short name
* @returns {COA.Opt} - this instance (for chainability)
*/
short(short) {
this._short = short;
this._cmd._optsByKey[`-${short}`] = this;
return this;
}
&nbsp;
/**
* Set a short key for option to be used with double hyphens from command line.
*
* @param {String} long - long name
* @returns {COA.Opt} - this instance (for chainability)
*/
long(long) {
this._long = long;
this._cmd._optsByKey[`--${long}`] = this;
return this;
}
&nbsp;
/**
* Make an option boolean, i.e. option without value.
*
* @returns {COA.Opt} - this instance (for chainability)
*/
flag() {
this._flag = true;
return this;
}
&nbsp;
/**
* Makes an option to act as a command,
* i.e. program will exit just after option action.
*
* @returns {COA.Opt} - this instance (for chainability)
*/
only() {
this._only = true;
return this;
}
&nbsp;
/**
* Add action for current option command.
* This action is performed if the current option
* is present in parsed options (with any value).
*
* @param {Function} act - action function,
* invoked in the context of command instance
* and has the parameters:
* - {Object} opts - parsed options
* - {Array} args - parsed arguments
* - {Object} res - actions result accumulator
* It can return rejected promise by Cmd.reject (in case of error)
* or any other value treated as result.
* @returns {COA.Opt} - this instance (for chainability)
*/
act(act) {
this._cmd.act((opts) =&gt; {
<span class="missing-if-branch" title="if path not taken" >I</span>if(!opts.hasOwnProperty(this._name)) <span class="cstat-no" title="statement not covered" >return;</span>
&nbsp;
const res = act.apply(this._cmd, arguments);
<span class="missing-if-branch" title="if path not taken" >I</span>if(!this._only) <span class="cstat-no" title="statement not covered" >return res;</span>
&nbsp;
return Q.when(res, (out) =&gt; this._cmd.reject({
toString : () =&gt; out.toString(),
exitCode : 0
}));
});
&nbsp;
return this;
}
&nbsp;
_saveVal(opts, val) {
this._val &amp;&amp; (val = this._val(val));
&nbsp;
const name = this._name;
this._arr
? (opts[name] || (opts[name] = [])).push(val)
: (opts[name] = val);
&nbsp;
return val;
}
&nbsp;
_parse(argv, opts) {
return this._saveVal(opts, this._flag ? true : argv.shift());
}
&nbsp;
_checkParsed(opts) {
return !opts.hasOwnProperty(this._name);
}
&nbsp;
_usage() {
const res = [],
nameStr = this._name.toUpperCase();
&nbsp;
<span class="missing-if-branch" title="else path not taken" >E</span>if(this._short) {
res.push('-', Color('lgreen', this._short));
this._flag || res.push(' ' + nameStr);
res.push(', ');
}
&nbsp;
<span class="missing-if-branch" title="if path not taken" >I</span>if(this._long) {
<span class="cstat-no" title="statement not covered" > res.push('--', Color('green', this._long));</span>
<span class="cstat-no" title="statement not covered" > this._flag || res.push('=' + nameStr);</span>
}
&nbsp;
res.push(' : ', this._title);
&nbsp;
this._req &amp;&amp; res.push(' ', Color('lred', '(required)'));
&nbsp;
return res.join('');
}
&nbsp;
_requiredText() {
return `Missing required option:\n ${this._usage()}`;
}
};
&nbsp;</pre></td></tr>
</table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Fri Feb 17 2017 22:25:28 GMT+0300 (MSK)
</div>
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../../sorter.js"></script>
</body>
</html>

View file

@ -0,0 +1,107 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for coa/lib/shell.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../../index.html">All files</a> / <a href="index.html">coa/lib</a> shell.js
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Statements</span>
<span class='fraction'>5/5</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Branches</span>
<span class='fraction'>4/4</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>2/2</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>5/5</span>
</div>
</div>
</div>
<div class='status-line high'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15</td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">6x</span>
<span class="cline-any cline-yes">6x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">module.exports = { escape, unescape };
&nbsp;
function unescape(w) {
w = w.charAt(0) === '"'
? w.replace(/^"|([^\\])"$/g, '$1')
: w.replace(/\\ /g, ' ');
&nbsp;
return w.replace(/\\("|'|\$|`|\\)/g, '$1');
}
&nbsp;
function escape(w) {
w = w.replace(/(["'$`\\])/g,'\\$1');
return w.match(/\s+/) ? `"${w}"` : w;
}
&nbsp;</pre></td></tr>
</table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Fri Feb 17 2017 22:25:28 GMT+0300 (MSK)
</div>
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../../sorter.js"></script>
</body>
</html>

View file

@ -0,0 +1,106 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for All files</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="prettify.css" />
<link rel="stylesheet" href="base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
All files
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">68.25% </span>
<span class="quiet">Statements</span>
<span class='fraction'>258/378</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">59.05% </span>
<span class="quiet">Branches</span>
<span class='fraction'>124/210</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">65.35% </span>
<span class="quiet">Functions</span>
<span class='fraction'>66/101</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">69.89% </span>
<span class="quiet">Lines</span>
<span class='fraction'>246/352</span>
</div>
</div>
</div>
<div class='status-line medium'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file high" data-value="coa"><a href="coa/index.html">coa</a></td>
<td data-value="100" class="pic high"><div class="chart"><div class="cover-fill cover-full" style="width: 100%;"></div><div class="cover-empty" style="width:0%;"></div></div></td>
<td data-value="100" class="pct high">100%</td>
<td data-value="1" class="abs high">1/1</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="0" class="abs high">0/0</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="0" class="abs high">0/0</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="1" class="abs high">1/1</td>
</tr>
<tr>
<td class="file medium" data-value="coa/lib"><a href="coa/lib/index.html">coa/lib</a></td>
<td data-value="68.17" class="pic medium"><div class="chart"><div class="cover-fill" style="width: 68%;"></div><div class="cover-empty" style="width:32%;"></div></div></td>
<td data-value="68.17" class="pct medium">68.17%</td>
<td data-value="377" class="abs medium">257/377</td>
<td data-value="59.05" class="pct medium">59.05%</td>
<td data-value="210" class="abs medium">124/210</td>
<td data-value="65.35" class="pct medium">65.35%</td>
<td data-value="101" class="abs medium">66/101</td>
<td data-value="69.8" class="pct medium">69.8%</td>
<td data-value="351" class="abs medium">245/351</td>
</tr>
</tbody>
</table>
</div><div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Fri Feb 17 2017 22:25:28 GMT+0300 (MSK)
</div>
</div>
<script src="prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="sorter.js"></script>
</body>
</html>

View file

@ -0,0 +1 @@
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

View file

@ -0,0 +1,158 @@
var addSorting = (function () {
"use strict";
var cols,
currentSort = {
index: 0,
desc: false
};
// returns the summary table element
function getTable() { return document.querySelector('.coverage-summary'); }
// returns the thead element of the summary table
function getTableHeader() { return getTable().querySelector('thead tr'); }
// returns the tbody element of the summary table
function getTableBody() { return getTable().querySelector('tbody'); }
// returns the th element for nth column
function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; }
// loads all columns
function loadColumns() {
var colNodes = getTableHeader().querySelectorAll('th'),
colNode,
cols = [],
col,
i;
for (i = 0; i < colNodes.length; i += 1) {
colNode = colNodes[i];
col = {
key: colNode.getAttribute('data-col'),
sortable: !colNode.getAttribute('data-nosort'),
type: colNode.getAttribute('data-type') || 'string'
};
cols.push(col);
if (col.sortable) {
col.defaultDescSort = col.type === 'number';
colNode.innerHTML = colNode.innerHTML + '<span class="sorter"></span>';
}
}
return cols;
}
// attaches a data attribute to every tr element with an object
// of data values keyed by column name
function loadRowData(tableRow) {
var tableCols = tableRow.querySelectorAll('td'),
colNode,
col,
data = {},
i,
val;
for (i = 0; i < tableCols.length; i += 1) {
colNode = tableCols[i];
col = cols[i];
val = colNode.getAttribute('data-value');
if (col.type === 'number') {
val = Number(val);
}
data[col.key] = val;
}
return data;
}
// loads all row data
function loadData() {
var rows = getTableBody().querySelectorAll('tr'),
i;
for (i = 0; i < rows.length; i += 1) {
rows[i].data = loadRowData(rows[i]);
}
}
// sorts the table using the data for the ith column
function sortByIndex(index, desc) {
var key = cols[index].key,
sorter = function (a, b) {
a = a.data[key];
b = b.data[key];
return a < b ? -1 : a > b ? 1 : 0;
},
finalSorter = sorter,
tableBody = document.querySelector('.coverage-summary tbody'),
rowNodes = tableBody.querySelectorAll('tr'),
rows = [],
i;
if (desc) {
finalSorter = function (a, b) {
return -1 * sorter(a, b);
};
}
for (i = 0; i < rowNodes.length; i += 1) {
rows.push(rowNodes[i]);
tableBody.removeChild(rowNodes[i]);
}
rows.sort(finalSorter);
for (i = 0; i < rows.length; i += 1) {
tableBody.appendChild(rows[i]);
}
}
// removes sort indicators for current column being sorted
function removeSortIndicators() {
var col = getNthColumn(currentSort.index),
cls = col.className;
cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
col.className = cls;
}
// adds sort indicators for current column being sorted
function addSortIndicators() {
getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted';
}
// adds event listeners for all sorter widgets
function enableUI() {
var i,
el,
ithSorter = function ithSorter(i) {
var col = cols[i];
return function () {
var desc = col.defaultDescSort;
if (currentSort.index === i) {
desc = !currentSort.desc;
}
sortByIndex(i, desc);
removeSortIndicators();
currentSort.index = i;
currentSort.desc = desc;
addSortIndicators();
};
};
for (i =0 ; i < cols.length; i += 1) {
if (cols[i].sortable) {
// add the click event handler on the th so users
// dont have to click on those tiny arrows
el = getNthColumn(i).querySelector('.sorter').parentElement;
if (el.addEventListener) {
el.addEventListener('click', ithSorter(i));
} else {
el.attachEvent('onclick', ithSorter(i));
}
}
}
}
// adds sorting functionality to the UI
return function () {
if (!getTable()) {
return;
}
cols = loadColumns();
loadData(cols);
addSortIndicators();
enableUI();
};
})();
window.addEventListener('load', addSorting);

View file

@ -0,0 +1 @@
module.exports = require(process.env.COVER? './lib-cov' : './lib');

View file

@ -0,0 +1,175 @@
// Generated by CoffeeScript 1.6.3
var Arg, Cmd, Color, Opt;
Color = require('./color').Color;
Cmd = require('./cmd').Cmd;
Opt = require('./opt').Opt;
/**
Argument
Unnamed entity. From command line arguments passed as list of unnamed values.
@namespace
@class Presents argument
*/
exports.Arg = Arg = (function() {
/**
@constructs
@param {COA.Cmd} cmd parent command
*/
function Arg(_cmd) {
this._cmd = _cmd;
this._cmd._args.push(this);
}
/**
Set a canonical argument identifier to be used anywhere in text messages.
@param {String} _name argument name
@returns {COA.Arg} this instance (for chainability)
*/
Arg.prototype.name = Opt.prototype.name;
/**
Set a long description for argument to be used anywhere in text messages.
@param {String} _title argument title
@returns {COA.Arg} this instance (for chainability)
*/
Arg.prototype.title = Cmd.prototype.title;
/**
Makes an argument accepts multiple values.
Otherwise, the value will be used by the latter passed.
@returns {COA.Arg} this instance (for chainability)
*/
Arg.prototype.arr = Opt.prototype.arr;
/**
Makes an argument required.
@returns {COA.Arg} this instance (for chainability)
*/
Arg.prototype.req = Opt.prototype.req;
/**
Set a validation (or value) function for argument.
Value from command line passes through before becoming available from API.
Using for validation and convertion simple types to any values.
@param {Function} _val validating function,
invoked in the context of argument instance
and has one parameter with value from command line
@returns {COA.Arg} this instance (for chainability)
*/
Arg.prototype.val = Opt.prototype.val;
/**
Set a default value for argument.
Default value passed through validation function as ordinary value.
@param {Object} _def
@returns {COA.Arg} this instance (for chainability)
*/
Arg.prototype.def = Opt.prototype.def;
/**
Set custom additional completion for current argument.
@param {Function} completion generation function,
invoked in the context of argument instance.
Accepts parameters:
- {Object} opts completion options
It can return promise or any other value treated as result.
@returns {COA.Arg} this instance (for chainability)
*/
Arg.prototype.comp = Cmd.prototype.comp;
/**
Make argument value inputting stream.
It's add useful validation and shortcut for STDIN.
@returns {COA.Arg} this instance (for chainability)
*/
Arg.prototype.input = Opt.prototype.input;
/**
Make argument value outputing stream.
It's add useful validation and shortcut for STDOUT.
@returns {COA.Arg} this instance (for chainability)
*/
Arg.prototype.output = Opt.prototype.output;
Arg.prototype._parse = function(arg, args) {
return this._saveVal(args, arg);
};
Arg.prototype._saveVal = Opt.prototype._saveVal;
Arg.prototype._checkParsed = function(opts, args) {
return !args.hasOwnProperty(this._name);
};
Arg.prototype._usage = function() {
var res;
res = [];
res.push(Color('lpurple', this._name.toUpperCase()), ' : ', this._title);
if (this._req) {
res.push(' ', Color('lred', '(required)'));
}
return res.join('');
};
Arg.prototype._requiredText = function() {
return 'Missing required argument:\n ' + this._usage();
};
/**
Return rejected promise with error code.
Use in .val() for return with error.
@param {Object} reject reason
You can customize toString() method and exitCode property
of reason object.
@returns {Q.promise} rejected promise
*/
Arg.prototype.reject = Cmd.prototype.reject;
/**
Finish chain for current option and return parent command instance.
@returns {COA.Cmd} parent command
*/
Arg.prototype.end = Cmd.prototype.end;
/**
Apply function with arguments in context of arg instance.
@param {Function} fn
@param {Array} args
@returns {COA.Arg} this instance (for chainability)
*/
Arg.prototype.apply = Cmd.prototype.apply;
return Arg;
})();

View file

@ -0,0 +1,605 @@
// Generated by CoffeeScript 1.6.3
var Cmd, Color, PATH, Q, UTIL,
__slice = [].slice;
UTIL = require('util');
PATH = require('path');
Color = require('./color').Color;
Q = require('q');
/**
Command
Top level entity. Commands may have options and arguments.
@namespace
@class Presents command
*/
exports.Cmd = Cmd = (function() {
/**
@constructs
@param {COA.Cmd} [cmd] parent command
*/
function Cmd(cmd) {
if (!(this instanceof Cmd)) {
return new Cmd(cmd);
}
this._parent(cmd);
this._cmds = [];
this._cmdsByName = {};
this._opts = [];
this._optsByKey = {};
this._args = [];
this._ext = false;
}
Cmd.get = function(propertyName, func) {
return Object.defineProperty(this.prototype, propertyName, {
configurable: true,
enumerable: true,
get: func
});
};
/**
Returns object containing all its subcommands as methods
to use from other programs.
@returns {Object}
*/
Cmd.get('api', function() {
var c, _fn,
_this = this;
if (!this._api) {
this._api = function() {
return _this.invoke.apply(_this, arguments);
};
}
_fn = function(c) {
return _this._api[c] = _this._cmdsByName[c].api;
};
for (c in this._cmdsByName) {
_fn(c);
}
return this._api;
});
Cmd.prototype._parent = function(cmd) {
this._cmd = cmd || this;
if (cmd) {
cmd._cmds.push(this);
if (this._name) {
this._cmd._cmdsByName[this._name] = this;
}
}
return this;
};
/**
Set a canonical command identifier to be used anywhere in the API.
@param {String} _name command name
@returns {COA.Cmd} this instance (for chainability)
*/
Cmd.prototype.name = function(_name) {
this._name = _name;
if (this._cmd !== this) {
this._cmd._cmdsByName[_name] = this;
}
return this;
};
/**
Set a long description for command to be used anywhere in text messages.
@param {String} _title command title
@returns {COA.Cmd} this instance (for chainability)
*/
Cmd.prototype.title = function(_title) {
this._title = _title;
return this;
};
/**
Create new or add existing subcommand for current command.
@param {COA.Cmd} [cmd] existing command instance
@returns {COA.Cmd} new subcommand instance
*/
Cmd.prototype.cmd = function(cmd) {
if (cmd) {
return cmd._parent(this);
} else {
return new Cmd(this);
}
};
/**
Create option for current command.
@returns {COA.Opt} new option instance
*/
Cmd.prototype.opt = function() {
return new (require('./opt').Opt)(this);
};
/**
Create argument for current command.
@returns {COA.Opt} new argument instance
*/
Cmd.prototype.arg = function() {
return new (require('./arg').Arg)(this);
};
/**
Add (or set) action for current command.
@param {Function} act action function,
invoked in the context of command instance
and has the parameters:
- {Object} opts parsed options
- {Array} args parsed arguments
- {Object} res actions result accumulator
It can return rejected promise by Cmd.reject (in case of error)
or any other value treated as result.
@param {Boolean} [force=false] flag for set action instead add to existings
@returns {COA.Cmd} this instance (for chainability)
*/
Cmd.prototype.act = function(act, force) {
if (!act) {
return this;
}
if (!force && this._act) {
this._act.push(act);
} else {
this._act = [act];
}
return this;
};
/**
Set custom additional completion for current command.
@param {Function} completion generation function,
invoked in the context of command instance.
Accepts parameters:
- {Object} opts completion options
It can return promise or any other value treated as result.
@returns {COA.Cmd} this instance (for chainability)
*/
Cmd.prototype.comp = function(_comp) {
this._comp = _comp;
return this;
};
/**
Apply function with arguments in context of command instance.
@param {Function} fn
@param {Array} args
@returns {COA.Cmd} this instance (for chainability)
*/
Cmd.prototype.apply = function() {
var args, fn;
fn = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
fn.apply(this, args);
return this;
};
/**
Make command "helpful", i.e. add -h --help flags for print usage.
@returns {COA.Cmd} this instance (for chainability)
*/
Cmd.prototype.helpful = function() {
return this.opt().name('help').title('Help').short('h').long('help').flag().only().act(function() {
return this.usage();
}).end();
};
/**
Adds shell completion to command, adds "completion" subcommand,
that makes all the magic.
Must be called only on root command.
@returns {COA.Cmd} this instance (for chainability)
*/
Cmd.prototype.completable = function() {
return this.cmd().name('completion').apply(require('./completion')).end();
};
/**
Allow command to be extendable by external node.js modules.
@param {String} [pattern] Pattern of node.js module to find subcommands at.
@returns {COA.Cmd} this instance (for chainability)
*/
Cmd.prototype.extendable = function(pattern) {
this._ext = pattern || true;
return this;
};
Cmd.prototype._exit = function(msg, code) {
return process.once('exit', function() {
if (msg) {
console.error(msg);
}
return process.exit(code || 0);
});
};
/**
Build full usage text for current command instance.
@returns {String} usage text
*/
Cmd.prototype.usage = function() {
var res;
res = [];
if (this._title) {
res.push(this._fullTitle());
}
res.push('', 'Usage:');
if (this._cmds.length) {
res.push(['', '', Color('lred', this._fullName()), Color('lblue', 'COMMAND'), Color('lgreen', '[OPTIONS]'), Color('lpurple', '[ARGS]')].join(' '));
}
if (this._opts.length + this._args.length) {
res.push(['', '', Color('lred', this._fullName()), Color('lgreen', '[OPTIONS]'), Color('lpurple', '[ARGS]')].join(' '));
}
res.push(this._usages(this._cmds, 'Commands'), this._usages(this._opts, 'Options'), this._usages(this._args, 'Arguments'));
return res.join('\n');
};
Cmd.prototype._usage = function() {
return Color('lblue', this._name) + ' : ' + this._title;
};
Cmd.prototype._usages = function(os, title) {
var o, res, _i, _len;
if (!os.length) {
return;
}
res = ['', title + ':'];
for (_i = 0, _len = os.length; _i < _len; _i++) {
o = os[_i];
res.push(' ' + o._usage());
}
return res.join('\n');
};
Cmd.prototype._fullTitle = function() {
return (this._cmd === this ? '' : this._cmd._fullTitle() + '\n') + this._title;
};
Cmd.prototype._fullName = function() {
return (this._cmd === this ? '' : this._cmd._fullName() + ' ') + PATH.basename(this._name);
};
Cmd.prototype._ejectOpt = function(opts, opt) {
var pos;
if ((pos = opts.indexOf(opt)) >= 0) {
if (opts[pos]._arr) {
return opts[pos];
} else {
return opts.splice(pos, 1)[0];
}
}
};
Cmd.prototype._checkRequired = function(opts, args) {
var all, i;
if (!(this._opts.filter(function(o) {
return o._only && o._name in opts;
})).length) {
all = this._opts.concat(this._args);
while (i = all.shift()) {
if (i._req && i._checkParsed(opts, args)) {
return this.reject(i._requiredText());
}
}
}
};
Cmd.prototype._parseCmd = function(argv, unparsed) {
var c, cmd, cmdDesc, e, i, optSeen, pkg;
if (unparsed == null) {
unparsed = [];
}
argv = argv.concat();
optSeen = false;
while (i = argv.shift()) {
if (!i.indexOf('-')) {
optSeen = true;
}
if (!optSeen && /^\w[\w-_]*$/.test(i)) {
cmd = this._cmdsByName[i];
if (!cmd && this._ext) {
if (typeof this._ext === 'string') {
if (~this._ext.indexOf('%s')) {
pkg = UTIL.format(this._ext, i);
} else {
pkg = this._ext + i;
}
} else if (this._ext === true) {
pkg = i;
c = this;
while (true) {
pkg = c._name + '-' + pkg;
if (c._cmd === c) {
break;
}
c = c._cmd;
}
}
try {
cmdDesc = require(pkg);
} catch (_error) {
e = _error;
}
if (cmdDesc) {
if (typeof cmdDesc === 'function') {
this.cmd().name(i).apply(cmdDesc).end();
} else if (typeof cmdDesc === 'object') {
this.cmd(cmdDesc);
cmdDesc.name(i);
} else {
throw new Error('Error: Unsupported command declaration type, ' + 'should be function or COA.Cmd() object');
}
cmd = this._cmdsByName[i];
}
}
if (cmd) {
return cmd._parseCmd(argv, unparsed);
}
}
unparsed.push(i);
}
return {
cmd: this,
argv: unparsed
};
};
Cmd.prototype._parseOptsAndArgs = function(argv) {
var a, arg, args, i, m, nonParsedArgs, nonParsedOpts, opt, opts, res;
opts = {};
args = {};
nonParsedOpts = this._opts.concat();
nonParsedArgs = this._args.concat();
while (i = argv.shift()) {
if (i !== '--' && !i.indexOf('-')) {
if (m = i.match(/^(--\w[\w-_]*)=(.*)$/)) {
i = m[1];
if (!this._optsByKey[i]._flag) {
argv.unshift(m[2]);
}
}
if (opt = this._ejectOpt(nonParsedOpts, this._optsByKey[i])) {
if (Q.isRejected(res = opt._parse(argv, opts))) {
return res;
}
} else {
return this.reject("Unknown option: " + i);
}
} else {
if (i === '--') {
i = argv.splice(0);
}
i = Array.isArray(i) ? i : [i];
while (a = i.shift()) {
if (arg = nonParsedArgs.shift()) {
if (arg._arr) {
nonParsedArgs.unshift(arg);
}
if (Q.isRejected(res = arg._parse(a, args))) {
return res;
}
} else {
return this.reject("Unknown argument: " + a);
}
}
}
}
return {
opts: this._setDefaults(opts, nonParsedOpts),
args: this._setDefaults(args, nonParsedArgs)
};
};
Cmd.prototype._setDefaults = function(params, desc) {
var i, _i, _len;
for (_i = 0, _len = desc.length; _i < _len; _i++) {
i = desc[_i];
if (!(i._name in params) && '_def' in i) {
i._saveVal(params, i._def);
}
}
return params;
};
Cmd.prototype._processParams = function(params, desc) {
var i, n, notExists, res, v, vals, _i, _j, _len, _len1;
notExists = [];
for (_i = 0, _len = desc.length; _i < _len; _i++) {
i = desc[_i];
n = i._name;
if (!(n in params)) {
notExists.push(i);
continue;
}
vals = params[n];
delete params[n];
if (!Array.isArray(vals)) {
vals = [vals];
}
for (_j = 0, _len1 = vals.length; _j < _len1; _j++) {
v = vals[_j];
if (Q.isRejected(res = i._saveVal(params, v))) {
return res;
}
}
}
return this._setDefaults(params, notExists);
};
Cmd.prototype._parseArr = function(argv) {
return Q.when(this._parseCmd(argv), function(p) {
return Q.when(p.cmd._parseOptsAndArgs(p.argv), function(r) {
return {
cmd: p.cmd,
opts: r.opts,
args: r.args
};
});
});
};
Cmd.prototype._do = function(input) {
var _this = this;
return Q.when(input, function(input) {
var cmd;
cmd = input.cmd;
return [_this._checkRequired].concat(cmd._act || []).reduce(function(res, act) {
return Q.when(res, function(res) {
return act.call(cmd, input.opts, input.args, res);
});
}, void 0);
});
};
/**
Parse arguments from simple format like NodeJS process.argv
and run ahead current program, i.e. call process.exit when all actions done.
@param {Array} argv
@returns {COA.Cmd} this instance (for chainability)
*/
Cmd.prototype.run = function(argv) {
var cb,
_this = this;
if (argv == null) {
argv = process.argv.slice(2);
}
cb = function(code) {
return function(res) {
var _ref, _ref1;
if (res) {
return _this._exit((_ref = res.stack) != null ? _ref : res.toString(), (_ref1 = res.exitCode) != null ? _ref1 : code);
} else {
return _this._exit();
}
};
};
Q.when(this["do"](argv), cb(0), cb(1)).done();
return this;
};
/**
Convenient function to run command from tests.
@param {Array} argv
@returns {Q.Promise}
*/
Cmd.prototype["do"] = function(argv) {
return this._do(this._parseArr(argv || []));
};
/**
Invoke specified (or current) command using provided
options and arguments.
@param {String|Array} cmds subcommand to invoke (optional)
@param {Object} opts command options (optional)
@param {Object} args command arguments (optional)
@returns {Q.Promise}
*/
Cmd.prototype.invoke = function(cmds, opts, args) {
var _this = this;
if (cmds == null) {
cmds = [];
}
if (opts == null) {
opts = {};
}
if (args == null) {
args = {};
}
if (typeof cmds === 'string') {
cmds = cmds.split(' ');
}
if (arguments.length < 3) {
if (!Array.isArray(cmds)) {
args = opts;
opts = cmds;
cmds = [];
}
}
return Q.when(this._parseCmd(cmds), function(p) {
if (p.argv.length) {
return _this.reject("Unknown command: " + cmds.join(' '));
}
return Q.all([_this._processParams(opts, _this._opts), _this._processParams(args, _this._args)]).spread(function(opts, args) {
return _this._do({
cmd: p.cmd,
opts: opts,
args: args
}).fail(function(res) {
if (res && res.exitCode === 0) {
return res.toString();
} else {
return _this.reject(res);
}
});
});
});
};
/**
Return reject of actions results promise with error code.
Use in .act() for return with error.
@param {Object} reject reason
You can customize toString() method and exitCode property
of reason object.
@returns {Q.promise} rejected promise
*/
Cmd.prototype.reject = function(reason) {
return Q.reject(reason);
};
/**
Finish chain for current subcommand and return parent command instance.
@returns {COA.Cmd} parent command
*/
Cmd.prototype.end = function() {
return this._cmd;
};
return Cmd;
})();

View file

@ -0,0 +1,25 @@
// Generated by CoffeeScript 1.6.3
var colors;
colors = {
black: '30',
dgray: '1;30',
red: '31',
lred: '1;31',
green: '32',
lgreen: '1;32',
brown: '33',
yellow: '1;33',
blue: '34',
lblue: '1;34',
purple: '35',
lpurple: '1;35',
cyan: '36',
lcyan: '1;36',
lgray: '37',
white: '1;37'
};
exports.Color = function(c, str) {
return ['\x1B[', colors[c], 'm', str, '\x1B[m'].join('');
};

View file

@ -0,0 +1,134 @@
// Generated by CoffeeScript 1.6.3
/**
Most of the code adopted from the npm package shell completion code.
See https://github.com/isaacs/npm/blob/master/lib/completion.js
*/
var Q, complete, dumpScript, escape, getOpts, unescape;
Q = require('q');
escape = require('./shell').escape;
unescape = require('./shell').unescape;
module.exports = function() {
return this.title('Shell completion').helpful().arg().name('raw').title('Completion words').arr().end().act(function(opts, args) {
var argv, cmd, e, _ref;
if (process.platform === 'win32') {
e = new Error('shell completion not supported on windows');
e.code = 'ENOTSUP';
e.errno = require('constants').ENOTSUP;
return this.reject(e);
}
if ((process.env.COMP_CWORD == null) || (process.env.COMP_LINE == null) || (process.env.COMP_POINT == null)) {
return dumpScript(this._cmd._name);
}
console.error('COMP_LINE: %s', process.env.COMP_LINE);
console.error('COMP_CWORD: %s', process.env.COMP_CWORD);
console.error('COMP_POINT: %s', process.env.COMP_POINT);
console.error('args: %j', args.raw);
opts = getOpts(args.raw);
_ref = this._cmd._parseCmd(opts.partialWords), cmd = _ref.cmd, argv = _ref.argv;
return Q.when(complete(cmd, opts), function(compls) {
console.error('filtered: %j', compls);
return console.log(compls.map(escape).join('\n'));
});
});
};
dumpScript = function(name) {
var defer, fs, path;
fs = require('fs');
path = require('path');
defer = Q.defer();
fs.readFile(path.resolve(__dirname, 'completion.sh'), 'utf8', function(err, d) {
var onError;
if (err) {
return defer.reject(err);
}
d = d.replace(/{{cmd}}/g, path.basename(name)).replace(/^\#\!.*?\n/, '');
onError = function(err) {
if (err.errno === require('constants').EPIPE) {
process.stdout.removeListener('error', onError);
return defer.resolve();
} else {
return defer.reject(err);
}
};
process.stdout.on('error', onError);
return process.stdout.write(d, function() {
return defer.resolve();
});
});
return defer.promise;
};
getOpts = function(argv) {
var i, line, partialLine, partialWord, partialWords, point, w, word, words;
line = process.env.COMP_LINE;
w = +process.env.COMP_CWORD;
point = +process.env.COMP_POINT;
words = argv.map(unescape);
word = words[w];
partialLine = line.substr(0, point);
partialWords = words.slice(0, w);
partialWord = argv[w] || '';
i = partialWord.length;
while (partialWord.substr(0, i) !== partialLine.substr(-1 * i) && i > 0) {
i--;
}
partialWord = unescape(partialWord.substr(0, i));
if (partialWord) {
partialWords.push(partialWord);
}
return {
line: line,
w: w,
point: point,
words: words,
word: word,
partialLine: partialLine,
partialWords: partialWords,
partialWord: partialWord
};
};
complete = function(cmd, opts) {
var compls, m, o, opt, optPrefix, optWord;
compls = [];
if (opts.partialWord.indexOf('-')) {
compls = Object.keys(cmd._cmdsByName);
} else {
if (m = opts.partialWord.match(/^(--\w[\w-_]*)=(.*)$/)) {
optWord = m[1];
optPrefix = optWord + '=';
} else {
compls = Object.keys(cmd._optsByKey);
}
}
if (!(o = opts.partialWords[opts.w - 1]).indexOf('-')) {
optWord = o;
}
if (optWord && (opt = cmd._optsByKey[optWord])) {
if (!opt._flag && opt._comp) {
compls = Q.join(compls, Q.when(opt._comp(opts), function(c, o) {
return c.concat(o.map(function(v) {
return (optPrefix || '') + v;
}));
}));
}
}
if (cmd._comp) {
compls = Q.join(compls, Q.when(cmd._comp(opts)), function(c, o) {
return c.concat(o);
});
}
return Q.when(compls, function(compls) {
console.error('partialWord: %s', opts.partialWord);
console.error('compls: %j', compls);
return compls.filter(function(c) {
return c.indexOf(opts.partialWord) === 0;
});
});
};

View file

@ -0,0 +1,43 @@
#!/usr/bin/env bash
###-begin-{{cmd}}-completion-###
#
# {{cmd}} command completion script
#
# Installation: {{cmd}} completion >> ~/.bashrc (or ~/.zshrc)
# Or, maybe: {{cmd}} completion > /usr/local/etc/bash_completion.d/{{cmd}}
#
COMP_WORDBREAKS=${COMP_WORDBREAKS/=/}
COMP_WORDBREAKS=${COMP_WORDBREAKS/@/}
export COMP_WORDBREAKS
if complete &>/dev/null; then
_{{cmd}}_completion () {
local si="$IFS"
IFS=$'\n' COMPREPLY=($(COMP_CWORD="$COMP_CWORD" \
COMP_LINE="$COMP_LINE" \
COMP_POINT="$COMP_POINT" \
{{cmd}} completion -- "${COMP_WORDS[@]}" \
2>/dev/null)) || return $?
IFS="$si"
}
complete -F _{{cmd}}_completion {{cmd}}
elif compctl &>/dev/null; then
_{{cmd}}_completion () {
local cword line point words si
read -Ac words
read -cn cword
let cword-=1
read -l line
read -ln point
si="$IFS"
IFS=$'\n' reply=($(COMP_CWORD="$cword" \
COMP_LINE="$line" \
COMP_POINT="$point" \
{{cmd}} completion -- "${words[@]}" \
2>/dev/null)) || return $?
IFS="$si"
}
compctl -K _{{cmd}}_completion {{cmd}}
fi
###-end-{{cmd}}-completion-###

View file

@ -0,0 +1,10 @@
// Generated by CoffeeScript 1.6.3
exports.Cmd = require('./cmd').Cmd;
exports.Opt = require('./cmd').Opt;
exports.Arg = require('./cmd').Arg;
exports.shell = require('./shell');
exports.require = require;

View file

@ -0,0 +1,338 @@
// Generated by CoffeeScript 1.6.3
var Cmd, Color, Opt, Q, fs;
fs = require('fs');
Q = require('q');
Color = require('./color').Color;
Cmd = require('./cmd').Cmd;
/**
Option
Named entity. Options may have short and long keys for use from command line.
@namespace
@class Presents option
*/
exports.Opt = Opt = (function() {
/**
@constructs
@param {COA.Cmd} cmd parent command
*/
function Opt(_cmd) {
this._cmd = _cmd;
this._cmd._opts.push(this);
}
/**
Set a canonical option identifier to be used anywhere in the API.
@param {String} _name option name
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.name = function(_name) {
this._name = _name;
return this;
};
/**
Set a long description for option to be used anywhere in text messages.
@param {String} _title option title
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.title = Cmd.prototype.title;
/**
Set a short key for option to be used with one hyphen from command line.
@param {String} _short
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.short = function(_short) {
this._short = _short;
return this._cmd._optsByKey['-' + _short] = this;
};
/**
Set a short key for option to be used with double hyphens from command line.
@param {String} _long
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.long = function(_long) {
this._long = _long;
return this._cmd._optsByKey['--' + _long] = this;
};
/**
Make an option boolean, i.e. option without value.
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.flag = function() {
this._flag = true;
return this;
};
/**
Makes an option accepts multiple values.
Otherwise, the value will be used by the latter passed.
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.arr = function() {
this._arr = true;
return this;
};
/**
Makes an option required.
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.req = function() {
this._req = true;
return this;
};
/**
Makes an option to act as a command,
i.e. program will exit just after option action.
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.only = function() {
this._only = true;
return this;
};
/**
Set a validation (or value) function for option.
Value from command line passes through before becoming available from API.
Using for validation and convertion simple types to any values.
@param {Function} _val validating function,
invoked in the context of option instance
and has one parameter with value from command line
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.val = function(_val) {
this._val = _val;
return this;
};
/**
Set a default value for option.
Default value passed through validation function as ordinary value.
@param {Object} _def
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.def = function(_def) {
this._def = _def;
return this;
};
/**
Make option value inputting stream.
It's add useful validation and shortcut for STDIN.
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.input = function() {
process.stdin.pause();
return this.def(process.stdin).val(function(v) {
var s;
if (typeof v === 'string') {
if (v === '-') {
return process.stdin;
} else {
s = fs.createReadStream(v, {
encoding: 'utf8'
});
s.pause();
return s;
}
} else {
return v;
}
});
};
/**
Make option value outputing stream.
It's add useful validation and shortcut for STDOUT.
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.output = function() {
return this.def(process.stdout).val(function(v) {
if (typeof v === 'string') {
if (v === '-') {
return process.stdout;
} else {
return fs.createWriteStream(v, {
encoding: 'utf8'
});
}
} else {
return v;
}
});
};
/**
Add action for current option command.
This action is performed if the current option
is present in parsed options (with any value).
@param {Function} act action function,
invoked in the context of command instance
and has the parameters:
- {Object} opts parsed options
- {Array} args parsed arguments
- {Object} res actions result accumulator
It can return rejected promise by Cmd.reject (in case of error)
or any other value treated as result.
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.act = function(act) {
var name, opt;
opt = this;
name = this._name;
this._cmd.act(function(opts) {
var res,
_this = this;
if (name in opts) {
res = act.apply(this, arguments);
if (opt._only) {
return Q.when(res, function(res) {
return _this.reject({
toString: function() {
return res.toString();
},
exitCode: 0
});
});
} else {
return res;
}
}
});
return this;
};
/**
Set custom additional completion for current option.
@param {Function} completion generation function,
invoked in the context of option instance.
Accepts parameters:
- {Object} opts completion options
It can return promise or any other value treated as result.
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.comp = Cmd.prototype.comp;
Opt.prototype._saveVal = function(opts, val) {
var _name;
if (this._val) {
val = this._val(val);
}
if (this._arr) {
(opts[_name = this._name] || (opts[_name] = [])).push(val);
} else {
opts[this._name] = val;
}
return val;
};
Opt.prototype._parse = function(argv, opts) {
return this._saveVal(opts, this._flag ? true : argv.shift());
};
Opt.prototype._checkParsed = function(opts, args) {
return !opts.hasOwnProperty(this._name);
};
Opt.prototype._usage = function() {
var nameStr, res;
res = [];
nameStr = this._name.toUpperCase();
if (this._short) {
res.push('-', Color('lgreen', this._short));
if (!this._flag) {
res.push(' ' + nameStr);
}
res.push(', ');
}
if (this._long) {
res.push('--', Color('green', this._long));
if (!this._flag) {
res.push('=' + nameStr);
}
}
res.push(' : ', this._title);
if (this._req) {
res.push(' ', Color('lred', '(required)'));
}
return res.join('');
};
Opt.prototype._requiredText = function() {
return 'Missing required option:\n ' + this._usage();
};
/**
Return rejected promise with error code.
Use in .val() for return with error.
@param {Object} reject reason
You can customize toString() method and exitCode property
of reason object.
@returns {Q.promise} rejected promise
*/
Opt.prototype.reject = Cmd.prototype.reject;
/**
Finish chain for current option and return parent command instance.
@returns {COA.Cmd} parent command
*/
Opt.prototype.end = Cmd.prototype.end;
/**
Apply function with arguments in context of option instance.
@param {Function} fn
@param {Array} args
@returns {COA.Opt} this instance (for chainability)
*/
Opt.prototype.apply = Cmd.prototype.apply;
return Opt;
})();

View file

@ -0,0 +1,14 @@
// Generated by CoffeeScript 1.6.3
exports.unescape = function(w) {
w = w.charAt(0) === '"' ? w.replace(/^"|([^\\])"$/g, '$1') : w.replace(/\\ /g, ' ');
return w.replace(/\\("|'|\$|`|\\)/g, '$1');
};
exports.escape = function(w) {
w = w.replace(/(["'$`\\])/g, '\\$1');
if (w.match(/\s+/)) {
return '"' + w + '"';
} else {
return w;
}
};

View file

@ -0,0 +1,44 @@
{
"name": "coa",
"description": "Command-Option-Argument: Yet another parser for command line options.",
"version": "1.0.4",
"homepage": "http://github.com/veged/coa",
"author": "Sergey Berezhnoy <veged@ya.ru> (http://github.com/veged)",
"maintainers": [
"Sergey Berezhnoy <veged@ya.ru> (http://github.com/veged)",
"Sergey Belov <peimei@ya.ru> (http://github.com/arikon)"
],
"contributors": [
"Sergey Belov <peimei@ya.ru> (http://github.com/arikon)"
],
"repository": {
"type": "git",
"url": "git://github.com/veged/coa.git"
},
"directories": {
"lib": "./lib"
},
"dependencies": {
"q": "^1.1.2"
},
"devDependencies": {
"coffee-script": "~1.6.3",
"istanbul": "~0.1.40",
"mocha-istanbul": "*",
"mocha": "~1.21.4",
"chai": "~1.7.2"
},
"scripts": {
"test": "make test",
"coverage": "make coverage"
},
"engines": {
"node": ">= 0.8.0"
},
"licenses": [
{
"type": "MIT"
}
],
"optionalDependencies": {}
}

View file

@ -0,0 +1,17 @@
const run = require('./test/util').run;
// run(cmd => cmd.arg().name('qwe').end().arg().name('zxc').end().act(function(opts, args) { console.log({opts, args}); }), ['qwe', 'zxc']) // cmd and args
// .then(res => {
// // code
// // stdout
// // stderr
// console.log(res);
// });
run(cmd => cmd.opt().name('version').short('v').only().flag().act((opts) => { return 'aasd'; }), ['-v']) // cmd and args
.then(res => {
// code
// stdout
// stderr
console.log(res);
});

View file

@ -0,0 +1,130 @@
Color = require('./color').Color
Cmd = require('./cmd').Cmd
Opt = require('./opt').Opt
###*
Argument
Unnamed entity. From command line arguments passed as list of unnamed values.
@namespace
@class Presents argument
###
exports.Arg = class Arg
###*
@constructs
@param {COA.Cmd} cmd parent command
###
constructor: (@_cmd) -> @_cmd._args.push @
###*
Set a canonical argument identifier to be used anywhere in text messages.
@param {String} _name argument name
@returns {COA.Arg} this instance (for chainability)
###
name: Opt::name
###*
Set a long description for argument to be used anywhere in text messages.
@param {String} _title argument title
@returns {COA.Arg} this instance (for chainability)
###
title: Cmd::title
###*
Makes an argument accepts multiple values.
Otherwise, the value will be used by the latter passed.
@returns {COA.Arg} this instance (for chainability)
###
arr: Opt::arr
###*
Makes an argument required.
@returns {COA.Arg} this instance (for chainability)
###
req: Opt::req
###*
Set a validation (or value) function for argument.
Value from command line passes through before becoming available from API.
Using for validation and convertion simple types to any values.
@param {Function} _val validating function,
invoked in the context of argument instance
and has one parameter with value from command line
@returns {COA.Arg} this instance (for chainability)
###
val: Opt::val
###*
Set a default value for argument.
Default value passed through validation function as ordinary value.
@param {Object} _def
@returns {COA.Arg} this instance (for chainability)
###
def: Opt::def
###*
Set custom additional completion for current argument.
@param {Function} completion generation function,
invoked in the context of argument instance.
Accepts parameters:
- {Object} opts completion options
It can return promise or any other value treated as result.
@returns {COA.Arg} this instance (for chainability)
###
comp: Cmd::comp
###*
Make argument value inputting stream.
It's add useful validation and shortcut for STDIN.
@returns {COA.Arg} this instance (for chainability)
###
input: Opt::input
###*
Make argument value outputing stream.
It's add useful validation and shortcut for STDOUT.
@returns {COA.Arg} this instance (for chainability)
###
output: Opt::output
_parse: (arg, args) ->
@_saveVal(args, arg)
_saveVal: Opt::_saveVal
_checkParsed: (opts, args) -> not args.hasOwnProperty(@_name)
_usage: ->
res = []
res.push Color('lpurple', @_name.toUpperCase()), ' : ', @_title
if @_req then res.push ' ', Color('lred', '(required)')
res.join ''
_requiredText: -> 'Missing required argument:\n ' + @_usage()
###*
Return rejected promise with error code.
Use in .val() for return with error.
@param {Object} reject reason
You can customize toString() method and exitCode property
of reason object.
@returns {Q.promise} rejected promise
###
reject: Cmd::reject
###*
Finish chain for current option and return parent command instance.
@returns {COA.Cmd} parent command
###
end: Cmd::end
###*
Apply function with arguments in context of arg instance.
@param {Function} fn
@param {Array} args
@returns {COA.Arg} this instance (for chainability)
###
apply: Cmd::apply

View file

@ -0,0 +1,456 @@
UTIL = require 'util'
PATH = require 'path'
Color = require('./color').Color
Q = require('q')
#inspect = require('eyes').inspector { maxLength: 99999, stream: process.stderr }
###*
Command
Top level entity. Commands may have options and arguments.
@namespace
@class Presents command
###
exports.Cmd = class Cmd
###*
@constructs
@param {COA.Cmd} [cmd] parent command
###
constructor: (cmd) ->
if this not instanceof Cmd
return new Cmd cmd
@_parent cmd
@_cmds = []
@_cmdsByName = {}
@_opts = []
@_optsByKey = {}
@_args = []
@_ext = false
@get: (propertyName, func) ->
Object.defineProperty @::, propertyName,
configurable: true
enumerable: true
get: func
###*
Returns object containing all its subcommands as methods
to use from other programs.
@returns {Object}
###
@get 'api', () ->
if not @_api
@_api = => @invoke.apply @, arguments
for c of @_cmdsByName
do (c) =>
@_api[c] = @_cmdsByName[c].api
@_api
_parent: (cmd) ->
@_cmd = cmd or this
if cmd
cmd._cmds.push @
if @_name then @_cmd._cmdsByName[@_name] = @
@
###*
Set a canonical command identifier to be used anywhere in the API.
@param {String} _name command name
@returns {COA.Cmd} this instance (for chainability)
###
name: (@_name) ->
if @_cmd isnt @ then @_cmd._cmdsByName[_name] = @
@
###*
Set a long description for command to be used anywhere in text messages.
@param {String} _title command title
@returns {COA.Cmd} this instance (for chainability)
###
title: (@_title) -> @
###*
Create new or add existing subcommand for current command.
@param {COA.Cmd} [cmd] existing command instance
@returns {COA.Cmd} new subcommand instance
###
cmd: (cmd) ->
if cmd then cmd._parent @
else new Cmd @
###*
Create option for current command.
@returns {COA.Opt} new option instance
###
opt: -> new (require('./opt').Opt) @
###*
Create argument for current command.
@returns {COA.Opt} new argument instance
###
arg: -> new (require('./arg').Arg) @
###*
Add (or set) action for current command.
@param {Function} act action function,
invoked in the context of command instance
and has the parameters:
- {Object} opts parsed options
- {Array} args parsed arguments
- {Object} res actions result accumulator
It can return rejected promise by Cmd.reject (in case of error)
or any other value treated as result.
@param {Boolean} [force=false] flag for set action instead add to existings
@returns {COA.Cmd} this instance (for chainability)
###
act: (act, force) ->
return @ unless act
if not force and @_act
@_act.push act
else
@_act = [act]
@
###*
Set custom additional completion for current command.
@param {Function} completion generation function,
invoked in the context of command instance.
Accepts parameters:
- {Object} opts completion options
It can return promise or any other value treated as result.
@returns {COA.Cmd} this instance (for chainability)
###
comp: (@_comp) -> @
###*
Apply function with arguments in context of command instance.
@param {Function} fn
@param {Array} args
@returns {COA.Cmd} this instance (for chainability)
###
apply: (fn, args...) ->
fn.apply this, args
@
###*
Make command "helpful", i.e. add -h --help flags for print usage.
@returns {COA.Cmd} this instance (for chainability)
###
helpful: ->
@opt()
.name('help').title('Help')
.short('h').long('help')
.flag()
.only()
.act ->
return @usage()
.end()
###*
Adds shell completion to command, adds "completion" subcommand,
that makes all the magic.
Must be called only on root command.
@returns {COA.Cmd} this instance (for chainability)
###
completable: ->
@cmd()
.name('completion')
.apply(require './completion')
.end()
###*
Allow command to be extendable by external node.js modules.
@param {String} [pattern] Pattern of node.js module to find subcommands at.
@returns {COA.Cmd} this instance (for chainability)
###
extendable: (pattern) ->
@_ext = pattern or true
@
_exit: (msg, code) ->
process.once 'exit', ->
if msg then console.error msg
process.exit code or 0
###*
Build full usage text for current command instance.
@returns {String} usage text
###
usage: ->
res = []
if @_title then res.push @_fullTitle()
res.push('', 'Usage:')
if @_cmds.length then res.push(['', '',
Color('lred', @_fullName()),
Color('lblue', 'COMMAND'),
Color('lgreen', '[OPTIONS]'),
Color('lpurple', '[ARGS]')].join ' ')
if @_opts.length + @_args.length then res.push(['', '',
Color('lred', @_fullName()),
Color('lgreen', '[OPTIONS]'),
Color('lpurple', '[ARGS]')].join ' ')
res.push(
@_usages(@_cmds, 'Commands'),
@_usages(@_opts, 'Options'),
@_usages(@_args, 'Arguments'))
res.join '\n'
_usage: ->
Color('lblue', @_name) + ' : ' + @_title
_usages: (os, title) ->
unless os.length then return
res = ['', title + ':']
for o in os
res.push ' ' + o._usage()
res.join '\n'
_fullTitle: ->
(if @_cmd is this then '' else @_cmd._fullTitle() + '\n') + @_title
_fullName: ->
(if this._cmd is this then '' else @_cmd._fullName() + ' ') + PATH.basename(@_name)
_ejectOpt: (opts, opt) ->
if (pos = opts.indexOf(opt)) >= 0
if opts[pos]._arr
opts[pos]
else
opts.splice(pos, 1)[0]
_checkRequired: (opts, args) ->
if not (@_opts.filter (o) -> o._only and o._name of opts).length
all = @_opts.concat @_args
while i = all.shift()
if i._req and i._checkParsed opts, args
return @reject i._requiredText()
_parseCmd: (argv, unparsed = []) ->
argv = argv.concat()
optSeen = false
while i = argv.shift()
if not i.indexOf '-'
optSeen = true
if not optSeen and /^\w[\w-_]*$/.test(i)
cmd = @_cmdsByName[i]
if not cmd and @_ext
# construct package name to require
if typeof @_ext is 'string'
if ~@_ext.indexOf('%s')
# use formatted string
pkg = UTIL.format(@_ext, i)
else
# just append subcommand name to the prefix
pkg = @_ext + i
else if @_ext is true
# use default scheme: <command>-<subcommand>-<subcommand> and so on
pkg = i
c = @
loop
pkg = c._name + '-' + pkg
if c._cmd is c then break
c = c._cmd
try
cmdDesc = require(pkg)
catch e
if cmdDesc
if typeof cmdDesc == 'function'
# set create subcommand, set its name and apply imported function
@cmd()
.name(i)
.apply(cmdDesc)
.end()
else if typeof cmdDesc == 'object'
# register subcommand
@cmd(cmdDesc)
# set command name
cmdDesc.name(i)
else
throw new Error 'Error: Unsupported command declaration type, ' +
'should be function or COA.Cmd() object'
cmd = @_cmdsByName[i]
if cmd
return cmd._parseCmd argv, unparsed
unparsed.push i
{ cmd: @, argv: unparsed }
_parseOptsAndArgs: (argv) ->
opts = {}
args = {}
nonParsedOpts = @_opts.concat()
nonParsedArgs = @_args.concat()
while i = argv.shift()
# opt
if i isnt '--' and not i.indexOf '-'
if m = i.match /^(--\w[\w-_]*)=(.*)$/
i = m[1]
# suppress 'unknown argument' error for flag options with values
if not @_optsByKey[i]._flag
argv.unshift m[2]
if opt = @_ejectOpt nonParsedOpts, @_optsByKey[i]
if Q.isRejected(res = opt._parse argv, opts)
return res
else
return @reject "Unknown option: #{ i }"
# arg
else
if i is '--'
i = argv.splice(0)
i = if Array.isArray(i) then i else [i]
while a = i.shift()
if arg = nonParsedArgs.shift()
if arg._arr then nonParsedArgs.unshift arg
if Q.isRejected(res = arg._parse a, args)
return res
else
return @reject "Unknown argument: #{ a }"
# set defaults
{
opts: @_setDefaults(opts, nonParsedOpts),
args: @_setDefaults(args, nonParsedArgs)
}
_setDefaults: (params, desc) ->
for i in desc
if i._name not of params and '_def' of i
i._saveVal params, i._def
params
_processParams: (params, desc) ->
notExists = []
for i in desc
n = i._name
if n not of params
notExists.push i
continue
vals = params[n]
delete params[n]
if not Array.isArray vals
vals = [vals]
for v in vals
if Q.isRejected(res = i._saveVal(params, v))
return res
# set defaults
@_setDefaults params, notExists
_parseArr: (argv) ->
Q.when @_parseCmd(argv), (p) ->
Q.when p.cmd._parseOptsAndArgs(p.argv), (r) ->
{ cmd: p.cmd, opts: r.opts, args: r.args }
_do: (input) ->
Q.when input, (input) =>
cmd = input.cmd
[@_checkRequired].concat(cmd._act or []).reduce(
(res, act) ->
Q.when res, (res) ->
act.call(
cmd
input.opts
input.args
res)
undefined
)
###*
Parse arguments from simple format like NodeJS process.argv
and run ahead current program, i.e. call process.exit when all actions done.
@param {Array} argv
@returns {COA.Cmd} this instance (for chainability)
###
run: (argv = process.argv.slice(2)) ->
cb = (code) => (res) =>
if res
@_exit res.stack ? res.toString(), res.exitCode ? code
else
@_exit()
Q.when(@do(argv), cb(0), cb(1)).done()
@
###*
Convenient function to run command from tests.
@param {Array} argv
@returns {Q.Promise}
###
do: (argv) ->
@_do(@_parseArr argv || [])
###*
Invoke specified (or current) command using provided
options and arguments.
@param {String|Array} cmds subcommand to invoke (optional)
@param {Object} opts command options (optional)
@param {Object} args command arguments (optional)
@returns {Q.Promise}
###
invoke: (cmds = [], opts = {}, args = {}) ->
if typeof cmds == 'string'
cmds = cmds.split(' ')
if arguments.length < 3
if not Array.isArray cmds
args = opts
opts = cmds
cmds = []
Q.when @_parseCmd(cmds), (p) =>
if p.argv.length
return @reject "Unknown command: " + cmds.join ' '
Q.all([@_processParams(opts, @_opts), @_processParams(args, @_args)])
.spread (opts, args) =>
@_do({ cmd: p.cmd, opts: opts, args: args })
# catch fails from .only() options
.fail (res) =>
if res and res.exitCode is 0
res.toString()
else
@reject(res)
###*
Return reject of actions results promise with error code.
Use in .act() for return with error.
@param {Object} reject reason
You can customize toString() method and exitCode property
of reason object.
@returns {Q.promise} rejected promise
###
reject: (reason) -> Q.reject(reason)
###*
Finish chain for current subcommand and return parent command instance.
@returns {COA.Cmd} parent command
###
end: -> @_cmd

View file

@ -0,0 +1,25 @@
colors =
black: '30'
dgray: '1;30'
red: '31'
lred: '1;31'
green: '32'
lgreen: '1;32'
brown: '33'
yellow: '1;33'
blue: '34'
lblue: '1;34'
purple: '35'
lpurple: '1;35'
cyan: '36'
lcyan: '1;36'
lgray: '37'
white: '1;37'
exports.Color = (c, str) ->
# Use \x1B instead of \033 because of CoffeeScript 1.3.x compilation error
[
'\x1B[', colors[c], 'm'
str
'\x1B[m'
].join ''

View file

@ -0,0 +1,156 @@
###*
Most of the code adopted from the npm package shell completion code.
See https://github.com/isaacs/npm/blob/master/lib/completion.js
###
Q = require 'q'
escape = require('./shell').escape
unescape = require('./shell').unescape
module.exports = ->
@title('Shell completion')
.helpful()
.arg()
.name('raw')
.title('Completion words')
.arr()
.end()
.act (opts, args) ->
if process.platform == 'win32'
e = new Error 'shell completion not supported on windows'
e.code = 'ENOTSUP'
e.errno = require('constants').ENOTSUP
return @reject(e)
# if the COMP_* isn't in the env, then just dump the script
if !process.env.COMP_CWORD? or !process.env.COMP_LINE? or !process.env.COMP_POINT?
return dumpScript(@_cmd._name)
console.error 'COMP_LINE: %s', process.env.COMP_LINE
console.error 'COMP_CWORD: %s', process.env.COMP_CWORD
console.error 'COMP_POINT: %s', process.env.COMP_POINT
console.error 'args: %j', args.raw
# completion opts
opts = getOpts args.raw
# cmd
{ cmd, argv } = @_cmd._parseCmd opts.partialWords
Q.when complete(cmd, opts), (compls) ->
console.error 'filtered: %j', compls
console.log compls.map(escape).join('\n')
dumpScript = (name) ->
fs = require 'fs'
path = require 'path'
defer = Q.defer()
fs.readFile path.resolve(__dirname, 'completion.sh'), 'utf8', (err, d) ->
if err then return defer.reject err
d = d.replace(/{{cmd}}/g, path.basename name).replace(/^\#\!.*?\n/, '')
onError = (err) ->
# Darwin is a real dick sometimes.
#
# This is necessary because the "source" or "." program in
# bash on OS X closes its file argument before reading
# from it, meaning that you get exactly 1 write, which will
# work most of the time, and will always raise an EPIPE.
#
# Really, one should not be tossing away EPIPE errors, or any
# errors, so casually. But, without this, `. <(cmd completion)`
# can never ever work on OS X.
if err.errno == require('constants').EPIPE
process.stdout.removeListener 'error', onError
defer.resolve()
else
defer.reject(err)
process.stdout.on 'error', onError
process.stdout.write d, -> defer.resolve()
defer.promise
getOpts = (argv) ->
# get the partial line and partial word, if the point isn't at the end
# ie, tabbing at: cmd foo b|ar
line = process.env.COMP_LINE
w = +process.env.COMP_CWORD
point = +process.env.COMP_POINT
words = argv.map unescape
word = words[w]
partialLine = line.substr 0, point
partialWords = words.slice 0, w
# figure out where in that last word the point is
partialWord = argv[w] or ''
i = partialWord.length
while partialWord.substr(0, i) isnt partialLine.substr(-1 * i) and i > 0
i--
partialWord = unescape partialWord.substr 0, i
if partialWord then partialWords.push partialWord
{
line: line
w: w
point: point
words: words
word: word
partialLine: partialLine
partialWords: partialWords
partialWord: partialWord
}
complete = (cmd, opts) ->
compls = []
# complete on cmds
if opts.partialWord.indexOf('-')
compls = Object.keys(cmd._cmdsByName)
# Complete on required opts without '-' in last partial word
# (if required not already specified)
#
# Commented out because of uselessness:
# -b, --block suggest results in '-' on cmd line;
# next completion suggest all options, because of '-'
#.concat Object.keys(cmd._optsByKey).filter (v) -> cmd._optsByKey[v]._req
else
# complete on opt values: --opt=| case
if m = opts.partialWord.match /^(--\w[\w-_]*)=(.*)$/
optWord = m[1]
optPrefix = optWord + '='
else
# complete on opts
# don't complete on opts in case of --opt=val completion
# TODO: don't complete on opts in case of unknown arg after commands
# TODO: complete only on opts with arr() or not already used
# TODO: complete only on full opts?
compls = Object.keys cmd._optsByKey
# complete on opt values: next arg case
if not (o = opts.partialWords[opts.w - 1]).indexOf '-'
optWord = o
# complete on opt values: completion
if optWord and opt = cmd._optsByKey[optWord]
if not opt._flag and opt._comp
compls = Q.join compls, Q.when opt._comp(opts), (c, o) ->
c.concat o.map (v) -> (optPrefix or '') + v
# TODO: complete on args values (context aware, custom completion?)
# custom completion on cmds
if cmd._comp
compls = Q.join compls, Q.when(cmd._comp(opts)), (c, o) ->
c.concat o
# TODO: context aware custom completion on cmds, opts and args
# (can depend on already entered values, especially options)
Q.when compls, (compls) ->
console.error 'partialWord: %s', opts.partialWord
console.error 'compls: %j', compls
compls.filter (c) -> c.indexOf(opts.partialWord) is 0

View file

@ -0,0 +1,5 @@
exports.Cmd = require('./cmd').Cmd
exports.Opt = require('./cmd').Opt
exports.Arg = require('./cmd').Arg
exports.shell = require('./shell')
exports.require = require;

View file

@ -0,0 +1,243 @@
fs = require 'fs'
Q = require 'q'
Color = require('./color').Color
Cmd = require('./cmd').Cmd
###*
Option
Named entity. Options may have short and long keys for use from command line.
@namespace
@class Presents option
###
exports.Opt = class Opt
###*
@constructs
@param {COA.Cmd} cmd parent command
###
constructor: (@_cmd) -> @_cmd._opts.push @
###*
Set a canonical option identifier to be used anywhere in the API.
@param {String} _name option name
@returns {COA.Opt} this instance (for chainability)
###
name: (@_name) -> @
###*
Set a long description for option to be used anywhere in text messages.
@param {String} _title option title
@returns {COA.Opt} this instance (for chainability)
###
title: Cmd::title
###*
Set a short key for option to be used with one hyphen from command line.
@param {String} _short
@returns {COA.Opt} this instance (for chainability)
###
short: (@_short) -> @_cmd._optsByKey['-' + _short] = @
###*
Set a short key for option to be used with double hyphens from command line.
@param {String} _long
@returns {COA.Opt} this instance (for chainability)
###
long: (@_long) -> @_cmd._optsByKey['--' + _long] = @
###*
Make an option boolean, i.e. option without value.
@returns {COA.Opt} this instance (for chainability)
###
flag: () ->
@_flag = true
@
###*
Makes an option accepts multiple values.
Otherwise, the value will be used by the latter passed.
@returns {COA.Opt} this instance (for chainability)
###
arr: ->
@_arr = true
@
###*
Makes an option required.
@returns {COA.Opt} this instance (for chainability)
###
req: ->
@_req = true
@
###*
Makes an option to act as a command,
i.e. program will exit just after option action.
@returns {COA.Opt} this instance (for chainability)
###
only: ->
@_only = true
@
###*
Set a validation (or value) function for option.
Value from command line passes through before becoming available from API.
Using for validation and convertion simple types to any values.
@param {Function} _val validating function,
invoked in the context of option instance
and has one parameter with value from command line
@returns {COA.Opt} this instance (for chainability)
###
val: (@_val) -> @
###*
Set a default value for option.
Default value passed through validation function as ordinary value.
@param {Object} _def
@returns {COA.Opt} this instance (for chainability)
###
def: (@_def) -> @
###*
Make option value inputting stream.
It's add useful validation and shortcut for STDIN.
@returns {COA.Opt} this instance (for chainability)
###
input: ->
# XXX: hack to workaround a bug in node 0.6.x,
# see https://github.com/joyent/node/issues/2130
process.stdin.pause();
@
.def(process.stdin)
.val (v) ->
if typeof v is 'string'
if v is '-'
process.stdin
else
s = fs.createReadStream v, { encoding: 'utf8' }
s.pause()
s
else v
###*
Make option value outputing stream.
It's add useful validation and shortcut for STDOUT.
@returns {COA.Opt} this instance (for chainability)
###
output: ->
@
.def(process.stdout)
.val (v) ->
if typeof v is 'string'
if v is '-'
process.stdout
else
fs.createWriteStream v, { encoding: 'utf8' }
else v
###*
Add action for current option command.
This action is performed if the current option
is present in parsed options (with any value).
@param {Function} act action function,
invoked in the context of command instance
and has the parameters:
- {Object} opts parsed options
- {Array} args parsed arguments
- {Object} res actions result accumulator
It can return rejected promise by Cmd.reject (in case of error)
or any other value treated as result.
@returns {COA.Opt} this instance (for chainability)
###
act: (act) ->
opt = @
name = @_name
@_cmd.act (opts) ->
if name of opts
res = act.apply @, arguments
if opt._only
Q.when res, (res) =>
@reject {
toString: -> res.toString()
exitCode: 0
}
else
res
@
###*
Set custom additional completion for current option.
@param {Function} completion generation function,
invoked in the context of option instance.
Accepts parameters:
- {Object} opts completion options
It can return promise or any other value treated as result.
@returns {COA.Opt} this instance (for chainability)
###
comp: Cmd::comp
_saveVal: (opts, val) ->
if @_val then val = @_val val
if @_arr
(opts[@_name] or= []).push val
else
opts[@_name] = val
val
_parse: (argv, opts) ->
@_saveVal(
opts,
if @_flag
true
else
argv.shift()
)
_checkParsed: (opts, args) -> not opts.hasOwnProperty @_name
_usage: ->
res = []
nameStr = @_name.toUpperCase()
if @_short
res.push '-', Color 'lgreen', @_short
unless @_flag then res.push ' ' + nameStr
res.push ', '
if @_long
res.push '--', Color 'green', @_long
unless @_flag then res.push '=' + nameStr
res.push ' : ', @_title
if @_req then res.push ' ', Color('lred', '(required)')
res.join ''
_requiredText: -> 'Missing required option:\n ' + @_usage()
###*
Return rejected promise with error code.
Use in .val() for return with error.
@param {Object} reject reason
You can customize toString() method and exitCode property
of reason object.
@returns {Q.promise} rejected promise
###
reject: Cmd::reject
###*
Finish chain for current option and return parent command instance.
@returns {COA.Cmd} parent command
###
end: Cmd::end
###*
Apply function with arguments in context of option instance.
@param {Function} fn
@param {Array} args
@returns {COA.Opt} this instance (for chainability)
###
apply: Cmd::apply

View file

@ -0,0 +1,10 @@
exports.unescape = (w) ->
w = if w.charAt(0) is '"'
w.replace(/^"|([^\\])"$/g, '$1')
else
w.replace(/\\ /g, ' ')
w.replace(/\\("|'|\$|`|\\)/g, '$1')
exports.escape = (w) ->
w = w.replace(/(["'$`\\])/g,'\\$1')
if w.match(/\s+/) then '"' + w + '"' else w

View file

@ -0,0 +1,496 @@
var assert = require('chai').assert,
COA = require('..');
/**
* Mocha BDD interface.
*/
/** @name describe @function */
/** @name it @function */
/** @name before @function */
/** @name after @function */
/** @name beforeEach @function */
/** @name afterEach @function */
describe('Opt', function() {
describe('Unknown option', function() {
var cmd = COA.Cmd();
it('should fail', function() {
return cmd.do(['-a'])
.then(assert.fail, emptyFn);
});
});
describe('Short options', function() {
var cmd = COA.Cmd()
.opt()
.name('a')
.short('a')
.end()
.opt()
.name('b')
.short('b')
.end()
.act(function(opts) {
return opts;
});
it('should return passed values', function() {
return cmd.do(['-a', 'a', '-b', 'b'])
.then(function(res) {
assert.deepEqual(res, { a: 'a', b: 'b' });
});
});
});
describe('Long options', function() {
var cmd = COA.Cmd()
.opt()
.name('long1')
.long('long1')
.end()
.opt()
.name('long2')
.long('long2')
.end()
.act(function(opts) {
return opts;
});
it('should return passed values', function() {
return cmd.do(['--long1', 'long value', '--long2=another long value'])
.then(function(res) {
assert.deepEqual(res, { long1: 'long value', long2: 'another long value' });
});
});
});
describe('Array option', function() {
var cmd = COA.Cmd()
.opt()
.name('a')
.short('a')
.arr()
.end()
.act(function(opts) {
return opts;
});
it('should return array of passed values', function() {
return cmd.do(['-a', '1', '-a', '2'])
.then(function(res) {
assert.deepEqual(res, { a: ['1', '2'] });
});
});
});
describe('Required option', function() {
var cmd = COA.Cmd()
.opt()
.name('a')
.short('a')
.req()
.end()
.act(function(opts) {
return opts;
});
it('should fail if not specified', function() {
return cmd.do()
.then(assert.fail, emptyFn);
});
it('should return passed value if specified', function() {
return cmd.do(['-a', 'test'])
.then(function(opts) {
assert.equal(opts.a, 'test');
});
});
});
describe('Option with default value', function() {
var cmd = COA.Cmd()
.opt()
.name('a')
.short('a')
.def('aaa')
.end()
.act(function(opts) {
return opts;
});
it('should return default value if not specified', function() {
return cmd.do()
.then(function(opts) {
assert.equal(opts.a, 'aaa');
});
});
it('should return passed value if specified', function() {
return cmd.do(['-a', 'test'])
.then(function(opts) {
assert.equal(opts.a, 'test');
});
});
});
describe('Validated / transformed option', function() {
var cmd = COA.Cmd()
.opt()
.name('a')
.short('a')
.val(function(v) {
if (v === 'invalid') return this.reject('fail');
return { value: v };
})
.end()
.act(function(opts) {
return opts;
});
it('should fail if custom checks suppose to do so', function() {
return cmd.do(['-a', 'invalid'])
.then(assert.fail, emptyFn);
});
it('should return transformed value', function() {
return cmd.do(['-a', 'test'])
.then(function(opts) {
assert.deepEqual(opts.a, { value: 'test' });
});
});
});
describe('Only option (--version case)', function() {
var ver = require('../package.json').version,
cmd = COA.Cmd()
.opt()
.name('version')
.long('version')
.flag()
.only()
.act(function() {
return ver;
})
.end()
.opt()
.name('req')
.short('r')
.req()
.end();
it('should process the only() option', function() {
return cmd.do(['--version'])
.then(assert.fail, function(res) {
assert.equal(res, ver);
});
});
});
it('input()');
it('output()');
});
describe('Arg', function() {
describe('Unknown arg', function() {
var cmd = COA.Cmd();
it('should fail', function() {
return cmd.do(['test'])
.then(assert.fail, emptyFn);
});
});
describe('Unknown arg after known', function() {
var cmd = COA.Cmd()
.arg()
.name('a')
.end();
it('should fail', function() {
return cmd.do(['test', 'unknown'])
.then(assert.fail, emptyFn);
});
});
describe('Array arg', function() {
var cmd = COA.Cmd()
.arg()
.name('a')
.arr()
.end()
.act(function(opts, args) {
return args;
});
it('should return array of passed values', function() {
return cmd.do(['value 1', 'value 2'])
.then(function(args) {
assert.deepEqual(args, { a: ['value 1', 'value 2'] });
});
});
});
describe('Required arg', function() {
var cmd = COA.Cmd()
.arg()
.name('a')
.req()
.end()
.act(function(opts, args) {
return args;
});
it('should fail if not specified', function() {
return cmd.do()
.then(assert.fail, emptyFn);
});
it('should return passed value if specified', function() {
return cmd.do(['value'])
.then(function(args) {
assert.equal(args.a, 'value');
});
});
});
describe('Args after options', function() {
var cmd = COA.Cmd()
.opt()
.name('opt')
.long('opt')
.end()
.arg()
.name('arg1')
.end()
.arg()
.name('arg2')
.arr()
.end()
.act(function(opts, args) {
return { opts: opts, args: args };
});
it('should return passed values', function() {
return cmd.do(['--opt', 'value', 'value', 'value 1', 'value 2'])
.then(function(o) {
assert.deepEqual(o, {
opts: { opt: 'value' },
args: {
arg1: 'value',
arg2: ['value 1', 'value 2']
}
});
});
});
});
describe('Raw args', function() {
var cmd = COA.Cmd()
.arg()
.name('raw')
.arr()
.end()
.act(function(opts, args) {
return args;
});
it('should return passed arg values', function() {
return cmd.do(['--', 'raw', 'arg', 'values'])
.then(function(args) {
assert.deepEqual(args, { raw: ['raw', 'arg', 'values'] });
});
});
});
});
describe('Cmd', function() {
var doTest = function(o) {
assert.deepEqual(o, {
opts: { opt: 'value' },
args: {
arg1: 'value',
arg2: ['value 1', 'value 2']
}
});
},
invokeOpts = { opt: 'value' },
invokeArgs = {
arg1: 'value',
arg2: ['value 1', 'value 2']
};
describe('Subcommand', function() {
var cmd = COA.Cmd()
.cmd()
.name('command')
.opt()
.name('opt')
.long('opt')
.end()
.arg()
.name('arg1')
.end()
.arg()
.name('arg2')
.arr()
.end()
.act(function(opts, args) {
return { opts: opts, args: args };
})
.end();
describe('when specified on command line', function() {
it('should be invoked and accept passed opts and args', function() {
return cmd.do(['command', '--opt', 'value', 'value', 'value 1', 'value 2'])
.then(doTest);
});
});
describe('when invoked using api', function() {
it('should be invoked and accept passed opts and args', function() {
return cmd.api.command(invokeOpts, invokeArgs)
.then(doTest);
});
});
describe('when invoked using invoke()', function() {
it('should be invoked and accept passed opts and args', function() {
return cmd.invoke('command', invokeOpts, invokeArgs)
.then(doTest);
});
});
describe('when unexisting command invoked using invoke()', function() {
it('should fail', function() {
return cmd.invoke('unexistent')
.then(assert.fail, emptyFn);
});
});
});
describe('External subcommand', function() {
describe('default scheme: cmd.extendable()', function() {
describe('when described as a function', function() {
var cmd = COA.Cmd()
.name('coa')
.extendable();
it('should be invoked and accept passed opts and args', function() {
return cmd.do(['test', '--opt', 'value', 'value', 'value 1', 'value 2'])
.then(doTest);
});
});
describe('when described as an COA.Cmd() object', function() {
var cmd = COA.Cmd()
.name('coa')
.extendable();
it('should be invoked and accept passed opts and args', function() {
return cmd.do(['test-obj', '--opt', 'value', 'value', 'value 1', 'value 2'])
.then(doTest);
});
});
describe('2nd level subcommand', function() {
var cmd = COA.Cmd()
.name('coa')
.cmd()
.name('test')
.extendable()
.end();
it('should be invoked and accept passed opts and args', function() {
return cmd.do(['test', 'obj', '--opt', 'value', 'value', 'value 1', 'value 2'])
.then(doTest);
});
});
});
describe("common prefix: cmd.extendable('coa-')", function() {
describe('when described as a function', function() {
var cmd = COA.Cmd()
.name('coa')
.extendable('coa-');
it('should be invoked and accept passed opts and args', function() {
return cmd.do(['test', '--opt', 'value', 'value', 'value 1', 'value 2'])
.then(doTest);
});
});
});
describe("format string: cmd.extendable('coa-%s')", function() {
describe('when described as a function', function() {
var cmd = COA.Cmd()
.name('coa')
.extendable('coa-%s');
it('should be invoked and accept passed opts and args', function() {
return cmd.do(['test', '--opt', 'value', 'value', 'value 1', 'value 2'])
.then(doTest);
});
});
});
});
it('helpful(), name(), title()');
});
function emptyFn() {
// empty function
}

View file

@ -0,0 +1,2 @@
--reporter spec
--timeout 20

View file

@ -0,0 +1,60 @@
var assert = require('chai').assert,
shell = require('..').shell;
/**
* Mocha BDD interface.
*/
/** @name describe @function */
/** @name it @function */
/** @name before @function */
/** @name after @function */
/** @name beforeEach @function */
/** @name afterEach @function */
describe('shell', function() {
describe('escape()', function() {
var escape = shell.escape;
it('Should wrap values with spaces in double quotes', function() {
assert.equal(escape('asd abc'), '"asd abc"');
});
it('Should escape double quote "', function() {
assert.equal(escape('"asd'), '\\"asd');
});
it("Should escape single quote '", function() {
assert.equal(escape("'asd"), "\\'asd");
});
it('Should escape backslash \\', function() {
assert.equal(escape('\\asd'), '\\\\asd');
});
it('Should escape dollar $', function() {
assert.equal(escape('$asd'), '\\$asd');
});
it('Should escape backtick `', function() {
assert.equal(escape('`asd'), '\\`asd');
});
});
describe('unescape()', function() {
var unescape = shell.unescape;
it('Should strip double quotes at the both ends', function() {
assert.equal(unescape('"asd"'), 'asd');
});
it('Should not strip escaped double quotes at the both ends', function() {
assert.equal(unescape('\\"asd\\"'), '"asd"');
});
});
});

View file

@ -0,0 +1,9 @@
require('..').Cmd()
.name('bla')
.title('Bla bla bla')
.helpful()
.invoke({ help: true })
.then(function(res) {
console.log(res);
})
.done(); // Q.done()

View file

@ -0,0 +1,6 @@
var argv = process.argv.slice(2);
require('..').Cmd()
.name('bla')
.title('Bla bla bla')
.helpful()
.run(argv.length? argv : ['-h']);

View file

@ -0,0 +1,325 @@
## 2.3.2 (March 11, 2016)
- Fix infinite loop on value parse (#328)
## 2.3.1 (January 6, 2016)
- Added `\0` IE hack support (#320)
## 2.3.0 (October 25, 2016)
- Added `beforeCompress` and `afterCompress` options support (#316)
- Fixed crash on empty argument in function (#317)
## 2.2.1 (July 25, 2016)
- Fixed shorthand optimisation issue when value has a color value or something unknown (#311)
- Fixed `cursor` broken fallback (#306)
## 2.2.0 (June 23, 2016)
- Implement AST cloning by adding `clone()` [function](https://github.com/css/csso#cloneast) and `clone` [option](https://github.com/css/csso#compressast-options) for `compress()` function (#296)
- Fix parse and translate attribute selector with flags but w/o operator (i.e. `[attrName i]`)
- Don't merge rules with flagged attribute selectors with others (#291)
- Take in account functions when merge TRBL-properties (#297, thanks to @ArturAralin)
- Improve partial merge (#304)
- Tweak scanner, reduce code deoptimizations and other small improvements
## 2.1.1 (May 11, 2016)
- Fix wrong declaration with `\9` hack merge (#295)
## 2.1.0 (May 8, 2016)
- New option `comments` to specify what comments to left: `exclamation`, `first-exclamation` and `none`
- Add `offset` to CSS parse error details
- Fix token `offset` computation
## 2.0.0 (April 5, 2016)
- No more `gonzales` AST format and related code
- `minify()` and `minifyBlock()` is always return an object as result now (i.e. `{ css: String, map: SourceMapGenerator or null }`)
- `parse()`
- Returns AST in new format (so called `internal`)
- Dynamic scanner implemented
- New AST format + dynamic scanner = performance boost and less memory consumption
- No more `context` argument, context should be specified via `options`
- Supported contexts now: `stylesheet`, `atrule`, `atruleExpression`, `ruleset`, `selector`, `simpleSelector`, `block`, `declaration` and `value`
- Drop `needPositions` option, `positions` option should be used instead
- Drop `needInfo` option, `info` object is attaching to nodes when some information is requested by `options`
- `options` should be an object, otherwise it treats as empty object
- `compress()`
- No more AST converting (performance boost and less memory consumption)
- Drop `outputAst` option
- Returns an object as result instead of AST (i.e. `{ ast: Object }`)
- Drop methods: `justDoIt()`, `stringify()`, `cleanInfo()`
## 1.8.1 (March 30, 2016)
- Don't remove spaces after function/braces/urls since unsafe (#289)
## 1.8.0 (March 24, 2016)
- Usage data support:
- Filter rulesets by tag names, class names and ids white lists.
- More aggressive ruleset moving using class name scopes information.
- New CLI option `--usage` to pass usage data file.
- Improve initial ruleset merge
- Change order of ruleset processing, now it's left to right. Previously unmerged rulesets may prevent lookup and other rulesets merge.
- Difference in pseudo signature just prevents ruleset merging, but don't stop lookup.
- Simplify block comparison (performance).
- New method `csso.minifyBlock()` for css block compression (e.g. `style` attribute content).
- Ruleset merge improvement: at-rules with block (like `@media` or `@supports`) now can be skipped during ruleset merge lookup if doesn't contain something prevents it.
- FIX: Add negation (`:not()`) to pseudo signature to avoid unsafe merge (old browsers doesn't support it).
- FIX: Check nested parts of value when compute compatibility. It fixes unsafe property merging.
## 1.7.1 (March 16, 2016)
- pass block mode to tokenizer for correct parsing of declarations properties with `//` hack
- fix wrongly `@import` and `@charset` removal on double exclamation comment
## 1.7.0 (March 10, 2016)
- support for [CSS Custom Properties](https://www.w3.org/TR/css-variables/) (#279)
- rework RTBL properties merge better merge for values with special units and don't merge values with CSS-wide keywords (#255)
- remove redundant universal selectors (#178)
- take in account `!important` when check for property overriding (#280)
- don't merge `text-align` declarations with some values (#281)
- add spaces around `/deep/` combinator on translate, since it together with universal selector can produce a comment
- better keyword and property name resolving (tolerant to hacks and so on)
- integration improvements
- compression log function could be customized by `logger` option for `compress()` and `minify()`
- make possible to set initial line and column for parser
## 1.6.4 (March 1, 2016)
- `npm` publish issue (#276)
## 1.6.3 (February 29, 2016)
- add `file` to generated source map since other tools can relay on it in source map transform chain
## 1.6.2 (February 29, 2016)
- tweak some parse error messages and their positions
- fix `:not()` parsing and selector groups in `:not()` is supported now (#215)
- `needPosition` parser option is deprecated, `positions` option should be used instead (`needPosition` is used still if `positions` option omitted)
- expose internal AST API as `csso.internal.*`
- `minify()` adds `sourcesContent` by default when source map is generated
- bring back support for node.js `0.10` until major release (#275)
## 1.6.1 (February 28, 2016)
- fix exception on zero length dimension compress outside declaration (#273)
## 1.6.0 (February 27, 2016)
- **source maps support**
- parser remake:
- various parsing issues fixed
- fix unicode sequence processing in ident (#191)
- support for flags in attribute selector (#270)
- position (line and column) of parse error (#109)
- 4x performance boost, less memory consumption
- compressor refactoring
- internal AST is using doubly linked lists (with safe transformation support during iteration) instead of arrays
- rename `restructuring` to `restructure` option for `minify()`/`compress()` (`restructuring` is alias for `restructure` now, with lower priority)
- unquote urls when possible (#141, #60)
- setup code coverage and a number of related fixes
- add eslint to check unused things
## 1.5.4 (January 27, 2016)
- one more fix (in `restructRuleset` this time) with merge of rulesets when a ruleset with same specificity places between them (#264)
- disable partial merge of rulesets in `@keyframes` rulesets (until sure it's correct)
## 1.5.3 (January 25, 2016)
- don't override display values with different browser support (#259)
- fix publish issue (one of modules leak in development state)
## 1.5.2 (January 24, 2016)
- don't merge rulesets if between them a ruleset with same specificity (#264)
## 1.5.1 (January 14, 2016)
- ensure `-` is not used as an identifier in attribute selectors (thanks to @mathiasbynens)
- fix broken `justDoIt()` function
- various small fixes
## 1.5.0 (January 14, 2016)
### Parser
- attach minus to number
### Compressor
- split code base into small modules and related refactoring
- introduce internal AST format for compressor (`gonzales``internal` and `internal``gonzales` convertors, walkers, translator)
- various optimizations: no snapshots, using caches and indexes
- sort selectors, merge selectors in alphabet order
- compute selector's specificity
- better ruleset restructuring, improve compression of partially equal blocks
- better ruleset merge not only closest but also disjoined by other rulesets when safe
- join `@media` with same query
- `outputAst` new option to specify output AST format (`gonzales` by default for backward compatibility)
- remove quotes surrounding attribute values in attribute selectors when possible (#73)
- replace `from``0%` and `100%``to` at `@keyframes` (#205)
- prevent partial merge of rulesets at `@keyframes` (#80, #197)
### API
- walker for `gonzales` AST was implemented
### CLI
- new option `--stat` (output stat in `stderr`)
- new optional parameter `level` for `--debug` option
## 1.4.4 (December 10, 2015)
- prevent removal of spaces after braces that before identifier that breaking at-rules expressions (#258)
## 1.4.3 (December 4, 2015)
- fix unicode-range parsing that cause to wrong function detection (#250)
## 1.4.2 (November 9, 2015)
- allow spaces between `progid:` and rest part of value for IE's `filter` property as `autoprefixer` generates this kind of code (#249)
- fixes for Windows:
- correct processing new lines
- normalize file content in test suite
- fixes to work in strict mode (#252)
- init compressor dictionaries for every css block (#248, #251)
- bump uglify-js version
## 1.4.1 (October 20, 2015)
- allow merge for `display` property (#167, #244)
- more accurate `rect` (`clip` property value) merge
- fix typo when specifying options in cli (thanks to @Taritsyn)
- fix safe unit values merge with keyword values (#244)
- fix wrong descendant combinator removal (#246)
- build browser version on `prepublish` (thanks to @silentroach)
- parser: store whitespaces as single token (performance and reduce memory consumption)
- rearrange compress tests layout
## 1.4 (October 16, 2015)
Bringing project back to life. Changed files structure, cleaned up and refactored most of sources.
### Common
- single code base (no more `src` folder)
- build browser version with `browserify` (no more `make`, and `web` folder), browser version is available at `dist/csso-browser.js`
- main file is `lib/index.js` now
- minimal `node.js` version is `0.12` now
- restrict file list to publish on npm (no more useless folders and files in package)
- add `jscs` to control code style
- automate `gh-pages` update
- util functions reworked
- translator reworked
- test suite reworked
- compressor refactored
- initial parser refactoring
### API
- new method `minify(src, options)`, options:
- `restructuring` if set to `false`, disable structure optimisations (`true` by default)
- `debug` - outputs intermediate state of CSS during compression (`false` by default)
- deprecate `justDoIt()` method (use `minify` instead)
- rename `treeToString()` method to `stringify()`
- drop `printTree()` method
- AST node info
- `column` and `offset` added
- `ln` renamed to `line`
- fix line counting across multiple files and input with CR LF (#147)
### CLI
- completely reworked, use [clap](https://github.com/lahmatiy/clap) to parse argv
- add support for input from stdin (#128)
- drop undocumented and obsoleted options `--rule` and `--parser` (suppose nobody use it)
- drop `-off` alias for `--restructure-off` as incorrect (only one letter options should starts with single `-`)
- new option `--debug` that reflecting to `options.debug` for `minify`
### Parsing and optimizations
- keep all exclamation comments (#194)
- add `/deep/` combinator support (#209)
- attribute selector
- allow colon in attribute name (#237)
- support for namespaces (#233)
- color
- support all css/html colors
- convert `hsla` to `rgba` and `hls` to `rgb`
- convert `rgba` with 1 as alpha value to `rgb` (#122)
- interpolate `rgb` and `rgba` percentage values to absolute values
- replace percentage values in `rgba` for normalized/interpolated values
- lowercase hex colors and color names (#169)
- fix color minification when hex value replaced for color name (#176)
- fit rgb values to 0..255 range (#181)
- calc
- remove spaces for multiple operator in calc
- don't remove units inside calc (#222)
- fix wrong white space removal around `+` and `-` (#228)
- don't remove units in `flex` property as it could change value meaning (#200)
- don't merge `\9` hack values (#231)
- merge property values only if they have the same functions (#150, #227)
- don't merge property values with some sort of units (#140, #161)
- fix `!important` issue for `top-right-bottom-left` properties (#189)
- fix `top-right-bottom-left` properties merge (#139, #175)
- support for unicode-range (#148)
- don't crash on ruleset with no selector (#135)
- tolerant to class names that starts with digit (#99, #105)
- fix background compressing (#170)
## 1.3.12 (October 8, 2015)
- Case insensitive check for `!important` (#187)
- Fix problems with using `csso` as cli command on Windows (#83, #136, #142 and others)
- Remove byte order marker (the UTF-8 BOM) from input
- Don't strip space between funktion-funktion and funktion-vhash (#134)
- Don't merge TRBL values having \9 (hack for IE8 in bootstrap) (#159, #214, #230, #231 and others)
- Don't strip units off dimensions of non-length (#226, #229 and others)
## 1.3.7 (February 11, 2013)
- Gonzales 1.0.7.
## 1.3.6 (November 26, 2012)
- Gonzales 1.0.6.
## 1.3.5 (October 28, 2012)
- Gonzales 1.0.5.
- Protecting copyright notices in CSS: https://github.com/css/csso/issues/92
- Zero CSS throws an error: https://github.com/css/csso/issues/96
- Don't minify the second `0s` in Firefox for animations: https://github.com/css/csso/issues/100
- Japan manual
- BEM ready documentation
## 1.3.4 (October 10, 2012)
- @page inside @media Causes Error: https://github.com/css/csso/issues/90
## 1.3.3 (October 9, 2012)
- CSSO 1.3.2 compresses ".t-1" and ".t-01" as identical classes: https://github.com/css/csso/issues/88
## 1.3.2 (October 8, 2012)
- filter + important breaks CSSO v1.3.1: https://github.com/css/csso/issues/87
## 1.3.1 (October 8, 2012)
- "filter" IE property breaks CSSO v1.3.0: https://github.com/css/csso/issues/86
## 1.3.0 (October 4, 2012)
- PeCode CSS parser replaced by Gonzales CSS parser

View file

@ -0,0 +1,19 @@
Copyright (C) 2011-2015 by Sergey Kryzhanovsky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,407 @@
[![NPM version](https://img.shields.io/npm/v/csso.svg)](https://www.npmjs.com/package/csso)
[![Build Status](https://travis-ci.org/css/csso.svg?branch=master)](https://travis-ci.org/css/csso)
[![Coverage Status](https://coveralls.io/repos/github/css/csso/badge.svg?branch=master)](https://coveralls.io/github/css/csso?branch=master)
[![NPM Downloads](https://img.shields.io/npm/dm/csso.svg)](https://www.npmjs.com/package/csso)
[![Twitter](https://img.shields.io/badge/Twitter-@cssoptimizer-blue.svg)](https://twitter.com/cssoptimizer)
CSSO (CSS Optimizer) is a CSS minifier. It performs three sort of transformations: cleaning (removing redundant), compression (replacement for shorter form) and restructuring (merge of declarations, rulesets and so on). As a result your CSS becomes much smaller.
[![Originated by Yandex](https://cdn.rawgit.com/css/csso/8d1b89211ac425909f735e7d5df87ee16c2feec6/docs/yandex.svg)](https://www.yandex.com/)
[![Sponsored by Avito](https://cdn.rawgit.com/css/csso/8d1b89211ac425909f735e7d5df87ee16c2feec6/docs/avito.svg)](https://www.avito.ru/)
## Usage
```
npm install -g csso
```
Or try out CSSO [right in your browser](http://css.github.io/csso/csso.html) (web interface).
### Runners
- Gulp: [gulp-csso](https://github.com/ben-eb/gulp-csso)
- Grunt: [grunt-csso](https://github.com/t32k/grunt-csso)
- Broccoli: [broccoli-csso](https://github.com/sindresorhus/broccoli-csso)
- PostCSS: [postcss-csso](https://github.com/lahmatiy/postcss-csso)
- Webpack: [csso-loader](https://github.com/sandark7/csso-loader)
### Command line
```
csso [input] [output] [options]
Options:
--comments <value> Comments to keep: exclamation (default), first-exclamation or none
--debug [level] Output intermediate state of CSS during compression
-h, --help Output usage information
-i, --input <filename> Input file
--input-map <source> Input source map: none, auto (default) or <filename>
-m, --map <destination> Generate source map: none (default), inline, file or <filename>
-o, --output <filename> Output file (result outputs to stdout if not set)
--restructure-off Turns structure minimization off
--stat Output statistics in stderr
-u, --usage <filenane> Usage data file
-v, --version Output version
```
Some examples:
```
> csso in.css
...output result in stdout...
> csso in.css --output out.css
> echo '.test { color: #ff0000; }' | csso
.test{color:red}
> cat source1.css source2.css | csso | gzip -9 -c > production.css.gz
```
### Source maps
Source map doesn't generate by default. To generate map use `--map` CLI option, that can be:
- `none` (default) don't generate source map
- `inline` add source map into result CSS (via `/*# sourceMappingURL=application/json;base64,... */`)
- `file` write source map into file with same name as output file, but with `.map` extension (in this case `--output` option is required)
- any other values treat as filename for generated source map
Examples:
```
> csso my.css --map inline
> csso my.css --output my.min.css --map file
> csso my.css --output my.min.css --map maps/my.min.map
```
Use `--input-map` option to specify input source map if needed. Possible values for option:
- `auto` (default) - attempt to fetch input source map by follow steps:
- try to fetch inline map from input
- try to fetch source map filename from input and read its content
- (when `--input` is specified) check file with same name as input file but with `.map` extension exists and read its content
- `none` - don't use input source map; actually it's using to disable `auto`-fetching
- any other values treat as filename for input source map
Generally you shouldn't care about input source map since defaults behaviour (`auto`) covers most use cases.
> NOTE: Input source map is using only if output source map is generating.
### Usage data
`CSSO` can use data about how `CSS` is using for better compression. File with this data (`JSON` format) can be set using `--usage` option. Usage data may contain follow sections:
- `tags` white list of tags
- `ids` white list of ids
- `classes` white list of classes
- `scopes` groups of classes which never used with classes from other groups on single element
All sections are optional. Value of `tags`, `ids` and `classes` should be array of strings, value of `scopes` should be an array of arrays of strings. Other values are ignoring.
#### Selector filtering
`tags`, `ids` and `classes` are using on clean stage to filter selectors that contains something that not in list. Selectors are filtering only by those kind of simple selector which white list is specified. For example, if only `tags` list is specified then type selectors are checking, and if selector hasn't any type selector (or even any type selector) it isn't filter.
> `ids` and `classes` names are case sensitive, `tags` is not.
Input CSS:
```css
* { color: green; }
ul, ol, li { color: blue; }
UL.foo, span.bar { color: red; }
```
Usage data:
```json
{
"tags": ["ul", "LI"]
}
```
Result CSS:
```css
*{color:green}ul,li{color:blue}ul.foo{color:red}
```
#### Scopes
Scopes is designed for CSS scope isolation solutions such as [css-modules](https://github.com/css-modules/css-modules). Scopes are similar to namespaces and defines lists of class names that exclusively used on some markup. This information allows the optimizer to move rulesets more agressive. Since it assumes selectors from different scopes can't to be matched on the same element. That leads to better ruleset merging.
Suppose we have a file:
```css
.module1-foo { color: red; }
.module1-bar { font-size: 1.5em; background: yellow; }
.module2-baz { color: red; }
.module2-qux { font-size: 1.5em; background: yellow; width: 50px; }
```
It can be assumed that first two rules never used with second two on the same markup. But we can't know that for sure without markup. The optimizer doesn't know it eather and will perform safe transformations only. The result will be the same as input but with no spaces and some semicolons:
```css
.module1-foo{color:red}.module1-bar{font-size:1.5em;background:#ff0}.module2-baz{color:red}.module2-qux{font-size:1.5em;background:#ff0;width:50px}
```
But with usage data `CSSO` can get better output. If follow usage data is provided:
```json
{
"scopes": [
["module1-foo", "module1-bar"],
["module2-baz", "module2-qux"]
]
}
```
New result (29 bytes extra saving):
```css
.module1-foo,.module2-baz{color:red}.module1-bar,.module2-qux{font-size:1.5em;background:#ff0}.module2-qux{width:50px}
```
If class name doesn't specified in `scopes` it belongs to default "scope". `scopes` doesn't affect `classes`. If class name presents in `scopes` but missed in `classes` (both sections specified) it will be filtered.
Note that class name can't be specified in several scopes. Also selector can't has classes from different scopes. In both cases an exception throws.
Currently the optimizer doesn't care about out-of-bounds selectors order changing safety (i.e. selectors that may be matched to elements with no class name of scope, e.g. `.scope div` or `.scope ~ :last-child`) since assumes scoped CSS modules doesn't relay on it's order. It may be fix in future if to be an issue.
### API
```js
var csso = require('csso');
var compressedCss = csso.minify('.test { color: #ff0000; }').css;
console.log(compressedCss);
// .test{color:red}
```
You may minify CSS by yourself step by step:
```js
var ast = csso.parse('.test { color: #ff0000; }');
var compressResult = csso.compress(ast);
var compressedCss = csso.translate(compressResult.ast);
console.log(compressedCss);
// .test{color:red}
```
Working with source maps:
```js
var css = fs.readFileSync('path/to/my.css', 'utf8');
var result = csso.minify(css, {
filename: 'path/to/my.css', // will be added to source map as reference to source file
sourceMap: true // generate source map
});
console.log(result);
// { css: '...minified...', map: SourceMapGenerator {} }
console.log(result.map.toString());
// '{ .. source map content .. }'
```
#### minify(source[, options])
Minify `source` CSS passed as `String`.
Options:
- sourceMap `Boolean` - generate source map if `true`
- filename `String` - filename of input, uses for source map
- debug `Boolean` - output debug information to `stderr`
- beforeCompress `function|array<function>` - called right after parse is run. Callbacks arguments are `ast, options`.
- afterCompress `function|array<function>` - called right after compress is run. Callbacks arguments are `compressResult, options`.
- other options are the same as for `compress()`
Returns an object with properties:
- css `String` resulting CSS
- map `Object` instance of `SourceMapGenerator` or `null`
```js
var result = csso.minify('.test { color: #ff0000; }', {
restructure: false, // don't change CSS structure, i.e. don't merge declarations, rulesets etc
debug: true // show additional debug information:
// true or number from 1 to 3 (greater number - more details)
});
console.log(result.css);
// > .test{color:red}
```
#### minifyBlock(source[, options])
The same as `minify()` but for style block. Usualy it's a `style` attribute content.
```js
var result = csso.minifyBlock('color: rgba(255, 0, 0, 1); color: #ff0000').css;
console.log(result.css);
// > color:red
```
#### parse(source[, options])
Parse CSS to AST.
> NOTE: Currenly parser omit redundant separators, spaces and comments (except exclamation comments, i.e. `/*! comment */`) on AST build, since those things are removing by compressor anyway.
Options:
- context `String` parsing context, useful when some part of CSS is parsing (see below)
- positions `Boolean` should AST contains node position or not, store data in `info` property of nodes (`false` by default)
- filename `String` filename of source that adds to info when `positions` is true, uses for source map generation (`<unknown>` by default)
- line `Number` initial line number, useful when parse fragment of CSS to compute correct positions
- column `Number` initial column number, useful when parse fragment of CSS to compute correct positions
Contexts:
- `stylesheet` (default) regular stylesheet, should be suitable in most cases
- `atrule` at-rule (e.g. `@media screen, print { ... }`)
- `atruleExpression` at-rule expression (`screen, print` for example above)
- `ruleset` rule (e.g. `.foo, .bar:hover { color: red; border: 1px solid black; }`)
- `selector` selector group (`.foo, .bar:hover` for ruleset example)
- `simpleSelector` selector (`.foo` or `.bar:hover` for ruleset example)
- `block` block content w/o curly braces (`color: red; border: 1px solid black;` for ruleset example)
- `declaration` declaration (`color: red` or `border: 1px solid black` for ruleset example)
- `value` declaration value (`red` or `1px solid black` for ruleset example)
```js
// simple parsing with no options
var ast = csso.parse('.example { color: red }');
// parse with options
var ast = csso.parse('.foo.bar', {
context: 'simpleSelector',
positions: true
});
```
#### compress(ast[, options])
Does the main task compress AST.
> NOTE: `compress` performs AST compression by transforming input AST by default (since AST cloning is expensive and needed in rare cases). Use `clone` option with truthy value in case you want to keep input AST untouched.
Options:
- restructure `Boolean` do the structure optimisations or not (`true` by default)
- clone `Boolean` - transform a copy of input AST if `true`, useful in case of AST reuse (`false` by default)
- comments `String` or `Boolean` specify what comments to left
- `'exclamation'` or `true` (default) left all exclamation comments (i.e. `/*! .. */`)
- `'first-exclamation'` remove every comments except first one
- `false` remove every comments
- usage `Object` - usage data for advanced optimisations (see [Usage data](#usage-data) for details)
- logger `Function` - function to track every step of transformations
#### clone(ast)
Make an AST node deep copy.
```js
var orig = csso.parse('.test { color: red }');
var copy = csso.clone(orig);
csso.walk(copy, function(node) {
if (node.type === 'Class') {
node.name = 'replaced';
}
});
console.log(csso.translate(orig));
// .test{color:red}
console.log(csso.translate(copy));
// .replaced{color:red}
```
#### translate(ast)
Converts AST to string.
```js
var ast = csso.parse('.test { color: red }');
console.log(csso.translate(ast));
// > .test{color:red}
```
#### translateWithSourceMap(ast)
The same as `translate()` but also generates source map (nodes should contain positions in `info` property).
```js
var ast = csso.parse('.test { color: red }', {
filename: 'my.css',
positions: true
});
console.log(csso.translateWithSourceMap(ast));
// { css: '.test{color:red}', map: SourceMapGenerator {} }
```
#### walk(ast, handler)
Visit all nodes of AST and call handler for each one. `handler` receives three arguments:
- node current AST node
- item node wrapper when node is a list member; this wrapper contains references to `prev` and `next` nodes in list
- list reference to list when node is a list member; it's useful for operations on list like `remove()` or `insert()`
Context for handler an object, that contains references to some parent nodes:
- root refers to `ast` or root node
- stylesheet refers to closest `StyleSheet` node, it may be a top-level or at-rule block stylesheet
- atruleExpression refers to `AtruleExpression` node if current node inside at-rule expression
- ruleset refers to `Ruleset` node if current node inside a ruleset
- selector refers to `Selector` node if current node inside a selector
- declaration refers to `Declaration` node if current node inside a declaration
- function refers to closest `Function` or `FunctionalPseudo` node if current node inside one of them
```js
// collect all urls in declarations
var csso = require('./lib/index.js');
var urls = [];
var ast = csso.parse(`
@import url(import.css);
.foo { background: url('foo.jpg'); }
.bar { background-image: url(bar.png); }
`);
csso.walk(ast, function(node) {
if (this.declaration !== null && node.type === 'Url') {
var value = node.value;
if (value.type === 'Raw') {
urls.push(value.value);
} else {
urls.push(value.value.substr(1, value.value.length - 2));
}
}
});
console.log(urls);
// [ 'foo.jpg', 'bar.png' ]
```
#### walkRules(ast, handler)
Same as `walk()` but visits `Ruleset` and `Atrule` nodes only.
#### walkRulesRight(ast, handler)
Same as `walkRules()` but visits nodes in reverse order (from last to first).
## More reading
- [Debugging](docs/debugging.md)
## License
MIT

View file

@ -0,0 +1,16 @@
#!/usr/bin/env node
var cli = require('../lib/cli.js');
try {
cli.run();
} catch (e) {
// output user frendly message if cli error
if (cli.isCliError(e)) {
console.error(e.message || e);
process.exit(2);
}
// otherwise re-throw exception
throw e;
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,338 @@
var fs = require('fs');
var path = require('path');
var cli = require('clap');
var SourceMapConsumer = require('source-map').SourceMapConsumer;
var csso = require('./index.js');
function readFromStream(stream, minify) {
var buffer = [];
// FIXME: don't chain until node.js 0.10 drop, since setEncoding isn't chainable in 0.10
stream.setEncoding('utf8');
stream
.on('data', function(chunk) {
buffer.push(chunk);
})
.on('end', function() {
minify(buffer.join(''));
});
}
function showStat(filename, source, result, inputMap, map, time, mem) {
function fmt(size) {
return String(size).split('').reverse().reduce(function(size, digit, idx) {
if (idx && idx % 3 === 0) {
size = ' ' + size;
}
return digit + size;
}, '');
}
map = map || 0;
result -= map;
console.error('Source: ', filename === '<stdin>' ? filename : path.relative(process.cwd(), filename));
if (inputMap) {
console.error('Map source:', inputMap);
}
console.error('Original: ', fmt(source), 'bytes');
console.error('Compressed:', fmt(result), 'bytes', '(' + (100 * result / source).toFixed(2) + '%)');
console.error('Saving: ', fmt(source - result), 'bytes', '(' + (100 * (source - result) / source).toFixed(2) + '%)');
if (map) {
console.error('Source map:', fmt(map), 'bytes', '(' + (100 * map / (result + map)).toFixed(2) + '% of total)');
console.error('Total: ', fmt(map + result), 'bytes');
}
console.error('Time: ', time, 'ms');
console.error('Memory: ', (mem / (1024 * 1024)).toFixed(3), 'MB');
}
function showParseError(source, filename, details, message) {
function processLines(start, end) {
return lines.slice(start, end).map(function(line, idx) {
var num = String(start + idx + 1);
while (num.length < maxNumLength) {
num = ' ' + num;
}
return num + ' |' + line;
}).join('\n');
}
var lines = source.split(/\n|\r\n?|\f/);
var column = details.column;
var line = details.line;
var startLine = Math.max(1, line - 2);
var endLine = Math.min(line + 2, lines.length + 1);
var maxNumLength = Math.max(4, String(endLine).length) + 1;
console.error('\nParse error ' + filename + ': ' + message);
console.error(processLines(startLine - 1, line));
console.error(new Array(column + maxNumLength + 2).join('-') + '^');
console.error(processLines(line, endLine));
console.error();
}
function debugLevel(level) {
// level is undefined when no param -> 1
return isNaN(level) ? 1 : Math.max(Number(level), 0);
}
function resolveSourceMap(source, inputMap, map, inputFile, outputFile) {
var inputMapContent = null;
var inputMapFile = null;
var outputMapFile = null;
switch (map) {
case 'none':
// don't generate source map
map = false;
inputMap = 'none';
break;
case 'inline':
// nothing to do
break;
case 'file':
if (!outputFile) {
console.error('Output filename should be specified when `--map file` is used');
process.exit(2);
}
outputMapFile = outputFile + '.map';
break;
default:
// process filename
if (map) {
// check path is reachable
if (!fs.existsSync(path.dirname(map))) {
console.error('Directory for map file should exists:', path.dirname(path.resolve(map)));
process.exit(2);
}
// resolve to absolute path
outputMapFile = path.resolve(process.cwd(), map);
}
}
switch (inputMap) {
case 'none':
// nothing to do
break;
case 'auto':
if (map) {
// try fetch source map from source
var inputMapComment = source.match(/\/\*# sourceMappingURL=(\S+)\s*\*\/\s*$/);
if (inputFile === '<stdin>') {
inputFile = false;
}
if (inputMapComment) {
// if comment found value is filename or base64-encoded source map
inputMapComment = inputMapComment[1];
if (inputMapComment.substr(0, 5) === 'data:') {
// decode source map content from comment
inputMapContent = new Buffer(inputMapComment.substr(inputMapComment.indexOf('base64,') + 7), 'base64').toString();
} else {
// value is filename resolve it as absolute path
if (inputFile) {
inputMapFile = path.resolve(path.dirname(inputFile), inputMapComment);
}
}
} else {
// comment doesn't found - look up file with `.map` extension nearby input file
if (inputFile && fs.existsSync(inputFile + '.map')) {
inputMapFile = inputFile + '.map';
}
}
}
break;
default:
if (inputMap) {
inputMapFile = inputMap;
}
}
// source map placed in external file
if (inputMapFile) {
inputMapContent = fs.readFileSync(inputMapFile, 'utf8');
}
return {
input: inputMapContent,
inputFile: inputMapFile || (inputMapContent ? '<inline>' : false),
output: map,
outputFile: outputMapFile
};
}
function processCommentsOption(value) {
switch (value) {
case 'exclamation':
case 'first-exclamation':
case 'none':
return value;
}
console.error('Wrong value for `comments` option: %s', value);
process.exit(2);
}
var command = cli.create('csso', '[input] [output]')
.version(require('../package.json').version)
.option('-i, --input <filename>', 'Input file')
.option('-o, --output <filename>', 'Output file (result outputs to stdout if not set)')
.option('-m, --map <destination>', 'Generate source map: none (default), inline, file or <filename>', 'none')
.option('-u, --usage <filenane>', 'Usage data file')
.option('--input-map <source>', 'Input source map: none, auto (default) or <filename>', 'auto')
.option('--restructure-off', 'Turns structure minimization off')
.option('--comments <value>', 'Comments to keep: exclamation (default), first-exclamation or none', 'exclamation')
.option('--stat', 'Output statistics in stderr')
.option('--debug [level]', 'Output intermediate state of CSS during compression', debugLevel, 0)
.action(function(args) {
var options = this.values;
var inputFile = options.input || args[0];
var outputFile = options.output || args[1];
var usageFile = options.usage;
var usageData = false;
var map = options.map;
var inputMap = options.inputMap;
var structureOptimisationOff = options.restructureOff;
var comments = processCommentsOption(options.comments);
var debug = options.debug;
var statistics = options.stat;
var inputStream;
if (process.stdin.isTTY && !inputFile && !outputFile) {
this.showHelp();
return;
}
if (!inputFile) {
inputFile = '<stdin>';
inputStream = process.stdin;
} else {
inputFile = path.resolve(process.cwd(), inputFile);
inputStream = fs.createReadStream(inputFile);
}
if (outputFile) {
outputFile = path.resolve(process.cwd(), outputFile);
}
if (usageFile) {
if (!fs.existsSync(usageFile)) {
console.error('Usage data file doesn\'t found (%s)', usageFile);
process.exit(2);
}
usageData = fs.readFileSync(usageFile, 'utf-8');
try {
usageData = JSON.parse(usageData);
} catch (e) {
console.error('Usage data parse error (%s)', usageFile);
process.exit(2);
}
}
readFromStream(inputStream, function(source) {
var time = process.hrtime();
var mem = process.memoryUsage().heapUsed;
var sourceMap = resolveSourceMap(source, inputMap, map, inputFile, outputFile);
var sourceMapAnnotation = '';
var result;
// main action
try {
result = csso.minify(source, {
filename: inputFile,
sourceMap: sourceMap.output,
usage: usageData,
restructure: !structureOptimisationOff,
comments: comments,
debug: debug
});
// for backward capability minify returns a string
if (typeof result === 'string') {
result = {
css: result,
map: null
};
}
} catch (e) {
if (e.parseError) {
showParseError(source, inputFile, e.parseError, e.message);
if (!debug) {
process.exit(2);
}
}
throw e;
}
if (sourceMap.output && result.map) {
// apply input map
if (sourceMap.input) {
result.map.applySourceMap(
new SourceMapConsumer(sourceMap.input),
inputFile
);
}
// add source map to result
if (sourceMap.outputFile) {
// write source map to file
fs.writeFileSync(sourceMap.outputFile, result.map.toString(), 'utf-8');
sourceMapAnnotation = '\n' +
'/*# sourceMappingURL=' +
path.relative(outputFile ? path.dirname(outputFile) : process.cwd(), sourceMap.outputFile) +
' */';
} else {
// inline source map
sourceMapAnnotation = '\n' +
'/*# sourceMappingURL=data:application/json;base64,' +
new Buffer(result.map.toString()).toString('base64') +
' */';
}
result.css += sourceMapAnnotation;
}
// output result
if (outputFile) {
fs.writeFileSync(outputFile, result.css, 'utf-8');
} else {
console.log(result.css);
}
// output statistics
if (statistics) {
var timeDiff = process.hrtime(time);
showStat(
path.relative(process.cwd(), inputFile),
source.length,
result.css.length,
sourceMap.inputFile,
sourceMapAnnotation.length,
parseInt(timeDiff[0] * 1e3 + timeDiff[1] / 1e6),
process.memoryUsage().heapUsed - mem
);
}
});
});
module.exports = {
run: command.run.bind(command),
isCliError: function(err) {
return err instanceof cli.Error;
}
};

View file

@ -0,0 +1,54 @@
module.exports = function cleanAtrule(node, item, list) {
if (node.block) {
// otherwise removed at-rule don't prevent @import for removal
this.root.firstAtrulesAllowed = false;
if (node.block.type === 'Block' && node.block.declarations.isEmpty()) {
list.remove(item);
return;
}
if (node.block.type === 'StyleSheet' && node.block.rules.isEmpty()) {
list.remove(item);
return;
}
}
switch (node.name) {
case 'charset':
if (node.expression.sequence.isEmpty()) {
list.remove(item);
return;
}
// if there is any rule before @charset -> remove it
if (item.prev) {
list.remove(item);
return;
}
break;
case 'import':
if (!this.root.firstAtrulesAllowed) {
list.remove(item);
return;
}
// if there are some rules that not an @import or @charset before @import
// remove it
list.prevUntil(item.prev, function(rule) {
if (rule.type === 'Atrule') {
if (rule.name === 'import' || rule.name === 'charset') {
return;
}
}
this.root.firstAtrulesAllowed = false;
list.remove(item);
return true;
}, this);
break;
}
};

View file

@ -0,0 +1,3 @@
module.exports = function cleanComment(data, item, list) {
list.remove(item);
};

View file

@ -0,0 +1,5 @@
module.exports = function cleanDeclartion(node, item, list) {
if (node.value.sequence.isEmpty()) {
list.remove(item);
}
};

View file

@ -0,0 +1,9 @@
module.exports = function cleanIdentifier(node, item, list) {
// remove useless universal selector
if (this.selector !== null && node.name === '*') {
// remove when universal selector isn't last
if (item.next && item.next.data.type !== 'Combinator') {
list.remove(item);
}
}
};

View file

@ -0,0 +1,39 @@
var hasOwnProperty = Object.prototype.hasOwnProperty;
function cleanUnused(node, usageData) {
return node.selector.selectors.each(function(selector, item, list) {
var hasUnused = selector.sequence.some(function(node) {
switch (node.type) {
case 'Class':
return usageData.classes && !hasOwnProperty.call(usageData.classes, node.name);
case 'Id':
return usageData.ids && !hasOwnProperty.call(usageData.ids, node.name);
case 'Identifier':
// ignore universal selector
if (node.name !== '*') {
// TODO: remove toLowerCase when type selectors will be normalized
return usageData.tags && !hasOwnProperty.call(usageData.tags, node.name.toLowerCase());
}
break;
}
});
if (hasUnused) {
list.remove(item);
}
});
}
module.exports = function cleanRuleset(node, item, list, usageData) {
if (usageData) {
cleanUnused(node, usageData);
}
if (node.selector.selectors.isEmpty() ||
node.block.declarations.isEmpty()) {
list.remove(item);
}
};

View file

@ -0,0 +1,16 @@
function canCleanWhitespace(node) {
if (node.type !== 'Operator') {
return false;
}
return node.value !== '+' && node.value !== '-';
}
module.exports = function cleanWhitespace(node, item, list) {
var prev = item.prev && item.prev.data;
var next = item.next && item.next.data;
if (canCleanWhitespace(prev) || canCleanWhitespace(next)) {
list.remove(item);
}
};

View file

@ -0,0 +1,17 @@
var walk = require('../../utils/walk.js').all;
var handlers = {
Space: require('./Space.js'),
Atrule: require('./Atrule.js'),
Ruleset: require('./Ruleset.js'),
Declaration: require('./Declaration.js'),
Identifier: require('./Identifier.js'),
Comment: require('./Comment.js')
};
module.exports = function(ast, usageData) {
walk(ast, function(node, item, list) {
if (handlers.hasOwnProperty(node.type)) {
handlers[node.type].call(this, node, item, list, usageData);
}
});
};

View file

@ -0,0 +1,9 @@
var resolveKeyword = require('../../utils/names.js').keyword;
var compressKeyframes = require('./atrule/keyframes.js');
module.exports = function(node) {
// compress @keyframe selectors
if (resolveKeyword(node.name).name === 'keyframes') {
compressKeyframes(node);
}
};

View file

@ -0,0 +1,33 @@
// Can unquote attribute detection
// Adopted implementation of Mathias Bynens
// https://github.com/mathiasbynens/mothereff.in/blob/master/unquoted-attributes/eff.js
var escapesRx = /\\([0-9A-Fa-f]{1,6})[ \t\n\f\r]?|\\./g;
var blockUnquoteRx = /^(-?\d|--)|[\u0000-\u002c\u002e\u002f\u003A-\u0040\u005B-\u005E\u0060\u007B-\u009f]/;
function canUnquote(value) {
if (value === '' || value === '-') {
return;
}
// Escapes are valid, so replace them with a valid non-empty string
value = value.replace(escapesRx, 'a');
return !blockUnquoteRx.test(value);
}
module.exports = function(node) {
var attrValue = node.value;
if (!attrValue || attrValue.type !== 'String') {
return;
}
var unquotedValue = attrValue.value.replace(/^(.)(.*)\1$/, '$2');
if (canUnquote(unquotedValue)) {
node.value = {
type: 'Identifier',
info: attrValue.info,
name: unquotedValue
};
}
};

View file

@ -0,0 +1,54 @@
var packNumber = require('./Number.js').pack;
var LENGTH_UNIT = {
// absolute length units
'px': true,
'mm': true,
'cm': true,
'in': true,
'pt': true,
'pc': true,
// relative length units
'em': true,
'ex': true,
'ch': true,
'rem': true,
// viewport-percentage lengths
'vh': true,
'vw': true,
'vmin': true,
'vmax': true,
'vm': true
};
module.exports = function compressDimension(node, item) {
var value = packNumber(node.value);
node.value = value;
if (value === '0' && this.declaration) {
var unit = node.unit.toLowerCase();
// only length values can be compressed
if (!LENGTH_UNIT.hasOwnProperty(unit)) {
return;
}
// issue #200: don't remove units in flex property as it could change value meaning
if (this.declaration.property.name === 'flex') {
return;
}
// issue #222: don't remove units inside calc
if (this['function'] && this['function'].name === 'calc') {
return;
}
item.data = {
type: 'Number',
info: node.info,
value: value
};
}
};

View file

@ -0,0 +1,22 @@
function packNumber(value) {
// 100 -> '100'
// 00100 -> '100'
// +100 -> '100'
// -100 -> '-100'
// 0.123 -> '.123'
// 0.12300 -> '.123'
// 0.0 -> ''
// 0 -> ''
value = String(value).replace(/^(?:\+|(-))?0*(\d*)(?:\.0*|(\.\d*?)0*)?$/, '$1$2$3');
if (value.length === 0 || value === '-') {
value = '0';
}
return value;
};
module.exports = function(node) {
node.value = packNumber(node.value);
};
module.exports.pack = packNumber;

View file

@ -0,0 +1,12 @@
module.exports = function(node) {
var value = node.value;
// remove escaped \n, i.e.
// .a { content: "foo\
// bar"}
// ->
// .a { content: "foobar" }
value = value.replace(/\\\n/g, '');
node.value = value;
};

View file

@ -0,0 +1,33 @@
var UNICODE = '\\\\[0-9a-f]{1,6}(\\r\\n|[ \\n\\r\\t\\f])?';
var ESCAPE = '(' + UNICODE + '|\\\\[^\\n\\r\\f0-9a-fA-F])';
var NONPRINTABLE = '\u0000\u0008\u000b\u000e-\u001f\u007f';
var SAFE_URL = new RegExp('^(' + ESCAPE + '|[^\"\'\\(\\)\\\\\\s' + NONPRINTABLE + '])*$', 'i');
module.exports = function(node) {
var value = node.value;
if (value.type !== 'String') {
return;
}
var quote = value.value[0];
var url = value.value.substr(1, value.value.length - 2);
// convert `\\` to `/`
url = url.replace(/\\\\/g, '/');
// remove quotes when safe
// https://www.w3.org/TR/css-syntax-3/#url-unquoted-diagram
if (SAFE_URL.test(url)) {
node.value = {
type: 'Raw',
info: node.value.info,
value: url
};
} else {
// use double quotes if string has no double quotes
// otherwise use original quotes
// TODO: make better quote type selection
node.value.value = url.indexOf('"') === -1 ? '"' + url + '"' : quote + url + quote;
}
};

View file

@ -0,0 +1,18 @@
var resolveName = require('../../utils/names.js').property;
var handlers = {
'font': require('./property/font.js'),
'font-weight': require('./property/font-weight.js'),
'background': require('./property/background.js')
};
module.exports = function compressValue(node) {
if (!this.declaration) {
return;
}
var property = resolveName(this.declaration.property.name);
if (handlers.hasOwnProperty(property.name)) {
handlers[property.name](node);
}
};

View file

@ -0,0 +1,21 @@
module.exports = function(node) {
node.block.rules.each(function(ruleset) {
ruleset.selector.selectors.each(function(simpleselector) {
simpleselector.sequence.each(function(data, item) {
if (data.type === 'Percentage' && data.value === '100') {
item.data = {
type: 'Identifier',
info: data.info,
name: 'to'
};
} else if (data.type === 'Identifier' && data.name === 'from') {
item.data = {
type: 'Percentage',
info: data.info,
value: '0'
};
}
});
});
});
};

View file

@ -0,0 +1,489 @@
var List = require('../../utils/list.js');
var packNumber = require('./Number.js').pack;
// http://www.w3.org/TR/css3-color/#svg-color
var NAME_TO_HEX = {
'aliceblue': 'f0f8ff',
'antiquewhite': 'faebd7',
'aqua': '0ff',
'aquamarine': '7fffd4',
'azure': 'f0ffff',
'beige': 'f5f5dc',
'bisque': 'ffe4c4',
'black': '000',
'blanchedalmond': 'ffebcd',
'blue': '00f',
'blueviolet': '8a2be2',
'brown': 'a52a2a',
'burlywood': 'deb887',
'cadetblue': '5f9ea0',
'chartreuse': '7fff00',
'chocolate': 'd2691e',
'coral': 'ff7f50',
'cornflowerblue': '6495ed',
'cornsilk': 'fff8dc',
'crimson': 'dc143c',
'cyan': '0ff',
'darkblue': '00008b',
'darkcyan': '008b8b',
'darkgoldenrod': 'b8860b',
'darkgray': 'a9a9a9',
'darkgrey': 'a9a9a9',
'darkgreen': '006400',
'darkkhaki': 'bdb76b',
'darkmagenta': '8b008b',
'darkolivegreen': '556b2f',
'darkorange': 'ff8c00',
'darkorchid': '9932cc',
'darkred': '8b0000',
'darksalmon': 'e9967a',
'darkseagreen': '8fbc8f',
'darkslateblue': '483d8b',
'darkslategray': '2f4f4f',
'darkslategrey': '2f4f4f',
'darkturquoise': '00ced1',
'darkviolet': '9400d3',
'deeppink': 'ff1493',
'deepskyblue': '00bfff',
'dimgray': '696969',
'dimgrey': '696969',
'dodgerblue': '1e90ff',
'firebrick': 'b22222',
'floralwhite': 'fffaf0',
'forestgreen': '228b22',
'fuchsia': 'f0f',
'gainsboro': 'dcdcdc',
'ghostwhite': 'f8f8ff',
'gold': 'ffd700',
'goldenrod': 'daa520',
'gray': '808080',
'grey': '808080',
'green': '008000',
'greenyellow': 'adff2f',
'honeydew': 'f0fff0',
'hotpink': 'ff69b4',
'indianred': 'cd5c5c',
'indigo': '4b0082',
'ivory': 'fffff0',
'khaki': 'f0e68c',
'lavender': 'e6e6fa',
'lavenderblush': 'fff0f5',
'lawngreen': '7cfc00',
'lemonchiffon': 'fffacd',
'lightblue': 'add8e6',
'lightcoral': 'f08080',
'lightcyan': 'e0ffff',
'lightgoldenrodyellow': 'fafad2',
'lightgray': 'd3d3d3',
'lightgrey': 'd3d3d3',
'lightgreen': '90ee90',
'lightpink': 'ffb6c1',
'lightsalmon': 'ffa07a',
'lightseagreen': '20b2aa',
'lightskyblue': '87cefa',
'lightslategray': '789',
'lightslategrey': '789',
'lightsteelblue': 'b0c4de',
'lightyellow': 'ffffe0',
'lime': '0f0',
'limegreen': '32cd32',
'linen': 'faf0e6',
'magenta': 'f0f',
'maroon': '800000',
'mediumaquamarine': '66cdaa',
'mediumblue': '0000cd',
'mediumorchid': 'ba55d3',
'mediumpurple': '9370db',
'mediumseagreen': '3cb371',
'mediumslateblue': '7b68ee',
'mediumspringgreen': '00fa9a',
'mediumturquoise': '48d1cc',
'mediumvioletred': 'c71585',
'midnightblue': '191970',
'mintcream': 'f5fffa',
'mistyrose': 'ffe4e1',
'moccasin': 'ffe4b5',
'navajowhite': 'ffdead',
'navy': '000080',
'oldlace': 'fdf5e6',
'olive': '808000',
'olivedrab': '6b8e23',
'orange': 'ffa500',
'orangered': 'ff4500',
'orchid': 'da70d6',
'palegoldenrod': 'eee8aa',
'palegreen': '98fb98',
'paleturquoise': 'afeeee',
'palevioletred': 'db7093',
'papayawhip': 'ffefd5',
'peachpuff': 'ffdab9',
'peru': 'cd853f',
'pink': 'ffc0cb',
'plum': 'dda0dd',
'powderblue': 'b0e0e6',
'purple': '800080',
'rebeccapurple': '639',
'red': 'f00',
'rosybrown': 'bc8f8f',
'royalblue': '4169e1',
'saddlebrown': '8b4513',
'salmon': 'fa8072',
'sandybrown': 'f4a460',
'seagreen': '2e8b57',
'seashell': 'fff5ee',
'sienna': 'a0522d',
'silver': 'c0c0c0',
'skyblue': '87ceeb',
'slateblue': '6a5acd',
'slategray': '708090',
'slategrey': '708090',
'snow': 'fffafa',
'springgreen': '00ff7f',
'steelblue': '4682b4',
'tan': 'd2b48c',
'teal': '008080',
'thistle': 'd8bfd8',
'tomato': 'ff6347',
'turquoise': '40e0d0',
'violet': 'ee82ee',
'wheat': 'f5deb3',
'white': 'fff',
'whitesmoke': 'f5f5f5',
'yellow': 'ff0',
'yellowgreen': '9acd32'
};
var HEX_TO_NAME = {
'800000': 'maroon',
'800080': 'purple',
'808000': 'olive',
'808080': 'gray',
'00ffff': 'cyan',
'f0ffff': 'azure',
'f5f5dc': 'beige',
'ffe4c4': 'bisque',
'000000': 'black',
'0000ff': 'blue',
'a52a2a': 'brown',
'ff7f50': 'coral',
'ffd700': 'gold',
'008000': 'green',
'4b0082': 'indigo',
'fffff0': 'ivory',
'f0e68c': 'khaki',
'00ff00': 'lime',
'faf0e6': 'linen',
'000080': 'navy',
'ffa500': 'orange',
'da70d6': 'orchid',
'cd853f': 'peru',
'ffc0cb': 'pink',
'dda0dd': 'plum',
'f00': 'red',
'ff0000': 'red',
'fa8072': 'salmon',
'a0522d': 'sienna',
'c0c0c0': 'silver',
'fffafa': 'snow',
'd2b48c': 'tan',
'008080': 'teal',
'ff6347': 'tomato',
'ee82ee': 'violet',
'f5deb3': 'wheat',
'ffffff': 'white',
'ffff00': 'yellow'
};
function hueToRgb(p, q, t) {
if (t < 0) {
t += 1;
}
if (t > 1) {
t -= 1;
}
if (t < 1 / 6) {
return p + (q - p) * 6 * t;
}
if (t < 1 / 2) {
return q;
}
if (t < 2 / 3) {
return p + (q - p) * (2 / 3 - t) * 6;
}
return p;
}
function hslToRgb(h, s, l, a) {
var r;
var g;
var b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hueToRgb(p, q, h + 1 / 3);
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - 1 / 3);
}
return [
Math.round(r * 255),
Math.round(g * 255),
Math.round(b * 255),
a
];
}
function toHex(value) {
value = value.toString(16);
return value.length === 1 ? '0' + value : value;
}
function parseFunctionArgs(functionArgs, count, rgb) {
var argument = functionArgs.head;
var args = [];
while (argument !== null) {
var argumentPart = argument.data.sequence.head;
var wasValue = false;
while (argumentPart !== null) {
var value = argumentPart.data;
var type = value.type;
switch (type) {
case 'Number':
case 'Percentage':
if (wasValue) {
return;
}
wasValue = true;
args.push({
type: type,
value: Number(value.value)
});
break;
case 'Operator':
if (wasValue || value.value !== '+') {
return;
}
break;
default:
// something we couldn't understand
return;
}
argumentPart = argumentPart.next;
}
argument = argument.next;
}
if (args.length !== count) {
// invalid arguments count
// TODO: remove those tokens
return;
}
if (args.length === 4) {
if (args[3].type !== 'Number') {
// 4th argument should be a number
// TODO: remove those tokens
return;
}
args[3].type = 'Alpha';
}
if (rgb) {
if (args[0].type !== args[1].type || args[0].type !== args[2].type) {
// invalid color, numbers and percentage shouldn't be mixed
// TODO: remove those tokens
return;
}
} else {
if (args[0].type !== 'Number' ||
args[1].type !== 'Percentage' ||
args[2].type !== 'Percentage') {
// invalid color, for hsl values should be: number, percentage, percentage
// TODO: remove those tokens
return;
}
args[0].type = 'Angle';
}
return args.map(function(arg) {
var value = Math.max(0, arg.value);
switch (arg.type) {
case 'Number':
// fit value to [0..255] range
value = Math.min(value, 255);
break;
case 'Percentage':
// convert 0..100% to value in [0..255] range
value = Math.min(value, 100) / 100;
if (!rgb) {
return value;
}
value = 255 * value;
break;
case 'Angle':
// fit value to (-360..360) range
return (((value % 360) + 360) % 360) / 360;
case 'Alpha':
// fit value to [0..1] range
return Math.min(value, 1);
}
return Math.round(value);
});
}
function compressFunction(node, item, list) {
var functionName = node.name;
var args;
if (functionName === 'rgba' || functionName === 'hsla') {
args = parseFunctionArgs(node.arguments, 4, functionName === 'rgba');
if (!args) {
// something went wrong
return;
}
if (functionName === 'hsla') {
args = hslToRgb.apply(null, args);
node.name = 'rgba';
}
if (args[3] !== 1) {
// replace argument values for normalized/interpolated
node.arguments.each(function(argument) {
var item = argument.sequence.head;
if (item.data.type === 'Operator') {
item = item.next;
}
argument.sequence = new List([{
type: 'Number',
info: item.data.info,
value: packNumber(args.shift())
}]);
});
return;
}
// otherwise convert to rgb, i.e. rgba(255, 0, 0, 1) -> rgb(255, 0, 0)
functionName = 'rgb';
}
if (functionName === 'hsl') {
args = args || parseFunctionArgs(node.arguments, 3, false);
if (!args) {
// something went wrong
return;
}
// convert to rgb
args = hslToRgb.apply(null, args);
functionName = 'rgb';
}
if (functionName === 'rgb') {
args = args || parseFunctionArgs(node.arguments, 3, true);
if (!args) {
// something went wrong
return;
}
// check if color is not at the end and not followed by space
var next = item.next;
if (next && next.data.type !== 'Space') {
list.insert(list.createItem({
type: 'Space'
}), next);
}
item.data = {
type: 'Hash',
info: node.info,
value: toHex(args[0]) + toHex(args[1]) + toHex(args[2])
};
compressHex(item.data, item);
}
}
function compressIdent(node, item) {
if (this.declaration === null) {
return;
}
var color = node.name.toLowerCase();
if (NAME_TO_HEX.hasOwnProperty(color)) {
var hex = NAME_TO_HEX[color];
if (hex.length + 1 <= color.length) {
// replace for shorter hex value
item.data = {
type: 'Hash',
info: node.info,
value: hex
};
} else {
// special case for consistent colors
if (color === 'grey') {
color = 'gray';
}
// just replace value for lower cased name
node.name = color;
}
}
}
function compressHex(node, item) {
var color = node.value.toLowerCase();
// #112233 -> #123
if (color.length === 6 &&
color[0] === color[1] &&
color[2] === color[3] &&
color[4] === color[5]) {
color = color[0] + color[2] + color[4];
}
if (HEX_TO_NAME[color]) {
item.data = {
type: 'Identifier',
info: node.info,
name: HEX_TO_NAME[color]
};
} else {
node.value = color;
}
}
module.exports = {
compressFunction: compressFunction,
compressIdent: compressIdent,
compressHex: compressHex
};

View file

@ -0,0 +1,22 @@
var walk = require('../../utils/walk.js').all;
var handlers = {
Atrule: require('./Atrule.js'),
Attribute: require('./Attribute.js'),
Value: require('./Value.js'),
Dimension: require('./Dimension.js'),
Percentage: require('./Number.js'),
Number: require('./Number.js'),
String: require('./String.js'),
Url: require('./Url.js'),
Hash: require('./color.js').compressHex,
Identifier: require('./color.js').compressIdent,
Function: require('./color.js').compressFunction
};
module.exports = function(ast) {
walk(ast, function(node, item, list) {
if (handlers.hasOwnProperty(node.type)) {
handlers[node.type].call(this, node, item, list);
}
});
};

View file

@ -0,0 +1,66 @@
var List = require('../../../utils/list.js');
module.exports = function compressBackground(node) {
function lastType() {
if (buffer.length) {
return buffer[buffer.length - 1].type;
}
}
function flush() {
if (lastType() === 'Space') {
buffer.pop();
}
if (!buffer.length) {
buffer.unshift(
{
type: 'Number',
value: '0'
},
{
type: 'Space'
},
{
type: 'Number',
value: '0'
}
);
}
newValue.push.apply(newValue, buffer);
buffer = [];
}
var newValue = [];
var buffer = [];
node.sequence.each(function(node) {
if (node.type === 'Operator' && node.value === ',') {
flush();
newValue.push(node);
return;
}
// remove defaults
if (node.type === 'Identifier') {
if (node.name === 'transparent' ||
node.name === 'none' ||
node.name === 'repeat' ||
node.name === 'scroll') {
return;
}
}
// don't add redundant spaces
if (node.type === 'Space' && (!buffer.length || lastType() === 'Space')) {
return;
}
buffer.push(node);
});
flush();
node.sequence = new List(newValue);
};

View file

@ -0,0 +1,22 @@
module.exports = function compressFontWeight(node) {
var value = node.sequence.head.data;
if (value.type === 'Identifier') {
switch (value.name) {
case 'normal':
node.sequence.head.data = {
type: 'Number',
info: value.info,
value: '400'
};
break;
case 'bold':
node.sequence.head.data = {
type: 'Number',
info: value.info,
value: '700'
};
break;
}
}
};

View file

@ -0,0 +1,45 @@
module.exports = function compressFont(node) {
var list = node.sequence;
list.eachRight(function(node, item) {
if (node.type === 'Identifier') {
if (node.name === 'bold') {
item.data = {
type: 'Number',
info: node.info,
value: '700'
};
} else if (node.name === 'normal') {
var prev = item.prev;
if (prev && prev.data.type === 'Operator' && prev.data.value === '/') {
this.remove(prev);
}
this.remove(item);
} else if (node.name === 'medium') {
var next = item.next;
if (!next || next.data.type !== 'Operator') {
this.remove(item);
}
}
}
});
// remove redundant spaces
list.each(function(node, item) {
if (node.type === 'Space') {
if (!item.prev || !item.next || item.next.data.type === 'Space') {
this.remove(item);
}
}
});
if (list.isEmpty()) {
list.insert(list.createItem({
type: 'Identifier',
name: 'normal'
}));
}
};

Some files were not shown because too many files have changed in this diff Show more