Removed GopherJS, basic frontend completed, need backend changes for

torrent storage
This commit is contained in:
2017-11-30 18:12:11 -05:00
parent 67fdef16b1
commit e98ad2cc88
69321 changed files with 5498914 additions and 337 deletions

View File

@@ -0,0 +1,29 @@
UglifyJS is released under the BSD license:
Copyright 2012-2013 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View File

@@ -0,0 +1,995 @@
UglifyJS 2
==========
[![Build Status](https://travis-ci.org/mishoo/UglifyJS2.svg)](https://travis-ci.org/mishoo/UglifyJS2)
UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
This page documents the command line utility. For
[API and internals documentation see my website](http://lisperator.net/uglifyjs/).
There's also an
[in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox,
Chrome and probably Safari).
#### Note:
- `uglify-js` only supports ECMAScript 5 (ES5).
- Support for `const` is [present but incomplete](#support-for-const), and may not be
transformed properly.
- Those wishing to minify ES2015+ (ES6+) should use the `npm` package [**uglify-es**](https://github.com/mishoo/UglifyJS2/tree/harmony).
Install
-------
First make sure you have installed the latest version of [node.js](http://nodejs.org/)
(You may need to restart your computer after this step).
From NPM for use as a command line app:
npm install uglify-js -g
From NPM for programmatic use:
npm install uglify-js
Usage
-----
uglifyjs [input files] [options]
UglifyJS2 can take multiple input files. It's recommended that you pass the
input files first, then pass the options. UglifyJS will parse input files
in sequence and apply any compression options. The files are parsed in the
same global scope, that is, a reference from a file to some
variable/function declared in another file will be matched properly.
If you want to read from STDIN instead, pass a single dash instead of input
files.
If you wish to pass your options before the input files, separate the two with
a double dash to prevent input files being used as option arguments:
uglifyjs --compress --mangle -- input.js
The available options are:
```
--source-map Specify an output file where to generate source
map.
--source-map-root The path to the original source to be included
in the source map.
--source-map-url The path to the source map to be added in //#
sourceMappingURL. Defaults to the value passed
with --source-map.
--source-map-include-sources Pass this flag if you want to include the
content of source files in the source map as
sourcesContent property.
--source-map-inline Write base64-encoded source map to the end of js output.
--in-source-map Input source map, useful if you're compressing
JS that was generated from some other original
code. Specify "inline" if the source map is included
inline with the sources.
--screw-ie8 Use this flag if you don't wish to support
Internet Explorer 6/7/8.
By default UglifyJS will not try to be IE-proof.
--support-ie8 Use this flag to support Internet Explorer 6/7/8.
Equivalent to setting `screw_ie8: false` in `minify()`
for `compress`, `mangle` and `output` options.
--expr Parse a single expression, rather than a
program (for parsing JSON)
-p, --prefix Skip prefix for original filenames that appear
in source maps. For example -p 3 will drop 3
directories from file names and ensure they are
relative paths. You can also specify -p
relative, which will make UglifyJS figure out
itself the relative paths between original
sources, the source map and the output file.
-o, --output Output file (default STDOUT).
-b, --beautify Beautify output/specify output options.
-m, --mangle Mangle names/pass mangler options.
-r, --reserved Reserved names to exclude from mangling.
-c, --compress Enable compressor/pass compressor options, e.g.
`-c 'if_return=false,pure_funcs=["Math.pow","console.log"]'`
Use `-c` with no argument to enable default compression
options.
-d, --define Global definitions
-e, --enclose Embed everything in a big function, with a
configurable parameter/argument list.
--comments Preserve copyright comments in the output. By
default this works like Google Closure, keeping
JSDoc-style comments that contain "@license" or
"@preserve". You can optionally pass one of the
following arguments to this flag:
- "all" to keep all comments
- a valid JS RegExp like `/foo/` or `/^!/` to
keep only matching comments.
Note that currently not *all* comments can be
kept when compression is on, because of dead
code removal or cascading statements into
sequences.
--preamble Preamble to prepend to the output. You can use
this to insert a comment, for example for
licensing information. This will not be
parsed, but the source map will adjust for its
presence.
--stats Display operations run time on STDERR.
--acorn Use Acorn for parsing.
--spidermonkey Assume input files are SpiderMonkey AST format
(as JSON).
--self Build itself (UglifyJS2) as a library (implies
--wrap=UglifyJS --export-all)
--wrap Embed everything in a big function, making the
“exports” and “global” variables available. You
need to pass an argument to this option to
specify the name that your module will take
when included in, say, a browser.
--export-all Only used when --wrap, this tells UglifyJS to
add code to automatically export all globals.
--lint Display some scope warnings
-v, --verbose Verbose
-V, --version Print version number and exit.
--noerr Don't throw an error for unknown options in -c,
-b or -m.
--bare-returns Allow return outside of functions. Useful when
minifying CommonJS modules and Userscripts that
may be anonymous function wrapped (IIFE) by the
.user.js engine `caller`.
--keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name.
--reserved-file File containing reserved names
--reserve-domprops Make (most?) DOM properties reserved for
--mangle-props
--mangle-props Mangle property names (default `0`). Set to
`true` or `1` to mangle all property names. Set
to `unquoted` or `2` to only mangle unquoted
property names. Mode `2` also enables the
`keep_quoted_props` beautifier option to
preserve the quotes around property names and
disables the `properties` compressor option to
prevent rewriting quoted properties with dot
notation. You can override these by setting
them explicitly on the command line.
--mangle-regex Only mangle property names matching the regex
--name-cache File to hold mangled names mappings
--pure-funcs Functions that can be safely removed if their
return value is not used, e.g.
`--pure-funcs Math.floor console.info`
(requires `--compress`)
```
Specify `--output` (`-o`) to declare the output file. Otherwise the output
goes to STDOUT.
## Source map options
UglifyJS2 can generate a source map file, which is highly useful for
debugging your compressed JavaScript. To get a source map, pass
`--source-map output.js.map` (full path to the file where you want the
source map dumped).
Additionally you might need `--source-map-root` to pass the URL where the
original files can be found. In case you are passing full paths to input
files to UglifyJS, you can use `--prefix` (`-p`) to specify the number of
directories to drop from the path prefix when declaring files in the source
map.
For example:
uglifyjs /home/doe/work/foo/src/js/file1.js \
/home/doe/work/foo/src/js/file2.js \
-o foo.min.js \
--source-map foo.min.js.map \
--source-map-root http://foo.com/src \
-p 5 -c -m
The above will compress and mangle `file1.js` and `file2.js`, will drop the
output in `foo.min.js` and the source map in `foo.min.js.map`. The source
mapping will refer to `http://foo.com/src/js/file1.js` and
`http://foo.com/src/js/file2.js` (in fact it will list `http://foo.com/src`
as the source map root, and the original files as `js/file1.js` and
`js/file2.js`).
### Composed source map
When you're compressing JS code that was output by a compiler such as
CoffeeScript, mapping to the JS code won't be too helpful. Instead, you'd
like to map back to the original code (i.e. CoffeeScript). UglifyJS has an
option to take an input source map. Assuming you have a mapping from
CoffeeScript → compiled JS, UglifyJS can generate a map from CoffeeScript →
compressed JS by mapping every token in the compiled JS to its original
location.
To use this feature you need to pass `--in-source-map
/path/to/input/source.map` or `--in-source-map inline` if the source map is
included inline with the sources. Normally the input source map should also
point to the file containing the generated JS, so if that's correct you can
omit input files from the command line.
## Mangler options
To enable the mangler you need to pass `--mangle` (`-m`). The following
(comma-separated) options are supported:
- `toplevel` — mangle names declared in the toplevel scope (disabled by
default).
- `eval` — mangle names visible in scopes where `eval` or `with` are used
(disabled by default).
When mangling is enabled but you want to prevent certain names from being
mangled, you can declare those names with `--reserved` (`-r`) — pass a
comma-separated list of names. For example:
uglifyjs ... -m -r '$,require,exports'
to prevent the `require`, `exports` and `$` names from being changed.
### Mangling property names (`--mangle-props`)
**Note:** this will probably break your code. Mangling property names is a
separate step, different from variable name mangling. Pass
`--mangle-props`. It will mangle all properties that are seen in some
object literal, or that are assigned to. For example:
```js
var x = {
foo: 1
};
x.bar = 2;
x["baz"] = 3;
x[condition ? "moo" : "boo"] = 4;
console.log(x.something());
```
In the above code, `foo`, `bar`, `baz`, `moo` and `boo` will be replaced
with single characters, while `something()` will be left as is.
In order for this to be of any use, we should avoid mangling standard JS
names. For instance, if your code would contain `x.length = 10`, then
`length` becomes a candidate for mangling and it will be mangled throughout
the code, regardless if it's being used as part of your own objects or
accessing an array's length. To avoid that, you can use `--reserved-file`
to pass a filename that should contain the names to be excluded from
mangling. This file can be used both for excluding variable names and
property names. It could look like this, for example:
```js
{
"vars": [ "define", "require", ... ],
"props": [ "length", "prototype", ... ]
}
```
`--reserved-file` can be an array of file names (either a single
comma-separated argument, or you can pass multiple `--reserved-file`
arguments) — in this case it will exclude names from all those files.
A default exclusion file is provided in `tools/domprops.json` which should
cover most standard JS and DOM properties defined in various browsers. Pass
`--reserve-domprops` to read that in.
You can also use a regular expression to define which property names should be
mangled. For example, `--mangle-regex="/^_/"` will only mangle property names
that start with an underscore.
When you compress multiple files using this option, in order for them to
work together in the end we need to ensure somehow that one property gets
mangled to the same name in all of them. For this, pass `--name-cache
filename.json` and UglifyJS will maintain these mappings in a file which can
then be reused. It should be initially empty. Example:
```
rm -f /tmp/cache.json # start fresh
uglifyjs file1.js file2.js --mangle-props --name-cache /tmp/cache.json -o part1.js
uglifyjs file3.js file4.js --mangle-props --name-cache /tmp/cache.json -o part2.js
```
Now, `part1.js` and `part2.js` will be consistent with each other in terms
of mangled property names.
Using the name cache is not necessary if you compress all your files in a
single call to UglifyJS.
#### Mangling unquoted names (`--mangle-props=unquoted` or `--mangle-props=2`)
Using quoted property name (`o["foo"]`) reserves the property name (`foo`)
so that it is not mangled throughout the entire script even when used in an
unquoted style (`o.foo`). Example:
```
$ echo 'var o={"foo":1, bar:3}; o.foo += o.bar; console.log(o.foo);' | uglifyjs --mangle-props=2 -mc
var o={"foo":1,a:3};o.foo+=o.a,console.log(o.foo);
```
#### Debugging property name mangling
You can also pass `--mangle-props-debug` in order to mangle property names
without completely obscuring them. For example the property `o.foo`
would mangle to `o._$foo$_` with this option. This allows property mangling
of a large codebase while still being able to debug the code and identify
where mangling is breaking things.
You can also pass a custom suffix using `--mangle-props-debug=XYZ`. This would then
mangle `o.foo` to `o._$foo$XYZ_`. You can change this each time you compile a
script to identify how a property got mangled. One technique is to pass a
random number on every compile to simulate mangling changing with different
inputs (e.g. as you update the input script with new properties), and to help
identify mistakes like writing mangled keys to storage.
## Compressor options
You need to pass `--compress` (`-c`) to enable the compressor. Optionally
you can pass a comma-separated list of options. Options are in the form
`foo=bar`, or just `foo` (the latter implies a boolean option that you want
to set `true`; it's effectively a shortcut for `foo=true`).
- `sequences` (default: true) -- join consecutive simple statements using the
comma operator. May be set to a positive integer to specify the maximum number
of consecutive comma sequences that will be generated. If this option is set to
`true` then the default `sequences` limit is `200`. Set option to `false` or `0`
to disable. The smallest `sequences` length is `2`. A `sequences` value of `1`
is grandfathered to be equivalent to `true` and as such means `200`. On rare
occasions the default sequences limit leads to very slow compress times in which
case a value of `20` or less is recommended.
- `properties` -- rewrite property access using the dot notation, for
example `foo["bar"] → foo.bar`
- `dead_code` -- remove unreachable code
- `drop_debugger` -- remove `debugger;` statements
- `unsafe` (default: false) -- apply "unsafe" transformations (discussion below)
- `unsafe_comps` (default: false) -- Reverse `<` and `<=` to `>` and `>=` to
allow improved compression. This might be unsafe when an at least one of two
operands is an object with computed values due the use of methods like `get`,
or `valueOf`. This could cause change in execution order after operands in the
comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true.
- `unsafe_math` (default: false) -- optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results.
- `unsafe_proto` (default: false) -- optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)`
- `unsafe_regexp` (default: false) -- enable substitutions of variables with
`RegExp` values the same way as if they are constants.
- `conditionals` -- apply optimizations for `if`-s and conditional
expressions
- `comparisons` -- apply certain optimizations to binary nodes, for example:
`!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary
nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
- `evaluate` -- attempt to evaluate constant expressions
- `booleans` -- various optimizations for boolean context, for example `!!a
? b : c → a ? b : c`
- `loops` -- optimizations for `do`, `while` and `for` loops when we can
statically determine the condition
- `unused` -- drop unreferenced functions and variables (simple direct variable
assignments do not count as references unless set to `"keep_assign"`)
- `toplevel` -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`)
in the toplevel scope (`false` by default, `true` to drop both unreferenced
functions and variables)
- `top_retain` -- prevent specific toplevel functions and variables from `unused`
removal (can be array, comma-separated, RegExp or function. Implies `toplevel`)
- `hoist_funs` -- hoist function declarations
- `hoist_vars` (default: false) -- hoist `var` declarations (this is `false`
by default because it seems to increase the size of the output in general)
- `if_return` -- optimizations for if/return and if/continue
- `join_vars` -- join consecutive `var` statements
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()`
- `collapse_vars` -- Collapse single-use `var` and `const` definitions
when possible.
- `reduce_vars` -- Improve optimization on variables assigned with and
used as constant values.
- `warnings` -- display warnings when dropping unreachable code or unused
declarations etc.
- `negate_iife` -- negate "Immediately-Called Function Expressions"
where the return value is discarded, to avoid the parens that the
code generator would insert.
- `pure_getters` -- the default is `false`. If you pass `true` for
this, UglifyJS will assume that object property access
(e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects.
Specify `"strict"` to treat `foo.bar` as side-effect-free only when
`foo` is certain to not throw, i.e. not `null` or `undefined`.
- `pure_funcs` -- default `null`. You can pass an array of names and
UglifyJS will assume that those functions do not produce side
effects. DANGER: will not check if the name is redefined in scope.
An example case here, for instance `var q = Math.floor(a/b)`. If
variable `q` is not used elsewhere, UglifyJS will drop it, but will
still keep the `Math.floor(a/b)`, not knowing what it does. You can
pass `pure_funcs: [ 'Math.floor' ]` to let it know that this
function won't produce any side effect, in which case the whole
statement would get discarded. The current implementation adds some
overhead (compression will be slower).
- `drop_console` -- default `false`. Pass `true` to discard calls to
`console.*` functions. If you wish to drop a specific function call
such as `console.info` and/or retain side effects from function arguments
after dropping the function call then use `pure_funcs` instead.
- `expression` -- default `false`. Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
- `keep_fargs` -- default `true`. Prevents the
compressor from discarding unused function arguments. You need this
for code which relies on `Function.length`.
- `keep_fnames` -- default `false`. Pass `true` to prevent the
compressor from discarding function names. Useful for code relying on
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle).
- `passes` -- default `1`. Number of times to run compress with a maximum of 3.
In some cases more than one pass leads to further compressed code. Keep in
mind more passes will take more time.
- `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome.
- `side_effects` -- default `true`. Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();`
### The `unsafe` option
It enables some transformations that *might* break code logic in certain
contrived cases, but should be fine for most code. You might want to try it
on your own code, it should reduce the minified size. Here's what happens
when this flag is on:
- `new Array(1, 2, 3)` or `Array(1, 2, 3)` → `[ 1, 2, 3 ]`
- `new Object()` → `{}`
- `String(exp)` or `exp.toString()` → `"" + exp`
- `new Object/RegExp/Function/Error/Array (...)` → we discard the `new`
- `typeof foo == "undefined"` → `foo === void 0`
- `void 0` → `undefined` (if there is a variable named "undefined" in
scope; we do it because the variable name will be mangled, typically
reduced to a single character)
### Conditional compilation
You can use the `--define` (`-d`) switch in order to declare global
variables that UglifyJS will assume to be constants (unless defined in
scope). For example if you pass `--define DEBUG=false` then, coupled with
dead code removal UglifyJS will discard the following from the output:
```javascript
if (DEBUG) {
console.log("debug stuff");
}
```
You can specify nested constants in the form of `--define env.DEBUG=false`.
UglifyJS will warn about the condition being always false and about dropping
unreachable code; for now there is no option to turn off only this specific
warning, you can pass `warnings=false` to turn off *all* warnings.
Another way of doing that is to declare your globals as constants in a
separate file and include it into the build. For example you can have a
`build/defines.js` file with the following:
```javascript
const DEBUG = false;
const PRODUCTION = true;
// etc.
```
and build your code like this:
uglifyjs build/defines.js js/foo.js js/bar.js... -c
UglifyJS will notice the constants and, since they cannot be altered, it
will evaluate references to them to the value itself and drop unreachable
code as usual. The build will contain the `const` declarations if you use
them. If you are targeting < ES6 environments which does not support `const`,
using `var` with `reduce_vars` (enabled by default) should suffice.
<a name="codegen-options"></a>
#### Conditional compilation, API
You can also use conditional compilation via the programmatic API. With the difference that the
property name is `global_defs` and is a compressor property:
```js
uglifyJS.minify([ "input.js"], {
compress: {
dead_code: true,
global_defs: {
DEBUG: false
}
}
});
```
## Beautifier options
The code generator tries to output shortest code possible by default. In
case you want beautified output, pass `--beautify` (`-b`). Optionally you
can pass additional arguments that control the code output:
- `beautify` (default `true`) -- whether to actually beautify the output.
Passing `-b` will set this to true, but you might need to pass `-b` even
when you want to generate minified code, in order to specify additional
arguments, so you can use `-b beautify=false` to override it.
- `indent-level` (default 4)
- `indent-start` (default 0) -- prefix all lines by that many spaces
- `quote-keys` (default `false`) -- pass `true` to quote all keys in literal
objects
- `space-colon` (default `true`) -- insert a space after the colon signs
- `ascii-only` (default `false`) -- escape Unicode characters in strings and
regexps (affects directives with non-ascii characters becoming invalid)
- `inline-script` (default `false`) -- escape the slash in occurrences of
`</script` in strings
- `width` (default 80) -- only takes effect when beautification is on, this
specifies an (orientative) line width that the beautifier will try to
obey. It refers to the width of the line text (excluding indentation).
It doesn't work very well currently, but it does make the code generated
by UglifyJS more readable.
- `max-line-len` (default 32000) -- maximum line length (for uglified code)
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
`do`, `while` or `with` statements, even if their body is a single
statement.
- `semicolons` (default `true`) -- separate statements with semicolons. If
you pass `false` then whenever possible we will use a newline instead of a
semicolon, leading to more readable output of uglified code (size before
gzip could be smaller; size after gzip insignificantly larger).
- `preamble` (default `null`) -- when passed it must be a string and
it will be prepended to the output literally. The source map will
adjust for this text. Can be used to insert a comment containing
licensing information, for example.
- `quote_style` (default `0`) -- preferred quote style for strings (affects
quoted property names and directives as well):
- `0` -- prefers double quotes, switches to single quotes when there are
more double quotes in the string itself.
- `1` -- always use single quotes
- `2` -- always use double quotes
- `3` -- always use the original quotes
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
quotes from property names in object literals.
### Keeping copyright notices or other comments
You can pass `--comments` to retain certain comments in the output. By
default it will keep JSDoc-style comments that contain "@preserve",
"@license" or "@cc_on" (conditional compilation for IE). You can pass
`--comments all` to keep all the comments, or a valid JavaScript regexp to
keep only comments that match this regexp. For example `--comments
'/foo|bar/'` will keep only comments that contain "foo" or "bar".
Note, however, that there might be situations where comments are lost. For
example:
```javascript
function f() {
/** @preserve Foo Bar */
function g() {
// this function is never called
}
return something();
}
```
Even though it has "@preserve", the comment will be lost because the inner
function `g` (which is the AST node to which the comment is attached to) is
discarded by the compressor as not referenced.
The safest comments where to place copyright information (or other info that
needs to be kept in the output) are comments attached to toplevel nodes.
## Support for the SpiderMonkey AST
UglifyJS2 has its own abstract syntax tree format; for
[practical reasons](http://lisperator.net/blog/uglifyjs-why-not-switching-to-spidermonkey-ast/)
we can't easily change to using the SpiderMonkey AST internally. However,
UglifyJS now has a converter which can import a SpiderMonkey AST.
For example [Acorn][acorn] is a super-fast parser that produces a
SpiderMonkey AST. It has a small CLI utility that parses one file and dumps
the AST in JSON on the standard output. To use UglifyJS to mangle and
compress that:
acorn file.js | uglifyjs --spidermonkey -m -c
The `--spidermonkey` option tells UglifyJS that all input files are not
JavaScript, but JS code described in SpiderMonkey AST in JSON. Therefore we
don't use our own parser in this case, but just transform that AST into our
internal AST.
### Use Acorn for parsing
More for fun, I added the `--acorn` option which will use Acorn to do all
the parsing. If you pass this option, UglifyJS will `require("acorn")`.
Acorn is really fast (e.g. 250ms instead of 380ms on some 650K code), but
converting the SpiderMonkey tree that Acorn produces takes another 150ms so
in total it's a bit more than just using UglifyJS's own parser.
### Using UglifyJS to transform SpiderMonkey AST
Now you can use UglifyJS as any other intermediate tool for transforming
JavaScript ASTs in SpiderMonkey format.
Example:
```javascript
function uglify(ast, options, mangle) {
// Conversion from SpiderMonkey AST to internal format
var uAST = UglifyJS.AST_Node.from_mozilla_ast(ast);
// Compression
uAST.figure_out_scope();
uAST = UglifyJS.Compressor(options).compress(uAST);
// Mangling (optional)
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
API Reference
-------------
Assuming installation via NPM, you can load UglifyJS in your application
like this:
```javascript
var UglifyJS = require("uglify-js");
```
It exports a lot of names, but I'll discuss here the basics that are needed
for parsing, mangling and compressing a piece of code. The sequence is (1)
parse, (2) compress, (3) mangle, (4) generate output code.
### The simple way
There's a single toplevel function which combines all the steps. If you
don't need additional customization, you might want to go with `minify`.
Example:
```javascript
var result = UglifyJS.minify("/path/to/file.js");
console.log(result.code); // minified output
// if you need to pass code instead of file name
var result = UglifyJS.minify("var b = function () {};", {fromString: true});
```
You can also compress multiple files:
```javascript
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ]);
console.log(result.code);
```
To generate a source map:
```javascript
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
outSourceMap: "out.js.map"
});
console.log(result.code); // minified output
console.log(result.map);
```
To generate a source map with the fromString option, you can also use an object:
```javascript
var result = UglifyJS.minify({"file1.js": "var a = function () {};"}, {
outSourceMap: "out.js.map",
outFileName: "out.js",
fromString: true
});
```
Note that the source map is not saved in a file, it's just returned in
`result.map`. The value passed for `outSourceMap` is only used to set
`//# sourceMappingURL=out.js.map` in `result.code`. The value of
`outFileName` is only used to set `file` attribute in source map file.
The `file` attribute in the source map (see [the spec][sm-spec]) will
use `outFileName` firstly, if it's falsy, then will be deduced from
`outSourceMap` (by removing `'.map'`).
You can set option `sourceMapInline` to be `true` and source map will
be appended to code.
You can also specify sourceRoot property to be included in source map:
```javascript
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
outSourceMap: "out.js.map",
sourceRoot: "http://example.com/src"
});
```
If you're compressing compiled JavaScript and have a source map for it, you
can use the `inSourceMap` argument:
```javascript
var result = UglifyJS.minify("compiled.js", {
inSourceMap: "compiled.js.map",
outSourceMap: "minified.js.map"
});
// same as before, it returns `code` and `map`
```
If your input source map is not in a file, you can pass it in as an object
using the `inSourceMap` argument:
```javascript
var result = UglifyJS.minify("compiled.js", {
inSourceMap: JSON.parse(my_source_map_string),
outSourceMap: "minified.js.map"
});
```
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
no sense otherwise).
To set the source map url, use the `sourceMapUrl` option.
If you're using the X-SourceMap header instead, you can just set the `sourceMapUrl` option to false.
Defaults to outSourceMap:
```javascript
var result = UglifyJS.minify([ "file1.js" ], {
outSourceMap: "out.js.map",
sourceMapUrl: "localhost/out.js.map"
});
```
Other options:
- `warnings` (default `false`) — pass `true` to display compressor warnings.
- `fromString` (default `false`) — if you pass `true` then you can pass
JavaScript source code, rather than file names.
- `mangle` (default `true`) — pass `false` to skip mangling names, or pass
an object to specify mangling options (see below).
- `mangleProperties` (default `false`) — pass an object to specify custom
mangle property options.
- `output` (default `null`) — pass an object if you wish to specify
additional [output options][codegen]. The defaults are optimized
for best compression.
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
Pass an object to specify custom [compressor options][compressor].
- `parse` (default {}) — pass an object if you wish to specify some
additional [parser options][parser]. (not all options available... see below)
##### mangle
- `except` - pass an array of identifiers that should be excluded from mangling
- `toplevel` — mangle names declared in the toplevel scope (disabled by
default).
- `eval` — mangle names visible in scopes where eval or with are used
(disabled by default).
- `keep_fnames` -- default `false`. Pass `true` to not mangle
function names. Useful for code relying on `Function.prototype.name`.
See also: the `keep_fnames` [compress option](#compressor-options).
Examples:
```javascript
//tst.js
var globalVar;
function funcName(firstLongName, anotherLongName)
{
var myVariable = firstLongName + anotherLongName;
}
UglifyJS.minify("tst.js").code;
// 'function funcName(a,n){}var globalVar;'
UglifyJS.minify("tst.js", { mangle: { except: ['firstLongName'] } }).code;
// 'function funcName(firstLongName,a){}var globalVar;'
UglifyJS.minify("tst.js", { mangle: { toplevel: true } }).code;
// 'function n(n,a){}var a;'
```
##### mangleProperties options
- `regex` — Pass a RegExp to only mangle certain names (maps to the `--mangle-regex` CLI arguments option)
- `ignore_quoted` Only mangle unquoted property names (maps to the `--mangle-props 2` CLI arguments option)
- `debug` Mangle names with the original name still present (maps to the `--mangle-props-debug` CLI arguments option). Defaults to `false`. Pass an empty string to enable, or a non-empty string to set the suffix.
We could add more options to `UglifyJS.minify` — if you need additional
functionality please suggest!
### The hard way
Following there's more detailed API info, in case the `minify` function is
too simple for your needs.
#### The parser
```javascript
var toplevel_ast = UglifyJS.parse(code, options);
```
`options` is optional and if present it must be an object. The following
properties are available:
- `strict` — disable automatic semicolon insertion and support for trailing
comma in arrays and objects
- `bare_returns` — Allow return outside of functions. (maps to the
`--bare-returns` CLI arguments option and available to `minify` `parse`
other options object)
- `filename` — the name of the file where this code is coming from
- `toplevel` — a `toplevel` node (as returned by a previous invocation of
`parse`)
The last two options are useful when you'd like to minify multiple files and
get a single file as the output and a proper source map. Our CLI tool does
something like this:
```javascript
var toplevel = null;
files.forEach(function(file){
var code = fs.readFileSync(file, "utf8");
toplevel = UglifyJS.parse(code, {
filename: file,
toplevel: toplevel
});
});
```
After this, we have in `toplevel` a big AST containing all our files, with
each token having proper information about where it came from.
#### Scope information
UglifyJS contains a scope analyzer that you need to call manually before
compressing or mangling. Basically it augments various nodes in the AST
with information about where is a name defined, how many times is a name
referenced, if it is a global or not, if a function is using `eval` or the
`with` statement etc. I will discuss this some place else, for now what's
important to know is that you need to call the following before doing
anything with the tree:
```javascript
toplevel.figure_out_scope()
```
#### Compression
Like this:
```javascript
var compressor = UglifyJS.Compressor(options);
var compressed_ast = compressor.compress(toplevel);
```
The `options` can be missing. Available options are discussed above in
“Compressor options”. Defaults should lead to best compression in most
scripts.
The compressor is destructive, so don't rely that `toplevel` remains the
original tree.
#### Mangling
After compression it is a good idea to call again `figure_out_scope` (since
the compressor might drop unused variables / unreachable code and this might
change the number of identifiers or their position). Optionally, you can
call a trick that helps after Gzip (counting character frequency in
non-mangleable words). Example:
```javascript
compressed_ast.figure_out_scope();
compressed_ast.compute_char_frequency();
compressed_ast.mangle_names();
```
#### Generating output
AST nodes have a `print` method that takes an output stream. Essentially,
to generate code you do this:
```javascript
var stream = UglifyJS.OutputStream(options);
compressed_ast.print(stream);
var code = stream.toString(); // this is your minified code
```
or, for a shortcut you can do:
```javascript
var code = compressed_ast.print_to_string(options);
```
As usual, `options` is optional. The output stream accepts a lot of options,
most of them documented above in section “Beautifier options”. The two
which we care about here are `source_map` and `comments`.
#### Keeping comments in the output
In order to keep certain comments in the output you need to pass the
`comments` option. Pass a RegExp (as string starting and closing with `/`
or pass a RegExp object), a boolean or a function. Stringified options
`all` and `some` can be passed too, where `some` behaves like it's cli
equivalent `--comments` without passing a value. If you pass a RegExp,
only those comments whose body matches the RegExp will be kept. Note that body
means without the initial `//` or `/*`. If you pass a function, it will be
called for every comment in the tree and will receive two arguments: the
node that the comment is attached to, and the comment token itself.
The comment token has these properties:
- `type`: "comment1" for single-line comments or "comment2" for multi-line
comments
- `value`: the comment body
- `pos` and `endpos`: the start/end positions (zero-based indexes) in the
original code where this comment appears
- `line` and `col`: the line and column where this comment appears in the
original code
- `file` — the file name of the original file
- `nlb` — true if there was a newline before this comment in the original
code, or if this comment contains a newline.
Your function should return `true` to keep the comment, or a falsy value
otherwise.
#### Generating a source mapping
You need to pass the `source_map` argument when calling `print`. It needs
to be a `SourceMap` object (which is a thin wrapper on top of the
[source-map][source-map] library).
Example:
```javascript
var source_map = UglifyJS.SourceMap(source_map_options);
var stream = UglifyJS.OutputStream({
...
source_map: source_map
});
compressed_ast.print(stream);
var code = stream.toString();
var map = source_map.toString(); // json output for your source map
```
The `source_map_options` (optional) can contain the following properties:
- `file`: the name of the JavaScript output file that this mapping refers to
- `root`: the `sourceRoot` property (see the [spec][sm-spec])
- `orig`: the "original source map", handy when you compress generated JS
and want to map the minified output back to the original code where it
came from. It can be simply a string in JSON, or a JSON object containing
the original source map.
[acorn]: https://github.com/ternjs/acorn
[source-map]: https://github.com/mozilla/source-map
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
[codegen]: http://lisperator.net/uglifyjs/codegen
[compressor]: http://lisperator.net/uglifyjs/compress
[parser]: http://lisperator.net/uglifyjs/parser
#### Support for `const`
`const` in `uglify-js@2.x` has function scope and as such behaves much like
`var` - unlike `const` in ES2015 (ES6) which has block scope. It is recommended
to avoid using `const` for this reason as it will have undefined behavior when
run on an ES2015 compatible browser.

View File

@@ -0,0 +1,77 @@
#! /usr/bin/env node
"use strict";
var U2 = require("../tools/node");
var fs = require("fs");
var yargs = require("yargs");
var ARGS = yargs
.describe("o", "Output file")
.argv;
var files = ARGS._.slice();
var output = {
vars: {},
props: {}
};
if (ARGS.o) try {
output = JSON.parse(fs.readFileSync(ARGS.o, "utf8"));
} catch(ex) {}
files.forEach(getProps);
if (ARGS.o) {
fs.writeFileSync(ARGS.o, JSON.stringify(output, null, 2), "utf8");
} else {
console.log("%s", JSON.stringify(output, null, 2));
}
function getProps(filename) {
var code = fs.readFileSync(filename, "utf8");
var ast = U2.parse(code);
ast.walk(new U2.TreeWalker(function(node){
if (node instanceof U2.AST_ObjectKeyVal) {
add(node.key);
}
else if (node instanceof U2.AST_ObjectProperty) {
add(node.key.name);
}
else if (node instanceof U2.AST_Dot) {
add(node.property);
}
else if (node instanceof U2.AST_Sub) {
addStrings(node.property);
}
}));
function addStrings(node) {
var out = {};
try {
(function walk(node){
node.walk(new U2.TreeWalker(function(node){
if (node instanceof U2.AST_Seq) {
walk(node.cdr);
return true;
}
if (node instanceof U2.AST_String) {
add(node.value);
return true;
}
if (node instanceof U2.AST_Conditional) {
walk(node.consequent);
walk(node.alternative);
return true;
}
throw out;
}));
})(node);
} catch(ex) {
if (ex !== out) throw ex;
}
}
function add(name) {
output.props[name] = true;
}
}

View File

@@ -0,0 +1,635 @@
#! /usr/bin/env node
// -*- js -*-
"use strict";
var UglifyJS = require("../tools/node");
var sys = require("util");
var yargs = require("yargs");
var fs = require("fs");
var path = require("path");
var acorn;
var screw_ie8 = true;
var ARGS = yargs
.usage("$0 input1.js [input2.js ...] [options]\n\
Use a single dash to read input from the standard input.\
\n\n\
NOTE: by default there is no mangling/compression.\n\
Without [options] it will simply parse input files and dump the AST\n\
with whitespace and comments discarded. To achieve compression and\n\
mangling you need to use `-c` and `-m`.\
")
.describe("source-map", "Specify an output file where to generate source map.")
.describe("source-map-root", "The path to the original source to be included in the source map.")
.describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.")
.describe("source-map-inline", "Write base64-encoded source map to the end of js output. Disabled by default")
.describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.")
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
.describe("screw-ie8", "Do not support Internet Explorer 6/7/8. This flag is enabled by default.")
.describe("support-ie8", "Support non-standard Internet Explorer 6/7/8 javascript.")
.describe("expr", "Parse a single expression, rather than a program (for parsing JSON)")
.describe("p", "Skip prefix for original filenames that appear in source maps. \
For example -p 3 will drop 3 directories from file names and ensure they are relative paths. \
You can also specify -p relative, which will make UglifyJS figure out itself the relative paths between original sources, \
the source map and the output file.")
.describe("o", "Output file (default STDOUT).")
.describe("b", "Beautify output/specify output options.")
.describe("m", "Mangle names/pass mangler options.")
.describe("r", "Reserved names to exclude from mangling.")
.describe("c", "Enable compressor/pass compressor options. \
Pass options like -c hoist_vars=false,if_return=false. \
Use -c with no argument to use the default compression options.")
.describe("d", "Global definitions")
.describe("e", "Embed everything in a big function, with a configurable parameter/argument list.")
.describe("comments", "Preserve copyright comments in the output. \
By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \
You can optionally pass one of the following arguments to this flag:\n\
- \"all\" to keep all comments\n\
- a valid JS RegExp like `/foo/`or `/^!/` to keep only matching comments.\n\
\
Note that currently not *all* comments can be kept when compression is on, \
because of dead code removal or cascading statements into sequences.")
.describe("preamble", "Preamble to prepend to the output. You can use this to insert a \
comment, for example for licensing information. This will not be \
parsed, but the source map will adjust for its presence.")
.describe("stats", "Display operations run time on STDERR.")
.describe("acorn", "Use Acorn for parsing.")
.describe("spidermonkey", "Assume input files are SpiderMonkey AST format (as JSON).")
.describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)")
.describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \
You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.")
.describe("export-all", "Only used when --wrap, this tells UglifyJS to add code to automatically export all globals.")
.describe("lint", "Display some scope warnings")
.describe("v", "Verbose")
.describe("V", "Print version number and exit.")
.describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.")
.describe("bare-returns", "Allow return outside of functions. Useful when minifying CommonJS modules.")
.describe("keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.")
.describe("quotes", "Quote style (0 - auto, 1 - single, 2 - double, 3 - original)")
.describe("reserved-file", "File containing reserved names")
.describe("reserve-domprops", "Make (most?) DOM properties reserved for --mangle-props")
.describe("mangle-props", "Mangle property names (0 - disabled, 1 - mangle all properties, 2 - mangle unquoted properies)")
.describe("mangle-regex", "Only mangle property names matching the regex")
.describe("name-cache", "File to hold mangled names mappings")
.describe("pure-funcs", "List of functions that can be safely removed if their return value is not used")
.describe("dump-spidermonkey-ast", "Dump SpiderMonkey AST to stdout.")
.describe("wrap-iife", "Wrap IIFEs in parenthesis. Note: this disables the negate_iife compression option")
.alias("p", "prefix")
.alias("o", "output")
.alias("v", "verbose")
.alias("b", "beautify")
.alias("m", "mangle")
.alias("c", "compress")
.alias("d", "define")
.alias("r", "reserved")
.alias("V", "version")
.alias("e", "enclose")
.alias("q", "quotes")
.string("source-map")
.string("source-map-root")
.string("source-map-url")
.string("b")
.string("beautify")
.string("m")
.string("mangle")
.string("mangle-props-debug")
.string("c")
.string("compress")
.string("d")
.string("define")
.string("e")
.string("enclose")
.string("comments")
.string("wrap")
.string("p")
.string("prefix")
.string("name-cache")
.array("reserved-file")
.array("pure-funcs")
.boolean("expr")
.boolean("source-map-inline")
.boolean("source-map-include-sources")
.boolean("screw-ie8")
.boolean("support-ie8")
.boolean("export-all")
.boolean("self")
.boolean("v")
.boolean("verbose")
.boolean("stats")
.boolean("acorn")
.boolean("spidermonkey")
.boolean("dump-spidermonkey-ast")
.boolean("lint")
.boolean("V")
.boolean("version")
.boolean("noerr")
.boolean("bare-returns")
.boolean("keep-fnames")
.boolean("reserve-domprops")
.boolean("wrap-iife")
.wrap(80)
.argv
;
normalize(ARGS);
if (ARGS.noerr) {
UglifyJS.DefaultsError.croak = function(msg, defs) {
print_error("WARN: " + msg);
};
}
if (ARGS.version || ARGS.V) {
var json = require("../package.json");
print(json.name + ' ' + json.version);
process.exit(0);
}
if (ARGS.ast_help) {
var desc = UglifyJS.describe_ast();
print(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
process.exit(0);
}
if (ARGS.h || ARGS.help) {
print(yargs.help());
process.exit(0);
}
if (ARGS.acorn) {
acorn = require("acorn");
}
var COMPRESS = getOptions("c", true);
var MANGLE = getOptions("m", true);
var BEAUTIFY = getOptions("b", true);
var RESERVED = null;
if (ARGS.reserved_file) ARGS.reserved_file.forEach(function(filename){
RESERVED = UglifyJS.readReservedFile(filename, RESERVED);
});
if (ARGS.reserve_domprops) {
RESERVED = UglifyJS.readDefaultReservedFile(RESERVED);
}
if (ARGS.d) {
if (COMPRESS) COMPRESS.global_defs = getOptions("d");
}
if (ARGS.pure_funcs) {
if (COMPRESS) COMPRESS.pure_funcs = ARGS.pure_funcs;
}
if (ARGS.r) {
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
}
if (RESERVED && MANGLE) {
if (!MANGLE.except) MANGLE.except = RESERVED.vars;
else MANGLE.except = MANGLE.except.concat(RESERVED.vars);
}
function readNameCache(key) {
return UglifyJS.readNameCache(ARGS.name_cache, key);
}
function writeNameCache(key, cache) {
return UglifyJS.writeNameCache(ARGS.name_cache, key, cache);
}
function extractRegex(str) {
if (/^\/.*\/[a-zA-Z]*$/.test(str)) {
var regex_pos = str.lastIndexOf("/");
return new RegExp(str.substr(1, regex_pos - 1), str.substr(regex_pos + 1));
} else {
throw new Error("Invalid regular expression: " + str);
}
}
if (ARGS.quotes === true) {
ARGS.quotes = 3;
}
if (ARGS.mangle_props === true) {
ARGS.mangle_props = 1;
} else if (ARGS.mangle_props === "unquoted") {
ARGS.mangle_props = 2;
}
var OUTPUT_OPTIONS = {
beautify : BEAUTIFY ? true : false,
max_line_len : 32000,
preamble : ARGS.preamble || null,
quote_style : ARGS.quotes != null ? ARGS.quotes : 0,
};
if (ARGS.mangle_props == 2) {
OUTPUT_OPTIONS.keep_quoted_props = true;
if (COMPRESS && !("properties" in COMPRESS))
COMPRESS.properties = false;
}
if (ARGS.support_ie8 === true && ARGS.screw_ie8 !== true) {
screw_ie8 = false;
}
if (COMPRESS) COMPRESS.screw_ie8 = screw_ie8;
if (MANGLE) MANGLE.screw_ie8 = screw_ie8;
OUTPUT_OPTIONS.screw_ie8 = screw_ie8;
if (ARGS.keep_fnames) {
if (COMPRESS) COMPRESS.keep_fnames = true;
if (MANGLE) MANGLE.keep_fnames = true;
}
if (ARGS.wrap_iife) {
if (COMPRESS) COMPRESS.negate_iife = false;
OUTPUT_OPTIONS.wrap_iife = true;
}
if (BEAUTIFY)
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
if (ARGS.comments === "") {
OUTPUT_OPTIONS.comments = "some";
} else {
OUTPUT_OPTIONS.comments = ARGS.comments;
}
var files = ARGS._.slice();
if (process.platform === "win32")
files = UglifyJS.simple_glob(files);
if (ARGS.self) {
if (files.length > 0) {
print_error("WARN: Ignoring input files since --self was passed");
}
files = UglifyJS.FILES;
if (!ARGS.wrap) ARGS.wrap = "UglifyJS";
}
var ORIG_MAP = ARGS.in_source_map;
if (ORIG_MAP && ORIG_MAP != "inline") {
ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP));
if (files.length == 0) {
print_error("INFO: Using file from the input source map: " + ORIG_MAP.file);
files = [ ORIG_MAP.file ];
}
}
if (files.length == 0) {
files = [ "-" ];
}
if (ORIG_MAP == "inline") {
if (files.length > 1) {
print_error("ERROR: Inline source map only works with singular input");
process.exit(1);
}
if (ARGS.acorn || ARGS.spidermonkey) {
print_error("ERROR: Inline source map only works with built-in parser");
process.exit(1);
}
}
if (files.indexOf("-") >= 0 && ARGS.source_map) {
print_error("ERROR: Source map doesn't work with input from STDIN");
process.exit(1);
}
if (files.filter(function(el){ return el == "-" }).length > 1) {
print_error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
process.exit(1);
}
var STATS = {};
var TOPLEVEL = null;
var P_RELATIVE = ARGS.p && ARGS.p == "relative";
var SOURCES_CONTENT = {};
var index = 0;
!function cb() {
if (index == files.length) return done();
var file = files[index++];
read_whole_file(file, function (err, code) {
if (err) {
print_error("ERROR: can't read file: " + file);
process.exit(1);
}
if (ORIG_MAP == "inline") {
ORIG_MAP = read_source_map(code);
}
if (ARGS.p != null) {
if (P_RELATIVE) {
file = path.relative(path.dirname(ARGS.source_map), file).replace(/\\/g, '/');
} else {
var p = parseInt(ARGS.p, 10);
if (!isNaN(p)) {
file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/");
}
}
}
SOURCES_CONTENT[file] = code;
time_it("parse", function(){
if (ARGS.spidermonkey) {
var program = JSON.parse(code);
if (!TOPLEVEL) TOPLEVEL = program;
else TOPLEVEL.body = TOPLEVEL.body.concat(program.body);
}
else if (ARGS.acorn) {
TOPLEVEL = acorn.parse(code, {
locations : true,
sourceFile : file,
program : TOPLEVEL
});
}
else {
try {
TOPLEVEL = UglifyJS.parse(code, {
filename : file,
toplevel : TOPLEVEL,
expression : ARGS.expr,
bare_returns : ARGS.bare_returns,
});
} catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Parse error at " + file + ":" + ex.line + "," + ex.col);
var col = ex.col;
var lines = code.split(/\r?\n/);
var line = lines[ex.line - 1];
if (!line && !col) {
line = lines[ex.line - 2];
col = line.length;
}
if (line) {
if (col > 40) {
line = line.slice(col - 40);
col = 40;
}
print_error(line.slice(0, 80));
print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
}
print_error(ex.stack);
process.exit(1);
}
throw ex;
}
};
});
cb();
});
}();
function done() {
var OUTPUT_FILE = ARGS.o;
var SOURCE_MAP = (ARGS.source_map || ARGS.source_map_inline) ? UglifyJS.SourceMap({
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
root: ARGS.source_map_root || ORIG_MAP && ORIG_MAP.sourceRoot,
orig: ORIG_MAP,
}) : null;
OUTPUT_OPTIONS.source_map = SOURCE_MAP;
try {
var output = UglifyJS.OutputStream(OUTPUT_OPTIONS);
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
} catch(ex) {
if (ex instanceof UglifyJS.DefaultsError) {
print_error(ex.message);
print_error("Supported options:");
print_error(sys.inspect(ex.defs));
process.exit(1);
}
}
if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
});
if (ARGS.wrap != null) {
TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all);
}
if (ARGS.enclose != null) {
var arg_parameter_list = ARGS.enclose;
if (arg_parameter_list === true) {
arg_parameter_list = [];
}
else if (!(arg_parameter_list instanceof Array)) {
arg_parameter_list = [arg_parameter_list];
}
TOPLEVEL = TOPLEVEL.wrap_enclose(arg_parameter_list);
}
if (ARGS.mangle_props || ARGS.name_cache) (function(){
var reserved = RESERVED ? RESERVED.props : null;
var cache = readNameCache("props");
var regex;
try {
regex = ARGS.mangle_regex ? extractRegex(ARGS.mangle_regex) : null;
} catch (e) {
print_error("ERROR: Invalid --mangle-regex: " + e.message);
process.exit(1);
}
TOPLEVEL = UglifyJS.mangle_properties(TOPLEVEL, {
reserved : reserved,
cache : cache,
only_cache : !ARGS.mangle_props,
regex : regex,
ignore_quoted : ARGS.mangle_props == 2,
debug : typeof ARGS.mangle_props_debug === "undefined" ? false : ARGS.mangle_props_debug
});
writeNameCache("props", cache);
})();
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint
var TL_CACHE = readNameCache("vars");
if (MANGLE) MANGLE.cache = TL_CACHE;
if (SCOPE_IS_NEEDED) {
time_it("scope", function(){
TOPLEVEL.figure_out_scope(MANGLE || { screw_ie8: screw_ie8, cache: TL_CACHE });
if (ARGS.lint) {
TOPLEVEL.scope_warnings();
}
});
}
if (COMPRESS) {
time_it("squeeze", function(){
TOPLEVEL = compressor.compress(TOPLEVEL);
});
}
if (SCOPE_IS_NEEDED) {
time_it("scope", function(){
TOPLEVEL.figure_out_scope(MANGLE || { screw_ie8: screw_ie8, cache: TL_CACHE });
if (MANGLE && !TL_CACHE) {
TOPLEVEL.compute_char_frequency(MANGLE);
}
});
}
if (MANGLE) time_it("mangle", function(){
TOPLEVEL.mangle_names(MANGLE);
});
writeNameCache("vars", TL_CACHE);
if (ARGS.source_map_include_sources) {
for (var file in SOURCES_CONTENT) {
if (SOURCES_CONTENT.hasOwnProperty(file)) {
SOURCE_MAP.get().setSourceContent(file, SOURCES_CONTENT[file]);
}
}
}
if (ARGS.dump_spidermonkey_ast) {
print(JSON.stringify(TOPLEVEL.to_mozilla_ast(), null, 2));
} else {
time_it("generate", function(){
TOPLEVEL.print(output);
});
output = output.get();
if (SOURCE_MAP) {
if (ARGS.source_map_inline) {
var base64_string = new Buffer(SOURCE_MAP.toString()).toString('base64');
output += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + base64_string;
} else {
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
var source_map_url = ARGS.source_map_url || (
P_RELATIVE
? path.relative(path.dirname(OUTPUT_FILE), ARGS.source_map)
: ARGS.source_map
);
output += "\n//# sourceMappingURL=" + source_map_url;
}
}
if (OUTPUT_FILE) {
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
} else {
print(output);
}
}
if (ARGS.stats) {
print_error(UglifyJS.string_template("Timing information (compressed {count} files):", {
count: files.length
}));
for (var i in STATS) if (STATS.hasOwnProperty(i)) {
print_error(UglifyJS.string_template("- {name}: {time}s", {
name: i,
time: (STATS[i] / 1000).toFixed(3)
}));
}
}
}
/* -----[ functions ]----- */
function normalize(o) {
for (var i in o) if (o.hasOwnProperty(i) && /-/.test(i)) {
o[i.replace(/-/g, "_")] = o[i];
delete o[i];
}
}
function getOptions(flag, constants) {
var x = ARGS[flag];
if (x == null || x === false) return null;
var ret = {};
if (x !== "") {
if (Array.isArray(x)) x = x.map(function (v) { return "(" + v + ")"; }).join(", ");
var ast;
try {
ast = UglifyJS.parse(x, { cli: true, expression: true });
} catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Error parsing arguments for flag `" + flag + "': " + x);
process.exit(1);
}
}
ast.walk(new UglifyJS.TreeWalker(function(node){
if (node instanceof UglifyJS.AST_Seq) return; // descend
if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string().replace(/-/g, "_");
var value = node.right;
if (constants)
value = new Function("return (" + value.print_to_string() + ")")();
ret[name] = value;
return true; // no descend
}
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_Binary) {
var name = node.print_to_string().replace(/-/g, "_");
ret[name] = true;
return true; // no descend
}
print_error(node.TYPE)
print_error("Error parsing arguments for flag `" + flag + "': " + x);
process.exit(1);
}));
}
return ret;
}
function read_whole_file(filename, cb) {
if (filename == "-") {
var chunks = [];
process.stdin.setEncoding('utf-8');
process.stdin.on('data', function (chunk) {
chunks.push(chunk);
}).on('end', function () {
cb(null, chunks.join(""));
});
process.openStdin();
} else {
fs.readFile(filename, "utf-8", cb);
}
}
function read_source_map(code) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code);
if (!match) {
print_error("WARN: inline source map not found");
return null;
}
return JSON.parse(new Buffer(match[2], "base64"));
}
function time_it(name, cont) {
var t1 = new Date().getTime();
var ret = cont();
if (ARGS.stats) {
var spent = new Date().getTime() - t1;
if (STATS[name]) STATS[name] += spent;
else STATS[name] = spent;
}
return ret;
}
function print_error(msg) {
console.error("%s", msg);
}
function print(txt) {
console.log("%s", txt);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,611 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
-------------------------------- (C) ---------------------------------
Author: Mihai Bazon
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
Distributed under the BSD license:
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
***********************************************************************/
"use strict";
(function(){
var normalize_directives = function(body) {
var in_directive = true;
for (var i = 0; i < body.length; i++) {
if (in_directive && body[i] instanceof AST_Statement && body[i].body instanceof AST_String) {
body[i] = new AST_Directive({
start: body[i].start,
end: body[i].end,
value: body[i].body.value
});
} else if (in_directive && !(body[i] instanceof AST_Statement && body[i].body instanceof AST_String)) {
in_directive = false;
}
}
return body;
};
var MOZ_TO_ME = {
Program: function(M) {
return new AST_Toplevel({
start: my_start_token(M),
end: my_end_token(M),
body: normalize_directives(M.body.map(from_moz))
});
},
FunctionDeclaration: function(M) {
return new AST_Defun({
start: my_start_token(M),
end: my_end_token(M),
name: from_moz(M.id),
argnames: M.params.map(from_moz),
body: normalize_directives(from_moz(M.body).body)
});
},
FunctionExpression: function(M) {
return new AST_Function({
start: my_start_token(M),
end: my_end_token(M),
name: from_moz(M.id),
argnames: M.params.map(from_moz),
body: normalize_directives(from_moz(M.body).body)
});
},
ExpressionStatement: function(M) {
return new AST_SimpleStatement({
start: my_start_token(M),
end: my_end_token(M),
body: from_moz(M.expression)
});
},
TryStatement: function(M) {
var handlers = M.handlers || [M.handler];
if (handlers.length > 1 || M.guardedHandlers && M.guardedHandlers.length) {
throw new Error("Multiple catch clauses are not supported.");
}
return new AST_Try({
start : my_start_token(M),
end : my_end_token(M),
body : from_moz(M.block).body,
bcatch : from_moz(handlers[0]),
bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
});
},
Property: function(M) {
var key = M.key;
var args = {
start : my_start_token(key),
end : my_end_token(M.value),
key : key.type == "Identifier" ? key.name : key.value,
value : from_moz(M.value)
};
if (M.kind == "init") return new AST_ObjectKeyVal(args);
args.key = new AST_SymbolAccessor({
name: args.key
});
args.value = new AST_Accessor(args.value);
if (M.kind == "get") return new AST_ObjectGetter(args);
if (M.kind == "set") return new AST_ObjectSetter(args);
},
ArrayExpression: function(M) {
return new AST_Array({
start : my_start_token(M),
end : my_end_token(M),
elements : M.elements.map(function(elem){
return elem === null ? new AST_Hole() : from_moz(elem);
})
});
},
ObjectExpression: function(M) {
return new AST_Object({
start : my_start_token(M),
end : my_end_token(M),
properties : M.properties.map(function(prop){
prop.type = "Property";
return from_moz(prop)
})
});
},
SequenceExpression: function(M) {
return AST_Seq.from_array(M.expressions.map(from_moz));
},
MemberExpression: function(M) {
return new (M.computed ? AST_Sub : AST_Dot)({
start : my_start_token(M),
end : my_end_token(M),
property : M.computed ? from_moz(M.property) : M.property.name,
expression : from_moz(M.object)
});
},
SwitchCase: function(M) {
return new (M.test ? AST_Case : AST_Default)({
start : my_start_token(M),
end : my_end_token(M),
expression : from_moz(M.test),
body : M.consequent.map(from_moz)
});
},
VariableDeclaration: function(M) {
return new (M.kind === "const" ? AST_Const : AST_Var)({
start : my_start_token(M),
end : my_end_token(M),
definitions : M.declarations.map(from_moz)
});
},
Literal: function(M) {
var val = M.value, args = {
start : my_start_token(M),
end : my_end_token(M)
};
if (val === null) return new AST_Null(args);
switch (typeof val) {
case "string":
args.value = val;
return new AST_String(args);
case "number":
args.value = val;
return new AST_Number(args);
case "boolean":
return new (val ? AST_True : AST_False)(args);
default:
var rx = M.regex;
if (rx && rx.pattern) {
// RegExpLiteral as per ESTree AST spec
args.value = new RegExp(rx.pattern, rx.flags).toString();
} else {
// support legacy RegExp
args.value = M.regex && M.raw ? M.raw : val;
}
return new AST_RegExp(args);
}
},
Identifier: function(M) {
var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
return new ( p.type == "LabeledStatement" ? AST_Label
: p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar)
: p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
: p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
: p.type == "CatchClause" ? AST_SymbolCatch
: p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef
: AST_SymbolRef)({
start : my_start_token(M),
end : my_end_token(M),
name : M.name
});
}
};
MOZ_TO_ME.UpdateExpression =
MOZ_TO_ME.UnaryExpression = function To_Moz_Unary(M) {
var prefix = "prefix" in M ? M.prefix
: M.type == "UnaryExpression" ? true : false;
return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
start : my_start_token(M),
end : my_end_token(M),
operator : M.operator,
expression : from_moz(M.argument)
});
};
map("EmptyStatement", AST_EmptyStatement);
map("BlockStatement", AST_BlockStatement, "body@body");
map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
map("LabeledStatement", AST_LabeledStatement, "label>label, body>body");
map("BreakStatement", AST_Break, "label>label");
map("ContinueStatement", AST_Continue, "label>label");
map("WithStatement", AST_With, "object>expression, body>body");
map("SwitchStatement", AST_Switch, "discriminant>expression, cases@body");
map("ReturnStatement", AST_Return, "argument>value");
map("ThrowStatement", AST_Throw, "argument>value");
map("WhileStatement", AST_While, "test>condition, body>body");
map("DoWhileStatement", AST_Do, "test>condition, body>body");
map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body");
map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
map("DebuggerStatement", AST_Debugger);
map("VariableDeclarator", AST_VarDef, "id>name, init>value");
map("CatchClause", AST_Catch, "param>argname, body%body");
map("ThisExpression", AST_This);
map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative");
map("NewExpression", AST_New, "callee>expression, arguments@args");
map("CallExpression", AST_Call, "callee>expression, arguments@args");
def_to_moz(AST_Toplevel, function To_Moz_Program(M) {
return to_moz_scope("Program", M);
});
def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) {
return {
type: "FunctionDeclaration",
id: to_moz(M.name),
params: M.argnames.map(to_moz),
body: to_moz_scope("BlockStatement", M)
}
});
def_to_moz(AST_Function, function To_Moz_FunctionExpression(M) {
return {
type: "FunctionExpression",
id: to_moz(M.name),
params: M.argnames.map(to_moz),
body: to_moz_scope("BlockStatement", M)
}
});
def_to_moz(AST_Directive, function To_Moz_Directive(M) {
return {
type: "ExpressionStatement",
expression: {
type: "Literal",
value: M.value
}
};
});
def_to_moz(AST_SimpleStatement, function To_Moz_ExpressionStatement(M) {
return {
type: "ExpressionStatement",
expression: to_moz(M.body)
};
});
def_to_moz(AST_SwitchBranch, function To_Moz_SwitchCase(M) {
return {
type: "SwitchCase",
test: to_moz(M.expression),
consequent: M.body.map(to_moz)
};
});
def_to_moz(AST_Try, function To_Moz_TryStatement(M) {
return {
type: "TryStatement",
block: to_moz_block(M),
handler: to_moz(M.bcatch),
guardedHandlers: [],
finalizer: to_moz(M.bfinally)
};
});
def_to_moz(AST_Catch, function To_Moz_CatchClause(M) {
return {
type: "CatchClause",
param: to_moz(M.argname),
guard: null,
body: to_moz_block(M)
};
});
def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
return {
type: "VariableDeclaration",
kind: M instanceof AST_Const ? "const" : "var",
declarations: M.definitions.map(to_moz)
};
});
def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) {
return {
type: "SequenceExpression",
expressions: M.to_array().map(to_moz)
};
});
def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) {
var isComputed = M instanceof AST_Sub;
return {
type: "MemberExpression",
object: to_moz(M.expression),
computed: isComputed,
property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property}
};
});
def_to_moz(AST_Unary, function To_Moz_Unary(M) {
return {
type: M.operator == "++" || M.operator == "--" ? "UpdateExpression" : "UnaryExpression",
operator: M.operator,
prefix: M instanceof AST_UnaryPrefix,
argument: to_moz(M.expression)
};
});
def_to_moz(AST_Binary, function To_Moz_BinaryExpression(M) {
return {
type: M.operator == "&&" || M.operator == "||" ? "LogicalExpression" : "BinaryExpression",
left: to_moz(M.left),
operator: M.operator,
right: to_moz(M.right)
};
});
def_to_moz(AST_Array, function To_Moz_ArrayExpression(M) {
return {
type: "ArrayExpression",
elements: M.elements.map(to_moz)
};
});
def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) {
return {
type: "ObjectExpression",
properties: M.properties.map(to_moz)
};
});
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
var key = {
type: "Literal",
value: M.key instanceof AST_SymbolAccessor ? M.key.name : M.key
};
var kind;
if (M instanceof AST_ObjectKeyVal) {
kind = "init";
} else
if (M instanceof AST_ObjectGetter) {
kind = "get";
} else
if (M instanceof AST_ObjectSetter) {
kind = "set";
}
return {
type: "Property",
kind: kind,
key: key,
value: to_moz(M.value)
};
});
def_to_moz(AST_Symbol, function To_Moz_Identifier(M) {
var def = M.definition();
return {
type: "Identifier",
name: def ? def.mangled_name || def.name : M.name
};
});
def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) {
var value = M.value;
return {
type: "Literal",
value: value,
raw: value.toString(),
regex: {
pattern: value.source,
flags: value.toString().match(/[gimuy]*$/)[0]
}
};
});
def_to_moz(AST_Constant, function To_Moz_Literal(M) {
var value = M.value;
if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) {
return {
type: "UnaryExpression",
operator: "-",
prefix: true,
argument: {
type: "Literal",
value: -value,
raw: M.start.raw
}
};
}
return {
type: "Literal",
value: value,
raw: M.start.raw
};
});
def_to_moz(AST_Atom, function To_Moz_Atom(M) {
return {
type: "Identifier",
name: String(M.value)
};
});
AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
AST_Null.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
AST_Hole.DEFMETHOD("to_mozilla_ast", function To_Moz_ArrayHole() { return null });
AST_Block.DEFMETHOD("to_mozilla_ast", AST_BlockStatement.prototype.to_mozilla_ast);
AST_Lambda.DEFMETHOD("to_mozilla_ast", AST_Function.prototype.to_mozilla_ast);
/* -----[ tools ]----- */
function raw_token(moznode) {
if (moznode.type == "Literal") {
return moznode.raw != null ? moznode.raw : moznode.value + "";
}
}
function my_start_token(moznode) {
var loc = moznode.loc, start = loc && loc.start;
var range = moznode.range;
return new AST_Token({
file : loc && loc.source,
line : start && start.line,
col : start && start.column,
pos : range ? range[0] : moznode.start,
endline : start && start.line,
endcol : start && start.column,
endpos : range ? range[0] : moznode.start,
raw : raw_token(moznode),
});
};
function my_end_token(moznode) {
var loc = moznode.loc, end = loc && loc.end;
var range = moznode.range;
return new AST_Token({
file : loc && loc.source,
line : end && end.line,
col : end && end.column,
pos : range ? range[1] : moznode.end,
endline : end && end.line,
endcol : end && end.column,
endpos : range ? range[1] : moznode.end,
raw : raw_token(moznode),
});
};
function map(moztype, mytype, propmap) {
var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
moz_to_me += "return new U2." + mytype.name + "({\n" +
"start: my_start_token(M),\n" +
"end: my_end_token(M)";
var me_to_moz = "function To_Moz_" + moztype + "(M){\n";
me_to_moz += "return {\n" +
"type: " + JSON.stringify(moztype);
if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){
var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop);
if (!m) throw new Error("Can't understand property map: " + prop);
var moz = m[1], how = m[2], my = m[3];
moz_to_me += ",\n" + my + ": ";
me_to_moz += ",\n" + moz + ": ";
switch (how) {
case "@":
moz_to_me += "M." + moz + ".map(from_moz)";
me_to_moz += "M." + my + ".map(to_moz)";
break;
case ">":
moz_to_me += "from_moz(M." + moz + ")";
me_to_moz += "to_moz(M." + my + ")";
break;
case "=":
moz_to_me += "M." + moz;
me_to_moz += "M." + my;
break;
case "%":
moz_to_me += "from_moz(M." + moz + ").body";
me_to_moz += "to_moz_block(M)";
break;
default:
throw new Error("Can't understand operator in propmap: " + prop);
}
});
moz_to_me += "\n})\n}";
me_to_moz += "\n}\n}";
//moz_to_me = parse(moz_to_me).print_to_string({ beautify: true });
//me_to_moz = parse(me_to_moz).print_to_string({ beautify: true });
//console.log(moz_to_me);
moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
exports, my_start_token, my_end_token, from_moz
);
me_to_moz = new Function("to_moz", "to_moz_block", "to_moz_scope", "return(" + me_to_moz + ")")(
to_moz, to_moz_block, to_moz_scope
);
MOZ_TO_ME[moztype] = moz_to_me;
def_to_moz(mytype, me_to_moz);
};
var FROM_MOZ_STACK = null;
function from_moz(node) {
FROM_MOZ_STACK.push(node);
var ret = node != null ? MOZ_TO_ME[node.type](node) : null;
FROM_MOZ_STACK.pop();
return ret;
};
AST_Node.from_mozilla_ast = function(node){
var save_stack = FROM_MOZ_STACK;
FROM_MOZ_STACK = [];
var ast = from_moz(node);
FROM_MOZ_STACK = save_stack;
return ast;
};
function set_moz_loc(mynode, moznode, myparent) {
var start = mynode.start;
var end = mynode.end;
if (start.pos != null && end.endpos != null) {
moznode.range = [start.pos, end.endpos];
}
if (start.line) {
moznode.loc = {
start: {line: start.line, column: start.col},
end: end.endline ? {line: end.endline, column: end.endcol} : null
};
if (start.file) {
moznode.loc.source = start.file;
}
}
return moznode;
};
function def_to_moz(mytype, handler) {
mytype.DEFMETHOD("to_mozilla_ast", function() {
return set_moz_loc(this, handler(this));
});
};
function to_moz(node) {
return node != null ? node.to_mozilla_ast() : null;
};
function to_moz_block(node) {
return {
type: "BlockStatement",
body: node.body.map(to_moz)
};
};
function to_moz_scope(type, node) {
var body = node.body.map(to_moz);
if (node.body[0] instanceof AST_SimpleStatement && node.body[0].body instanceof AST_String) {
body.unshift(to_moz(new AST_EmptyStatement(node.body[0])));
}
return {
type: type,
body: body
};
};
})();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,264 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
-------------------------------- (C) ---------------------------------
Author: Mihai Bazon
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
Distributed under the BSD license:
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
***********************************************************************/
"use strict";
function find_builtins() {
// NaN will be included due to Number.NaN
var a = [
"null",
"true",
"false",
"Infinity",
"-Infinity",
"undefined",
];
[ Object, Array, Function, Number,
String, Boolean, Error, Math,
Date, RegExp
].forEach(function(ctor){
Object.getOwnPropertyNames(ctor).map(add);
if (ctor.prototype) {
Object.getOwnPropertyNames(ctor.prototype).map(add);
}
});
function add(name) {
push_uniq(a, name);
}
return a;
}
function mangle_properties(ast, options) {
options = defaults(options, {
cache: null,
debug: false,
ignore_quoted: false,
only_cache: false,
regex: null,
reserved: null,
});
var reserved = options.reserved;
if (reserved == null)
reserved = find_builtins();
var cache = options.cache;
if (cache == null) {
cache = {
cname: -1,
props: new Dictionary()
};
}
var regex = options.regex;
var ignore_quoted = options.ignore_quoted;
// note debug is either false (disabled), or a string of the debug suffix to use (enabled).
// note debug may be enabled as an empty string, which is falsey. Also treat passing 'true'
// the same as passing an empty string.
var debug = (options.debug !== false);
var debug_name_suffix;
if (debug) {
debug_name_suffix = (options.debug === true ? "" : options.debug);
}
var names_to_mangle = [];
var unmangleable = [];
var ignored = {};
// step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){
if (node instanceof AST_ObjectKeyVal) {
add(node.key, ignore_quoted && node.quote);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above
add(node.key.name);
}
else if (node instanceof AST_Dot) {
add(node.property);
}
else if (node instanceof AST_Sub) {
addStrings(node.property, ignore_quoted);
}
}));
// step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node){
if (node instanceof AST_ObjectKeyVal) {
if (!(ignore_quoted && node.quote))
node.key = mangle(node.key);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter
node.key.name = mangle(node.key.name);
}
else if (node instanceof AST_Dot) {
node.property = mangle(node.property);
}
else if (node instanceof AST_Sub) {
if (!ignore_quoted)
node.property = mangleStrings(node.property);
}
// else if (node instanceof AST_String) {
// if (should_mangle(node.value)) {
// AST_Node.warn(
// "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", {
// file : node.start.file,
// line : node.start.line,
// col : node.start.col,
// prop : node.value
// }
// );
// }
// }
}));
// only function declarations after this line
function can_mangle(name) {
if (unmangleable.indexOf(name) >= 0) return false;
if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) {
return cache.props.has(name);
}
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
return true;
}
function should_mangle(name) {
if (ignore_quoted && name in ignored) return false;
if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false;
return cache.props.has(name)
|| names_to_mangle.indexOf(name) >= 0;
}
function add(name, ignore) {
if (ignore) {
ignored[name] = true;
return;
}
if (can_mangle(name))
push_uniq(names_to_mangle, name);
if (!should_mangle(name)) {
push_uniq(unmangleable, name);
}
}
function mangle(name) {
if (!should_mangle(name)) {
return name;
}
var mangled = cache.props.get(name);
if (!mangled) {
if (debug) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
if (can_mangle(debug_mangled) && !(ignore_quoted && debug_mangled in ignored)) {
mangled = debug_mangled;
}
}
// either debug mode is off, or it is on and we could not use the mangled name
if (!mangled) {
// note can_mangle() does not check if the name collides with the 'ignored' set
// (filled with quoted properties when ignore_quoted set). Make sure we add this
// check so we don't collide with a quoted name.
do {
mangled = base54(++cache.cname);
} while (!can_mangle(mangled) || (ignore_quoted && mangled in ignored));
}
cache.props.set(name, mangled);
}
return mangled;
}
function addStrings(node, ignore) {
var out = {};
try {
(function walk(node){
node.walk(new TreeWalker(function(node){
if (node instanceof AST_Seq) {
walk(node.cdr);
return true;
}
if (node instanceof AST_String) {
add(node.value, ignore);
return true;
}
if (node instanceof AST_Conditional) {
walk(node.consequent);
walk(node.alternative);
return true;
}
throw out;
}));
})(node);
} catch(ex) {
if (ex !== out) throw ex;
}
}
function mangleStrings(node) {
return node.transform(new TreeTransformer(function(node){
if (node instanceof AST_Seq) {
node.cdr = mangleStrings(node.cdr);
}
else if (node instanceof AST_String) {
node.value = mangle(node.value);
}
else if (node instanceof AST_Conditional) {
node.consequent = mangleStrings(node.consequent);
node.alternative = mangleStrings(node.alternative);
}
return node;
}));
}
}

