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

@sylphx/synth-html

v0.2.3

Published

High-performance HTML5 parser for Synth - streaming-capable with language-agnostic AST

Downloads

36

Readme

@sylphx/synth-html

High-performance HTML5 parser for Synth. Converts HTML into a language-agnostic AST that works seamlessly with the Synth ecosystem.

Features

  • Language-Agnostic AST: Uses Synth's universal BaseNode interface
  • Full HTML5 Support: DOCTYPE, elements, attributes, void elements, self-closing tags
  • Streaming-Capable: Ready for future streaming implementation
  • Plugin System: Compatible with Synth's transform and visitor plugins
  • Async Support: Both sync and async parsing with automatic async plugin detection
  • TypeScript: Fully typed with comprehensive type utilities

Installation

npm install @sylphx/synth-html

Usage

Basic Parsing

import { parse } from '@sylphx/synth-html'

const tree = parse('<div class="container"><h1>Hello</h1></div>')

Using the Parser Class

import { HTMLParser } from '@sylphx/synth-html'

const parser = new HTMLParser()
const tree = parser.parse('<html><body><p>Content</p></body></html>')

// Access the tree
console.log(tree.nodes)

Working with AST Nodes

import {
  parse,
  isElementNode,
  getTagName,
  getAttribute,
  isTextNode,
  getTextValue,
} from '@sylphx/synth-html'

const tree = parse('<div id="main" class="container">Hello World</div>')

// Find elements
const div = tree.nodes.find(n => isElementNode(n) && getTagName(n) === 'div')

// Get attributes
const id = getAttribute(div, 'id') // "main"
const className = getAttribute(div, 'class') // "container"

// Find text nodes
const text = tree.nodes.find(n => isTextNode(n))
const value = getTextValue(text) // "Hello World"

Plugin Support

import { parse } from '@sylphx/synth-html'
import { createTransformPlugin } from '@sylphx/synth'

// Create a plugin
const addIdsPlugin = createTransformPlugin(
  { name: 'add-ids', version: '1.0.0' },
  (tree) => {
    // Transform the tree
    return tree
  }
)

// Use the plugin
const tree = parse('<div>Content</div>', {
  plugins: [addIdsPlugin],
})

Async Parsing

import { parseAsync } from '@sylphx/synth-html'
import { createTransformPlugin } from '@sylphx/synth'

const asyncPlugin = createTransformPlugin(
  { name: 'async-transform', version: '1.0.0' },
  async (tree) => {
    // Async transformation
    await somethingAsync()
    return tree
  }
)

const tree = await parseAsync('<div>Content</div>', {
  plugins: [asyncPlugin],
})

Registered Plugins

import { HTMLParser } from '@sylphx/synth-html'
import { createTransformPlugin } from '@sylphx/synth'

const parser = new HTMLParser()

// Register plugins
parser
  .use(plugin1)
  .use(plugin2)
  .use(plugin3)

// Plugins apply to all parse() calls
const tree = parser.parse('<div>Content</div>')

API Reference

Parsing Functions

  • parse(html, options?): Parse HTML synchronously
  • parseAsync(html, options?): Parse HTML asynchronously
  • createParser(): Create a new parser instance

Type Guards

  • isDocumentNode(node): Check if node is root document
  • isDoctypeNode(node): Check if node is DOCTYPE declaration
  • isElementNode(node): Check if node is an HTML element
  • isTextNode(node): Check if node is a text node
  • isCommentNode(node): Check if node is a comment
  • isCDATANode(node): Check if node is a CDATA section

Data Accessors

Elements:

  • getTagName(node): Get element tag name
  • getAttributes(node): Get all attributes
  • getAttribute(node, name): Get specific attribute
  • isVoidElement(node): Check if element is void (br, img, etc.)
  • isSelfClosing(node): Check if element is self-closing

Text/Comments/CDATA:

  • getTextValue(node): Get text content
  • getCommentValue(node): Get comment content
  • getCDATAValue(node): Get CDATA content

DOCTYPE:

  • getDoctypeName(node): Get DOCTYPE name
  • getDoctypePublicId(node): Get PUBLIC identifier
  • getDoctypeSystemId(node): Get SYSTEM identifier

Parse Options

interface HTMLParseOptions {
  /** Build query index for fast lookups */
  buildIndex?: boolean

  /** Plugins to apply during parsing */
  plugins?: Plugin[]
}

Node Structure

All nodes follow Synth's language-agnostic BaseNode interface:

interface BaseNode {
  id: number
  type: string  // 'element', 'text', 'comment', etc.
  parent: number | null
  children: number[]
  data?: Record<string, unknown>  // HTML-specific data
}

HTML-specific data is stored in the data field:

// Element node
{
  id: 1,
  type: 'element',
  parent: 0,
  children: [2, 3],
  data: {
    tagName: 'div',
    attributes: { class: 'container', id: 'main' },
    selfClosing: false,
    void: false
  }
}

// Text node
{
  id: 2,
  type: 'text',
  parent: 1,
  children: [],
  data: {
    value: 'Hello World'
  }
}

Performance

Built on the same architecture as @sylphx/synth-md (26-42x faster than remark), this parser is designed for:

  • Fast tokenization: Character-based parsing with minimal allocations
  • Efficient tree building: Arena-based storage for cache locality
  • Streaming-ready: Architecture supports future streaming implementation

Examples

Parse a Complete HTML Document

import { parse, isElementNode, getTagName } from '@sylphx/synth-html'

const html = `<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>My Page</title>
  </head>
  <body>
    <h1>Welcome</h1>
    <p>Content here</p>
  </body>
</html>`

const tree = parse(html)

// Find all headings
const headings = tree.nodes.filter(n =>
  isElementNode(n) && getTagName(n)?.startsWith('h')
)

Transform HTML

import { parse, isElementNode, getTagName } from '@sylphx/synth-html'
import { createTransformPlugin } from '@sylphx/synth'

// Add IDs to all headings
const addHeadingIds = createTransformPlugin(
  { name: 'add-heading-ids', version: '1.0.0' },
  (tree) => {
    tree.nodes.forEach(node => {
      if (isElementNode(node)) {
        const tag = getTagName(node)
        if (tag && /^h[1-6]$/.test(tag)) {
          // Add id attribute
          if (!node.data) node.data = {}
          const attrs = node.data.attributes as Record<string, string>
          if (!attrs.id) {
            attrs.id = `heading-${node.id}`
          }
        }
      }
    })
    return tree
  }
)

const tree = parse('<h1>Title</h1><h2>Subtitle</h2>', {
  plugins: [addHeadingIds],
})

License

MIT