Skip to content

Commit 4744ca1

Browse files
committed
Turbopack: Allow a custom list of file extensions to use with @next/mdx
This allows users to customize the file extensions to process with `@next/mdx`. Before this change, `@next/mdx` with Turbopack did not consider the `extension` property at all. This was challenging since it doesn’t support all of the different ways of testing module specifiers that webpack does, such as with a regular expression. This implements (and documents) a different behavior for Turbopack: accepting a list of extensions as `extension`. This is fairly backwards compatible, as it does not change how the plugin behaves with webpack, and it was ignored by Turbopack previously. For example, to configure `next.config.js` with `@next/mdx` and support for both the `mdx` and `md` extension: ```js import createMDX from '@next/mdx' const withMDX = createMDX({ extension: ['mdx', 'md'], }) export default withMDX({}) ``` This is equivalent to the following with Next.js and webpack: ```js import createMDX from '@next/mdx' const withMDX = createMDX({ extension: /\.mdx?$/, }) export default withMDX({}) ``` Test Plan: Changed the existing mdx test to import an `md` component as well.
1 parent ea5dfd7 commit 4744ca1

File tree

9 files changed

+50
-21
lines changed

9 files changed

+50
-21
lines changed

docs/01-app/02-guides/mdx.mdx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,22 @@ const nextConfig = {
5151
}
5252

5353
const withMDX = createMDX({
54+
extension: process.env.TURBOPACK ? ['mdx', 'md'] : /\.mdx?$/,
5455
// Add markdown plugins here, as desired
5556
})
5657

5758
// Merge MDX config with Next.js config
5859
export default withMDX(nextConfig)
5960
```
6061

62+
> **Good to know**:
63+
>
64+
> The `extension` option specifies the file extensions to be processed by the MDX loader.
65+
> For Next.js with webpack, this is a webpack rule test, such as a regular expression.
66+
> For Turbopack, this is either a single file extension string, or an array of file extensions.
67+
>
68+
> For compatibility reasons, webpack with Next.js does not understand the list format as extensions.
69+
6170
This allows `.md` and `.mdx` files to act as pages, routes, or imports in your application.
6271

6372
## Add an `mdx-components.tsx` file

packages/next-mdx/index.js

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
module.exports =
22
(pluginOptions = {}) =>
33
(inputConfig = {}) => {
4-
const extension = pluginOptions.extension || /\.mdx$/
4+
const extension =
5+
pluginOptions.extension || (process.env.TURBOPACK ? 'mdx' : /\.mdx$/)
56
const userProvidedMdxOptions = pluginOptions.options
67

78
const mdxRsOptions = inputConfig?.experimental?.mdxRs
@@ -47,13 +48,35 @@ module.exports =
4748
})
4849

4950
if (process.env.TURBOPACK) {
51+
if (
52+
!(
53+
Array.isArray(extension) &&
54+
extension.every((e) => typeof e === 'string')
55+
) &&
56+
typeof extension !== 'string'
57+
) {
58+
throw new Error(
59+
'@next/mdx: Turbopack only supports a single extension string or list of extensions. `extension` was',
60+
extension
61+
)
62+
}
63+
64+
const extensions = (
65+
typeof extension === 'string' ? [extension] : extension
66+
)
67+
// Remove any leading dot from the extension as this is a common mistake.
68+
.map((ext) => ext.replace(/^\./, ''))
69+
70+
const rules = nextConfig?.turbopack?.rules || {}
71+
for (const ext of extensions) {
72+
rules['*.' + ext] = {
73+
loaders: [loader],
74+
as: '*.tsx',
75+
}
76+
}
77+
5078
nextConfig.turbopack = Object.assign({}, nextConfig?.turbopack, {
51-
rules: Object.assign({}, nextConfig?.turbopack?.rules, {
52-
'*.mdx': {
53-
loaders: [loader],
54-
as: '*.tsx',
55-
},
56-
}),
79+
rules,
5780
resolveAlias: Object.assign({}, nextConfig?.turbopack?.resolveAlias, {
5881
'next-mdx-import-source-file':
5982
'@vercel/turbopack-next/mdx-import-source',

test/development/acceptance-app/ReactRefreshRegression.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,9 +335,7 @@ describe('ReactRefreshRegression app', () => {
335335
files.set(
336336
'next.config.js',
337337
outdent`
338-
const withMDX = require("@next/mdx")({
339-
extension: /\\.mdx?$/,
340-
});
338+
const withMDX = require("@next/mdx")();
341339
module.exports = withMDX({
342340
pageExtensions: ["js", "mdx"],
343341
});

test/development/acceptance/ReactRefreshRegression.test.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,18 @@ describe('ReactRefreshRegression', () => {
2525
outdent`
2626
import Document from 'next/document'
2727
import { ServerStyleSheet } from 'styled-components'
28-
28+
2929
export default class MyDocument extends Document {
3030
static async getInitialProps(ctx) {
3131
const sheet = new ServerStyleSheet()
3232
const originalRenderPage = ctx.renderPage
33-
33+
3434
try {
3535
ctx.renderPage = () =>
3636
originalRenderPage({
3737
enhanceApp: App => props => sheet.collectStyles(<App {...props} />),
3838
})
39-
39+
4040
const initialProps = await Document.getInitialProps(ctx)
4141
return {
4242
...initialProps,
@@ -321,9 +321,7 @@ describe('ReactRefreshRegression', () => {
321321
[
322322
'next.config.js',
323323
outdent`
324-
const withMDX = require("@next/mdx")({
325-
extension: /\\.mdx?$/,
326-
});
324+
const withMDX = require("@next/mdx")();
327325
module.exports = withMDX({
328326
pageExtensions: ["js", "mdx"],
329327
});

test/e2e/app-dir/mdx/next.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import nextMDX from '@next/mdx'
22
const withMDX = nextMDX({
3-
extension: /\.mdx?$/,
3+
extension: process.env.TURBOPACK ? ['md', 'mdx'] : /\.mdx?$/,
44
options: {
55
remarkPlugins: [],
66
rehypePlugins: [['rehype-katex', { strict: true, throwOnError: true }]],
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import MDXContent from './mdx-file.mdx'
1+
import MDContent from './md-file.md'
2+
23
export default function Page() {
34
return (
45
<>
5-
<MDXContent />
6+
<MDContent />
67
</>
78
)
89
}
File renamed without changes.

test/integration/plugin-mdx-rs/next.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const withMDX = require('@next/mdx')({
2-
extension: /\.mdx?$/,
2+
extension: process.env.TURBOPACK ? ['mdx', 'md'] : /\.mdx?$/,
33
})
44
module.exports = withMDX({
55
pageExtensions: ['js', 'jsx', 'mdx'],

test/integration/plugin-mdx-rs/test/index.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('MDX-rs Configuration', () => {
4545

4646
nextConfig.write(`
4747
const withMDX = require('@next/mdx')({
48-
extension: /\\.mdx?$/,
48+
extension: process.env.TURBOPACK ? ['mdx', 'md'] : /\\.mdx?$/,
4949
})
5050
module.exports = withMDX({
5151
pageExtensions: ['js', 'jsx', 'mdx'],

0 commit comments

Comments
 (0)