forked from hashicorp/next-mdx-enhanced
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathloader.js
140 lines (125 loc) · 4.54 KB
/
loader.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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
const path = require('path')
const matter = require('gray-matter')
const glob = require('glob')
const stringifyObject = require('stringify-object')
const { getOptions } = require('loader-utils')
const { extendFrontMatter, normalizeToUnixPath } = require('./util')
// Loads markdown files with front matter and renders them into a layout.
// Layout can be set using the `layout` key in the front matter, and will map
// to a file name in the pages/layouts directory.
module.exports = async function mdxEnhancedLoader(src) {
const callback = this.async()
const options = getOptions(this)
// Parse the front matter
let content, data
try {
const res = matter(src, { safeLoad: true, filename: this.resourcePath })
content = res.content
data = res.data
} catch (err) {
callback(err)
}
// Scan for plugin `scan` option to return results based on RegEx patterns provided in config
const scans = scanContent(options, content)
// Get file path relative to project root
const resourcePath = normalizeToUnixPath(this.resourcePath)
.replace(
normalizeToUnixPath(
path.join(normalizeToUnixPath(this.rootContext), 'pages')
),
''
)
.substring(1)
// Checks if there's a layout, if there is, resolve the layout and wrap the content in it.
processLayout
.call(this, options, data, content, resourcePath, scans)
.then((result) => callback(null, result))
.catch((err) => callback(err))
}
function scanContent(options, content) {
const { mdxEnhancedPluginOptions: pluginOpts } = options
if (!pluginOpts.scan) return {}
return Object.keys(pluginOpts.scan).reduce((acc, opt) => {
// Put the result of the pattern match onto the `scans` object: `{ key : result }`
if (content.match(pluginOpts.scan[opt].pattern)) {
acc[opt] =
// Check to see if a `transform` function & it is a function
pluginOpts.scan[opt].transform &&
typeof pluginOpts.scan[opt].transform === 'function'
? pluginOpts.scan[opt].transform(
content.match(pluginOpts.scan[opt].pattern)
)
: content.match(opt.pattern) // Otherwise pass the raw Array of matches as the result for this key. More info here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match#Return_value
}
return acc
}, {})
}
async function processLayout(
options,
frontMatter,
content,
resourcePath,
scans
) {
const { mdxEnhancedPluginOptions: pluginOpts } = options
const extendedFm = await extendFrontMatter({
content,
frontMatter: {
...frontMatter,
__resourcePath: resourcePath,
__scans: scans,
},
phase: 'loader',
extendFm: pluginOpts.extendFrontMatter,
})
const mergedFrontMatter = {
...frontMatter,
...extendedFm,
__resourcePath: resourcePath,
__scans: scans,
}
// If no layout is provided and the default layout setting is not on, return the
// content directly.
if (!mergedFrontMatter.layout && !pluginOpts.defaultLayout) return content
// Set the default if the option is active and there's no layout
if (!mergedFrontMatter.layout && pluginOpts.defaultLayout) {
mergedFrontMatter.layout = 'index'
}
// Layouts default to resolving from "<root>/layouts", but this is configurable.
// If the frontMatter doesn't have a layout and defaultLayout is true, try to
// resolve the index file within the layouts path.
const layoutPath = path.resolve(
options.dir,
pluginOpts.layoutPath,
mergedFrontMatter.layout
)
// If the layout doesn't exist, throw a descriptive error
// We use glob to check for existence, since the file could have multiple page
// extensions depending on the config
const layoutMatcher = `${layoutPath}.+(${options.config.pageExtensions.join(
'|'
)})`
const matches = await new Promise((resolve, reject) => {
glob(layoutMatcher, (err, matches) =>
err ? reject(err) : resolve(matches)
)
})
if (!matches.length) {
throw new Error(
`File "${resourcePath}" specified "${mergedFrontMatter.layout}" as its layout, but no matching file was found at "${layoutMatcher}"`
)
}
const { onContent } = pluginOpts
if (onContent && this._compiler.name === 'server') {
onContent({
...mergedFrontMatter,
content,
})
}
// Import the layout, export the layout-wrapped content, pass front matter into layout
return `import layout from '${normalizeToUnixPath(layoutPath)}'
export * from '${normalizeToUnixPath(layoutPath)}'
export default layout(${stringifyObject(mergedFrontMatter)})
${content}
`
}