npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@taml/ast

v1.0.0

Published

Abstract Syntax Tree types and utilities for TAML (Terminal ANSI Markup Language)

Downloads

139

Readme

@taml/ast

Foundation package providing AST node types, visitor patterns, and tree traversal utilities for TAML documents.

npm version npm downloads TypeScript License: MIT CI Publish

TAML Ecosystem

TAML (Terminal ANSI Markup Language) is a lightweight markup language for styling terminal output with ANSI escape codes. For the complete specification, visit the TAML Specification Repository.

Package Dependencies

graph TD
    B["@taml/parser"] --> A["@taml/ast"]
    C["@taml/react"] --> A
    C --> B
    D["@taml/docusaurus"] --> C
    F["@taml/cli"] --> E["@taml/encoder"]

Related Packages

Core Infrastructure

  • @taml/ast - Foundation package providing AST node types, visitor patterns, and tree traversal utilities for TAML documents.
  • @taml/parser - Robust parser that converts TAML markup strings into typed AST nodes with comprehensive error handling and validation.

Input/Output Tools

  • @taml/encoder - Converts raw ANSI escape sequences into clean TAML markup for further processing and manipulation.
  • @taml/cli - Command-line tool for converting ANSI escape sequences to TAML format in batch operations.

Integration Packages

  • @taml/react - React component that renders TAML markup as styled JSX elements with full TypeScript support and performance optimization.
  • @taml/docusaurus - Docusaurus theme that automatically detects and renders TAML code blocks in documentation sites.

Installation

npm

npm install @taml/ast

yarn

yarn add @taml/ast

pnpm

pnpm add @taml/ast

bun

bun add @taml/ast

TypeScript Setup

This package includes TypeScript declarations out of the box. No additional setup is required for TypeScript projects.

// ESM
import { createDocument, createElement, createText } from "@taml/ast";

// CommonJS
const { createDocument, createElement, createText } = require("@taml/ast");

Quick Start

Here's a 5-minute introduction to building and working with TAML AST trees:

import {
  createDocument,
  createElement,
  createText,
  visit,
  getAllText,
  getElementsWithTag,
} from "@taml/ast";

// Create AST nodes
const helloText = createText("Hello ");
const worldText = createText("World");
const exclamationText = createText("!");

// Build a tree: <red>Hello <bold>World</bold>!</red>
const boldElement = createElement("bold", [worldText]);
const redElement = createElement("red", [
  helloText,
  boldElement,
  exclamationText,
]);
const document = createDocument([redElement]);

// Extract all text content
const allText = getAllText(document);
console.log(allText); // "Hello World!"

// Find specific elements
const boldElements = getElementsWithTag(document, "bold");
console.log(boldElements.length); // 1

// Traverse with visitor pattern
visit(document, {
  visitElement: (node) => {
    console.log(`Found element: ${node.tagName}`);
  },
  visitText: (node) => {
    console.log(`Found text: "${node.content}"`);
  },
});

Core Concepts

AST Node Types

The TAML AST consists of three fundamental node types:

DocumentNode

The root container for all top-level nodes in a TAML document.

interface DocumentNode extends TamlNode {
  type: "document";
  children: TamlNode[];
}

ElementNode

Represents a TAML tag with optional children (e.g., <red>, <bold>).

interface ElementNode extends TamlNode {
  type: "element";
  tagName: TamlTag;
  children: TamlNode[];
}

TextNode

Contains plain text content without any styling.

interface TextNode extends TamlNode {
  type: "text";
  content: string;
}

Tree Structure

All nodes maintain parent-child relationships and position information:

interface TamlNode {
  type: NodeType;
  start: number; // Start position in source text
  end: number; // End position in source text
  parent?: TamlNode; // Parent node reference
}

TAML Tags

The library supports all 37 valid TAML tags from the specification:

  • Standard Colors: black, red, green, yellow, blue, magenta, cyan, white
  • Bright Colors: brightBlack, brightRed, brightGreen, etc.
  • Background Colors: bgBlack, bgRed, bgBrightBlue, etc.
  • Text Styles: bold, dim, italic, underline, strikethrough

Usage Examples

Building AST Trees

Manual Construction

import {
  createDocument,
  createElement,
  createText,
  appendChild,
} from "@taml/ast";

// Create nodes
const document = createDocument();
const redElement = createElement("red");
const text = createText("Styled text");

// Build tree structure
appendChild(redElement, text);
appendChild(document, redElement);

Nested Elements

// Create: <red>Error: <bold>File not found</bold></red>
const errorText = createText("Error: ");
const fileText = createText("File not found");
const boldElement = createElement("bold", [fileText]);
const redElement = createElement("red", [errorText, boldElement]);
const document = createDocument([redElement]);

