Astring
A tiny and fast JavaScript code generator from an ESTree-compliant AST.
Key features
- Generates JavaScript code up to version 7 and finished proposals.
- Works on ESTree-compliant ASTs such as the ones produced by Acorn.
- Extendable with custom AST node handlers.
- Considerably faster than Bublé (up to 5×), Escodegen (up to 10×), Babel (up to 50×), UglifyJS (up to 125×), and Prettier (up to 380×).
- Supports source map generation with Source Map.
- Supports comment generation with Astravel.
- No dependencies and small footprint (≈ 16 KB minified, ≈ 4 KB gziped).
Checkout the live demo showing Astring in action.
Contents
Installation
:warning: Astring relies on String.prototype.repeat(amount)
and String.prototype.endsWith(string)
. If the environment running Astring does not define these methods, use string.prototype.repeat
, string.prototype.endsWith
or babel-polyfill
.
Install 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
Import
With JavaScript 6 modules:
import { generate } from 'astring'
With CommonJS:
const { generate } = require('astring')
A browser-ready minified bundle containing Astring is available at dist/astring.min.js
. The module exposes a global variable astring
:
<script src="astring.min.js" type="text/javascript"></script>
<script type="text/javascript">
var generate = astring.generate
</script>
API
The astring
module exposes the following properties:
generate(node: object, options: object): string | object
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 "␣␣"
)lineEnd
: string to use for line endings (defaults to "\n"
)startingIndentLevel
: indent level to start from (defaults to 0
)comments
: generate comments if true
(defaults to false
)output
: output stream to write the rendered code to (defaults to null
)generator
: custom code generator (defaults to astring.baseGenerator
)sourceMap
: source map generator (defaults to null
)
baseGenerator: object
Base generator that can be used to extend Astring.
Examples
The following examples are written in JavaScript 5 with Astring imported à la CommonJS.
Generating code
This example uses Acorn, a blazingly fast JavaScript AST producer and therefore the perfect companion of Astring.
var code = "let answer = 4 + 7 * 5 + 3;\n"
var ast = acorn.parse(code, { ecmaVersion: 6 })
var formattedCode = astring.generate(ast)
console.log((code === formattedCode) ? 'It works !' : 'Something went wrong…')
Generating source maps
This example uses the source map generator from the Source Map module.
var code = 'function add(a, b) { return a + b; }\n'
var ast = acorn.parse(code, {
ecmaVersion: 6,
sourceType: 'module',
locations: true,
})
var map = new sourceMap.SourceMapGenerator({
file: 'script.js',
})
var formattedCode = generate(ast, {
sourceMap: map,
})
console.log(map.toString())
Using writable streams
This example for Node shows how to use writable streams to get the rendered code.
var code = 'let answer = 4 + 7 * 5 + 3;\n'
var ast = acorn.parse(code, { ecmaVersion: 6 })
var stream = astring.generate(ast, {
output: process.stdout,
})
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.
var code =
[
'// Compute the answer to everything',
'let answer = 4 + 7 * 5 + 3;',
'// Display it',
'console.log(answer);',
].join('\n') + '\n'
var comments = []
var ast = acorn.parse(code, {
ecmaVersion: 6,
locations: true,
onComment: comments,
})
astravel.attachComments(ast, comments)
var formattedCode = astring.generate(ast, {
comments: true,
})
console.log(code === formattedCode ? 'It works !' : 'Something went wrong…')
Extending
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
exposes the write
method that takes generated code strings.
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.
var customGenerator = Object.assign({}, astring.baseGenerator, {
AwaitExpression: function(node, state) {
state.write('await ')
var argument = node.argument
if (argument != null) {
this[argument.type](argument, state)
}
},
})
var ast = {
type: 'AwaitExpression',
argument: {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: 'callable',
},
arguments: [],
},
}
var code = astring.generate(ast, {
generator: customGenerator,
})
console.log(
code === 'await callable();\n' ? 'It works!' : 'Something went wrong…'
)
Command line interface
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 "␣␣"
)-l
, --line-end
: string to use for line endings (defaults to "\n"
)-s
, --starting-indent-level
: indent level to start from (defaults to 0
)-h
, --help
: print a usage message and exit-v
, --version
: print package version and exit
The utility reads the AST from a provided list of files or from stdin
if none is supplied and prints the generated code.
Example
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:
cat script.js | acorn --ecma6 | astring > result.js
This command does the same, but reads the AST from an intermediary file:
acorn --ecma6 script.js > ast.json
astring ast.json > result.js
This command reads JavaScript 6 code from stdin
and outputs a prettified version:
cat | acorn --ecma6 | astring
Building
All building scripts are defined in the package.json
file. All commands must be run from within the root repository folder.
Production
The source code of Astring is written in JavaScript 6 and located at src/astring.js
. It is compiled down to a JavaScript 5 file located at dist/astring.js
, with its source map at dist/astring.js.map
using Babel. This is achieved by running:
npm run build
If you are already using a JavaScript 6 to 5 compiler for your project, or a JavaScript 6 compliant interpreter, you can include the src/astring.js
file directly.
A minified version of Astring located at dist/astring.min.js
along with its source map at dist/astring.min.js.map
can be generated by running:
npm run build:minified
Development
If you are working on Astring, you can enable Babel's watch mode to automatically generate dist/astring.js
and dist/astring.js.map
at each update by running:
npm start
Tests
While making changes to Astring, make sure it passes the tests (it checks code formatting and unit tests):
npm test
You can also get an HTML report of the coverage:
npm run coverage
You can also run tests on a large array of files:
npm run test:scripts
Benchmark
The benchmark compares Astring against other code generators. These are not included in the dependencies and should be installed first:
npm install escodegen@1.8 uglify-js@2 babel-generator@6 buble@0.15
Benchmarks can be run using Node in version 8 with:
npm run benchmark
Roadmap
Planned features and releases are outlined on the milestones page.