Upgrade to Webpack 5 breaking Storybook 5 - reactjs

In process of upgrading a webpack 4/storybook 5 project to webpack 5 to hopefully take advantage of federated modules. I have regular webpack --config webpack.config.js building working atfer some struggle, but I can't seem to overcome this storybook config issue to get that working. there's not a lot in the storybook webpack.config.js - just some module rules for testing for less files and using the appropriate loaders. It seems the error I'm encountering is typical when upgrading webpack majors, as I've found a number of folks that encountered the same thing going from 3-4, but anything I've tried has failed so far. The specific stacktrace is:
Cannot read property 'tapAsync' of undefined
at ExternalModuleFactoryPlugin.apply (/Users/tbullard/Workspace/unify/node_modules/webpack/lib/ExternalModuleFactoryPlugin.js:29:39)
at compiler.hooks.compile.tap (/Users/tbullard/Workspace/unify/node_modules/webpack/lib/ExternalsPlugin.js:24:63)
at SyncHook.eval [as call] (eval at create (/Users/tbullard/Workspace/unify/node_modules/tapable/lib/HookCodeFactory.js:19:10), <anonymous>:7:1)
at SyncHook.lazyCompileHook (/Users/tbullard/Workspace/unify/node_modules/tapable/lib/Hook.js:154:20)
at hooks.beforeCompile.callAsync.err (/Users/tbullard/Workspace/unify/node_modules/#storybook/core/node_modules/webpack/lib/Compiler.js:665:23)
at _err0 (eval at create (/Users/tbullard/Workspace/unify/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:11:1)
at compiler.inputFileSystem.readFile (/Users/tbullard/Workspace/unify/node_modules/#storybook/core/node_modules/webpack/lib/DllReferencePlugin.js:72:15)
at process.nextTick (/Users/tbullard/Workspace/unify/node_modules/#storybook/core/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:85:15)
at process._tickCallback (internal/process/next_tick.js:61:11)
Leads me to believe there's a plugin incompatibility 🤷🏻‍♂️ TIA!

As of 6.2.0, Storybook's preview builder is officially Webpack 5 compatible and the manager builder is unofficially Webpack 5 compatible. See this explanation of the builders and Webpack 5 compatibility. Also check out this gist/comments section with more detailed installation instructions.
If I understand correctly, setting the builder to Webpack 5 (as per these instructions) will force the preview builder to use Webpack 5, allowing you to expose your UI components for cool, new Webpack 5 features such as module federation.
However, if you also want to force the manager builder to use Webpack 5 (so that you can just finish breaking up with Webpack 4), you'll need to make use of Yarn selective dependency resolutions. Here is what I did specifically (in a package.json):
"resolutions": {
"webpack": "^5.27.2",
"css-loader": "^5.0.1",
"dotenv-webpack": "^7.0.2",
"html-webpack-plugin": "^5.0.0",
"style-loader": "^2.0.0",
"terser-webpack-plugin": "^5.1.1",
"webpack-dev-middleware": "^4.1.0",
"webpack-virtual-modules": "^0.4.2"
}
With these resolutions, Yarn detects that Webpack 4 is no longer used and removes it. The SB build gives this warning:
info #storybook/react v6.2.0-rc.10
info
info => Loading presets
WARN Unexpected webpack version in manager-builder
WARN - Received: 5.27.2
WARN - Expected: 4.x
One of the tasks of migrating from Webpack 4 to 5 involves manually providing browser polyfills for node packages which were automatically provided by Webpack 4. I want to note that if you find yourself manually providing a ton of polyfills while upgrading Storybook to Webpack 5, you have probably gotten off in the wrong direction. The Storybook dev-server builds get cached in a local (to the package where Storybook is installed) node_modules directory (whatever-package/node_modules/.cache/storybook/dev-server). Deleting the dev-server sub-directory regularly can help you debug your build and potentially spare you from building out long lists of unnecessary node polyfills. (I learned this the hard way).
With that said, for a cleanish install of Storybook you might not actually need any polyfills. On the other hand, some cases do require the node polyfills (e.g. #storybook/addon-docs requires "assert" (see below)). Here is one way to add polyfills (and addons, if you want) to Storybook's Webpack config in main.js:
module.exports = {
core: {
builder: 'webpack5',
},
stories: ['../whatever/src/**/*.stories.#(ts|tsx)'],
addons: [
'#storybook/addon-actions',
'#storybook/addon-controls',
'#storybook/addon-docs',
],
webpackFinal: (config) => {
return {
...config,
resolve: {
...config.resolve,
fallback: {
...config.fallback,
assert: require.resolve('assert-browserify/'),
},
},
};
},
};
Re: addons, I had serious issues with node polyfills when attempting to use addon-essentials. I've been... adding... addons piecemeal instead (standalone packages via npm), and that seems to be working without any polyfills (#storybook/actions and #storybook/controls are good oob; #storybook/docs requires the assert polyfill (above), #storybook/addons is also working fine with theming in manager.ts---that is:
import { addons } from '#storybook/addons';
import { themes } from '#storybook/theming';
addons.setConfig({
theme: themes.dark,
});
I also want to note that adding sass-loader to Webpack config.module.rules works as expected. Some people were running into problems with some scss preset with Storybook and Webpack 5. Here's the relevant portion of a proper Storybook Webpack config for Sass:
module: {
...config.module,
rules: [
...config.module.rules,
{
test: /\.(scss)$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: function () {
return [require('precss'), require('autoprefixer')];
},
},
},
},
{
loader: require.resolve('sass-loader'),
options: {
// using sass (Dart) instead of node-sass because node-sass (Javascript) cannot resolve Yarn 2's Plug'N'Play synthetic node_modules directory
// Evidently, sass is a bit slower to compile than node-sass, but I think I prefer sass anyway for latest features (such as #use)
implementation: require('sass'),
},
},
],
},
],
},
Hope that will get you off the ground 🛫

Storybook is not yet ready to work with Webpack 5 but it is on their roadmap for version 7.0.
More context in this GitHub issue.

Upgrading webpack v4 to v5 in storybook v6.
check the below link for detailed explanation.
https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#webpack-5
"#storybook/builder-webpack5": "^6.5.15",
"#storybook/manager-webpack5": "^6.5.15",

It happened to me as well, in the end I solved it setting the dependency for storybook webpack5 but using webpack4:
"#storybook/addon-actions": "^6.2.9",
"#storybook/addon-controls": "^6.2.9",
"#storybook/addon-storysource": "^6.2.9",
"#storybook/builder-webpack5": "^6.2.9",
"#storybook/vue": "^6.2.9",
As I read here: https://stackoverflow.com/a/67075112/5384339 I think it's better to wait a bit before using webpack5

Related

Storybook webpack config not working - trying to run Storybook out of separate local project than components

I have a mature CRA-based React app running with Webpack 5. I would like to have a separate project (in git, etc) where Storybook lives and points to the components in the app. (The app has tons of devs in and out of it, and dropping a bunch of Storybook packages in there, as well as introducing legacy-peer-dependencies thanks to webpack 5, would be quite frowned upon).
I also want devs to have a good experience being able to use Storybook to write components, so I want Storybook to see the current code of the project components, not some exported package. And same as above, there are many devs and a lot of inertia, so moving components to a separate standalone library is not an option.
My ideal for local development:
components and stories: /MyProject-App/src/Components/...
storybook app. : /MyProject-Storybook/stories/...
(Production I'm not worried about yet)
Installing Storybook inside the app works fine (as long as you run with --legacy-peer-deps). I am using the npx storybook init script and it works fine. But if I try to run Storybook out of a separate directory and target the app directory's Components, it breaks. If I run Storybook out of the app, and point it to stories/components outside that repo (which I copied and pasted just as a debugging measure), it breaks. Going up and out of the current project root breaks.
To do this, I am trying to point stories in /MyProject-Storybook/.storybook/main.js to ../../MyProject-App/src/Components.... When I do this and npm run storybook, I get the error output:
File was processed with these loaders:
* ./node_modules/#pmmmwh/react-refresh-webpack-plugin/loader/index.js
* ./node_modules/#storybook/source-loader/dist/cjs/index.js
**You may need an additional loader to handle the result of these loaders.**
The error is always on some basic ES6 syntax, arrow functions etc. If I run the same Storybook install out of MyProject-App (same version numbers / same main.js just pointed at the local path instead of the ../other path) it works.
In addition to this, I tried it the other way - running storybook out of the App folder (where I know it runs), and only changing the main.js stories directory to an outside-that-repo folder where I copied my Components and stories into. It breaks in the same way - I get the same You may need an additional loader to handle the result of these loaders. message, with it pointing to any example of ES6 syntax as an 'error'.
I found this similar question - Storybook can't process TS files outside of the project
recommending to look into Storybook's webpack loaders - https://storybook.js.org/docs/react/builders/webpack
So I updated my .storybook/main.js to be the following:
module.exports = {
stories: [
'../../MyProject-Storybook/src/**/*.stories.mdx',
'../../MyProject-Storybook/src/**/*.stories.#(js|jsx|ts|tsx)'
],
addons: [
'#storybook/addon-links',
'#storybook/addon-essentials',
'#storybook/addon-interactions',
'#storybook/preset-create-react-app'
],
framework: '#storybook/react',
core: {
builder: '#storybook/builder-webpack5'
},
webpackFinal: async (config, { configType }) => {
config.module.rules.push({
test: /\.(js|jsx)$/,
use: [
{
loader: require.resolve('babel-loader'),
options: {
reportFiles: ['../**/src/**/*.{js,jsx}', '../../MyProject-Storybook/**.stories.{js,jsx}']
}
}
]
});
config.resolve.extensions.push('.js', 'jsx');
return config;
}
};
but to no avail - output from npm run storybook remains unchanged, an excerpt:
File was processed with these loaders:
* ./node_modules/#pmmmwh/react-refresh-webpack-plugin/loader/index.js
* ./node_modules/#storybook/source-loader/dist/cjs/index.js
You may need an additional loader to handle the result of these loaders.
| backgroundColor: { control: 'color' },
| },
> } as ComponentMeta<typeof Button>;
|

