forked from mdx-js/mdx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
103 lines (89 loc) · 3.03 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
100
101
102
103
/**
* @typedef {import('vfile').VFileCompatible} VFileCompatible
* @typedef {import('vfile').VFile} VFile
* @typedef {import('vfile-message').VFileMessage} VFileMessage
* @typedef {import('@mdx-js/mdx').CompileOptions} CompileOptions
* @typedef {Pick<CompileOptions, 'SourceMapGenerator'>} Defaults
* @typedef {Omit<CompileOptions, 'SourceMapGenerator'>} Options
* @typedef {import('webpack').LoaderContext<unknown>} LoaderContext
* @typedef {import('webpack').Compiler} WebpackCompiler
* @typedef {(vfileCompatible: VFileCompatible) => Promise<VFile>} Process
*/
import {createHash} from 'node:crypto'
import path from 'node:path';
import {SourceMapGenerator} from 'source-map'
import {createFormatAwareProcessors} from '@mdx-js/mdx/lib/util/create-format-aware-processors.js'
const own = {}.hasOwnProperty
// Note: the cache is heavily inspired by:
// <https://github.com/TypeStrong/ts-loader/blob/5c030bf/src/instance-cache.ts>
const marker = /** @type {WebpackCompiler} */ ({})
/** @type {WeakMap<WebpackCompiler, Map<string, Process>>} */
const cache = new WeakMap()
/**
* A Webpack (5+) loader for MDX.
* See `webpack.cjs`, which wraps this, because Webpack loaders must currently
* be CommonJS.
*
* @this {LoaderContext}
* @param {string} value
* @param {(error: Error|null|undefined, content?: string|Buffer, map?: Object) => void} callback
*/
export function loader(value, callback) {
/** @type {Defaults} */
const defaults = this.sourceMap ? {SourceMapGenerator} : {}
const options = /** @type {CompileOptions} */ (this.getOptions())
const config = {...defaults, ...options}
const hash = getOptionsHash(options)
// Some loaders set `undefined` (see `TypeStrong/ts-loader`).
/* c8 ignore next */
const compiler = this._compiler || marker
/* Removed option. */
/* c8 ignore next 5 */
if ('renderer' in config) {
throw new Error(
'`options.renderer` is no longer supported. Please see <https://mdxjs.com/migrating/v2/> for more information'
)
}
let map = cache.get(compiler)
if (!map) {
map = new Map()
cache.set(compiler, map)
}
let process = map.get(hash)
if (!process) {
process = createFormatAwareProcessors(config).process
map.set(hash, process)
}
if(options.execute) {
options.execute.call(this, value, process, callback)
} else {
process({value, path: this.resourcePath}).then(
(file) => {
callback(null, file.value, file.map)
},
(/** @type VFileMessage */ e) => {
const fpath = path.relative(this.context, this.resourcePath);
e.message = `${fpath}:${e.name}: ${e.message}`;
callback(e);
}
)
}
}
/**
* @param {Options} options
*/
function getOptionsHash(options) {
const hash = createHash('sha256')
/** @type {keyof Options} */
let key
for (key in options) {
if (own.call(options, key)) {
const value = options[key]
if (value !== undefined) {
const valueString = JSON.stringify(value)
hash.update(key + valueString)
}
}
}
return hash.digest('hex').slice(0, 16)
}