From 7fb788dbe6dd53dd1a4b9116c7939838997121b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Val=C3=A9rian=20Galliat?= Date: Fri, 8 Apr 2022 09:14:18 -0400 Subject: [PATCH] Add a `getTokensText` option --- CHANGELOG.md | 9 +++++++- README.md | 59 ++++++++++++++++++++++++++++++++++++++---------- index.js | 13 +++++++---- test.js | 16 +++++++++++++ types/index.d.ts | 1 + 5 files changed, 81 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 589228f..71f58da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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]) @@ -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 @@ -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 diff --git a/README.md b/README.md index 5ea9cd1..6c2bdde 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/index.js b/index.js index 31b53c2..6a850c0 100644 --- a/index.js +++ b/index.js @@ -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 @@ -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') @@ -91,6 +95,7 @@ anchor.defaults = { slugify, uniqueSlugStartIndex: 1, tabIndex: '-1', + getTokensText, // Legacy options. permalink: false, diff --git a/test.js b/test.js index 254dcf8..4fe2783 100644 --- a/test.js +++ b/test.js @@ -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** inline html'), + '

H1 link image code strike em strong inline html

\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 image code em

\n' + ) +}) + nest('permalink.linkInsideHeader', test => { test('default', t => { t.is( diff --git a/types/index.d.ts b/types/index.d.ts index 7c74da2..d87da78 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -43,6 +43,7 @@ declare namespace anchor { level?: number | number[]; slugify?(str: string): string; + getTokensText?(tokens: Token[]): string; uniqueSlugStartIndex?: number; permalink?: PermalinkGenerator;