View File

@@ -0,0 +1,656 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
-------------------------------- (C) ---------------------------------
Author: Mihai Bazon
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
Distributed under the BSD license:
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
***********************************************************************/
"use strict";
function SymbolDef(scope, index, orig) {
this.name = orig.name;
this.orig = [ orig ];
this.scope = scope;
this.references = [];
this.global = false;
this.mangled_name = null;
this.undeclared = false;
this.index = index;
this.id = SymbolDef.next_id++;
};
SymbolDef.next_id = 1;
SymbolDef.prototype = {
unmangleable: function(options) {
if (!options) options = {};
return (this.global && !options.toplevel)
|| this.undeclared
|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with))
|| (options.keep_fnames
&& (this.orig[0] instanceof AST_SymbolLambda
|| this.orig[0] instanceof AST_SymbolDefun));
},
mangle: function(options) {
var cache = options.cache && options.cache.props;
if (this.global && cache && cache.has(this.name)) {
this.mangled_name = cache.get(this.name);
}
else if (!this.mangled_name && !this.unmangleable(options)) {
var s = this.scope;
var sym = this.orig[0];
if (!options.screw_ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope;
var def;
if (this.defun && (def = this.defun.variables.get(this.name))) {
this.mangled_name = def.mangled_name || def.name;
} else
this.mangled_name = s.next_mangled(options, this);
if (this.global && cache) {
cache.set(this.name, this.mangled_name);
}
}
}
};
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
options = defaults(options, {
cache: null,
screw_ie8: true,
});
// pass 1: setup scope chaining and handle definitions
var self = this;
var scope = self.parent_scope = null;
var labels = new Dictionary();
var defun = null;
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Catch) {
var save_scope = scope;
scope = new AST_Scope(node);
scope.init_scope_vars(save_scope);
descend();
scope = save_scope;
return true;
}
if (node instanceof AST_Scope) {
node.init_scope_vars(scope);
var save_scope = scope;
var save_defun = defun;
var save_labels = labels;
defun = scope = node;
labels = new Dictionary();
descend();
scope = save_scope;
defun = save_defun;
labels = save_labels;
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_LabeledStatement) {
var l = node.label;
if (labels.has(l.name)) {
throw new Error(string_template("Label {name} defined twice", l));
}
labels.set(l.name, l);
descend();
labels.del(l.name);
return true; // no descend again
}
if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope)
s.uses_with = true;
return;
}
if (node instanceof AST_Symbol) {
node.scope = scope;
}
if (node instanceof AST_Label) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolLambda) {
defun.def_function(node);
}
else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is
// the parent scope. The reason is that we enter a new
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
(node.scope = defun.parent_scope).def_function(node);
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolConst) {
defun.def_variable(node);
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
});
self.walk(tw);
// pass 2: find back references and eval
var func = null;
var globals = self.globals = new Dictionary();
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Lambda) {
var prev_func = func;
func = node;
descend();
func = prev_func;
return true;
}
if (node instanceof AST_LoopControl && node.label) {
node.label.thedef.references.push(node);
return true;
}
if (node instanceof AST_SymbolRef) {
var name = node.name;
if (name == "eval" && tw.parent() instanceof AST_Call) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
s.uses_eval = true;
}
}
var sym = node.scope.find_variable(name);
if (node.scope instanceof AST_Lambda && name == "arguments") {
node.scope.uses_arguments = true;
}
if (!sym) {
sym = self.def_global(node);
}
node.thedef = sym;
node.reference(options);
return true;
}
});
self.walk(tw);
// pass 3: fix up any scoping issue with IE8
if (!options.screw_ie8) {
self.walk(new TreeWalker(function(node, descend) {
if (node instanceof AST_SymbolCatch) {
var name = node.name;
var refs = node.thedef.references;
var scope = node.thedef.defun;
var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node);
refs.forEach(function(ref) {
ref.thedef = def;
ref.reference(options);
});
node.thedef = def;
return true;
}
}));
}
if (options.cache) {
this.cname = options.cache.cname;
}
});
AST_Toplevel.DEFMETHOD("def_global", function(node){
var globals = this.globals, name = node.name;
if (globals.has(name)) {
return globals.get(name);
} else {
var g = new SymbolDef(this, globals.size(), node);
g.undeclared = true;
g.global = true;
globals.set(name, g);
return g;
}
});
AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
this.parent_scope = parent_scope; // the parent scope
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
this.cname = -1; // the current index for mangling functions/variables
});
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false;
this.def_variable(new AST_SymbolVar({
name: "arguments",
start: this.start,
end: this.end
}));
});
AST_Symbol.DEFMETHOD("mark_enclosed", function(options) {
var def = this.definition();
var s = this.scope;
while (s) {
push_uniq(s.enclosed, def);
if (options.keep_fnames) {
s.functions.each(function(d) {
push_uniq(def.scope.enclosed, d);
});
}
if (s === def.scope) break;
s = s.parent_scope;
}
});
AST_Symbol.DEFMETHOD("reference", function(options) {
this.definition().references.push(this);
this.mark_enclosed(options);
});
AST_Scope.DEFMETHOD("find_variable", function(name){
if (name instanceof AST_Symbol) name = name.name;
return this.variables.get(name)
|| (this.parent_scope && this.parent_scope.find_variable(name));
});
AST_Scope.DEFMETHOD("def_function", function(symbol){
this.functions.set(symbol.name, this.def_variable(symbol));
});
AST_Scope.DEFMETHOD("def_variable", function(symbol){
var def;
if (!this.variables.has(symbol.name)) {
def = new SymbolDef(this, this.variables.size(), symbol);
this.variables.set(symbol.name, def);
def.global = !this.parent_scope;
} else {
def = this.variables.get(symbol.name);
def.orig.push(symbol);
}
return symbol.thedef = def;
});
AST_Scope.DEFMETHOD("next_mangled", function(options){
var ext = this.enclosed;
out: while (true) {
var m = base54(++this.cname);
if (!is_identifier(m)) continue; // skip over "do"
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
// shadow a name excepted from mangling.
if (options.except.indexOf(m) >= 0) continue;
// we must ensure that the mangled name does not shadow a name
// from some parent scope that is referenced in this or in
// inner scopes.
for (var i = ext.length; --i >= 0;) {
var sym = ext[i];
var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
if (m == name) continue out;
}
return m;
}
});
AST_Function.DEFMETHOD("next_mangled", function(options, def){
// #179, #326
// in Safari strict mode, something like (function x(x){...}) is a syntax error;
// a function expression's argument cannot shadow the function expression's name
var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition();
// the function's mangled_name is null when keep_fnames is true
var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null;
while (true) {
var name = AST_Lambda.prototype.next_mangled.call(this, options, def);
if (!tricky_name || tricky_name != name)
return name;
}
});
AST_Symbol.DEFMETHOD("unmangleable", function(options){
return this.definition().unmangleable(options);
});
// labels are always mangleable
AST_Label.DEFMETHOD("unmangleable", function(){
return false;
});
AST_Symbol.DEFMETHOD("unreferenced", function(){
return this.definition().references.length == 0
&& !(this.scope.uses_eval || this.scope.uses_with);
});
AST_Symbol.DEFMETHOD("undeclared", function(){
return this.definition().undeclared;
});
AST_LabelRef.DEFMETHOD("undeclared", function(){
return false;
});
AST_Label.DEFMETHOD("undeclared", function(){
return false;
});
AST_Symbol.DEFMETHOD("definition", function(){
return this.thedef;
});
AST_Symbol.DEFMETHOD("global", function(){
return this.definition().global;
});
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, {
eval : false,
except : [],
keep_fnames : false,
screw_ie8 : true,
sort : false, // Ignored. Flag retained for backwards compatibility.
toplevel : false,
});
});
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = this._default_mangler_options(options);
// Never mangle arguments
options.except.push('arguments');
// We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's
// present (and for AST_SymbolRef-s it'll use the mangled name of
// the AST_SymbolDeclaration that it points to).
var lname = -1;
var to_mangle = [];
if (options.cache) {
this.globals.each(function(symbol){
if (options.except.indexOf(symbol.name) < 0) {
to_mangle.push(symbol);
}
});
}
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_LabeledStatement) {
// lname is incremented when we get to the AST_Label
var save_nesting = lname;
descend();
lname = save_nesting;
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_Scope) {
var p = tw.parent(), a = [];
node.variables.each(function(symbol){
if (options.except.indexOf(symbol.name) < 0) {
a.push(symbol);
}
});
to_mangle.push.apply(to_mangle, a);
return;
}
if (node instanceof AST_Label) {
var name;
do name = base54(++lname); while (!is_identifier(name));
node.mangled_name = name;
return true;
}
if (options.screw_ie8 && node instanceof AST_SymbolCatch) {
to_mangle.push(node.definition());
return;
}
});
this.walk(tw);
to_mangle.forEach(function(def){ def.mangle(options) });
if (options.cache) {
options.cache.cname = this.cname;
}
});
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
options = this._default_mangler_options(options);
var tw = new TreeWalker(function(node){
if (node instanceof AST_Constant)
base54.consider(node.print_to_string());
else if (node instanceof AST_Return)
base54.consider("return");
else if (node instanceof AST_Throw)
base54.consider("throw");
else if (node instanceof AST_Continue)
base54.consider("continue");
else if (node instanceof AST_Break)
base54.consider("break");
else if (node instanceof AST_Debugger)
base54.consider("debugger");
else if (node instanceof AST_Directive)
base54.consider(node.value);
else if (node instanceof AST_While)
base54.consider("while");
else if (node instanceof AST_Do)
base54.consider("do while");
else if (node instanceof AST_If) {
base54.consider("if");
if (node.alternative) base54.consider("else");
}
else if (node instanceof AST_Var)
base54.consider("var");
else if (node instanceof AST_Const)
base54.consider("const");
else if (node instanceof AST_Lambda)
base54.consider("function");
else if (node instanceof AST_For)
base54.consider("for");
else if (node instanceof AST_ForIn)
base54.consider("for in");
else if (node instanceof AST_Switch)
base54.consider("switch");
else if (node instanceof AST_Case)
base54.consider("case");
else if (node instanceof AST_Default)
base54.consider("default");
else if (node instanceof AST_With)
base54.consider("with");
else if (node instanceof AST_ObjectSetter)
base54.consider("set" + node.key);
else if (node instanceof AST_ObjectGetter)
base54.consider("get" + node.key);
else if (node instanceof AST_ObjectKeyVal)
base54.consider(node.key);
else if (node instanceof AST_New)
base54.consider("new");
else if (node instanceof AST_This)
base54.consider("this");
else if (node instanceof AST_Try)
base54.consider("try");
else if (node instanceof AST_Catch)
base54.consider("catch");
else if (node instanceof AST_Finally)
base54.consider("finally");
else if (node instanceof AST_Symbol && node.unmangleable(options))
base54.consider(node.name);
else if (node instanceof AST_Unary || node instanceof AST_Binary)
base54.consider(node.operator);
else if (node instanceof AST_Dot)
base54.consider(node.property);
});
this.walk(tw);
base54.sort();
});
var base54 = (function() {
var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
var chars, frequency;
function reset() {
frequency = Object.create(null);
chars = string.split("").map(function(ch){ return ch.charCodeAt(0) });
chars.forEach(function(ch){ frequency[ch] = 0 });
}
base54.consider = function(str){
for (var i = str.length; --i >= 0;) {
var code = str.charCodeAt(i);
if (code in frequency) ++frequency[code];
}
};
base54.sort = function() {
chars = mergeSort(chars, function(a, b){
if (is_digit(a) && !is_digit(b)) return 1;
if (is_digit(b) && !is_digit(a)) return -1;
return frequency[b] - frequency[a];
});
};
base54.reset = reset;
reset();
base54.get = function(){ return chars };
base54.freq = function(){ return frequency };
function base54(num) {
var ret = "", base = 54;
num++;
do {
num--;
ret += String.fromCharCode(chars[num % base]);
num = Math.floor(num / base);
base = 64;
} while (num > 0);
return ret;
};
return base54;
})();
AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
options = defaults(options, {
assign_to_global : true,
eval : true,
func_arguments : true,
nested_defuns : true,
undeclared : false, // this makes a lot of noise
unreferenced : true,
});
var tw = new TreeWalker(function(node){
if (options.undeclared
&& node instanceof AST_SymbolRef
&& node.undeclared())
{
// XXX: this also warns about JS standard names,
// i.e. Object, Array, parseInt etc. Should add a list of
// exceptions.
AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", {
name: node.name,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.assign_to_global)
{
var sym = null;
if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef)
sym = node.left;
else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef)
sym = node.init;
if (sym
&& (sym.undeclared()
|| (sym.global() && sym.scope !== sym.definition().scope))) {
AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", {
msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",
name: sym.name,
file: sym.start.file,
line: sym.start.line,
col: sym.start.col
});
}
}
if (options.eval
&& node instanceof AST_SymbolRef
&& node.undeclared()
&& node.name == "eval") {
AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start);
}
if (options.unreferenced
&& (node instanceof AST_SymbolDeclaration || node instanceof AST_Label)
&& !(node instanceof AST_SymbolCatch)
&& node.unreferenced()) {
AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {
type: node instanceof AST_Label ? "Label" : "Symbol",
name: node.name,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.func_arguments
&& node instanceof AST_Lambda
&& node.uses_arguments) {
AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", {
name: node.name ? node.name.name : "anonymous",
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.nested_defuns
&& node instanceof AST_Defun
&& !(tw.parent() instanceof AST_Scope)) {
AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", {
name: node.name.name,
type: tw.parent().TYPE,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
});
this.walk(tw);
});

