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 🙏

© 2024 – Pkg Stats / Ryan Hefner

rehype-twoslash

v1.1.1

Published

rehype plugin to process TypeScript and JavaScript code with `twoslash` and highlight it with `starry-night`

Downloads

377

Readme

rehype-twoslash

Build Coverage Downloads Size Sponsors Backers Chat

rehype plugin to process JavaScript and TypeScript code with twoslash and highlight it with starry-night.

Contents

What is this?

This package is a unified (rehype) plugin to process JavaScript and TypeScript code with twoslash and highlight it with starry-night.

twoslash is a tool to run code through the TypeScript compiler and extract info about that code. Info you also see in your editor. This info can for example be type errors or type info that is shown on hover. twoslash also supports a command syntax through comments in the code, so an author can highlight a particular piece of code, ignore certain errors, or show a specific file.

starry-night is a beautiful syntax highlighter, like what GitHub uses to highlight code, but free and in JavaScript.

When should I use this?

This plugin is particularly useful for your own website or blog, or any place where you want to talk about JavaScript-y code, and want to improve the experience of your readers by showing them more info about the code.

You can combine this package with rehype-starry-night. That applies syntax highlighting with starry-night to all code.

If you are not using remark or rehype, you can instead use twoslash directly. If you don’t care for starry-night, you can use @shikijs/twoslash.

Install

This package is ESM only. In Node.js (version 16+), install with npm:

npm install rehype-twoslash

In Deno with esm.sh:

import rehypeTwoslash from 'https://esm.sh/rehype-twoslash@1'

In browsers with esm.sh:

<script type="module">
  import rehypeTwoslash from 'https://esm.sh/rehype-twoslash@1?bundle'
</script>

Use

Say we have the following file example.md:

# Jupiter

```js twoslash
const name = 'Jupiter'
console.log('Hello, ' + name + '!')
```

…and our module example.js contains:

import rehypeStringify from 'rehype-stringify'
import rehypeTwoslash from 'rehype-twoslash'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import {read} from 'to-vfile'
import {unified} from 'unified'

const file = await read('example.md')

await unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypeTwoslash)
  .use(rehypeStringify)
  .process(file)

console.log(String(file))

…then running node example.js yields:

