Git Product home page Git Product logo

esformatter's Introduction

esformatter

Build Status

ECMAScript code beautifier/formatter.

Important

This tool is still missing support for many important features. Please report any bugs you find, the code is only as good as the test cases. Feature requests are very welcome.

We are looking for contributors!!

Why?

jsbeautifier.org doesn't have enough options and not all IDEs/Editors have a good JavaScript code formatter. I would like to have a command line tool (and standalone lib) at least as powerful/flexible as the WebStorm and FDT code formatters so that it can be plugged into any editor and reused by other tools like escodegen.

For more reasoning behind it and history of the project see: esformatter & rocambole

How?

This tool uses rocambole and babylon to recursively parse the tokens and transform it in place.

Goals

  • granular control about white spaces, indent and line breaks.
  • command line interface (cli).
  • be non-destructive.
  • support for local/global config file so settings can be shared between team members.
  • be extensive (plugins and other cli tools).
  • support most popular style guides through plugins (Google, jQuery, Idiomatic.js).
  • be the best JavaScript code formatter!

API

var esformatter = require('esformatter');
var fs = require('fs');
var codeStr = fs.readFileSync('path/to/js/file.js').toString();

// for a list of available options check "lib/preset/default.js"
var options = {
  indent : {
    value : '  '
  },
  lineBreak : {
    before : {
      // at least one line break before BlockStatement
      BlockStatement : '>=1',
      // only one line break before DoWhileStatementOpeningBrace
      DoWhileStatementOpeningBrace : 1,
      // ...
    }
  },
  whiteSpace : {
    // ...
  }
};

// return a string with the formatted code
var formattedCode = esformatter.format(codeStr, options);

See the doc/api.md file for a list of all the public methods and detailed documentation about each one.

See doc/config.md for more info about the configuration options.

CLI

You can also use the simple command line interface to process the stdin or read from a file.

npm install [-g] esformatter

Usage:

Pass the --help flag to see the available options or see doc/cli.txt.

esformatter --help

Examples:

# Format
# ======

# format "test.js" and output result to stdout
esformatter test.js
# you can also pipe other shell commands (read file from stdin)
cat test.js | esformatter
# format "test.js" using options in "options.json" and output result to stdout
esformatter --config options.json test.js
# process "test.js" and writes to "test.out.js"
esformatter test.js > test.out.js
# you can override the default settings, see lib/preset/default.js for
# a list of available options
esformatter test.js --indent.value="\t" --lineBreak.before.IfStatementOpeningBrace=0
# format "test.js" and output result to "test.js"
esformatter -i test.js
# format and overwrite all the ".js" files inside the "lib/" folder
esformatter -i 'lib/*.js'
# format and overwrite all the ".js" files inside "lib/" and it's subfolders
esformatter -i 'lib/**/*.js'

# **important:** surround the glob with single quotes to avoid expansion; [glob
# syntax reference](https://github.com/isaacs/node-glob/#glob-primer)

# Diff
# ======

# check if "test.js" matches style and output diff to stdout
esformatter --diff test.js
# check if "test.js" matches style and output unified diff to stdout
esformatter --diff-unified test.js
# check if "test.js" matches "options.json" style and output diff to stdout
esformatter --diff --config options.json test.js
# check all files inside "lib/" and it's subfolders
esformatter --diff 'lib/**/*.js'

Local version

If a locally installed esformatter is found, the CLI uses that instead of the global executable (this means you can have multiple projects depending on different versions of esformatter).

protip: add esformatter and all the plugins that you need on your project to the package.json devDependencies that way you can use locally installed plugins and also make sure everyone on your team is using the same version/settings.

{
  "devDependencies": {
    "esformatter": "~0.6.0",
    "esformatter-quotes": "^1.0.1"
  },
  "esformatter": {
    "plugins": ["esformatter-quotes"],
    "quotes": {
      "type": "single"
    }
  }
}

IDE / Editor integration

Since esformatter is available as a command-line tool, it can be used in any editor that supports external shell commands.

Configuration

See doc/config.md.

Presets

Presets are reusable config files that can require other presets/plugins and override configs.

{
  // presets are used as "base settings"
  "extends": [
    "preset:foobar", // load "esformatter-preset-foobar" from "./node_modules"
    "./lorem_ipsum.json" // load relative config file
  ],

  // you can still override any setting from the preset if needed
  "indent": {
    "value": "  "
  }
}