rollup bundled react development resources

I'm using "vite": "^2.8.6" for React project. What I know is that Vite is using Rollup as module bundler, but I stumbled on a problem where Rollup still bundling my react-dom.development.js and react.development.js. I've used "rollup-plugin-replace" to replace my 'process.env.NODE_ENV' to production, but the problem still occur. Here is the my rollup config:
rollupOptions: {
// https://reactjs.org/docs/optimizing-performance.html#rollup
plugins: [
rollupPluginReplace({
'process.env.NODE_ENV': JSON.stringify('production')
}),
rollupPluginCommonjs(),
terser(),
visualizer()
],
},
When I analyze with rollup-visualizer, you can see that rollup bundled both production and development dependency, which supposedly only bundled one of them right?
The problem with this is that there is extra 1MB of dead code in the bundle, it will be great if I can eliminate it.
This generally means that rollup does not understand that your app is directed towards production code. In my case it was because I had set up library mode.
lib: {
entry: './src/app.ts',
fileName: 'app.ts',
name: 'AppClass',
formats: ['iife'],
}
Removing this block finally generated a build which was sane in size. For more information, see the vite documentation.
If you were also trying to get vite/rollup to build your app as an IIFE, setting rollupOptions worked for me:
rollupOptions: {
output: {
entryFileNames: `[name].js`,
assetFileNames: `app.[ext]`,
format: 'iife',
},
input: ['./src/app.ts'],
},

