Skip to content

Commit

Permalink
Gracefully handle splice in permalink renderers
Browse files Browse the repository at this point in the history
  • Loading branch information
valeriangalliat committed Aug 26, 2021
1 parent 3c08c0c commit fe9a9a8
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 10 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

## [8.3.0] - 2021-08-26
* Make core loop resilient to permalink renderers mutating the token
stream with `splice`. ([#100])

## [8.2.0] - 2021-08-26
* Introduce a `linkInsideHeader` permalink option, which is the closest
to the permalink in previous versions. ([#101])
Expand Down Expand Up @@ -191,7 +195,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [1.0.0] - 2015-03-18
* Initial release.

[Unreleased]: https://github.com/valeriangalliat/markdown-it-anchor/compare/v8.2.0...HEAD
[Unreleased]: https://github.com/valeriangalliat/markdown-it-anchor/compare/v8.3.0...HEAD
[8.3.0]: https://github.com/valeriangalliat/markdown-it-anchor/compare/v8.2.0...v8.3.0
[8.2.0]: https://github.com/valeriangalliat/markdown-it-anchor/compare/v8.1.3...v8.2.0
[8.1.3]: https://github.com/valeriangalliat/markdown-it-anchor/compare/v8.1.2...v8.1.3
[8.1.2]: https://github.com/valeriangalliat/markdown-it-anchor/compare/v8.1.1...v8.1.2
Expand Down Expand Up @@ -276,6 +281,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
[#97]: https://github.com/valeriangalliat/markdown-it-anchor/pull/97
[#98]: https://github.com/valeriangalliat/markdown-it-anchor/issues/98
[#99]: https://github.com/valeriangalliat/markdown-it-anchor/pull/99
[#100]: https://github.com/valeriangalliat/markdown-it-anchor/issues/100
[#101]: https://github.com/valeriangalliat/markdown-it-anchor/issues/101
[#102]: https://github.com/valeriangalliat/markdown-it-anchor/pull/102
[#103]: https://github.com/valeriangalliat/markdown-it-anchor/issues/103
20 changes: 11 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ function anchor (md, opts) {
? isLevelSelectedArray(opts.level)
: isLevelSelectedNumber(opts.level)

for (const token of tokens) {
for (let idx = 0; idx < tokens.length; idx++) {
const token = tokens[idx]

if (token.type !== 'heading_open') {
continue
}
Expand All @@ -43,12 +45,8 @@ function anchor (md, opts) {
continue
}

// A permalink renderer could modify the `tokens` array so
// make sure to get the up-to-date index on each iteration.
const index = tokens.indexOf(token)

// Aggregate the next token children text.
const title = tokens[index + 1]
const title = tokens[idx + 1]
.children
.filter(token => token.type === 'text' || token.type === 'code_inline')
.reduce((acc, t) => acc + t.content, '')
Expand All @@ -68,13 +66,17 @@ function anchor (md, opts) {
}

if (typeof opts.permalink === 'function') {
opts.permalink(slug, opts, state, index)
opts.permalink(slug, opts, state, idx)
} else if (opts.permalink) {
opts.renderPermalink(slug, opts, state, index)
opts.renderPermalink(slug, opts, state, idx)
} else if (opts.renderPermalink && opts.renderPermalink !== permalink.legacy) {
opts.renderPermalink(slug, opts, state, index)
opts.renderPermalink(slug, opts, state, idx)
}

// A permalink renderer could modify the `tokens` array so
// make sure to get the up-to-date index on each iteration.
idx = tokens.indexOf(token)

if (opts.callback) {
opts.callback(token, { slug, title })
}
Expand Down
24 changes: 24 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,30 @@ nest('permalink.linkAfterHeader', test => {
'<h1 id="h1" tabindex="-1">H1</h1>\n<a class="header-anchor" href="#h1"><span aria-hidden="true">#</span><span class="visually-hidden">Permalink to “H1”</span></a>'
)
})

test('custom splice wrapper', t => {
const linkAfterHeader = anchor.permalink.linkAfterHeader({
style: 'visually-hidden',
assistiveText: title => `Permalink to “${title}”`,
visuallyHiddenClass: 'visually-hidden'
})

t.is(
md().use(anchor, {
permalink (slug, opts, state, idx) {
state.tokens.splice(idx, 0, Object.assign(new state.Token('div_open', 'div', 1), {
attrs: [['class', 'wrapper']],
block: true
}))

state.tokens.splice(idx + 4, 0, Object.assign(new state.Token('div_close', 'div', -1)))

linkAfterHeader(slug, opts, state, idx + 1)
}
}).render('# H1'),
'<div class="wrapper">\n<h1 id="h1" tabindex="-1">H1</h1>\n<a class="header-anchor" href="#h1"><span class="visually-hidden">Permalink to “H1”</span> <span aria-hidden="true">#</span></a></div>'
)
})
})

nest('tokens', test => {
Expand Down

0 comments on commit fe9a9a8

Please sign in to comment.