Skip to content

Commit

Permalink
Add support for content w/ wrap
Browse files Browse the repository at this point in the history
Related-to: GH-20.
Related-to: GH-21.

Co-authored-by: Chris Weber <chris963@gmail.com>
  • Loading branch information
wooorm and chrisweb committed Nov 8, 2023
1 parent b968c99 commit 31159c0
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 8 deletions.
34 changes: 28 additions & 6 deletions lib/index.js
Expand Up @@ -29,8 +29,11 @@
* Configuration.
* @property {Behavior | null | undefined} [behavior='prepend']
* How to create links (default: `'prepend'`).
* @property {Readonly<ElementContent> | ReadonlyArray<ElementContent> | Build | null | undefined} [content={type: 'element', tagName: 'span', properties: {className: ['icon', 'icon-link']}, children: []}]
* Content to insert in the link (default: `<span class="icon icon-link"></span>`), if `behavior` is not `'wrap'`.
* @property {Readonly<ElementContent> | ReadonlyArray<ElementContent> | Build | null | undefined} [content]
* Content to insert in the link (default: if `'wrap'` then `undefined`,
* otherwise `<span class="icon icon-link"></span>`);
* if `behavior` is `'wrap'` and `Build` is passed, its result replaces the
* existing content, otherwise the content is added after existing content.
* @property {Readonly<ElementContent> | ReadonlyArray<ElementContent> | Build | null | undefined} [group]
* Content to wrap the heading and link with, if `behavior` is `'after'` or
* `'before'` (optional).
Expand Down Expand Up @@ -96,7 +99,7 @@ export default function rehypeAutolinkHeadings(options) {
const settings = options || emptyOptions
let props = settings.properties
const behavior = settings.behavior || 'prepend'
const content = settings.content || contentDefaults
const content = settings.content
const group = settings.group
const is = convertElement(settings.test)

Expand Down Expand Up @@ -133,7 +136,7 @@ export default function rehypeAutolinkHeadings(options) {

/** @type {import('unist-util-visit').Visitor<Element>} */
function inject(node) {
const children = toChildren(content, node)
const children = toChildren(content || contentDefaults, node)
node.children[behavior === 'prepend' ? 'unshift' : 'push'](
create(node, toProperties(props, node), children)
)
Expand All @@ -146,7 +149,7 @@ export default function rehypeAutolinkHeadings(options) {
/* c8 ignore next -- uncommon */
if (typeof index !== 'number' || !parent) return

const children = toChildren(content, node)
const children = toChildren(content || contentDefaults, node)
const link = create(node, toProperties(props, node), children)
let nodes = behavior === 'before' ? [link, node] : [node, link]

Expand All @@ -166,7 +169,26 @@ export default function rehypeAutolinkHeadings(options) {

/** @type {import('unist-util-visit').Visitor<Element>} */
function wrap(node) {
node.children = [create(node, toProperties(props, node), node.children)]
/** @type {Array<ElementContent>} */
let before = node.children
/** @type {Array<ElementContent> | ElementContent} */
let after = []

if (typeof content === 'function') {
before = []
after = content(node)
} else if (content) {
after = clone(content)
}

node.children = [
create(
node,
toProperties(props, node),
Array.isArray(after) ? [...before, ...after] : [...before, after]
)
]

return [SKIP]
}
}
Expand Down
7 changes: 5 additions & 2 deletions readme.md
Expand Up @@ -208,8 +208,11 @@ Configuration (TypeScript type).
* `behavior` ([`Behavior`][api-behavior], default: `'prepend'`)
— how to create links
* `content` ([`Array<Node>`][hast-node], `Node`, or [`Build`][api-build],
default: equivalent of `<span class="icon icon-link"></span>`)
— content to insert in the link, if `behavior` is not `'wrap'`
default: if `'wrap'` then `undefined`, otherwise equivalent of
`<span class="icon icon-link"></span>`)
— content to insert in the link;
if `behavior` is `'wrap'` and `Build` is passed, its result replaces the
existing content, otherwise the content is added after existing content
* `group` ([`Array<Node>`][hast-node], `Node`, or [`Build`][api-build],
optional)
— content to wrap the heading and link with, if `behavior` is `'after'` or
Expand Down
11 changes: 11 additions & 0 deletions test/fixtures/behavior-wrap-content-multiple/config.json
@@ -0,0 +1,11 @@
{
"behavior": "wrap",
"content": [
{
"type": "element",
"tagName": "span",
"properties": {"className": ["icon", "icon-link"]},
"children": []
}
]
}
1 change: 1 addition & 0 deletions test/fixtures/behavior-wrap-content-multiple/input.html
@@ -0,0 +1 @@
<h1 id="foo">Bar</h1>
1 change: 1 addition & 0 deletions test/fixtures/behavior-wrap-content-multiple/output.html
@@ -0,0 +1 @@
<h1 id="foo"><a href="#foo">Bar<span class="icon icon-link"></span></a></h1>
9 changes: 9 additions & 0 deletions test/fixtures/behavior-wrap-content-one/config.json
@@ -0,0 +1,9 @@
{
"behavior": "wrap",
"content": {
"type": "element",
"tagName": "span",
"properties": {"className": ["icon", "icon-link"]},
"children": []
}
}
1 change: 1 addition & 0 deletions test/fixtures/behavior-wrap-content-one/input.html
@@ -0,0 +1 @@
<h1 id="foo">Bar</h1>
1 change: 1 addition & 0 deletions test/fixtures/behavior-wrap-content-one/output.html
@@ -0,0 +1 @@
<h1 id="foo"><a href="#foo">Bar<span class="icon icon-link"></span></a></h1>
47 changes: 47 additions & 0 deletions test/index.js
Expand Up @@ -42,6 +42,53 @@ test('rehypeAutolinkHeadings', async function (t) {
'<div><h1 id="a">b</h1><a data-x="y" href="#a"><i></i></a></div>'
)
})

await t.test('should support functions', async function () {
const file = await rehype()
.data('settings', {fragment: true})
.use(rehypeAutolinkHeadings, {
behavior: 'after',
content(node) {
assert.equal(node.properties.id, 'a')
return {type: 'element', tagName: 'i', properties: {}, children: []}
},
group(node) {
assert.equal(node.properties.id, 'a')
return {type: 'element', tagName: 'div', properties: {}, children: []}
},
properties(node) {
assert.equal(node.properties.id, 'a')
return {dataX: 'y'}
}
})
.process('<h1 id=a>b</h1>')

assert.deepEqual(
String(file),
'<div><h1 id="a">b</h1><a data-x="y" href="#a"><i></i></a></div>'
)
})

await t.test('should `content` as a function w/ `wrap`', async function () {
assert.deepEqual(
String(
await rehype()
.data('settings', {fragment: true})
.use(rehypeAutolinkHeadings, {
behavior: 'wrap',
content(node) {
assert.equal(node.properties.id, 'a')
return [
{type: 'element', tagName: 'i', properties: {}, children: []},
...node.children
]
}
})
.process('<h1 id=a>b</h1>')
),
'<h1 id="a"><a href="#a"><i></i>b</a></h1>'
)
})
})

test('fixtures', async function (t) {
Expand Down

0 comments on commit 31159c0

Please sign in to comment.