Merge pull request #192 from evocateur/node-package

Moving npm package upstream
This commit is contained in:
Liam Newman 2013-03-19 12:24:46 -07:00
commit f6757d0eb1
8 changed files with 615 additions and 2 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
node_modules
*.pyc
gh-pages

9
.npmignore Normal file
View File

@ -0,0 +1,9 @@
Makefile
attic/
favicon.png
index.html
jquery/
php/
python/
third-party/
unpackers/

131
README.md
View File

@ -8,7 +8,11 @@ JavaScript, unpack scripts packed by Dean Edwards popular packer,
as well as deobfuscate scripts processed by
[javascriptobfuscator.com](http://javascriptobfuscator.com/).
To beautify from the command-line you can use provided python script/library.
## Usage
To beautify from the command-line you can use the provided Python script/library or [npm](http://npmjs.org/) package.
### Python
`./js-beautify file.js` beautifies a file, output goes to `stdout`.
@ -28,8 +32,131 @@ opts.indent_size = 2
res = jsbeautifier.beautify('some javascript', opts)
```
### JavaScript
As an alternative to the Python script, you may install the NPM package `js-beautify`. When installed globally, it provides an executable `js-beautify` script. As with the Python script, the beautified result is sent to `stdout` unless otherwise configured.
```bash
$ npm -g install js-beautify
$ js-beautify foo.js
```
You can also use `js-beautify` as a `node` library (install locally, the `npm` default):
```bash
$ npm install js-beautify
```
```js
var beautify = require('js-beautify').js_beautify,
fs = require('fs');
fs.readFile('foo.js', 'utf8', function (err, data) {
if (err) {
throw err;
}
console.log(beautify(data, { indent_size: 2 }));
});
```
### Options
These are the command-line flags for both Python and JS scripts:
```text
CLI Options:
-f, --file Input file(s) (Pass '-' for stdin). These can also be passed directly.
-r, --replace Write output in-place, replacing input
-o, --outfile Write output to file (default stdout)
--config Path to config file
--type [js|css|html] ["js"]
-q, --quiet Suppress logging to stdout
-v, --version Show the version
-h, --help Show this help
Beautifier Options:
-s, --indent-size Indentation size [4]
-c, --indent-char Indentation character [" "]
-l, --indent-level Initial indentation level [0]
-t, --indent-with-tabs Indent with tabs, overrides -s and -c
-p, --preserve-newlines Preserve existing line-breaks (--no-preserve-newlines disables)
-m, --max-preserve-newlines Maximum number of line-breaks to be preserved in one chunk [10]
-j, --jslint-happy Enable jslint-stricter mode
-b, --brace-style [collapse|expand|end-expand|expand-strict] ["collapse"]
-B, --break-chained-methods Break chained method calls across subsequent lines
-k, --keep-array-indentation Preserve array indentation
-x, --unescape-strings Decode printable characters encoded in xNN notation
-w, --wrap-line-length Wrap lines at next opportunity after N characters [0]
--good-stuff Warm the cockles of Crockford's heart
```
These largely correspond to the underscored option keys for both library interfaces, which have these defaults:
```json
{
"indent_size": 4,
"indent_char": " ",
"indent_level": 0,
"indent_with_tabs": false,
"preserve_newlines": true,
"max_preserve_newlines": 10,
"jslint_happy": false,
"brace_style": "collapse",
"keep_array_indentation": false,
"keep_function_indentation": false,
"space_before_conditional": true,
"break_chained_methods": false,
"eval_code": false,
"unescape_strings": false,
"wrap_line_length": 0
}
```
In addition to CLI arguments, you may pass config to the JS executable via:
* any `jsbeautify_`-prefixed environment variables
* a `JSON`-formatted file indicated by the `--config` parameter
* a `.jsbeautifyrc` file containing `JSON` data at any level of the filesystem above `$PWD`
Configuration sources provided earlier in this stack will override later ones.
You might notice that the CLI options and defaults hash aren't 100% correlated. Historically, the Python and JS APIs have not been 100% identical. For example, `space_before_conditional` is currently JS-only, and not addressable from the CLI script. There are a few other additional cases keeping us from 100% API-compatibility. Patches welcome!
#### CSS & HTML
In addition to the `js-beautify` executable, `css-beautify` and `html-beautify` are also provided as an easy interface into those scripts. Alternatively, `js-beautify --css` or `js-beautify --html` will accomplish the same thing, respectively.
```js
// Programmatic access
var beautify_js = require('js-beautify'); // also available under "js" export
var beautify_css = require('js-beautify').css;
var beautify_html = require('js-beautify').html;
// All methods accept two arguments, the string to be beautified, and an options object.
```
The CSS & HTML beautifiers are much simpler in scope, and possess far fewer options.
```text
CSS Beautifier Options:
-s, --indent-size Indentation size [4]
-c, --indent-char Indentation character [" "]
HTML Beautifier Options:
-s, --indent-size Indentation size [4]
-c, --indent-char Indentation character [" "]
-b, --brace-style [collapse|expand|end-expand] ["collapse"]
-S, --indent-scripts [keep|separate|normal] ["normal"]
-W, --max-char Maximum characters per line (0 disables) [250]
-U, --unformatted List of tags (defaults to inline) that should not be reformatted
```
## License
You are free to use this in any way you want, in case you find this
useful or working for you.
useful or working for you. (MIT)
## Credits
Written by Einar Lielmanis, <einar@jsbeautifier.org>
Python version flourished by Stefano Sanfilippo <a.little.coder@gmail.com>

View File

@ -37,6 +37,11 @@
});
*/
if (typeof process !== 'undefined' && process.argv[0] === "node") {
var css_beautify = require(__dirname + '/beautify-css.js').css_beautify;
var js_beautify = require(__dirname + '/beautify.js').js_beautify;
}
function style_html(html_source, options) {
//Wrapper function to invoke all the necessary constructors and deal with the output.

378
cli.js Executable file
View File

@ -0,0 +1,378 @@
#!/usr/bin/env node
var debug = process.env.DEBUG_JSBEAUTIFY || process.env.JSBEAUTIFY_DEBUG
? function () { console.error.apply(console, arguments); }
: function () {};
var fs = require('fs'),
cc = require('config-chain'),
beautify = require('./index'),
nopt = require('nopt'),
path = require('path'),
knownOpts = {
// Beautifier
"indent_size": Number,
"indent_char": String,
"indent_level": Number,
"indent_with_tabs": Boolean,
"preserve_newlines": Boolean,
"max_preserve_newlines": Number,
"jslint_happy": Boolean,
"brace_style": ["collapse", "expand", "end-expand", "expand-strict"],
"break_chained_methods": Boolean,
"keep_array_indentation": Boolean,
"unescape_strings": Boolean,
"wrap_line_length": Number,
// HTML-only
"max_char": Number,
"unformatted": [String, Array],
"indent_scripts": ["keep", "separate", "normal"],
// CLI
"version": Boolean,
"help": Boolean,
"files": [path, Array],
"outfile": path,
"replace": Boolean,
"quiet": Boolean,
"type": ["js", "css", "html"],
"config": path
},
// dasherizeShorthands provides { "indent-size": ["--indent_size"] }
// translation, allowing more convenient dashes in CLI arguments
shortHands = dasherizeShorthands({
// Beautifier
"s": ["--indent_size"],
"c": ["--indent_char"],
"l": ["--indent_level"],
"t": ["--indent_with_tabs"],
"p": ["--preserve_newlines"],
"m": ["--max_preserve_newlines"],
"j": ["--jslint_happy"],
"b": ["--brace_style"],
"B": ["--break_chained_methods"],
"k": ["--keep_array_indentation"],
"x": ["--unescape_strings"],
"w": ["--wrap_line_length"],
// HTML-only
"W": ["--max_char"],
"U": ["--unformatted"],
"S": ["--indent_scripts"],
// non-dasherized hybrid shortcuts
"good-stuff": [
"--keep_array_indentation",
"--keep_function_indentation",
"--jslint_happy"
],
"js" : ["--type", "js"],
"css" : ["--type", "css"],
"html": ["--type", "html"],
// CLI
"v": ["--version"],
"h": ["--help"],
"f": ["--files"],
"o": ["--outfile"],
"r": ["--replace"],
"q": ["--quiet"]
// no shorthand for "config"
});
// var cli = require('js-beautify/cli'); cli.interpret();
var interpret = exports.interpret = function (argv, slice) {
var parsed = nopt(knownOpts, shortHands, argv, slice);
if (parsed.version) {
console.log(require('./package.json').version);
process.exit(0);
}
else if (parsed.help) {
usage();
process.exit(0);
}
var cfg = cc(
parsed,
cleanOptions(cc.env('jsbeautify_'), knownOpts),
parsed.config,
cc.find('.jsbeautifyrc'),
__dirname + '/config/defaults.json'
).snapshot;
try {
// Verify arguments
checkType(cfg);
checkFiles(cfg);
checkIndent(cfg);
debug(cfg);
// Process files synchronously to avoid EMFILE error
cfg.files.forEach(processInputSync, { cfg: cfg });
}
catch (ex) {
debug(cfg);
// usage(ex);
console.error(ex);
console.error('Run `' + getScriptName() + ' -h` for help.');
process.exit(1);
}
};
// interpret args immediately when called as executable
if (require.main === module) {
interpret();
}
function usage(err) {
var scriptName = getScriptName();
var msg = [
scriptName + '@' + require('./package.json').version,
'',
'CLI Options:',
' -f, --file Input file(s) (Pass \'-\' for stdin)',
' -r, --replace Write output in-place, replacing input',
' -o, --outfile Write output to file (default stdout)',
' --config Path to config file',
' --type [js|css|html] ["js"]',
' -q, --quiet Suppress logging to stdout',
' -h, --help Show this help',
' -v, --version Show the version',
'',
'Beautifier Options:',
' -s, --indent-size Indentation size [4]',
' -c, --indent-char Indentation character [" "]'
];
switch (scriptName.split('-').shift()) {
case "js":
msg.push(' -l, --indent-level Initial indentation level [0]');
msg.push(' -t, --indent-with-tabs Indent with tabs, overrides -s and -c');
msg.push(' -p, --preserve-newlines Preserve line-breaks (--no-preserve-newlines disables)');
msg.push(' -m, --max-preserve-newlines Number of line-breaks to be preserved in one chunk [10]');
msg.push(' -j, --jslint-happy Enable jslint-stricter mode');
msg.push(' -b, --brace-style [collapse|expand|end-expand|expand-strict] ["collapse"]');
msg.push(' -B, --break-chained-methods Break chained method calls across subsequent lines');
msg.push(' -k, --keep-array-indentation Preserve array indentation');
msg.push(' -x, --unescape-strings Decode printable characters encoded in xNN notation');
msg.push(' -w, --wrap-line-length Wrap lines at next opportunity after N characters [0]');
msg.push(' --good-stuff Warm the cockles of Crockford\'s heart');
break;
case "html":
msg.push(' -b, --brace-style [collapse|expand|end-expand] ["collapse"]');
msg.push(' -S, --indent-scripts [keep|separate|normal] ["normal"]');
msg.push(' -W, --max-char Maximum characters per line (0 disables) [250]');
msg.push(' -U, --unformatted List of tags (defaults to inline) that should not be reformatted');
break;
}
if (err) {
msg.push(err);
msg.push('');
console.error(msg.join('\n'));
} else {
console.log(msg.join('\n'));
}
}
// main iterator, {cfg} passed as thisArg of forEach call
function processInputSync(filepath) {
var data = '',
config = this.cfg,
outfile = config.outfile,
input;
// -o passed with no value overwrites
if (outfile === true || config.replace) {
outfile = filepath;
}
if (filepath === '-') {
input = process.stdin;
input.resume();
input.setEncoding('utf8');
input.on('data', function (chunk) {
data += chunk;
});
input.on('end', function () {
makePretty(data, config, outfile, writePretty);
});
}
else {
data = fs.readFileSync(filepath, 'utf8');
makePretty(data, config, outfile, writePretty);
}
}
function makePretty(code, config, outfile, callback) {
try {
var fileType = getOutputType(outfile, config.type);
var pretty = beautify[fileType](code, config);
// ensure newline at end of beautified output
pretty += '\n';
callback(null, pretty, outfile, config);
}
catch (ex) {
callback(ex);
}
}
function writePretty(err, pretty, outfile, config) {
if (err) {
console.error(err);
process.exit(1);
}
if (outfile) {
try {
fs.writeFileSync(outfile, pretty, 'utf8');
logToStdout('beautified ' + path.relative(process.cwd(), outfile), config);
}
catch (ex) {
onOutputError(ex);
}
}
else {
process.stdout.write(pretty);
}
}
// workaround the fact that nopt.clean doesn't return the object passed in :P
function cleanOptions(data, types) {
nopt.clean(data, types);
return data;
}
// error handler for output stream that swallows errors silently,
// allowing the loop to continue over unwritable files.
function onOutputError(err) {
if (err.code === 'EACCES') {
console.error(err.path + " is not writable. Skipping!");
}
else {
console.error(err);
process.exit(0);
}
}
// turn "--foo_bar" into "foo-bar"
function dasherizeFlag(str) {
return str.replace(/^\-+/, '').replace(/_/g, '-');
}
// translate weird python underscored keys into dashed argv,
// avoiding single character aliases.
function dasherizeShorthands(hash) {
// operate in-place
Object.keys(hash).forEach(function (key) {
// each key value is an array
var val = hash[key][0];
// only dasherize one-character shorthands
if (key.length === 1 && val.indexOf('_') > -1) {
hash[dasherizeFlag(val)] = val;
}
});
return hash;
}
function getOutputType(outfile, configType) {
if (outfile && /\.(js|css|html)$/.test(outfile)) {
return outfile.split('.').pop();
}
return configType;
}
function getScriptName() {
return path.basename(process.argv[1]);
}
function checkType(parsed) {
var scriptType = getScriptName().split('-').shift();
debug("executable type:", scriptType);
var parsedType = parsed.type;
debug("parsed type:", parsedType);
if (!parsedType) {
debug("type defaulted:", scriptType);
parsed.type = scriptType;
}
}
function checkIndent(parsed) {
if (parsed["indent_with_tabs"]) {
parsed["indent_size"] = 1;
parsed["indent_char"] = "\t";
}
return parsed;
}
function checkFiles(parsed) {
var argv = parsed.argv;
if (!parsed.files) {
parsed.files = [];
}
else {
if (argv.cooked.indexOf('-') > -1) {
// strip stdin path eagerly added by nopt in '-f -' case
parsed.files.some(removeDashedPath);
}
}
if (argv.remain.length) {
// assume any remaining args are files
argv.remain.forEach(function (f) {
parsed.files.push(path.resolve(f));
});
}
if ('string' === typeof parsed.outfile && !parsed.files.length) {
// use outfile as input when no other files passed in args
parsed.files.push(parsed.outfile);
// operation is now an implicit overwrite
parsed.replace = true;
}
if (argv.original.indexOf('-') > -1) {
// ensure '-' without '-f' still consumes stdin
parsed.files.push('-');
}
if (!parsed.files.length) {
throw 'Must define at least one file.';
}
debug('files.length ' + parsed.files.length);
parsed.files.forEach(testFilePath);
return parsed;
}
function removeDashedPath(filepath, i, arr) {
var found = filepath.lastIndexOf('-') === (filepath.length - 1);
if (found) {
arr.splice(i, 1);
}
return found;
}
function testFilePath(filepath) {
try {
if (filepath !== "-") {
fs.statSync(filepath);
}
}
catch (err) {
throw 'Unable to open path "' + filepath + '"';
}
}
function logToStdout(str, config) {
if (typeof config.quiet === "undefined" || !config.quiet) {
console.log(str);
}
}

17
config/defaults.json Normal file
View File

@ -0,0 +1,17 @@
{
"indent_size": 4,
"indent_char": " ",
"indent_level": 0,
"indent_with_tabs": false,
"preserve_newlines": true,
"max_preserve_newlines": 10,
"jslint_happy": false,
"brace_style": "collapse",
"keep_array_indentation": false,
"keep_function_indentation": false,
"space_before_conditional": true,
"break_chained_methods": false,
"eval_code": false,
"unescape_strings": false,
"wrap_line_length": 0
}

35
index.js Normal file
View File

@ -0,0 +1,35 @@
/**
The following batches are equivalent:
var beautify_js = require('js-beautify');
var beautify_js = require('js-beautify').js;
var beautify_js = require('js-beautify').js_beautify;
var beautify_css = require('js-beautify').css;
var beautify_css = require('js-beautify').css_beautify;
var beautify_html = require('js-beautify').html;
var beautify_html = require('js-beautify').html_beautify;
All methods returned accept two arguments, the source string and an options object.
**/
var js_beautify = require('./beautify').js_beautify;
var css_beautify = require('./beautify-css').css_beautify;
var html_beautify = require('./beautify-html').html_beautify;
// the default is js
var beautify = function (src, config) {
return js_beautify(src, config);
};
// short aliases
beautify.js = js_beautify;
beautify.css = css_beautify;
beautify.html = html_beautify;
// legacy aliases
beautify.js_beautify = js_beautify;
beautify.css_beautify = css_beautify;
beautify.html_beautify = html_beautify;
module.exports = beautify;

41
package.json Normal file
View File

@ -0,0 +1,41 @@
{
"name": "js-beautify",
"version": "0.4.1",
"description": "jsbeautifier.org for node",
"main": "index.js",
"preferGlobal": true,
"bin": {
"css-beautify": "./cli.js",
"html-beautify": "./cli.js",
"js-beautify": "./cli.js"
},
"directories": {
"test": "tests"
},
"scripts": {
"test": "node ./tests/beautify-tests.js"
},
"repository": {
"type": "git",
"url": "git://github.com/evocateur/js-beautify.git#node-package"
},
"keywords": [
"beautify",
"beautifier",
"code-quality"
],
"author": "Einar Lielmanis <einar@jsbeautifier.org>",
"contributors": [
"Vital Batmanov <vital76@gmail.com>",
"Chris J. Shull <chrisjshull@gmail.com>",
"Gian Marco Gherardi <gianmarco.gherardi@gmail.com>",
"Stan <stasson@orc.ru>",
"Vittorio Gambaletta <VittGam@vittgam.net>",
"Daniel Stockman <daniel.stockman@gmail.com>"
],
"license": "MIT",
"dependencies": {
"config-chain": "~1.1.5",
"nopt": "~2.1.1"
}
}