diff --git a/lib/markdown/link.js b/lib/markdown/link.js index 99ca1aefd9..bbe9550d63 100644 --- a/lib/markdown/link.js +++ b/lib/markdown/link.js @@ -2,6 +2,8 @@ // 1. adding target="_blank" to external links // 2. converting internal links to +const indexRE = /(.*)(index|readme).md(#?.*)$/i + module.exports = (md, externalAttrs) => { let hasOpenRouterLink = false let hasOpenExternalLink = false @@ -37,15 +39,28 @@ module.exports = (md, externalAttrs) => { const links = md.__data.links || (md.__data.links = []) links.push(to) - to = to - .replace(/\.md$/, '.html') - .replace(/\.md(#.*)$/, '.html$1') - // normalize links to README/index - if (/^index|readme\.html/i.test(to)) { - to = '/' + const indexMatch = to.match(indexRE) + if (indexMatch) { + const [, path, , hash] = indexMatch + to = path + hash + } else { + to = to + .replace(/\.md$/, '.html') + .replace(/\.md(#.*)$/, '.html$1') + } + + // relative path usage. + if (!to.startsWith('/')) { + to = ensureBeginningDotSlash(to) } + // markdown-it encodes the uri link[1] = decodeURI(to) + + // export the router links for testing + const routerLinks = md.__data.routerLinks || (md.__data.routerLinks = []) + routerLinks.push(to) + return Object.assign({}, token, { tag: 'router-link' }) @@ -65,3 +80,12 @@ module.exports = (md, externalAttrs) => { return self.renderToken(tokens, idx, options) } } + +const beginningSlashRE = /^\.\// + +function ensureBeginningDotSlash (path) { + if (beginningSlashRE.test(path)) { + return path + } + return './' + path +} diff --git a/test/markdown/__snapshots__/link.spec.js.snap b/test/markdown/__snapshots__/link.spec.js.snap new file mode 100644 index 0000000000..930f5e3575 --- /dev/null +++ b/test/markdown/__snapshots__/link.spec.js.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`link should render external links correctly 1`] = ` +

+ vue + + +

+`; + +exports[`link should render external links correctly 2`] = ` +

+ vue + + +

+`; + +exports[`link should render external links correctly 3`] = ` +

+ some link with code + + +

+`; diff --git a/test/markdown/link.spec.js b/test/markdown/link.spec.js new file mode 100644 index 0000000000..620c9fa488 --- /dev/null +++ b/test/markdown/link.spec.js @@ -0,0 +1,77 @@ +import { Md } from './util' +import link from '@/markdown/link.js' +import { dataReturnable } from '@/markdown/index.js' + +const mdL = Md().use(link, { + target: '_blank', + rel: 'noopener noreferrer' +}) + +dataReturnable(mdL) + +const internalLinkAsserts = { + // START abosolute path usage + '/': '/', + + '/foo/': '/foo/', + '/foo/#hash': '/foo/#hash', + + '/foo/two.md': '/foo/two.html', + '/foo/two.html': '/foo/two.html', + // END abosolute path usage + + // START relative path usage + 'README.md': './', + './README.md': './', + + 'index.md': './', + './index.md': './', + + 'one.md': './one.html', + './one.md': './one.html', + + 'foo/README.md': './foo/', + './foo/README.md': './foo/', + + 'foo/README.md#hash': './foo/#hash', + './foo/README.md#hash': './foo/#hash', + + '../README.md': './../', + '../README.md#hash': './../#hash', + + '../foo.md': './../foo.html', + '../foo.md#hash': './../foo.html#hash', + + '../foo/one.md': './../foo/one.html', + '../foo/one.md#hash': './../foo/one.html#hash' + // END relative path usage +} + +const externalLinks = [ + '[vue](https://vuejs.org/)', + '[vue](http://vuejs.org/)', + '[some **link** with `code`](https://google.com)' // #496 +] + +describe('link', () => { + test('should convert internal links to router links correctly', () => { + for (const before in internalLinkAsserts) { + const input = `[${before}](${before})` + const output = mdL.render(input) + const after = getCompiledLink(output) + expect(after).toBe(internalLinkAsserts[before]) + } + }) + + test('should render external links correctly', () => { + for (const link of externalLinks) { + const { html } = mdL.render(link) + expect(html).toMatchSnapshot() + } + }) +}) + +function getCompiledLink (output) { + const { data: { routerLinks }} = output + return routerLinks[0] +}