1 # Project Structure / Contributing
4 Check open issues for a list of features/bugs that we would like to see
10 To make sure we all use the same basic settings (indent, EOL, EOF) please
11 install [EditorConfig](http://editorconfig.org/#download). It will make code
15 ## New Features / Bugs
17 The easiest way to add new features and fix bugs is to create a test file with
18 mixed input and use the [rocambole-visualize](http://piuccio.github.io/rocambole-visualize/)
19 or the [esprima parser demo](http://esprima.org/demo/parse.html) to visualize
20 the syntax tree and implement each step separately.
22 A good example of a commit that fixes a bug:
23 https://github.com/millermedeiros/esformatter/commit/ebafa00f76 and a good
24 example of a commit that introduces a new feature:
25 https://github.com/millermedeiros/esformatter/commit/e7d82cc81e
31 We augment the AST with
32 [rocambole](https://github.com/millermedeiros/rocambole), so every `node` have
34 properties](https://github.com/millermedeiros/rocambole#extra-properties) that
35 can be used to traverse the AST (similar to the DOM).
37 The whole process is very similar to working with the DOM. Don't feel
38 intimidated by *complex names* like `ConditionalExpressionConsequent`, use the
39 [esprima parser demo](http://esprima.org/demo/parse.html) and/or
40 [rocambole-visualize](http://piuccio.github.io/rocambole-visualize/) as reference
41 and you should be good to go.
43 We rely on some external libraries to add/remove/find tokens:
45 - [rocambole-token](https://github.com/millermedeiros/rocambole-token)
46 - [rocambole-whitespace](https://github.com/millermedeiros/rocambole-whitespace)
47 - [rocambole-linebreak](https://github.com/millermedeiros/rocambole-linebreak)
51 The recursion starts from the *leaf nodes* and moves till it reaches the
52 `Program` root. It works this way because child nodes usually affects the
53 parent nodes structure (specially line breaks), that gives the option for the
54 parent node to override the child node behavior if needed.
56 ### Adding/Removing line breaks and white spaces
58 The `format` method of each object exposed on `lib/hooks.js` is called once for
59 each matching `node`. The `format` method is responsible for adding/removing
60 line breaks and white spaces based on the node structure and config options.
64 We do the indentation after the whole process; that is necessary since the
65 parent nodes affects the indentation of the child nodes (line breaks might be
66 added or removed during the process).
68 The core logic is handled by `lib/indent.js`, but most nodes actually require
69 specific rules to detect the *indent edges*, so most `lib/hooks` also implement
70 a method `getIndentEdges` that can return:
72 - *falsy* value: means the node should **not** be indented;
73 - single `IndentEdge` object: indent in between `startToken` and `endToken`;
74 - array containing multiple `IndentEdge` objects and/or *falsy* values: indent
75 inside any `IndentEdge` object and ignore *falsy* values.
77 The `IndentEdge` is just a Plain JavaScript Object with the properties:
79 - `startToken:Token`: points to token that delimits the indent start (eg. a token
81 - `endToken:Token`: pointer to a token that sets the last token that should be
82 indented (eg. a line break just before `]`)
83 - `level:Int` (optional): sets how many indents should be added for that
84 `IndentEdge`, that is very useful for cases where you might have different
85 options for the same parent node (eg. the `FunctionExpression` hook also
86 handles the `ParameterList` indentation); if `level <= 0` that *edge* is not
87 indented (we don't support negative indent so far)
89 If the hook doesn't implement `getIndentEdges` we consider `node.startToken`
90 and `node.endToken` as the default edge. We only indent nodes that have a value
91 greater than zero on the configuration options.
93 Also important to notice that we only add `Indent` tokens to the beginning of
94 lines and if `edge.startToken` is `{` or `[` or `(` we use the
95 `edge.startToken.next` as the first token since that is usually the expected
96 behavior (you want to indent what is inside the braces/parenthesis).
98 The reason why we decided to handle indentation as multiple `IndentEdge`s is
99 because there are many cases where the lines between `node.startToken` and
100 `node.endToken` are not really the *range* that you want to indent (eg. on
101 `IfStatement` you what to indent the `test`, `consequent` and `alt` but not the
102 lines that open/close the curly braces). Trying to handle all the cases
103 automatically is a path doomed to failure. This is the most flexible
104 abstraction, and cleanest implementation, that we could think of so far but it
105 is indeed complex; it would be way easier if we had a real CST (concrete syntax
106 tree) but that ship already sailed.
110 ## Branches and Pull Requests
112 We will create `-wip` branches (work in progress) for *unfinished* features
113 (mostly because of failing tests) and try to keep master only with *stable*
114 code. We will try hard to not rewrite the commit history of `master` branch but
115 will do it for `-wip` branches.
117 If you plan to implement a new feature check the existing branches, I will push
118 all my local `-wip` branches if I don't complete the feature in the same day.
119 So that should give a good idea on what I'm currently working.
121 Try to split your pull requests into small chunks (separate features), that way
122 it is easier to review and merge. But feel free to do large refactors as well,
123 will be harder to merge but we can work it out. - see [issue-guidelines for
124 more info about good pull
125 requests](https://github.com/necolas/issue-guidelines/blob/master/CONTRIBUTING.md#pull-requests)
131 The default settings should be as *conservative* as possible, [Google
133 Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml)
134 should be used as a reference.
136 We have plans to support other presets like
137 [Idiomatic.js](https://github.com/rwldrn/idiomatic.js/) and [jQuery Style
138 Guide](http://contribute.jquery.org/style-guide/js).
144 Tests are done by comparing the result of `esformatter.parse()` of files with
145 name ending on `-in.js` with the files `-out.js`. The folder
146 `test/compare/default` tests the default settings and files inside
147 `test/compare/custom` tests custom settings. Tests inside the `compare/custom`
148 folder should try to test the *opposite* of the default settings whenever
151 To run the tests install the `devDependencies` by running `npm install`
152 (only required once) and then run `npm test`.
154 `mocha` source code was edited to provide better error
155 messages. See [mocha/issues/710](https://github.com/visionmedia/mocha/pull/710)
161 # bail stops at first failed test
163 # GREP is used to filter the specs to run (only specs that contain "indent" in the name)
164 GREP='indent' npm test
165 # can also use "INVERT=true" to only execute specs that doesn't contain "cli" in the name
166 GREP=cli INVERT=true npm test
167 # to set the mocha reporter
168 REPORTER=dot npm test
169 # enable logging for the specified module
170 DEBUG=rocambole:br:* npm test
173 **protip:** files starting with double underscore (`__`) are on our
174 `.gitignore` file and won't be commited, so I usually have a `__tmp-in.js` and
175 `__tmp-out.js` test files that contains the bare minimum code that reproduces
176 the bug that I'm trying to fix and execute that with
177 `DEBUG=esformatter:*:*,rocambole:*:* GREP=tmp npm test` to get all the logs.
182 We have an IRC channel [#esformatter on
183 irc.freenode.net](http://webchat.freenode.net/?channels=esformatter) for quick
184 discussions about the project development/structure.