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

@twinkleplop/markdown-core

v0.2.1

Published

Shared language configuration, fence metadata and HTML rendering for [`@twinkleplop/rehype`](../rehype), [`@twinkleplop/markdown-it`](../markdown-it) and [`@twinkleplop/remark`](../remark).

Readme

@twinkleplop/markdown-core

Shared language configuration, fence metadata and HTML rendering for @twinkleplop/rehype, @twinkleplop/markdown-it and @twinkleplop/remark.

All three plugins use the options below. markdown-it and remark return HTML strings. rehype parses the HTML into hast nodes by default, which are then serialised by the pipeline's stringifier.

Install the plugin for your markdown processor. It includes this package as a dependency.

Setup

import rehype_twinkleplop from "@twinkleplop/rehype";
import { language as typescript } from "@twinkleplop/typescript";
import { language as css } from "@twinkleplop/css";

.use(rehype_twinkleplop, {
  languages: { ts: typescript({ annotation }), js: "ts", css: css() },
});
```ts {1,3-4} /total/ :line-numbers=10 title="math.ts"
const total = add(1, 2);
const other = 2;
export { total };
export { other };
```

Inline `const x = 1{:ts}` in prose.

Options

| option | default | meaning | | --- | --- | --- | | languages | required | fence name to highlight function, { highlight, twoslash }, or the name of another entry | | default_language | none | used by a fence that names no language | | on_unknown_language | "throw" | "plain" renders the fence as escaped text instead | | line_numbers | false | site default; the meta overrides it | | inline | false | "tailing-curly-colon" highlights `code{:lang}` | | twoslash | "meta" | "always" routes every fence through the entry's twoslash highlighter | | parse_meta | none | (raw, parsed) => render, for custom metadata | | render | {} | render options under the per-fence ones |

A highlight function is what a language package's language(...) returns, so fidelity tiers, annotation plugins and every other per-language option are configured where the entry is created.

Language registry

Values are a highlight function, an entry with a second highlighter for twoslash fences, or the name of another entry. Aliases can refer to other aliases and are resolved during setup. Circular aliases, missing entries and an unregistered default_language cause setup errors.

languages: {
  ts: { highlight: typescript(), twoslash: create_highlighter({ lang: "ts" }) },
  js: "ts",
  mjs: "js",
}

The fence language is the first word of the info string and everything after it is the meta string. Matching is exact and case sensitive: TS is not ts. A fence with no language uses default_language, and with none set it is left exactly as the toolchain rendered it.

In rehype and remark, default_language applies to both indented code blocks and fences without a language. In markdown-it, it applies only to fences. Indented blocks use markdown-it's existing renderer.

Meta conventions

The plugins support the Shiki, VitePress and rehype-pretty-code conventions below. Use parse_meta for custom metadata.

| convention | source | effect | | --- | --- | --- | | {1,3-4} | shiki / VitePress | highlight on those lines | | {1,3-4}#id | rehype-pretty-code | plus data-highlighted-line-id | | /word/ | shiki | highlighted-word on every occurrence, code and comments alike | | /word/3-5 | rehype-pretty-code | only the 3rd to 5th occurrences | | /word/#id | rehype-pretty-code | plus data-chars-id | | :line-numbers, :line-numbers=N, :no-line-numbers | VitePress | numbers on, from N, off | | showLineNumbers, showLineNumbers{N} | rehype-pretty-code | the same two | | [title] | VitePress | title caption | | title="…" | rehype-pretty-code | title caption | | caption="…" | rehype-pretty-code | caption below the block | | twoslash | shiki | route through the entry's twoslash highlighter |

A pattern may hold spaces (/two words/) and escape a slash (/a\/b/). A quoted value and a bracket title may hold spaces too. [!code …] inside the body is not a meta convention: it belongs to the registered language's annotation plugins, and shiki_notation from @twinkleplop/annotation reads it unchanged.

Word IDs are added to the token spans inside the overlay wrapper. The wrapper supports classes only.

Output

A fence without a title or caption renders as a code block:

<pre class="twinkleplop language-ts has-highlight" data-language="ts"><code>…</code></pre>

A title or a caption wraps it in a figure:

<figure class="twinkleplop-block" data-language="ts">
<figcaption class="twinkleplop-title">math.ts</figcaption>
<pre class="twinkleplop language-ts" data-language="ts"><code>…</code></pre>
<figcaption class="twinkleplop-caption">the running total</figcaption>
</figure>

Inline code is <code class="twinkleplop-inline language-ts"> around the inline structure (no block, no line elements, <br> between lines).

The registered highlighter renders the block. Classes and hidden ranges from @twinkleplop/annotation work as they do when calling it directly. class_name from render replaces twinkleplop; language-<name> and data-language use the language name written in the fence, including aliases.

The fence body reaches the highlighter as written: entities decoded, tabs preserved, the fence's trailing newline removed.

parse_meta

parse_meta: (raw, parsed) => ({
  ...parsed,
  class_name: raw.includes("danger") ? parsed.class_name + " danger" : parsed.class_name,
});

raw contains the complete metadata string. parsed contains render options from recognised conventions, including overlays for {1,3-4} and /word/. Return the render options to use for the fence.

Twoslash

A fence marked twoslash uses the entry's Twoslash highlighter. Other fences use highlight. Set twoslash: "always" to use Twoslash by default where available. Requesting Twoslash for an entry without a Twoslash highlighter throws an error identifying the fence.

Twoslash fences render through @twinkleplop/twoslash, which owns its own block markup: the figure, the title and the caption still come from here, but data-language, the line-number and line-highlight conventions do not reach inside that block. Set class_name on the twoslash highlighter to style it.

Diagnostics

Annotation issues use the highlighter's configured on_error handler. Errors thrown by the highlighter keep their class and stack. Plugin errors identify the fence and include the file and line when available:

unknown fence language "rust" at docs/guide.md:41
line 3 is beyond the fence's 2 lines (`ts` fence at docs/guide.md:41)
two title conventions (`ts` fence at docs/guide.md:41)

A line reference beyond the end of a fence throws an error. A /word/ pattern with no matches renders normally.