Skip to content

Commit

Permalink
feat: auto detect invalid inbound links
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Apr 13, 2018
1 parent f8807b4 commit ca82906
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 23 deletions.
6 changes: 3 additions & 3 deletions docs/guide/markdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ meta:

## Header Anchors

Headers automatically get anchor links applied. Rendering of anchors can be configured using the [`markdown.anchor`](./config.md#markdownanchor) option.
Headers automatically get anchor links applied. Rendering of anchors can be configured using the [`markdown.anchor`](../config/#markdownanchor) option.

## Links

- Inbound links ending in `.md` or `.html` are converted to `<router-link>` for SPA navigation.

- [Home](/)
- [Configuring Markdown](./config.md#markdown)
- [Configuring Markdown](../config/#markdown)

- Outbound links automatically gets `target="_blank"`:

Expand Down Expand Up @@ -93,7 +93,7 @@ meta:

[[toc]]

Rendering of TOC can be configured using the [`markdown.toc`](./config.md#markdowntoc) option.
Rendering of TOC can be configured using the [`markdown.toc`](../config/#markdowntoc) option.

## Custom Containers

Expand Down
2 changes: 1 addition & 1 deletion docs/guide/using-vue.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Directives also work:

### Access to Site & Page Data

The compiled component does not have any private data but do have access to the [site metadata](./theming.md#site-and-page-metadata). For example:
The compiled component does not have any private data but do have access to the [site metadata](./custom-themes.md#site-and-page-metadata). For example:

**Input**

Expand Down
2 changes: 1 addition & 1 deletion lib/dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ module.exports = async function dev (sourceDir, cliOptions = {}) {
await serve({
compiler,
host: process.env.DEBUG ? '0.0.0.0' : 'localhost',
dev: { logLevel: 'error' },
dev: { logLevel: 'warn' },
hot: { logLevel: 'error' },
logLevel: 'error',
port,
Expand Down
13 changes: 2 additions & 11 deletions lib/markdown/hoist.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
module.exports = md => {
const RE = /^<(script|style)(?=(\s|>|$))/i
let hoistedTags

md.renderer.rules.html_block = (tokens, idx) => {
const content = tokens[idx].content
if (hoistedTags && RE.test(content.trim())) {
const hoistedTags = md.__data.hoistedTags || (md.__data.hoistedTags = [])
if (RE.test(content.trim())) {
hoistedTags.push(content)
return ''
} else {
return content
}
}

md.renderWithHoisting = (...args) => {
hoistedTags = []
const html = md.render(...args)
return {
html,
hoistedTags
}
}
}
11 changes: 11 additions & 0 deletions lib/markdown/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ module.exports = ({ markdown = {}}) => {
includeLevel: [2, 3]
}, markdown.toc))

// override render to allow custom plugins return data
const render = md.render
md.render = (...args) => {
md.__data = {}
const html = render.call(md, ...args)
return {
html,
data: md.__data
}
}

// apply user config
if (markdown.config) {
markdown.config(md)
Expand Down
8 changes: 7 additions & 1 deletion lib/markdown/link.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,14 @@ module.exports = md => {
function toRouterLink (token, link) {
link[0] = 'to'
let to = link[1]

// convert link to filename and export it for existence check
const links = md.__data.links || (md.__data.links = [])
links.push(to)

to = to
.replace(/\.md$/, '.html')
.replace(/\.md(#[\w-]*)/, '.html$1')
.replace(/\.md(#[\w-]*)$/, '.html$1')
// normalize links to README/index
if (/^index|readme\.html/i.test(to)) {
to = '/'
Expand Down
5 changes: 4 additions & 1 deletion lib/webpack/createBaseConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,10 @@ module.exports = function createBaseConfig ({
.end()
.use('markdown-loader')
.loader(require.resolve('./markdownLoader'))
.options({ markdown })
.options({
sourceDir,
markdown
})

config.module
.rule('images')
Expand Down
47 changes: 42 additions & 5 deletions lib/webpack/markdownLoader.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
const fs = require('fs')
const path = require('path')
const hash = require('hash-sum')
const { EventEmitter } = require('events')
const { getOptions } = require('loader-utils')
const yaml = require('yaml-front-matter')
const { inferTitle, extractHeaders } = require('../util')

const cache = new Map()
const devCache = new Map()

module.exports = function (src) {
// we implement a manual cache here because this loader is chained before
// vue-loader, and will be applied on the same file multiple times when
// selecting the individual blocks.
const file = this.resourcePath
const key = hash(file + src)
const cached = cache.get(key)
if (cached) {
return cached
}

const isProd = process.env.NODE_ENV === 'production'
const isServer = this.target === 'node'
const { markdown } = getOptions(this)
const { markdown, sourceDir } = getOptions(this)

const frontmatter = yaml.loadFront(src)
const content = frontmatter.__content
Expand All @@ -20,7 +34,7 @@ module.exports = function (src) {

// diff frontmatter and title, since they are not going to be part of the
// returned component, changes in frontmatter do not trigger proper updates
const cachedData = cache.get(this.resourcePath)
const cachedData = devCache.get(file)
if (cachedData && (
cachedData.inferredTitle !== inferredTitle ||
JSON.stringify(cachedData.frontmatter) !== JSON.stringify(frontmatter) ||
Expand All @@ -30,20 +44,43 @@ module.exports = function (src) {
module.exports.frontmatterEmitter.emit('update')
}

cache.set(this.resourcePath, {
devCache.set(file, {
headers,
frontmatter,
inferredTitle
})
}

const { html, hoistedTags } = markdown.renderWithHoisting(content)
return (
// the render method has been augmented to allow plugins to
// register data during render
const { html, data: { hoistedTags, links }} = markdown.render(content)

// check if relative links are valid
links && links.forEach(link => {
const filename = link
.replace(/#[\w-]*$/, '')
.replace(/\.html$/, '.md')
.replace(/\/$/, '/README.md')
.replace(/^\//, sourceDir + '/')
const file = path.resolve(path.dirname(this.resourcePath), filename)
if (!fs.existsSync(file)) {
this.emitWarning(
new Error(
`\nFile for relative link "${link}" does not exist.\n` +
`(Resolved file: ${file})\n`
)
)
}
})

const res = (
`<template>\n` +
`<div class="content">${html}</div>\n` +
`</template>\n` +
hoistedTags.join('\n')
)
cache.set(key, res)
return res
}

function headersChanged (a, b) {
Expand Down

0 comments on commit ca82906

Please sign in to comment.