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:
{
"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:
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) and use the
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 and rocambole-node to simplify the AST manipulation process.
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.
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;
};
A way to replace the input string, it should ALWAYS return a string.
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)
Replaces the output string.
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)
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.
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);
}
};
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.
Called once for each node
of the program (eg. VariableDeclaration,
IfStatement, FunctionExpression...) before the esformatter default
manipulations.
Called once for each node
of the program (eg. VariableDeclaration,
IfStatement, FunctionExpression...) after the esformatter default
manipulations.
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
});
}
}
};
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
setting.
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.
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 setting.