I'm trying to configure Storybook to run from a directory that is not the root of the project and I'm having a little trouble. I've setup a mono-rep using https://github.com/jibin2706/cra-monorepo-demo as base.
My project directory looks like this:
- project
-- packages
---- app
---- components
---- utils
---- stories
------ .storybook
-------- main.js
------ ComponentA
-------- ComponentA.stories.mdx
Because I'm using a monorep with aliases (e.g. a component can import from #project/utils) I've configured webpack in .storybook/main.js to read like:
const path = require('path');
module.exports = {
stories: ['../**/*.stories.mdx', '../../**/*.stories.#(js|jsx|ts|tsx)'],
addons: [
'#storybook/addon-links',
'#storybook/addon-essentials',
'#storybook/preset-create-react-app',
],
webpackFinal: async (config, { configType }) => {
const result = {
...config,
resolve: {
...config.resolve,
alias: {
...config.resolve.alias,
'#project/components': path.resolve(
process.cwd(),
'packages/components'
),
},
},
};
},
};
Then within my ComponentA.stories.mdx I have an import like import { ComponentA } from '#project/components';
When I run this however, I'm always hitting an error when it encounters JSX within a .js file:
ERROR in ./packages/components/MyComponent1/MyComponent1.js 106:11
Module parse failed: Unexpected token (106:11)
File was processed with these loaders:
./node_modules/#pmmmwh/react-refresh-webpack-plugin/loader/index.js
You may need an additional loader to handle the result of these loaders.
|
return <React.Fragment>{children}</React.Fragment>;
I can't seem to work out why this error is throwing. I've tried running with yarn storybook --debug-webpack which seems to include a loader for both jsx and js files. I'm not 100% sure if this is correct, but it looks roughly right from other docs I've read.
module: {
rules: [
{
test: /\.(mjs|tsx?|jsx?)$/,
use: [
{
loader: '/home/ian/src/cra-monorepo-demo/node_modules/babel-loader/lib/index.js',
options: {
sourceType: 'unambiguous',
presets: [
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/preset-env/lib/index.js',
{ shippedProposals: true, loose: true }
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/preset-typescript/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/preset-react/lib/index.js'
],
plugins: [
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-shorthand-properties/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-block-scoping/lib/index.js',
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-decorators/lib/index.js',
{ legacy: true }
],
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-class-properties/lib/index.js',
{ loose: true }
],
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-private-methods/lib/index.js',
{ loose: true }
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-export-default-from/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-syntax-dynamic-import/lib/index.js',
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-object-rest-spread/lib/index.js',
{ loose: true, useBuiltIns: true }
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-classes/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-arrow-functions/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-parameters/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-destructuring/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-spread/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-for-of/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#storybook/core-common/node_modules/babel-plugin-macros/dist/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-optional-chaining/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-nullish-coalescing-operator/lib/index.js',
[
'/home/ian/src/cra-monorepo-demo/node_modules/babel-plugin-polyfill-corejs3/lib/index.js',
{
method: 'usage-global',
absoluteImports: '/home/ian/src/cra-monorepo-demo/node_modules/core-js/index.js',
version: '3.16.1'
}
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-template-literals/lib/index.js'
]
}
}
],
include: [ '/home/ian/src/cra-monorepo-demo' ],
exclude: [ /node_modules/, /dist/ ]
},
{
test: /\.js$/,
use: [
{
loader: '/home/ian/src/cra-monorepo-demo/node_modules/babel-loader/lib/index.js',
options: {
sourceType: 'unambiguous',
presets: [
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/preset-env/lib/index.js',
{
shippedProposals: true,
modules: false,
loose: true,
targets: 'defaults'
}
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/preset-react/lib/index.js'
],
plugins: [
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-shorthand-properties/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-block-scoping/lib/index.js',
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-decorators/lib/index.js',
{ legacy: true }
],
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-class-properties/lib/index.js',
{ loose: true }
],
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-private-methods/lib/index.js',
{ loose: true }
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-export-default-from/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-syntax-dynamic-import/lib/index.js',
[
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-object-rest-spread/lib/index.js',
{ loose: true, useBuiltIns: true }
],
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-classes/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-arrow-functions/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-parameters/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-destructuring/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-spread/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-transform-for-of/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#storybook/core-common/node_modules/babel-plugin-macros/dist/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-optional-chaining/lib/index.js',
'/home/ian/src/cra-monorepo-demo/node_modules/#babel/plugin-proposal-nullish-coalescing-operator/lib/index.js',
[
'/home/ian/src/cra-monorepo-demo/node_modules/babel-plugin-polyfill-corejs3/lib/index.js',
{
method: 'usage-global',
absoluteImports: '/home/ian/src/cra-monorepo-demo/node_modules/core-js/index.js',
version: '3.16.1'
}
]
]
}
}
],
include: [Function: include]
},
...
Can anyone see what I might be missing here, or what additional config is required?
The issue is probably with the Storybook project root. The default babel-loader defines an include that is equal to the project root. And the "project root" is usually the closest .git folder.
A workaround is to set the correct project root:
const path = require("path");
module.exports = {
// ...
webpackFinal: async (config, { configType }) => {
const babelLoaderRule = config.module.rules.find(
(rule) => rule.test.toString() === /\.(mjs|tsx?|jsx?)$/.toString()
);
// set correct project root
babelLoaderRule.include = [path.resolve(__dirname, "../..")];
return config;
}
};
What "correct" path is, depends on your setup.
Check my post for a longer write-up.
As far as I can tell your .storybook/main.js looks fine.
The built-in loader should also work as expected.
Concerning the error you're seeing: have you tried changing the file-type (aka renaming the file ending) from .js to .jsx?
Because the loader can discern between .js and .jsx files, however the interpreter cannot comprehend jsx-notation if it's not explicitely told to do so (as is the case with your .js files).
Related
I have a React component library that is built by webpack 5.75.0. The library exports 2 components:
// src/index.ts
export { default as ComponentA } from "components/ComponentA";
export { default as ComponentB } from "components/ComponentB";
A simple CRA app uses this library and imports only ComponentA. But when analyzing the bundle I see that ComponentB's dependencies are in it and have not been tree-shaken.
The parent app is just a CRA app with no config overrides. Tree shaking works there for other modules (MUI, lodash-es, etc), so the problem is with the my library.
I seem to have followed all requirements: in my package.json I do have
"sideEffects": false,
"main": "lib/index.js",
"module": "lib/index.js",
Here's my babel config:
module.exports = {
presets: [
["#babel/preset-env", { targets: { node: "current", modules: false } }],
"#babel/preset-typescript",
[
"#babel/preset-react",
{
runtime: "automatic",
},
],
],
plugins: [
"#babel/plugin-syntax-jsx",
[
"#babel/plugin-transform-runtime",
{
regenerator: true,
useESModules: true,
},
],
],
};
And here's my webpack config:
const config = {
mode: "production",
devtool: false,
entry: "./src/index.ts",
output: {
path: path.resolve(__dirname, "lib"),
filename: "index.js",
module: true,
library: {
type: "module",
},
},
plugins: [
//...
],
module: {
rules: [
//...
],
},
resolve: {
//...
},
externals: [
"react",
"react-dom",
],
experiments: {
outputModule: true,
},
optimization: {
minimize: false,
},
};
But still, the ComponentB's dependencies are showing up in the bundle of the CRA app that uses this library.
Am I missing something? Is there some official example of how to configure tree-shaking library with webpack 5?
Thanks.
P.S. My question relates to this question, but since 2021 it's become possible to build ES modules with Webpack 5.
I use rollup to create multiple libs, rollup config file as below:
import terser from '#rollup/plugin-terser';
import { nodeResolve } from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import { babel } from '#rollup/plugin-babel';
import eslint from '#rollup/plugin-eslint';
const distDir = "dist/libs"
const api = {
input: "src/libs/api/index.js",
external: ["axios"],
output: [
{
file: `${distDir}/api.umd.js`,
format: "umd",
name: 'Api',
globals: {axios: 'axios'}
},
{
file: `${distDir}/api.es.js`,
format: "es"
}
],
plugins: [
terser(),
babel({babelHelpers: 'runtime', exclude: 'node_modules/**'}),
nodeResolve({
browser:true,
}),
commonjs(),
eslint({
throwOnError: true,
throwOnWarning: true,
include: ['src/**'],
exclude: ['node_modules/**']
})
]
}
const test = {
...
}
export default ()=>{
return [
api, // it contains some polyfill
test // it also contains some polyfill
]
};
My .babelrc is:
{
"presets": [
["#babel/preset-env", {
"corejs": {"version": "3", "proposals": false},
"useBuiltIns": "usage"
}
]
],
"plugins": [
[
"#babel/plugin-transform-runtime"
]
]
}
Now, api and test contain some polyfill. Is there a way to extract the used polyfills of all modules to form a single umd file but not Full Function polyfill.Thank you.
So I'm trying to require dynamically from my node application a dist asset. And I'm getting the following error:
{"applicationLog":{"severity":"ERROR","timestamp":"2022-08-05T06:54:57.275Z","exception":{"type":"Error","message":"Cannot find module '/Users/lekoma/evelin/product-listing-fragment/dist/modern/fragment.js'","stack":"Error: Cannot find module '/Users/lekoma/evelin/product-listing-fragment/dist/modern/fragment.js'\n at webpackEmptyContext (webpack://product-listing-fragment/./node_modules/#jsmdg/react-fragment-scripts/server/_sync?:2:10)\n at getFragment (webpack://product-listing-fragment/./node_modules/#jsmdg/react-fragment-scripts/server/fragmentSsrRouter.js?:84:98)\n at createFragmentSsrRouter (webpack://product-listing-fragment/./node_modules/#jsmdg/react-fragment-scripts/server/fragmentSsrRouter.js?:121:20)\n at eval (webpack://product-listing-fragment/./node_modules/#jsmdg/react-fragment-scripts/server/createServer.js?:142:5)\n at Plugin.exec (/Users/lekoma/evelin/product-listing-fragment/node_modules/avvio/plugin.js:132:19)\n at Boot.loadPlugin (/Users/lekoma/evelin/product-listing-fragment/node_modules/avvio/plugin.js:274:10)\n at processTicksAndRejections (node:internal/process/task_queues:83:21)","code":"MODULE_NOT_FOUND"},"message":"Could not read fragment entry point"}}
The dist folder structure looks like following:
/dist/modern/fragment.js
The output of the node generated code is in a different folder:
build/server/index.js
Do you know who I could achieve to read dynamically from the dist folder?
source code node index.js
function getFragment(fragmentAssets) {
try {
const { Fragment } = require(path.join(
paths.clientOutputDirectory,
MODERN_BROWSER_STAGE || 'modern',
getFragmentEntryPointFromAssets(fragmentAssets), // the resolving path is correct, but the module could not be found/interpreted
));
return Fragment;
} catch (error) {
unpreparedLogger.withException(error).error('Could not read fragment entry point');
process.exit(1);
}
}
webpack config
module.exports = function webpackServerConfig() {
return {
target: 'node', // in order to ignore built-in modules like path, fs, etc.
mode: 'development',
externalsPresets: { node: true }, // in order to ignore built-in modules like path, fs, etc
externals: [
nodeExternals({
allowlist: [/#jsmdg\/react-fragment-scripts\/.*/, /#jsmdg\/yoshi\/.*/]
})], // in order to ignore all modules in node_modules folder
entry: {
server: {
import: paths.serverEntry,
filename: 'server/index.js',
},
shared: {
import: paths.sharedEntry,
filename: 'shared/index.js',
},
},
resolve: {
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypescript || !ext.includes('ts')),
},
output: {
path: paths.BFFOutputDirectory,
clean: true,
},
optimization: {
minimize: isEnvProduction,
minimizer: [new TerserPlugin()],
},
module: {
strictExportPresence: true,
rules: [
{
oneOf: [
{
test: /\.(js|jsx|ts|tsx)$/,
include: [
paths.clientSourceDirectory,
paths.sharedSourceDirectory,
paths.serverSourceDirectory,
/node_modules\/(#jsmdg\/react-fragment-scripts|#jsmdg\/yoshi)\/.*/,
],
exclude: [
isEnvProduction ? /__mock/ : undefined,
].filter(Boolean),
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
presets: [[require.resolve('../babel/server')]],
},
},
],
},
],
},
};
};
module.exports = {
presets: [
'#vue/app'
],
module:{
rules: [
{
test: /\.vue$/,
exclude: [
'./src/components/Homepages/number1',
'./src/components/Homepages/number2'
]
}
]
}
}
I am trying to include just one of 'Homepages/number1', 'Homepages/number2', 'Homepages/number3' folders in my project conditionally and others shoud be excluded from the project when running npm run build to decrease my dist folder size. I am trying this code but I am sure its not the right solution. I am doing this config in babel.config.js.
One option is to use the Copy plugin:
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{
from: path.posix.join(
path.resolve(__dirname, "src").replace(/\\/g, "/"),
"**/*"
),
globOptions: {
ignore: [
// Ignore all `txt` files
"**/*.txt",
// Ignore all files in all subdirectories
"**/subdir/**",
],
},
},
],
}),
],
};
I'm using the following webpack.config.js file to build two CSS files (editor.css and style.css) and a JS file (block.build.js) making use of the mini-css-extract-plugin plugin:
// Load webpack for use of certain webpack tools and methods
const webpack = require( 'webpack' );
// For extracting CSS (and SASS) into separate files
const MiniCssExtractPlugin = require( 'mini-css-extract-plugin' );
// Define JavaScript entry points
const entryPointNames = [ 'blocks', 'frontend' ];
// Setup externals
const externals = {};
// Setup external for each entry point
entryPointNames.forEach( entryPointName => {
externals[ '#/lg6' + entryPointName ] = {
this: [ 'lg6', entryPointName ]
}
} );
// Define WordPress dependencies
const wpDependencies = [ 'components', 'element', 'blocks', 'utils', 'date' ];
// Setup externals for all WordPress dependencies
wpDependencies.forEach( wpDependency => {
externals[ '#wordpress/' + wpDependency ] = {
this: [ 'wp', wpDependency ]
};
});
// Start of main webpack config
const config = {
// Set mode
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
// Go through each entry point and prepare for use with externals
entry: {
index: './index.js',
style: './style.scss',
editor: './editor.scss',
},
// Include externals
externals,
// Set output
output: {
// Place all bundles JS in current directory
filename: 'block.build.js',
path: __dirname,
library: [ 'pluginnamespace', '[name]' ],
libraryTarget: 'this'
},
// Fall back to node_modules for file resolution
resolve: {
modules: [ __dirname, 'node_modules' ]
},
optimization: {
splitChunks: {
cacheGroups: {
editor: {
name: 'editor',
test: /editor\.(sc|sa|c)ss$/,
chunks: 'all',
enforce: true,
},
style: {
name: 'style',
test: /style\.(sc|sa|c)ss$/,
chunks: 'all',
enforce: true,
},
default: false,
},
},
},
module: {
rules: [
{
// Run JavaScript files through Babel
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader',
},
{
// Setup SASS (and CSS) to be extracted
test: /\.(sc|sa|c)ss$/,
exclude: /node_modules/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
sourceMap: process.env.NODE_ENV !== 'production',
},
},
{
loader: 'postcss-loader',
options: {
plugins: [ require( 'autoprefixer' ) ]
}
},
{
loader: 'sass-loader',
options: {
sourceMap: process.env.NODE_ENV !== 'production',
},
},
],
},
]
},
plugins: [
// Setup environment conditions
new webpack.DefinePlugin( {
'process.env.NODE_ENV': JSON.stringify(
process.env.NODE_ENV || 'development'
)
} ),
new MiniCssExtractPlugin( {
filename: './css/[name].css',
} ),
// For migrations from webpack 1 to webpack 2+
new webpack.LoaderOptionsPlugin( {
minimize: process.env.NODE_ENV === 'production',
debug: process.env.NODE_ENV !== 'production',
} )
],
// Do not include information about children in stats
stats: {
children: false
}
};
module.exports = config;
Everything is working as expected but, for some reason, in addition to the block.build.js file, I'm getting two more JS files named 0.block.build.js and 2.block.build.js with the following content:
(window.webpackJsonp=window.webpackJsonp||[]).push([[0],[,function(n,w,o){}]]);
My question is, why are these two additional files are being created and how can I avoid this?
Thanks in advance
You should remove these 2 line
style: './style.scss',
editor: './editor.scss',
Also you can import those 2 scss file in your index.js
import "style.scss";
import "editor.scss";
And mini-css-extract-plugin will take care the rest for you
As an alternative, if you don't want to import the scss files in your js files, I found you can use a webpack plugin such as Ignore Emit Webpack in your webpack.config.js file to prevent the creation of the extra js files:
const IgnoreEmitPlugin = require('ignore-emit-webpack-plugin');
module.exports = {
// ...
plugins: [
new IgnoreEmitPlugin(['0.block.build.js', '2.block.build.js'])
]
// ...
};