How to configure Storybook to run from a directory other than the project root - javascript

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

Library built with Webpack 5 not tree shakeable

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.

How to extract all used polyfill from multiple libs by rollup?

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.

webpack - cannot find module from dist bundle

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')]],
},
},
],
},
],
},
};
};

how can I exclude some folders containing .vue files from being built in vuejs?

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/**",
],
},
},
],
}),
],
};

mini-css-extract-plugin generates two additional JS files

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'])
]
// ...
};

Categories

Resources