View File

@@ -0,0 +1,97 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
-------------------------------- (C) ---------------------------------
Author: Mihai Bazon
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
Distributed under the BSD license:
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
***********************************************************************/
"use strict";
// a small wrapper around fitzgen's source-map library
function SourceMap(options) {
options = defaults(options, {
file : null,
root : null,
orig : null,
orig_line_diff : 0,
dest_line_diff : 0,
});
var generator = new MOZ_SourceMap.SourceMapGenerator({
file : options.file,
sourceRoot : options.root
});
var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig);
if (orig_map && Array.isArray(options.orig.sources)) {
orig_map._sources.toArray().forEach(function(source) {
var sourceContent = orig_map.sourceContentFor(source, true);
if (sourceContent) {
generator.setSourceContent(source, sourceContent);
}
});
}
function add(source, gen_line, gen_col, orig_line, orig_col, name) {
if (orig_map) {
var info = orig_map.originalPositionFor({
line: orig_line,
column: orig_col
});
if (info.source === null) {
return;
}
source = info.source;
orig_line = info.line;
orig_col = info.column;
name = info.name || name;
}
generator.addMapping({
generated : { line: gen_line + options.dest_line_diff, column: gen_col },
original : { line: orig_line + options.orig_line_diff, column: orig_col },
source : source,
name : name
});
};
return {
add : add,
get : function() { return generator },
toString : function() { return JSON.stringify(generator.toJSON()); }
};
};

