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

@rettangoli/sites

v1.2.0

Published

Generate static sites using Markdown and YAML for docs, blogs, and marketing sites.

Readme

Rettangoli Sites

@rettangoli/sites is the static-site engine used by Rettangoli. It renders pages from YAML and Markdown into _site/, with support for templates, partials, global data, collections, and watch mode.

It can run directly with bunx rtgl, so a site-level package.json is optional.

Quick Start

# scaffold
bunx rtgl sites init my-site

# run
cd my-site
bunx rtgl sites build

Package Contract

my-site/
  pages/           # YAML or Markdown pages (with optional frontmatter)
  templates/       # YAML templates
  partials/        # YAML partials
  data/            # Global YAML data
  static/          # Static assets copied to _site/
  sites.config.yaml # Optional site settings
  _site/           # Generated output

What It Supports

  • YAML pages rendered through jempl + yahtml
  • Markdown pages rendered through markdown-it + Shiki (default rtglMarkdown)
  • Frontmatter (template, url, tags, arbitrary page metadata)
  • Global data from data/*.yaml and optional inline sites.config.yaml data
  • Collections built from page tags
  • $if, $for, $partial, template functions
  • Static file copying from static/ to _site/
  • Default sitemap generation when data.site.baseUrl is configured
  • Watch mode with local dev server + websocket reload

Site Config

Use sites.config.yaml (or sites.config.yml) with top-level markdownit for supported settings. Legacy key markdown is still accepted as an alias.

markdownit:
  preset: default
  html: true
  xhtmlOut: false
  linkify: true
  typographer: false
  breaks: false
  langPrefix: language-
  quotes: "\u201c\u201d\u2018\u2019"
  maxNesting: 100
  shiki:
    enabled: true
    theme: slack-dark
  codePreview:
    enabled: false
    showSource: true
    theme: slack-dark
  headingAnchors:
    enabled: true
    slugMode: unicode
    wrap: true
    fallback: section
build:
  keepMarkdownFiles: false
imports:
  templates:
    base: https://example.com/templates/base.yaml
    docs: https://example.com/templates/docs.yaml
  partials:
    docs/nav: https://example.com/partials/docs-nav.yaml
data:
  site:
    baseUrl: https://example.com
  themeCssHref: /public/theme.css
  themeBodyClass: dark
sitemap:
  outputPath: sitemap.xml
  defaults:
    changefreq: weekly
    priority: 0.5
  exclude:
    - /drafts/*
  pages:
    /:
      priority: 1

In the default starter template, CDN runtime scripts are controlled via data/site.yaml:

assets:
  loadUiFromCdn: true
  loadConstructStyleSheetsPolyfill: true

Enable codePreview if you want fenced blocks like ```html codePreview to render a live preview panel. Use showSource to show/hide the source pane and theme to override the highlight theme for preview blocks.

Set build.keepMarkdownFiles: true to keep source Markdown files in output in addition to generated HTML. Example mappings:

  • pages/index.md -> _site/index.html and _site/index.md
  • pages/docs/intro.md -> _site/docs/intro/index.html and _site/docs/intro.md

For Markdown pages with a custom url, the copied .md file follows the custom URL path. For example, url: /guides/start/ writes _site/guides/start/index.html and _site/guides/start.md.

Pages use their file path as the URL by default:

  • pages/index.* -> /
  • pages/about.* -> /about/
  • pages/docs/intro.* -> /docs/intro/

Set url in page frontmatter to override that path:

---
title: Company
url: /company/
---

url is normalized to a site-relative clean URL with a leading and trailing slash, so company becomes /company/. External URLs, query strings, fragments, whitespace, and . / .. path segments are rejected. Duplicate page URLs are rejected after normalization.

Sitemap

Sites writes _site/sitemap.xml by default when data.site.baseUrl is configured. Use sitemap in sites.config.yaml to customize output, or set sitemap: false to disable it.

data:
  site:
    baseUrl: https://example.com
sitemap:
  outputPath: sitemap.xml
  defaults:
    changefreq: weekly
    priority: 0.5
  exclude:
    - /drafts/*
  pages:
    /:
      priority: 1
      changefreq: daily
      lastmod: "2026-05-25"
    /private/: false

If you do not use data.site.baseUrl, set sitemap.siteUrl instead. Generated entries use normalized page URLs, including page frontmatter url overrides. Use page frontmatter for per-page control:

---
sitemap:
  changefreq: monthly
  priority: 0.8
  lastmod: "2026-05-25"
---

Set sitemap: false in page frontmatter to exclude one page. sitemap.exclude accepts exact page URLs and prefix patterns ending in *, such as /drafts/*.

imports lets you map aliases to remote YAML files (HTTP/HTTPS only). Use aliases in pages/templates:

  • page frontmatter: template: base or template: docs
  • template/page content: $partial: docs/nav

Use top-level data in sites.config.yaml for small global values that do not deserve their own data/*.yaml file. sites.config.yaml data and data/*.yaml are merged, with data/*.yaml winning on conflicts. Inline config data requires rtgl >= 1.1.4 or @rettangoli/sites >= 1.0.3.

Imported files are cached on disk under .rettangoli/sites/imports/{templates|partials}/ (hashed filenames). Alias/url/hash mapping is tracked in .rettangoli/sites/imports/index.yaml. Build is cache-first: if a cached file exists, it is used without a network request.

When an alias exists both remotely and locally, local files under templates/ and partials/ override the imported one.

If you want to publish a manual llms.txt, place it in static/llms.txt; it will be copied to _site/llms.txt.

System Frontmatter

Use _bind to map global data keys into page-local variables.

Example:

---
template: base
_bind:
  docs: feDocs
---

This resolves docs from data/feDocs.yaml for that page. _bind is a system property and is not exposed to templates directly.

Rules:

  • _bind must be an object
  • each _bind value must be a non-empty string
  • each _bind value must point to an existing data/*.yaml key
  • _bind is removed from public frontmatter before rendering/collections

Binding order:

  1. build page context from deepMerge(globalData, frontmatterWithoutSystemKeys)
  2. apply _bind aliases on top (alias wins for that key)

Reusable Asset Package

@rettangoli/sites is the engine only.

Reusable themes, templates, partials, helper assets, schemas, and VT coverage now live in packages/rettangoli-sitekit/ and publish from @rettangoli/sitekit.

Use @rettangoli/sitekit when you want curated importable site assets. Keep @rettangoli/sites for build/watch/init behavior.

Template Authoring Pattern

Keep base templates as shells with minimal logic:

  • document root (html, head, body)
  • main content slot ("${content}")
  • stable layout containers

Put variant-specific behavior and data wiring in partials instead. Partials accept explicit parameters via $partial, so they are the preferred place for:

  • section-specific navigation data
  • conditional UI branches
  • reusable interactive blocks

This keeps one template reusable across many page variants and avoids duplicated template files.

Commands

bunx rtgl sites build
bunx rtgl sites watch
bunx rtgl sites build --quiet
bunx rtgl sites watch --quiet
bunx rtgl sites watch --reload-mode full
bunx rtgl sites build --root-dir . --output-path dist
bunx rtgl sites watch --root-dir . --output-path dist --reload-mode full

--reload-mode body (default) does fast body replacement; --reload-mode full forces full page refresh. --root-dir/--output-path are the preferred option names (--rootDir/--outputPath remain as legacy aliases).

Built-in Template Functions

Available in YAML templates/pages without extra setup:

  • encodeURI(value)
  • encodeURIComponent(value)
  • decodeURI(value)
  • decodeURIComponent(value)
  • jsonStringify(value, space = 0)
  • formatDate(value, format = "YYYYMMDDHHmmss", useUtc = true)
  • now(format = "YYYYMMDDHHmmss", useUtc = true)
  • sort(list, key, order = "asc")
  • chunk(list, size = 1, pad = false, fillValue = null)
  • md(content)
  • toQueryString(object)

formatDate tokens: YYYY, MMM, MM, DD, D, HH, mm, ss. decodeURI/decodeURIComponent return the original input when decoding fails. sort supports order as asc or desc (default: asc), accepts dot-path keys (for example data.date), and returns a new array. chunk splits arrays into rows of size; with pad = true, the last row is padded with fillValue. md returns raw rendered HTML from Markdown for template insertion.

Screenshots

@rettangoli/sites builds pages; screenshot capture is handled by @rettangoli/vt.

Use VT against your generated site:

  1. Add vt/specs/*.html specs (use frontmatter url for the page to capture).
  2. Add vt config in rettangoli.config.yaml.
  3. Run rtgl vt generate, rtgl vt report, and rtgl vt accept.

Docker runtime (recommended for stable Playwright/browser versions):

IMAGE="han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.1.0"
docker pull "$IMAGE"
docker run --rm -v "$PWD:/workspace" -w /workspace "$IMAGE" rtgl vt screenshot
docker run --rm -v "$PWD:/workspace" -w /workspace "$IMAGE" rtgl vt report
docker run --rm -v "$PWD:/workspace" -w /workspace "$IMAGE" rtgl vt accept

Example:

vt:
  path: ./vt
  url: http://127.0.0.1:4173
  service:
    start: bun run preview
  sections:
    - title: pages
      files: .
---
title: home
url: /
---
<div></div>

bun run preview (or any equivalent local server command) must serve your built site on vt.url (for example serving _site/ on port 4173).

For a maintained example asset pack and VT lab, see packages/rettangoli-sitekit/.

Full Architecture And Analysis

See docs/architecture-and-analysis.md for:

  • End-to-end rendering flow
  • Data/context model used during render
  • URL/output mapping rules
  • Config contract details
  • Full robustness analysis and prioritized improvements