Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a getTokensText option #113

Merged
merged 1 commit into from Apr 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion CHANGELOG.md
Expand Up @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

## [8.6.0] - 2021-04-08

* Add a `getTokensText` option to customize how we extract the title
text from the heading tokens. ([#112])

## [8.5.0] - 2021-04-04
* Support wrapping output of `linkAfterHeader`. ([#100], [#110])

Expand Down Expand Up @@ -213,7 +218,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.5.0...HEAD
[Unreleased]: https://github.com/valeriangalliat/markdown-it-anchor/compare/v8.6.0...HEAD
[8.6.0]: https://github.com/valeriangalliat/markdown-it-anchor/compare/v8.5.0...v8.6.0
[8.5.0]: https://github.com/valeriangalliat/markdown-it-anchor/compare/v8.4.1...v8.5.0
[8.4.1]: https://github.com/valeriangalliat/markdown-it-anchor/compare/v8.4.0...v8.4.1
[8.4.0]: https://github.com/valeriangalliat/markdown-it-anchor/compare/v8.3.1...v8.4.0
Expand Down Expand Up @@ -310,5 +316,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
[#106]: https://github.com/valeriangalliat/markdown-it-anchor/pull/106
[#107]: https://github.com/valeriangalliat/markdown-it-anchor/issues/107
[#110]: https://github.com/valeriangalliat/markdown-it-anchor/issues/110
[#112]: https://github.com/valeriangalliat/markdown-it-anchor/issues/112

[`6fcc502`]: https://github.com/valeriangalliat/markdown-it-anchor/commit/6fcc50233d593458aa883e5b515cb8311114555c
59 changes: 47 additions & 12 deletions README.md
Expand Up @@ -17,15 +17,18 @@ See a [demo as JSFiddle](https://jsfiddle.net/9ukc8dy6/).

The `opts` object can contain:

| Name | Description | Default |
|------------------------|---------------------------------------------------------------------------|----------------------------|
| `level` | Minimum level to apply anchors, or array of selected levels. | 1 |
| `slugify` | A custom slugification function. | See [`index.js`](index.js) |
| `uniqueSlugStartIndex` | Index to start with when making duplicate slugs unique. | 1 |
| `permalink` | A function to render permalinks, see [permalinks] below. | `undefined` |
| `callback` | Called with token and info after rendering. | `undefined` |
| `tabIndex` | Value of the `tabindex` attribute on headings, set to `false` to disable. | `-1` |

| Name | Description | Default |
|------------------------|---------------------------------------------------------------------------|-----------------------------------------|
| `level` | Minimum level to apply anchors, or array of selected levels. | 1 |
| `permalink` | A function to render permalinks, see [permalinks] below. | `undefined` |
| `slugify` | A custom slugification function. | See [`index.js`][index-slugify] |
| `callback` | Called with token and info after rendering. | `undefined` |
| `getTokensText` | A custom function to get the text contents of the title from its tokens. | See [`index.js`][index-get-tokens-text] |
| `tabIndex` | Value of the `tabindex` attribute on headings, set to `false` to disable. | `-1` |
| `uniqueSlugStartIndex` | Index to start with when making duplicate slugs unique. | 1 |

[index-slugify]: https://github.com/valeriangalliat/markdown-it-anchor/blob/master/index.js#L3
[index-get-tokens-text]: https://github.com/valeriangalliat/markdown-it-anchor/blob/master/index.js#L5
[permalinks]: #permalinks

All headers greater than the minimum `level` will have an `id` attribute
Expand All @@ -37,12 +40,15 @@ only level 2 and 3 headers.
If a `permalink` renderer is given, it will be called for each matching header
to add a permalink. See [permalinks] below.

If a `slugify` function is given, you can decide how to transform a
heading text to a URL slug. See [user-friendly URLs](#user-friendly-urls).

The `callback` option is a function that will be called at the end of
rendering with the `token` and an `info` object. The `info` object has
`title` and `slug` properties with the token content and the slug used
for the identifier.

Finally, we set by default [`tabindex="-1"`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex)
We set by default [`tabindex="-1"`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex)
on headers. This marks the headers as focusable elements that are not
reachable by keyboard navigation. The effect is that screen readers will
read the title content when it's being jumped to. Outside of screen
Expand All @@ -51,12 +57,15 @@ can override this behavior with the `tabIndex` option. Set it to `false`
to remove the attribute altogether, otherwise the value will be used as
attribute value.

Finally, you can customize how the title text is extracted from the
markdown-it tokens (to later generate the slug). See [user-friendly URLs](#user-friendly-urls).

## User-friendly URLs

Starting from v5.0.0, markdown-it-anchor dropped the [`string`](https://github.com/jprichardson/string.js)
package to retain our core value of being an impartial and secure
library. Nevertheless, users looking for backward compatibility may want the old
`slugify` function:
library. Nevertheless, users looking for backward compatibility may want
the old `slugify` function:

```sh
npm install string
Expand Down Expand Up @@ -84,6 +93,32 @@ const md = require('markdown-it')()
.use(require('markdown-it-anchor'), { slugify: s => slugify(s) })
```

Additionally, if you want to further customize the title that gets
passed to the `slugify` function, you can do so by customizing the
`getTokensText` function, that gets the plain text from a list of
markdown-it inline tokens:

```js
function getTokensText (tokens) {
return tokens
.filter(token => !['html_inline', 'image'].includes(token.type))
.map(t => t.content)
.join('')
}

const md = require('markdown-it')()
.use(require('markdown-it-anchor'), { getTokensText })
```

By default we include only `text` and `code_inline` tokens, which
appeared to be a sensible approach for the vast majority of use cases.

An alternative approach is to include every token's content except for
`html_inline` and `image` tokens, which yields the exact same results as
the previous approach with a stock markdown-it, but would also include
custom tokens added by any of your markdown-it plugins, which might or
might not be desirable for you. Now you have the option!

## Explicit `id`s

You might want to explicitly set the `id` attribute of your headings
Expand Down
13 changes: 9 additions & 4 deletions index.js
Expand Up @@ -2,6 +2,13 @@ import * as permalink from './permalink'

const slugify = (s) => encodeURIComponent(String(s).trim().toLowerCase().replace(/\s+/g, '-'))

function getTokensText (tokens) {
return tokens
.filter(t => ['text', 'code_inline'].includes(t.type))
.map(t => t.content)
.join('')
}

function uniqueSlug (slug, slugs, failOnNonUnique, startIndex) {
let uniq = slug
let i = startIndex
Expand Down Expand Up @@ -46,10 +53,7 @@ function anchor (md, opts) {
}

// Aggregate the next token children text.
const title = tokens[idx + 1]
.children
.filter(token => token.type === 'text' || token.type === 'code_inline')
.reduce((acc, t) => acc + t.content, '')
const title = opts.getTokensText(tokens[idx + 1].children)

let slug = token.attrGet('id')

Expand Down Expand Up @@ -91,6 +95,7 @@ anchor.defaults = {
slugify,
uniqueSlugStartIndex: 1,
tabIndex: '-1',
getTokensText,

// Legacy options.
permalink: false,
Expand Down
16 changes: 16 additions & 0 deletions test.js
Expand Up @@ -216,6 +216,22 @@ test('uniqueSlugStartIndex', t => {
)
})

test('nested things', t => {
t.is(
md({ html: true }).use(anchor).render('# H1 [link](link) ![image](link) `code` ~~strike~~ _em_ **strong** <span>inline html</span>'),
'<h1 id="h1-link-code-strike-em-strong-inline-html" tabindex="-1">H1 <a href="link">link</a> <img src="link" alt="image"> <code>code</code> <s>strike</s> <em>em</em> <strong>strong</strong> <span>inline html</span></h1>\n'
)
})

test('getTokensText', t => {
t.is(
md().use(anchor, {
getTokensText: tokens => tokens.filter(t => ['text', 'image'].includes(t.type)).map(t => t.content).join('')
}).render('# H1 ![image](link) `code` _em_'),
'<h1 id="h1-image-em" tabindex="-1">H1 <img src="link" alt="image"> <code>code</code> <em>em</em></h1>\n'
)
})

nest('permalink.linkInsideHeader', test => {
test('default', t => {
t.is(
Expand Down
1 change: 1 addition & 0 deletions types/index.d.ts
Expand Up @@ -43,6 +43,7 @@ declare namespace anchor {
level?: number | number[];

slugify?(str: string): string;
getTokensText?(tokens: Token[]): string;

uniqueSlugStartIndex?: number;
permalink?: PermalinkGenerator;
Expand Down