Can I have multiple babel.config.js in a monorepo? - reactjs

I have a monorepo with the structure like below
babel.config.js
packages/
|---mobile/
|----package.json
|----src/index.js
|---desktop/
|----package.json
|----src/index.js
|---server/
|----package.json
|----src/index.js
So my babel configuration for mobile and desktop packages are same, whereas configuration for serverpackage is different.
Now, how can I have that configuration done? One solution, that I can think of is that to have a babel.config.js at the root of monorepo which would have configurations for mobile and desktop packages and a separate configuration for server package in a babel.config.js at server package level. I am not sure, can we even have multiple babel.config.js.

Yes, you can achieve what you asked by having multiple config files. It is mentioned as File-relative configuration in babel documentation.
Create a .babelrc or .babelrc.js file in each of your directories (mobile, desktop, server)
For more details look at https://babeljs.io/docs/en/next/config-files

Personally, I think using separate files would lead to confusion. Assuming you've set up your system in a way that works already and you're just asking how to specify different configs for different locations, you can use the "overrides" option. For example, your config can do
module.exports = {
overrides: [{
test: [
'./desktop',
'./mobile',
],
// put all your normal babel options for these folders here
}, {
test: [
'./server',
],
// put all your normal babel options for the server here
}],
};

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>;
|

Problem configuring nvim-lspconfig, ESLint and Typescript path aliases correctly?

I have a react monorepo project with a number of aliases (typescript paths) setup which makes importing files easier without the need to use relative paths everywhere.
For example if I have a component in src/components/Sidebar/Toggle.jsx and I want to import that anywhere in the application I can just do import {Toggle} from '#company/components/Sidebar/Toggle' and there’s no need to do any relative path importing like ../../../Toggle.
Company is just an example alias to the src directory setup in tsconfig.json like:
"paths": {
"#company/*": ["./src/*"]
},
This works fine in vscode but in neovim (I’m using nvim-lspconfig with eslint) all exported functions which are imported using the alias have a warning
Exported declaration not used within other modules
even though they are.
If I import them using relative paths it works without warning.
Does anyone have a suggestion as what config I need to change so that neovim can see that these functions are in fact used in other files?
I've tried adding config in .eslintrc.json like this as suggested by https://www.npmjs.com/package/eslint-import-resolver-typescript but this did not solve it.
settings: {
'import/resolver': {
typescript: {
project: ['packages/*/tsconfig.json'],
},
},
}
I should also note that running eslint directly on the file with my current configuration works fine with no errors so this is somehow related to the neovim plugin.
With a bit more debugging I can see that the eslint plugin doesn't seem to be using the correct configuration file as it's root. There is an .eslintrc.js file in a sub folder but the main .eslintrc.js file lives higher up in the directory tree. The plugin seems to find the first .eslintrc.js and use that as the root file.
This seems to have turned out to be related to the eslint plugin in nvim-lsp. More here https://github.com/neovim/nvim-lspconfig/issues/2400

How to make a path alias in CRA TypeScript in 2022?

I just initiated CRA npx create-react-app my-app --template typescript and I want to make an alias when calling components, like:
import components from '#components'
where the components is located at src/components.
I've tried to config in tsconfig.json by adding:
{
"compilerOptions": {
...
"baseUrl": "./src",
"paths": {
"#utils/": ["./utils/"],
"#utils/*": ["./utils/*"]
}
}
}
Also in webpack.config.js by adding:
// const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin')
const path = require('path')
module.exports = {
resolve: {
// plugins: [new TsconfigPathsPlugin()],
alias: {
'#utils': path.resolve(__dirname, './src/utils/'),
'#utils/*': path.resolve(__dirname, './src/utils/*')
}
}
}
But it's still doesn't work.
Anyone could help me to solving these problem? But, I don't wont to use other libraries like #craco/craco.
The issue is that CRA uses its own Webpack config under the hood. Simply making a new webpack.config file doesn't actually point CRA to it, unless you run npm run eject.
Doing so is irreversible, but will add the config files to your project. From there, you should be able to modify your build settings to fit your needs.
Reminder that this cannot be undone in your project, barring perhaps a git reset, and may be more than you bargained for.
This issue with aliases seems to be a known one. Something people deemed possible earlier seems to no longer be working, or supported. Some people are speculating this could have something to do with the recent update of Webpack to version 5. And while some people claim that craco doesn't work for them, I was able to get it to work in a brand new CRA app with minimal changes. I know you're not interested in that so I won't post it here.
Alternatively, CRA allows the use of absolute imports via the src baseUrl. This will point both VSCode and Webpack to your final files, but you won't be able to set up custom paths.
"baseUrl" : "."
Using multiple index.ts files and exporting nested code up to the highest level in the directory, I'm able to keep the import paths as short as an alias:
import { persistor, store } from "src/state-management";
This could be good enough for you. If not, consider adding a package to override some of CRA's Webpack settings, or ejecting and taking matters into your own hands.

