aboutsummaryrefslogtreecommitdiff
path: root/node_modules/vuepress/lib/webpack/markdownLoader.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/vuepress/lib/webpack/markdownLoader.js')
-rw-r--r--node_modules/vuepress/lib/webpack/markdownLoader.js101
1 files changed, 101 insertions, 0 deletions
diff --git a/node_modules/vuepress/lib/webpack/markdownLoader.js b/node_modules/vuepress/lib/webpack/markdownLoader.js
new file mode 100644
index 00000000..3c49b747
--- /dev/null
+++ b/node_modules/vuepress/lib/webpack/markdownLoader.js
@@ -0,0 +1,101 @@
+const fs = require('fs')
+const path = require('path')
+const hash = require('hash-sum')
+const { EventEmitter } = require('events')
+const { getOptions } = require('loader-utils')
+const { inferTitle, extractHeaders, parseFrontmatter } = require('../util')
+const LRU = require('lru-cache')
+
+const cache = LRU({ max: 1000 })
+const devCache = LRU({ max: 1000 })
+
+module.exports = function (src) {
+ const isProd = process.env.NODE_ENV === 'production'
+ const isServer = this.target === 'node'
+ const { markdown, sourceDir } = getOptions(this)
+
+ // 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 && (isProd || /\?vue/.test(this.resourceQuery))) {
+ return cached
+ }
+
+ const frontmatter = parseFrontmatter(src)
+ const content = frontmatter.content
+
+ if (!isProd && !isServer) {
+ const inferredTitle = inferTitle(frontmatter)
+ const headers = extractHeaders(content, ['h2', 'h3'], markdown)
+ delete frontmatter.content
+
+ // 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 = devCache.get(file)
+ if (cachedData && (
+ cachedData.inferredTitle !== inferredTitle ||
+ JSON.stringify(cachedData.frontmatter) !== JSON.stringify(frontmatter) ||
+ headersChanged(cachedData.headers, headers)
+ )) {
+ // frontmatter changed... need to do a full reload
+ module.exports.frontmatterEmitter.emit('update')
+ }
+
+ devCache.set(file, {
+ headers,
+ frontmatter,
+ inferredTitle
+ })
+ }
+
+ // 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 => {
+ link = decodeURIComponent(link)
+ const shortname = link
+ .replace(/#.*$/, '')
+ .replace(/\.html$/, '.md')
+ const filename = shortname
+ .replace(/\/$/, '/README.md')
+ .replace(/^\//, sourceDir + '/')
+ const altname = shortname
+ .replace(/\/$/, '/index.md')
+ .replace(/^\//, sourceDir + '/')
+ const dir = path.dirname(this.resourcePath)
+ const file = path.resolve(dir, filename)
+ const altfile = altname !== filename ? path.resolve(dir, altname) : null
+ if (!fs.existsSync(file) && (!altfile || !fs.existsSync(altfile))) {
+ 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) {
+ if (a.length !== b.length) return true
+ return a.some((h, i) => (
+ h.title !== b[i].title ||
+ h.level !== b[i].level
+ ))
+}
+
+module.exports.frontmatterEmitter = new EventEmitter()