For more info see presets.md

Pipe other CLI tools

Since we don't expect everyone to write plugins that only works with esformatter we decided to encourage the usage of standalone CLI tools.

{
  // pipe is a simple way to "pipe" multiple binaries input/output
  "pipe": {
    // scripts listed as "before" will be executed before esformatter
    // and will forward output to next command in the queue
    "before": [
      "strip-debug",
      "./bin/my-custom-script.sh --foo true -zx"
    ],
    // scripts listed as "after" will be executed after esformatter
    "after": [
      "baz --keepLineBreaks"
    ]
  }
}

Plugins

Plugins are automatically loaded from node_modules if you pass the module id in the config file:

{
  "plugins": [ "esformatter-sample-plugin", "foobar" ]
}

List of plugins and plugins wish list: https://github.com/millermedeiros/esformatter/wiki/Plugins

List of plugins with easy filterable search: http://pgilad.github.io/esformatter-plugins/

For detailed information about plugins structure and API see doc/plugins.md

IRC

We have an IRC channel #esformatter on irc.freenode.net for quick discussions about the project development/structure.

Wiki

See project Wiki for more info: https://github.com/millermedeiros/esformatter/wiki

Project structure / Contributing

See CONTRIBUTING.md

Popular Alternatives

License

Released under the MIT license

esformatter's People

Contributors

eduardolundgren avatar flet avatar futurist avatar goloroden avatar jzaefferer avatar kl3ryk avatar leftstick avatar leobalter avatar lloiser avatar maxhawkins avatar mbuttu avatar mcandre avatar millermedeiros avatar mwdg avatar nisaacson avatar nmaxcom avatar peterdavehello avatar pgilad avatar raitobezarius avatar rmetzler avatar ruyadorno avatar shinnn avatar sindresorhus avatar soldair avatar stutrek avatar tadeuzagallo avatar tommyminds avatar wbinnssmith avatar webcarrot avatar wesleytodd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

esformatter's Issues

Tests should run formatter on expected result, ensure unchanged

Looking at https://github.com/millermedeiros/esformatter/blob/master/test/format.spec.js at these lines 2432db5#L0R38 , I notice that it does not run the resulting output through the formatter again to ensure consistent results.

Jsbeautifier has seen several bugs where output would flip flop between two states or otherwise change with multiple executions. That project works with incomplete parsing so it is more likely to encounter this problem, but with sufficiently complex rules this class of issue is worth covering.

line breaks inside IfStatement should cause indent

input:

function isPlainObject(){
    // Not own constructor property must be Object
    if ( obj.constructor &&
        !core_hasOwn.call(obj, "constructor") &&
        !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
        return false;
    }
}

output:

function isPlainObject(){
    // Not own constructor property must be Object
    if (obj.constructor &&
!core_hasOwn.call(obj, "constructor") &&
!core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
        return false;
    }
}

I think it should keep the line breaks and just indent each line.

double check parenthesis behavior

expected:

doc = (context && context.nodeType ? context.ownerDocument || context : document);

actual:

doc =( context && context.nodeType ? context.ownerDocument || context : document);

maybe try to handle it automatically inside _ws.beforeIfNeeded.

_br.afterIfNeeded should insert LineBreak after the comment

input:

            if ( window.DOMParser ) { // Standard
                tmp = new DOMParser();
                xml = tmp.parseFromString( data , "text/xml" );
            } else { // IE
                xml = new ActiveXObject( "Microsoft.XMLDOM" );
                xml.async = "false";
                xml.loadXML( data );
            }

output:

                if (window.DOMParser) {
// Standard
                    tmp = new DOMParser();
                    xml = tmp.parseFromString(data, "text/xml");
                } else {
// IE
                    xml = new ActiveXObject("Microsoft.XMLDOM");
                    xml.async = "false";
                    xml.loadXML(data);
                }

I think the best place to do it is on _br.afterIfNeeded() since it will probably fix this issue in multiple places at once.

Two feature requests

Format an AST

At the moment the format function only accepts a string.
It would be nice to support a rocambole AST. In my use case, I use rocambole to fetch the AST with all those extra tokens, modify it, and want be able to format it.

Don't reformat code

Assuming that the feature request above is done, I would like esformatter to be able to take an AST and output the correspondent source code WITHOUT formatting. I don't know if this gets out of the scope of this tool.

Infer options from code sample

There are a lot of rules and not all of them are easy to grasp.