View File

@@ -0,0 +1,218 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
-------------------------------- (C) ---------------------------------
Author: Mihai Bazon
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
Distributed under the BSD license:
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
***********************************************************************/
"use strict";
// Tree transformer helpers.
function TreeTransformer(before, after) {
TreeWalker.call(this);
this.before = before;
this.after = after;
}
TreeTransformer.prototype = new TreeWalker;
(function(undefined){
function _(node, descend) {
node.DEFMETHOD("transform", function(tw, in_list){
var x, y;
tw.push(this);
if (tw.before) x = tw.before(this, descend, in_list);
if (x === undefined) {
if (!tw.after) {
x = this;
descend(x, tw);
} else {
tw.stack[tw.stack.length - 1] = x = this;
descend(x, tw);
y = tw.after(x, in_list);
if (y !== undefined) x = y;
}
}
tw.pop(this);
return x;
});
};
function do_list(list, tw) {
return MAP(list, function(node){
return node.transform(tw, true);
});
};
_(AST_Node, noop);
_(AST_LabeledStatement, function(self, tw){
self.label = self.label.transform(tw);
self.body = self.body.transform(tw);
});
_(AST_SimpleStatement, function(self, tw){
self.body = self.body.transform(tw);
});
_(AST_Block, function(self, tw){
self.body = do_list(self.body, tw);
});
_(AST_DWLoop, function(self, tw){
self.condition = self.condition.transform(tw);
self.body = self.body.transform(tw);
});
_(AST_For, function(self, tw){
if (self.init) self.init = self.init.transform(tw);
if (self.condition) self.condition = self.condition.transform(tw);
if (self.step) self.step = self.step.transform(tw);
self.body = self.body.transform(tw);
});
_(AST_ForIn, function(self, tw){
self.init = self.init.transform(tw);
self.object = self.object.transform(tw);
self.body = self.body.transform(tw);
});
_(AST_With, function(self, tw){
self.expression = self.expression.transform(tw);
self.body = self.body.transform(tw);
});
_(AST_Exit, function(self, tw){
if (self.value) self.value = self.value.transform(tw);
});
_(AST_LoopControl, function(self, tw){
if (self.label) self.label = self.label.transform(tw);
});
_(AST_If, function(self, tw){
self.condition = self.condition.transform(tw);
self.body = self.body.transform(tw);
if (self.alternative) self.alternative = self.alternative.transform(tw);
});
_(AST_Switch, function(self, tw){
self.expression = self.expression.transform(tw);
self.body = do_list(self.body, tw);
});
_(AST_Case, function(self, tw){
self.expression = self.expression.transform(tw);
self.body = do_list(self.body, tw);
});
_(AST_Try, function(self, tw){
self.body = do_list(self.body, tw);
if (self.bcatch) self.bcatch = self.bcatch.transform(tw);
if (self.bfinally) self.bfinally = self.bfinally.transform(tw);
});
_(AST_Catch, function(self, tw){
self.argname = self.argname.transform(tw);
self.body = do_list(self.body, tw);
});
_(AST_Definitions, function(self, tw){
self.definitions = do_list(self.definitions, tw);
});
_(AST_VarDef, function(self, tw){
self.name = self.name.transform(tw);
if (self.value) self.value = self.value.transform(tw);
});
_(AST_Lambda, function(self, tw){
if (self.name) self.name = self.name.transform(tw);
self.argnames = do_list(self.argnames, tw);
self.body = do_list(self.body, tw);
});
_(AST_Call, function(self, tw){
self.expression = self.expression.transform(tw);
self.args = do_list(self.args, tw);
});
_(AST_Seq, function(self, tw){
self.car = self.car.transform(tw);
self.cdr = self.cdr.transform(tw);
});
_(AST_Dot, function(self, tw){
self.expression = self.expression.transform(tw);
});
_(AST_Sub, function(self, tw){
self.expression = self.expression.transform(tw);
self.property = self.property.transform(tw);
});
_(AST_Unary, function(self, tw){
self.expression = self.expression.transform(tw);
});
_(AST_Binary, function(self, tw){
self.left = self.left.transform(tw);
self.right = self.right.transform(tw);
});
_(AST_Conditional, function(self, tw){
self.condition = self.condition.transform(tw);
self.consequent = self.consequent.transform(tw);
self.alternative = self.alternative.transform(tw);
});
_(AST_Array, function(self, tw){
self.elements = do_list(self.elements, tw);
});
_(AST_Object, function(self, tw){
self.properties = do_list(self.properties, tw);
});
_(AST_ObjectProperty, function(self, tw){
self.value = self.value.transform(tw);
});
})();

View File

