New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

rehype-external-links

Package Overview
Dependencies
Maintainers
2
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rehype-external-links - npm Package Compare versions

Comparing version

to
3.0.0

lib/index.d.ts

74

index.d.ts

@@ -1,67 +0,7 @@

export default function rehypeExternalLinks(
this: import('unified').Processor<void, import('hast').Root, void, void>,
...settings: [(Options | undefined)?] | void[]
):
| void
| import('unified').Transformer<import('hast').Root, import('hast').Root>
export type Root = import('hast').Root
export type Properties = import('hast').Properties
export type Element = import('hast').Element
export type Test = import('hast-util-is-element').Test
export type ElementChild = Element['children'][number]
export type Target = '_self' | '_blank' | '_parent' | '_top'
export type Rel = Array<string> | string
export type Protocols = Array<string>
export type Content = ElementChild | Array<ElementChild>
export type ContentProperties = Properties
export type TargetCallback = (node: Element) => Target | null | undefined
export type RelCallback = (node: Element) => Rel | null | undefined
export type ProtocolsCallback = (node: Element) => Protocols | null | undefined
export type ContentCallback = (node: Element) => Content | null | undefined
export type ContentPropertiesCallback = (
node: Element
) => Properties | null | undefined
/**
* Configuration.
*/
export type Options = {
/**
* How to display referenced documents (`string?`: `_self`, `_blank`,
* `_parent`, or `_top`, default: `_blank`).
* The default (nothing) is to not set `target`s on links.
*/
target?: Target | TargetCallback | undefined
/**
* Link types to hint about the referenced documents.
* Pass an empty array (`[]`) to not set `rel`s on links.
*
* **Note**: when using a `target`, add `noopener` and `noreferrer` to avoid
* exploitation of the `window.opener` API.
*/
rel?: Rel | RelCallback | undefined
/**
* Protocols to check, such as `mailto` or `tel`.
*/
protocols?: Protocols | ProtocolsCallback | undefined
/**
* hast content to insert at the end of external links.
* Will be inserted in a `<span>` element.
*
* Useful for improving accessibility by giving users advanced warning when
* opening a new window.
*/
content?: Content | ContentCallback | undefined
/**
* hast properties to add to the `span` wrapping `content`, when given.
*/
contentProperties?:
| import('hast').Properties
| ContentPropertiesCallback
| undefined
/**
* Additional test to define which external link elements are modified.
* Any test that can be given to `hast-util-is-element` is supported.
* The default (no test) is to modify all external links.
*/
test?: Test
}
export { default } from "./lib/index.js";
export type CreateContent = import('./lib/index.js').CreateContent;
export type CreateProperties = import('./lib/index.js').CreateProperties;
export type CreateRel = import('./lib/index.js').CreateRel;
export type CreateTarget = import('./lib/index.js').CreateTarget;
export type Options = import('./lib/index.js').Options;
export type Target = import('./lib/index.js').Target;
/**
* @typedef {import('hast').Root} Root
* @typedef {import('hast').Properties} Properties
* @typedef {import('hast').Element} Element
* @typedef {import('hast-util-is-element').Test} Test
*
* @typedef {Element['children'][number]} ElementChild
*
* @typedef {'_self'|'_blank'|'_parent'|'_top'} Target
* @typedef {Array<string>|string} Rel
* @typedef {Array<string>} Protocols
* @typedef {ElementChild|Array<ElementChild>} Content
* @typedef {Properties} ContentProperties
*
* @callback TargetCallback
* @param {Element} node
* @returns {Target|null|undefined}
*
* @callback RelCallback
* @param {Element} node
* @returns {Rel|null|undefined}
*
* @callback ProtocolsCallback
* @param {Element} node
* @returns {Protocols|null|undefined}
*
* @callback ContentCallback
* @param {Element} node
* @returns {Content|null|undefined}
*
* @callback ContentPropertiesCallback
* @param {Element} node
* @returns {Properties|null|undefined}
*
* @typedef Options
* Configuration.
* @property {Target|TargetCallback} [target]
* How to display referenced documents (`string?`: `_self`, `_blank`,
* `_parent`, or `_top`, default: `_blank`).
* The default (nothing) is to not set `target`s on links.
* @property {Rel|RelCallback} [rel=['nofollow', 'noopener', 'noreferrer']]
* Link types to hint about the referenced documents.
* Pass an empty array (`[]`) to not set `rel`s on links.
*
* **Note**: when using a `target`, add `noopener` and `noreferrer` to avoid
* exploitation of the `window.opener` API.
* @property {Protocols|ProtocolsCallback} [protocols=['http', 'https']]
* Protocols to check, such as `mailto` or `tel`.
* @property {Content|ContentCallback} [content]
* hast content to insert at the end of external links.
* Will be inserted in a `<span>` element.
*
* Useful for improving accessibility by giving users advanced warning when
* opening a new window.
* @property {ContentProperties|ContentPropertiesCallback} [contentProperties]
* hast properties to add to the `span` wrapping `content`, when given.
* @property {Test} [test]
* Additional test to define which external link elements are modified.
* Any test that can be given to `hast-util-is-element` is supported.
* The default (no test) is to modify all external links.
* @typedef {import('./lib/index.js').CreateContent} CreateContent
* @typedef {import('./lib/index.js').CreateProperties} CreateProperties
* @typedef {import('./lib/index.js').CreateRel} CreateRel
* @typedef {import('./lib/index.js').CreateTarget} CreateTarget
* @typedef {import('./lib/index.js').Options} Options
* @typedef {import('./lib/index.js').Target} Target
*/
import {visit} from 'unist-util-visit'
import {parse} from 'space-separated-tokens'
import {convertElement} from 'hast-util-is-element'
import isAbsoluteUrl from 'is-absolute-url'
import extend from 'extend'
const defaultRel = ['nofollow']
const defaultProtocols = ['http', 'https']
/**
* If this is a value, return that.
* If this is a function instead, call it to get the result.
*
* @template T
* @param {T} value
* @param {Element} node
* @returns {T extends Function ? ReturnType<T> : T}
*/
function callIfNeeded(value, node) {
return typeof value === 'function' ? value(node) : value
}
/**
* Plugin to automatically add `target` and `rel` attributes to external links.
*
* @type {import('unified').Plugin<[Options?] | Array<void>, Root>}
*/
export default function rehypeExternalLinks(options = {}) {
const is = convertElement(options.test)
return (tree) => {
visit(tree, 'element', (node, index, parent) => {
if (
node.tagName === 'a' &&
node.properties &&
typeof node.properties.href === 'string' &&
is(node, index, parent)
) {
const url = node.properties.href
const protocol = url.slice(0, url.indexOf(':'))
const target = callIfNeeded(options.target, node)
const relRaw = callIfNeeded(options.rel, node) || defaultRel
const rel = typeof relRaw === 'string' ? parse(relRaw) : relRaw
const protocols =
callIfNeeded(options.protocols, node) || defaultProtocols
const contentRaw = callIfNeeded(options.content, node)
const content =
contentRaw && !Array.isArray(contentRaw) ? [contentRaw] : contentRaw
const contentProperties =
callIfNeeded(options.contentProperties, node) || {}
if (
isAbsoluteUrl(url)
? protocols.includes(protocol)
: url.startsWith('//')
) {
if (target) {
node.properties.target = target
}
if (rel.length > 0) {
node.properties.rel = rel.concat()
}
if (content) {
node.children.push({
type: 'element',
tagName: 'span',
properties: extend(true, contentProperties),
children: extend(true, content)
})
}
}
}
})
}
}
export {default} from './lib/index.js'
{
"name": "rehype-external-links",
"version": "2.1.0",
"version": "3.0.0",
"description": "rehype plugin to automatically add `target` and `rel` attributes to external links",

@@ -30,5 +30,5 @@ "license": "MIT",

"type": "module",
"main": "index.js",
"types": "index.d.ts",
"exports": "./index.js",
"files": [
"lib/",
"index.d.ts",

@@ -38,44 +38,40 @@ "index.js"

"dependencies": {
"@types/hast": "^2.0.0",
"extend": "^3.0.0",
"hast-util-is-element": "^2.0.0",
"@types/hast": "^3.0.0",
"@ungap/structured-clone": "^1.0.0",
"hast-util-is-element": "^3.0.0",
"is-absolute-url": "^4.0.0",
"space-separated-tokens": "^2.0.0",
"unified": "^10.0.0",
"unist-util-visit": "^4.0.0"
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@types/tape": "^4.0.0",
"c8": "^7.0.0",
"prettier": "^2.0.0",
"rehype": "^12.0.0",
"@types/node": "^20.0.0",
"@types/ungap__structured-clone": "^0.3.0",
"c8": "^8.0.0",
"prettier": "^3.0.0",
"rehype": "^13.0.0",
"remark-cli": "^11.0.0",
"remark-preset-wooorm": "^9.0.0",
"rimraf": "^3.0.0",
"tape": "^5.0.0",
"type-coverage": "^2.0.0",
"typescript": "^5.0.0",
"xo": "^0.54.0"
"xo": "^0.56.0"
},
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage",
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
"build": "tsc --build --clean && tsc --build && type-coverage",
"format": "remark . --frail --output --quiet && prettier . --log-level warn --write && xo --fix",
"prepack": "npm run build && npm run format",
"test": "npm run build && npm run format && npm run test-coverage",
"test-api": "node --conditions development test.js",
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov npm run test-api",
"test": "npm run build && npm run format && npm run test-coverage"
"test-coverage": "c8 --100 --check-coverage --reporter lcov npm run test-api"
},
"prettier": {
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": false,
"singleQuote": true,
"bracketSpacing": false,
"semi": false,
"trailingComma": "none"
"tabWidth": 2,
"trailingComma": "none",
"useTabs": false
},
"xo": {
"prettier": true
},
"remarkConfig": {
"plugins": [
"preset-wooorm"
"remark-preset-wooorm"
]

@@ -86,5 +82,8 @@ },

"detail": true,
"strict": true,
"ignoreCatch": true
"ignoreCatch": true,
"strict": true
},
"xo": {
"prettier": true
}
}

