@@ -37,19 +37,17 @@ function find(ctx) {
3737 space[constants.referenceId] = references
3838 space[constants.landmarkId] = landmarks
3939
40− landmarks[absolute] = true
40+ landmarks[absolute] = {'': true}
4141
4242 slugs.reset()
4343
4444 visit(ctx.tree, mark)
4545
46− // eslint-disable-next-line complexity
4746 function mark(node) {
4847 var data = node.data || {}
4948 var props = data.hProperties || {}
5049 var id = props.name || props.id || data.id
51− var filePath = node.url ? urlToPath(node.url, config) : null
52− var numberSignIndex
50+ var info = node.url ? urlToPath(node.url, config) : null
5351 var refs
5452 var fp
5553 var hash
@@ -59,52 +57,36 @@ function find(ctx) {
5957 }
6058
6159 if (id) {
62− landmarks[absolute + numberSign + id] = true
60+ landmarks[absolute][id] = true
6361 }
6462
65− if (filePath) {
66− numberSignIndex = filePath.indexOf(numberSign)
67− fp = filePath
68−
69− if (numberSignIndex !== -1) {
70− fp = filePath.slice(0, numberSignIndex)
71− hash = filePath.slice(numberSignIndex).toLowerCase()
72−
73− // Ignore the hash if it references lines in a file or doesn’t start
74− // with a heading prefix.
75− if (
76− (config.lines && lineExpression.test(hash)) ||
77− hash.slice(0, config.headingPrefix.length) !== config.headingPrefix
78− ) {
79− numberSignIndex = -1
80− }
81− // Use the hash if it starts with a heading prefix.
82− else {
83− filePath = fp + numberSign + hash.slice(config.headingPrefix.length)
84− }
85− }
63+ if (info) {
64+ fp = info.filePath
65+ hash = info.hash
66+ refs = references[fp] || (references[fp] = {})
8667
87− refs = references[fp] || (references[fp] = [])
88− refs.push(node)
68+ add('', node)
8969
9070 // With a heading
91− if (numberSignIndex !== -1) {
92− // Add with heading.
71+ if (hash) {
9372 if (fileSet || fp === absolute) {
94− refs = references[filePath] || (references[filePath] = [])
95− refs.push(node)
73+ add(hash, node)
9674 }
9775
98− if (
99− fileSet &&
100− alreadyAdded.indexOf(fp) === -1 &&
101− numberSignIndex !== 0
102− ) {
76+ if (fileSet && fp && alreadyAdded.indexOf(fp) === -1) {
10377 alreadyAdded.push(fp)
10478 fileSet.add(vfile({cwd: file.cwd, path: path.relative(file.cwd, fp)}))
10579 }
10680 }
10781 }
82+
83+ function add(hash, node) {
84+ if (refs[hash]) {
85+ refs[hash].push(node)
86+ } else {
87+ refs[hash] = [node]
88+ }
89+ }
10890 }
10991}
11092
@@ -151,7 +133,7 @@ function urlToPath(value, config) {
151133 .slice(1)
152134 .join(slash)
153135
154− return path.resolve(config.root, value + url.hash)
136+ return normalize(path.resolve(config.root, value + url.hash), config)
155137 }
156138
157139 // Remove the search: `?foo=bar`.
@@ -170,9 +152,42 @@ function urlToPath(value, config) {
170152
171153 // Local: `#heading`.
172154 if (value.charAt(0) === numberSign) {
173− return config.path ? config.path + value : value
155+ value = config.path ? config.path + value : value
174156 }
175−
176157 // Anything else, such as `readme.md`.
177− return config.path ? path.resolve(config.base, value) : ''
158+ else {
159+ value = config.path ? path.resolve(config.base, value) : ''
160+ }
161+
162+ return normalize(value, config)
163+}
164+
165+function normalize(url, config) {
166+ var numberSignIndex = url.indexOf(numberSign)
167+ var lines = config.lines
168+ var prefix = config.headingPrefix
169+ var filePath
170+ var hash
171+
172+ if (numberSignIndex === -1) {
173+ filePath = url
174+ } else {
175+ filePath = url.slice(0, numberSignIndex)
176+ hash = url.slice(numberSignIndex).toLowerCase()
177+
178+ // Ignore the hash if it references lines in a file or doesn’t start
179+ // with a heading prefix.
180+ if (
181+ (lines && lineExpression.test(hash)) ||
182+ hash.slice(0, prefix.length) !== prefix
183+ ) {
184+ hash = undefined
185+ }
186+ // Use the hash if it starts with a heading prefix.
187+ else {
188+ hash = hash.slice(prefix.length)
189+ }
190+ }
191+
192+ return {filePath: decodeURIComponent(filePath), hash: hash}
178193}