🌳 Tiny and fast JavaScript code generator from an ESTree-compliant AST.
- Generates JavaScript code up to version 15 (2024) and stage 3 proposals.
- Works on ESTree-compliant ASTs such as the ones produced by Meriyah or 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).
- Runs on 🦕 Deno.
⚠️ Astring relies onString.prototype.repeat(amount)andString.prototype.endsWith(string). If the environment running Astring does not define these methods, usestring.prototype.repeat,string.prototype.endsWithorbabel-polyfill.
Install with the Node Package Manager:
npm install astringOr install with JSR:
deno add @davidbonnet/astringAlternatively, checkout this repository and install the development dependencies to build the module file:
git clone https://github.com/davidbonnet/astring.git
cd astring
npm installImport it from Deno's third party module repository:
const { generate } = await import('https://deno.land/x/astring/src/astring.js')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>The astring module exposes the following properties:
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 to0)comments: generate comments iftrue(defaults tofalse)output: output stream to write the rendered code to (defaults tonull)generator: custom code generator (defaults toGENERATOR)sourceMap: source map generator (defaults tonull)expressionsPrecedence: custom map of node types and their precedence level (defaults toEXPRESSIONS_PRECEDENCE)
Base generator that can be used to extend Astring.
Mapping of node types and their precedence level to let the generator know when to use parentheses.
Default precedence level that always triggers the use of parentheses.
⚠️ Deprecated, useGENERATORinstead.
Operations per second for generating each sample code from a pre-parsed AST:
| code sample (length) | escodegen | astring | uglify | babel | prettier |
|---|---|---|---|---|---|
| tiny code (11) | 1,257,527 | 7,185,642 | 129,467 | 156,184 | 333 |
| everything (8532) | 1,366 | 8,008 | 0 | 346 | 64 |
Operations per second for parsing and generating each sample code:
| code sample (length) | acorn + astring | meriyah + astring | buble | sucrase |
|---|---|---|---|---|
| tiny code (11) | 92,578 | 864,665 | 25,911 | 575,370 |
| everything (8532) | 706 | 1,425 | 132 | 1,403 |
The following examples are written in JavaScript 5 with Astring imported à la CommonJS.
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.generate(ast)
// Check it
console.log(code === formattedCode ? 'It works!' : 'Something went wrong…')This example uses the source map generator from the Source Map module.
// Make sure acorn, sourceMap and astring modules are imported
var code = 'function add(a, b) { return a + b; }\n'
var ast = acorn.parse(code, {
ecmaVersion: 6,
sourceType: 'module',
// Locations are needed in order for the source map generator to work
locations: true,
})
// Create empty source map generator
var map = new sourceMap.SourceMapGenerator({
// Source file name must be set and will be used for mappings
file: 'script.js',
})
var formattedCode = generate(ast, {
// Enable source maps
sourceMap: map,
})
// Display generated source map
console.log(map.toString())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.generate(ast, {
output: process.stdout,
})
// The returned value is the output stream
console.log('Does stream equal 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.generate(ast, {
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 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.
// Make sure the astring module is imported and that `Object.assign` is defined
// Create a custom generator that inherits from Astring's base generator
var customGenerator = Object.assign({}, astring.GENERATOR, {
AwaitExpression: function (node, state) {
state.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 obtained from a valid code)
var ast = {
type: 'AwaitExpression',
argument: {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: 'callable',
},
arguments: [],
},
}
// Format it
var code = astring.generate(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"␣␣")-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 a provided list of files or from stdin if none is supplied and prints the generated 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 > result.jsThis command does the same, but reads the AST from an intermediary file:
acorn --ecma6 script.js > ast.json
astring ast.json > result.jsThis command reads JavaScript 6 code from stdin and outputs a prettified version:
cat | acorn --ecma6 | astring