or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-astring

JavaScript code generator from an ESTree-compliant AST.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/astring@1.9.x

To install, run

npx @tessl/cli install tessl/npm-astring@1.9.0

index.mddocs/

Astring

Astring 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.

Package Information

  • Package Name: astring
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install astring

Core Imports

import { 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");

Basic Usage

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
});

Architecture

Astring is built around several key components:

  • Code Generator: The core generate function that traverses AST nodes and produces JavaScript code
  • Node Handlers: The GENERATOR object containing handler functions for each ESTree node type
  • Precedence System: The EXPRESSIONS_PRECEDENCE mapping that determines when parentheses are needed
  • State Management: Internal state tracking for indentation, line endings, and output formatting
  • Extensibility: Custom generators can be provided to extend or modify code generation behavior

Capabilities

Code Generation

Converts 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 });

Base Generator

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 });

Expression Precedence

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 
});

Legacy Support

Deprecated base generator alias for backward compatibility.

/**
 * @deprecated Use GENERATOR instead
 * Legacy alias for the base generator object
 */
const baseGenerator: Generator;

CLI Tool

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 --version

CLI 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

Types

// 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;
}