<h1>Jupiter</h1>
<div class="highlight highlight-js">
<pre><code class="language-js"><span class="pl-k">const</span> <span class="rehype-twoslash-popover-target" data-popover-target="rehype-twoslash-cnJcnJcn-0"><span class="pl-c1">name</span></span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>Jupiter<span class="pl-pds">'</span></span>
<span class="rehype-twoslash-popover-target" data-popover-target="rehype-twoslash-cnJcnJcn-1"><span class="pl-en">console</span></span>.<span class="rehype-twoslash-popover-target" data-popover-target="rehype-twoslash-cnJcnJcn-2"><span class="pl-c1">log</span></span>(<span class="pl-s"><span class="pl-pds">'</span>Hello, <span class="pl-pds">'</span></span> <span class="pl-k">+</span> <span class="rehype-twoslash-popover-target" data-popover-target="rehype-twoslash-cnJcnJcn-3">name</span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">'</span>!<span class="pl-pds">'</span></span>)
</code></pre>
<div class="rehype-twoslash-hover rehype-twoslash-popover" id="rehype-twoslash-cnJcnJcn-0" popover=""><pre class="rehype-twoslash-popover-code"><code class="language-ts"><span class="pl-k">const</span> <span class="pl-c1">name</span><span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">"</span>Jupiter<span class="pl-pds">"</span></span></code></pre></div>
<div class="rehype-twoslash-hover rehype-twoslash-popover" id="rehype-twoslash-cnJcnJcn-1" popover=""><pre class="rehype-twoslash-popover-code"><code class="language-ts"><span class="pl-k">var</span> <span class="pl-smi">console</span><span class="pl-k">:</span> <span class="pl-en">Console</span></code></pre></div>
<div class="rehype-twoslash-hover rehype-twoslash-popover" id="rehype-twoslash-cnJcnJcn-2" popover=""><pre class="rehype-twoslash-popover-code"><code class="language-ts">(<span class="pl-smi">method</span>) <span class="pl-c1">Console</span>.<span class="pl-en">log</span>(<span class="pl-k">...</span><span class="pl-smi">data</span>: <span class="pl-smi">any</span>[]): <span class="pl-k">void</span></code></pre><div class="rehype-twoslash-popover-description"><p><a href="https://developer.mozilla.org/docs/Web/API/console/log_static">MDN Reference</a></p></div></div>
<div class="rehype-twoslash-hover rehype-twoslash-popover" id="rehype-twoslash-cnJcnJcn-3" popover=""><pre class="rehype-twoslash-popover-code"><code class="language-ts"><span class="pl-k">const</span> <span class="pl-c1">name</span><span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">"</span>Jupiter<span class="pl-pds">"</span></span></code></pre></div>
</div>

With some CSS and JavaScript that could look like this:

API

Options

Configuration for rehype-twoslash.

Notes

rehype-twoslash runs on <code> elements with a twoslash directive. That directive can be passed as a word in markdown (```ts twoslash) or as a class in HTML (<code class="language-ts twoslash">).

The inverse occurs when directive is false. All <code> where the language class is JavaScript or TypeScript is processed. Then no-twoslash (```ts no-twoslash, <code class="language-ts no-twoslash">) can be used to prevent processing.

Fields
  • directive? (boolean | null | undefined) — whether to require a twoslash directive (default: true)
  • grammars? (ReadonlyArray<Grammar> | null | undefined) — grammars for starry-night to support (default: [sourceJson, sourceJs, sourceTsx, sourceTs])
  • idPrefix? (string | null | undefined) — prefix before IDs (default: 'rehype-twoslash-')
  • renderers? (Renderers | null | undefined) — renderers for twoslash annotations (optional)
  • twoslash? (TwoslashOptions | null | undefined) — options passed to twoslash (optional); this includes fields such as cache, customTransformers, and filterNode; see TwoslashOptions from twoslash for more info

Render

Render function.

Takes a particular annotation from the TypeScript compiler (such as an error) and turns it into hast (HTML) content. See lib/render.js for examples.

Notes

You can return Array<ElementContent> directly instead of a RenderResult when you don’t have content for a footer.

Type
(
  state: State,
  annotation: Annotation,
  children: Array<ElementContent>
) => Array<ElementContent> | RenderResult

RenderResult

Result from Render.

Fields
  • content? (Array<ElementContent> | undefined) — main inline content to use in the code block; for example a <span> that causes a tooltip to show
  • footer? (Array<ElementContent> | undefined) — extra content to use that relates to the code block; for example a <div> for a tooltip

Renderers

Renderers.

Each key is a type of an annotation (such as error or hover) and each value a corresponding render function.

Type
{ completion?: Render<NodeCompletion> | null | undefined; error?: Render<NodeError> | null | undefined; highlight?: Render<NodeHighlight> | null | undefined; hover?: Render<...> | ... 1 more ... | undefined; query?: Render<...> | ... 1 more ... | undefined; }

rehypeTwoslash(options) (default)

Plugin to process JavaScript and TypeScript code with twoslash and highlight it with starry-night.

Parameters
  • options? (Readonly<Options> | null | undefined) — configuration (optional)
Returns

Transform ((tree: Root, file: VFile) => Promise<Root>).

HTML

On the input side, this plugin looks for code blocks with a twoslash class. So:

<pre><code class="language-ts twoslash">console.log('Hello, Mercury!')</code></pre>

It will warn when that class is used with a programming language that twoslash does not understand (such as Rust).

If you want to process all JavaScript and TypeScript code blocks, you can set directive: false in options. Then the language-* class is enough and no directive is needed. You can still prevent processing of a particular block with a no-twoslash class:

<pre><code class="language-ts no-twoslash">console.log('Hello, Mars!')</code></pre>

On the output side, this plugin generates markup that can be enhanced with CSS and JavaScript into tooltips and more. You can also choose to generate different HTML by passing custom render functions in options.renderers.

To illustrate, here is an example of a tooltip target for the identifier in a variable declaration (const name = …):

<span
  class="rehype-twoslash-popover-target"
  data-popover-target="rehype-twoslash-cnJcnJcn-3"
><span class="pl-c1">name</span></span>

It has a corresponding tooltip:

<div
  class="rehype-twoslash-hover rehype-twoslash-popover"
  id="rehype-twoslash-cnJcnJcn-3"
  popover=""
>
  <pre class="rehype-twoslash-popover-code"><code class="language-ts"><span class="pl-k">const</span> <span class="pl-c1">name</span><span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">"</span>Jupiter<span class="pl-pds">"</span></span></code></pre>
</div>

Observe that there are sufficient classes to hook into with CSS and JavaScript and that unique identifiers connect the popover and its popover target together.

Markdown

When combined with remark-parse and remark-rehype, this plugin works similarly on markdown to how it does on HTML as described above. It then understands the twoslash and no-twoslash word in the info string, right after the language. To illustrate:

```ts twoslash
console.log('Hello, Venus!')
```

```ts no-twoslash
console.log('Hello, Earth!')
```

CSS

This plugin generates sufficient classes that can be styled with CSS. Which ones to use and how to style them depends on the rest of your website and your heart’s desire. To illustrate, see demo/index.css. But get creative!

JavaScript

This plugin generates markup that needs to be made interactive with JavaScript. What to do exactly, and how to do it, depends on your website and your preferences. For inspiration, see demo/index.js.

Compatibility

Projects maintained by the unified collective are compatible with maintained versions of Node.js.

When we cut a new major release, we drop support for unmaintained versions of Node. This means we try to keep the current release line, rehype-twoslash@1, compatible with Node.js 16.

Security

Use of rehype-twoslash is likely not safe on arbitrary user content, as it passes code through the TypeScript compiler, which I assume has some access to the file system and there might be ways to exploit it.

Related

Contribute

See contributing.md in rehypejs/.github for ways to get started. See support.md for ways to get help.

This project has a code of conduct. By interacting with this repository, organization, or community you agree to abide by its terms.

License

MIT © Titus Wormer