Plugin system for apps compiled using Webpack

For the context: I'm developing my own product using Symfony on the back-end and react/react-router on the front-end, which is tied together by Webpack. I'm planning to divide my app into "extensions", so I would have "core" bundle and multiple different extending bundles around it (which would be sets of additional features for my product).
Now, I would like for my front-end to be as extensible as my back-end. I would like to be able to add new React components with my extending bundles to the existing "core" set of components in my "CoreBundle".
However, it seems like the Webpack is encapsulating everything too tightly to be able to produce that kind of a plugin system. Is it possible to have multiple bundles that would have separate Webpack configurations, but their JavaScript would be interconnected in a way that would allow for developing of a plugin system? The goal is being able to develop JS of one Bundle independently but at the same time being able to use some already compiled JS resources from another Bundle in the process.
I think you should be able to achieve this using the DllPlugin and the DllReferencePlugin
The DllPlugin is used in a separate webpack config to create a dll
only bundle. It also creates a manifest.json file which is used by the
DllReferencePlugin to map dependencies.
Refer to the detailed documentation at
https://webpack.js.org/plugins/dll-plugin/
In my case, I use this to combine all vendor libraries (React, Flux, etc) in one build and then use that as a reference in my Other Webpack Config which bundles all my React components etc. but references React and other libraries using the DllReferencePlugin.
My webpack.dll.js config file:
var path = require("path");
var webpack = require("webpack");
module.exports = {
entry: {
libs: [path.join(__dirname, "common", "lib.js")]
},
output: {
path: path.join(__dirname, "dist", "dll"),
filename: "[name].dll.js",
library: "[name]"
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, "dll", "[name]-manifest.json"),
name: "[name]",
context: path.resolve(__dirname, "common")
}),
]
};
And then in my main webpack.config.js, I use the reference plugin.
new webpack.DllReferencePlugin({
context: path.resolve(__dirname, "common"),
manifest:require('./dll/libs-manifest.json')
})
Depending upon how you want to split your code, you can create multiple Dlls, each with a separate webpack config as per your requirements. And then refer the dll's as per your requirements in different other webpack bundles.

Webpack: difference about entry between production and development environment

I'm reading tutorial about Webpack on this: Github Webpack tutorial In this, there is a section about config webpack for production and development.
Here is development configuration:
// webpack.config.dev.js
module.exports = {
devtool: 'cheap-eval-source-map',
entry: [
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/dev-server',
'./src/index'
],
Here is production configuration:
// webpack.config.prod.js
module.exports = {
devtool: 'source-map',
entry: ['./src/index'],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
I understand the difference in option of devtool. The thing I don't understand about entry. Why in production, entry is only about src/index but in development configuration, entry also includes webpack-dev-server
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/dev-server',
'./src/index'
The lines 'webpack-dev-server/client?http://localhost:8080' and 'webpack/hot/dev-server' are configuring/defining which port to attach an active websocket to, in this case localhost:8080, and the content base which in this case is folder/path /client. In a production environment you would never run webpack-dev-server as your bundled client assets (bundle.js or similar) would be served by a server (IIS, Node, etc), that is why there are no webpack related items in entry of the production configuration.
The Webpack plugin in question webpack-dev-server is not required to run Webpack and compile your JS sources, it simply is a tool that can be used during the development process to watch for changes and reload changes.
Technically the entry array property in development could simply be the './src/index', but then it wouldn't enable the webpack-dev-server and/or it's hot module reloading. If you wanted to run webpack-dev-server without these configuration items then you'd then need to add command line arguments when starting webpack to specify the port and/or content base.
Hopefully that helps!
Here is the 2 things you should know before understanding:
As your linked in Webpack the confusing part, there are 3 types of entry: String Array and Object. As above code, that is array type. Meaning of entry array is: Webpack will merged all those javascript files in array together. This is often unnecessary because Webpack is intelligent enough to know which javascript files need to merge while processing. You often need to do this to enhance some features from different javascript files that you don't include somewhere else in your code.
This is "little tricky" part. You see webpack/hot/dev-serverand webpack-dev-server/client?http://localhost:8080 look like a web url rather than some javascript files, right? If you check your project directory, you see there are those files: your_app_directory/node_modules/webpack/hot/dev-server.js and your_app_directory/node_modules/webpack-dev-server/client.js. And that is the real meaning: you are importing two javascript files from two modules webpack-dev-server and webpack.
Back again to your webpack configuration:
entry: [
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/dev-server',
'./src/index'
],
That means we will merge three different javascript files together as point 2 I have figured out. As I explain in point 1, you will do this for enhancing some features. You include file webpack-dev-server/client.js for making a server for serving your code. You include file webpack/hot/dev-server.js for allowing your code autoloading. This is super useful when you in development mode without start/stop server each time you modify your code.

Resources