Webpack bundled packages can't find peer dependencies in vite_rails project

I have a rails project that I'm migrating from webpacker to vite.js and a number of my private packages, bundled with webpack all are built with react and so the rails project was setup with webpack and installed react/-dom itself for use in these installed packages.
In the webpacker config I had to setup aliases for most of these so they could find React and React Dom.
const { resolve } = require('path');
module.exports = {
React: resolve(__dirname, '../../node_modules/react'),
ReactDOM: resolve(__dirname, '../../node_modules/react-dom'),
"react": resolve(__dirname, '../../node_modules/react'),
"react-dom": resolve(__dirname, '../../node_modules/react-dom'),
AsyncStorage: resolve(__dirname, '../../node_modules/#react-native-community/async-storage'),
Pusher: resolve(__dirname, '../../node_modules/pusher-js')
}
But I can't find in the vite.js or rollup.js documentation where or even if I have to do something similar. This section of vite's docs makes me think that it's supposed to just be handling it magically but appears to not be.
Here's an example of the error I'm getting:
✘ [ERROR] Could not resolve "ReactDOM"
node_modules/skarsnik/bundled/plugin.js:1:107:
1 │ ...of exports&&"object"==typeof module?module.exports=e(require("React"),require("ReactDOM"),require("GobblaPackage")):"function"==typeof define&&define.amd?define(["Re...
╵ ~~~~~~~~~~
You can mark the path "ReactDOM" as external to exclude it from the bundle, which will remove this
error. You can also surround this "require" call with a try/catch block to handle this failure at
run-time instead of bundle-time.
Any ideas would be greatly appreciated
EDIT
also looking at one of the webpack bundled packages I have externals config which may explain the "Could not resolve 'ReactDOM'":
externals: {
'react': "React",
'react-dom': 'ReactDOM',
'#realgeeks/gobbla/bundled/plugin': "GobblaPackage",
"#react-native-community/async-storage": "AsyncStorage"
}

Syntax Error In IE 11 for this node_moduels

I am getting a syntax error in IE when this component of react is loaded in the webpage. Has anybody got the same problem? This is an inherited package, and a syntax error from node_modules makes no sense?
"use strict";
/* WEBPACK VAR INJECTION */(function(module) {
const colorConvert = __webpack_require__(/*! color-convert */ "./node_modules/color-convert/index.js");
const wrapAnsi16 = (fn, offset) => function () {
const code = fn.apply(colorConvert, arguments);
return `\u001B[${code + offset}m`;
};
const wrapAnsi256 = (fn, offset) => function () {
const code = fn.apply(colorConvert, arguments);
return `\u001B[${38 + offset};5;${code}m`;
};
If you are using newer versions of Node/NPM, check your package.json file -> "browserslist" section.
This is the default "browserslist" created for you if you do not have one defined:
In this case, if you run "npm start" on your LOCAL Environment, Babel will not create Polyfills for IE11 because its not included as a target browser in "development". To get this working, I deleted my node_modules directory completely, ran 'npm install', updated package.json with:
and ran 'npm start.
The reason why this fails is that babel or your other favorite transpiler might ignore node_modules (if that's how its configured), so you need to include it manually because IE does not support arrow function syntax.
First, if you search for wrapAnsi16 or wrapAnsi256 function names online it'll point you to common npm packages, such as: ansi-styles, chalk or color-convert, debug, strip-ansi, etc.
If you are using Webpack you can add the following to your rules:
module: {
rules: [{
exclude: /node_modules\/(?!(color-convert|ansi-styles|strip-ansi|ansi-regex|debug|react-dev-utils|chalk)\/).*/
}]
}
or, easier to read:
module: {
rules: [{
include: [
path.resolve(__dirname, 'node_modules/ansi-styles'),
path.resolve(__dirname, 'node_modules/strip-ansi'),
... other's here...
path.resolve(__dirname, 'src'),
]
}]
}
Hope this helps somebody in the future ;)
TLDR; you don't need this library, just run
npm run build
And it will be excluded from your build.
I have same problem with create-react-app, and I solve it (no). From my discovery, this library should not appear in browser, because it was designed for nodejs environment. Also I found, this library come to me as dependency of jest, and jest is dependency for tests and it come as dependency for react.
So, I run
npm run build
server -s build
And try my application in IE. And it work. So, when you run
npm start
It make file including dev dependencies and other garbage that should not appear in production and in browser at all. When you run
npm run build
It make file only with required project libraries.
I had similar issue #punkbit solution and installing 'react-app-polyfill'
and importing it at the top of the index.js file solved it
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
If it still does not work delete node-modules and reinstall also clear cache in IE.
All the best :)
This problem occurs because your compiled code contains (modern) ES6 syntax whilst IE11 only supports ES5.
A way to fix this is to instruct webpack to specifically compile the mentioned packages into ES5;
module: {
rules: [{
test: /\.(tsx?|js)$/,
include: [
// These dependencies have es6 syntax which ie11 doesn't like.
// Whenever you see a "SyntaxError" that crashes IE11 because of a new lib, add it here.
path.join(__dirname, 'node_modules/react-intl'),
path.join(__dirname, 'node_modules/pkce-challenge'),
path.join(__dirname, 'node_modules/fuse.js')
],
use: [{
loader: 'ts-loader', // Or whatever loader you're using
}]
}]
}
for me this was: fuse.js, pkce-challenge and react-intl.

babel and reactjs: how to use es2015 style imports

I'm new to javascript and javascript build scripts, and I'm trying to build a "future-proof" build script for building a ReactJS/Redux app. I'm having trouble with getting imports to work between javascript files.
My question is what is the recommended approach to add support for es2016-style import statements?
As I've been trying to get this working, these are the questions and comments that are rolling around in my head that help color where I'm coming from.
I've just been getting a little more comfortable with Gulp. Is it possible to use just Gulp, Babel, and npm to add support for es2016-style import statements?
I'm wondering if Gulp still the recommended way to go for building javascript bundles, or should I learn WebPack instead.
In the past, I've used to use Browserify for including other javascript files, but I've heard people mention that you can do what Browserify does with pure npm and that Browserify may be falling out of favor.
I've noticed a lot of ReactJS examples using WebPack. I'm not sure where WebPack fits in or if it's necessary. I'm wondering if WebPack takes the place of Browserify and if I need WebPack or if I can do without it.
I'd prefer to use whatever import syntax is the recommended. I believe that Browserify uses require() and es2015 syntax uses "import ... from". I'm wondering if the "import ... from" is the recommended syntax to use for imports now or if I should be using something else.
I've been trying to use Babel 6 to use es2015-style code. I've noticed that it doesn't pre-process the import statements and I think I read somewhere that Babel 6 removed support for import statements. I'm wondering what to use in place of that to pre-process import statements.
I'd be interested in minimizing the amount of configuration (dot files and such) to build a basic project.
Below is a simple example that I've been trying to get working, using Gulp. Currently, when Gulp runs, is creates a bundle, but the import statement doesn't seem to work. When I try to load index.html, everything looks concated together and I get javascript errors.
more package.json
{
"name": "test_jsx",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"babel-cli": "^6.14.0",
"babel-plugin-transform-react-jsx": "^6.8.0"
},
"devDependencies": {
"babel-preset-es2015": "^6.14.0",
"babel-preset-react": "^6.11.1",
"babel-preset-stage-0": "^6.5.0",
"gulp": "^3.9.1",
"gulp-babel": "^6.1.2",
"gulp-cli": "^1.2.2",
"gulp-concat": "^2.6.0",
"gulp-print": "^2.0.1",
"gulp-sourcemaps": "^1.6.0"
}
}
more gulpfile.js
var gulp = require("gulp");
var print = require('gulp-print');
var sourcemaps = require("gulp-sourcemaps");
var babel = require("gulp-babel");
var concat = require("gulp-concat");
const paths = {
src: 'src/**/*js',
dest: 'build'
}
gulp.task("default", function () {
return gulp.src(paths.src)
.pipe(print())
.pipe(sourcemaps.init())
.pipe(babel({ presets: ['react', 'es2015', ]}))
.pipe(concat("bundle.js"))
.pipe(sourcemaps.write("."))
.pipe(gulp.dest("dist"));
});
more src/test.js
// This import statement is what I'm trying to get working.
import { square } from './lib';
var profile = <div>
<img src="avatar.png" className="profile" />
<h3>{[user.firstName, user.lastName].join(' ')}</h3>
</div>;
more src/lib.js
// This is just a example function that I want to try to import
export function square(x) {
return x * x;
}
more index.html
<script src="dist/bundle.js"></script>
TEST
Build steps
npm install
./node_modules/.bin/gulp
You can use either webpack or browserify to build your bundle, but they'll both be leveraging babel to provide ES6 support. I am not sure where you read that Babel 6 removed the import statement - I use Babel 6 and have had no such issue. You can build the bundle with gulp too but I find it's more work and tends to be harder to maintain. But, you might be getting a lot of opinionated answers here.
There is a tool that was provided by Facebook recently to bootstrap a React app without having to configure build tools: Create React App. You might want to either try that or one of the available boilerplate starters on Github, unless you like tinkering with build scripts. It's less flexible but if you are looking to reduce the amount of configuration it does the job.

Resources