MDX: Use @mdx-js/mdx to process MDX/markdown files.

This commit is contained in:
SonMooSans
2023-12-09 22:21:17 +08:00
parent c1c0534522
commit 55a2321770
12 changed files with 146 additions and 139 deletions
+9
View File
@@ -0,0 +1,9 @@
---
'next-docs-ui': minor
---
**Use `@mdx-js/mdx` to process MDX/markdown files.**
You no longer need `@next/loader` and `@mdx-js/loader` to be installed on your project, `next-docs-mdx` will process files with `@mdx-js/mdx` directly.
*This change will not break most of the projects*
-1
View File
@@ -7,7 +7,6 @@ import remarkMath from 'remark-math'
const withAnalyzer = createBundleAnalyzer({
enabled: process.env.ANALYZE === 'true'
})
/** @type {import('next').NextConfig} */
const config = {
pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
-3
View File
@@ -10,10 +10,7 @@
"start": "next start"
},
"dependencies": {
"@mdx-js/loader": "^3.0.0",
"@mdx-js/react": "^3.0.0",
"@next/env": "^14.0.0",
"@next/mdx": "^14.0.0",
"algoliasearch": "^4.20.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
+8
View File
@@ -0,0 +1,8 @@
/* eslint-env node */
module.exports = function (code) {
const callback = this.async()
import('./dist/loader-mdx.mjs').then(mod =>
mod.default.call(this, code, callback)
)
}
+12 -9
View File
@@ -12,9 +12,14 @@
],
"exports": {
"./loader": "./loader.js",
"./*": {
"import": "./dist/*.mjs",
"types": "./dist/*.d.mts"
"./loader-mdx": "./loader-mdx.js",
"./config": {
"import": "./dist/config.mjs",
"types": "./dist/config.d.mts"
},
"./map": {
"import": "./dist/map.mjs",
"types": "./dist/map.d.mts"
}
},
"typesVersions": {
@@ -38,23 +43,21 @@
"types:check": "tsc --noEmit"
},
"dependencies": {
"@mdx-js/mdx": "^3.0.0",
"estree-util-value-to-estree": "^3.0.1",
"fast-glob": "^3.3.1",
"remark-frontmatter": "^5.0.0",
"yaml": "^2.3.3",
"gray-matter": "^4.0.3",
"next-docs-zeta": "workspace:*",
"zod": "^3.22.4"
},
"peerDependencies": {
"@next/mdx": ">= 13",
"next-docs-zeta": "5.0.0"
"next": ">= 13.4"
},
"devDependencies": {
"@next/mdx": "14.0.4",
"@types/mdast": "^4.0.3",
"@types/mdx": "^2.0.8",
"@types/react": "18.2.0",
"next": "14.0.4",
"next-docs-zeta": "workspace:*",
"unified": "^11.0.4",
"webpack": "^5.89.0"
},
+36 -30
View File
@@ -1,5 +1,5 @@
import path from 'path'
import createNextMDX, { type NextMDXOptions } from '@next/mdx'
import type { ProcessorOptions } from '@mdx-js/mdx'
import type { NextConfig } from 'next'
import {
rehypeNextDocs,
@@ -8,21 +8,17 @@ import {
remarkToc,
type RehypeNextDocsOptions
} from 'next-docs-zeta/mdx-plugins'
import remarkFrontmatter, {
type Options as RemarkFrontmatterOptions
} from 'remark-frontmatter'
import type { PluggableList } from 'unified'
import type { Configuration } from 'webpack'
import type { LoaderOptions } from './loader'
import remarkMdxExport from './mdx-plugins/remark-exports'
import remarkMdxFrontmatter from './mdx-plugins/remark-frontmatter'
import { NextDocsWebpackPlugin } from './webpack-plugins/next-docs'
type WithMDX = (config: NextConfig) => NextConfig
type Loader = (options: NextMDXOptions) => WithMDX
type Loader = (options: ProcessorOptions) => WithMDX
type MDXOptions = Omit<
NonNullable<NextMDXOptions['options']>,
NonNullable<ProcessorOptions>,
'rehypePlugins' | 'remarkPlugins'
> & {
rehypePlugins?: ResolvePlugins
@@ -81,7 +77,6 @@ function pluginOption(
const createNextDocs =
({
loader = options => createNextMDX(options),
mdxOptions = {},
cwd = process.cwd(),
rootMapPath = './_map.ts',
@@ -98,8 +93,6 @@ const createNextDocs =
const remarkPlugins = pluginOption(
v => [
remarkGfm,
[remarkFrontmatter, 'yaml' satisfies RemarkFrontmatterOptions],
remarkMdxFrontmatter,
remarkStructure,
remarkToc,
...v,
@@ -113,29 +106,42 @@ const createNextDocs =
mdxOptions.rehypePlugins
)
const withMDX = loader({
extension: /\.mdx?$/,
options: {
...mdxOptions,
remarkPlugins,
rehypePlugins
}
})
nextConfig = withMDX(nextConfig)
return Object.assign({}, nextConfig, {
webpack: (config: Configuration, options) => {
config.module!.rules!.push({
test: _mapPath,
use: {
loader: 'next-docs-mdx/loader',
options: {
cwd,
rootContentPath
} satisfies LoaderOptions
const alias = config.resolve!.alias as Record<string, unknown>
alias['next-mdx-import-source-file'] = [
'private-next-root-dir/src/mdx-components',
'private-next-root-dir/mdx-components',
'@mdx-js/react'
]
config.module!.rules!.push(
{
test: /\.mdx?$/,
use: [
options.defaultLoaders.babel,
{
loader: 'next-docs-mdx/loader-mdx',
options: {
providerImportSource: 'next-mdx-import-source-file',
...mdxOptions,
remarkPlugins,
rehypePlugins
}
}
]
},
{
test: _mapPath,
use: {
loader: 'next-docs-mdx/loader',
options: {
cwd,
rootContentPath
} satisfies LoaderOptions
}
}
})
)
config.plugins!.push(
new NextDocsWebpackPlugin({ rootMapFile: _mapPath })
+65
View File
@@ -0,0 +1,65 @@
import path from 'node:path'
import { createProcessor, type ProcessorOptions } from '@mdx-js/mdx'
import type { Processor } from '@mdx-js/mdx/lib/core'
import grayMatter from 'gray-matter'
import type { LoaderContext } from 'webpack'
type Options = ProcessorOptions
export type NextDocsBuildInfo = {
__next_docs?: {
path: string
/**
* `vfile.data` parsed from file
*/
data: unknown
}
}
const cache = new Map<string, Processor>()
export default function loader(
this: LoaderContext<Options>,
source: string,
callback: LoaderContext<Options>['callback']
) {
this.cacheable(true)
const context = this.context
const filePath = this.resourcePath
const options = this.getOptions()
const { content, data } = grayMatter(source)
const config = {
development: this.mode === 'development',
...options
}
const format = config.format ?? filePath.endsWith('.mdx') ? 'mdx' : 'md'
let processor = cache.get(format)
if (!processor) {
processor = createProcessor(config)
cache.set(format, processor)
}
processor.process({ value: content, path: filePath }).then(
file => {
const result = String(file.value)
const info = this._module!.buildInfo as NextDocsBuildInfo
info.__next_docs = {
path: this.resourcePath,
data: file.data
}
const final = `export const frontmatter = ${JSON.stringify(
data
)};\n${result}`
callback(undefined, final, file.map || undefined)
},
error => {
const fpath = path.relative(context, filePath)
error.message = `${fpath}:${error.name}: ${error.message}`
callback(error)
}
)
}
+1 -1
View File
@@ -13,7 +13,7 @@ export type LoaderOptions = {
export default function loader(
this: LoaderContext<LoaderOptions>,
_source: string,
callback: (err?: null | Error, content?: string | Buffer) => void
callback: LoaderContext<LoaderOptions>['callback']
) {
const { cwd, rootContentPath } = this.getOptions()
@@ -1,34 +0,0 @@
import { type Literal, type Root } from 'mdast'
import type { Transformer } from 'unified'
import { parse as parseYaml } from 'yaml'
import { getMdastExport } from './utils'
export interface RemarkMdxFrontmatterOptions {
/**
* Exported name
* @default 'frontmatter'
*/
name?: string
}
/**
* A remark plugin to expose frontmatter data as named exports.
*/
function remarkMdxFrontmatter({
name = 'frontmatter'
}: RemarkMdxFrontmatterOptions = {}): Transformer<Root, Root> {
return ast => {
let data: unknown = {}
const node = ast.children.find(child => child.type === 'yaml')
if (node) {
const { value } = node as Literal
data = parseYaml(value)
}
// @ts-ignore Supported by MDX
ast.children.unshift(getMdastExport(name, data))
}
}
export default remarkMdxFrontmatter
+2 -2
View File
@@ -2,9 +2,9 @@ import { defineConfig } from 'tsup'
import tsconfig from './tsconfig.json'
export default defineConfig({
entry: ['./src/{config,map,types,loader}.ts'],
entry: ['./src/{config,map,types,loader,loader-mdx}.ts'],
format: 'esm',
external: ['next-docs-zeta'],
external: ['next-docs-zeta', 'webpack'],
dts: true,
target: tsconfig.compilerOptions.target as 'es2016'
})
+1 -2
View File
@@ -75,12 +75,11 @@
"@types/react": "18.2.0",
"@types/react-dom": "18.2.1",
"algoliasearch": "^4.20.0",
"autoprefixer": "10.4.16",
"next": "14.0.4",
"postcss": "8.4.32",
"postcss-cli": "^11.0.0",
"postcss-import": "^15.1.0",
"postcss-lightningcss": "^1.0.0",
"@algolia/client-search": "^4.20.0",
"tailwindcss": "3.3.6"
},
"keywords": [
+12 -57
View File
@@ -60,18 +60,9 @@ importers:
apps/docs:
dependencies:
'@mdx-js/loader':
specifier: ^3.0.0
version: 3.0.0([email protected])
'@mdx-js/react':
specifier: ^3.0.0
version: 3.0.0(@types/[email protected])([email protected])
'@next/env':
specifier: ^14.0.0
version: 14.0.0
'@next/mdx':
specifier: ^14.0.0
version: 14.0.0(@mdx-js/[email protected])(@mdx-js/[email protected])
algoliasearch:
specifier: ^4.20.0
version: 4.20.0
@@ -396,25 +387,25 @@ importers:
packages/next-docs-mdx:
dependencies:
'@mdx-js/mdx':
specifier: ^3.0.0
version: 3.0.0
estree-util-value-to-estree:
specifier: ^3.0.1
version: 3.0.1
fast-glob:
specifier: ^3.3.1
version: 3.3.1
remark-frontmatter:
specifier: ^5.0.0
version: 5.0.0
yaml:
specifier: ^2.3.3
version: 2.3.3
gray-matter:
specifier: ^4.0.3
version: 4.0.3
next-docs-zeta:
specifier: workspace:*
version: link:../next-docs
zod:
specifier: ^3.22.4
version: 3.22.4
devDependencies:
'@next/mdx':
specifier: 14.0.4
version: 14.0.4
'@types/mdast':
specifier: ^4.0.3
version: 4.0.3
@@ -427,9 +418,6 @@ importers:
next:
specifier: 14.0.4
version: 14.0.4(@babel/[email protected])([email protected])([email protected])
next-docs-zeta:
specifier: workspace:*
version: link:../next-docs
unified:
specifier: ^11.0.4
version: 11.0.4
@@ -497,6 +485,9 @@ importers:
specifier: ^1.0.7
version: 1.0.7([email protected])
devDependencies:
'@algolia/client-search':
specifier: ^4.20.0
version: 4.20.0
'@types/react':
specifier: 18.2.0
version: 18.2.0
@@ -506,9 +497,6 @@ importers:
algoliasearch:
specifier: ^4.20.0
version: 4.20.0
autoprefixer:
specifier: 10.4.16
version: 10.4.16([email protected])
next:
specifier: 14.0.4
version: 14.0.4(@babel/[email protected])([email protected])([email protected])
@@ -518,9 +506,6 @@ importers:
postcss-cli:
specifier: ^11.0.0
version: 11.0.0([email protected])
postcss-import:
specifier: ^15.1.0
version: 15.1.0([email protected])
postcss-lightningcss:
specifier: ^1.0.0
version: 1.0.0([email protected])
@@ -1687,22 +1672,6 @@ packages:
glob: 7.1.7
dev: true
/@next/[email protected](@mdx-js/[email protected])(@mdx-js/[email protected]):
resolution: {integrity: sha512-pFSJO81fGoK6wVZPa8j/z+DKY5t3shMY2/hajdhxMjITL4nLzUZenuq42j8cLsN/nNJymaUJCA1SjiRb7jNQ1A==}
peerDependencies:
'@mdx-js/loader': '>=0.15.0'
'@mdx-js/react': '>=0.15.0'
peerDependenciesMeta:
'@mdx-js/loader':
optional: true
'@mdx-js/react':
optional: true
dependencies:
'@mdx-js/loader': 3.0.0([email protected])
'@mdx-js/react': 3.0.0(@types/[email protected])([email protected])
source-map: 0.7.4
dev: false
/@next/[email protected](@mdx-js/[email protected])(@mdx-js/[email protected]):
resolution: {integrity: sha512-9hikFxx1XQOReOp5SBO1o3fGs1Z1GT1flKm1wrjFOwOTNI0M4x9CuQsUx5KJwvbt0FejCS5bsuNGXqxHchcMCQ==}
peerDependencies:
@@ -1719,20 +1688,6 @@ packages:
source-map: 0.7.4
dev: false
/@next/[email protected]:
resolution: {integrity: sha512-w0b+A2LRdlqqTIzmaeqPOaafid2cYYYjETA+G+3ZFwkNbBQjvZp57P1waOexF3MGHzcCEoXEnhYpAc+FO6S0Rg==}
peerDependencies:
'@mdx-js/loader': '>=0.15.0'
'@mdx-js/react': '>=0.15.0'
peerDependenciesMeta:
'@mdx-js/loader':
optional: true
'@mdx-js/react':
optional: true
dependencies:
source-map: 0.7.4
dev: true
/@next/[email protected]:
resolution: {integrity: sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==}
engines: {node: '>= 10'}