@@ -0,0 +1,362 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
-------------------------------- (C) ---------------------------------
Author: Mihai Bazon
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
Distributed under the BSD license:
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
***********************************************************************/
"use strict";
function array_to_hash(a) {
var ret = Object.create(null);
for (var i = 0; i < a.length; ++i)
ret[a[i]] = true;
return ret;
};
function slice(a, start) {
return Array.prototype.slice.call(a, start || 0);
};
function characters(str) {
return str.split("");
};
function member(name, array) {
return array.indexOf(name) >= 0;
};
function find_if(func, array) {
for (var i = 0, n = array.length; i < n; ++i) {
if (func(array[i]))
return array[i];
}
};
function repeat_string(str, i) {
if (i <= 0) return "";
if (i == 1) return str;
var d = repeat_string(str, i >> 1);
d += d;
if (i & 1) d += str;
return d;
};
function configure_error_stack(fn) {
Object.defineProperty(fn.prototype, "stack", {
get: function() {
var err = new Error(this.message);
err.name = this.name;
try {
throw err;
} catch(e) {
return e.stack;
}
}
});
}
function DefaultsError(msg, defs) {
this.message = msg;
this.defs = defs;
};
DefaultsError.prototype = Object.create(Error.prototype);
DefaultsError.prototype.constructor = DefaultsError;
DefaultsError.prototype.name = "DefaultsError";
configure_error_stack(DefaultsError);
DefaultsError.croak = function(msg, defs) {
throw new DefaultsError(msg, defs);
};
function defaults(args, defs, croak) {
if (args === true)
args = {};
var ret = args || {};
if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i))
DefaultsError.croak("`" + i + "` is not a supported option", defs);
for (var i in defs) if (HOP(defs, i)) {
ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
}
return ret;
};
function merge(obj, ext) {
var count = 0;
for (var i in ext) if (HOP(ext, i)) {
obj[i] = ext[i];
count++;
}
return count;
};
function noop() {}
function return_false() { return false; }
function return_true() { return true; }
function return_this() { return this; }
function return_null() { return null; }
var MAP = (function(){
function MAP(a, f, backwards) {
var ret = [], top = [], i;
function doit() {
var val = f(a[i], i);
var is_last = val instanceof Last;
if (is_last) val = val.v;
if (val instanceof AtTop) {
val = val.v;
if (val instanceof Splice) {
top.push.apply(top, backwards ? val.v.slice().reverse() : val.v);
} else {
top.push(val);
}
}
else if (val !== skip) {
if (val instanceof Splice) {
ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v);
} else {
ret.push(val);
}
}
return is_last;
};
if (a instanceof Array) {
if (backwards) {
for (i = a.length; --i >= 0;) if (doit()) break;
ret.reverse();
top.reverse();
} else {
for (i = 0; i < a.length; ++i) if (doit()) break;
}
}
else {
for (i in a) if (HOP(a, i)) if (doit()) break;
}
return top.concat(ret);
};
MAP.at_top = function(val) { return new AtTop(val) };
MAP.splice = function(val) { return new Splice(val) };
MAP.last = function(val) { return new Last(val) };
var skip = MAP.skip = {};
function AtTop(val) { this.v = val };
function Splice(val) { this.v = val };
function Last(val) { this.v = val };
return MAP;
})();
function push_uniq(array, el) {
if (array.indexOf(el) < 0)
array.push(el);
};
function string_template(text, props) {
return text.replace(/\{(.+?)\}/g, function(str, p){
return props && props[p];
});
};
function remove(array, el) {
for (var i = array.length; --i >= 0;) {
if (array[i] === el) array.splice(i, 1);
}
};
function mergeSort(array, cmp) {
if (array.length < 2) return array.slice();
function merge(a, b) {
var r = [], ai = 0, bi = 0, i = 0;
while (ai < a.length && bi < b.length) {
cmp(a[ai], b[bi]) <= 0
? r[i++] = a[ai++]
: r[i++] = b[bi++];
}
if (ai < a.length) r.push.apply(r, a.slice(ai));
if (bi < b.length) r.push.apply(r, b.slice(bi));
return r;
};
function _ms(a) {
if (a.length <= 1)
return a;
var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m);
left = _ms(left);
right = _ms(right);
return merge(left, right);
};
return _ms(array);
};
function set_difference(a, b) {
return a.filter(function(el){
return b.indexOf(el) < 0;
});
};
function set_intersection(a, b) {
return a.filter(function(el){
return b.indexOf(el) >= 0;
});
};
// this function is taken from Acorn [1], written by Marijn Haverbeke
// [1] https://github.com/marijnh/acorn
function makePredicate(words) {
if (!(words instanceof Array)) words = words.split(" ");
var f = "", cats = [];
out: for (var i = 0; i < words.length; ++i) {
for (var j = 0; j < cats.length; ++j)
if (cats[j][0].length == words[i].length) {
cats[j].push(words[i]);
continue out;
}
cats.push([words[i]]);
}
function quote(word) {
return JSON.stringify(word).replace(/[\u2028\u2029]/g, function(s) {
switch (s) {
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
}
return s;
});
}
function compareTo(arr) {
if (arr.length == 1) return f += "return str === " + quote(arr[0]) + ";";
f += "switch(str){";
for (var i = 0; i < arr.length; ++i) f += "case " + quote(arr[i]) + ":";
f += "return true}return false;";
}
// When there are more than three length categories, an outer
// switch first dispatches on the lengths, to save on comparisons.
if (cats.length > 3) {
cats.sort(function(a, b) {return b.length - a.length;});
f += "switch(str.length){";
for (var i = 0; i < cats.length; ++i) {
var cat = cats[i];
f += "case " + cat[0].length + ":";
compareTo(cat);
}
f += "}";
// Otherwise, simply generate a flat `switch` statement.
} else {
compareTo(words);
}
return new Function("str", f);
};
function all(array, predicate) {
for (var i = array.length; --i >= 0;)
if (!predicate(array[i]))
return false;
return true;
};
function Dictionary() {
this._values = Object.create(null);
this._size = 0;
};
Dictionary.prototype = {
set: function(key, val) {
if (!this.has(key)) ++this._size;
this._values["$" + key] = val;
return this;
},
add: function(key, val) {
if (this.has(key)) {
this.get(key).push(val);
} else {
this.set(key, [ val ]);
}
return this;
},
get: function(key) { return this._values["$" + key] },
del: function(key) {
if (this.has(key)) {
--this._size;
delete this._values["$" + key];
}
return this;
},
has: function(key) { return ("$" + key) in this._values },
each: function(f) {
for (var i in this._values)
f(this._values[i], i.substr(1));
},
size: function() {
return this._size;
},
map: function(f) {
var ret = [];
for (var i in this._values)
ret.push(f(this._values[i], i.substr(1)));
return ret;
},
toObject: function() { return this._values }
};
Dictionary.fromObject = function(obj) {
var dict = new Dictionary();
dict._size = merge(dict._values, obj);
return dict;
};
function HOP(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
// return true if the node at the top of the stack (that means the
// innermost node in the current output) is lexically the first in
// a statement.
function first_in_statement(stack) {
var node = stack.parent(-1);
for (var i = 0, p; p = stack.parent(i); i++) {
if (p instanceof AST_Statement && p.body === node)
return true;
if ((p instanceof AST_Seq && p.car === node ) ||
(p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
(p instanceof AST_Dot && p.expression === node ) ||
(p instanceof AST_Sub && p.expression === node ) ||
(p instanceof AST_Conditional && p.condition === node ) ||
(p instanceof AST_Binary && p.left === node ) ||
(p instanceof AST_UnaryPostfix && p.expression === node ))
{
node = p;
} else {
return false;
}
}
}

View File

@@ -0,0 +1,374 @@
## Change Log
### v3.10.0 (2015/05/29 04:25 +00:00)
- [#165](https://github.com/bcoe/yargs/pull/165) expose yargs.terminalWidth() thanks @ensonic (@bcoe)
- [#164](https://github.com/bcoe/yargs/pull/164) better array handling thanks @getify (@bcoe)
### v3.9.1 (2015/05/20 05:14 +00:00)
- [b6662b6](https://github.com/bcoe/yargs/commit/b6662b6774cfeab4876f41ec5e2f67b7698f4e2f) clarify .config() docs (@linclark)
- [0291360](https://github.com/bcoe/yargs/commit/02913606285ce31ce81d7f12c48d8a3029776ec7) fixed tests, switched to nyc for coverage, fixed security issue, added Lin as collaborator (@bcoe)
### v3.9.0 (2015/05/10 18:32 +00:00)
- [#157](https://github.com/bcoe/yargs/pull/157) Merge pull request #157 from bcoe/command-yargs. allows handling of command specific arguments. Thanks for the suggestion @ohjames (@bcoe)
- [#158](https://github.com/bcoe/yargs/pull/158) Merge pull request #158 from kemitchell/spdx-license. Update license format (@kemitchell)
### v3.8.0 (2015/04/24 23:10 +00:00)
- [#154](https://github.com/bcoe/yargs/pull/154) showHelp's method signature was misleading fixes #153 (@bcoe)
- [#151](https://github.com/bcoe/yargs/pull/151) refactor yargs' table layout logic to use new helper library (@bcoe)
- [#150](https://github.com/bcoe/yargs/pull/150) Fix README example in argument requirements (@annonymouse)
### v3.7.2 (2015/04/13 11:52 -07:00)
* [679fbbf](https://github.com/bcoe/yargs/commit/679fbbf55904030ccee8a2635e8e5f46551ab2f0) updated yargs to use the [standard](https://github.com/feross/standard) style guide (agokjr)
* [22382ee](https://github.com/bcoe/yargs/commit/22382ee9f5b495bc2586c1758cd1091cec3647f9 various bug fixes for $0 (@nylen)
### v3.7.1 (2015/04/10 11:06 -07:00)
* [89e1992](https://github.com/bcoe/yargs/commit/89e1992a004ba73609b5f9ee6890c4060857aba4) detect iojs bin along with node bin. (@bcoe)
* [755509e](https://github.com/bcoe/yargs/commit/755509ea90041e5f7833bba3b8c5deffe56f0aab) improvements to example documentation in README.md (@rstacruz)
* [0d2dfc8](https://github.com/bcoe/yargs/commit/0d2dfc822a43418242908ad97ddd5291a1b35dc6) showHelp() no longer requires that .argv has been called (@bcoe)
### v3.7.0 (2015/04/04 02:29 -07:00)
* [56cbe2d](https://github.com/bcoe/yargs/commit/56cbe2ddd33dc176dcbf97ba40559864a9f114e4) make .requiresArg() work with type hints. (@bcoe).
* [2f5d562](https://github.com/bcoe/yargs/commit/2f5d5624f736741deeedf6a664d57bc4d857bdd0) serialize arrays and objects in usage strings. (@bcoe).
* [5126304](https://github.com/bcoe/yargs/commit/5126304dd18351fc28f10530616fdd9361e0af98) be more lenient about alias/primary key ordering in chaining API. (@bcoe)
### v3.6.0 (2015/03/21 01:00 +00:00)
- [4e24e22](https://github.com/bcoe/yargs/commit/4e24e22e6a195e55ab943ede704a0231ac33b99c) support for .js configuration files. (@pirxpilot)
### v3.5.4 (2015/03/12 05:56 +00:00)
- [c16cc08](https://github.com/bcoe/yargs/commit/c16cc085501155cf7fd853ccdf8584b05ab92b78) message for non-option arguments is now optional, thanks to (@raine)
### v3.5.3 (2015/03/09 06:14 +00:00)
- [870b428](https://github.com/bcoe/yargs/commit/870b428cf515d560926ca392555b7ad57dba9e3d) completion script was missing in package.json (@bcoe)
### v3.5.2 (2015/03/09 06:11 +00:00)
- [58a4b24](https://github.com/bcoe/yargs/commit/58a4b2473ebbb326713d522be53e32d3aabb08d2) parse was being called multiple times, resulting in strange behavior (@bcoe)
### v3.5.1 (2015/03/09 04:55 +00:00)
- [4e588e0](https://github.com/bcoe/yargs/commit/4e588e055afbeb9336533095f051496e3977f515) accidentally left testing logic in (@bcoe)
### v3.5.0 (2015/03/09 04:49 +00:00)
- [718bacd](https://github.com/bcoe/yargs/commit/718bacd81b9b44f786af76b2afe491fe06274f19) added support for bash completions see #4 (@bcoe)
- [a192882](https://github.com/bcoe/yargs/commit/a19288270fc431396c42af01125eeb4443664528) downgrade to mocha 2.1.0 until https://github.com/mochajs/mocha/issues/1585 can be sorted out (@bcoe)
### v3.4.7 (2015/03/09 04:09 +00:00)
- [9845e5c](https://github.com/bcoe/yargs/commit/9845e5c1a9c684ba0be3f0bfb40e7b62ab49d9c8) the Argv singleton was not being updated when manually parsing arguments, fixes #114 (@bcoe)
### v3.4.6 (2015/03/09 04:01 +00:00)
- [45b4c80](https://github.com/bcoe/yargs/commit/45b4c80b890d02770b0a94f326695a8a566e8fe9) set placeholders for all keys fixes #115 (@bcoe)
### v3.4.5 (2015/03/01 20:31 +00:00)
- [a758e0b](https://github.com/bcoe/yargs/commit/a758e0b2556184f067cf3d9c4ef886d39817ebd2) fix for count consuming too many arguments (@bcoe)
### v3.4.4 (2015/02/28 04:52 +00:00)
- [0476af7](https://github.com/bcoe/yargs/commit/0476af757966acf980d998b45108221d4888cfcb) added nargs feature, allowing you to specify the number of arguments after an option (@bcoe)
- [092477d](https://github.com/bcoe/yargs/commit/092477d7ab3efbf0ba11cede57f7d8cfc70b024f) updated README with full example of v3.0 API (@bcoe)
### v3.3.3 (2015/02/28 04:23 +00:00)
- [0c4b769](https://github.com/bcoe/yargs/commit/0c4b769516cd8d93a7c4e5e675628ae0049aa9a8) remove string dependency, which conflicted with other libraries see #106 (@bcoe)
### v3.3.2 (2015/02/28 04:11 +00:00)
- [2a98906](https://github.com/bcoe/yargs/commit/2a9890675821c0e7a12f146ce008b0562cb8ec9a) add $0 to epilog (@schnittstabil)
### v3.3.1 (2015/02/24 03:28 +00:00)
- [ad485ce](https://github.com/bcoe/yargs/commit/ad485ce748ebdfce25b88ef9d6e83d97a2f68987) fix for applying defaults to camel-case args (@bcoe)
### v3.3.0 (2015/02/24 00:49 +00:00)
- [8bfe36d](https://github.com/bcoe/yargs/commit/8bfe36d7fb0f93a799ea3f4c756a7467c320f8c0) fix and document restart() command, as a tool for building nested CLIs (@bcoe)
### v3.2.1 (2015/02/22 05:45 +00:00)
- [49a6d18](https://github.com/bcoe/yargs/commit/49a6d1822a4ef9b1ea6f90cc366be60912628885) you can now provide a function that generates a default value (@bcoe)
### v3.2.0 (2015/02/22 05:24 +00:00)
- [7a55886](https://github.com/bcoe/yargs/commit/7a55886c9343cf71a20744ca5cdd56d2ea7412d5) improvements to yargs two-column text layout (@bcoe)
- [b6ab513](https://github.com/bcoe/yargs/commit/b6ab5136a4c3fa6aa496f6b6360382e403183989) Tweak NPM version badge (@nylen)
### v3.1.0 (2015/02/19 19:37 +00:00)
- [9bd2379](https://github.com/bcoe/yargs/commit/9bd237921cf1b61fd9f32c0e6d23f572fc225861) version now accepts a function, making it easy to load version #s from a package.json (@bcoe)
### v3.0.4 (2015/02/14 01:40 +00:00)
- [0b7c19b](https://github.com/bcoe/yargs/commit/0b7c19beaecb747267ca4cc10e5cb2a8550bc4b7) various fixes for dot-notation handling (@bcoe)
### v3.0.3 (2015/02/14 00:59 +00:00)
- [c3f35e9](https://github.com/bcoe/yargs/commit/c3f35e99bd5a0d278073fcadd95e2d778616cc17) make sure dot-notation is applied to aliases (@bcoe)
### 3.0.2 (2015/02/13 16:50 +00:00)
- [74c8967](https://github.com/bcoe/yargs/commit/74c8967c340c204a0a7edf8a702b6f46c2705435) document epilog shorthand of epilogue. (@bcoe)
- [670110f](https://github.com/bcoe/yargs/commit/670110fc01bedc4831b6fec6afac54517d5a71bc) any non-truthy value now causes check to fail see #76 (@bcoe)
- [0d8f791](https://github.com/bcoe/yargs/commit/0d8f791a33c11ced4cd431ea8d3d3a337d456b56) finished implementing my wish-list of fetures for yargs 3.0. see #88 (@bcoe)
- [5768447](https://github.com/bcoe/yargs/commit/5768447447c4c8e8304f178846206ce86540f063) fix coverage. (@bcoe)
- [82e793f](https://github.com/bcoe/yargs/commit/82e793f3f61c41259eaacb67f0796aea2cf2aaa0) detect console width and perform word-wrapping. (@bcoe)
- [67476b3](https://github.com/bcoe/yargs/commit/67476b37eea07fee55f23f35b9e0c7d76682b86d) refactor two-column table layout so that we can use it for examples and usage (@bcoe)
- [4724cdf](https://github.com/bcoe/yargs/commit/4724cdfcc8e37ae1ca3dcce9d762f476e9ef4bb4) major refactor of index.js, in prep for 3.x release. (@bcoe)
### v2.3.0 (2015/02/08 20:41 +00:00)
- [d824620](https://github.com/bcoe/yargs/commit/d824620493df4e63664af1fe320764dd1a9244e6) allow for undefined boolean defaults (@ashi009)
### v2.2.0 (2015/02/08 20:07 +00:00)
- [d6edd98](https://github.com/bcoe/yargs/commit/d6edd9848826e7389ed1393858c45d03961365fd) in-prep for further refactoring, and a 3.x release I've shuffled some things around and gotten test-coverage to 100%. (@bcoe)
### v2.1.2 (2015/02/08 06:05 +00:00)
- [d640745](https://github.com/bcoe/yargs/commit/d640745a7b9f8d476e0223879d056d18d9c265c4) switch to path.relative (@bcoe)
- [3bfd41f](https://github.com/bcoe/yargs/commit/3bfd41ff262a041f29d828b88936a79c63cad594) remove mocha.opts. (@bcoe)
- [47a2f35](https://github.com/bcoe/yargs/commit/47a2f357091db70903a402d6765501c1d63f15fe) document using .string('_') for string ids. see #56 (@bcoe)
- [#57](https://github.com/bcoe/yargs/pull/57) Merge pull request #57 from eush77/option-readme (@eush77)
### v2.1.1 (2015/02/06 08:08 +00:00)
- [01c6c61](https://github.com/bcoe/yargs/commit/01c6c61d67b4ebf88f41f0b32a345ec67f0ac17d) fix for #71, 'newAliases' of undefined (@bcoe)
### v2.1.0 (2015/02/06 07:59 +00:00)
- [6a1a3fa](https://github.com/bcoe/yargs/commit/6a1a3fa731958e26ccd56885f183dd8985cc828f) try to guess argument types, and apply sensible defaults see #73 (@bcoe)
### v2.0.1 (2015/02/06 07:54 +00:00)
- [96a06b2](https://github.com/bcoe/yargs/commit/96a06b2650ff1d085a52b7328d8bba614c20cc12) Fix for strange behavior with --sort option, see #51 (@bcoe)
### v2.0.0 (2015/02/06 07:45 +00:00)
- [0250517](https://github.com/bcoe/yargs/commit/0250517c9643e53f431b824e8ccfa54937414011) - [108fb84](https://github.com/bcoe/yargs/commit/108fb8409a3a63dcaf99d917fe4dfcfaa1de236d) fixed bug with boolean parsing, when bools separated by = see #66 (@bcoe)
- [a465a59](https://github.com/bcoe/yargs/commit/a465a5915f912715738de890982e4f8395958b10) Add `files` field to the package.json (@shinnn)
- [31043de](https://github.com/bcoe/yargs/commit/31043de7a38a17c4c97711f1099f5fb164334db3) fix for yargs.argv having the same keys added multiple times see #63 (@bcoe)
- [2d68c5b](https://github.com/bcoe/yargs/commit/2d68c5b91c976431001c4863ce47c9297850f1ad) Disable process.exit calls using .exitProcess(false) (@cianclarke)
- [45da9ec](https://github.com/bcoe/yargs/commit/45da9ec4c55a7bd394721bc6a1db0dabad7bc52a) Mention .option in README (@eush77)
### v1.3.2 (2014/10/06 21:56 +00:00)
- [b8d3472](https://github.com/bcoe/yargs/commit/b8d34725482e5821a3cc809c0df71378f282f526) 1.3.2 (@chevex)
### list (2014/08/30 18:41 +00:00)
- [fbc777f](https://github.com/bcoe/yargs/commit/fbc777f416eeefd37c84e44d27d7dfc7c1925721) Now that yargs is the successor to optimist, I'm changing the README language to be more universal. Pirate speak isn't very accessible to non-native speakers. (@chevex)
- [a54d068](https://github.com/bcoe/yargs/commit/a54d0682ae2efc2394d407ab171cc8a8bbd135ea) version output will not print extra newline (@boneskull)
- [1cef5d6](https://github.com/bcoe/yargs/commit/1cef5d62a9d6d61a3948a49574892e01932cc6ae) Added contributors section to package.json (@chrisn)
- [cc295c0](https://github.com/bcoe/yargs/commit/cc295c0a80a2de267e0155b60d315fc4b6f7c709) Added 'require' and 'required' as synonyms for 'demand' (@chrisn)
- [d0bf951](https://github.com/bcoe/yargs/commit/d0bf951d949066b6280101ed606593d079ee15c8) Updating minimist. (@chevex)
- [c15f8e7](https://github.com/bcoe/yargs/commit/c15f8e7f245b261e542cf205ce4f4313630cbdb4) Fix #31 (bad interaction between camelCase options and strict mode) (@nylen)
- [d991b9b](https://github.com/bcoe/yargs/commit/d991b9be687a68812dee1e3b185ba64b7778b82d) Added .help() and .version() methods (@chrisn)
- [e8c8aa4](https://github.com/bcoe/yargs/commit/e8c8aa46268379357cb11e9fc34b8c403037724b) Added .showHelpOnFail() method (@chrisn)
- [e855af4](https://github.com/bcoe/yargs/commit/e855af4a933ea966b5bbdd3c4c6397a4bac1a053) Allow boolean flag with .demand() (@chrisn)
- [14dbec2](https://github.com/bcoe/yargs/commit/14dbec24fb7380683198e2b20c4deb8423e64bea) Fixes issue #22. Arguments are no longer printed to the console when using .config. (@chevex)
- [bef74fc](https://github.com/bcoe/yargs/commit/bef74fcddc1544598a804f80d0a3728459f196bf) Informing users that Yargs is the official optimist successor. (@chevex)
- [#24](https://github.com/bcoe/yargs/pull/24) Merge pull request #24 from chrisn/strict (@chrisn)
- [889a2b2](https://github.com/bcoe/yargs/commit/889a2b28eb9768801b05163360a470d0fd6c8b79) Added requiresArg option, for options that require values (@chrisn)
- [eb16369](https://github.com/bcoe/yargs/commit/eb163692262be1fe80b992fd8803d5923c5a9b18) Added .strict() method, to report error if unknown arguments are given (@chrisn)
- [0471c3f](https://github.com/bcoe/yargs/commit/0471c3fd999e1ad4e6cded88b8aa02013b66d14f) Changed optimist to yargs in usage-options.js example (@chrisn)
- [5c88f74](https://github.com/bcoe/yargs/commit/5c88f74e3cf031b17c54b4b6606c83e485ff520e) Change optimist to yargs in examples (@chrisn)
- [66f12c8](https://github.com/bcoe/yargs/commit/66f12c82ba3c943e4de8ca862980e835da8ecb3a) Fix a couple of bad interactions between aliases and defaults (@nylen)
- [8fa1d80](https://github.com/bcoe/yargs/commit/8fa1d80f14b03eb1f2898863a61f1d1615bceb50) Document second argument of usage(message, opts) (@Gobie)
- [56e6528](https://github.com/bcoe/yargs/commit/56e6528cf674ff70d63083fb044ff240f608448e) For "--some-option", also set argv.someOption (@nylen)
- [ed5f6d3](https://github.com/bcoe/yargs/commit/ed5f6d33f57ad1086b11c91b51100f7c6c7fa8ee) Finished porting unit tests to Mocha. (@chevex)
### v1.0.15 (2014/02/05 23:18 +00:00)
- [e2b1fc0](https://github.com/bcoe/yargs/commit/e2b1fc0c4a59cf532ae9b01b275e1ef57eeb64d2) 1.0.15 update to badges (@chevex)
### v1.0.14 (2014/02/05 23:17 +00:00)
- [f33bbb0](https://github.com/bcoe/yargs/commit/f33bbb0f00fe18960f849cc8e15a7428a4cd59b8) Revert "Fixed issue which caused .demand function not to work correctly." (@chevex)
### v1.0.13 (2014/02/05 22:13 +00:00)
- [6509e5e](https://github.com/bcoe/yargs/commit/6509e5e7dee6ef1a1f60eea104be0faa1a045075) Fixed issue which caused .demand function not to work correctly. (@chevex)
### v1.0.12 (2013/12/13 00:09 +00:00)
- [05eb267](https://github.com/bcoe/yargs/commit/05eb26741c9ce446b33ff006e5d33221f53eaceb) 1.0.12 (@chevex)
### v1.0.11 (2013/12/13 00:07 +00:00)
- [c1bde46](https://github.com/bcoe/yargs/commit/c1bde46e37318a68b87d17a50c130c861d6ce4a9) 1.0.11 (@chevex)
### v1.0.10 (2013/12/12 23:57 +00:00)
- [dfebf81](https://github.com/bcoe/yargs/commit/dfebf8164c25c650701528ee581ca483a99dc21c) Fixed formatting in README (@chevex)
### v1.0.9 (2013/12/12 23:47 +00:00)
- [0b4e34a](https://github.com/bcoe/yargs/commit/0b4e34af5e6d84a9dbb3bb6d02cd87588031c182) Update README.md (@chevex)
### v1.0.8 (2013/12/06 16:36 +00:00)
- [#1](https://github.com/bcoe/yargs/pull/1) fix error caused by check() see #1 (@martinheidegger)
### v1.0.7 (2013/11/24 18:01 +00:00)
- [a247d88](https://github.com/bcoe/yargs/commit/a247d88d6e46644cbb7303c18b1bb678fc132d72) Modified Pirate Joe image. (@chevex)
### v1.0.6 (2013/11/23 19:21 +00:00)
- [d7f69e1](https://github.com/bcoe/yargs/commit/d7f69e1d34bc929736a8bdccdc724583e21b7eab) Updated Pirate Joe image. (@chevex)
### v1.0.5 (2013/11/23 19:09 +00:00)
- [ece809c](https://github.com/bcoe/yargs/commit/ece809cf317cc659175e1d66d87f3ca68c2760be) Updated readme notice again. (@chevex)
### v1.0.4 (2013/11/23 19:05 +00:00)
- [9e81e81](https://github.com/bcoe/yargs/commit/9e81e81654028f83ba86ffc3ac772a0476084e5e) Updated README with a notice about yargs being a fork of optimist and what that implies. (@chevex)
### v1.0.3 (2013/11/23 17:43 +00:00)
- [65e7a78](https://github.com/bcoe/yargs/commit/65e7a782c86764944d63d084416aba9ee6019c5f) Changed some small wording in README.md. (@chevex)
- [459e20e](https://github.com/bcoe/yargs/commit/459e20e539b366b85128dd281ccd42221e96c7da) Fix a bug in the options function, when string and boolean options weren't applied to aliases. (@shockone)
### v1.0.2 (2013/11/23 09:46 +00:00)
- [3d80ebe](https://github.com/bcoe/yargs/commit/3d80ebed866d3799224b6f7d596247186a3898a9) 1.0.2 (@chevex)
### v1.0.1 (2013/11/23 09:39 +00:00)
- [f80ff36](https://github.com/bcoe/yargs/commit/f80ff3642d580d4b68bf9f5a94277481bd027142) Updated image. (@chevex)
### v1.0.0 (2013/11/23 09:33 +00:00)
- [54e31d5](https://github.com/bcoe/yargs/commit/54e31d505f820b80af13644e460894b320bf25a3) Rebranded from optimist to yargs in the spirit of the fork :D (@chevex)
- [4ebb6c5](https://github.com/bcoe/yargs/commit/4ebb6c59f44787db7c24c5b8fe2680f01a23f498) Added documentation for demandCount(). (@chevex)
- [4561ce6](https://github.com/bcoe/yargs/commit/4561ce66dcffa95f49e8b4449b25b94cd68acb25) Simplified the error messages returned by .check(). (@chevex)
- [661c678](https://github.com/bcoe/yargs/commit/661c67886f479b16254a830b7e1db3be29e6b7a6) Fixed an issue with demand not accepting a zero value. (@chevex)
- [731dd3c](https://github.com/bcoe/yargs/commit/731dd3c37624790490bd6df4d5f1da8f4348279e) Add .fail(fn) so death isn't the only option. Should fix issue #39. (@chevex)
- [fa15417](https://github.com/bcoe/yargs/commit/fa15417ff9e70dace0d726627a5818654824c1d8) Added a few missing 'return self' (@chevex)
- [e655e4d](https://github.com/bcoe/yargs/commit/e655e4d99d1ae1d3695ef755d51c2de08d669761) Fix showing help in certain JS environments. (@chevex)
- [a746a31](https://github.com/bcoe/yargs/commit/a746a31cd47c87327028e6ea33762d6187ec5c87) Better string representation of default values. (@chevex)
- [6134619](https://github.com/bcoe/yargs/commit/6134619a7e90b911d5443230b644c5d447c1a68c) Implies: conditional demands (@chevex)
- [046b93b](https://github.com/bcoe/yargs/commit/046b93b5d40a27367af4cb29726e4d781d934639) Added support for JSON config files. (@chevex)
- [a677ec0](https://github.com/bcoe/yargs/commit/a677ec0a0ecccd99c75e571d03323f950688da03) Add .example(cmd, desc) feature. (@chevex)
- [1bd4375](https://github.com/bcoe/yargs/commit/1bd4375e11327ba1687d4bb6e5e9f3c30c1be2af) Added 'defaults' as alias to 'default' so as to avoid usage of a reserved keyword. (@chevex)
- [6b753c1](https://github.com/bcoe/yargs/commit/6b753c16ca09e723060e70b773b430323b29c45c) add .normalize(args..) support for normalizing paths (@chevex)
- [33d7d59](https://github.com/bcoe/yargs/commit/33d7d59341d364f03d3a25f0a55cb99004dbbe4b) Customize error messages with demand(key, msg) (@chevex)
- [647d37f](https://github.com/bcoe/yargs/commit/647d37f164c20f4bafbf67dd9db6cd6e2cd3b49f) Merge branch 'rewrite-duplicate-test' of github.com:isbadawi/node-optimist (@chevex)
- [9059d1a](https://github.com/bcoe/yargs/commit/9059d1ad5e8aea686c2a01c89a23efdf929fff2e) Pass aliases object to check functions for greater versatility. (@chevex)
- [623dc26](https://github.com/bcoe/yargs/commit/623dc26c7331abff2465ef8532e3418996d42fe6) Added ability to count boolean options and rolled minimist library back into project. (@chevex)
- [49f0dce](https://github.com/bcoe/yargs/commit/49f0dcef35de4db544c3966350d36eb5838703f6) Fixed small typo. (@chevex)
- [79ec980](https://github.com/bcoe/yargs/commit/79ec9806d9ca6eb0014cfa4b6d1849f4f004baf2) Removed dependency on wordwrap module. (@chevex)
- [ea14630](https://github.com/bcoe/yargs/commit/ea14630feddd69d1de99dd8c0e08948f4c91f00a) Merge branch 'master' of github.com:chbrown/node-optimist (@chevex)
- [2b75da2](https://github.com/bcoe/yargs/commit/2b75da2624061e0f4f3107d20303c06ec9054906) Merge branch 'master' of github.com:seanzhou1023/node-optimist (@chevex)
- [d9bda11](https://github.com/bcoe/yargs/commit/d9bda1116e26f3b40e833ca9ca19263afea53565) Merge branch 'patch-1' of github.com:thefourtheye/node-optimist (@chevex)
- [d6cc606](https://github.com/bcoe/yargs/commit/d6cc6064a4f1bea38a16a4430b8a1334832fbeff) Renamed README. (@chevex)
- [9498d3f](https://github.com/bcoe/yargs/commit/9498d3f59acfb5e102826503e681623c3a64b178) Renamed readme and added .gitignore. (@chevex)
- [bbd1fe3](https://github.com/bcoe/yargs/commit/bbd1fe37fefa366dde0fb3dc44d91fe8b28f57f5) Included examples for ```help``` and ```showHelp``` functions and fixed few formatting issues (@thefourtheye)
- [37fea04](https://github.com/bcoe/yargs/commit/37fea0470a5796a0294c1dcfff68d8041650e622) .alias({}) behaves differently based on mapping direction when generating descriptions (@chbrown)
- [855b20d](https://github.com/bcoe/yargs/commit/855b20d0be567ca121d06b30bea64001b74f3d6d) Documented function signatures are useful for dynamically typed languages. (@chbrown)
### 0.6.0 (2013/06/25 08:48 +00:00)
- [d37bfe0](https://github.com/bcoe/yargs/commit/d37bfe05ae6d295a0ab481efe4881222412791f4) all tests passing using minimist (@substack)
- [76f1352](https://github.com/bcoe/yargs/commit/76f135270399d01f2bbc621e524a5966e5c422fd) all parse tests now passing (@substack)
- [a7b6754](https://github.com/bcoe/yargs/commit/a7b6754276c38d1565479a5685c3781aeb947816) using minimist, some tests passing (@substack)
- [6655688](https://github.com/bcoe/yargs/commit/66556882aa731cbbbe16cc4d42c85740a2e98099) Give credit where its due (@DeadAlready)
- [602a2a9](https://github.com/bcoe/yargs/commit/602a2a92a459f93704794ad51b115bbb08b535ce) v0.5.3 - Remove wordwrap as dependency (@DeadAlready)
### 0.5.2 (2013/05/31 03:46 +00:00)
- [4497ca5](https://github.com/bcoe/yargs/commit/4497ca55e332760a37b866ec119ded347ca27a87) fixed the whitespace bug without breaking anything else (@substack)
- [5a3dd1a](https://github.com/bcoe/yargs/commit/5a3dd1a4e0211a38613c6e02f61328e1031953fa) failing test for whitespace arg (@substack)
### 0.5.1 (2013/05/30 07:17 +00:00)
- [a20228f](https://github.com/bcoe/yargs/commit/a20228f62a454755dd07f628a7c5759113918327) fix parse() to work with functions before it (@substack)
- [b13bd4c](https://github.com/bcoe/yargs/commit/b13bd4cac856a9821d42fa173bdb58f089365a7d) failing test for parse() with modifiers (@substack)
### 0.5.0 (2013/05/18 21:59 +00:00)
- [c474a64](https://github.com/bcoe/yargs/commit/c474a649231527915c222156e3b40806d365a87c) fixes for dash (@substack)
### 0.4.0 (2013/04/13 19:03 +00:00)
- [dafe3e1](https://github.com/bcoe/yargs/commit/dafe3e18d7c6e7c2d68e06559df0e5cbea3adb14) failing short test (@substack)
### 0.3.7 (2013/04/04 04:07 +00:00)
- [6c7a0ec](https://github.com/bcoe/yargs/commit/6c7a0ec94ce4199a505f0518b4d6635d4e47cc81) Fix for windows. On windows there is no _ in environment. (@hdf)
### 0.3.6 (2013/04/04 04:04 +00:00)
- [e72346a](https://github.com/bcoe/yargs/commit/e72346a727b7267af5aa008b418db89970873f05) Add support for newlines in -a="" arguments (@danielbeardsley)
- [71e1fb5](https://github.com/bcoe/yargs/commit/71e1fb55ea9987110a669ac6ec12338cfff3821c) drop 0.4, add 0.8 to travis (@substack)
### 0.3.5 (2012/10/10 11:09 +00:00)
- [ee692b3](https://github.com/bcoe/yargs/commit/ee692b37554c70a0bb16389a50a26b66745cbbea) Fix parsing booleans (@vojtajina)
- [5045122](https://github.com/bcoe/yargs/commit/5045122664c3f5b4805addf1be2148d5856f7ce8) set $0 properly in the tests (@substack)
### 0.3.4 (2012/04/30 06:54 +00:00)
- [f28c0e6](https://github.com/bcoe/yargs/commit/f28c0e62ca94f6e0bb2e6d82fc3d91a55e69b903) bump for string "true" params (@substack)
- [8f44aeb](https://github.com/bcoe/yargs/commit/8f44aeb74121ddd689580e2bf74ef86a605e9bf2) Fix failing test for aliased booleans. (@coderarity)
- [b9f7b61](https://github.com/bcoe/yargs/commit/b9f7b613b1e68e11e6c23fbda9e555a517dcc976) Add failing test for short aliased booleans. (@coderarity)
### 0.3.3 (2012/04/30 06:45 +00:00)
- [541bac8](https://github.com/bcoe/yargs/commit/541bac8dd787a5f1a5d28f6d8deb1627871705e7) Fixes #37.
### 0.3.2 (2012/04/12 20:28 +00:00)
- [3a0f014](https://github.com/bcoe/yargs/commit/3a0f014c1451280ac1c9caa1f639d31675586eec) travis badge (@substack)
- [4fb60bf](https://github.com/bcoe/yargs/commit/4fb60bf17845f4ce3293f8ca49c9a1a7c736cfce) Fix boolean aliases. (@coderarity)
- [f14dda5](https://github.com/bcoe/yargs/commit/f14dda546efc4fe06ace04d36919bfbb7634f79b) Adjusted package.json to use tap (@jfhbrook)
- [88e5d32](https://github.com/bcoe/yargs/commit/88e5d32295be6e544c8d355ff84e355af38a1c74) test/usage.js no longer hangs (@jfhbrook)
- [e1e740c](https://github.com/bcoe/yargs/commit/e1e740c27082f3ce84deca2093d9db2ef735d0e5) two tests for combined boolean/alias opts parsing (@jfhbrook)
### 0.3.1 (2011/12/31 08:44 +00:00)
- [d09b719](https://github.com/bcoe/yargs/commit/d09b71980ef711b6cf3918cd19beec8257e40e82) If "default" is set to false it was not passed on, fixed. (@wolframkriesing)
### 0.3.0 (2011/12/09 06:03 +00:00)
- [6e74aa7](https://github.com/bcoe/yargs/commit/6e74aa7b46a65773e20c0cb68d2d336d4a0d553d) bump and documented dot notation (@substack)
### 0.2.7 (2011/10/20 02:25 +00:00)
- [94adee2](https://github.com/bcoe/yargs/commit/94adee20e17b58d0836f80e8b9cdbe9813800916) argv._ can be told 'Hey! argv._! Don't be messing with my args.', and it WILL obey (@colinta)
- [c46fdd5](https://github.com/bcoe/yargs/commit/c46fdd56a05410ae4a1e724a4820c82e77ff5469) optimistic critter image (@substack)
- [5c95c73](https://github.com/bcoe/yargs/commit/5c95c73aedf4c7482bd423e10c545e86d7c8a125) alias options() to option() (@substack)
- [f7692ea](https://github.com/bcoe/yargs/commit/f7692ea8da342850af819367833abb685fde41d8) [fix] Fix for parsing boolean edge case (@indexzero)
- [d1f92d1](https://github.com/bcoe/yargs/commit/d1f92d1425bd7f356055e78621b30cdf9741a3c2)
- [b01bda8](https://github.com/bcoe/yargs/commit/b01bda8d86e455bbf74ce497864cb8ab5b9fb847) [fix test] Update to ensure optimist is aware of default booleans. Associated tests included (@indexzero)
- [aa753e7](https://github.com/bcoe/yargs/commit/aa753e7c54fb3a12f513769a0ff6d54aa0f63943) [dist test] Update devDependencies in package.json. Update test pathing to be more npm and require.paths future-proof (@indexzero)
- [7bfce2f](https://github.com/bcoe/yargs/commit/7bfce2f3b3c98e6539e7549d35fbabced7e9341e) s/sys/util/ (@substack)
- [d420a7a](https://github.com/bcoe/yargs/commit/d420a7a9c890d2cdb11acfaf3ea3f43bc3e39f41) update usage output (@substack)
- [cf86eed](https://github.com/bcoe/yargs/commit/cf86eede2e5fc7495b6ec15e6d137d9ac814f075) some sage readme protips about parsing rules (@substack)
- [5da9f7a](https://github.com/bcoe/yargs/commit/5da9f7a5c0e1758ec7c5801fb3e94d3f6e970513) documented all the methods finally (@substack)
- [8ca6879](https://github.com/bcoe/yargs/commit/8ca6879311224b25933642987300f6a29de5c21b) fenced syntax highlighting (@substack)
- [b72bacf](https://github.com/bcoe/yargs/commit/b72bacf1d02594778c1935405bc8137eb61761dc) right-alignment of wrapped extra params (@substack)
- [2b980bf](https://github.com/bcoe/yargs/commit/2b980bf2656b4ee8fc5134dc5f56a48855c35198) now with .wrap() (@substack)
- [d614f63](https://github.com/bcoe/yargs/commit/d614f639654057d1b7e35e3f5a306e88ec2ad1e4) don't show 'Options:' when there aren't any (@substack)
- [691eda3](https://github.com/bcoe/yargs/commit/691eda354df97b5a86168317abcbcaabdc08a0fb) failing test for multi-aliasing (@substack)
- [0826c9f](https://github.com/bcoe/yargs/commit/0826c9f462109feab2bc7a99346d22e72bf774b7) "Options:" > "options:" (@substack)
- [72f7490](https://github.com/bcoe/yargs/commit/72f749025d01b7f295738ed370a669d885fbada0) [minor] Update formatting for `.showHelp()` (@indexzero)
- [75aecce](https://github.com/bcoe/yargs/commit/75aeccea74329094072f95800e02c275e7d999aa) options works again, too lazy to write a proper test right now (@substack)
- [f742e54](https://github.com/bcoe/yargs/commit/f742e5439817c662dc3bd8734ddd6467e6018cfd) line_count_options example, which breaks (@substack)
- [4ca06b8](https://github.com/bcoe/yargs/commit/4ca06b8b4ea99b5d5714b315a2a8576bee6e5537) line count example (@substack)
- [eeb8423](https://github.com/bcoe/yargs/commit/eeb8423e0a5ecc9dc3eb1e6df9f3f8c1c88f920b) remove self.argv setting in boolean (@substack)
- [6903412](https://github.com/bcoe/yargs/commit/69034126804660af9cc20ea7f4457b50338ee3d7) removed camel case for now (@substack)
- [5a0d88b](https://github.com/bcoe/yargs/commit/5a0d88bf23e9fa79635dd034e2a1aa992acc83cd) remove dead longest checking code (@substack)
- [d782170](https://github.com/bcoe/yargs/commit/d782170babf7284b1aa34f5350df0dd49c373fa8) .help() too (@substack)
- [622ec17](https://github.com/bcoe/yargs/commit/622ec17379bb5374fdbb190404c82bc600975791) rm old help generator (@substack)
- [7c8baac](https://github.com/bcoe/yargs/commit/7c8baac4d66195e9f5158503ea9ebfb61153dab7) nub keys (@substack)
- [8197785](https://github.com/bcoe/yargs/commit/8197785ad4762465084485b041abd722f69bf344) generate help message based on the previous calls, todo: nub (@substack)
- [3ffbdc3](https://github.com/bcoe/yargs/commit/3ffbdc33c8f5e83d4ea2ac60575ce119570c7ede) stub out new showHelp, better checks (@substack)
- [d4e21f5](https://github.com/bcoe/yargs/commit/d4e21f56a4830f7de841900d3c79756fb9886184) let .options() take single options too (@substack)
- [3c4cf29](https://github.com/bcoe/yargs/commit/3c4cf2901a29bac119cca8e983028d8669230ec6) .options() is now heaps simpler (@substack)
- [89f0d04](https://github.com/bcoe/yargs/commit/89f0d043cbccd302f10ab30c2069e05d2bf817c9) defaults work again, all tests pass (@substack)
- [dd87333](https://github.com/bcoe/yargs/commit/dd8733365423006a6e4156372ebb55f98323af58) update test error messages, down to 2 failing tests (@substack)
- [53f7bc6](https://github.com/bcoe/yargs/commit/53f7bc626b9875f2abdfc5dd7a80bde7f14143a3) fix for bools doubling up, passes the parse test again, others fail (@substack)
- [2213e2d](https://github.com/bcoe/yargs/commit/2213e2ddc7263226fba717fb041dc3fde9bc2ee4) refactored for an argv getter, failing several tests (@substack)
- [d1e7379](https://github.com/bcoe/yargs/commit/d1e737970f15c6c006bebdd8917706827ff2f0f2) just rescan for now, alias test passes (@substack)
- [b2f8c99](https://github.com/bcoe/yargs/commit/b2f8c99cc477a8eb0fdf4cf178e1785b63185cfd) failing alias test (@substack)
- [d0c0174](https://github.com/bcoe/yargs/commit/d0c0174daa144bfb6dc7290fdc448c393c475e15) .alias() (@substack)
- [d85f431](https://github.com/bcoe/yargs/commit/d85f431ad7d07b058af3f2a57daa51495576c164) [api] Remove `.describe()` in favor of building upon the existing `.usage()` API (@indexzero)
- [edbd527](https://github.com/bcoe/yargs/commit/edbd5272a8e213e71acd802782135c7f9699913a) [doc api] Add `.describe()`, `.options()`, and `.showHelp()` methods along with example. (@indexzero)
- [be4902f](https://github.com/bcoe/yargs/commit/be4902ff0961ae8feb9093f2c0a4066463ded2cf) updates for coffee since it now does argv the node way (@substack)
- [e24cb23](https://github.com/bcoe/yargs/commit/e24cb23798ee64e53b60815e7fda78b87f42390c) more general coffeescript detection (@substack)
- [78ac753](https://github.com/bcoe/yargs/commit/78ac753e5d0ec32a96d39d893272afe989e42a4d) Don't trigger the CoffeeScript hack when running under node_g. (@papandreou)
- [bcfe973](https://github.com/bcoe/yargs/commit/bcfe9731d7f90d4632281b8a52e8d76eb0195ae6) .string() but failing test (@substack)
- [1987aca](https://github.com/bcoe/yargs/commit/1987aca28c7ba4e8796c07bbc547cb984804c826) test hex strings (@substack)
- [ef36db3](https://github.com/bcoe/yargs/commit/ef36db32259b0b0d62448dc907c760e5554fb7e7) more keywords (@substack)
- [cc53c56](https://github.com/bcoe/yargs/commit/cc53c56329960bed6ab077a79798e991711ba01d) Added camelCase function that converts --multi-word-option to camel case (so it becomes argv.multiWordOption). (@papandreou)
- [60b57da](https://github.com/bcoe/yargs/commit/60b57da36797716e5783a633c6d5c79099016d45) fixed boolean bug by rescanning (@substack)
- [dff6d07](https://github.com/bcoe/yargs/commit/dff6d078d97f8ac503c7d18dcc7b7a8c364c2883) boolean examples (@substack)
- [0e380b9](https://github.com/bcoe/yargs/commit/0e380b92c4ef4e3c8dac1da18b5c31d85b1d02c9) boolean() with passing test (@substack)
- [62644d4](https://github.com/bcoe/yargs/commit/62644d4bffbb8d1bbf0c2baf58a1d14a6359ef07) coffee compatibility with node regex for versions too (@substack)
- [430fafc](https://github.com/bcoe/yargs/commit/430fafcf1683d23774772826581acff84b456827) argv._ fixed by fixing the coffee detection (@substack)
- [343b8af](https://github.com/bcoe/yargs/commit/343b8afefd98af274ebe21b5a16b3a949ec5429f) whichNodeArgs test fails too (@substack)
- [63df2f3](https://github.com/bcoe/yargs/commit/63df2f371f31e63d7f1dec2cbf0022a5f08da9d2) replicated mnot's bug in whichNodeEmpty test (@substack)
- [35473a4](https://github.com/bcoe/yargs/commit/35473a4d93a45e5e7e512af8bb54ebb532997ae1) test for ./bin usage (@substack)
- [13df151](https://github.com/bcoe/yargs/commit/13df151e44228eed10e5441c7cd163e086c458a4) don't coerce booleans to numbers (@substack)
- [85f8007](https://github.com/bcoe/yargs/commit/85f8007e93b8be7124feea64b1f1916d8ba1894a) package bump for automatic number conversion (@substack)
- [8f17014](https://github.com/bcoe/yargs/commit/8f170141cded4ccc0c6d67a849c5bf996aa29643) updated readme and examples with new auto-numberification goodness (@substack)
- [73dc901](https://github.com/bcoe/yargs/commit/73dc9011ac968e39b55e19e916084a839391b506) auto number conversion works yay (@substack)
- [bcec56b](https://github.com/bcoe/yargs/commit/bcec56b3d031e018064cbb691539ccc4f28c14ad) failing test for not-implemented auto numification (@substack)
- [ebd2844](https://github.com/bcoe/yargs/commit/ebd2844d683feeac583df79af0e5124a7a7db04e) odd that eql doesn't check types careflly (@substack)
- [fd854b0](https://github.com/bcoe/yargs/commit/fd854b02e512ce854b76386d395672a7969c1bc4) package author + keywords (@substack)
- [656a1d5](https://github.com/bcoe/yargs/commit/656a1d5a1b7c0e49d72e80cb13f20671d56f76c6) updated readme with .default() stuff (@substack)
- [cd7f8c5](https://github.com/bcoe/yargs/commit/cd7f8c55f0b82b79b690d14c5f806851236998a1) passing tests for new .default() behavior (@substack)
- [932725e](https://github.com/bcoe/yargs/commit/932725e39ce65bc91a0385a5fab659a5fa976ac2) new default() thing for setting default key/values (@substack)
- [4e6c7ab](https://github.com/bcoe/yargs/commit/4e6c7aba6374ac9ebc6259ecf91f13af7bce40e3) test for coffee usage (@substack)
- [d54ffcc](https://github.com/bcoe/yargs/commit/d54ffccf2a5a905f51ed5108f7c647f35d64ae23) new --key value style with passing tests. NOTE: changes existing behavior (@substack)
- [ed2a2d5](https://github.com/bcoe/yargs/commit/ed2a2d5d828100ebeef6385c0fb88d146a5cfe9b) package bump for summatix's coffee script fix (@substack)
- [75a975e](https://github.com/bcoe/yargs/commit/75a975eed8430d28e2a79dc9e6d819ad545f4587) Added support for CoffeeScript (@summatix)
- [56b2b1d](https://github.com/bcoe/yargs/commit/56b2b1de8d11f8a2b91979d8ae2d6db02d8fe64d) test coverage for the falsy check() usage (@substack)
- [a4843a9](https://github.com/bcoe/yargs/commit/a4843a9f0e69ffb4afdf6a671d89eb6f218be35d) check bug fixed plus a handy string (@substack)
- [857bd2d](https://github.com/bcoe/yargs/commit/857bd2db933a5aaa9cfecba0ced2dc9b415f8111) tests for demandCount, back up to 100% coverage (@substack)
- [073b776](https://github.com/bcoe/yargs/commit/073b7768ebd781668ef05c13f9003aceca2f5c35) call demandCount from demand (@substack)
- [4bd4b7a](https://github.com/bcoe/yargs/commit/4bd4b7a085c8b6ce1d885a0f486cc9865cee2db1) add demandCount to check for the number of arguments in the _ list (@marshall)
- [b8689ac](https://github.com/bcoe/yargs/commit/b8689ac68dacf248119d242bba39a41cb0adfa07) Rebase checks. That will be its own module eventually. (@substack)
- [e688370](https://github.com/bcoe/yargs/commit/e688370b576f0aa733c3f46183df69e1b561668e) a $0 like in perl (@substack)
- [2e5e196](https://github.com/bcoe/yargs/commit/2e5e1960fc19afb21fb3293752316eaa8bcd3609) usage test hacking around process and console (@substack)
- [fcc3521](https://github.com/bcoe/yargs/commit/fcc352163fbec6a1dfe8caf47a0df39de24fe016) description pun (@substack)
- [87a1fe2](https://github.com/bcoe/yargs/commit/87a1fe29037ca2ca5fefda85141aaeb13e8ce761) mit/x11 license (@substack)
- [8d089d2](https://github.com/bcoe/yargs/commit/8d089d24cd687c0bde3640a96c09b78f884900dd) bool example is more consistent and also shows off short option grouping (@substack)
- [448d747](https://github.com/bcoe/yargs/commit/448d7473ac68e8e03d8befc9457b0d9e21725be0) start of the readme and examples (@substack)
- [da74dea](https://github.com/bcoe/yargs/commit/da74dea799a9b59dbf022cbb8001bfdb0d52eec9) more tests for long and short captures (@substack)
- [ab6387e](https://github.com/bcoe/yargs/commit/ab6387e6769ca4af82ca94c4c67c7319f0d9fcfa) silly bug in the tests with s/not/no/, all tests pass now (@substack)
- [102496a](https://github.com/bcoe/yargs/commit/102496a319e8e06f6550d828fc2f72992c7d9ecc) hack an instance for process.argv onto Argv so the export can be called to create an instance or used for argv, which is the most common case (@substack)
- [a01caeb](https://github.com/bcoe/yargs/commit/a01caeb532546d19f68f2b2b87f7036cfe1aaedd) divide example (@substack)
- [443da55](https://github.com/bcoe/yargs/commit/443da55736acbaf8ff8b04d1b9ce19ab016ddda2) start of the lib with a package.json (@substack)

View File

@@ -0,0 +1,21 @@
Copyright 2010 James Halliday (mail@substack.net)
This project is free software released under the MIT/X11 license:
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,926 @@
yargs
========
Yargs be a node.js library fer hearties tryin' ter parse optstrings.
With yargs, ye be havin' a map that leads straight to yer treasure! Treasure of course, being a simple option hash.
[![Build Status](https://travis-ci.org/bcoe/yargs.png)](https://travis-ci.org/bcoe/yargs)
[![Dependency Status](https://gemnasium.com/bcoe/yargs.png)](https://gemnasium.com/bcoe/yargs)
[![Coverage Status](https://coveralls.io/repos/bcoe/yargs/badge.svg?branch=)](https://coveralls.io/r/bcoe/yargs?branch=)
[![NPM version](https://img.shields.io/npm/v/yargs.svg)](https://www.npmjs.com/package/yargs)
> Yargs is the official successor to optimist. Please feel free to submit issues and pull requests. If you'd like to contribute and don't know where to start, have a look at [the issue list](https://github.com/bcoe/yargs/issues) :)
examples
========
With yargs, the options be just a hash!
-------------------------------------------------------------------
plunder.js:
````javascript
#!/usr/bin/env node
var argv = require('yargs').argv;
if (argv.ships > 3 && argv.distance < 53.5) {
console.log('Plunder more riffiwobbles!');
}
else {
console.log('Retreat from the xupptumblers!');
}
````
***
$ ./plunder.js --ships=4 --distance=22
Plunder more riffiwobbles!
$ ./plunder.js --ships 12 --distance 98.7
Retreat from the xupptumblers!
![Joe was one optimistic pirate.](http://i.imgur.com/4WFGVJ9.png)
But don't walk the plank just yet! There be more! You can do short options:
-------------------------------------------------
short.js:
````javascript
#!/usr/bin/env node
var argv = require('yargs').argv;
console.log('(%d,%d)', argv.x, argv.y);
````
***
$ ./short.js -x 10 -y 21
(10,21)
And booleans, both long, short, and even grouped:
----------------------------------
bool.js:
````javascript
#!/usr/bin/env node
var util = require('util');
var argv = require('yargs').argv;
if (argv.s) {
util.print(argv.fr ? 'Le perroquet dit: ' : 'The parrot says: ');
}
console.log(
(argv.fr ? 'couac' : 'squawk') + (argv.p ? '!' : '')
);
````
***
$ ./bool.js -s
The parrot says: squawk
$ ./bool.js -sp
The parrot says: squawk!
$ ./bool.js -sp --fr
Le perroquet dit: couac!
And non-hyphenated options too! Just use `argv._`!
-------------------------------------------------
nonopt.js:
````javascript
#!/usr/bin/env node
var argv = require('yargs').argv;
console.log('(%d,%d)', argv.x, argv.y);
console.log(argv._);
````
***
$ ./nonopt.js -x 6.82 -y 3.35 rum
(6.82,3.35)
[ 'rum' ]
$ ./nonopt.js "me hearties" -x 0.54 yo -y 1.12 ho
(0.54,1.12)
[ 'me hearties', 'yo', 'ho' ]
Yargs even counts your booleans!
----------------------------------------------------------------------
count.js
````javascript
#!/usr/bin/env node
var argv = require('yargs')
.count('verbose')
.alias('v', 'verbose')
.argv;
VERBOSE_LEVEL = argv.verbose;
function WARN() { VERBOSE_LEVEL >= 0 && console.log.apply(console, arguments); }
function INFO() { VERBOSE_LEVEL >= 1 && console.log.apply(console, arguments); }
function DEBUG() { VERBOSE_LEVEL >= 2 && console.log.apply(console, arguments); }
WARN("Showing only important stuff");
INFO("Showing semi-mportant stuff too");
DEBUG("Extra chatty mode");
````
***
$ node count.js
Showing only important stuff
$ node count.js -v
Showing only important stuff
Showing semi-important stuff too
$ node count.js -vv
Showing only important stuff
Showing semi-important stuff too
Extra chatty mode
$ node count.js -v --verbose
Showing only important stuff
Showing semi-important stuff too
Extra chatty mode
Tell users how to use yer options and make demands.
-------------------------------------------------
area.js:
````javascript
#!/usr/bin/env node
var argv = require('yargs')
.usage('Usage: $0 -w [num] -yh[num]')
.demand(['w','h'])
.argv;
console.log("The area is:", argv.w * argv.h);
````
***
$ ./area.js -w 55 -h 11
605
$ node ./area.js -w 4.91 -w 2.51
Usage: node ./area.js -w [num] -h [num]
Options:
-w [required]
-h [required]
Missing required arguments: h
After yer demands have been met, demand more! Ask for non-hypenated arguments!
-----------------------------------------
demand_count.js:
````javascript
#!/usr/bin/env node
var argv = require('yargs')
.demand(2)
.argv;
console.dir(argv)
````
***
$ ./demand_count.js a
Not enough arguments, expected 2, but only found 1
$ ./demand_count.js a b
{ _: [ 'a', 'b' ], '$0': 'node ./demand_count.js' }
$ ./demand_count.js a b c
{ _: [ 'a', 'b', 'c' ], '$0': 'node ./demand_count.js' }
EVEN MORE SHIVER ME TIMBERS!
------------------
default_singles.js:
````javascript
#!/usr/bin/env node
var argv = require('yargs')
.default('x', 10)
.default('y', 10)
.argv
;
console.log(argv.x + argv.y);
````
***
$ ./default_singles.js -x 5
15
default_hash.js:
````javascript
#!/usr/bin/env node
var argv = require('yargs')
.default({ x : 10, y : 10 })
.argv
;
console.log(argv.x + argv.y);
````
***
$ ./default_hash.js -y 7
17
And if you really want to get all descriptive about it...
---------------------------------------------------------
boolean_single.js
````javascript
#!/usr/bin/env node
var argv = require('yargs')
.boolean('v')
.argv
;
console.dir(argv.v);
console.dir(argv._);
````
***
$ ./boolean_single.js -v "me hearties" yo ho
true
[ 'me hearties', 'yo', 'ho' ]
boolean_double.js
````javascript
#!/usr/bin/env node
var argv = require('yargs')
.boolean(['x','y','z'])
.argv
;
console.dir([ argv.x, argv.y, argv.z ]);
console.dir(argv._);
````
***
$ ./boolean_double.js -x -z one two three
[ true, false, true ]
[ 'one', 'two', 'three' ]
Yargs is here to help you...
---------------------------
Ye can describe parameters fer help messages and set aliases. Yargs figures
out how ter format a handy help string automatically.
line_count.js
````javascript
#!/usr/bin/env node
var argv = require('yargs')
.usage('Usage: $0 <command> [options]')
.command('count', 'Count the lines in a file')
.demand(1)
.example('$0 count -f foo.js', 'count the lines in the given file')
.demand('f')
.alias('f', 'file')
.nargs('f', 1)
.describe('f', 'Load a file')
.help('h')
.alias('h', 'help')
.epilog('copyright 2015')
.argv;
var fs = require('fs');
var s = fs.createReadStream(argv.file);
var lines = 0;
s.on('data', function (buf) {
lines += buf.toString().match(/\n/g).length;
});
s.on('end', function () {
console.log(lines);
});
````
***
$ node line_count.js count
Usage: node test.js <command> [options]
Commands:
count Count the lines in a file
Options:
-f, --file Load a file [required]
-h, --help Show help
Examples:
node test.js count -f foo.js count the lines in the given file
copyright 2015
Missing required arguments: f
$ node line_count.js count --file line_count.js
20
$ node line_count.js count -f line_count.js
20
methods
=======
By itself,
````javascript
require('yargs').argv
`````
will use `process.argv` array to construct the `argv` object.
You can pass in the `process.argv` yourself:
````javascript
require('yargs')([ '-x', '1', '-y', '2' ]).argv
````
or use .parse() to do the same thing:
````javascript
require('yargs').parse([ '-x', '1', '-y', '2' ])
````
The rest of these methods below come in just before the terminating `.argv`.
.alias(key, alias)
------------------
Set key names as equivalent such that updates to a key will propagate to aliases
and vice-versa.
Optionally `.alias()` can take an object that maps keys to aliases.
Each key of this object should be the canonical version of the option, and each
value should be a string or an array of strings.
.default(key, value, [description])
--------------------
Set `argv[key]` to `value` if no option was specified on `process.argv`.
Optionally `.default()` can take an object that maps keys to default values.
But wait, there's more! the default value can be a `function` which returns
a value. The name of the function will be used in the usage string:
```js
var argv = require('yargs')
.default('random', function randomValue() {
return Math.random() * 256;
}).argv;
```
Optionally, `description` can also be provided and will take precedence over
displaying the value in the usage instructions:
```js
.default('timeout', 60000, '(one-minute)');
```
.demand(key, [msg | boolean])
-----------------------------
.require(key, [msg | boolean])
------------------------------
.required(key, [msg | boolean])
-------------------------------
If `key` is a string, show the usage information and exit if `key` wasn't
specified in `process.argv`.
If `key` is a number, demand at least as many non-option arguments, which show
up in `argv._`.
If `key` is an Array, demand each element.
If a `msg` string is given, it will be printed when the argument is missing,
instead of the standard error message. This is especially helpful for the non-option arguments in `argv._`.
If a `boolean` value is given, it controls whether the option is demanded;
this is useful when using `.options()` to specify command line parameters.
.requiresArg(key)
-----------------
Specifies either a single option key (string), or an array of options that
must be followed by option values. If any option value is missing, show the
usage information and exit.
The default behaviour is to set the value of any key not followed by an
option value to `true`.
.implies(x, y)
--------------
Given the key `x` is set, it is required that the key `y` is set.
implies can also accept an object specifying multiple implications.
.describe(key, desc)
--------------------
Describe a `key` for the generated usage information.
Optionally `.describe()` can take an object that maps keys to descriptions.
.option(key, opt)
-----------------
.options(key, opt)
------------------
Instead of chaining together `.alias().demand().default().describe().string()`, you can specify
keys in `opt` for each of the chainable methods.
For example:
````javascript
var argv = require('yargs')
.option('f', {
alias : 'file',
demand: true,
default: '/etc/passwd',
describe: 'x marks the spot',
type: 'string'
})
.argv
;
````
is the same as
````javascript
var argv = require('yargs')
.alias('f', 'file')
.default('f', '/etc/passwd')
.argv
;
````
Optionally `.options()` can take an object that maps keys to `opt` parameters.
````javascript
var argv = require('yargs')
.options({
'f': {
alias: 'file',
demand: true,
default: '/etc/passwd',
describe: 'x marks the spot',
type: 'string'
}
})
.argv
;
````
.usage(message, opts)
---------------------
Set a usage message to show which commands to use. Inside `message`, the string
`$0` will get interpolated to the current script name or node command for the
present script similar to how `$0` works in bash or perl.
`opts` is optional and acts like calling `.options(opts)`.
.command(cmd, desc, [fn])
-------------------
Document the commands exposed by your application.
use `desc` to provide a description for each command your application accepts (the
values stored in `argv._`).
Optionally, you can provide a handler `fn` which will be executed when
a given command is provided. The handler will be executed with an instance
of `yargs`, which can be used to compose nested commands.
Here's an example of top-level and nested commands in action:
```js
var argv = require('yargs')
.usage('npm <command>')
.command('install', 'tis a mighty fine package to install')
.command('publish', 'shiver me timbers, should you be sharing all that', function (yargs) {
argv = yargs.option('f', {
alias: 'force',
description: 'yar, it usually be a bad idea'
})
.help('help')
.argv
})
.help('help')
.argv;
```
.example(cmd, desc)
-------------------
Give some example invocations of your program. Inside `cmd`, the string
`$0` will get interpolated to the current script name or node command for the
present script similar to how `$0` works in bash or perl.
Examples will be printed out as part of the help message.
.epilogue(str)
--------------
.epilog(str)
------------
A message to print at the end of the usage instructions, e.g.,
```js
var argv = require('yargs')
.epilogue('for more information, find our manual at http://example.com');
```
.check(fn)
----------
Check that certain conditions are met in the provided arguments.
`fn` is called with two arguments, the parsed `argv` hash and an array of options and their aliases.
If `fn` throws or returns a non-truthy value, show the thrown error, usage information, and
exit.
.fail(fn)
---------
Method to execute when a failure occurs, rather then printing the failure message.
`fn` is called with the failure message that would have been printed.
.boolean(key)
-------------
Interpret `key` as a boolean. If a non-flag option follows `key` in
`process.argv`, that string won't get set as the value of `key`.
`key` will default to `false`, unless an `default(key, undefined)` is
explicitly set.
If `key` is an Array, interpret all the elements as booleans.
.string(key)
------------
Tell the parser logic not to interpret `key` as a number or boolean.
This can be useful if you need to preserve leading zeros in an input.
If `key` is an Array, interpret all the elements as strings.
`.string('_')` will result in non-hyphenated arguments being interpreted as strings,
regardless of whether they resemble numbers.
.array(key)
----------
Tell the parser to interpret `key` as an array. If `.array('foo')` is set,
`--foo foo bar` will be parsed as `['foo', 'bar']` rather than as `'bar'`.
.nargs(key, count)
-----------
The number of arguments that should be consumed after a key. This can be a
useful hint to prevent parsing ambiguity:
```js
var argv = require('yargs')
.nargs('token', 1)
.parse(['--token', '-my-token']);
```
parses as:
`{ _: [], token: '-my-token', '$0': 'node test' }`
Optionally `.nargs()` can take an object of `key`/`narg` pairs.
.config(key)
------------
Tells the parser that if the option specified by `key` is passed in, it
should be interpreted as a path to a JSON config file. The file is loaded
and parsed, and its properties are set as arguments.
.wrap(columns)
--------------
Format usage output to wrap at `columns` many columns.
By default wrap will be set to `Math.min(80, windowWidth)`. Use `.wrap(null)` to
specify no column limit.
`yargs.wrap(yargs.terminalWidth())` can be used to maximize the width
of yargs' usage instructions.
.strict()
---------
Any command-line argument given that is not demanded, or does not have a
corresponding description, will be reported as an error.
.help([option, [description]])
------------------------------
Add an option (e.g., `--help`) that displays the usage string and exits the
process. If present, the `description` parameter customises the description of
the help option in the usage string.
If invoked without parameters, `.help` returns the generated usage string.
Example:
```
var yargs = require("yargs")
.usage("$0 -operand1 number -operand2 number -operation [add|subtract]");
console.log(yargs.help());
```
Later on, ```argv``` can be retrived with ```yargs.argv```
.version(version, [option], [description])
----------------------------------------
Add an option (e.g., `--version`) that displays the version number (given by the
`version` parameter) and exits the process. If present, the `description`
parameter customizes the description of the version option in the usage string.
You can provide a `function` for version, rather than a string.
This is useful if you want to use the version from your package.json:
```js
var argv = require('yargs')
.version(function() {
return require('../package').version;
})
.argv;
```
.showHelpOnFail(enable, [message])
----------------------------------
By default, yargs outputs a usage string if any error is detected. Use the
`.showHelpOnFail` method to customize this behaviour. if `enable` is `false`,
the usage string is not output. If the `message` parameter is present, this
message is output after the error message.
line_count.js
````javascript
#!/usr/bin/env node
var argv = require('yargs')
.usage('Count the lines in a file.\nUsage: $0')
.demand('f')
.alias('f', 'file')
.describe('f', 'Load a file')
.showHelpOnFail(false, "Specify --help for available options")
.argv;
// etc.
````
***
$ node line_count.js --file
Missing argument value: f
Specify --help for available options
.showHelp(consoleLevel='error')
---------------------------
Print the usage data using the [`console`](https://nodejs.org/api/console.html) function `consoleLevel` for printing.
Example:
```
var yargs = require("yargs")
.usage("$0 -operand1 number -operand2 number -operation [add|subtract]");
yargs.showHelp();
```
Or, to print the usage data to `stdout` instead, you can specify the use of `console.log`:
```
yargs.showHelp("log");
```
Later on, ```argv``` can be retrived with ```yargs.argv```
.completion(cmd, [description], [fn]);
-------------
Enable bash-completion shortcuts for commands and options.
`cmd`: when present in `argv._`, will result in the `.bashrc` completion script
being outputted. To enable bash completions, concat the generated script to your
`.bashrc`, or `.bash_profile`.
`description`: provide a description in your usage instructions for the command
that generates bash completion scripts.
`fn`, rather than relying on yargs' default completion functionlity, which
shiver me timbers is pretty awesome, you can provide your own completion
method.
```js
var argv = require('yargs')
.completion('completion', function(current, argv) {
// 'current' is the current command being completed.
// 'argv' is the parsed arguments so far.
// simply return an array of completions.
return [
'foo',
'bar'
];
})
.argv;
```
But wait, there's more! you can provide asynchronous completions.
```js
var argv = require('yargs')
.completion('completion', function(current, argv, done) {
setTimeout(function() {
done([
'apple',
'banana'
]);
}, 500);
})
.argv;
```
.showCompletionScript()
----------------------
Generate a bash completion script. Users of your application can install this
script in their `.bashrc`, and yargs will provide completion shortcuts for
commands and options.
.exitProcess(enable)
----------------------------------
By default, yargs exits the process when the user passes a help flag, uses the `.version` functionality or when validation fails. Calling `.exitProcess(false)` disables this behavior, enabling further actions after yargs have been validated.
.parse(args)
------------
Parse `args` instead of `process.argv`. Returns the `argv` object.
.reset()
--------
Reset the argument object built up so far. This is useful for
creating nested command line interfaces.
```js
var yargs = require('./yargs')
.usage('$0 command')
.command('hello', 'hello command')
.command('world', 'world command')
.demand(1, 'must provide a valid command'),
argv = yargs.argv,
command = argv._[0];
if (command === 'hello') {
yargs.reset()
.usage('$0 hello')
.help('h')
.example('$0 hello', 'print the hello message!')
.argv
console.log('hello!');
} else if (command === 'world'){
yargs.reset()
.usage('$0 world')
.help('h')
.example('$0 world', 'print the world message!')
.argv
console.log('world!');
} else {
yargs.showHelp();
}
```
.argv
-----
Get the arguments as a plain old object.
Arguments without a corresponding flag show up in the `argv._` array.
The script name or node command is available at `argv.$0` similarly to how `$0`
works in bash or perl.
parsing tricks
==============
stop parsing
------------
Use `--` to stop parsing flags and stuff the remainder into `argv._`.
$ node examples/reflect.js -a 1 -b 2 -- -c 3 -d 4
{ _: [ '-c', '3', '-d', '4' ],
'$0': 'node ./examples/reflect.js',
a: 1,
b: 2 }
negate fields
-------------
If you want to explicity set a field to false instead of just leaving it
undefined or to override a default you can do `--no-key`.
$ node examples/reflect.js -a --no-b
{ _: [],
'$0': 'node ./examples/reflect.js',
a: true,
b: false }
numbers
-------
Every argument that looks like a number (`!isNaN(Number(arg))`) is converted to
one. This way you can just `net.createConnection(argv.port)` and you can add
numbers out of `argv` with `+` without having that mean concatenation,
which is super frustrating.
duplicates
----------
If you specify a flag multiple times it will get turned into an array containing
all the values in order.
$ node examples/reflect.js -x 5 -x 8 -x 0
{ _: [],
'$0': 'node ./examples/reflect.js',
x: [ 5, 8, 0 ] }
dot notation
------------
When you use dots (`.`s) in argument names, an implicit object path is assumed.
This lets you organize arguments into nested objects.
$ node examples/reflect.js --foo.bar.baz=33 --foo.quux=5
{ _: [],
'$0': 'node ./examples/reflect.js',
foo: { bar: { baz: 33 }, quux: 5 } }
short numbers
-------------
Short numeric `head -n5` style argument work too:
$ node reflect.js -n123 -m456
{ '3': true,
'6': true,
_: [],
'$0': 'node ./reflect.js',
n: 123,
m: 456 }
installation
============
With [npm](http://github.com/isaacs/npm), just do:
npm install yargs
or clone this project on github:
git clone http://github.com/bcoe/yargs.git
To run the tests with npm, just do:
npm test
inspired by
===========
This module is loosely inspired by Perl's
[Getopt::Casual](http://search.cpan.org/~photo/Getopt-Casual-0.13.1/Casual.pm).

View File

@@ -0,0 +1,22 @@
###-begin-{{app_name}}-completions-###
#
# yargs command completion script
#
# Installation: {{app_path}} completion >> ~/.bashrc
# or {{app_path}} completion >> ~/.bash_profile on OSX.
#
_yargs_completions()
{
local cur_word args type_list
cur_word="${COMP_WORDS[COMP_CWORD]}"
args=$(printf "%s " "${COMP_WORDS[@]}")
# ask yargs to generate completions.
type_list=`{{app_path}} --get-yargs-completions $args`
COMPREPLY=( $(compgen -W "${type_list}" -- ${cur_word}) )
return 0
}
complete -F _yargs_completions {{app_name}}
###-end-{{app_name}}-completions-###

View File

@@ -0,0 +1,504 @@
var assert = require('assert'),
path = require('path'),
Completion = require('./lib/completion'),
Parser = require('./lib/parser'),
Usage = require('./lib/usage'),
Validation = require('./lib/validation')
Argv(process.argv.slice(2))
var exports = module.exports = Argv
function Argv (processArgs, cwd) {
processArgs = processArgs || [] // handle calling yargs().
var self = {}
var completion = null
var usage = null
var validation = null
if (!cwd) cwd = process.cwd()
self.$0 = process.argv
.slice(0, 2)
.map(function (x, i) {
// ignore the node bin, specify this in your
// bin file with #!/usr/bin/env node
if (i === 0 && /\b(node|iojs)$/.test(x)) return
var b = rebase(cwd, x)
return x.match(/^\//) && b.length < x.length
? b : x
})
.join(' ').trim()
if (process.env._ !== undefined && process.argv[1] === process.env._) {
self.$0 = process.env._.replace(
path.dirname(process.execPath) + '/', ''
)
}
var options
self.resetOptions = self.reset = function () {
// put yargs back into its initial
// state, this is useful for creating a
// nested CLI.
options = {
array: [],
boolean: [],
string: [],
narg: {},
key: {},
alias: {},
default: {},
defaultDescription: {},
requiresArg: [],
count: [],
normalize: [],
config: []
}
usage = Usage(self) // handle usage output.
validation = Validation(self, usage) // handle arg validation.
completion = Completion(self, usage)
demanded = {}
exitProcess = true
strict = false
helpOpt = null
versionOpt = null
completionOpt = null
commandHandlers = {}
self.parsed = false
return self
}
self.resetOptions()
self.boolean = function (bools) {
options.boolean.push.apply(options.boolean, [].concat(bools))
return self
}
self.array = function (arrays) {
options.array.push.apply(options.array, [].concat(arrays))
return self
}
self.nargs = function (key, n) {
if (typeof key === 'object') {
Object.keys(key).forEach(function (k) {
self.nargs(k, key[k])
})
} else {
options.narg[key] = n
}
return self
}
self.normalize = function (strings) {
options.normalize.push.apply(options.normalize, [].concat(strings))
return self
}
self.config = function (configs) {
options.config.push.apply(options.config, [].concat(configs))
return self
}
self.example = function (cmd, description) {
usage.example(cmd, description)
return self
}
self.command = function (cmd, description, fn) {
usage.command(cmd, description)
if (fn) commandHandlers[cmd] = fn
return self
}
var commandHandlers = {}
self.getCommandHandlers = function () {
return commandHandlers
}
self.string = function (strings) {
options.string.push.apply(options.string, [].concat(strings))
return self
}
self.default = function (key, value, defaultDescription) {
if (typeof key === 'object') {
Object.keys(key).forEach(function (k) {
self.default(k, key[k])
})
} else {
if (typeof value === 'function') {
defaultDescription = usage.functionDescription(value, defaultDescription)
value = value.call()
}
options.defaultDescription[key] = defaultDescription
options.default[key] = value
}
return self
}
self.alias = function (x, y) {
if (typeof x === 'object') {
Object.keys(x).forEach(function (key) {
self.alias(key, x[key])
})
} else {
options.alias[x] = (options.alias[x] || []).concat(y)
}
return self
}
self.count = function (counts) {
options.count.push.apply(options.count, [].concat(counts))
return self
}
var demanded = {}
self.demand = self.required = self.require = function (keys, msg) {
if (typeof keys === 'number') {
if (!demanded._) demanded._ = { count: 0, msg: null }
demanded._.count += keys
demanded._.msg = msg
} else if (Array.isArray(keys)) {
keys.forEach(function (key) {
self.demand(key, msg)
})
} else {
if (typeof msg === 'string') {
demanded[keys] = { msg: msg }
} else if (msg === true || typeof msg === 'undefined') {
demanded[keys] = { msg: undefined }
}
}
return self
}
self.getDemanded = function () {
return demanded
}
self.requiresArg = function (requiresArgs) {
options.requiresArg.push.apply(options.requiresArg, [].concat(requiresArgs))
return self
}
self.implies = function (key, value) {
validation.implies(key, value)
return self
}
self.usage = function (msg, opts) {
if (!opts && typeof msg === 'object') {
opts = msg
msg = null
}
usage.usage(msg)
if (opts) self.options(opts)
return self
}
self.epilogue = self.epilog = function (msg) {
usage.epilog(msg)
return self
}
self.fail = function (f) {
usage.failFn(f)
return self
}
self.check = function (f) {
validation.check(f)
return self
}
self.defaults = self.default
self.describe = function (key, desc) {
options.key[key] = true
usage.describe(key, desc)
return self
}
self.parse = function (args) {
return parseArgs(args)
}
self.option = self.options = function (key, opt) {
if (typeof key === 'object') {
Object.keys(key).forEach(function (k) {
self.options(k, key[k])
})
} else {
assert(typeof opt === 'object', 'second argument to option must be an object')
options.key[key] = true // track manually set keys.
if (opt.alias) self.alias(key, opt.alias)
var demand = opt.demand || opt.required || opt.require
if (demand) {
self.demand(key, demand)
} if ('default' in opt) {
self.default(key, opt.default)
} if ('nargs' in opt) {
self.nargs(key, opt.nargs)
} if (opt.boolean || opt.type === 'boolean') {
self.boolean(key)
if (opt.alias) self.boolean(opt.alias)
} if (opt.array || opt.type === 'array') {
self.array(key)
if (opt.alias) self.array(opt.alias)
} if (opt.string || opt.type === 'string') {
self.string(key)
if (opt.alias) self.string(opt.alias)
} if (opt.count || opt.type === 'count') {
self.count(key)
}
var desc = opt.describe || opt.description || opt.desc
if (desc) {
self.describe(key, desc)
}
if (opt.requiresArg) {
self.requiresArg(key)
}
}
return self
}
self.getOptions = function () {
return options
}
self.wrap = function (cols) {
usage.wrap(cols)
return self
}
var strict = false
self.strict = function () {
strict = true
return self
}
self.getStrict = function () {
return strict
}
self.showHelp = function (level) {
if (!self.parsed) parseArgs(processArgs) // run parser, if it has not already been executed.
usage.showHelp(level)
return self
}
var versionOpt = null
self.version = function (ver, opt, msg) {
versionOpt = opt || 'version'
usage.version(ver)
self.boolean(versionOpt)
self.describe(versionOpt, msg || 'Show version number')
return self
}
var helpOpt = null
self.addHelpOpt = function (opt, msg) {
helpOpt = opt
self.boolean(opt)
self.describe(opt, msg || 'Show help')
return self
}
self.showHelpOnFail = function (enabled, message) {
usage.showHelpOnFail(enabled, message)
return self
}
var exitProcess = true
self.exitProcess = function (enabled) {
if (typeof enabled !== 'boolean') {
enabled = true
}
exitProcess = enabled
return self
}
self.getExitProcess = function () {
return exitProcess
}
self.help = function () {
if (arguments.length > 0) return self.addHelpOpt.apply(self, arguments)
if (!self.parsed) parseArgs(processArgs) // run parser, if it has not already been executed.
return usage.help()
}
var completionOpt = null,
completionCommand = null
self.completion = function (cmd, desc, fn) {
// a function to execute when generating
// completions can be provided as the second
// or third argument to completion.
if (typeof desc === 'function') {
fn = desc
desc = null
}
// register the completion command.
completionCommand = cmd
completionOpt = completion.completionKey
self.command(completionCommand, desc || 'generate bash completion script')
// a function can be provided
if (fn) completion.registerFunction(fn)
return self
}
self.showCompletionScript = function ($0) {
$0 = $0 || self.$0
console.log(completion.generateCompletionScript($0))
return self
}
self.getUsageInstance = function () {
return usage
}
self.getValidationInstance = function () {
return validation
}
self.terminalWidth = function () {
return require('window-size').width
}
Object.defineProperty(self, 'argv', {
get: function () {
var args = null
try {
args = parseArgs(processArgs)
} catch (err) {
usage.fail(err.message)
}
return args
},
enumerable: true
})
function parseArgs (args) {
var parsed = Parser(args, options),
argv = parsed.argv,
aliases = parsed.aliases
argv.$0 = self.$0
self.parsed = parsed
// generate a completion script for adding to ~/.bashrc.
if (completionCommand && ~argv._.indexOf(completionCommand)) {
self.showCompletionScript()
if (exitProcess) {
process.exit(0)
}
}
// if there's a handler associated with a
// command defer processing to it.
var handlerKeys = Object.keys(self.getCommandHandlers())
for (var i = 0, command; (command = handlerKeys[i]) !== undefined; i++) {
if (~argv._.indexOf(command)) {
self.getCommandHandlers()[command](self.reset())
return self.argv
}
}
Object.keys(argv).forEach(function (key) {
if (key === helpOpt && argv[key]) {
self.showHelp('log')
if (exitProcess) {
process.exit(0)
}
} else if (key === versionOpt && argv[key]) {
usage.showVersion()
if (exitProcess) {
process.exit(0)
}
} else if (key === completionOpt) {
// we allow for asynchronous completions,
// e.g., loading in a list of commands from an API.
completion.getCompletion(function (completions) {
;(completions || []).forEach(function (completion) {
console.log(completion)
})
if (exitProcess) {
process.exit(0)
}
})
return
}
})
validation.nonOptionCount(argv)
validation.missingArgumentValue(argv)
validation.requiredArguments(argv)
if (strict) {
validation.unknownArguments(argv, aliases)
}
validation.customChecks(argv, aliases)
validation.implications(argv)
setPlaceholderKeys(argv)
return argv
}
function setPlaceholderKeys (argv) {
Object.keys(options.key).forEach(function (key) {
if (typeof argv[key] === 'undefined') argv[key] = undefined
})
}
sigletonify(self)
return self
}
// rebase an absolute path to a relative one with respect to a base directory
// exported for tests
exports.rebase = rebase
function rebase (base, dir) {
return path.relative(base, dir)
}
/* Hack an instance of Argv with process.argv into Argv
so people can do
require('yargs')(['--beeble=1','-z','zizzle']).argv
to parse a list of args and
require('yargs').argv
to get a parsed version of process.argv.
*/
function sigletonify (inst) {
Object.keys(inst).forEach(function (key) {
if (key === 'argv') {
Argv.__defineGetter__(key, inst.__lookupGetter__(key))
} else {
Argv[key] = typeof inst[key] === 'function'
? inst[key].bind(inst)
: inst[key]
}
})
}

View File

@@ -0,0 +1,71 @@
var fs = require('fs'),
path = require('path')
// add bash completions to your
// yargs-powered applications.
module.exports = function (yargs, usage) {
var self = {
completionKey: 'get-yargs-completions'
}
// get a list of completion commands.
self.getCompletion = function (done) {
var completions = [],
current = process.argv[process.argv.length - 1],
previous = process.argv.slice(process.argv.indexOf('--' + self.completionKey) + 1),
argv = yargs.parse(previous)
// a custom completion function can be provided
// to completion().
if (completionFunction) {
if (completionFunction.length < 3) {
// synchronous completion function.
return done(completionFunction(current, argv))
} else {
// asynchronous completion function
return completionFunction(current, argv, function (completions) {
done(completions)
})
}
}
if (!current.match(/^-/)) {
usage.getCommands().forEach(function (command) {
completions.push(command[0])
})
}
if (current.match(/^-/)) {
Object.keys(yargs.getOptions().key).forEach(function (key) {
completions.push('--' + key)
})
}
done(completions)
}
// generate the completion script to add to your .bashrc.
self.generateCompletionScript = function ($0) {
var script = fs.readFileSync(
path.resolve(__dirname, '../completion.sh.hbs'),
'utf-8'
),
name = path.basename($0)
// add ./to applications not yet installed as bin.
if ($0.match(/\.js$/)) $0 = './' + $0
script = script.replace(/{{app_name}}/g, name)
return script.replace(/{{app_path}}/g, $0)
}
// register a function to perform your own custom
// completions., this function can be either
// synchrnous or asynchronous.
var completionFunction = null
self.registerFunction = function (fn) {
completionFunction = fn
}
return self
}

View File

@@ -0,0 +1,448 @@
// fancy-pants parsing of argv, originally forked
// from minimist: https://www.npmjs.com/package/minimist
var camelCase = require('camelcase'),
path = require('path')
function increment (orig) {
return orig !== undefined ? orig + 1 : 0
}
module.exports = function (args, opts) {
if (!opts) opts = {}
var flags = { arrays: {}, bools: {}, strings: {}, counts: {}, normalize: {}, configs: {} }
;[].concat(opts['array']).filter(Boolean).forEach(function (key) {
flags.arrays[key] = true
})
;[].concat(opts['boolean']).filter(Boolean).forEach(function (key) {
flags.bools[key] = true
})
;[].concat(opts.string).filter(Boolean).forEach(function (key) {
flags.strings[key] = true
})
;[].concat(opts.count).filter(Boolean).forEach(function (key) {
flags.counts[key] = true
})
;[].concat(opts.normalize).filter(Boolean).forEach(function (key) {
flags.normalize[key] = true
})
;[].concat(opts.config).filter(Boolean).forEach(function (key) {
flags.configs[key] = true
})
var aliases = {},
newAliases = {}
extendAliases(opts.key)
extendAliases(opts.alias)
var defaults = opts['default'] || {}
Object.keys(defaults).forEach(function (key) {
if (/-/.test(key) && !opts.alias[key]) {
aliases[key] = aliases[key] || []
}
(aliases[key] || []).forEach(function (alias) {
defaults[alias] = defaults[key]
})
})
var argv = { _: [] }
Object.keys(flags.bools).forEach(function (key) {
setArg(key, !(key in defaults) ? false : defaults[key])
})
var notFlags = []
if (args.indexOf('--') !== -1) {
notFlags = args.slice(args.indexOf('--') + 1)
args = args.slice(0, args.indexOf('--'))
}
for (var i = 0; i < args.length; i++) {
var arg = args[i],
broken,
key,
letters,
m,
next,
value
// -- seperated by =
if (arg.match(/^--.+=/)) {
// Using [\s\S] instead of . because js doesn't support the
// 'dotall' regex modifier. See:
// http://stackoverflow.com/a/1068308/13216
m = arg.match(/^--([^=]+)=([\s\S]*)$/)
// nargs format = '--f=monkey washing cat'
if (checkAllAliases(m[1], opts.narg)) {
args.splice(i + 1, m[1], m[2])
i = eatNargs(i, m[1], args)
// arrays format = '--f=a b c'
} else if (checkAllAliases(m[1], flags.arrays) && args.length > i + 1) {
args.splice(i + 1, m[1], m[2])
i = eatArray(i, m[1], args)
} else {
setArg(m[1], m[2])
}
} else if (arg.match(/^--no-.+/)) {
key = arg.match(/^--no-(.+)/)[1]
setArg(key, false)
// -- seperated by space.
} else if (arg.match(/^--.+/)) {
key = arg.match(/^--(.+)/)[1]
// nargs format = '--foo a b c'
if (checkAllAliases(key, opts.narg)) {
i = eatNargs(i, key, args)
// array format = '--foo a b c'
} else if (checkAllAliases(key, flags.arrays) && args.length > i + 1) {
i = eatArray(i, key, args)
} else {
next = args[i + 1]
if (next !== undefined && !next.match(/^-/)
&& !checkAllAliases(key, flags.bools)
&& !checkAllAliases(key, flags.counts)) {
setArg(key, next)
i++
} else if (/^(true|false)$/.test(next)) {
setArg(key, next)
i++
} else {
setArg(key, defaultForType(guessType(key, flags)))
}
}
// dot-notation flag seperated by '='.
} else if (arg.match(/^-.\..+=/)) {
m = arg.match(/^-([^=]+)=([\s\S]*)$/)
setArg(m[1], m[2])
// dot-notation flag seperated by space.
} else if (arg.match(/^-.\..+/)) {
next = args[i + 1]
key = arg.match(/^-(.\..+)/)[1]
if (next !== undefined && !next.match(/^-/)
&& !checkAllAliases(key, flags.bools)
&& !checkAllAliases(key, flags.counts)) {
setArg(key, next)
i++
} else {
setArg(key, defaultForType(guessType(key, flags)))
}
} else if (arg.match(/^-[^-]+/)) {
letters = arg.slice(1, -1).split('')
broken = false
for (var j = 0; j < letters.length; j++) {
next = arg.slice(j + 2)
if (letters[j + 1] && letters[j + 1] === '=') {
value = arg.slice(j + 3)
key = letters[j]
// nargs format = '-f=monkey washing cat'
if (checkAllAliases(letters[j], opts.narg)) {
args.splice(i + 1, 0, value)
i = eatNargs(i, key, args)
// array format = '-f=a b c'
} else if (checkAllAliases(key, flags.arrays) && args.length > i + 1) {
args.splice(i + 1, 0, value)
i = eatArray(i, key, args)
} else {
setArg(key, value)
}
broken = true
break
}
if (next === '-') {
setArg(letters[j], next)
continue
}
if (/[A-Za-z]/.test(letters[j])
&& /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
setArg(letters[j], next)
broken = true
break
}
if (letters[j + 1] && letters[j + 1].match(/\W/)) {
setArg(letters[j], arg.slice(j + 2))
broken = true
break
} else {
setArg(letters[j], defaultForType(guessType(letters[j], flags)))
}
}
key = arg.slice(-1)[0]
if (!broken && key !== '-') {
// nargs format = '-f a b c'
if (checkAllAliases(key, opts.narg)) {
i = eatNargs(i, key, args)
// array format = '-f a b c'
} else if (checkAllAliases(key, flags.arrays) && args.length > i + 1) {
i = eatArray(i, key, args)
} else {
if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1])
&& !checkAllAliases(key, flags.bools)
&& !checkAllAliases(key, flags.counts)) {
setArg(key, args[i + 1])
i++
} else if (args[i + 1] && /true|false/.test(args[i + 1])) {
setArg(key, args[i + 1])
i++
} else {
setArg(key, defaultForType(guessType(key, flags)))
}
}
}
} else {
argv._.push(
flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
)
}
}
setConfig(argv)
applyDefaultsAndAliases(argv, aliases, defaults)
Object.keys(flags.counts).forEach(function (key) {
setArg(key, defaults[key])
})
notFlags.forEach(function (key) {
argv._.push(key)
})
// how many arguments should we consume, based
// on the nargs option?
function eatNargs (i, key, args) {
var toEat = checkAllAliases(key, opts.narg)
if (args.length - (i + 1) < toEat) throw Error('not enough arguments following: ' + key)
for (var ii = i + 1; ii < (toEat + i + 1); ii++) {
setArg(key, args[ii])
}
return (i + toEat)
}
// if an option is an array, eat all non-hyphenated arguments
// following it... YUM!
// e.g., --foo apple banana cat becomes ["apple", "banana", "cat"]
function eatArray (i, key, args) {
for (var ii = i + 1; ii < args.length; ii++) {
if (/^-/.test(args[ii])) break
i = ii
setArg(key, args[ii])
}
return i
}
function setArg (key, val) {
// handle parsing boolean arguments --foo=true --bar false.
if (checkAllAliases(key, flags.bools) || checkAllAliases(key, flags.counts)) {
if (typeof val === 'string') val = val === 'true'
}
if (/-/.test(key) && !(aliases[key] && aliases[key].length)) {
var c = camelCase(key)
aliases[key] = [c]
newAliases[c] = true
}
var value = !checkAllAliases(key, flags.strings) && isNumber(val) ? Number(val) : val
if (checkAllAliases(key, flags.counts)) {
value = increment
}
var splitKey = key.split('.')
setKey(argv, splitKey, value)
;(aliases[splitKey[0]] || []).forEach(function (x) {
x = x.split('.')
// handle populating dot notation for both
// the key and its aliases.
if (splitKey.length > 1) {
var a = [].concat(splitKey)
a.shift() // nuke the old key.
x = x.concat(a)
}
setKey(argv, x, value)
})
var keys = [key].concat(aliases[key] || [])
for (var i = 0, l = keys.length; i < l; i++) {
if (flags.normalize[keys[i]]) {
keys.forEach(function (key) {
argv.__defineSetter__(key, function (v) {
val = path.normalize(v)
})
argv.__defineGetter__(key, function () {
return typeof val === 'string' ?
path.normalize(val) : val
})
})
break
}
}
}
// set args from config.json file, this should be
// applied last so that defaults can be applied.
function setConfig (argv) {
var configLookup = {}
// expand defaults/aliases, in-case any happen to reference
// the config.json file.
applyDefaultsAndAliases(configLookup, aliases, defaults)
Object.keys(flags.configs).forEach(function (configKey) {
var configPath = argv[configKey] || configLookup[configKey]
if (configPath) {
try {
var config = require(path.resolve(process.cwd(), configPath))
Object.keys(config).forEach(function (key) {
// setting arguments via CLI takes precedence over
// values within the config file.
if (argv[key] === undefined) {
delete argv[key]
setArg(key, config[key])
}
})
} catch (ex) {
throw Error('invalid json config file: ' + configPath)
}
}
})
}
function applyDefaultsAndAliases (obj, aliases, defaults) {
Object.keys(defaults).forEach(function (key) {
if (!hasKey(obj, key.split('.'))) {
setKey(obj, key.split('.'), defaults[key])
;(aliases[key] || []).forEach(function (x) {
setKey(obj, x.split('.'), defaults[key])
})
}
})
}
function hasKey (obj, keys) {
var o = obj
keys.slice(0, -1).forEach(function (key) {
o = (o[key] || {})
})
var key = keys[keys.length - 1]
return key in o
}
function setKey (obj, keys, value) {
var o = obj
keys.slice(0, -1).forEach(function (key) {
if (o[key] === undefined) o[key] = {}
o = o[key]
})
var key = keys[keys.length - 1]
if (value === increment) {
o[key] = increment(o[key])
} else if (o[key] === undefined && checkAllAliases(key, flags.arrays)) {
o[key] = Array.isArray(value) ? value : [value]
} else if (o[key] === undefined || typeof o[key] === 'boolean') {
o[key] = value
} else if (Array.isArray(o[key])) {
o[key].push(value)
} else {
o[key] = [ o[key], value ]
}
}
// extend the aliases list with inferred aliases.
function extendAliases (obj) {
Object.keys(obj || {}).forEach(function (key) {
aliases[key] = [].concat(opts.alias[key] || [])
// For "--option-name", also set argv.optionName
aliases[key].concat(key).forEach(function (x) {
if (/-/.test(x)) {
var c = camelCase(x)
aliases[key].push(c)
newAliases[c] = true
}
})
aliases[key].forEach(function (x) {
aliases[x] = [key].concat(aliases[key].filter(function (y) {
return x !== y
}))
})
})
}
// check if a flag is set for any of a key's aliases.
function checkAllAliases (key, flag) {
var isSet = false,
toCheck = [].concat(aliases[key] || [], key)
toCheck.forEach(function (key) {
if (flag[key]) isSet = flag[key]
})
return isSet
}
// return a default value, given the type of a flag.,
// e.g., key of type 'string' will default to '', rather than 'true'.
function defaultForType (type) {
var def = {
boolean: true,
string: '',
array: []
}
return def[type]
}
// given a flag, enforce a default type.
function guessType (key, flags) {
var type = 'boolean'
if (flags.strings && flags.strings[key]) type = 'string'
else if (flags.arrays && flags.arrays[key]) type = 'array'
return type
}
function isNumber (x) {
if (typeof x === 'number') return true
if (/^0x[0-9a-f]+$/i.test(x)) return true
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x)
}
return {
argv: argv,
aliases: aliases,
newAliases: newAliases
}
}

View File

@@ -0,0 +1,314 @@
// this file handles outputting usage instructions,
// failures, etc. keeps logging in one place.
var cliui = require('cliui'),
decamelize = require('decamelize'),
wsize = require('window-size')
module.exports = function (yargs) {
var self = {}
// methods for ouputting/building failure message.
var fails = []
self.failFn = function (f) {
fails.push(f)
}
var failMessage = null
var showHelpOnFail = true
self.showHelpOnFail = function (enabled, message) {
if (typeof enabled === 'string') {
message = enabled
enabled = true
} else if (typeof enabled === 'undefined') {
enabled = true
}
failMessage = message
showHelpOnFail = enabled
return self
}
self.fail = function (msg) {
if (fails.length) {
fails.forEach(function (f) {
f(msg)
})
} else {
if (showHelpOnFail) yargs.showHelp('error')
if (msg) console.error(msg)
if (failMessage) {
if (msg) console.error('')
console.error(failMessage)
}
if (yargs.getExitProcess()) {
process.exit(1)
} else {
throw new Error(msg)
}
}
}
// methods for ouputting/building help (usage) message.
var usage
self.usage = function (msg) {
usage = msg
}
var examples = []
self.example = function (cmd, description) {
examples.push([cmd, description || ''])
}
var commands = []
self.command = function (cmd, description) {
commands.push([cmd, description || ''])
}
self.getCommands = function () {
return commands
}
var descriptions = {}
self.describe = function (key, desc) {
if (typeof key === 'object') {
Object.keys(key).forEach(function (k) {
self.describe(k, key[k])
})
} else {
descriptions[key] = desc
}
}
self.getDescriptions = function () {
return descriptions
}
var epilog
self.epilog = function (msg) {
epilog = msg
}
var wrap = windowWidth()
self.wrap = function (cols) {
wrap = cols
}
self.help = function () {
normalizeAliases()
var demanded = yargs.getDemanded(),
options = yargs.getOptions(),
keys = Object.keys(
Object.keys(descriptions)
.concat(Object.keys(demanded))
.concat(Object.keys(options.default))
.reduce(function (acc, key) {
if (key !== '_') acc[key] = true
return acc
}, {})
),
ui = cliui({
width: wrap,
wrap: !!wrap
})
// the usage string.
if (usage) {
var u = usage.replace(/\$0/g, yargs.$0)
ui.div(u + '\n')
}
// your application's commands, i.e., non-option
// arguments populated in '_'.
if (commands.length) {
ui.div('Commands:')
commands.forEach(function (command) {
ui.div(
{text: command[0], padding: [0, 2, 0, 2], width: maxWidth(commands) + 4},
{text: command[1]}
)
})
ui.div()
}
// the options table.
var aliasKeys = (Object.keys(options.alias) || [])
.concat(Object.keys(yargs.parsed.newAliases) || [])
keys = keys.filter(function (key) {
return !yargs.parsed.newAliases[key] && aliasKeys.every(function (alias) {
return (options.alias[alias] || []).indexOf(key) === -1
})
})
var switches = keys.reduce(function (acc, key) {
acc[key] = [ key ].concat(options.alias[key] || [])
.map(function (sw) {
return (sw.length > 1 ? '--' : '-') + sw
})
.join(', ')
return acc
}, {})
if (keys.length) {
ui.div('Options:')
keys.forEach(function (key) {
var kswitch = switches[key]
var desc = descriptions[key] || ''
var type = null
if (~options.boolean.indexOf(key)) type = '[boolean]'
if (~options.count.indexOf(key)) type = '[count]'
if (~options.string.indexOf(key)) type = '[string]'
if (~options.normalize.indexOf(key)) type = '[string]'
if (~options.array.indexOf(key)) type = '[array]'
var extra = [
type,
demanded[key] ? '[required]' : null,
defaultString(options.default[key], options.defaultDescription[key])
].filter(Boolean).join(' ')
ui.span(
{text: kswitch, padding: [0, 2, 0, 2], width: maxWidth(switches) + 4},
desc
)
if (extra) ui.div({text: extra, padding: [0, 0, 0, 2], align: 'right'})
else ui.div()
})
ui.div()
}
// describe some common use-cases for your application.
if (examples.length) {
ui.div('Examples:')
examples.forEach(function (example) {
example[0] = example[0].replace(/\$0/g, yargs.$0)
})
examples.forEach(function (example) {
ui.div(
{text: example[0], padding: [0, 2, 0, 2], width: maxWidth(examples) + 4},
example[1]
)
})
ui.div()
}
// the usage string.
if (epilog) {
var e = epilog.replace(/\$0/g, yargs.$0)
ui.div(e + '\n')
}
return ui.toString()
}
// return the maximum width of a string
// in the left-hand column of a table.
function maxWidth (table) {
var width = 0
// table might be of the form [leftColumn],
// or {key: leftColumn}}
if (!Array.isArray(table)) {
table = Object.keys(table).map(function (key) {
return [table[key]]
})
}
table.forEach(function (v) {
width = Math.max(v[0].length, width)
})
// if we've enabled 'wrap' we should limit
// the max-width of the left-column.
if (wrap) width = Math.min(width, parseInt(wrap * 0.5, 10))
return width
}
// make sure any options set for aliases,
// are copied to the keys being aliased.
function normalizeAliases () {
var options = yargs.getOptions(),
demanded = yargs.getDemanded()
;(Object.keys(options.alias) || []).forEach(function (key) {
options.alias[key].forEach(function (alias) {
// copy descriptions.
if (descriptions[alias]) self.describe(key, descriptions[alias])
// copy demanded.
if (demanded[alias]) yargs.demand(key, demanded[alias].msg)
// type messages.
if (~options.boolean.indexOf(alias)) yargs.boolean(key)
if (~options.count.indexOf(alias)) yargs.count(key)
if (~options.string.indexOf(alias)) yargs.string(key)
if (~options.normalize.indexOf(alias)) yargs.normalize(key)
if (~options.array.indexOf(alias)) yargs.array(key)
})
})
}
self.showHelp = function (level) {
level = level || 'error'
console[level](self.help())
}
self.functionDescription = function (fn, defaultDescription) {
if (defaultDescription) {
return defaultDescription
}
var description = fn.name ? decamelize(fn.name, '-') : 'generated-value'
return ['(', description, ')'].join('')
}
// format the default-value-string displayed in
// the right-hand column.
function defaultString (value, defaultDescription) {
var string = '[default: '
if (value === undefined) return null
if (defaultDescription) {
string += defaultDescription
} else {
switch (typeof value) {
case 'string':
string += JSON.stringify(value)
break
case 'object':
string += JSON.stringify(value)
break
default:
string += value
}
}
return string + ']'
}
// guess the width of the console window, max-width 80.
function windowWidth () {
return wsize.width ? Math.min(80, wsize.width) : null
}
// logic for displaying application version.
var version = null
self.version = function (ver, opt, msg) {
version = ver
}
self.showVersion = function () {
if (typeof version === 'function') console.log(version())
else console.log(version)
}
return self
}

View File

@@ -0,0 +1,196 @@
// validation-type-stuff, missing params,
// bad implications, custom checks.
module.exports = function (yargs, usage) {
var self = {}
// validate appropriate # of non-option
// arguments were provided, i.e., '_'.
self.nonOptionCount = function (argv) {
var demanded = yargs.getDemanded()
if (demanded._ && argv._.length < demanded._.count) {
if (demanded._.msg !== undefined) {
usage.fail(demanded._.msg)
} else {
usage.fail('Not enough non-option arguments: got '
+ argv._.length + ', need at least ' + demanded._.count
)
}
}
}
// make sure that any args that require an
// value (--foo=bar), have a value.
self.missingArgumentValue = function (argv) {
var options = yargs.getOptions(),
defaultValues = [true, false, '']
if (options.requiresArg.length > 0) {
var missingRequiredArgs = []
options.requiresArg.forEach(function (key) {
var value = argv[key]
// if a value is explicitly requested,
// flag argument as missing if it does not
// look like foo=bar was entered.
if (~defaultValues.indexOf(value)
|| (Array.isArray(value) && !value.length)) {
missingRequiredArgs.push(key)
}
})
if (missingRequiredArgs.length === 1) {
usage.fail('Missing argument value: ' + missingRequiredArgs[0])
} else if (missingRequiredArgs.length > 1) {
var message = 'Missing argument values: ' + missingRequiredArgs.join(', ')
usage.fail(message)
}
}
}
// make sure all the required arguments are present.
self.requiredArguments = function (argv) {
var demanded = yargs.getDemanded(),
missing = null
Object.keys(demanded).forEach(function (key) {
if (!argv.hasOwnProperty(key)) {
missing = missing || {}
missing[key] = demanded[key]
}
})
if (missing) {
var customMsgs = []
Object.keys(missing).forEach(function (key) {
var msg = missing[key].msg
if (msg && customMsgs.indexOf(msg) < 0) {
customMsgs.push(msg)
}
})
var customMsg = customMsgs.length ? '\n' + customMsgs.join('\n') : ''
usage.fail('Missing required arguments: ' + Object.keys(missing).join(', ') + customMsg)
}
}
// check for unknown arguments (strict-mode).
self.unknownArguments = function (argv, aliases) {
var descriptions = usage.getDescriptions(),
demanded = yargs.getDemanded(),
unknown = [],
aliasLookup = {}
Object.keys(aliases).forEach(function (key) {
aliases[key].forEach(function (alias) {
aliasLookup[alias] = key
})
})
Object.keys(argv).forEach(function (key) {
if (key !== '$0' && key !== '_' &&
!descriptions.hasOwnProperty(key) &&
!demanded.hasOwnProperty(key) &&
!aliasLookup.hasOwnProperty(key)) {
unknown.push(key)
}
})
if (unknown.length === 1) {
usage.fail('Unknown argument: ' + unknown[0])
} else if (unknown.length > 1) {
usage.fail('Unknown arguments: ' + unknown.join(', '))
}
}
// custom checks, added using the `check` option on yargs.
var checks = []
self.check = function (f) {
checks.push(f)
}
self.customChecks = function (argv, aliases) {
checks.forEach(function (f) {
try {
var result = f(argv, aliases)
if (!result) {
usage.fail('Argument check failed: ' + f.toString())
} else if (typeof result === 'string') {
usage.fail(result)
}
} catch (err) {
usage.fail(err.message ? err.message : err)
}
})
}
// check implications, argument foo implies => argument bar.
var implied = {}
self.implies = function (key, value) {
if (typeof key === 'object') {
Object.keys(key).forEach(function (k) {
self.implies(k, key[k])
})
} else {
implied[key] = value
}
}
self.getImplied = function () {
return implied
}
self.implications = function (argv) {
var implyFail = []
Object.keys(implied).forEach(function (key) {
var num,
origKey = key,
value = implied[key]
// convert string '1' to number 1
num = Number(key)
key = isNaN(num) ? key : num
if (typeof key === 'number') {
// check length of argv._
key = argv._.length >= key
} else if (key.match(/^--no-.+/)) {
// check if key doesn't exist
key = key.match(/^--no-(.+)/)[1]
key = !argv[key]
} else {
// check if key exists
key = argv[key]
}
num = Number(value)
value = isNaN(num) ? value : num
if (typeof value === 'number') {
value = argv._.length >= value
} else if (value.match(/^--no-.+/)) {
value = value.match(/^--no-(.+)/)[1]
value = !argv[value]
} else {
value = argv[value]
}
if (key && !value) {
implyFail.push(origKey)
}
})
if (implyFail.length) {
var msg = 'Implications failed:\n'
implyFail.forEach(function (key) {
msg += (' ' + key + ' -> ' + implied[key])
})
usage.fail(msg)
}
}
return self
}

View File

@@ -0,0 +1,117 @@
{
"_args": [
[
"yargs@3.10.0",
"C:\\Users\\deranjer\\GoglandProjects\\torrent-project\\torrent-project"
]
],
"_from": "yargs@3.10.0",
"_id": "yargs@3.10.0",
"_inBundle": false,
"_integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
"_location": "/webpack/uglify-js/yargs",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "yargs@3.10.0",
"name": "yargs",
"escapedName": "yargs",
"rawSpec": "3.10.0",
"saveSpec": null,
"fetchSpec": "3.10.0"
},
"_requiredBy": [
"/webpack/uglify-js"
],
"_resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
"_spec": "3.10.0",
"_where": "C:\\Users\\deranjer\\GoglandProjects\\torrent-project\\torrent-project",
"author": {
"name": "Alex Ford",
"email": "Alex.Ford@CodeTunnel.com",
"url": "http://CodeTunnel.com"
},
"bugs": {
"url": "https://github.com/bcoe/yargs/issues"
},
"contributors": [
{
"name": "Benjamin Coe",
"email": "ben@npmjs.com",
"url": "https://github.com/bcoe"
},
{
"name": "Chris Needham",
"email": "chris@chrisneedham.com",
"url": "http://chrisneedham.com"
},
{
"name": "James Nylen",
"email": "jnylen@gmail.com",
"url": "https://github.com/nylen"
},
{
"name": "Benjamin Horsleben",
"url": "https://github.com/fizker"
},
{
"name": "Lin Clark",
"url": "https://github.com/linclark"
}
],
"dependencies": {
"camelcase": "^1.0.2",
"cliui": "^2.1.0",
"decamelize": "^1.0.0",
"window-size": "0.1.0"
},
"description": "Light-weight option parsing with an argv hash. No optstrings attached.",
"devDependencies": {
"chai": "^2.2.0",
"coveralls": "^2.11.2",
"hashish": "0.0.4",
"mocha": "^2.2.1",
"nyc": "^2.2.1",
"standard": "^3.11.1"
},
"engine": {
"node": ">=0.4"
},
"files": [
"index.js",
"lib",
"completion.sh.hbs",
"LICENSE"
],
"homepage": "https://github.com/bcoe/yargs#readme",
"keywords": [
"argument",
"args",
"option",
"parser",
"parsing",
"cli",
"command"
],
"license": "MIT",
"main": "./index.js",
"name": "yargs",
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/bcoe/yargs.git"
},
"scripts": {
"coverage": "nyc report --reporter=text-lcov | coveralls",
"test": "standard && nyc mocha --check-leaks && nyc report"
},
"standard": {
"ignore": [
"**/example/**"
],
"globals": [
"it"
]
},
"version": "3.10.0"
}

View File

@@ -0,0 +1,98 @@
{
"_args": [
[
"uglify-js@2.8.29",
"C:\\Users\\deranjer\\GoglandProjects\\torrent-project\\torrent-project"
]
],
"_from": "uglify-js@2.8.29",
"_id": "uglify-js@2.8.29",
"_inBundle": false,
"_integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
"_location": "/webpack/uglify-js",
"_phantomChildren": {
"camelcase": "1.2.1",
"cliui": "2.1.0",
"decamelize": "1.2.0",
"window-size": "0.1.0"
},
"_requested": {
"type": "version",
"registry": true,
"raw": "uglify-js@2.8.29",
"name": "uglify-js",
"escapedName": "uglify-js",
"rawSpec": "2.8.29",
"saveSpec": null,
"fetchSpec": "2.8.29"
},
"_requiredBy": [
"/webpack/uglifyjs-webpack-plugin"
],
"_resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
"_spec": "2.8.29",
"_where": "C:\\Users\\deranjer\\GoglandProjects\\torrent-project\\torrent-project",
"author": {
"name": "Mihai Bazon",
"email": "mihai.bazon@gmail.com",
"url": "http://lisperator.net/"
},
"bin": {
"uglifyjs": "bin/uglifyjs"
},
"browserify": {
"transform": [
"uglify-to-browserify"
]
},
"bugs": {
"url": "https://github.com/mishoo/UglifyJS2/issues"
},
"dependencies": {
"source-map": "~0.5.1",
"uglify-to-browserify": "~1.0.0",
"yargs": "~3.10.0"
},
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"devDependencies": {
"acorn": "~5.0.3",
"mocha": "~2.3.4"
},
"engines": {
"node": ">=0.8.0"
},
"files": [
"bin",
"lib",
"tools",
"LICENSE"
],
"homepage": "http://lisperator.net/uglifyjs",
"keywords": [
"uglify",
"uglify-js",
"minify",
"minifier"
],
"license": "BSD-2-Clause",
"main": "tools/node.js",
"maintainers": [
{
"name": "Mihai Bazon",
"email": "mihai.bazon@gmail.com",
"url": "http://lisperator.net/"
}
],
"name": "uglify-js",
"optionalDependencies": {
"uglify-to-browserify": "~1.0.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/mishoo/UglifyJS2.git"
},
"scripts": {
"test": "node test/run-tests.js"
},
"version": "2.8.29"
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
exports["Compressor"] = Compressor;
exports["DefaultsError"] = DefaultsError;
exports["Dictionary"] = Dictionary;
exports["JS_Parse_Error"] = JS_Parse_Error;
exports["MAP"] = MAP;
exports["OutputStream"] = OutputStream;
exports["SourceMap"] = SourceMap;
exports["TreeTransformer"] = TreeTransformer;
exports["TreeWalker"] = TreeWalker;
exports["base54"] = base54;
exports["defaults"] = defaults;
exports["mangle_properties"] = mangle_properties;
exports["merge"] = merge;
exports["parse"] = parse;
exports["push_uniq"] = push_uniq;
exports["string_template"] = string_template;
exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier;
exports["SymbolDef"] = SymbolDef;

View File

@@ -0,0 +1,320 @@
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
var path = require("path");
var fs = require("fs");
var UglifyJS = exports;
var FILES = UglifyJS.FILES = [
"../lib/utils.js",
"../lib/ast.js",
"../lib/parse.js",
"../lib/transform.js",
"../lib/scope.js",
"../lib/output.js",
"../lib/compress.js",
"../lib/sourcemap.js",
"../lib/mozilla-ast.js",
"../lib/propmangle.js",
"./exports.js",
].map(function(file){
return require.resolve(file);
});
new Function("MOZ_SourceMap", "exports", FILES.map(function(file){
return fs.readFileSync(file, "utf8");
}).join("\n\n"))(
require("source-map"),
UglifyJS
);
UglifyJS.AST_Node.warn_function = function(txt) {
console.error("WARN: %s", txt);
};
function read_source_map(code) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code);
if (!match) {
UglifyJS.AST_Node.warn("inline source map not found");
return null;
}
return JSON.parse(new Buffer(match[2], "base64"));
}
UglifyJS.minify = function(files, options) {
options = UglifyJS.defaults(options, {
compress : {},
fromString : false,
inSourceMap : null,
mangle : {},
mangleProperties : false,
nameCache : null,
outFileName : null,
output : null,
outSourceMap : null,
parse : {},
sourceMapInline : false,
sourceMapUrl : null,
sourceRoot : null,
spidermonkey : false,
warnings : false,
});
UglifyJS.base54.reset();
var inMap = options.inSourceMap;
if (typeof inMap == "string" && inMap != "inline") {
inMap = JSON.parse(fs.readFileSync(inMap, "utf8"));
}
// 1. parse
var toplevel = null,
sourcesContent = {};
if (options.spidermonkey) {
if (inMap == "inline") {
throw new Error("inline source map only works with built-in parser");
}
toplevel = UglifyJS.AST_Node.from_mozilla_ast(files);
} else {
function addFile(file, fileUrl) {
var code = options.fromString
? file
: fs.readFileSync(file, "utf8");
if (inMap == "inline") {
inMap = read_source_map(code);
}
sourcesContent[fileUrl] = code;
toplevel = UglifyJS.parse(code, {
filename: fileUrl,
toplevel: toplevel,
bare_returns: options.parse ? options.parse.bare_returns : undefined
});
}
if (!options.fromString) {
files = UglifyJS.simple_glob(files);
if (inMap == "inline" && files.length > 1) {
throw new Error("inline source map only works with singular input");
}
}
[].concat(files).forEach(function (files, i) {
if (typeof files === 'string') {
addFile(files, options.fromString ? i : files);
} else {
for (var fileUrl in files) {
addFile(files[fileUrl], fileUrl);
}
}
});
}
if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll);
}
// 2. compress
if (options.compress) {
var compress = { warnings: options.warnings };
UglifyJS.merge(compress, options.compress);
toplevel.figure_out_scope(options.mangle);
var sq = UglifyJS.Compressor(compress);
toplevel = sq.compress(toplevel);
}
// 3. mangle properties
if (options.mangleProperties || options.nameCache) {
options.mangleProperties.cache = UglifyJS.readNameCache(options.nameCache, "props");
toplevel = UglifyJS.mangle_properties(toplevel, options.mangleProperties);
UglifyJS.writeNameCache(options.nameCache, "props", options.mangleProperties.cache);
}
// 4. mangle
if (options.mangle) {
toplevel.figure_out_scope(options.mangle);
toplevel.compute_char_frequency(options.mangle);
toplevel.mangle_names(options.mangle);
}
// 5. output
var output = { max_line_len: 32000 };
if (options.outSourceMap || options.sourceMapInline) {
output.source_map = UglifyJS.SourceMap({
// prefer outFileName, otherwise use outSourceMap without .map suffix
file: options.outFileName || (typeof options.outSourceMap === 'string' ? options.outSourceMap.replace(/\.map$/i, '') : null),
orig: inMap,
root: options.sourceRoot
});
if (options.sourceMapIncludeSources) {
for (var file in sourcesContent) {
if (sourcesContent.hasOwnProperty(file)) {
output.source_map.get().setSourceContent(file, sourcesContent[file]);
}
}
}
}
if (options.output) {
UglifyJS.merge(output, options.output);
}
var stream = UglifyJS.OutputStream(output);
toplevel.print(stream);
var source_map = output.source_map;
if (source_map) {
source_map = source_map + "";
}
var mappingUrlPrefix = "\n//# sourceMappingURL=";
if (options.sourceMapInline) {
stream += mappingUrlPrefix + "data:application/json;charset=utf-8;base64," + new Buffer(source_map).toString("base64");
} else if (options.outSourceMap && typeof options.outSourceMap === "string" && options.sourceMapUrl !== false) {
stream += mappingUrlPrefix + (typeof options.sourceMapUrl === "string" ? options.sourceMapUrl : options.outSourceMap);
}
return {
code : stream + "",
map : source_map
};
};
// UglifyJS.describe_ast = function() {
// function doitem(ctor) {
// var sub = {};
// ctor.SUBCLASSES.forEach(function(ctor){
// sub[ctor.TYPE] = doitem(ctor);
// });
// var ret = {};
// if (ctor.SELF_PROPS.length > 0) ret.props = ctor.SELF_PROPS;
// if (ctor.SUBCLASSES.length > 0) ret.sub = sub;
// return ret;
// }
// return doitem(UglifyJS.AST_Node).sub;
// }
UglifyJS.describe_ast = function() {
var out = UglifyJS.OutputStream({ beautify: true });
function doitem(ctor) {
out.print("AST_" + ctor.TYPE);
var props = ctor.SELF_PROPS.filter(function(prop){
return !/^\$/.test(prop);
});
if (props.length > 0) {
out.space();
out.with_parens(function(){
props.forEach(function(prop, i){
if (i) out.space();
out.print(prop);
});
});
}
if (ctor.documentation) {
out.space();
out.print_string(ctor.documentation);
}
if (ctor.SUBCLASSES.length > 0) {
out.space();
out.with_block(function(){
ctor.SUBCLASSES.forEach(function(ctor, i){
out.indent();
doitem(ctor);
out.newline();
});
});
}
};
doitem(UglifyJS.AST_Node);
return out + "";
};
function readReservedFile(filename, reserved) {
if (!reserved) {
reserved = { vars: [], props: [] };
}
var data = fs.readFileSync(filename, "utf8");
data = JSON.parse(data);
if (data.vars) {
data.vars.forEach(function(name){
UglifyJS.push_uniq(reserved.vars, name);
});
}
if (data.props) {
data.props.forEach(function(name){
UglifyJS.push_uniq(reserved.props, name);
});
}
return reserved;
}
UglifyJS.readReservedFile = readReservedFile;
UglifyJS.readDefaultReservedFile = function(reserved) {
return readReservedFile(require.resolve("./domprops.json"), reserved);
};
UglifyJS.readNameCache = function(filename, key) {
var cache = null;
if (filename) {
try {
var cache = fs.readFileSync(filename, "utf8");
cache = JSON.parse(cache)[key];
if (!cache) throw "init";
cache.props = UglifyJS.Dictionary.fromObject(cache.props);
} catch(ex) {
cache = {
cname: -1,
props: new UglifyJS.Dictionary()
};
}
}
return cache;
};
UglifyJS.writeNameCache = function(filename, key, cache) {
if (filename) {
var data;
try {
data = fs.readFileSync(filename, "utf8");
data = JSON.parse(data);
} catch(ex) {
data = {};
}
data[key] = {
cname: cache.cname,
props: cache.props.toObject()
};
fs.writeFileSync(filename, JSON.stringify(data, null, 2), "utf8");
}
};
// A file glob function that only supports "*" and "?" wildcards in the basename.
// Example: "foo/bar/*baz??.*.js"
// Argument `glob` may be a string or an array of strings.
// Returns an array of strings. Garbage in, garbage out.
UglifyJS.simple_glob = function simple_glob(glob) {
if (Array.isArray(glob)) {
return [].concat.apply([], glob.map(simple_glob));
}
if (glob.match(/\*|\?/)) {
var dir = path.dirname(glob);
try {
var entries = fs.readdirSync(dir);
} catch (ex) {}
if (entries) {
var pattern = "^" + path.basename(glob)
.replace(/[.+^$[\]\\(){}]/g, "\\$&")
.replace(/\*/g, "[^/\\\\]*")
.replace(/\?/g, "[^/\\\\]") + "$";
var mod = process.platform === "win32" ? "i" : "";
var rx = new RegExp(pattern, mod);
var results = entries.filter(function(name) {
return rx.test(name);
}).map(function(name) {
return path.join(dir, name);
});
if (results.length) return results;
}
}
return [ glob ];
};

View File

@@ -0,0 +1,61 @@
<html>
<head>
</head>
<body>
<script>(function(){
var props = {};
function addObject(obj) {
if (obj == null) return;
try {
Object.getOwnPropertyNames(obj).forEach(add);
} catch(ex) {}
if (obj.prototype) {
Object.getOwnPropertyNames(obj.prototype).forEach(add);
}
if (typeof obj == "function") {
try {
Object.getOwnPropertyNames(new obj).forEach(add);
} catch(ex) {}
}
}
function add(name) {
props[name] = true;
}
Object.getOwnPropertyNames(window).forEach(function(thing){
addObject(window[thing]);
});
try {
addObject(new Event("click"));
addObject(new Event("contextmenu"));
addObject(new Event("mouseup"));
addObject(new Event("mousedown"));
addObject(new Event("keydown"));
addObject(new Event("keypress"));
addObject(new Event("keyup"));
} catch(ex) {}
var ta = document.createElement("textarea");
ta.style.width = "100%";
ta.style.height = "20em";
ta.style.boxSizing = "border-box";
<!-- ta.value = Object.keys(props).sort(cmp).map(function(name){ -->
<!-- return JSON.stringify(name); -->
<!-- }).join(",\n"); -->
ta.value = JSON.stringify({
vars: [],
props: Object.keys(props).sort(cmp)
}, null, 2);
document.body.appendChild(ta);
function cmp(a, b) {
a = a.toLowerCase();
b = b.toLowerCase();
return a < b ? -1 : a > b ? 1 : 0;
}
})();</script>
</body>
</html>