Traversing & Searching

Finding Nodes

import { findAll, findFirst, getTextNodes } from "@taml/ast";

// Find all bold elements
const boldElements = findAll(
  document,
  (node) => node.type === "element" && node.tagName === "bold",
);

// Find first text node
const firstText = findFirst(document, (node) => node.type === "text");

// Get all text nodes
const textNodes = getTextNodes(document);

Text Extraction

import { getAllText, getElementsWithTag } from "@taml/ast";

// Extract all text content
const content = getAllText(document);

// Get elements by tag
const redElements = getElementsWithTag(document, "red");
const boldElements = getElementsWithTag(document, "bold");

Transforming Trees

Visitor Pattern

import { visit, createCollectorVisitor } from "@taml/ast";

// Custom visitor
visit(document, {
  enterNode: (node) => console.log(`Entering ${node.type}`),
  exitNode: (node) => console.log(`Exiting ${node.type}`),
  visitElement: (node) => {
    if (node.tagName === "red") {
      console.log("Found red element");
    }
  },
});

// Collector visitor
const textCollector = createCollectorVisitor(
  (node): node is TextNode => node.type === "text",
);
visit(document, textCollector);
console.log(textCollector.nodes); // All text nodes

Async Operations

import { visitAsync } from "@taml/ast";

await visitAsync(document, {
  visitText: async (node) => {
    // Async processing of text nodes
    await processText(node.content);
  },
});

Tree Transformation

import { transform } from "@taml/ast";

// Transform to HTML
const html = transform(document, {
  visitDocument: (node) =>
    node.children.map((child) => transform(child, this)).join(""),
  visitElement: (node) => {
    const content = node.children
      .map((child) => transform(child, this))
      .join("");
    return `<span class="${node.tagName}">${content}</span>`;
  },
  visitText: (node) => node.content,
});

Tree Manipulation

Adding and Removing Nodes

import { appendChild, removeChild, replaceChild, cloneNode } from "@taml/ast";

// Add child
const newText = createText("New content");
appendChild(element, newText);

// Remove child
removeChild(newText);

// Replace child
const replacement = createText("Replacement");
replaceChild(element, newText, replacement);

// Clone subtree
const clone = cloneNode(element);

Navigation

import {
  getRoot,
  getAncestors,
  getSiblings,
  getPreviousSibling,
  getNextSibling,
} from "@taml/ast";

// Navigate tree
const root = getRoot(textNode);
const ancestors = getAncestors(textNode);
const siblings = getSiblings(textNode);
const prev = getPreviousSibling(textNode);
const next = getNextSibling(textNode);

Integration with TAML Ecosystem

With Parser

import { parse } from "@taml/parser";
import { visit, getAllText } from "@taml/ast";

// Parse TAML markup into AST
const ast = parse("<red>Hello <bold>World</bold>!</red>");

// Process the AST
const text = getAllText(ast);
visit(ast, {
  visitElement: (node) => console.log(`Element: ${node.tagName}`),
});

With React

import { createElement, createText } from '@taml/ast';
import { TamlRenderer } from '@taml/react';

// Create AST
const element = createElement('red', [
  createText('Hello '),
  createElement('bold', [createText('World')])
]);

// Render with React
function MyComponent() {
  return <TamlRenderer ast={element} />;
}

With CLI Tools

# Convert ANSI to TAML, then parse to AST
echo -e "\033[31mHello\033[0m" | taml-cli encode | node -e "
  const { parse } = require('@taml/parser');
  const { getAllText } = require('@taml/ast');

  let input = '';
  process.stdin.on('data', chunk => input += chunk);
  process.stdin.on('end', () => {
    const ast = parse(input.trim());
    console.log(getAllText(ast));
  });
"

Complete Pipeline Example

import { encode } from '@taml/encoder';
import { parse } from '@taml/parser';
import { visit, getAllText, getElementsWithTag } from '@taml/ast';

// Complete ANSI → TAML → AST → Analysis pipeline
const ansiText = '\033[31mError:\033[0m \033[1mFile not found\033[0m';

// 1. Convert ANSI to TAML
const tamlMarkup = encode(ansiText);
console.log(tamlMarkup); // "<red>Error:</red> <bold>File not found</bold>"

// 2. Parse TAML to AST
const ast = parse(tamlMarkup);

// 3. Analyze AST
const plainText = getAllText(ast);
const redElements = getElementsWithTag(ast, 'red');
const boldElements = getElementsWithTag(ast, 'bold');

