rehype-twoslash v1.1.1
rehype-twoslash
rehype plugin to process JavaScript and TypeScript code
with twoslash and highlight it with
starry-night.
Contents
- What is this?
- When should I use this?
- Install
- Use
- API
- HTML
- Markdown
- CSS
- JavaScript
- Compatibility
- Security
- Related
- Contribute
- License
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-twoslashIn 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 atwoslashdirective (default:true)grammars?(ReadonlyArray<Grammar> | null | undefined) — grammars forstarry-nightto support (default:[sourceJson, sourceJs, sourceTsx, sourceTs])idPrefix?(string | null | undefined) — prefix before IDs (default:'rehype-twoslash-')renderers?(Renderers | null | undefined) — renderers fortwoslashannotations (optional)twoslash?(TwoslashOptions | null | undefined) — options passed totwoslash(optional); this includes fields such ascache,customTransformers, andfilterNode; seeTwoslashOptionsfromtwoslashfor 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> | RenderResultRenderResult
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 showfooter?(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
rehype-starry-night— apply syntax highlighting withstarry-nightto all code
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.