Skip to content

Commit 85da492

Browse files
committed
fix(search): clean markdown elements in search contents
1 parent 4ad3e36 commit 85da492

File tree

3 files changed

+97
-10
lines changed

3 files changed

+97
-10
lines changed

src/plugins/search/markdown-to-txt.js

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* This is a modified version of the
3+
* [markdown-to-txt](https://www.npmjs.com/package/markdown-to-txt) library.
4+
*/
5+
import { marked } from 'marked';
6+
import { escape, unescape } from 'lodash';
7+
const block = text => text + '\n\n';
8+
const escapeBlock = text => escape(text) + '\n\n';
9+
const line = text => text + '\n';
10+
const inline = text => text;
11+
const newline = () => '\n';
12+
const empty = () => '';
13+
14+
const TxtRenderer = {
15+
// Block elements
16+
code: escapeBlock,
17+
blockquote: block,
18+
html: empty,
19+
heading: block,
20+
hr: newline,
21+
list: text => block(text.trim()),
22+
listitem: line,
23+
checkbox: empty,
24+
paragraph: block,
25+
table: (header, body) => line(header + body),
26+
tablerow: text => line(text.trim()),
27+
tablecell: text => text + ' ',
28+
// Inline elements
29+
strong: inline,
30+
em: inline,
31+
codespan: inline,
32+
br: newline,
33+
del: inline,
34+
link: (_0, _1, text) => text,
35+
image: (_0, _1, text) => text,
36+
text: inline,
37+
// etc.
38+
options: {},
39+
};
40+
41+
/**
42+
* Converts markdown to plaintext using the marked Markdown library.
43+
* Accepts [MarkedOptions](https://marked.js.org/using_advanced#options) as
44+
* the second argument.
45+
*
46+
* NOTE: The output of markdownToTxt is NOT sanitized. The output may contain
47+
* valid HTML, JavaScript, etc. Be sure to sanitize if the output is intended
48+
* for web use.
49+
*
50+
* @param markdown the markdown text to txtify
51+
* @param options the marked options
52+
* @returns the unmarked text
53+
*/
54+
export function markdownToTxt(markdown, options) {
55+
const unmarked = marked(markdown, { ...options, renderer: TxtRenderer });
56+
const unescaped = unescape(unmarked);
57+
const trimmed = unescaped.trim();
58+
return trimmed;
59+
}
60+
61+
export default markdownToTxt;

src/plugins/search/search.js

+17-10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
getAndRemoveConfig,
33
getAndRemoveDocisfyIgnoreConfig,
44
} from '../../core/render/utils.js';
5+
import { markdownToTxt } from './markdown-to-txt.js';
56

67
let INDEXS = {};
78

@@ -34,6 +35,17 @@ function escapeHtml(string) {
3435
return String(string).replace(/[&<>"']/g, s => entityMap[s]);
3536
}
3637

38+
function formatContent(text) {
39+
return escapeHtml(cleanMarkdown(ignoreDiacriticalMarks(text)));
40+
}
41+
42+
function cleanMarkdown(text) {
43+
if (text) {
44+
text = markdownToTxt(text);
45+
}
46+
return text;
47+
}
48+
3749
function getAllPaths(router) {
3850
const paths = [];
3951

@@ -175,19 +187,14 @@ export function search(query) {
175187
keywords.forEach(keyword => {
176188
// From https://github.com/sindresorhus/escape-string-regexp
177189
const regEx = new RegExp(
178-
escapeHtml(ignoreDiacriticalMarks(keyword)).replace(
179-
/[|\\{}()[\]^$+*?.]/g,
180-
'\\$&',
181-
),
190+
formatContent(keyword).replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'),
182191
'gi',
183192
);
184193
let indexTitle = -1;
185194
let indexContent = -1;
186-
handlePostTitle = postTitle
187-
? escapeHtml(ignoreDiacriticalMarks(postTitle))
188-
: postTitle;
195+
handlePostTitle = postTitle ? formatContent(postTitle) : postTitle;
189196
handlePostContent = postContent
190-
? escapeHtml(ignoreDiacriticalMarks(postContent))
197+
? formatContent(postContent)
191198
: postContent;
192199

193200
indexTitle = postTitle ? handlePostTitle.search(regEx) : -1;
@@ -226,8 +233,8 @@ export function search(query) {
226233

227234
if (matchesScore > 0) {
228235
const matchingPost = {
229-
title: handlePostTitle,
230-
content: postContent ? resultStr : '',
236+
title: formatContent(handlePostTitle),
237+
content: formatContent(postContent ? resultStr : ''),
231238
url: postUrl,
232239
score: matchesScore,
233240
};

test/e2e/search.test.js

+19
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,23 @@ test.describe('Search Plugin Tests', () => {
232232
await page.keyboard.press('z');
233233
await expect(searchFieldElm).toBeFocused();
234234
});
235+
test('search result should remove markdown', async ({ page }) => {
236+
const docsifyInitConfig = {
237+
markdown: {
238+
homepage: `
239+
# The [mock](example.com) link
240+
There is lots of words.
241+
`,
242+
},
243+
scriptURLs: ['/dist/plugins/search.js'],
244+
};
245+
246+
const searchFieldElm = page.locator('input[type=search]');
247+
const resultsHeadingElm = page.locator('.results-panel h2');
248+
249+
await docsifyInit(docsifyInitConfig);
250+
251+
await searchFieldElm.fill('There');
252+
await expect(resultsHeadingElm).toHaveText('The mock link');
253+
});
235254
});

0 commit comments

Comments
 (0)