Skip to content

Commit 66cce1d

Browse files
author
Dimitri POSTOLOV
authoredSep 5, 2023
[v3] bundle to ESM only, use next.config.mjs or next.config.js with "type": "module" (#2254)

24 files changed

+245
-247
lines changed
 

Diff for: ‎.changeset/mighty-cars-hear.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88

99
- remove `Card` export, use `Cards.Card` instead
1010

11-
- disallow import md/mdx files that are outside of the working directory, use symlinks instead
11+
- disallow import md/mdx files that are outside the working directory, use symlinks instead

Diff for: ‎.changeset/moody-pianos-hide.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'nextra': major
3+
---
4+
5+
**BREAKING** bundle to ESM only
6+
7+
> ⚠️⚠️⚠️ use `next.config.mjs` or `next.config.js` with `"type": "module"`

Diff for: ‎docs/pages/docs/blog-theme/start.mdx

+7-5
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,20 @@ npm i next react react-dom nextra nextra-theme-blog
3333

3434
### Add Next.js Config
3535

36-
Create the following `next.config.js` file in your project’s root directory:
36+
Create the following `next.config.mjs` file in your project’s root directory:
3737

38-
```js filename="next.config.js"
39-
const withNextra = require('nextra')({
38+
```js filename="next.config.mjs"
39+
import nextra from 'nextra'
40+
41+
const withNextra = nextra({
4042
theme: 'nextra-theme-blog',
4143
themeConfig: './theme.config.jsx'
4244
})
4345

44-
module.exports = withNextra()
46+
export default withNextra()
4547

4648
// If you have other Next.js configurations, you can pass them as the parameter:
47-
// module.exports = withNextra({ /* other next.js config */ })
49+
// export default withNextra({ /* other next.js config */ })
4850
```
4951

5052
With the above configuration, Nextra can handle Markdown files in your Next.js

Diff for: ‎docs/pages/docs/custom-theme.mdx

+7-7
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ First, you need to tell Nextra to use your custom theme file instead of official
2121
ones. In your Next.js config, you can pass the path to your theme file to the
2222
Nextra plugin:
2323

24-
```js {2} filename="next.config.js"
25-
const withNextra = require('nextra')({
26-
theme: './theme.tsx',
27-
})
24+
```js {4} filename="next.config.mjs"
25+
import nextra from 'nextra'
2826

29-
module.exports = withNextra({
30-
// Other Next.js configurations
31-
...
27+
const withNextra = nextra({
28+
theme: './theme.tsx'
3229
})
30+
31+
// If you have other Next.js configurations, you can pass them as the parameter:
32+
// export default withNextra({ /* other next.js config */ })
3333
```
3434

3535
### Create a Basic Theme

Diff for: ‎docs/pages/docs/docs-theme/start.mdx

+7-5
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,20 @@ npm i next react react-dom nextra nextra-theme-docs
5454

5555
### Add Next.js Config
5656

57-
Create the following `next.config.js` file in your project’s root directory:
57+
Create the following `next.config.mjs` file in your project’s root directory:
5858

59-
```js filename="next.config.js"
60-
const withNextra = require('nextra')({
59+
```js filename="next.config.mjs"
60+
import nextra from 'nextra'
61+
62+
const withNextra = nextra({
6163
theme: 'nextra-theme-docs',
6264
themeConfig: './theme.config.jsx'
6365
})
6466

65-
module.exports = withNextra()
67+
export default withNextra()
6668

6769
// If you have other Next.js configurations, you can pass them as the parameter:
68-
// module.exports = withNextra({ /* other next.js config */ })
70+
// export default withNextra({ /* other next.js config */ })
6971
```
7072

7173
With the above configuration, Nextra can handle Markdown files in your Next.js

Diff for: ‎docs/pages/docs/get-started.mdx.backup

+5-3
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ Nextra works like a Next.js plugin, and it accepts a theme config (layout) to re
1919

2020
3. Create the following Next.js config:
2121

22-
```js filename="next.config.js"
23-
const withNextra = require('nextra')({
22+
```js filename="next.config.mjs"
23+
import nextra from 'nextra'
24+
25+
const withNextra = nextra({
2426
theme: 'nextra-theme-docs',
2527
themeConfig: './theme.config.jsx'
2628
// optional: add `staticImage: true` to enable Nextra's auto image import
2729
})
28-
module.exports = withNextra()
30+
export default withNextra()
2931
```
3032

3133
4. Create a `theme.config.jsx` file for the docs theme:

Diff for: ‎docs/pages/docs/guide/advanced/latex.mdx

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
# LaTeX
22

33
Nextra uses [KaTeX](https://katex.org/) to render LaTeX expressions directly in MDX.
4-
To enable LaTeX support, you must enable the `latex` option in your `next.config.js` file:
4+
To enable LaTeX support, you must enable the `latex` option in your `next.config.mjs` file:
55

6-
```js filename="next.config.js"
7-
module.exports = require('nextra')({
6+
```js filename="next.config.mjs"
7+
import nextra from 'nextra'
8+
9+
const withNextra = nextra({
810
latex: true
911
})
12+
13+
export default withNextra()
1014
```
1115

1216
When enabled, KaTeX’s CSS and fonts will be automatically included in your site, and you can start writing math expressions in your MDX files. Using LaTeX within MDX is as simple as wrapping your expression in `$` or `$$`.

Diff for: ‎docs/pages/docs/guide/i18n.mdx

+6-4
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,17 @@ out of the box. These docs explain how to configure and use it.
1212
### Add I18n Config
1313

1414
To add multi-language pages to your Nextra application, you need to config
15-
`i18n` in `next.config.js` first:
15+
`i18n` in `next.config.mjs` first:
1616

17-
```js filename="next.config.js" {7-10}
18-
const withNextra = require('nextra')({
17+
```js filename="next.config.mjs" {9-12}
18+
import nextra from 'nextra'
19+
20+
const withNextra = nextra({
1921
theme: 'nextra-theme-docs',
2022
themeConfig: './theme.config.tsx'
2123
})
2224

23-
module.exports = withNextra({
25+
export default withNextra({
2426
i18n: {
2527
locales: ['en', 'zh', 'de'],
2628
defaultLocale: 'en'

Diff for: ‎docs/pages/docs/guide/syntax-highlighting.mdx

+5-5
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ console.log('hello, world')
103103
```
104104

105105
You can enable this feature globally by setting `defaultShowCopyCode: true` in
106-
your Nextra configuration (`next.config.js` file). Once it's enabled globally,
106+
your Nextra configuration (`next.config.mjs` file). Once it's enabled globally,
107107
you can disable it via the `copy=false` attribute.
108108

109109
### Line Numbers
@@ -293,7 +293,7 @@ to see how it works.
293293

294294
You can opt out of syntax highlighting for using one of your own. You can
295295
disable syntax highlighting globally by setting `codeHighlight: false` in your
296-
Nextra configuration (`next.config.js` file).
296+
Nextra configuration (`next.config.mjs` file).
297297

298298
<OptionTable
299299
options={[
@@ -313,9 +313,9 @@ for syntax highlighting with custom language grammars.
313313

314314
You can provide these grammars by overriding the `getHighlighter` function in
315315
`mdxOptions.rehypePrettyCodeOptions` option in your Nextra config inside
316-
`next.config.js`:
316+
`next.config.mjs`:
317317

318-
```js filename="next.config.js" {13-18}
318+
```js filename="next.config.mjs" {13-18}
319319
import { BUNDLED_LANGUAGES } from 'shiki'
320320

321321
nextra({
@@ -346,7 +346,7 @@ nextra({
346346
Within `mdxOptions.rehypePrettyCodeOptions` you may also provide custom themes
347347
instead of [relying on CSS Variables](/docs/guide/syntax-highlighting):
348348

349-
```js filename="next.config.js" {4}
349+
```js filename="next.config.mjs" {4}
350350
nextra({
351351
// ... other options
352352
mdxOptions: {

Diff for: ‎examples/docs/src/pages/features/i18n.mdx

+9-4
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,17 @@ Nextra supports
1111
out of the box.
1212

1313
To add multi-language pages to your Nextra application, just need to config
14-
`i18n` in `next.config.js`:
14+
`i18n` in `next.config.mjs`:
1515

16-
```js filename="next.config.js"
17-
const withNextra = require('nextra')('nextra-theme-docs', './theme.config.js')
16+
```js filename="next.config.mjs"
17+
import nextra from 'nextra'
1818

19-
module.exports = withNextra({
19+
const withNextra = nextra({
20+
theme: 'nextra-theme-docs',
21+
themeConfig: './theme.config.js'
22+
})
23+
24+
export default withNextra({
2025
i18n: {
2126
locales: ['en', 'zh', 'de'],
2227
defaultLocale: 'en'

Diff for: ‎examples/docs/src/pages/features/latex.mdx

+7-3
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22

33
Nextra uses [KaTeX](https://katex.org/) to render LaTeX expressions directly in
44
MDX. To enable LaTeX support, you must add the following to your
5-
`next.config.js`:
5+
`next.config.mjs`:
66

7-
```js filename="next.config.js"
8-
module.exports = require('nextra')({
7+
```js filename="next.config.mjs"
8+
import nextra from 'nextra'
9+
10+
const withNextra = nextra({
911
latex: true
1012
})
13+
14+
export default withNextra()
1115
```
1216

1317
Using LaTeX within MDX is as simple as wrapping your expression in `$$` or `$`.

Diff for: ‎examples/docs/src/pages/get-started.mdx

+5-3
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ npm i nextra-theme-docs
3333

3434
### Create the following Next.js config and theme config under the root directory
3535

36-
```js filename="next.config.js"
37-
const withNextra = require('nextra')({
36+
```js filename="next.config.mjs"
37+
import nextra from 'nextra'
38+
39+
const withNextra = nextra({
3840
theme: 'nextra-theme-blog',
3941
themeConfig: './theme.config.js'
4042
// optional: add `staticImage: true` to enable Nextra's auto image import
4143
})
42-
module.exports = withNextra()
44+
export default withNextra()
4345
```
4446

4547
### Create a `theme.config.js` file for the docs theme

Diff for: ‎examples/docs/src/pages/themes/blog/index.mdx

+5-3
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ commands:
1919
3. Create the following Next.js config and theme config under the root
2020
directory:
2121

22-
```js filename="next.config.js"
23-
const withNextra = require('nextra')({
22+
```js filename="next.config.mjs"
23+
import nextra from 'nextra'
24+
25+
const withNextra = nextra({
2426
theme: 'nextra-theme-blog',
2527
themeConfig: './theme.config.js'
2628
// optional: add `staticImage: true` to enable Nextra's auto image import
2729
})
28-
module.exports = withNextra()
30+
export default withNextra()
2931
```
3032

3133
```jsx filename="theme.config.js"

Diff for: ‎examples/docs/src/pages/themes/docs/configuration.mdx

+5-3
Original file line numberDiff line numberDiff line change
@@ -580,15 +580,17 @@ Enable Nextra built-in search
580580

581581
**Example:**
582582

583-
```js filename="next.config.js"
584-
const withNextra = require('nextra')({
583+
```js filename="next.config.mjs"
584+
import nextra from 'nextra'
585+
586+
const withNextra = nextra({
585587
theme: 'nextra-theme-blog',
586588
themeConfig: './theme.config.js',
587589
flexsearch: {
588590
codeblock: false
589591
}
590592
})
591-
module.exports = withNextra()
593+
export default withNextra()
592594
```
593595

594596
<Unstable />

Diff for: ‎examples/docs/src/pages/themes/docs/index.mdx

+5-3
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ npm i nextra-theme-docs
2424

2525
### Create the following Next.js config and theme config under the root directory:
2626

27-
```jsx filename="next.config.js"
28-
const withNextra = require('nextra')({
27+
```jsx filename="next.config.mjs"
28+
import nextra from 'nextra'
29+
30+
const withNextra = nextra({
2931
theme: 'nextra-theme-blog',
3032
themeConfig: './theme.config.js'
3133
// optional: add `staticImage: true` to enable Nextra's auto image import
3234
})
33-
module.exports = withNextra()
35+
export default withNextra()
3436
```
3537

3638
```jsx filename="theme.config.js"

Diff for: ‎packages/nextra/package.json

+4-5
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
"engines": {
88
"node": ">=18"
99
},
10-
"main": "./dist/index.js",
1110
"exports": {
1211
"./package.json": "./package.json",
13-
".": "./dist/index.js",
12+
".": "./dist/index.mjs",
1413
"./catch-all": "./dist/catch-all.js",
1514
"./data": {
1615
"import": "./dist/data.js",
@@ -58,7 +57,7 @@
5857
"types": "./dist/*.d.mts"
5958
}
6059
},
61-
"types": "./dist/types.d.mts",
60+
"types": "./dist/index.d.mts",
6261
"typesVersions": {
6362
"*": {
6463
"compile": [
@@ -129,15 +128,15 @@
129128
"katex": "^0.16.8",
130129
"lodash.get": "^4.4.2",
131130
"next-mdx-remote": "^4.2.1",
132-
"p-limit": "^3.1.0",
131+
"p-limit": "^4.0.0",
133132
"rehype-katex": "^6.0.3",
134133
"rehype-pretty-code": "0.9.11",
135134
"rehype-raw": "^7.0.0",
136135
"remark-gfm": "^3.0.1",
137136
"remark-math": "^5.1.1",
138137
"remark-reading-time": "^2.0.1",
139138
"shiki": "^0.14.3",
140-
"slash": "^3.0.0",
139+
"slash": "^5.1.0",
141140
"title": "^3.5.3",
142141
"unist-util-remove": "^4.0.0",
143142
"unist-util-visit": "^5.0.0",

Diff for: ‎packages/nextra/src/constants.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ export const IS_PRODUCTION = process.env.NODE_ENV === 'production'
99

1010
export const DEFAULT_LOCALE = 'en-US'
1111

12-
export const DEFAULT_CONFIG: Omit<NextraConfig, 'theme'> = {
12+
export const DEFAULT_CONFIG = {
1313
staticImage: true,
1414
flexsearch: {
1515
codeblocks: true
1616
},
1717
codeHighlight: true
18-
}
18+
} satisfies Partial<NextraConfig>
1919

2020
export const OFFICIAL_THEMES = ['nextra-theme-docs', 'nextra-theme-blog']
2121

Diff for: ‎packages/nextra/src/env.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ declare namespace globalThis {
1515
var __nextra_resolvePageMap: () => Promise<PageMapItem[]>
1616
}
1717

18-
declare module 'next/dist/compiled/webpack/webpack' {
18+
declare module 'next/dist/compiled/webpack/webpack.js' {
1919
export { default as webpack, sources } from 'webpack'
2020
}

Diff for: ‎packages/nextra/src/index.js renamed to ‎packages/nextra/src/index.ts

+25-40
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
11
/* eslint-env node */
2+
import { createRequire } from 'node:module'
3+
import type { NextConfig } from 'next'
24
import {
35
DEFAULT_CONFIG,
46
DEFAULT_LOCALE,
57
DEFAULT_LOCALES,
68
MARKDOWN_EXTENSION_REGEX,
79
MARKDOWN_EXTENSIONS
810
} from './constants'
11+
import type { Nextra } from './types'
912
import { logger } from './utils'
1013
import { NextraPlugin, NextraSearchPlugin } from './webpack-plugins'
1114

1215
const DEFAULT_EXTENSIONS = ['js', 'jsx', 'ts', 'tsx']
1316

14-
const nextra = (themeOrNextraConfig, themeConfig) =>
15-
function withNextra(nextConfig = {}) {
16-
const nextraConfig = {
17-
...DEFAULT_CONFIG,
18-
...(typeof themeOrNextraConfig === 'string'
19-
? { theme: themeOrNextraConfig, themeConfig }
20-
: themeOrNextraConfig)
21-
}
17+
const require = createRequire(import.meta.url)
2218

19+
const nextra: Nextra = nextraConfig =>
20+
function withNextra(nextConfig = {}) {
2321
const hasI18n = !!nextConfig.i18n?.locales
2422

2523
if (hasI18n) {
@@ -32,13 +30,8 @@ const nextra = (themeOrNextraConfig, themeConfig) =>
3230
}
3331
const locales = nextConfig.i18n?.locales || DEFAULT_LOCALES
3432

35-
const rewrites = async () => {
36-
const rules = [
37-
{
38-
source: '/:path*/_meta',
39-
destination: '/404'
40-
}
41-
]
33+
const rewrites: NextConfig['rewrites'] = async () => {
34+
const rules = [{ source: '/:path*/_meta', destination: '/404' }]
4235

4336
if (nextConfig.rewrites) {
4437
const originalRewrites = await nextConfig.rewrites()
@@ -55,13 +48,13 @@ const nextra = (themeOrNextraConfig, themeConfig) =>
5548
}
5649

5750
const nextraLoaderOptions = {
51+
...DEFAULT_CONFIG,
5852
...nextraConfig,
59-
locales,
60-
defaultLocale: nextConfig.i18n?.defaultLocale || DEFAULT_LOCALE
53+
locales
6154
}
6255

6356
// Check if there's a theme provided
64-
if (!nextraLoaderOptions.theme) {
57+
if (!nextraConfig.theme) {
6558
throw new Error('No Nextra theme found!')
6659
}
6760

@@ -70,7 +63,8 @@ const nextra = (themeOrNextraConfig, themeConfig) =>
7063
...(nextConfig.output !== 'export' && { rewrites }),
7164
...(hasI18n && {
7265
env: {
73-
NEXTRA_DEFAULT_LOCALE: nextraLoaderOptions.defaultLocale,
66+
NEXTRA_DEFAULT_LOCALE:
67+
nextConfig.i18n?.defaultLocale || DEFAULT_LOCALE,
7468
...nextConfig.env
7569
},
7670
i18n: undefined
@@ -84,28 +78,11 @@ const nextra = (themeOrNextraConfig, themeConfig) =>
8478
config.plugins ||= []
8579
config.plugins.push(new NextraPlugin({ locales }))
8680

87-
if (nextraConfig.flexsearch) {
81+
if (nextraLoaderOptions.flexsearch) {
8882
config.plugins.push(new NextraSearchPlugin())
8983
}
9084
}
9185

92-
/* Adds client-side webpack optimization rules for splitting chunks during build-time */
93-
if (!options.isServer && config.optimization.splitChunks) {
94-
config.optimization.splitChunks.cacheGroups = {
95-
...config.optimization.splitChunks.cacheGroups,
96-
...Object.fromEntries(
97-
nextraLoaderOptions.locales.map(locale => [
98-
`nextra-page-map-${locale}`,
99-
{
100-
test: new RegExp(`nextra-page-map-${locale}`),
101-
name: `nextra-page-map-${locale}`,
102-
enforce: true
103-
}
104-
])
105-
)
106-
}
107-
}
108-
10986
const defaultESMAppPath = require.resolve('next/dist/esm/pages/_app.js')
11087
const defaultCJSAppPath = require.resolve('next/dist/pages/_app.js')
11188

@@ -114,8 +91,7 @@ const nextra = (themeOrNextraConfig, themeConfig) =>
11491
// Resolves ESM _app file instead cjs, so we could import theme.config via `import` statement
11592
[defaultCJSAppPath]: defaultESMAppPath
11693
}
117-
118-
config.module.rules.push(
94+
;(config.module.rules as RuleSetRule[]).push(
11995
{
12096
// Match Markdown imports from non-pages. These imports have an
12197
// issuer, which can be anything as long as it's not empty.
@@ -170,4 +146,13 @@ const nextra = (themeOrNextraConfig, themeConfig) =>
170146
}
171147
}
172148

173-
module.exports = nextra
149+
// TODO: take this type from webpack directly
150+
type RuleSetRule = {
151+
issuer: (value: string) => boolean
152+
test: RegExp
153+
use: unknown[]
154+
}
155+
156+
export default nextra
157+
158+
export type * from './types'

Diff for: ‎packages/nextra/src/types.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,9 @@ export type NextraConfig = {
156156
}
157157

158158
export type Nextra = (
159-
...args: [NextraConfig] | [theme: Theme, themeConfig: string]
159+
nextraConfig: NextraConfig
160160
) => (nextConfig: NextConfig) => NextConfig
161161

162-
const nextra: Nextra = () => () => ({})
163-
164-
export default nextra
165-
166162
export type ThemeConfig = any | null
167163

168164
export type NextraThemeLayoutProps = {

Diff for: ‎packages/nextra/src/webpack-plugins/nextra-search.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { sources, webpack } from 'next/dist/compiled/webpack/webpack'
1+
import pkg from 'next/dist/compiled/webpack/webpack.js'
22
import type { Compiler } from 'webpack'
33
import { IS_PRODUCTION } from '../constants'
44
import type { SearchData } from '../types'
55

6+
const { sources, webpack } = pkg
7+
68
export class NextraSearchPlugin {
79
apply(compiler: Compiler) {
810
const pluginName = this.constructor.name

Diff for: ‎packages/nextra/src/webpack-plugins/nextra.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { join } from 'node:path'
2-
import { promises as fs } from 'graceful-fs'
2+
import pkg from 'graceful-fs'
33
import type { Compiler } from 'webpack'
44
import { CHUNKS_DIR } from '../constants'
55
import { PAGES_DIR } from '../file-system'
66
import { getDynamicMeta } from '../page-map'
77
import { collectFiles } from '../plugin'
88
import type { Folder } from '../types'
99

10+
const fs = pkg.promises
11+
1012
export class NextraPlugin {
1113
constructor(private config: { locales: string[] }) {}
1214

Diff for: ‎packages/nextra/tsup.config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ const sharedConfig = {
6666
export default defineConfig([
6767
{
6868
name: 'nextra',
69-
entry: ['src/index.js', 'src/__temp__.js', 'src/catch-all.ts'],
69+
entry: ['src/__temp__.js', 'src/catch-all.ts'],
7070
format: 'cjs',
7171
dts: false
7272
},
@@ -76,6 +76,7 @@ export default defineConfig([
7676
'src/**/*.ts',
7777
'!src/**/*.d.ts',
7878
'!src/catch-all.ts',
79+
'!src/types.ts',
7980
...CLIENT_ENTRY.map(filePath => `!${filePath}`)
8081
],
8182
...sharedConfig

Diff for: ‎pnpm-lock.yaml

+116-139
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
Please sign in to comment.