Skip to content

Commit 294af4f

Browse files
committed
Resolves issue #52 - Adding anchors to headings
Credit to @fabiosantoscode for his initial work on this fix
1 parent 8732a17 commit 294af4f

File tree

6 files changed

+102
-1
lines changed

6 files changed

+102
-1
lines changed

build.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ const markdown = require('metalsmith-markdown')
1111
const prism = require('metalsmith-prism')
1212
const stylus = require('metalsmith-stylus')
1313
const permalinks = require('metalsmith-permalinks')
14+
const marked = require('marked')
1415
const path = require('path')
1516
const fs = require('fs')
1617
const ncp = require('ncp')
1718

1819
const filterStylusPartials = require('./scripts/plugins/filter-stylus-partials')
1920
const mapHandlebarsPartials = require('./scripts/plugins/map-handlebars-partials')
21+
const anchorMarkdownHeadings = require('./scripts/plugins/anchor-markdown-headings')
2022
const loadVersions = require('./scripts/load-versions')
2123

2224
/** Build **/
@@ -25,6 +27,14 @@ const loadVersions = require('./scripts/load-versions')
2527
// for properties which are not present in the given language
2628
const DEFAULT_LANG = 'en'
2729

30+
const renderer = new marked.Renderer()
31+
renderer.heading = anchorMarkdownHeadings
32+
33+
const markedOptions = {
34+
langPrefix: 'language-',
35+
renderer: renderer
36+
}
37+
2838
function i18nJSON (lang) {
2939
var defaultJSON = require(`./locale/${DEFAULT_LANG}/site.json`)
3040
var templateJSON = require(`./locale/${lang}/site.json`)
@@ -93,7 +103,7 @@ function buildlocale (source, locale) {
93103
refer: false
94104
}
95105
}))
96-
.use(markdown({ langPrefix: 'language-' }))
106+
.use(markdown(markedOptions))
97107
.use(prism())
98108
.use(filterStylusPartials())
99109
.use(stylus({

layouts/css/base.styl

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ pre
8585
@import 'page-modules/_in-the-news'
8686
@import 'page-modules/_download'
8787
@import 'page-modules/_scrollToTop'
88+
@import 'page-modules/_anchorLinks'
8889

8990
.intro
9091
margin-top 140px
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Anchor links for page headings
2+
.anchor
3+
color #ccc
4+
background none
5+
padding 0 .25em
6+
7+
&:link
8+
&:active
9+
&:hover
10+
color #ccc
11+
background inherit
12+
13+
14+
h1, h2, h3, h4, h5, h6
15+
&:hover > .anchor:before
16+
content '#'
17+
18+
.anchor:focus:before
19+
content '#'

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"chokidar": "^1.0.5",
2929
"handlebars": "^3.0.0",
3030
"map-async": "~0.1.1",
31+
"marked": "^0.3.5",
3132
"metalsmith": "^2.0.0",
3233
"metalsmith-collections": "^0.7.0",
3334
"metalsmith-feed": "0.0.6",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use strict'
2+
3+
module.exports = function anchorMarkdownHeadings (text, level, raw) {
4+
var escapedText = raw
5+
.replace(/(\[([^\]]+)\]\([^)]+\))/g, '$2')
6+
.replace(/[^\w]+/g, '-')
7+
.replace(/-{2,}/g, '-')
8+
.replace(/(^-|-$)/g, '')
9+
.toLowerCase()
10+
return '<h' + level + '>' + text + '<a name="' +
11+
escapedText + '" class="anchor" href="#' +
12+
escapedText + '"></a></h' + level + '>'
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
'use strict'
2+
3+
const test = require('tape')
4+
5+
test('anchorMarkdownHeadings', (t) => {
6+
const anchorMarkdownHeadings = require('../../scripts/plugins/anchor-markdown-headings')
7+
8+
t.test('correctly pharses markdown heading without links', (t) => {
9+
const text = 'Simple title'
10+
const level = 1
11+
const raw = 'Simple title'
12+
const output = anchorMarkdownHeadings(text, level, raw)
13+
const expected = '<h1>Simple title<a name="simple-title" class="anchor" ' +
14+
'href="#simple-title"></a></h1>'
15+
16+
t.equal(output, expected)
17+
t.end()
18+
})
19+
20+
t.test('correctly pharses markdown heading with a single link', (t) => {
21+
const text = 'Title with <a href="#">link</a>'
22+
const level = 3
23+
const raw = 'Title with [link](#)'
24+
const output = anchorMarkdownHeadings(text, level, raw)
25+
const expected = '<h3>Title with <a href="#">link</a>' +
26+
'<a name="title-with-link" class="anchor" href="#title-with-link"></a></h3>'
27+
28+
t.equal(output, expected)
29+
t.end()
30+
})
31+
32+
t.test('correctly pharses markdown heading with multiple links', (t) => {
33+
const text = 'a <a href="b">b</a> c<a href="d">d</a>e'
34+
const level = 2
35+
const raw = 'a [b](b) c[d](d)e'
36+
const output = anchorMarkdownHeadings(text, level, raw)
37+
const expected = '<h2>a <a href="b">b</a> c<a href="d">d</a>e' +
38+
'<a name="a-b-cde" class="anchor" href="#a-b-cde"></a></h2>'
39+
40+
t.equal(output, expected)
41+
t.end()
42+
})
43+
44+
t.test('makes pretty slugs', (t) => {
45+
const text = '$$$ WIN BIG! $$$'
46+
const level = 4
47+
const raw = '$$$ WIN BIG! $$$'
48+
const output = anchorMarkdownHeadings(text, level, raw)
49+
const expected = '<h4>$$$ WIN BIG! $$$' +
50+
'<a name="win-big" class="anchor" href="#win-big"></a></h4>'
51+
52+
t.equal(output, expected)
53+
t.end()
54+
})
55+
56+
t.end()
57+
})

0 commit comments

Comments
 (0)