A tiny and fast JavaScript code generator from an ESTree-compliant AST.
Key features:
- Generates JavaScript code up to version 6.
- Works on ESTree-compliant ASTs such as the ones produced by Acorn.
- Considerably faster than Esotope (up to 4×), Escodegen (up to 10×), and UglifyJS (up to 125×).
- No dependencies and small footprint (≈ 16 KB minified, ≈ 4 KB gziped).
- Supports comment generation with Astravel.
- Extendable with custom AST node handlers.
The easiest way is to install it with the Node Package Manager:
npm install astring
Alternatively, checkout this repository and install the development dependencies to build the module file:
git clone https://github.com/davidbonnet/astring.git
cd astring
npm install
The path to the module file is dist/astring.min.js
and it can be included from an HTML webpage. When used in a browser environment, the module exposes a global variable astring
:
<script src="astring.js" type="text/javascript"></script>
The module can also be used in the browser via a CommonJS bundler such as Browserify or Webpack.
A live demo showing Astring in action is available.
The astring
module consists of a function that takes two arguments: node
and options
. It returns a string representing the rendered code of the provided AST node
. However, if an output
stream is provided in the options, it writes to that stream and returns it.
The options
are:
indent
: string to use for indentation (defaults to"\t"
)lineEnd
: string to use for line endings (defaults to"\n"
)startingIndentLevel
: indent level to start from (defaults to0
)comments
: generate comments iftrue
(defaults tofalse
)output
: output stream to write the rendered code to (defaults tonull
)generator
: custom code generator (defaults toastring.defaultGenerator
)
This example uses Acorn, a blazingly fast JavaScript AST producer and therefore the perfect companion of Astring.
// Make sure acorn and astring modules are imported
// Set example code
var code = "let answer = 4 + 7 * 5 + 3;\n";
// Parse it into an AST
var ast = acorn.parse(code, { ecmaVersion: 6 });
// Format it into a code string
var formattedCode = astring(ast, {
indent: ' ',
lineEnd: '\n'
});
// Check it
console.log((code === formattedCode) ? 'It works !' : 'Something went wrong…');
This example for Node shows how to use writable streams to get the rendered code.
// Make sure acorn and astring modules are imported
// Set example code
var code = "let answer = 4 + 7 * 5 + 3;\n";
// Parse it into an AST
var ast = acorn.parse(code, { ecmaVersion: 6 });
// Format it and write the result to stdout
var stream = astring(ast, {
output: process.stdout
});
// The returned value is the output stream
console.log('stream is process.stdout?', stream === process.stdout);
Astring supports comment generation, provided they are stored on the AST nodes. To do so, this example uses Astravel, a fast AST traveller and modifier.
// Make sure acorn, astravel and astring modules are imported
// Set example code
var code = [
"// Compute the answer to everything",
"let answer = 4 + 7 * 5 + 3;",
"// Display it",
"console.log(answer);"
].join('\n') + '\n';
// Parse it into an AST and retrieve the list of comments
var comments = [];
var ast = acorn.parse(code, {
ecmaVersion: 6,
locations: true,
onComment: comments
});
// Attach comments to AST nodes
astravel.attachComments(ast, comments);
// Format it into a code string
var formattedCode = astring(ast, {
indent: ' ',
lineEnd: '\n',
comments: true
});
// Check it
console.log(code === formattedCode ? 'It works !' : 'Something went wrong…');
Astring can easily be extended by updating or passing a custom code generator
. A code generator
consists of a mapping of node names and functions that take two arguments: node
and state
. The node
points to the node from which to generate the code and the state
holds various values and objects, the most important one being the output
code stream.
This example shows how to support the await
keyword which is part of the asynchronous functions proposal. The corresponding AwaitExpression
node is based on this suggested definition.
// Make sure the astring module is imported and that `Object.assign` is defined
// Create a custom generator that inherits from Astring's default generator
var customGenerator = Object.assign({}, astring.defaultGenerator, {
AwaitExpression: function(node, state) {
state.output.write('await ');
var argument = node.argument;
if (argument != null) {
this[argument.type](argument, state);
}
}
});
// Obtain a custom AST somehow (note that this AST is not from obtained from a valid code)
var ast = {
type: "Program",
body: [{
type: "ExpressionStatement",
expression: {
type: "AwaitExpression",
argument: {
type: "CallExpression",
callee: {
type: "Identifier",
name: "callable"
},
arguments: []
}
}
}],
sourceType: "module"
};
// Format it
var code = astring(ast, {
generator: customGenerator
});
// Check it
console.log(code === 'await callable();\n' ? 'It works!' : 'Something went wrong…');
The bin/astring
utility can be used to convert a JSON-formatted ESTree compliant AST of a JavaScript code. It accepts the following arguments:
-i
,--indent
: string to use as indentation (defaults to"\t"
)-l
,--line-end
: string to use for line endings (defaults to"\n"
)-s
,--starting-indent-level
: indent level to start from (defaults to0
)-h
,--help
: print a usage message and exit-v
,--version
: print package version and exit
The utility reads the AST from stdin
or from a provided list of files, and prints out the resulting code.
As in the previous example, these examples use Acorn to get the JSON-formatted AST. This command pipes the AST output by Acorn from a script.js
file to Astring and writes the formatted JavaScript code into a result.js
file:
acorn --ecma6 script.js | astring --indent " " > result.js
This command does the same, but reads the AST from an intermediary file:
acorn --ecma6 script.js > ast.json
astring --indent " " ast.json > result.js
All building scripts are defined in the package.json
file and rely on the Node Package Manager. All commands must be run from within the root repository folder.
The source code of Astring is written in ECMAScript 2015 and located at src/astring.js
. It is compiled down to a plain JavaScript (ES5) file located at dist/astring.js
using Babel. This is achieved by running:
npm install
If you are already using an ES2015 to ES5 transpiler for your project, or running in an ES2015 compliant environment, you can include the src/astring.js
file directly.
If you are working on Astring, you can run the build in watch mode and including a source map, running:
npm start
While making changes to Astring, make sure it passes the tests by running:
npm test
Also, make sure that the modifications don't alter the performance by running benchmarks that compare Astring against Escodegen and Esotope:
npm run benchmark
Planned features and releases are outlined on the milestones page.