Spaces:
Running
Running
| # Astring | |
| [](https://www.npmjs.org/package/astring) | |
| [](https://codecov.io/gh/davidbonnet/astring) | |
| 🌳 Tiny and fast JavaScript code generator from an [ESTree](https://github.com/estree/estree)-compliant AST. | |
| ### Key features | |
| - Generates JavaScript code up to [version 15 (2024)](https://tc39.github.io/ecma262/) and [stage 3 proposals](https://github.com/tc39/proposals#stage-3). | |
| - Works on [ESTree](https://github.com/estree/estree)-compliant ASTs such as the ones produced by [Meriyah](https://github.com/meriyah/meriyah) or [Acorn](https://github.com/acornjs/acorn). | |
| - Extendable with custom AST node handlers. | |
| - Considerably faster than [Bublé](https://gitlab.com/Rich-Harris/buble) (up to 5×), [Escodegen](https://github.com/estools/escodegen) (up to 10×), [Babel](https://github.com/babel/babel) (up to 50×), [UglifyJS](https://github.com/mishoo/UglifyJS2) (up to 125×), and [Prettier](https://github.com/prettier/prettier) (up to 380×). | |
| - Supports source map generation with [Source Map](https://github.com/mozilla/source-map#sourcemapgenerator). | |
| - Supports comment generation with [Astravel](https://github.com/davidbonnet/astravel). | |
| - No dependencies and small footprint (≈ 16 KB minified, ≈ 4 KB gziped). | |
| - Runs on [🦕 Deno](https://deno.land/x/astring). | |
| ## 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`](https://www.npmjs.com/package/string.prototype.repeat), [`string.prototype.endsWith`](https://www.npmjs.com/package/string.prototype.endswith) or [`babel-polyfill`](https://www.npmjs.com/package/babel-polyfill). | |
| Install with the [Node Package Manager](https://www.npmjs.com/package/astring): | |
| ```bash | |
| npm install astring | |
| ``` | |
| Or install with [JSR](https://jsr.io/@davidbonnet/astring): | |
| ```bash | |
| deno add @davidbonnet/astring | |
| ``` | |
| Alternatively, checkout this repository and install the development dependencies to build the module file: | |
| ```bash | |
| git clone https://github.com/davidbonnet/astring.git | |
| cd astring | |
| npm install | |
| ``` | |
| ## Import | |
| Import it from [Deno's third party module repository](https://deno.land/x/astring/src/astring.js): | |
| ```js | |
| const { generate } = await import('https://deno.land/x/astring/src/astring.js') | |
| ``` | |
| With JavaScript 6 modules: | |
| ```js | |
| import { generate } from 'astring' | |
| ``` | |
| With CommonJS: | |
| ```js | |
| 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`: | |
| ```html | |
| <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 `GENERATOR`) | |
| - `sourceMap`: [source map generator](https://github.com/mozilla/source-map#sourcemapgenerator) (defaults to `null`) | |
| - `expressionsPrecedence`: custom map of node types and their precedence level (defaults to `EXPRESSIONS_PRECEDENCE`) | |
| ### `GENERATOR: object` | |
| Base generator that can be used to [extend Astring](#extending). | |
| ### `EXPRESSIONS_PRECEDENCE: object` | |
| Mapping of node types and their precedence level to let the generator know when to use parentheses. | |
| ### `NEEDS_PARENTHESES: number` | |
| Default precedence level that always triggers the use of parentheses. | |
| ### `baseGenerator: object` | |
| > :warning: Deprecated, use `GENERATOR` instead. | |
| ## Benchmark | |
| ### Generating code | |
| 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 | | |
| ### Parsing and generating code | |
| 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 | | |
| ## Examples | |
| The following examples are written in JavaScript 5 with Astring imported _à la CommonJS_. | |
| ### Generating code | |
| This example uses [Acorn](https://github.com/acornjs/acorn), a blazingly fast JavaScript AST producer and therefore the perfect companion of Astring. | |
| ```javascript | |
| // 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…') | |
| ``` | |
| ### Generating source maps | |
| This example uses the source map generator from the [Source Map](https://github.com/mozilla/source-map#sourcemapgenerator) module. | |
| ```javascript | |
| // 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()) | |
| ``` | |
| ### Using writable streams | |
| This example for [Node](http://nodejs.org) shows how to use writable streams to get the rendered code. | |
| ```javascript | |
| // 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) | |
| ``` | |
| ### Generating comments | |
| Astring supports comment generation, provided they are stored on the AST nodes. To do so, this example uses [Astravel](https://github.com/davidbonnet/astravel), a fast AST traveller and modifier. | |
| ```javascript | |
| // 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…') | |
| ``` | |
| ### 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](https://github.com/tc39/ecmascript-asyncawait). The corresponding `AwaitExpression` node is based on [this suggested definition](https://github.com/estree/estree/blob/master/es2017.md). | |
| ```javascript | |
| // 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…', | |
| ) | |
| ``` | |
| ## 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](https://github.com/acornjs/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: | |
| ```bash | |
| acorn --ecma6 script.js | astring > result.js | |
| ``` | |
| This command does the same, but reads the AST from an intermediary file: | |
| ```bash | |
| acorn --ecma6 script.js > ast.json | |
| astring ast.json > result.js | |
| ``` | |
| This command reads JavaScript 6 code from `stdin` and outputs a prettified version: | |
| ```bash | |
| cat | acorn --ecma6 | astring | |
| ``` | |