-
Notifications
You must be signed in to change notification settings - Fork 72
/
index.js
124 lines (97 loc) · 3.52 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import * as permalink from './permalink'
const slugify = (s) => encodeURIComponent(String(s).trim().toLowerCase().replace(/\s+/g, '-'))
function getTokensText (tokens) {
return tokens
.filter(t => ['text', 'code_inline'].includes(t.type))
.map(t => t.content)
.join('')
}
function uniqueSlug (slug, slugs, failOnNonUnique, startIndex) {
let uniq = slug
let i = startIndex
if (failOnNonUnique && Object.prototype.hasOwnProperty.call(slugs, uniq)) {
throw new Error(`User defined \`id\` attribute \`${slug}\` is not unique. Please fix it in your Markdown to continue.`)
} else {
while (Object.prototype.hasOwnProperty.call(slugs, uniq)) {
uniq = `${slug}-${i}`
i += 1
}
}
slugs[uniq] = true
return uniq
}
const isLevelSelectedNumber = selection => level => level >= selection
const isLevelSelectedArray = selection => level => selection.includes(level)
function anchor (md, opts) {
opts = Object.assign({}, anchor.defaults, opts)
md.core.ruler.push('anchor', state => {
const slugs = {}
const tokens = state.tokens
const isLevelSelected = Array.isArray(opts.level)
? isLevelSelectedArray(opts.level)
: isLevelSelectedNumber(opts.level)
for (let idx = 0; idx < tokens.length; idx++) {
const token = tokens[idx]
if (token.type !== 'heading_open') {
continue
}
if (!isLevelSelected(Number(token.tag.substr(1)))) {
continue
}
// Aggregate the next token children text.
const title = opts.getTokensText(tokens[idx + 1].children)
let slug = token.attrGet('id')
if (slug == null) {
if (opts.slugifyWithState) {
slug = opts.slugifyWithState(title, state)
} else {
slug = opts.slugify(title)
}
slug = uniqueSlug(slug, slugs, false, opts.uniqueSlugStartIndex)
} else {
slug = uniqueSlug(slug, slugs, true, opts.uniqueSlugStartIndex)
}
token.attrSet('id', slug)
if (opts.tabIndex !== false) {
token.attrSet('tabindex', `${opts.tabIndex}`)
}
if (typeof opts.permalink === 'function') {
opts.permalink(slug, opts, state, idx)
} else if (opts.permalink) {
opts.renderPermalink(slug, opts, state, idx)
} else if (opts.renderPermalink && opts.renderPermalink !== permalink.legacy) {
opts.renderPermalink(slug, opts, state, idx)
}
// A permalink renderer could modify the `tokens` array so
// make sure to get the up-to-date index on each iteration.
idx = tokens.indexOf(token)
if (opts.callback) {
opts.callback(token, { slug, title })
}
}
})
}
anchor.permalink = permalink
anchor.defaults = {
level: 1,
slugify,
uniqueSlugStartIndex: 1,
tabIndex: '-1',
getTokensText,
// Legacy options.
permalink: false,
renderPermalink: permalink.legacy,
permalinkClass: permalink.ariaHidden.defaults.class,
permalinkSpace: permalink.ariaHidden.defaults.space,
permalinkSymbol: '¶',
permalinkBefore: permalink.ariaHidden.defaults.placement === 'before',
permalinkHref: permalink.ariaHidden.defaults.renderHref,
permalinkAttrs: permalink.ariaHidden.defaults.renderAttrs
}
// Dirty hack to make `import anchor from 'markdown-it-anchor'` work with
// TypeScript which doesn't support the `module` field of `package.json` and
// will always get the CommonJS version which otherwise wouldn't have a
// `default` key, resulting in markdown-it-anchor being undefined when being
// imported that way.
anchor.default = anchor
export default anchor