-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
99 lines (88 loc) · 3.55 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
"use strict"
const fs = require('fs')
const path = require('path')
const child_process = require('child_process')
module.exports = {
hooks: {
'page:before': function (page) {
const options = Object.assign({problem: 'error'}, this.options.pluginsConfig["snippet"])
const problem = (filename, message) => {
const fullMessage = `${filename}: ${message}\n`
switch (options.problem) {
case 'warn': {
this.log.warn(fullMessage)
break
}
case 'error': {
this.log.error(fullMessage)
break
}
case 'fail': {
throw new Error(fullMessage)
}
}
return message
}
const dir = path.dirname(page.rawPath)
const sourceByFilepath = {}
const readFilePromises = []
const readFile = (filepath) => {
return new Promise((resolve) => {
fs.readFile(filepath, "utf-8", (err, source) => {
if (err) return resolve() // We'll error later, when the file is not found
sourceByFilepath[filepath] = source
resolve()
})
})
}
const gitCat = (commitMessage, filepath) => {
return new Promise((resolve) => {
child_process.exec(`${__dirname}/bin/gitcat "${commitMessage}" "${filepath}"`, (err, source) => {
if (err) return resolve() // We'll error later, when the file is not found
sourceByFilepath[filepath] = source
resolve()
})
})
}
const snippetRegexp = /^\s*\[(?:un)?snippet\]\(([^#\)]+)#?([^\)@]+)?@?([^\)]+)?\)\s*$/gm
// find all [snippet]() statements,
// read and cache target files
let snippetMatch
while (snippetMatch = snippetRegexp.exec(page.content)) {
const filename = snippetMatch[1]
const commitMessage = snippetMatch[3]
const filepath = path.join(dir, filename)
const promise = commitMessage ? gitCat(commitMessage, filepath) : readFile(filepath)
readFilePromises.push(promise)
}
// once all files are read, replace snippet statements with
// appropriate file content
return Promise.all(readFilePromises)
.then(() => {
page.content = page.content.replace(snippetRegexp, (snippetLink, filename, fragment) => {
const shouldUnindent = snippetLink.startsWith('[un')
const filepath = path.join(dir, filename)
const source = sourceByFilepath[filepath]
if (!source) return problem(filename, `${snippetLink} *FILE NOT FOUND: ${filename}*`)
if (!fragment) return removeMarkers(source)
// find the content between two anchors, which can be "### [fragment-nane]" or "/// [fragment-nane]"
const fragmentRegexp = new RegExp(`[\\s\\S]*(?:###|\\/\\/\\/)\\s*\\[${fragment}\\][^\\n]*\\n([\\s\\S]*)(?:\\n[^\\n]*###|\\/\\/\\/)\\s*\\[${fragment}\\]`)
const fragmentMatch = source.match(fragmentRegexp)
if (!fragmentMatch) return problem(filename, `${snippetLink} *FRAGMENT NOT FOUND: ${filename}#${fragment}*`)
return removeMarkers(shouldUnindent? unindent(fragmentMatch[1]) : fragmentMatch[1] )
})
return page
})
}
}
}
const unindent = s => {
// https://twitter.com/gasparnagy/status/778134306128551940
for (; !s.match(/^\S/m); s = s.replace(/^\s/gm, ''));
return s
}
const removeMarkers = s => {
const markerRegexp = new RegExp(`(?:\\n[^\\n]*###|\\/\\/\\/)\\s*\\[[^\\]]*\\]`, 'g' );
s = s.replace( markerRegexp, '' );
return s;
}