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

@taprootio/wtfm

v0.15.0

Published

WTFM — documentation tooling for Custom Elements Manifest-based component libraries

Readme

Write the F*in Manual

Tools for documentation driven development: an Eleventy plugin and supporting tooling for documenting component libraries from their Custom Elements Manifest.

Install

npm install --save-dev @taprootio/wtfm

Entry points

  • @taprootio/wtfm (or /plugin) — the Eleventy plugin.
  • /type-extractor — TypeScript type extraction for manifest docs.
  • /renderers — section renderers for manifest-driven doc pages.
  • /anchors — the shared slug, explicit-id, and Markdown anchor helpers.
  • /urls — root-absolute and document-relative URL helpers.
  • /surfaces — surface collection and validation helpers.
  • /help-document — the restricted semantic Markdown renderer for help pages.
  • /help-manifest, /check-help-anchors — help link-index generation and consumer compatibility checks.
  • /data/components, /data/surfaces, /data/types, /data/manifest — data helpers.
  • /bundler/manifest, /bundler/copy-assets — bundler plugins.
  • /client/runtime, /client/code-block, /client/theme, /client/oklch — client-side runtime pieces.
  • /cem-plugin — Custom Elements Manifest analyzer plugin.
  • /validate-manifest — manifest validation.

Stable heading anchors

The Eleventy plugin gives every Markdown heading an id. Generated ids use lowercase kebab-case (CSS Properties becomes css-properties). Generated section/item headings use the same convention. Item ids include their section to avoid cross-section collisions (attributes--icon and slots--icon). Duplicate ids fail the build instead of being silently renumbered, because published fragment URLs are a compatibility contract.

Pin an exact, case-sensitive id in authored Markdown with an id-only heading attribute:

## Page title {#Title}

Generated renderer items may instead carry @helpAnchor Title in their JSDoc. Register the tag alongside the other WTFM tags when configuring @wc-toolkit/jsdoc-tags:

helpAnchor: {
  description: "Exact stable id for this documentation heading",
  type: "string",
  tagMapping: "helpAnchor",
},

When component docs are composed into a surface, generated ids are additionally namespaced by custom-element tag (for example article-fields--attributes--title). Explicit ids are never changed or namespaced. CEM events without names are omitted with a build warning because they cannot receive a stable semantic anchor.

Composed documentation surfaces

A surface is an ordered group of custom elements with one reference page and one separately authored help page. Declare its stable identity and members on the owning custom element:

/**
 * @docSurface settings
 * @docSurfaceTitle Settings Surface
 * @docSurfaceParts surface-shell, surface-panel
 * @menuLabel Settings
 * @menuOrder 3
 */
export class SurfaceShell extends HTMLElement {}

Register the same tags with jsDocTagsPlugin when generating the CEM:

const surfaceTags = Object.fromEntries(
  ["docSurface", "docSurfaceTitle", "docSurfaceParts"].map((name) => [
    name,
    { type: "string", tagMapping: name },
  ]),
);

The plugin exposes validated surfaces as docSurfaces global data and provides renderSurfaceDocs(slug) for the reference page. The default routes are /surfaces/<slug>/ and /surfaces/<slug>/help/; consumers can supply referenceUrlBuilder and helpUrlBuilder plugin options to own those routes. Use /data/surfaces when a separate pagination data file is preferable.

Surface member order follows @docSurfaceParts. Unknown or ambiguous member tags, duplicate slugs or members, missing metadata, and invalid slugs stop the build with an actionable error. Existing @menuLabel, @menuIcon, @menuGroup, and @menuOrder metadata is reused for navigation. Surface slugs and their derived URLs are permanent link targets; renaming one after release is a breaking documentation change.

Authored help documents

Reference docs and help prose are separate inputs. Use renderHelpDocs in a surface help template, passing the surface slug and authored Markdown:

export default async function (data) {
  return this.renderHelpDocs(data.surface.slug, data.helpMarkdown);
}

The equivalent standalone API is renderHelpDocument(markdown, { documentUrl }) from /help-document. Raw HTML and MathJax are disabled. Output is limited to headings, paragraphs, emphasis, lists, tables, code, blockquotes, images, links, horizontal rules, and line breaks. Only heading id, link href, and image src/alt attributes are emitted; wrappers, classes, styles, scripts, and framework attributes fail the output contract.

Pin field-level anchors with exact, case-sensitive heading ids such as ## Title {#Title}. Relative links and images are resolved from the surface's help route into root-absolute URLs before Eleventy applies its pathPrefix.

Help manifest and anchor compatibility

After a filesystem build, the Eleventy plugin writes help-manifest.json at the output root. Its versioned entries contain each surface slug, the final path-prefixed reference and help URLs, and help heading ids in document order:

{
  "schemaVersion": 1,
  "surfaces": [
    {
      "slug": "settings",
      "referenceUrl": "/help/surfaces/settings/",
      "helpUrl": "/help/surfaces/settings/help/",
      "anchors": ["settings-help", "Title"]
    }
  ]
}

Consumers keep their field-name contract in a separate versioned file:

{
  "schemaVersion": 1,
  "surfaces": {
    "settings": ["Title"]
  }
}

Run the checker after the documentation build in consumer CI:

npx wtfm-check-help-anchors \
  _site/help-manifest.json expected-help-anchors.json

Missing surfaces or anchors fail. Extra built surfaces and anchors warn by default because they may be intentional additions; pass --strict to make them fail too. Duplicate, malformed, and schema-version-mismatched inputs always fail.

Path-prefixed Eleventy sites

WTFM emits internal page, breadcrumb, and bundler-manifest asset URLs as root-absolute paths. Do not add the deployment prefix to those values. Let Eleventy apply it once to final HTML with HtmlBasePlugin:

import { HtmlBasePlugin } from "@11ty/eleventy";
import wtfmPlugin from "@taprootio/wtfm";

export default function (eleventyConfig) {
  eleventyConfig.addPlugin(HtmlBasePlugin);
  eleventyConfig.addPlugin(wtfmPlugin, {
    cemPath: "custom-elements.json",
  });
}

export const config = {
  pathPrefix: "/help/",
};

This transforms /components/button/ and /dist/docs.js to /help/components/button/ and /help/dist/docs.js in built HTML while leaving external and fragment-only URLs unchanged. WTFM also applies the prefix to root-absolute URLs inside demo HTML before that source is base64 encoded for <wtfm-code-block>. Relative and external demo URLs remain unchanged. inlineSvg reads and returns file content and therefore needs no URL prefix. The WTFM client runtime constructs no asset or navigation URLs of its own.