Codepainter has this useful feature of inferring the style from a user supplied code sample. I think that would make esformatter a lot more approachable and easier to use.

Comments and line breaks inside ConditionalExpression

input:

// from jquery
jQuery.prototype = {
    get: function( num ) {
        return num == null ?

            // Return a 'clean' array
            this.toArray() :

            // Return just the object
            ( num < 0 ? this[ this.length + num ] : this[ num ] );
    }
};

output:

// from jquery
jQuery.prototype = {
        get : function(num) {
            return num == null ?             // Return a 'clean' array
this.toArray() :             // Return just the object
(num < 0 ? this[this.length + num] : this[num]);
        }
};

it should keep original line break if that is the case.

SublimeText plugin

Hello,
do you know if anyone is working on a SublimeText plugin for formatting code using esformatter?

I really need it and I can work on it, but I'm new to Python, so I just want to be sure that I don't waste time if someone else is doing it already.

Try/Catch is not being indented properly

input:

jQuery.ready.promise = function( obj ) {
    try {
        top = window.frameElement == null && document.documentElement;
    } catch(e) {}
};

output:

jQuery.ready.promise = function( obj ) {
    try {
        top = window.frameElement == null && document.documentElement;
} catch (e) {
}
};

also notice that empty catch block should probably be on a single line.

Config to add whitespace inside parenthesis

I've been trying for a while now to add a config option to add more whitespace inside parenthesis, specifically for rather generic expressions. These appear on ExpressionStatements and sometimes BinaryStatements. Since the former can have many forms and parenthesis are, kind of like comments, not a primary citizen, this turns out to be pretty difficult.

My test file looks like this:

;( ++n < 10 );
;( a == b );
( a === b );
x = ( value / 10 );
if (x * ( x + 1 ) + ( y + 2 )) {
    y = ( "123" + "3456" );
}

I haven't yet been able to find a solution for this. My current experiment is on a branch here: jzaefferer@77c30ed
I ended up applying the same logic to BinaryStatement as for ExpressionStatement. This actually gets pretty close, for my limited test at least:
bla

It'll probably mess up other code though, since so many things are ExpressionStatements.

TypeError: Cannot read property 'start' of undefined

Trying esformatter for the first time, I didn't make it far. I did an npm install esformatter, then saved this as test.js and ran it with node:

var esformatter = require( "esformatter" ),
    fs = require( "fs" ),
    src = fs.readFileSync( "test.js", "utf-8" ),
    formatted = esformatter.format( src );
console.log( formatted );

Results in this stacktrace:

$ node test.js

[...]/node_modules/esformatter/lib/util/token.js:93
        var lineDiff = target.loc.start.line - target.loc.end.line;
                                 ^
TypeError: Cannot read property 'start' of undefined
    at Object.remove (/Users/jza/dev/jquery-ui/node_modules/esformatter/lib/util/token.js:93:34)
    at Object.sanitizeWhiteSpaces (/Users/jza/dev/jquery-ui/node_modules/esformatter/lib/util/whiteSpace.js:148:17)
    at Object.format (/Users/jza/dev/jquery-ui/node_modules/esformatter/lib/esformatter.js:42:9)
    at Object.<anonymous> (/Users/jza/dev/jquery-ui/test.js:4:25)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.runMain (module.js:492:10)
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)

Some basic usage instructions in the readme would be nice, in case that's not how I'm supposed to use it. While at that, I didn't yet see anything for actually configuring options, but then I haven't looked very far either.

change settings to use integers instead of booleans

integers are more flexible since in some cases I think we should have 3+ states. I'm thinking about a setting that keeps the original white spaces (for cases where the user added the spaces to manually align some tokens - think about tabular data) so we should have a way to set if it should remove the existing white space, keep it or add if it doesn't exist, example:

// remove if it exists (consecutive tokens) and don't add if it's missing
var REMOVE = 0;
// add if missing or keep a single one 
var ADD_IF_MISSING = 1;
// add if missing or keep if it already exists (no matter how many)
var ADD_OR_KEEP = 2;
// keep exact amount (remove if more, add if less)
var EXACT = { count : n };
// don't change
var NOOP = 4;

var DEFAULT_OPTS = {
  lineBreak : {
    before : {
      // pass an object for advanced settings
      // this will keep 3 line breaks (add if missing)
      FunctionDeclaration : {
        count : 3
      }
    },
    after : {
      FunctionDeclarationOpeningBrace : ADD_OR_KEEP
    }
  }
}

