mdx-bundler
mdx-bundler copied to clipboard
Nextjs and Storybook: ModuleNotFoundError: Module not found: Error: Can't resolve 'fs'
-
mdx-bundler
version: 8.0.1 -
node
version: 14.18.1 -
npm
version: 6.14.15 -
next
version: 12.0.8
What you did:
I am building my site with Nextjs and using mdx-bundler for my blog. I was trying to add storybook for my blog page, which loads the posts using mdx-bundler.
The blog loads the posts in a similar way as the reproduction below.
What happened:
Starting storybook I get the following error, followed by more similar errors (which seems to be related to esbuild):
ModuleNotFoundError: Module not found: Error: Can't resolve 'fs' in 'C:\example\node_modules\resolve\lib'
at C:\example\node_modules\webpack\lib\Compilation.js:925:10
at C:\example\node_modules\webpack\lib\NormalModuleFactory.js:401:22
at C:\example\node_modules\webpack\lib\NormalModuleFactory.js:130:21
at C:\example\node_modules\webpack\lib\NormalModuleFactory.js:224:22
at C:\example\node_modules\neo-async\async.js:2830:7
at C:\example\node_modules\neo-async\async.js:6877:13
at C:\example\node_modules\webpack\lib\NormalModuleFactory.js:214:25
at C:\example\node_modules\webpack\node_modules\enhanced-resolve\lib\Resolver.js:213:14
at C:\example\node_modules\webpack\node_modules\enhanced-resolve\lib\Resolver.js:285:5
at eval (eval at create (C:\example\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:15:1)
at C:\example\node_modules\webpack\node_modules\enhanced-resolve\lib\UnsafeCachePlugin.js:44:7
at C:\example\node_modules\webpack\node_modules\enhanced-resolve\lib\Resolver.js:285:5
at eval (eval at create (C:\example\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:15:1)
at C:\example\node_modules\webpack\node_modules\enhanced-resolve\lib\Resolver.js:285:5
at eval (eval at create (C:\example\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:27:1)
at C:\example\node_modules\webpack\node_modules\enhanced-resolve\lib\DescriptionFilePlugin.js:67:43
After commenting getStaticProps
at Home
component, storybook runs as expected.
Reproduction repository:
https://codesandbox.io/s/nextjs-storybook-mdx-bundler-2jheo?file=/pages/index.js
Running yarn dev
in terminal or restarting sandbox will raise the error at the terminal
Late answer, but maybe it will be helpful for someone else:
Storybook is a client side application, so Node.js modules like fs
do not work. Therefore, mdx-bundler
can not be called from within storybook.
What you can do is doing the bundeling in a separate script:
// bundle-mdx.js
const { bundleMDX } = require('mdx-bundler')
const fs = require('fs-extra')
const { join } = require('path')
;(async function () {
const sourceDir = join(__dirname, 'mdx')
const sourceFiles = await fs.readdir(sourceDir, 'utf-8')
const targetDir = join(__dirname, 'bundled')
await fs.remove(targetDir)
await fs.ensureDir(targetDir)
await Promise.all(
sourceFiles.map(async file => {
const sourceFilePath = join(sourceDir, file)
const source = await fs.readFile(sourceFilePath, 'utf-8')
const bundled = await bundleMDX({ source })
const json = JSON.stringify(
bundled,
undefined,
process.env.NODE_ENV === 'production' ? null : 2
)
const targetFile = join(targetDir, file.replace(/\.mdx$/, '.json'))
await fs.writeFile(targetFile, json)
})
)
})()
And add to your package.json
:
{
"scripts": {
"bundle-story-mdx": "node ./bundle-mdx.js",
"bundle-story-mdx:watch": "nodemon --watch ./mdx -e md,mdx ./bundle-mdx.js",
},
...
}
Create a mdx
folder next to bundle-mdx.js
. When you run npm run bundle-story-mdx
(or yarn bundle-story-mdx
, if you are using yarn), all mdx files within will get bundled by the script.
To automatically run the script whenever you change a mdx file, install nodemon via npm i -D nodemon
, and run npm run bundle-story-mdx:watch
.
In storybook, you can then just import the bundles and use them in your stories:
// MyComponent.stories.ts
import React from 'react'
import { Meta, Story } from '@storybook/react/types-6-0'
import { MyComponent, MyComponentProps } from './MyComponent'
import bundle from './bundled/example.json'
const meta: Meta = {
title: 'MyComponent',
component: MyComponent,
}
export default meta
const Template: Story<MyComponentProps> = args => <MyComponent {...args} />
export const Standard = Template.bind({})
Standard.args = {
// pass bundle down to your components
bundledMdx: bundle.code,
}
You might have to adjust things to make it work with your Next.js page.