CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-astring

JavaScript code generator from an ESTree-compliant AST.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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

docs

index.md

tile.json