Of course this will increase the complexity a lot and will take considerably more time. This is definitely not a priority for now.

indent array childs?

this might be tricky to implement specially since it might not be the desired behavior in all cases:

var tuples = [
    // action, add listener, listener list, final state
    ["resolve", "done", jQuery.Callbacks("once memory"), "resolved"],
    ["reject", "fail", jQuery.Callbacks("once memory"), "rejected"],
    ["notify", "progress", jQuery.Callbacks("memory")]
];

probably do it only if user added line breaks.

LineComment indent level inside VariableDeclaration is wrong

Input:

(function( window, undefined ) {
var
    // A central reference to the root jQuery(document)
    rootjQuery,

    // The deferred used on DOM ready
    readyList;
}());

Output:

(function(window, undefined){
    var
            // A central reference to the root jQuery(document)
        rootjQuery,

            // The deferred used on DOM ready
        readyList;
}());

Comment indent should be aligned with the var declarations.

Error when tweaking preset options

If I change the ArgumentList from 0 to 1 in whiteSpace before:

Error: 
/Users/satazor/Work/indigo/dejavu/node_modules/esformatter/lib/util/token.js:14
        var startRange = target.range[0];
                               ^
TypeError: Cannot read property 'range' of undefined
    at Object.before (/Users/satazor/Work/indigo/dejavu/node_modules/esformatter/lib/util/token.js:14:32)

indent chained method calls

this should be indented properly:

returned.promise()
.done(newDefer.resolve)
.fail(newDefer.reject)
.progress(newDefer.notify);

condense most _ws.aroundIfNeeded and _br.aroundIfNeeded into same helper

dumb code duplication is dumb :P

_ws.aroundIfNeeded(node.body.startToken, 'FunctionDeclarationOpeningBrace');
_ws.aroundIfNeeded(node.body.endToken, 'FunctionDeclarationClosingBrace');

_br.aroundIfNeeded(node.body.startToken, 'FunctionDeclarationOpeningBrace');
_br.aroundIfNeeded(node.body.endToken, 'FunctionDeclarationClosingBrace');

could be written as:

_insert.aroundIfNeeded(node.body.startToken. 'FunctionDeclarationOpeningBrace');
_insert.aroundIfNeeded(node.body.endToken. 'FunctionDeclarationClosingBrace');

this is repeated on many hooks.

Tests should default to also running input inside statement block

Unless the test input specifies otherwise, all tests should automatically take their expected input and wrap it in a statement block:

[1,2,3]

{
    statementBefore();
    [1,2,3];
    statementAfter();
}

Most scenarios should remain unchanged except for being indented one layer deeper and it will add one degree of automatic breadth to the test matrix.

Not responding to -h or --help

When invoked after a pipe, esformatter seems to work fine. However, invoking the help message via -h or --help causes it to hang and the prompt does not return. Installed esformatter via npm install -g esformatter on a RHEL Linux x86_64 machine.

Indentation of line breaks gets wrong

If I have

function foo(a){
  return
    a<5?23:12;
}

I get:

function foo(a) {
    return    a < 5 ? 23 : 12;
}

Instead, it should be:

function foo(a) {
    return a < 5 ? 23 : 12;
}

multiple var declaration + function expression = wrong indent

input:

(function(){
var
    // A central reference to the root jQuery(document)
    rootjQuery,

    // Define a local copy of jQuery
    jQuery = function( selector, context ) {
        // The jQuery object is actually just the init constructor 'enhanced'
        return new jQuery.fn.init( selector, context, rootjQuery );
    };
}());

output:

(function(){
    var
            // A central reference to the root jQuery(document)
        rootjQuery,

            // Define a local copy of jQuery
        jQuery = function(selector, context) {
                // The jQuery object is actually just the init constructor 'enhanced'
        return new jQuery.fn.init(selector, context, rootjQuery);
    };
}());

might be related to #30

Wrong whitespace default for object expressions

From test/compare/default/README.dm:

The default settings should be as conservative as possible, Google JavaScript Style Guide should be used as a reference.

For object expressions, Google doesn't have a space in front of the colon:

// Object initializer.
var inset = {
  top: 10,
  right: 20,
  bottom: 15,
  left: 12
};

Following that, the default for whiteSpace.after.PropertyName should be 0, not 1.

keepEmptyLines: 1

If I run esformatter against

