Skip to content

Commit b967b75

Browse files
committed
made file reading async
1 parent 1362a90 commit b967b75

File tree

3 files changed

+104
-77
lines changed

3 files changed

+104
-77
lines changed

lib/prepare.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ async function resolveOptions (sourceDir) {
188188
}
189189

190190
// extract yaml frontmatter
191-
const { explodedSrc: content } = mdExplodeIncludes({
191+
const { explodedSrc: content } = await mdExplodeIncludes({
192192
cwd: path.dirname(path.resolve(sourceDir, file)),
193193
src: await fs.readFile(path.resolve(sourceDir, file), 'utf-8')
194194
})

lib/webpack/markdownLoader.js

+76-71
Original file line numberDiff line numberDiff line change
@@ -10,88 +10,93 @@ const { mdExplodeIncludes } = require('./util')
1010
const cache = LRU({ max: 1000 })
1111
const devCache = LRU({ max: 1000 })
1212

13-
module.exports = function (src) {
14-
const file = this.resourcePath
15-
const dir = path.dirname(file)
13+
module.exports = async function (src) {
14+
const cb = this.async()
15+
try {
16+
const file = this.resourcePath
17+
const dir = path.dirname(file)
1618

17-
const { explodedSrc, dependencies } = mdExplodeIncludes({ dir, src })
18-
dependencies.forEach(d => this.addDependency(d))
19+
const { explodedSrc, dependencies } = await mdExplodeIncludes({ cwd: dir, src })
20+
dependencies.forEach(d => this.addDependency(d))
1921

20-
const isProd = process.env.NODE_ENV === 'production'
21-
const isServer = this.target === 'node'
22-
const { markdown, sourceDir } = getOptions(this)
22+
const isProd = process.env.NODE_ENV === 'production'
23+
const isServer = this.target === 'node'
24+
const { markdown, sourceDir } = getOptions(this)
2325

24-
// we implement a manual cache here because this loader is chained before
25-
// vue-loader, and will be applied on the same file multiple times when
26-
// selecting the individual blocks.
27-
const key = hash(file + explodedSrc)
28-
const cached = cache.get(key)
29-
if (cached && (isProd || /\?vue/.test(this.resourceQuery))) {
30-
return cached
31-
}
26+
// we implement a manual cache here because this loader is chained before
27+
// vue-loader, and will be applied on the same file multiple times when
28+
// selecting the individual blocks.
29+
const key = hash(file + explodedSrc)
30+
const cached = cache.get(key)
31+
if (cached && (isProd || /\?vue/.test(this.resourceQuery))) {
32+
return cb(null, cached)
33+
}
3234

33-
const frontmatter = parseFrontmatter(explodedSrc)
34-
const content = frontmatter.content
35+
const frontmatter = parseFrontmatter(explodedSrc)
36+
const content = frontmatter.content
3537

36-
if (!isProd && !isServer) {
37-
const inferredTitle = inferTitle(frontmatter)
38-
const headers = extractHeaders(content, ['h2', 'h3'], markdown)
39-
delete frontmatter.content
38+
if (!isProd && !isServer) {
39+
const inferredTitle = inferTitle(frontmatter)
40+
const headers = extractHeaders(content, ['h2', 'h3'], markdown)
41+
delete frontmatter.content
4042

41-
// diff frontmatter and title, since they are not going to be part of the
42-
// returned component, changes in frontmatter do not trigger proper updates
43-
const cachedData = devCache.get(file)
44-
if (cachedData && (
45-
cachedData.inferredTitle !== inferredTitle ||
46-
JSON.stringify(cachedData.frontmatter) !== JSON.stringify(frontmatter) ||
47-
headersChanged(cachedData.headers, headers)
48-
)) {
49-
// frontmatter changed... need to do a full reload
50-
module.exports.frontmatterEmitter.emit('update')
51-
}
43+
// diff frontmatter and title, since they are not going to be part of the
44+
// returned component, changes in frontmatter do not trigger proper updates
45+
const cachedData = devCache.get(file)
46+
if (cachedData && (
47+
cachedData.inferredTitle !== inferredTitle ||
48+
JSON.stringify(cachedData.frontmatter) !== JSON.stringify(frontmatter) ||
49+
headersChanged(cachedData.headers, headers)
50+
)) {
51+
// frontmatter changed... need to do a full reload
52+
module.exports.frontmatterEmitter.emit('update')
53+
}
5254

53-
devCache.set(file, {
54-
headers,
55-
frontmatter,
56-
inferredTitle
57-
})
58-
}
55+
devCache.set(file, {
56+
headers,
57+
frontmatter,
58+
inferredTitle
59+
})
60+
}
5961

60-
// the render method has been augmented to allow plugins to
61-
// register data during render
62-
const { html, data: { hoistedTags, links }} = markdown.render(content)
62+
// the render method has been augmented to allow plugins to
63+
// register data during render
64+
const { html, data: { hoistedTags, links }} = markdown.render(content)
6365

64-
// check if relative links are valid
65-
links && links.forEach(link => {
66-
const shortname = link
67-
.replace(/#.*$/, '')
68-
.replace(/\.html$/, '.md')
69-
const filename = shortname
70-
.replace(/\/$/, '/README.md')
71-
.replace(/^\//, sourceDir + '/')
72-
const altname = shortname
73-
.replace(/\/$/, '/index.md')
74-
.replace(/^\//, sourceDir + '/')
75-
const file = path.resolve(dir, filename)
76-
const altfile = altname !== filename ? path.resolve(dir, altname) : null
77-
if (!fs.existsSync(file) && (!altfile || !fs.existsSync(altfile))) {
78-
this.emitWarning(
79-
new Error(
80-
`\nFile for relative link "${link}" does not exist.\n` +
81-
`(Resolved file: ${file})\n`
66+
// check if relative links are valid
67+
links && links.forEach(link => {
68+
const shortname = link
69+
.replace(/#.*$/, '')
70+
.replace(/\.html$/, '.md')
71+
const filename = shortname
72+
.replace(/\/$/, '/README.md')
73+
.replace(/^\//, sourceDir + '/')
74+
const altname = shortname
75+
.replace(/\/$/, '/index.md')
76+
.replace(/^\//, sourceDir + '/')
77+
const file = path.resolve(dir, filename)
78+
const altfile = altname !== filename ? path.resolve(dir, altname) : null
79+
if (!fs.existsSync(file) && (!altfile || !fs.existsSync(altfile))) {
80+
this.emitWarning(
81+
new Error(
82+
`\nFile for relative link "${link}" does not exist.\n` +
83+
`(Resolved file: ${file})\n`
84+
)
8285
)
83-
)
84-
}
85-
})
86+
}
87+
})
8688

87-
const res = (
88-
`<template>\n` +
89-
`<div class="content">${html}</div>\n` +
90-
`</template>\n` +
91-
(hoistedTags || []).join('\n')
92-
)
93-
cache.set(key, res)
94-
return res
89+
const res = (
90+
`<template>\n` +
91+
`<div class="content">${html}</div>\n` +
92+
`</template>\n` +
93+
(hoistedTags || []).join('\n')
94+
)
95+
cache.set(key, res)
96+
return cb(null, res)
97+
} catch (e) {
98+
return cb(e)
99+
}
95100
}
96101

97102
function headersChanged (a, b) {

lib/webpack/util.js

+27-5
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,39 @@
1-
const { readFileSync } = require('fs')
1+
const { promisify } = require('util')
2+
const fs = require('fs')
3+
const readFile = promisify(fs.readFile)
24
const { resolve, dirname } = require('path')
35

4-
const mdExplodeIncludes = exports.mdExplodeIncludes = ({ cwd, src }) => {
6+
const asyncReplace = async (str, regex, aReplacer) => {
7+
regex = new RegExp(regex, 'g')
8+
const replacedParts = []
9+
let match
10+
let i = 0
11+
while ((match = regex.exec(str)) !== null) {
12+
// put non matching string
13+
replacedParts.push(str.slice(i, match.index))
14+
// call the async replacer function with the matched array spreaded
15+
replacedParts.push(aReplacer(...match))
16+
i = regex.lastIndex
17+
}
18+
19+
// put the rest of str
20+
replacedParts.push(str.slice(i))
21+
22+
// wait for aReplacer calls to finish and join them back into string
23+
return (await Promise.all(replacedParts)).join('')
24+
}
25+
26+
const mdExplodeIncludes = exports.mdExplodeIncludes = async ({ cwd, src }) => {
527
const deps = []
628

729
return {
8-
explodedSrc: src.replace(/<!--\s*include\s+([^\s]+)\s*-->/g, (match, path) => {
30+
explodedSrc: await asyncReplace(src, /<!--\s*include\s+([^\s]+)\s*-->/g, async (match, path) => {
931
try {
1032
const absolutePath = resolve(cwd, path)
11-
const content = readFileSync(absolutePath, 'utf8')
33+
const content = await readFile(absolutePath, 'utf8')
1234

1335
// recursively explode the included file
14-
const { explodedSrc, dependencies } = mdExplodeIncludes({
36+
const { explodedSrc, dependencies } = await mdExplodeIncludes({
1537
cwd: dirname(absolutePath),
1638
src: content
1739
})

0 commit comments

Comments
 (0)