Webpack error in Storybook after installing react-map-gl library
The Context
Working on a NextJS project with Storybook to display components in isolation for our frontend. We were adding a map component and wanted to add a decalrative react wrapper for MapBox - so we decided on react-map-gl.
The Problem
After installing and building the feature in my local development environment, I ran Storybook, which uses Webpack to bundle it’s code and got the following stack trace.
Debugging
I read stack trace to find where the error was occurring:
ERROR in <filepath> 19:46.
Module parse failed: Unexpected token (19:46).
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See
https://webpack.js.org/concepts#loaders
This points me to the fact that Webpack can’t bundle this type of file → the
Unexpected token
was on the line thisRef.current.props.onGeolocate?.(e);
which is likely due to the optional chaining syntax (ES6) that it can’t parse.We looked inside the current Webpack config to see all the existing loaders for
.js
files. The pattern matcher (this was the include
property in config.module.rules
which tells Webpack which js
files to use a given loader for) was a function ([Function]
), so we printed out the function’s code using console.log(include.toString())
(when building the project) to see how it was deciding which files to apply the loader to. This just called another function from somewhere within the storybook library, so…We checked on GitHub for the storybook source code (press
.
to open this in web VS code and use cmd+shft+f
to grep search for the function I’m looking for). We found the function which decides which files use the ES6 loader (nodeModulesThatNeedToBeParsedBecauseTheyExposeES6). Adjacent to this was the exact snippet of code I needed to add into my Webpack config to fix my issue (I just needed to specify the modules I wanted to use ES6 with).The Code
Webpack config before:
const path = require('path'); const CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = async ({ config }) => { config.plugins.push( new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, '../public/fonts'), to: 'fonts', }, ], }), ); config.resolve.modules = [ ...(config.resolve.modules || []), path.resolve('./'), ]; return config; };
Webpack config afterwards:
const { getStorybookBabelConfig } = require('../node_modules/@storybook/core-common/dist/cjs/utils/babel'); const { plugins: storybookBabelPlugins } = getStorybookBabelConfig(); ... // inside the config // use babel ES6 loader with ES6 packages config.module.rules.push({ test: /\.js$/, use: [ { loader: require.resolve('babel-loader'), options: { sourceType: 'unambiguous', presets: [ [ require.resolve('@babel/preset-env'), { shippedProposals: true, modules: false, loose: true, targets: 'defaults', }, ], require.resolve('@babel/preset-react'), ], plugins: storybookBabelPlugins, }, }, ], // add node_modules to be bundled with ES6 here include: [/node_modules\/@mapbox/, /node_modules\/react-map-gl/], });