esformatter
Version:
ECMAScript code beautifier/formatter
207 lines (158 loc) • 6.87 kB
Markdown
## Plugins
Esformatter also have support for plugins (v0.2.0+).
JavaScript is a very flexible language, which means people write it in many
different ways, since adding support for every single kind of style would be
*impossible*, we decided to introduce plugins; that should give enough
flexibility to tailor the formatting to match the craziest needs.
Plugins are automatically loaded from `node_modules` if you pass the module id
in the config file:
```json
{
"indent": {
"value": "\t"
},
"plugins": ["esformatter-sample-plugin", "foobar"]
}
```
List of plugins and plugins wish list: https://github.com/millermedeiros/esformatter/wiki/Plugins
You also have the option to `register` a plugin programmatically:
```js
var plugin = {
nodeAfter: function(node) {
// transform node here
}
};
esformatter.register(plugin);
```
Plugins are executed in the same order as they are registered (first in, first
out).
The plugin methods are executed in the following order: `setOptions` > `stringBefore` > `transformBefore` > `tokenBefore` > `nodeBefore` > `nodeAfter` > `tokenAfter` > `transformAfter` > `stringAfter`.
**All plugin methods are optional.**
**IMPORTANT:** If you need to edit the structure of the AST (add/remove nodes,
change the order of elements) we recommend you to write it as a standalone CLI
tool whenever possible (eg.
[strip-debug](https://www.npmjs.com/package/strip-debug)) and use the
[pipe](./config.md#pipe) option or implement it using
`stringBefore`/`stringAfter`. Plugins should ideally only add/remove/edit the
`WhiteSpace`, `Indent`, `LineBreak` and `Comment` tokens, otherwise you might
have conflicts with other plugins and esformatter itself.
**protip:** You can use
[rocambole-token](https://github.com/millermedeiros/rocambole-token) and
[rocambole-node](https://github.com/millermedeiros/rocambole-node) to simplify
the AST manipulation process.
### setOptions(options)
Called once before any manipulation, the object is shared with the esformatter
which means you can use this method to override default options if needed.
```js
var options;
plugin.setOptions = function(opts) {
// override the default settings (objects are passed by reference, changing
// the value here will also change the value used by esformatter)
opts.indent.value = ' ';
// store the options to be used later
options = opts;
};
```
### stringBefore(inputString):String
A way to replace the input string, it should **ALWAYS** return a string.
```js
plugin.stringBefore = function(str) {
// prepend a variable declaration to the file
return 'var foo = "bar";\n' + str;
};
```
PS: you should only really use this method if you need to store some state
during `stringBefore` to be used by your other methods or if you need to
change the program structure (eg. add/remove nodes/semi-colons, anything that
would generate a different AST structure)
### stringAfter(outputString):String
Replaces the output string.
```js
plugin.stringAfter = function(str) {
// replaces all the occurances of "foo" with "bar" (very naive)
// using regular expressions or string manipulation methods to process code
// is very error-prone! BEWARE!
return str.replace(/foo/g, 'bar');
};
```
PS: you should only really use this method if you need to recover from some of
the changes introduced by the other plugin methods or esformatter itself; favor
`stringBefore` to reduce conflicts with other plugins (eg. if you introduce
a single quote and the `esformatter-quotes` plugin should convert all quotes to
double quotes)
### tokenBefore(token)
Called once for each token (eg. Keyword, Punctuator, WhiteSpace, Indent...)
before processing the nodes. Can be used to manipulate the token value or
add/remove/replace the token or tokens around it.
```js
var tk = require('rocambole-token');
plugin.tokenBefore = function(token) {
if (tk.isSemiColon(token) && tk.isSemiColon(token.next)) {
// remove semicolon if next token is also a semicolon
tk.remove(token);
}
};
```
### tokenAfter(token)
Called once for each token (eg. Keyword, Punctuator, WhiteSpace, Indent...)
after processing all the nodes. Can be used to manipulate the token value or
add/remove/replace the token or tokens around it.
### nodeBefore(node)
Called once for each `node` of the program (eg. VariableDeclaration,
IfStatement, FunctionExpression...) before the esformatter default
manipulations.
### nodeAfter(node)
Called once for each `node` of the program (eg. VariableDeclaration,
IfStatement, FunctionExpression...) after the esformatter default
manipulations.
```js
var tk = require('rocambole-token');
plugin.nodeAfter = function(node) {
if (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration') {
if (node.body) {
// insert a line break before the function body
tk.before(node.body.startToken, {
type: 'LineBreak',
value: options.lineBreak.value
});
}
}
};
```
### transformBefore(ast)
Called before esformatter loops through all the nodes, it receives the whole
AST in case you need a different loop strategy than `rocambole.moonwalk`. In
most cases the `nodeBefore` method is enough.
This method **should only** be used to add/remove `WhiteSpace`, `LineBreak` and
`Indent` tokens.
It's very important to note that adding/removing/reordering `nodes` might cause
some serious problems on the code formatting. esformatter will skip nodes
unless you instrument them properly (adding all the properties that
`rocambole.moonwalk`, `rocambole.recursive` and future plugins expects) so it
is not recommended to do it here.
If you need to edit the tree structure please use the `stringBefore` method or
write a standalone CLI tool that can be used on the [pipe](./config.md#pipe)
setting.
### transformAfter(ast)
Called after all nodes and tokens are processed, allows overriding all the
changes (including indentation).
This method **should only** be used to add/remove `WhiteSpace`, `LineBreak` and
`Indent` tokens.
```js
var rocambole = require('rocambole');
plugin.transformAfter = function(ast) {
// if you need to manipulate multiple nodes you can use the
// rocambole.moonwalk or rocambole.walk methods. we don't do it
// automatically since you might have very specific needs
rocambole.moonwalk(ast, function(node) {
doStuff(node);
});
};
```
It's very important to note that adding/removing/reordering `nodes` might cause
undesired side effects on other plugins (`rocambole.moonwalk` and
`rocambole.walk` might not work as expected and/or you might forget some
`node.[start|end]Token` and/or `token.[next|prev]` and break other plugins). So
if you need to edit the tree structure please use the `stringBefore` and
`stringAfter` methods or write a standalone CLI tool that can be used on the
[pipe](./config.md#pipe) setting.