@@ -21,4 +21,8 @@ # rehype-external-links

* [`unified().use(rehypeExternalLinks[, options])`](#unifieduserehypeexternallinks-options)
* [Examples](#examples)
* [Example: dynamic options](#example-dynamic-options)
* [`CreateContent`](#createcontent)
* [`CreateProperties`](#createproperties)
* [`CreateRel`](#createrel)
* [`CreateTarget`](#createtarget)
* [`Options`](#options)
* [`Target`](#target)
* [Types](#types)

@@ -55,4 +59,4 @@ * [Compatibility](#compatibility)

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][]:
This package is [ESM only][esm].
In Node.js (version 16+), install with [npm][]:

@@ -66,3 +70,3 @@ ```sh

```js
import rehypeExternalLinks from 'https://esm.sh/rehype-external-links@1'
import rehypeExternalLinks from 'https://esm.sh/rehype-external-links@3'
```

@@ -74,3 +78,3 @@

<script type="module">
import rehypeExternalLinks from 'https://esm.sh/rehype-external-links@1?bundle'
import rehypeExternalLinks from 'https://esm.sh/rehype-external-links@3?bundle'
</script>

@@ -81,10 +85,10 @@ ```

Say our module `example.js` looks as follows:
Say our module `example.js` contains:
```js
import {unified} from 'unified'
import rehypeExternalLinks from 'rehype-external-links'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeExternalLinks from 'rehype-external-links'
import rehypeStringify from 'rehype-stringify'
import {unified} from 'unified'

@@ -101,3 +105,3 @@ const file = await unified()

Now running `node example.js` yields:
…then running `node example.js` yields:

@@ -111,113 +115,143 @@ ```html

This package exports no identifiers.
The default export is `rehypeExternalLinks`.
The default export is [`rehypeExternalLinks`][api-rehype-external-links].
### `unified().use(rehypeExternalLinks[, options])`
Add `rel` (and `target`) to external links.
Automatically add `rel` (and `target`?) to external links.
##### `options`
###### Parameters
Configuration (optional).
* `options` ([`Options`][api-options], optional)
— configuration
###### `options.target`
###### Returns
How to open external documents (`string?`: `_self`, `_blank`, `_parent`,
or `_top`, default: `undefined`).
Can also be a function called with the current element to get `target`
dynamically.
The default (nothing) is to not set `target`s on links.
Transform ([`Transformer`][unified-transformer]).
> 👉 **Note**: [you should likely not configure this][css-tricks].
###### Notes
###### `options.rel`
You should [likely not configure `target`][css-tricks].
[Link types][mdn-rel] to hint about the referenced documents (`Array<string>`
or `string`, default: `['nofollow']`).
Can also be a function called with the current element to get `rel` dynamically.
Pass an empty array (`[]`) to not set `rel`s on links.
You should at least set `rel` to `['nofollow']`.
When using a `target`, add `noopener` and `noreferrer` to avoid exploitation
of the `window.opener` API.
> 👉 **Note**: you should at least set `['nofollow']`.
When using a `target`, you should set `content` to adhere to accessibility
guidelines by [giving users advanced warning when opening a new window][g201].
> ⚠️ **Danger**: when using a `target`, add [`noopener` and `noreferrer`][mdn-a]
> to avoid exploitation of the `window.opener` API.
### `CreateContent`
###### `options.protocols`
Create a target for the element (TypeScript type).
Protocols to see as external, such as `mailto` or `tel` (`Array<string>`,
default: `['http', 'https']`).
Can also be a function called with the current element to get `protocols`
dynamically.
###### Parameters
###### `options.content`
* `element` ([`Element`][hast-element])
— element to check
**[hast][]** content to insert at the end of external links ([`Node`][node] or
[`Children`][children], optional).
Can also be a function called with the current element to get `content`
dynamically.
The content will be inserted in a `<span>` element.
###### Returns
> 👉 **Note**: you should set this when using `target` to adhere to
> accessibility guidelines by [giving users advanced warning when opening a new
> window][g201].
Content to add (`Array<Node>` or `Node`, optional).
###### `options.contentProperties`
### `CreateProperties`
Attributes to add to the `<span>`s wrapping `options.content`
([`Properties`][properties], optional).
Can also be a function called with the current element to get
`contentProperties` dynamically.
Create properties for an element (TypeScript type).
###### `options.test`
###### Parameters
Additional test to define which external link elements are modified.
Any test that can be given to [hast-util-is-element](https://github.com/syntax-tree/hast-util-is-element)
is supported. The default (no test) is to modify all external links.
* `element` ([`Element`][hast-element])
— element to check
> 👉 **Note**: in this case it only makes sense to provide a test function
> since this plugin will only consider `a` tags.
###### Returns
## Examples
Properties to add ([`Properties`][hast-properties], optional).
### Example: dynamic options
### `CreateRel`
This example shows how to define options dynamically.
That means that you can choose per element what to generate.
Create a `rel` for the element (TypeScript type).
Each option can be a function which is called with the current element
(`Element`) and returns the corresponding value.
###### Parameters
Taking the above `example.js` and applying the following diff:
* `element` ([`Element`][hast-element])
— element to check
```diff
const file = await unified()
.use(remarkParse)
.use(remarkRehype)
- .use(rehypeExternalLinks, {rel: ['nofollow']})
+ .use(rehypeExternalLinks, {
+ target(element) {
+ return element.properties && element.properties.id === '5'
+ ? '_blank'
+ : undefined
+ },
+ rel: ['nofollow']
+ })
.use(rehypeStringify)
.process('[rehype](https://github.com/rehypejs/rehype)')
###### Returns
`rel` to use (`Array<string>`, optional).
### `CreateTarget`
Create a `target` for the element (TypeScript type).
###### Parameters
* `element` ([`Element`][hast-element])
— element to check
###### Returns
`target` to use ([`Target`][api-target], optional).
### `Options`
Configuration (TypeScript type).
###### Fields
* `content` (`Array<Node>`, [`CreateContent`][api-create-content], or `Node`,
optional)
— content to insert at the end of external links; will be inserted in a
`<span>` element; useful for improving accessibility by giving users
advanced warning when opening a new window
* `contentProperties` ([`CreateProperties`][api-create-properties] or
[`Properties`][hast-properties], optional)
— properties to add to the `span` wrapping `content`
* `properties` ([`CreateProperties`][api-create-properties] or
[`Properties`][hast-properties], optional)
— properties to add to the link itself
* `protocols` (`Array<string>`, default: `['http', 'https']`)
— protocols to see as external, such as `mailto` or `tel`
* `rel` (`Array<string>`, [`CreateRel`][api-create-rel], or `string`,
default: `['nofollow']`)
— [link types][mdn-rel] to hint about the referenced documents; pass an
empty array (`[]`) to not set `rel`s on links; when using a `target`, add `noopener`
and `noreferrer` to avoid exploitation of the `window.opener` API
* `target` ([`CreateTarget`][api-create-target] or [`Target`][api-target],
optional)
— how to display referenced documents; the default (nothing) is to not set
`target`s on links
* `test` ([`Test`][is-test], optional)
— extra test to define which external link elements are modified; any test
that can be given to `hast-util-is-element` is supported
### `Target`
Target (TypeScript type).
###### Type
```ts
type Target = '_blank' | '_parent' | '_self' | '_top'
```
Changes to apply `target="_blank"` on the element with an `id="5"`.
## Types
This package is fully typed with [TypeScript][].
It exports an `Options` type, which specifies the interface of the accepted
options.
It exports the additional types
[`CreateContent`][api-create-content],
[`CreateProperties`][api-create-properties],
[`CreateRel`][api-create-rel],
[`CreateTarget`][api-create-target],
[`Options`][api-options], and
[`Target`][api-target].
## Compatibility
Projects maintained by the unified collective are compatible with all maintained
Projects maintained by the unified collective are compatible with 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.
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-external-links@^3`,
compatible with Node.js 16.
This plugin works with `rehype-parse` version 3+, `rehype-stringify` version 3+,

@@ -262,5 +296,5 @@ `rehype` version 4+, and `unified` version 6+.

[size-badge]: https://img.shields.io/bundlephobia/minzip/rehype-external-links.svg
[size-badge]: https://img.shields.io/bundlejs/size/rehype-external-links
[size]: https://bundlephobia.com/result?p=rehype-external-links
[size]: https://bundlejs.com/?q=rehype-external-links

@@ -279,2 +313,4 @@ [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg

[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[esmsh]: https://esm.sh

@@ -294,2 +330,12 @@

[hast-properties]: https://github.com/syntax-tree/hast#properties
[is-test]: https://github.com/syntax-tree/hast-util-is-element#test
[mdn-rel]: https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types
[rehype]: https://github.com/rehypejs/rehype
[rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize
[typescript]: https://www.typescriptlang.org

@@ -299,22 +345,24 @@

[rehype]: https://github.com/rehypejs/rehype
[unified-transformer]: https://github.com/unifiedjs/unified#transformer
[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
[rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize
[hast-element]: https://github.com/syntax-tree/hast#element
[mdn-rel]: https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types
[g201]: https://www.w3.org/WAI/WCAG21/Techniques/general/G201
[mdn-a]: https://developer.mozilla.org/en/docs/Web/HTML/Element/a
[css-tricks]: https://css-tricks.com/use-target_blank/
[hast]: https://github.com/syntax-tree/hast
[api-create-content]: #createcontent
[properties]: https://github.com/syntax-tree/hast#properties
[api-create-properties]: #createproperties
[node]: https://github.com/syntax-tree/hast#nodes
[api-create-rel]: #createrel
[children]: https://github.com/syntax-tree/unist#child
[api-create-target]: #createtarget
[g201]: https://www.w3.org/WAI/WCAG21/Techniques/general/G201
[api-options]: #options
[css-tricks]: https://css-tricks.com/use-target_blank/
[api-target]: #target
[api-rehype-external-links]: #unifieduserehypeexternallinks-options