2022-11-03 02:24:05 +01:00
|
|
|
const { SourceMapGenerator } = require('source-map')
|
|
|
|
const path = require('path')
|
|
|
|
const { createHash } = require('crypto')
|
|
|
|
|
|
|
|
const markdownExtensions = [
|
|
|
|
'md',
|
|
|
|
'markdown',
|
|
|
|
'mdown',
|
|
|
|
'mkdn',
|
|
|
|
'mkd',
|
|
|
|
'mdwn',
|
|
|
|
'mkdown',
|
|
|
|
'ron',
|
|
|
|
]
|
|
|
|
const mdx = ['.mdx']
|
|
|
|
const md = markdownExtensions.map((/** @type {string} */ d) => '.' + d)
|
|
|
|
|
|
|
|
const own = {}.hasOwnProperty
|
|
|
|
|
|
|
|
const marker = {}
|
|
|
|
const cache = new WeakMap()
|
|
|
|
|
2024-04-22 23:51:47 +02:00
|
|
|
/*
|
|
|
|
* From next.config.js's mdxRs option, construct an actual option object that mdxRs compiler accepts.
|
|
|
|
*/
|
|
|
|
function coereceMdxTransformOptions(options = {}) {
|
|
|
|
const { mdxType, ...restOptions } = options
|
|
|
|
|
|
|
|
let parse = undefined
|
|
|
|
switch (mdxType) {
|
|
|
|
case 'gfm':
|
|
|
|
parse = {
|
|
|
|
constructs: {
|
|
|
|
gfmAutolinkLiteral: true,
|
|
|
|
gfmFootnoteDefinition: true,
|
|
|
|
gfmLabelStartFootnote: true,
|
|
|
|
gfmStrikethrough: true,
|
|
|
|
gfmTable: true,
|
|
|
|
gfmTaskListItem: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
break
|
|
|
|
case 'commonMark':
|
|
|
|
default:
|
|
|
|
parse = { gfmStrikethroughSingleTilde: true, mathTextSingleDollar: true }
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
...restOptions,
|
|
|
|
parse,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-03 02:24:05 +01:00
|
|
|
/**
|
|
|
|
* A webpack loader for mdx-rs. This is largely based on existing @mdx-js/loader,
|
|
|
|
* replaces internal compilation logic to use mdx-rs instead.
|
|
|
|
*/
|
|
|
|
function loader(value, bindings, callback) {
|
|
|
|
const defaults = this.sourceMap ? { SourceMapGenerator } : {}
|
|
|
|
const options = this.getOptions()
|
|
|
|
const config = { ...defaults, ...options }
|
|
|
|
const hash = getOptionsHash(options)
|
|
|
|
const compiler = this._compiler || marker
|
|
|
|
|
|
|
|
let map = cache.get(compiler)
|
|
|
|
|
|
|
|
if (!map) {
|
|
|
|
map = new Map()
|
|
|
|
cache.set(compiler, map)
|
|
|
|
}
|
|
|
|
|
|
|
|
let process = map.get(hash)
|
|
|
|
|
|
|
|
if (!process) {
|
2024-04-22 23:51:47 +02:00
|
|
|
process = createFormatAwareProcessors(
|
|
|
|
bindings,
|
|
|
|
coereceMdxTransformOptions(config)
|
|
|
|
).compile
|
2022-11-03 02:24:05 +01:00
|
|
|
map.set(hash, process)
|
|
|
|
}
|
|
|
|
|
|
|
|
process({ value, path: this.resourcePath }).then(
|
|
|
|
(code) => {
|
|
|
|
// TODO: no sourcemap
|
|
|
|
callback(null, code, null)
|
|
|
|
},
|
|
|
|
(error) => {
|
|
|
|
const fpath = path.relative(this.context, this.resourcePath)
|
|
|
|
error.message = `${fpath}:${error.name}: ${error.message}`
|
|
|
|
callback(error)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
function getOptionsHash(options) {
|
|
|
|
const hash = createHash('sha256')
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
function createFormatAwareProcessors(bindings, compileOptions = {}) {
|
|
|
|
const mdExtensions = compileOptions.mdExtensions || md
|
|
|
|
const mdxExtensions = compileOptions.mdxExtensions || mdx
|
|
|
|
|
|
|
|
let cachedMarkdown
|
|
|
|
let cachedMdx
|
|
|
|
|
|
|
|
return {
|
|
|
|
extnames:
|
|
|
|
compileOptions.format === 'md'
|
|
|
|
? mdExtensions
|
|
|
|
: compileOptions.format === 'mdx'
|
|
|
|
? mdxExtensions
|
|
|
|
: mdExtensions.concat(mdxExtensions),
|
|
|
|
compile,
|
|
|
|
}
|
|
|
|
|
|
|
|
function compile({ value, path: p }) {
|
|
|
|
const format =
|
|
|
|
compileOptions.format === 'md' || compileOptions.format === 'mdx'
|
|
|
|
? compileOptions.format
|
|
|
|
: path.extname(p) &&
|
|
|
|
(compileOptions.mdExtensions || md).includes(path.extname(p))
|
|
|
|
? 'md'
|
|
|
|
: 'mdx'
|
|
|
|
|
|
|
|
const options = {
|
|
|
|
parse: compileOptions.parse,
|
|
|
|
development: compileOptions.development,
|
|
|
|
providerImportSource: compileOptions.providerImportSource,
|
|
|
|
jsx: compileOptions.jsx,
|
|
|
|
jsxRuntime: compileOptions.jsxRuntime,
|
|
|
|
jsxImportSource: compileOptions.jsxImportSource,
|
|
|
|
pragma: compileOptions.pragma,
|
|
|
|
pragmaFrag: compileOptions.pragmaFrag,
|
|
|
|
pragmaImportSource: compileOptions.pragmaImportSource,
|
|
|
|
filepath: p,
|
|
|
|
}
|
|
|
|
|
|
|
|
const compileMdx = (input) => bindings.mdx.compile(input, options)
|
|
|
|
|
|
|
|
const processor =
|
|
|
|
format === 'md'
|
|
|
|
? cachedMarkdown || (cachedMarkdown = compileMdx)
|
|
|
|
: cachedMdx || (cachedMdx = compileMdx)
|
|
|
|
|
|
|
|
return processor(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = function (code) {
|
|
|
|
const callback = this.async()
|
|
|
|
const { loadBindings } = require('next/dist/build/swc')
|
|
|
|
|
|
|
|
loadBindings().then((bindings) => {
|
|
|
|
return loader.call(this, code, bindings, callback)
|
|
|
|
})
|
|
|
|
}
|