Skip to content

Commit

Permalink
Merge pull request #172 from curbengh/custom-anchor
Browse files Browse the repository at this point in the history
feat: anchorAlias option to set custom header id
curbengh authored Sep 25, 2020
2 parents 9058e8b + a32e36a commit 3aac49e
Showing 4 changed files with 52 additions and 1 deletion.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ marked:
smartypants: true
quotes: '“”‘’'
modifyAnchors: 0
anchorAlias: false
autolink: true
mangle: true
sanitizeUrl: false
@@ -59,6 +60,9 @@ marked:
* This is to obscure email address from _basic_ crawler used by spam bot, while still readable to web browsers.
- **sanitizeUrl** - Remove URLs that start with `javascript:`, `vbscript:` and `data:`.
- **headerIds** - Insert header id, e.g. `<h1 id="value">text</h1>`. Useful for inserting anchor link to each paragraph with a heading.
- **anchorAlias** - Enables custom header id
* Example: `## [foo](#bar)`, id will be set as "bar".
* Requires **headerIds** to be enabled.
- **lazyload** - Lazy loading images via `loading="lazy"` attribute.
- **prependRoot** - Prepend root value to (internal) image path.
* Example `_config.yml`:
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ hexo.config.marked = Object.assign({
mangle: true,
sanitizeUrl: false,
headerIds: true,
anchorAlias: false,
lazyload: false,
// TODO: enable prependRoot by default in v3
prependRoot: false,
15 changes: 14 additions & 1 deletion lib/renderer.js
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ const { encodeURL, slugize, stripHTML, url_for, isExternalLink } = require('hexo
const MarkedRenderer = marked.Renderer;
const MarkedTokenizer = marked.Tokenizer;
const { basename, dirname, extname, join } = require('path').posix;
const rATag = /<a(?:\s+?|\s+?[^<>]+\s+?)?href=["'](?:#)([^<>"']+)["'][^<>]*>/i;

const anchorId = (str, transformOption) => {
return slugize(str.trim(), {transform: transformOption});
@@ -20,7 +21,7 @@ class Renderer extends MarkedRenderer {

// Add id attribute to headings
heading(text, level) {
const { headerIds, modifyAnchors } = this.options;
const { anchorAlias, headerIds, modifyAnchors } = this.options;
const { _headingId } = this;

if (!headerIds) {
@@ -31,13 +32,25 @@ class Renderer extends MarkedRenderer {
let id = anchorId(stripHTML(text), transformOption);
const headingId = _headingId;

const anchorAliasOpt = anchorAlias && text.startsWith('<a href="#');
if (anchorAliasOpt) {
const customAnchor = text.match(rATag)[1];
id = anchorId(stripHTML(customAnchor), transformOption);
}

// Add a number after id if repeated
if (headingId[id]) {
id += `-${headingId[id]++}`;
} else {
headingId[id] = 1;
}

if (anchorAliasOpt) {
text = text.replace(rATag, (str, alias) => {
return str.replace(alias, id);
});
}

// add headerlink
return `<h${level} id="${id}"><a href="#${id}" class="headerlink" title="${stripHTML(text)}"></a>${text}</h${level}>`;
}
33 changes: 33 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -74,6 +74,39 @@ describe('Marked renderer', () => {
].join(''));
});

describe('anchorAlias', () => {
beforeEach(() => { hexo.config.marked.anchorAlias = true; });

it('default', () => {
const body = '## [foo](#alias)';

const result = r({text: body});
result.should.eql('<h2 id="alias"><a href="#alias" class="headerlink" title="foo"></a><a href="#alias">foo</a></h2>');
});

it('duplicate anchors', () => {
const body = [
'## [foo](#alias)',
'## [bar](#alias)'
].join('\n');

const result = r({text: body});
result.should.eql([
'<h2 id="alias"><a href="#alias" class="headerlink" title="foo"></a><a href="#alias">foo</a></h2>',
'<h2 id="alias-1"><a href="#alias-1" class="headerlink" title="bar"></a><a href="#alias-1">bar</a></h2>'
].join(''));
});

it('modifyAnchors', () => {
hexo.config.marked.modifyAnchors = 2;
const body = '## [foo](#alias)';

const result = r({text: body});
result.should.eql('<h2 id="ALIAS"><a href="#ALIAS" class="headerlink" title="foo"></a><a href="#ALIAS">foo</a></h2>');
});
});


it('should handle duplicate headings properly', () => {
const body = [
'## foo',

0 comments on commit 3aac49e

Please sign in to comment.