console.log('Plain text:', plainText);
console.log('Red elements:', redElements.length);
console.log('Bold elements:', boldElements.length);

// 4. Custom processing
visit(ast, {
  visitElement: (node) => {
    if (node.tagName === 'red') {
      console.log('Found error styling');
    }
  }
});

API Reference

Types and Constants

Core Types

// Node types
type NodeType = "document" | "element" | "text";

// TAML tag types
type TamlTag =
  | "black"
  | "red"
  | "green"
  | "yellow"
  | "blue"
  | "magenta"
  | "cyan"
  | "white"
  | "brightBlack"
  | "brightRed"
  | "brightGreen"
  | "brightYellow"
  | "brightBlue"
  | "brightMagenta"
  | "brightCyan"
  | "brightWhite"
  | "bgBlack"
  | "bgRed"
  | "bgGreen"
  | "bgYellow"
  | "bgBlue"
  | "bgMagenta"
  | "bgCyan"
  | "bgWhite"
  | "bgBrightBlack"
  | "bgBrightRed"
  | "bgBrightGreen"
  | "bgBrightYellow"
  | "bgBrightBlue"
  | "bgBrightMagenta"
  | "bgBrightCyan"
  | "bgBrightWhite"
  | "bold"
  | "dim"
  | "italic"
  | "underline"
  | "strikethrough";

// Position information
interface Position {
  start: number;
  end: number;
  line: number;
  column: number;
}

Constants

// Tag collections
const STANDARD_COLORS: readonly string[];
const BRIGHT_COLORS: readonly string[];
const BACKGROUND_COLORS: readonly string[];
const TEXT_STYLES: readonly string[];
const VALID_TAGS: Set<TamlTag>;

Type Guards

// Tag validation
function isValidTag(tag: string): tag is TamlTag;
function isStandardColor(tag: TamlTag): boolean;
function isBrightColor(tag: TamlTag): boolean;
function isBackgroundColor(tag: TamlTag): boolean;
function isTextStyle(tag: TamlTag): boolean;

// Node type guards
function isDocumentNode(node: TamlNode): node is DocumentNode;
function isElementNode(node: TamlNode): node is ElementNode;
function isTextNode(node: TamlNode): node is TextNode;

Node Operations

Factory Functions

// Create nodes
function createDocument(
  children?: TamlNode[],
  start?: number,
  end?: number,
): DocumentNode;
function createElement(
  tagName: TamlTag,
  children?: TamlNode[],
  start?: number,
  end?: number,
): ElementNode;
function createText(content: string, start?: number, end?: number): TextNode;

Tree Manipulation

// Modify tree structure
function appendChild(parent: DocumentNode | ElementNode, child: TamlNode): void;
function removeChild(child: TamlNode): void;
function replaceChild(
  parent: DocumentNode | ElementNode,
  oldChild: TamlNode,
  newChild: TamlNode,
): void;
function cloneNode(node: TamlNode): TamlNode;

Navigation

// Tree navigation
function getRoot(node: TamlNode): DocumentNode;
function getAncestors(node: TamlNode): TamlNode[];
function getDepth(node: TamlNode): number;
function getSiblings(node: TamlNode): TamlNode[];
function getPreviousSibling(node: TamlNode): TamlNode | null;
function getNextSibling(node: TamlNode): TamlNode | null;

Visitor Pattern

Interfaces

// Visitor interface
interface Visitor {
  visitDocument?(node: DocumentNode): void;
  visitElement?(node: ElementNode): void;
  visitText?(node: TextNode): void;
  enterNode?(node: TamlNode): void;
  exitNode?(node: TamlNode): void;
}

// Transformer interface
interface Transformer<T = TamlNode> {
  visitDocument?(node: DocumentNode): T;
  visitElement?(node: ElementNode): T;
  visitText?(node: TextNode): T;
}

// Async visitor interface
interface AsyncVisitor {
  visitDocument?(node: DocumentNode): Promise<void>;
  visitElement?(node: ElementNode): Promise<void>;
  visitText?(node: TextNode): Promise<void>;
  enterNode?(node: TamlNode): Promise<void>;
  exitNode?(node: TamlNode): Promise<void>;
}

Visitor Functions

// Core visitor functions
function visit(node: TamlNode, visitor: Visitor): void;
function transform<T>(node: TamlNode, transformer: Transformer<T>): T;
function visitAsync(node: TamlNode, visitor: AsyncVisitor): Promise<void>;

Helper Visitors

// Specialized visitors
function createTypedVisitor<T extends TamlNode>(
  nodeTest: (node: TamlNode) => node is T,
  callback: (node: T) => void,
): Visitor;

function createCollectorVisitor<T extends TamlNode>(
  nodeTest: (node: TamlNode) => node is T,
): Visitor & { nodes: T[] };

function createCounterVisitor(): Visitor & {
  counts: { document: number; element: number; text: number };
};

Traversal Utilities

Basic Traversal

// Tree walking
function walk(node: TamlNode, callback: (node: TamlNode) => void): void;
function walkAsync(
  node: TamlNode,
  callback: (node: TamlNode) => Promise<void>,
): Promise<void>;

Search Functions

// Finding nodes
function findAll(
  root: TamlNode,
  predicate: (node: TamlNode) => boolean,
): TamlNode[];
function findFirst(
  root: TamlNode,
  predicate: (node: TamlNode) => boolean,
): TamlNode | null;
function filter(
  root: TamlNode,
  predicate: (node: TamlNode) => boolean,
): TamlNode[];

Specialized Queries

// Content extraction
function getAllText(root: TamlNode): string;
function getElementsWithTag(root: TamlNode, tagName: TamlTag): ElementNode[];
function getTextNodes(root: TamlNode): TextNode[];
function getElementNodes(root: TamlNode): ElementNode[];

Tree Analysis

// Tree relationships
function contains(ancestor: TamlNode, descendant: TamlNode): boolean;
function getCommonAncestor(node1: TamlNode, node2: TamlNode): TamlNode | null;

// Tree properties
function isEmpty(node: TamlNode): boolean;
function countNodes(root: TamlNode): {
  document: number;
  element: number;
  text: number;
};
function getMaxDepth(root: TamlNode): number;

// Tree utilities
function flatten(root: TamlNode): TamlNode[];
function createDepthMap(root: TamlNode): Map<TamlNode, number>;

Advanced Topics

Performance Considerations

Memory Management

// Efficient tree construction
const document = createDocument();
const elements = [];

// Build elements first, then add to tree
for (const data of largeDataSet) {
  elements.push(createElement(data.tag, [createText(data.content)]));
}

// Add all at once to minimize parent reference updates
elements.forEach((element) => appendChild(document, element));

Large Tree Traversal

// Use early termination for large trees
function findFirstError(root: TamlNode): ElementNode | null {
  return findFirst(
    root,
    (node): node is ElementNode =>
      node.type === "element" && node.tagName === "red",
  ) as ElementNode | null;
}

// Prefer specific queries over general traversal
const redElements = getElementsWithTag(root, "red"); // Faster
// vs
const redElements2 = findAll(
  root,
  (node) => node.type === "element" && node.tagName === "red",
); // Slower

Error Handling Patterns

// Safe tree navigation
function safeGetText(node: TamlNode): string {
  try {
    return getAllText(node);
  } catch (error) {
    console.warn("Failed to extract text:", error);
    return "";
  }
}

// Validate tree structure
function validateTree(root: TamlNode): boolean {
  try {
    let isValid = true;
    visit(root, {
      enterNode: (node) => {
        if (node.parent && !contains(node.parent, node)) {
          isValid = false;
          throw new Error("Invalid parent reference");
        }
      },
    });
    return isValid;
  } catch {
    return false;
  }
}

Integration Patterns

Parser Integration

// Error-tolerant parsing workflow
import { parse, ParseError } from "@taml/parser";

function parseWithFallback(markup: string): DocumentNode {
  try {
    return parse(markup);
  } catch (error) {
    if (error instanceof ParseError) {
      // Create fallback AST with error information
      return createDocument([
        createElement("red", [createText("Parse Error: ")]),
        createText(error.message),
      ]);
    }
    throw error;
  }
}

Custom Serialization

// Serialize AST to custom format
function serializeToJson(node: TamlNode): any {
  return transform(node, {
    visitDocument: (node) => ({
      type: "document",
      children: node.children.map((child) => serializeToJson(child)),
    }),
    visitElement: (node) => ({
      type: "element",
      tag: node.tagName,
      children: node.children.map((child) => serializeToJson(child)),
    }),
    visitText: (node) => ({
      type: "text",
      content: node.content,
    }),
  });
}

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

# Clone the repository
git clone https://github.com/suin/taml-ast.git
cd taml-ast

# Install dependencies
bun install

# Run tests
bun test

# Build the project
bun run build

# Lint and format
bun run lint
bun run format

Testing

The project uses Bun for testing with comprehensive test coverage:

# Run all tests
bun test

# Run tests in watch mode
bun test --watch

# Run specific test file
bun test nodes.test.ts

License

MIT © suin


Part of the TAML ecosystem - Visit the TAML Specification for more information about the Terminal ANSI Markup Language.