function add(a,b)
{
return a+b;
}


function sub(a,b)
{ return a-b;}

I get

function add(a, b) {
    return a + b;
}


function sub(a, b) {
    return a - b;
}

Shouldn't it be:

function add(a, b) {
    return a + b;
}

function sub(a, b) {
    return a - b;
}

(Only one single empty line according to keepEmptyLines: 1 instead of two lines?)

performance: it is too slow to be used on big files

did a "quick" (not as quick as I expected) test using jQuery 1.8.3 and it takes forever to process the file.

half of the reason is because rocambole is too slow (see millermedeiros/rocambole#4), the other half of the time is spend on sanitizeWhiteSpaces and executing the hooks for each node.

I added a few console.time and console.timeEnd calls, here is the output:

opts     : 2ms
indent   : 2ms
trail    : 1ms
empty    : 0ms
parse    : 184354ms
sanitize : 61622ms
moonwalk : 119392ms
toString : 20ms
TOTAL    : 365393ms (aka. forever)

Still need to profile it better (only did the console.time so far) but I know I'm doing a lot of unnecessary work (lots of function calls and removing/inserting unnecessary tokens).

I hope to bring the total time below 5 seconds (ideally under 3s), lets see how it goes.

Make it right before you make it faster.

PS: I will probably "finish" the support for all statements before working on the performance.

Options for switch block

It would be nice to configure the formatting of switch blocks

Input

switch(day){
    case 0:
        x = "Today it's Sunday";
        break;
    case 1: case 2:
        x = "Today it's Monday";
        break;
}

Output

switch(day){
case 0:
x = "Today it's Sunday";
break;
case 1:case 2:
x = "Today it's Monday";
break;
}

The options likely to be configured are:

  • space after switch
  • space or newline before or after curly bracket
  • newline after break
case 0:
    break;

case 1:
  • newline after fallthrough
case 0:
case 1:
  • Indentation
switch (day) {
    case 0:
        x = 1;
        break;

or

switch (day) {
case 0:
    x = 1;
    break;

or

switch (day) {
    case 0:
        x = 1;
    break;

or many others

we should probably not remove original indentation

right now I remove trailing whitespace and indentation before processing the tokens (using RegExp) I think a better approach would be to convert the simple WhiteSpace tokens that are at the start of the line to an Indent token (without changing the amount of chars or anything like that - just set the token.type) and make our indentation code simply replace the amount of chars (or add a new token if it doesn't exist).

That will probably make it easier to implement #10 and #12 and will also make it easier to keep user indentation (if needed).

empty ForInStatement should be kept as single line

input:

// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.

var key;
for ( key in obj ) {}

output:

// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.

var key;
for (key in obj) {
}

Opening curly brace in same line

Which option do I need to change to force an opening curly brace to be on the same line as the appropriate statement / expression?

E.g.:

var add = function(a, b)
{
    return a + b;
}

should become

var add = function(a, b) {
    return a + b;
}

indent of BlockComment with multiple lines is wrong

the indent should affect all lines of the comment block not just the first line.

it will require a different logic, need to edit the comment.raw value directly and it is only possible to do it after #82 gets merged (needs original indentation to make it work properly).

mout instead of amd-utils

Shouldn't the dependency of amd-utils be changed to mout, since mout is more actively updated now?

Line break in an array or object after a comma preceding an object

I write a lot of code in ExtJS, and it's desirable to format configs in this manner:

Ext.define('VMS.model.User', {
  extend: 'Ext.data.Model',
  idProperty: 'UserID',
  fields: [
    {
      name: 'UserID',
      type: 'int'
    },
    {
      name: 'FirstName',
      type: 'string'
    },
    {
      name: 'LastName',
      type: 'string'
    }
  ]
});

However, I was unable to find an option to add line breaks in this manner (how the fields array is formatted). This would be a great addition to EsFormatter.

create a roadmap

I already have a lot of ideas on how I would like things to work but I should communicate them better with possible contributors through a roadmap (explaining planned features and how far we are from reaching the goal). Maybe that can motivate more contributors.

improve token helpers to accept string instead of callback

I think we should augment all the token methods to also accept a string (instead of a callback) for brevity, so this block:

start = _tk.findInBetween(start, end, function(token) {
    return token.value === '(';
});

could be rewritten as:

start = _tk.findInBetween(start, end, '(');

right now the string only checks for token.type, but I think it should also fallback to token.value to make it simpler.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.