JavaScript code generator from an ESTree-compliant AST.
npx @tessl/cli install tessl/npm-astring@1.9.0Astring is a tiny and fast JavaScript code generator that transforms ESTree-compliant Abstract Syntax Trees (ASTs) into JavaScript code. It supports modern JavaScript features up to ES2024 and stage 3 proposals, offering significantly better performance compared to alternatives like Babel, Escodegen, and Prettier.
npm install astringimport { generate, GENERATOR, EXPRESSIONS_PRECEDENCE, NEEDS_PARENTHESES } from "astring";For CommonJS:
const { generate, GENERATOR, EXPRESSIONS_PRECEDENCE, NEEDS_PARENTHESES } = require("astring");For browsers (UMD):
<script src="astring.min.js"></script>
<script>
const { generate, GENERATOR, EXPRESSIONS_PRECEDENCE, NEEDS_PARENTHESES } = astring;
</script>For Deno:
const { generate } = await import("https://deno.land/x/astring/src/astring.js");import { generate } from "astring";
import { parse } from "acorn"; // or any ESTree-compliant parser
// Parse JavaScript code into an AST
const code = "let answer = 4 + 7 * 5 + 3;";
const ast = parse(code, { ecmaVersion: 2024 });
// Generate code from the AST
const generatedCode = generate(ast);
console.log(generatedCode); // "let answer = 4 + 7 * 5 + 3;\n"
// With options
const formattedCode = generate(ast, {
indent: " ", // 4 spaces
lineEnd: "\r\n", // Windows line endings
comments: true, // Include comments if present
});Astring is built around several key components:
generate function that traverses AST nodes and produces JavaScript codeGENERATOR object containing handler functions for each ESTree node typeEXPRESSIONS_PRECEDENCE mapping that determines when parentheses are neededConverts ESTree-compliant AST nodes into JavaScript code strings with extensive formatting options.
/**
* Returns a string representing the rendered code of the provided AST node.
* If an output stream is provided in the options, it writes to that stream and returns it.
* @param node - ESTree-compliant AST node to generate code from
* @param options - Optional configuration object
* @returns Generated JavaScript code string, or the output stream if one was provided
*/
function generate(node: Node, options?: Options<null>): string;
function generate(node: Node, options?: Options<Writable>): Writable;
interface Node {
type: string;
[key: string]: any;
}
interface Options<Output = null> {
/** String to use for indentation (defaults to " ") */
indent?: string;
/** String to use for line endings (defaults to "\n") */
lineEnd?: string;
/** Indent level to start from (defaults to 0) */
startingIndentLevel?: number;
/** Generate comments if true (defaults to false) */
comments?: boolean;
/** Output stream to write the rendered code to (defaults to null) */
output?: Output;
/** Custom code generator (defaults to GENERATOR) */
generator?: Generator;
/** Source map generator for debugging support */
sourceMap?: SourceMapGenerator;
/** Custom map of node types and their precedence level (defaults to EXPRESSIONS_PRECEDENCE) */
expressionsPrecedence?: { [key: string]: number };
}Usage Examples:
import { generate } from "astring";
// Basic usage
const code = generate(astNode);
// With custom formatting
const formattedCode = generate(astNode, {
indent: "\t", // Use tabs for indentation
lineEnd: "\n", // Unix line endings
startingIndentLevel: 1, // Start with one level of indentation
comments: true, // Include comments from AST
});
// Stream output to file
import fs from "fs";
const output = fs.createWriteStream("output.js");
generate(astNode, { output });
// With source maps
import { SourceMapGenerator } from "source-map";
const map = new SourceMapGenerator({ file: "output.js" });
const code = generate(astNode, { sourceMap: map });The base code generator object containing handler functions for all ESTree node types.
/**
* Base code generator containing handlers for each AST node type.
* Can be extended or customized for specialized code generation needs.
*/
const GENERATOR: Generator;
type Generator = {
[NodeType in EstreeNodeType]: (node: EstreeNode & { type: NodeType }, state: State) => void;
};
interface State {
output: string;
write(code: string, node?: EstreeNode): void;
writeComments: boolean;
indent: string;
lineEnd: string;
indentLevel: number;
line?: number;
column?: number;
lineEndSize?: number;
mapping?: Mapping;
}Usage Examples:
import { generate, GENERATOR } from "astring";
// Extend the base generator with custom node handlers
const customGenerator = {
...GENERATOR,
Identifier(node, state) {
// Custom handling for identifier nodes
state.write(`custom_${node.name}`);
},
Literal(node, state) {
// Custom handling for literal nodes
if (typeof node.value === "string") {
state.write(`"${node.value.toUpperCase()}"`);
} else {
GENERATOR.Literal(node, state); // Fall back to default
}
}
};
// Use custom generator
const code = generate(astNode, { generator: customGenerator });Mapping of node types and their precedence levels for determining when parentheses are needed.
/**
* Mapping of expression node types to their precedence levels.
* Higher numbers indicate higher precedence.
*/
const EXPRESSIONS_PRECEDENCE: { [key: string]: number };
/**
* Special precedence value that always triggers parentheses.
*/
const NEEDS_PARENTHESES: 17;Usage Examples:
import { generate, EXPRESSIONS_PRECEDENCE, NEEDS_PARENTHESES } from "astring";
// Custom precedence for specialized expressions
const customPrecedence = {
...EXPRESSIONS_PRECEDENCE,
MyCustomExpression: 15, // Custom precedence level
AlwaysParenthesized: NEEDS_PARENTHESES, // Always use parentheses
};
const code = generate(astNode, {
expressionsPrecedence: customPrecedence
});Deprecated base generator alias for backward compatibility.
/**
* @deprecated Use GENERATOR instead
* Legacy alias for the base generator object
*/
const baseGenerator: Generator;Astring includes a command-line interface for converting JSON AST files to JavaScript code.
Installation and Usage:
# Install globally for CLI access
npm install -g astring
# Convert AST from file
astring ast.json
# Convert AST from stdin
cat ast.json | astring
# With options
astring --indent " " --line-end "\r\n" ast.json
# Show help
astring --help
# Show version
astring --versionCLI Options:
-i, --indent INDENT: Set indentation string-l, --line-end LINE_END: Set line ending string-s, --starting-indent-level LEVEL: Set starting indent level-h, --help: Show help information-v, --version: Show version number// Core types from 'estree' package
interface EstreeNode {
type: string;
[key: string]: any;
}
// Core types from 'source-map' package
interface SourceMapGenerator {
addMapping(mapping: Mapping): void;
toString(): string;
}
interface Mapping {
generated: { line: number; column: number };
original?: { line: number; column: number };
source?: string;
name?: string;
}
// Astring-specific types
interface State {
output: string;
write(code: string, node?: EstreeNode): void;
writeComments: boolean;
indent: string;
lineEnd: string;
indentLevel: number;
line?: number;
column?: number;
lineEndSize?: number;
mapping?: Mapping;
}
type Generator = {
[T in EstreeNode['type']]: (
node: EstreeNode & { type: T },
state: State,
) => void;
};
interface Options<Output = null> {
sourceMap?: SourceMapGenerator;
indent?: string;
lineEnd?: string;
startingIndentLevel?: number;
comments?: boolean;
output?: Output;
generator?: Generator;
}
interface Node {
type: string;
}