Socket
Socket
Sign inDemoInstall

rehype-highlight

Package Overview
Dependencies
Maintainers
2
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rehype-highlight - npm Package Compare versions

Comparing version 5.0.1 to 5.0.2

lib/index.d.ts

54

index.d.ts

@@ -1,52 +0,2 @@

/**
* Plugin to highlight the syntax of code with lowlight (`highlight.js`).
*
* @type {import('unified').Plugin<[Options?] | void[], Root>}
*/
export default function rehypeHighlight(
options?: void | Options | undefined
):
| void
| import('unified').Transformer<import('hast').Root, import('hast').Root>
export type LowlightRoot = import('lowlight').Root
export type HighlightSyntax = import('lowlight/lib/core.js').HighlightSyntax
export type Root = import('hast').Root
export type Element = import('hast').Element
export type Node = Root | Root['children'][number]
/**
* Configuration.
*/
export type Options = {
/**
* Prefix to use before classes.
*/
prefix?: string | undefined
/**
* Scope of languages to check when auto-detecting (default: all languages).
* Pass `false` to not highlight code without language classes.
*/
subset?: boolean | string[] | undefined
/**
* Swallow errors for missing languages.
* By default, unregistered syntaxes throw an error when they are used.
* Pass `true` to swallow those errors and thus ignore code with unknown code
* languages.
*/
ignoreMissing?: boolean | undefined
/**
* List of plain-text languages.
* Pass any languages you would like to be kept as plain-text instead of
* getting highlighted.
*/
plainText?: string[] | undefined
/**
* Register more aliases.
* Passed to `lowlight.registerAlias`.
*/
aliases?: Record<string, string | string[]> | undefined
/**
* Register more languages.
* Each key/value pair passed as arguments to `lowlight.registerLanguage`.
*/
languages?: Record<string, import('highlight.js').LanguageFn> | undefined
}
export {default} from './lib/index.js'
export type Options = import('./lib/index.js').Options
/**
* @typedef {import('lowlight').Root} LowlightRoot
* @typedef {import('lowlight/lib/core.js').HighlightSyntax} HighlightSyntax
* @typedef {import('hast').Root} Root
* @typedef {import('hast').Element} Element
* @typedef {Root|Root['children'][number]} Node
*
* @typedef Options
* Configuration.
* @property {string} [prefix='hljs-']
* Prefix to use before classes.
* @property {boolean|string[]} [subset]
* Scope of languages to check when auto-detecting (default: all languages).
* Pass `false` to not highlight code without language classes.
* @property {boolean} [ignoreMissing=false]
* Swallow errors for missing languages.
* By default, unregistered syntaxes throw an error when they are used.
* Pass `true` to swallow those errors and thus ignore code with unknown code
* languages.
* @property {string[]} [plainText=[]]
* List of plain-text languages.
* Pass any languages you would like to be kept as plain-text instead of
* getting highlighted.
* @property {Record<string, string|string[]>} [aliases={}]
* Register more aliases.
* Passed to `lowlight.registerAlias`.
* @property {Record<string, HighlightSyntax>} [languages={}]
* Register more languages.
* Each key/value pair passed as arguments to `lowlight.registerLanguage`.
* @typedef {import('./lib/index.js').Options} Options
*/
import {lowlight} from 'lowlight'
import {toText} from 'hast-util-to-text'
import {visit} from 'unist-util-visit'
const own = {}.hasOwnProperty
/**
* Plugin to highlight the syntax of code with lowlight (`highlight.js`).
*
* @type {import('unified').Plugin<[Options?] | void[], Root>}
*/
export default function rehypeHighlight(options = {}) {
const {aliases, languages, prefix, plainText, ignoreMissing, subset} = options
let name = 'hljs'
if (aliases) {
lowlight.registerAlias(aliases)
}
if (languages) {
/** @type {string} */
let key
for (key in languages) {
if (own.call(languages, key)) {
lowlight.registerLanguage(key, languages[key])
}
}
}
if (prefix) {
const pos = prefix.indexOf('-')
name = pos > -1 ? prefix.slice(0, pos) : prefix
}
return (tree) => {
// eslint-disable-next-line complexity
visit(tree, 'element', (node, _, givenParent) => {
const parent = /** @type {Node?} */ (givenParent)
if (
!parent ||
!('tagName' in parent) ||
parent.tagName !== 'pre' ||
node.tagName !== 'code' ||
!node.properties
) {
return
}
const lang = language(node)
if (
lang === false ||
(!lang && subset === false) ||
(lang && plainText && plainText.includes(lang))
) {
return
}
if (!Array.isArray(node.properties.className)) {
node.properties.className = []
}
if (!node.properties.className.includes(name)) {
node.properties.className.unshift(name)
}
/** @type {LowlightRoot} */
let result
try {
result = lang
? lowlight.highlight(lang, toText(parent), {prefix})
: // @ts-expect-error: we checked that `subset` is not a boolean.
lowlight.highlightAuto(toText(parent), {prefix, subset})
} catch (error) {
const exception = /** @type {Error} */ (error)
if (!ignoreMissing || !/Unknown language/.test(exception.message)) {
throw error
}
return
}
if (!lang && result.data.language) {
node.properties.className.push('language-' + result.data.language)
}
if (Array.isArray(result.children) && result.children.length > 0) {
node.children = result.children
}
})
}
}
/**
* Get the programming language of `node`.
*
* @param {Element} node
* @returns {false|string|undefined}
*/
function language(node) {
const className = node.properties && node.properties.className
let index = -1
if (!Array.isArray(className)) {
return
}
while (++index < className.length) {
const value = String(className[index])
if (value === 'no-highlight' || value === 'nohighlight') {
return false
}
if (value.slice(0, 5) === 'lang-') {
return value.slice(5)
}
if (value.slice(0, 9) === 'language-') {
return value.slice(9)
}
}
}
export {default} from './lib/index.js'
{
"name": "rehype-highlight",
"version": "5.0.1",
"version": "5.0.2",
"description": "rehype plugin to highlight code blocks with lowlight (highlight.js)",

@@ -32,2 +32,3 @@ "license": "MIT",

"files": [
"lib/",
"index.d.ts",

@@ -57,3 +58,3 @@ "index.js"

"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage",
"build": "rimraf \"lib/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage",
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",

@@ -60,0 +61,0 @@ "test-api": "node --conditions development test.js",

@@ -11,17 +11,71 @@ # rehype-highlight

[**rehype**][rehype] plugin to highlight the syntax of code with
[**lowlight**][lowlight] ([`highlight.js`][highlight-js]).
**[rehype][]** plugin to apply syntax highlighting to code with
[`highlight.js`][highlight-js] (through [`lowlight`][lowlight]).
`rehype-highlight` is built to work with all syntaxes supported by
[`highlight.js`][highlight-js].
It starts off with 35 [common languages][common] registered.
You can add up to 191 languages.
## Contents
* [What is this?](#what-is-this)
* [When should I use this?](#when-should-i-use-this)
* [Install](#install)
* [Use](#use)
* [API](#api)
* [`unified().use(rehypeHighlight[, options])`](#unifieduserehypehighlight-options)
* [Example](#example)
* [Example: ignoring](#example-ignoring)
* [Example: registering](#example-registering)
* [Example: aliases](#example-aliases)
* [Example: sanitation](#example-sanitation)
* [Types](#types)
* [Compatibility](#compatibility)
* [Security](#security)
* [Related](#related)
* [Contribute](#contribute)
* [License](#license)
## What is this?
This package is a [unified][] ([rehype][]) plugin to apply syntax highlighting
to code with `highlight.js`.
`highlight.js` is pretty fast, relatively small, and a quite good syntax
highlighter which has support for up to 190 different languages.
This package bundles 35 [common languages][common] by default and you can
register more.
It looks for `<code>` elements (when directly in `<pre>` elements) and changes
them.
You can specify the code language (such as Python) with a `language-*` or
`lang-*` class, where the `*` can be for example `js` (so `language-js`), `md`,
`css`, etc.
By default, even without a class, all `<pre><code>` is highlighted by
automatically detecting which code language it seems to be.
You can prevent that with a `no-highlight` or `nohighlight` class on the
`<code>` or by passing `options.subset: false`.
**unified** is a project that transforms content with abstract syntax trees
(ASTs).
**rehype** adds support for HTML to unified.
**hast** is the HTML AST that rehype uses.
This is a rehype plugin that applies syntax highlighting to the AST.
## When should I use this?
This project is useful when you want to apply syntax highlighting in rehype.
One reason to do that is that it typically means the highlighting happens once
at build time instead of every time at run time.
There are several other community plugins that apply syntax highlighting.
Some of them are great choices but some are broken.
As anyone can make rehype plugins, make sure to carefully assess the quality of
rehype plugins.
This plugin is built on [`lowlight`][lowlight], which is a virtual version of
highlight.js.
You can make a plugin based on this one with lowlight when you want to do things
differently.
## Install
This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c):
Node 12+ is needed to use it and it must be `import`ed instead of `require`d.
This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c).
In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]:
[npm][]:
```sh

@@ -31,5 +85,19 @@ npm install rehype-highlight

In Deno with [Skypack][]:
```js
import rehypeHighlight from 'https://cdn.skypack.dev/rehype-highlight@5?dts'
```
In browsers with [Skypack][]:
```html
<script type="module">
import rehypeHighlight from 'https://cdn.skypack.dev/rehype-highlight@5?min'
</script>
```
## Use
Say `example.html` looks as follows:
Say we have the following file `example.html`:

@@ -43,30 +111,28 @@ ```html

…and `example.js` like this:
And our module `example.js` looks as follows:
```js
import {readSync} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {read} from 'to-vfile'
import {rehype} from 'rehype'
import rehypeHighlight from 'rehype-highlight'
const file = readSync('example.html')
main()
rehype()
.data('settings', {fragment: true})
.use(rehypeHighlight)
.process(file)
.then((file) => {
console.error(reporter(file))
console.log(String(file))
})
async function main() {
const file = await rehype()
.data('settings', {fragment: true})
.use(rehypeHighlight)
.process(await read('example.html'))
console.log(String(file))
}
```
Now, running `node example` yields:
Now running `node example.js` yields:
```html
example.html: no issues found
<h1>Hello World!</h1>
<pre><code class="hljs language-js"><span class="hljs-keyword">var</span> name = <span class="hljs-string">"World"</span>;
<span class="hljs-built_in">console</span>.warn(<span class="hljs-string">"Hello, "</span> + name + <span class="hljs-string">"!"</span>)</code></pre>
<span class="hljs-variable hljs-language">console</span>.<span class="hljs-title hljs-function">warn</span>(<span class="hljs-string">"Hello, "</span> + name + <span class="hljs-string">"!"</span>)</code></pre>
```

@@ -81,17 +147,8 @@

Syntax highlight `pre > code`.
Uses [**lowlight**][lowlight] under the hood, which is a virtual version of
[`highlight.js`][highlight-js].
Apply syntax highlighting to code with `highlight.js`.
Configure the language by using a `lang-js` or `language-js` class.
Ignore `code` with a `no-highlight` or `nohighlight` class.
Will auto-detect the syntax language otherwise.
##### `options`
`rehype-highlight` is built to work with all syntaxes supported by
`highlight.js`.
It starts off with 35 [common languages][common] registered.
You can add up to 191 languages.
Configuration (optional).
##### `options`
###### `options.prefix`

@@ -103,6 +160,14 @@

Scope of languages to check when auto-detecting (`boolean` or `Array.<string>`,
default: all languages).
Scope of languages to check when automatically detecting (`boolean` or
`Array<string>`, default: all languages).
Pass `false` to not highlight code without language classes.
###### `options.plainText`
List of plain-text languages (`Array<string>`, default: `[]`).
Pass any languages you would like to be kept as plain-text instead of getting
highlighted.
This is like setting a `no-highlight` class assuming `txt` was listed, then
`language-txt` would be treated as such too.
###### `options.ignoreMissing`

@@ -115,11 +180,5 @@

###### `options.plainText`
List of plain-text languages (`Array.<string>`, default: `[]`).
Pass any languages you would like to be kept as plain-text instead of getting
highlighted.
###### `options.aliases`
Register more aliases (`Object<string | Array.<string>>`, default: `{}`).
Register more aliases (`Record<string, string|Array<string>>`, default: `{}`).
Passed to [`lowlight.registerAlias`][register-alias].

@@ -133,13 +192,228 @@

`rehype-highlight` is built to work with all syntaxes supported by
`highlight.js`.
It starts off with 35 [common languages][common] registered.
You can add up to 191 languages.
## Example
### Example: ignoring
There are three ways to not apply syntax highlighting to code blocks.
They can be ignored with an explicit class of `no-highlight` (or `nohighlight`),
an explicit language name that’s listed in `options.plainText`, or by setting
`options.subset` to `false`, which prevents `<code>` without a class from being
automatically detected.
For example, with `example.html`:
```html
<pre><code>this won’t be highlighted due to `subset: false`</code></pre>
<pre><code class="no-highlight">this won’t be highlighted due to its class</code></pre>
<pre><code class="language-txt">this won’t be highlighted due to `plainText: ['txt']`</code></pre>
```
And `example.js`:
```js
import {read} from 'to-vfile'
import {rehype} from 'rehype'
import rehypeHighlight from 'rehype-highlight'
main()
async function main() {
const file = await rehype()
.data('settings', {fragment: true})
.use(rehypeHighlight, {subset: false, plainText: ['txt', 'text']})
.process(await read('example.html'))
console.log(String(file))
}
```
Running that yields the same as `example.html`: none of them are highlighted.
### Example: registering
`rehype-highlight` supports 35 common used languages by default.
This makes it small to load in browsers and Node.js, while supporting most cases
by default.
It’s possible to add support for more languages.
For example, with `example.html`:
```html
<pre><code class="language-bnf">a ::= 'a' | 'A'</code></pre>
```
And `example.js`:
```js
import {read} from 'to-vfile'
import {rehype} from 'rehype'
import rehypeHighlight from 'rehype-highlight'
import bnf from 'highlight.js/lib/languages/bnf'
main()
async function main() {
const file = await rehype()
.data('settings', {fragment: true})
.use(rehypeHighlight, {languages: {bnf}})
.process(await read('example.html'))
console.log(String(file))
}
```
Running that yields:
```html
<pre><code class="hljs language-bnf">a ::= <span class="hljs-string">'a'</span> | <span class="hljs-string">'A'</span></code></pre>
```
### Example: aliases
You can map your own language flags to `highlight.js` languages.
For example, with `example.html`:
```html
<pre><code class="language-custom-script">console.log(1)</code></pre>
```
And `example.js`:
```js
import {read} from 'to-vfile'
import {rehype} from 'rehype'
import rehypeHighlight from 'rehype-highlight'
main()
async function main() {
const file = await rehype()
.data('settings', {fragment: true})
// 👉 **Note**: the keys are registered and full highlight.js names, and
// the values are the flags that you want to allow as `x` in `language-x`
// classes.
.use(rehypeHighlight, {aliases: {'javascript': 'custom-script'}})
.process(await read('example.html'))
console.log(String(file))
}
```
Running that yields:
```html
<pre><code class="hljs language-custom-script"><span class="hljs-variable hljs-language">console</span>.<span class="hljs-title hljs-function">log</span>(<span class="hljs-number">1</span>)</code></pre>
```
### Example: sanitation
Applying syntax highlighting in rehype operates on `<code>` elements with
certain classes and it injects many `<span>` elements with classes.
Allowing arbitrary classes is an opening for XSS vulnerabilities.
Working with user input and HTML generally opens you up to XSS vulnerabilities,
so it’s recommend to use sanitation mechanisms, typically
[`rehype-sanitize`][rehype-sanitize].
Because arbitrary classes are one such opening that `rehype-sanitize` takes care
off, using `rehype-highlight` with `rehype-sanitize` requires some configuration
to make it work.
There are two ways to make it work.
Either by using `rehype-sanitize` first while allowing the classes on `<code>`
and then using `rehype-highlight`, or alternatively first using
`rehype-highlight` and then using `rehype-sanitize` while allowing the classes
on `<span>` elements.
Using `rehype-sanitize` before `rehype-highlight`:
```js
import {unified} from 'unified'
import rehypeParse from 'rehype-parse'
import rehypeHighlight from 'rehype-highlight'
import rehypeSanitize, {defaultSchema} from './index.js'
import rehypeStringify from 'rehype-stringify'
main()
async function main() {
const file = await unified()
.use(rehypeParse, {fragment: true})
.use(rehypeSanitize, {
...defaultSchema,
attributes: {
...defaultSchema.attributes,
code: [
...(defaultSchema.attributes.code || []),
// List of all allowed languages:
['className', 'language-js', 'language-css', 'language-md']
]
}
})
.use(rehypeHighlight, {subset: false})
.use(rehypeStringify)
.process('<pre><code className="language-js">console.log(1)</code></pre>')
console.log(String(file))
}
```
Using `rehype-highlight` before `rehype-sanitize`:
```diff
async function main() {
const file = await unified()
.use(rehypeParse, {fragment: true})
+ .use(rehypeHighlight, {subset: false})
.use(rehypeSanitize, {
...defaultSchema,
attributes: {
...defaultSchema.attributes,
- code: [
- ...(defaultSchema.attributes.code || []),
- // List of all allowed languages:
- ['className', 'hljs', 'language-js', 'language-css', 'language-md']
+ span: [
+ ...(defaultSchema.attributes.span || []),
+ // List of all allowed tokens:
+ ['className', 'hljs-addition', 'hljs-attr', 'hljs-attribute', 'hljs-built_in', 'hljs-bullet', 'hljs-char', 'hljs-code', 'hljs-comment', 'hljs-deletion', 'hljs-doctag', 'hljs-emphasis', 'hljs-formula', 'hljs-keyword', 'hljs-link', 'hljs-literal', 'hljs-meta', 'hljs-name', 'hljs-number', 'hljs-operator', 'hljs-params', 'hljs-property', 'hljs-punctuation', 'hljs-quote', 'hljs-regexp', 'hljs-section', 'hljs-selector-attr', 'hljs-selector-class', 'hljs-selector-id', 'hljs-selector-pseudo', 'hljs-selector-tag', 'hljs-string', 'hljs-strong', 'hljs-subst', 'hljs-symbol', 'hljs-tag', 'hljs-template-tag', 'hljs-template-variable', 'hljs-title', 'hljs-type', 'hljs-variable'
+ ]
]
}
})
- .use(rehypeHighlight, {subset: false})
.use(rehypeStringify)
.process('<pre><code className="language-js">console.log(1)</code></pre>')
```
## Types
This package is fully typed with [TypeScript][].
It exports an `Options` type, which specifies the interface of the accepted
options.
## Compatibility
Projects maintained by the unified collective are compatible with all maintained
versions of Node.js.
As of now, that is Node.js 12.20+, 14.14+, and 16.0+.
Our projects sometimes work with older versions, but this is not guaranteed.
This plugin works with `rehype-parse` version 1+, `rehype-stringify` version 1+,
`rehype` version 1+, and `unified` version 4+.
## Security
Use of `rehype-highlight` *should* be safe to use as `lowlight` *should* be safe
to use.
When in doubt, use [`rehype-sanitize`][sanitize].
Use of `rehype-highlight` *should* be safe to use as `highlight.js` and
`lowlight` *should* be safe to use.
When in doubt, use [`rehype-sanitize`][rehype-sanitize].
## Related
* [`rehype-meta`](https://github.com/rehypejs/rehype-meta)
— add metadata to the head of a document
* [`rehype-document`](https://github.com/rehypejs/rehype-document)
— wrap a fragment in a document
## Contribute

@@ -189,2 +463,4 @@

[skypack]: https://www.skypack.dev
[health]: https://github.com/rehypejs/.github

@@ -202,2 +478,6 @@

[typescript]: https://www.typescriptlang.org
[unified]: https://github.com/unifiedjs/unified
[rehype]: https://github.com/rehypejs/rehype

@@ -213,4 +493,4 @@

[sanitize]: https://github.com/rehypejs/rehype-sanitize
[rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize
[common]: https://github.com/wooorm/lowlight#syntaxes
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc