Skip to content

Commit 77df32d

Browse files
committed
fix: improve slug generation
1 parent 3c114d1 commit 77df32d

File tree

4 files changed

+49
-16
lines changed

4 files changed

+49
-16
lines changed

src/core/render/compiler/heading.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import {
66
import { slugify } from '../slugify.js';
77

88
export const headingCompiler = ({ renderer, router, compiler }) =>
9-
(renderer.heading = function ({ tokens, depth }) {
10-
const text = this.parser.parseInline(tokens);
11-
let { str, config } = getAndRemoveConfig(text);
9+
(renderer.heading = function ({ tokens, depth, text }) {
10+
const parsedText = this.parser.parseInline(tokens);
11+
let { str, config } = getAndRemoveConfig(parsedText);
1212
const nextToc = { depth, title: str };
1313

1414
const { content, ignoreAllSubs, ignoreSubHeading } =
@@ -18,7 +18,7 @@ export const headingCompiler = ({ renderer, router, compiler }) =>
1818
nextToc.title = removeAtag(str);
1919
nextToc.ignoreAllSubs = ignoreAllSubs;
2020
nextToc.ignoreSubHeading = ignoreSubHeading;
21-
const slug = slugify(config.id || str);
21+
const slug = slugify(config.id || text);
2222
const url = router.toURL(router.getCurrentPath(), { id: slug });
2323
nextToc.slug = url;
2424
compiler.toc.push(nextToc);

src/core/render/slugify.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ export function slugify(str) {
1212

1313
let slug = str
1414
.trim()
15+
.normalize('NFKD')
16+
.replace(/[\p{Emoji_Presentation}\p{Extended_Pictographic}]/gu, '')
1517
.replace(/[A-Z]+/g, lower)
1618
.replace(/<[^>]+>/g, '')
1719
.replace(re, '')
1820
.replace(/\s/g, '-')
19-
.replace(/-+/g, '-')
2021
.replace(/^(\d)/, '_$1');
2122
let count = cache[slug];
2223

src/plugins/search/search.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
getAndRemoveConfig,
33
getAndRemoveDocsifyIgnoreConfig,
4+
removeAtag,
45
} from '../../core/render/utils.js';
56
import { markdownToTxt } from './markdown-to-txt.js';
67
import Dexie from 'dexie';
@@ -110,16 +111,11 @@ export function genIndex(path, content = '', router, depth, indexKey) {
110111
if (token.type === 'heading' && token.depth <= depth) {
111112
const { str, config } = getAndRemoveConfig(token.text);
112113

113-
const text = getAndRemoveDocsifyIgnoreConfig(token.text).content;
114-
115-
if (config.id) {
116-
slug = router.toURL(path, { id: slugify(config.id) });
117-
} else {
118-
slug = router.toURL(path, { id: slugify(escapeHtml(text)) });
119-
}
114+
slug = router.toURL(path, { id: slugify(config.id || token.text) });
120115

121116
if (str) {
122117
title = getAndRemoveDocsifyIgnoreConfig(str).content;
118+
title = removeAtag(title.trim());
123119
}
124120

125121
index[slug] = {

test/unit/render-util.test.js

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,49 @@ describe('core/render/tpl', () => {
168168

169169
describe('core/render/slugify', () => {
170170
test('slugify()', () => {
171-
const result = slugify(
171+
const htmlStrippedSlug = slugify(
172172
'Bla bla bla <svg aria-label="broken" class="broken" viewPort="0 0 1 1"><circle cx="0.5" cy="0.5"/></svg>',
173173
);
174-
const result2 = slugify(
174+
expect(htmlStrippedSlug).toBe('bla-bla-bla-');
175+
176+
const nestedHtmlStrippedSlug = slugify(
175177
'Another <span style="font-size: 1.2em" class="foo bar baz">broken <span class="aaa">example</span></span>',
176178
);
177-
expect(result).toBe('bla-bla-bla-');
178-
expect(result2).toBe('another-broken-example');
179+
expect(nestedHtmlStrippedSlug).toBe('another-broken-example');
180+
181+
const emojiRemovedSlug = slugify('emoji test ⚠️🔥✅');
182+
expect(emojiRemovedSlug).toBe('emoji-test-️');
183+
184+
const multiSpaceSlug = slugify('Title with multiple spaces');
185+
expect(multiSpaceSlug).toBe('title----with---multiple-spaces');
186+
187+
const numberLeadingSlug = slugify('123abc');
188+
expect(numberLeadingSlug).toBe('_123abc');
189+
190+
const firstDuplicate = slugify('duplicate');
191+
expect(firstDuplicate).toBe('duplicate');
192+
193+
const secondDuplicate = slugify('duplicate');
194+
expect(secondDuplicate).toBe('duplicate-1');
195+
196+
const thirdDuplicate = slugify('duplicate');
197+
expect(thirdDuplicate).toBe('duplicate-2');
198+
199+
const mixedCaseSlug = slugify('This Is Mixed CASE');
200+
expect(mixedCaseSlug).toBe('this-is-mixed-case');
201+
202+
const chinesePreservedSlug = slugify('你好 world');
203+
expect(chinesePreservedSlug).toBe('你好-world');
204+
205+
const specialCharSlug = slugify('C++ vs. Java & Python!');
206+
expect(specialCharSlug).toBe('c-vs-java--python');
207+
208+
const docsifyIgnoreSlug = slugify(
209+
'Ignore Heading <!-- {docsify-ignore} -->',
210+
);
211+
expect(docsifyIgnoreSlug).toBe('ignore-heading-');
212+
213+
const quoteCleanedSlug = slugify('"The content"');
214+
expect(quoteCleanedSlug).toBe('the-content');
179215
});
180216
});

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy