From 8521782d073dcce2ddd45577485365fb3aa3475e Mon Sep 17 00:00:00 2001 From: Bartosz Kaszubowski Date: Sat, 25 Jul 2020 15:54:54 +0200 Subject: [PATCH 1/3] fix(v1): remove HTML content from hash links --- packages/docusaurus-1.x/lib/core/__tests__/toc.test.js | 4 ++-- packages/docusaurus-1.x/lib/core/anchors.js | 5 ++++- packages/docusaurus-1.x/lib/core/toc.js | 10 ++++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/docusaurus-1.x/lib/core/__tests__/toc.test.js b/packages/docusaurus-1.x/lib/core/__tests__/toc.test.js index 274ecb9c009b..926b68e0a43b 100644 --- a/packages/docusaurus-1.x/lib/core/__tests__/toc.test.js +++ b/packages/docusaurus-1.x/lib/core/__tests__/toc.test.js @@ -42,7 +42,7 @@ describe('getTOC', () => { test('html tag in source', () => { const headings = getTOC(`## Foo`, 'h2', []); - expect(headings[0].hashLink).toEqual('a-namefooa-foo'); + expect(headings[0].hashLink).toEqual('foo'); expect(headings[0].rawContent).toEqual(` Foo`); expect(headings[0].content).toEqual(` Foo`); }); @@ -50,7 +50,7 @@ describe('getTOC', () => { test('transform markdown syntax to html syntax', () => { const headings = getTOC(`## _Foo_`, 'h2', []); - expect(headings[0].hashLink).toEqual('a-namefooa-_foo_'); + expect(headings[0].hashLink).toEqual('_foo_'); expect(headings[0].rawContent).toEqual(` _Foo_`); expect(headings[0].content).toEqual(` Foo`); diff --git a/packages/docusaurus-1.x/lib/core/anchors.js b/packages/docusaurus-1.x/lib/core/anchors.js index 138724df8bd5..229981619638 100644 --- a/packages/docusaurus-1.x/lib/core/anchors.js +++ b/packages/docusaurus-1.x/lib/core/anchors.js @@ -22,7 +22,10 @@ function anchors(md) { const textToken = tokens[idx + 1]; if (textToken.content) { - const anchor = toSlug(textToken.content, slugger); + const anchor = toSlug( + textToken.content.replace(/<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi, ''), + slugger, + ); return ``; } diff --git a/packages/docusaurus-1.x/lib/core/toc.js b/packages/docusaurus-1.x/lib/core/toc.js index 9f07cb0ef07d..773000b2373b 100644 --- a/packages/docusaurus-1.x/lib/core/toc.js +++ b/packages/docusaurus-1.x/lib/core/toc.js @@ -36,13 +36,15 @@ function getTOC(content, headingTags = 'h2', subHeadingTags = 'h3') { let current; headings.forEach((heading) => { - const rawContent = heading.content; - const rendered = md.renderInline(rawContent); - - const hashLink = toSlug(rawContent, slugger); if (!allowedHeadingLevels.includes(heading.lvl)) { return; } + const rawContent = heading.content; + const rendered = md.renderInline(rawContent); + const hashLink = toSlug( + rawContent.replace(/<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi, ''), + slugger, + ); const entry = { hashLink, rawContent, From 532dc507b90a8e365dc20fad405282139c51de45 Mon Sep 17 00:00:00 2001 From: Bartosz Kaszubowski Date: Sat, 25 Jul 2020 16:25:13 +0200 Subject: [PATCH 2/3] fix one more test --- packages/docusaurus-1.x/lib/core/toc.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/docusaurus-1.x/lib/core/toc.js b/packages/docusaurus-1.x/lib/core/toc.js index 773000b2373b..f11efe256c6e 100644 --- a/packages/docusaurus-1.x/lib/core/toc.js +++ b/packages/docusaurus-1.x/lib/core/toc.js @@ -36,15 +36,15 @@ function getTOC(content, headingTags = 'h2', subHeadingTags = 'h3') { let current; headings.forEach((heading) => { - if (!allowedHeadingLevels.includes(heading.lvl)) { - return; - } const rawContent = heading.content; const rendered = md.renderInline(rawContent); const hashLink = toSlug( rawContent.replace(/<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi, ''), slugger, ); + if (!allowedHeadingLevels.includes(heading.lvl)) { + return; + } const entry = { hashLink, rawContent, From 159c152596ee980ad36ae73dad69ed5abe171e3d Mon Sep 17 00:00:00 2001 From: Bartosz Kaszubowski Date: Mon, 27 Jul 2020 23:06:40 +0200 Subject: [PATCH 3/3] rewrite changes as new feature to prevent breaking changes --- docs/api-site-config.md | 7 +++++++ .../lib/core/__tests__/toc.test.js | 14 +++++++++++-- packages/docusaurus-1.x/lib/core/anchors.js | 11 +++++----- .../docusaurus-1.x/lib/core/nav/OnPageNav.js | 14 +++++++++++-- .../docusaurus-1.x/lib/core/renderMarkdown.js | 2 +- packages/docusaurus-1.x/lib/core/toc.js | 20 ++++++++++++------- packages/docusaurus-1.x/lib/server/docs.js | 2 +- 7 files changed, 52 insertions(+), 18 deletions(-) diff --git a/docs/api-site-config.md b/docs/api-site-config.md index e7b8c3b20315..68accd3eb423 100644 --- a/docs/api-site-config.md +++ b/docs/api-site-config.md @@ -318,6 +318,10 @@ Set this to `true` if you want to enable the scroll to top button at the bottom Optional options configuration for the scroll to top button. You do not need to use this, even if you set `scrollToTop` to `true`; it just provides you more configuration control of the button. You can find more options [here](https://github.com/vfeskov/vanilla-back-to-top/blob/v7.1.14/OPTIONS.md). By default, we set the zIndex option to 100. +#### `slugPreprocessor` [function] + +Define the slug preprocessor function if you want to customize the text used for generating the hash links. Function provides the base string as the first argument and must always return a string. + #### `stylesheets` [array] An array of CSS sources to load. The values can be either strings or plain objects of attribute-value maps. The link tag will be inserted in the HTML head. @@ -463,6 +467,9 @@ const siteConfig = { scrollToTopOptions: { zIndex: 100, }, + // Remove the HTML tags and HTML tags content before generating the slug + slugPreprocessor: (slugBase) => + slugBase.replace(/<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi, ''), }; module.exports = siteConfig; diff --git a/packages/docusaurus-1.x/lib/core/__tests__/toc.test.js b/packages/docusaurus-1.x/lib/core/__tests__/toc.test.js index 926b68e0a43b..7a5b95e07a71 100644 --- a/packages/docusaurus-1.x/lib/core/__tests__/toc.test.js +++ b/packages/docusaurus-1.x/lib/core/__tests__/toc.test.js @@ -42,7 +42,7 @@ describe('getTOC', () => { test('html tag in source', () => { const headings = getTOC(`## Foo`, 'h2', []); - expect(headings[0].hashLink).toEqual('foo'); + expect(headings[0].hashLink).toEqual('a-namefooa-foo'); expect(headings[0].rawContent).toEqual(` Foo`); expect(headings[0].content).toEqual(` Foo`); }); @@ -50,7 +50,7 @@ describe('getTOC', () => { test('transform markdown syntax to html syntax', () => { const headings = getTOC(`## _Foo_`, 'h2', []); - expect(headings[0].hashLink).toEqual('_foo_'); + expect(headings[0].hashLink).toEqual('a-namefooa-_foo_'); expect(headings[0].rawContent).toEqual(` _Foo_`); expect(headings[0].content).toEqual(` Foo`); @@ -69,6 +69,16 @@ describe('getTOC', () => { expect(headings[0].rawContent).toEqual(`function1 [array]`); expect(headings[0].content).toEqual(`function1 [array]`); }); + + test('test slugPreprocessor', () => { + const headings = getTOC(`## Foo`, 'h2', [], (s) => + s.replace(/foo/gi, 'bar'), + ); + + expect(headings[0].hashLink).toEqual('a-namebara-bar'); + expect(headings[0].rawContent).toEqual(` Foo`); + expect(headings[0].content).toEqual(` Foo`); + }); }); describe('insertTOC', () => { diff --git a/packages/docusaurus-1.x/lib/core/anchors.js b/packages/docusaurus-1.x/lib/core/anchors.js index 229981619638..ef0b028983eb 100644 --- a/packages/docusaurus-1.x/lib/core/anchors.js +++ b/packages/docusaurus-1.x/lib/core/anchors.js @@ -11,7 +11,7 @@ const toSlug = require('./toSlug'); /** * The anchors plugin adds GFM-style anchors to headings. */ -function anchors(md) { +function anchors(md, slugPreprocessor) { const originalRender = md.renderer.rules.heading_open; md.renderer.rules.heading_open = function (tokens, idx, options, env) { @@ -22,10 +22,11 @@ function anchors(md) { const textToken = tokens[idx + 1]; if (textToken.content) { - const anchor = toSlug( - textToken.content.replace(/<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi, ''), - slugger, - ); + const slugBase = + slugPreprocessor && typeof slugPreprocessor === 'function' + ? slugPreprocessor(textToken.content) + : textToken.content; + const anchor = toSlug(slugBase, slugger); return ``; } diff --git a/packages/docusaurus-1.x/lib/core/nav/OnPageNav.js b/packages/docusaurus-1.x/lib/core/nav/OnPageNav.js index d3562b55a2e4..75fe4ac2889e 100644 --- a/packages/docusaurus-1.x/lib/core/nav/OnPageNav.js +++ b/packages/docusaurus-1.x/lib/core/nav/OnPageNav.js @@ -37,8 +37,18 @@ class OnPageNav extends React.Component { render() { const customTags = siteConfig.onPageNavHeadings; const headings = customTags - ? getTOC(this.props.rawContent, customTags.topLevel, customTags.sub) - : getTOC(this.props.rawContent); + ? getTOC( + this.props.rawContent, + customTags.topLevel, + customTags.sub, + siteConfig.slugPreprocessor, + ) + : getTOC( + this.props.rawContent, + undefined, + undefined, + siteConfig.slugPreprocessor, + ); return ; } diff --git a/packages/docusaurus-1.x/lib/core/renderMarkdown.js b/packages/docusaurus-1.x/lib/core/renderMarkdown.js index 6727685956f1..47863c47e23d 100644 --- a/packages/docusaurus-1.x/lib/core/renderMarkdown.js +++ b/packages/docusaurus-1.x/lib/core/renderMarkdown.js @@ -102,7 +102,7 @@ class MarkdownRenderer { const md = new Markdown(markdownOptions); // Register anchors plugin - md.use(anchors); + md.use(anchors, siteConfig.slugPreprocessor); // Linkify md.use(linkify); diff --git a/packages/docusaurus-1.x/lib/core/toc.js b/packages/docusaurus-1.x/lib/core/toc.js index f11efe256c6e..9354f1420336 100644 --- a/packages/docusaurus-1.x/lib/core/toc.js +++ b/packages/docusaurus-1.x/lib/core/toc.js @@ -19,7 +19,12 @@ const tocRegex = new RegExp('', 'i'); * Array of heading objects with `hashLink`, `content` and `children` fields * */ -function getTOC(content, headingTags = 'h2', subHeadingTags = 'h3') { +function getTOC( + content, + headingTags = 'h2', + subHeadingTags = 'h3', + slugPreprocessor = undefined, +) { const tagToLevel = (tag) => Number(tag.slice(1)); const headingLevels = [].concat(headingTags).map(tagToLevel); const subHeadingLevels = subHeadingTags @@ -38,10 +43,11 @@ function getTOC(content, headingTags = 'h2', subHeadingTags = 'h3') { headings.forEach((heading) => { const rawContent = heading.content; const rendered = md.renderInline(rawContent); - const hashLink = toSlug( - rawContent.replace(/<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi, ''), - slugger, - ); + const slugBase = + slugPreprocessor && typeof slugPreprocessor === 'function' + ? slugPreprocessor(rawContent) + : rawContent; + const hashLink = toSlug(slugBase, slugger); if (!allowedHeadingLevels.includes(heading.lvl)) { return; } @@ -63,12 +69,12 @@ function getTOC(content, headingTags = 'h2', subHeadingTags = 'h3') { // takes the content of a doc article and returns the content with a table of // contents inserted -function insertTOC(rawContent) { +function insertTOC(rawContent, slugPreprocessor = undefined) { if (!rawContent || !tocRegex.test(rawContent)) { return rawContent; } const filterRe = /^`[^`]*`/; - const headers = getTOC(rawContent, 'h3', null); + const headers = getTOC(rawContent, 'h3', null, slugPreprocessor); const tableOfContents = headers .filter((header) => filterRe.test(header.rawContent)) .map((header) => ` - [${header.rawContent}](#${header.hashLink})`) diff --git a/packages/docusaurus-1.x/lib/server/docs.js b/packages/docusaurus-1.x/lib/server/docs.js index b7145113f25f..7b054d362844 100644 --- a/packages/docusaurus-1.x/lib/server/docs.js +++ b/packages/docusaurus-1.x/lib/server/docs.js @@ -104,7 +104,7 @@ function mdToHtmlify(oldContent, mdToHtml, metadata, siteConfig) { function getMarkup(rawContent, mdToHtml, metadata, siteConfig) { // generate table of contents - let content = insertTOC(rawContent); + let content = insertTOC(rawContent, siteConfig.slugPreprocessor); // replace any links to markdown files to their website html links content = mdToHtmlify(content, mdToHtml, metadata, siteConfig);