I have a Rails app that uses Webpack to bundle its assets. It doesn't currently use React.
In a separate repository, I have created a React app. This React app basically implements a complex custom UI element. The plan is that I can import this react component into my main application.
So far I have added the git repo to my package.json file and can see that the source code of my react app is being downloaded into the /node_modules folder.
I can get Webpack to bundle the app by adding:
To the React app package.json:
"prepare": "npm run build"
And in my main application webpack.config
module.exports = {
...
resolve: {
...
alias: {
MyReactApp: 'my-react-app/dist/bundle.js'
}
}
}
But it seems to be bundling all the libraries that my React app uses inside bundle.js and not adding them to the dependency tree.
It appears that the prepare command is basically bundling my React app into a /dist/bundle.js file and Webpack is simply including this file as-is. I need Webpack to manage the dependencies of my React app, such that I don't have unnecessary libraries duplicated in the final Webpack output.
Is there a better way to achieve what I am trying to achieve?
It is better to bundle them together. Basically, it is trying to bundle already bundled code.
This may work:
resolve: {
alias: {
MyReactApp: 'my-react-app/index.js'
}
}
index.js should be main jsx starting point.
Additionally, you need to add compile rules for jsx files, I never bundled Rail apps but React apps you should follow these steps;
Add rule to webpack.config.js
module: {
rules: [
{
// this is so that we can compile any React,
// ES6 and above into normal ES5 syntax
test: /\.(js|jsx)$/,
// we do not want anything from node_modules to be compiled
exclude: /node_modules/,
use: ["babel-loader"]
},
...
]
}
Install Babel modules:
npm install #babel/core #babel/node #babel/preset-env #babel/preset-react babel-loader
create a file called .babelrc and paste the following code
{
"presets": ["#babel/env", "#babel/react"],
"plugins": ["#babel/plugin-proposal-class-properties"]
}
Run webpack though babel-node like following script
"webpack": "babel-node ./node_modules/webpack/bin/webpack"
This would be my approach to solve this problem. It will still import react to your project. But instead of adding the compiled project, it will add jsx files and will compile them during the bundling process.
Note:
babel/core this is used to compile ES6 and above into ES5
babel/node this is used so that we can import our plugins and packages inside the webpack.config.js rather than require them
(it’s just something that I like, and maybe you’ll like it too)
babel/preset-env this will determinate which transformations or plugins to use and polyfills (i.e it provides modern
functionality on older browsers that do not natively support it)
based on the browser matrix you want to support
babel/preset-react this is going to compile the React code into ES5 code
babel-loader this is a Webpack helper that transforms your JavaScript dependencies with Babel (i.e. will transform the import
statements into require ones)
Related
I am creating my own React component library. The package.json is using react and react-dom as peer dependencies because I only want to ship code for components only. This package will then be used in other React-based projects so I will provide React myself in those projects.
I've decided for adding StorybookJS in the package for two reasons
Help during development to preview my components
Create a static storybook site that I will host somewhere showcasing the components
My project structure:
package.json
src/
components/ // contains components
storybook/ // <-- not part of the package
index.ts // <-- this is entry point of the package
However this introduces a problem. Since StorybookJS requires react and react-dom to run, I would have to include them as dependencies of the library.
I will be using webpack to actually bundle only the component code so it can be distributed. Should I just use externals property in configuration to exclude react and react-dom from the bundle? I guess it would work but then the package.json would still list React as its dependencies (which I do not want).
What is the correct approach here?
I ended up putting react and react-dom into peerDependencies as that is correct approach in my opinion. Since that package file belongs to library itself (and not Storybook), it should correctly state its dependencies.
To avoid bundling React into my library bundle, I put it into externals webpack configuration like so:
externals: {
react: 'react',
'react-dom': 'react-dom',
},
This way, React or ReactDOM does not end up in the bundle. The library is React component library so it can't be used without React anyway and you should provide it.
The final step to make Storybook work, I added prepare script that installs peer dependencies when you install the package. That way React and ReactDOM are provided for Storybook application.
This is the prepare script in my package.json:
"scripts": {
"prepare": "install-peers && npm run build"
}
I used install-peers package to install React and ReactDOM. For newer version of NPM client, you will not need this package and running npm install should install peer dependencies as well.
I've got a problem with making a React Component npm package. In a component in this package I use an image (import MyPicture from 'path/image.png'). I wanted to export it in as a git npm package so I installed babel to transpile it and it failed on converting png to js so I installed webpack and used image-loader + babel-loader. Everything on npm start works but my IDE (Webstorm) doesn't see the paths after webpack builds. If I build it using babel, the IDE finds the correct paths, but the image isn't transpiled. Anyone got similar problem?
As a reusable component, you need to inline the asset. You need to use a plugin that will embed your image, and have to setup a rule in your webpack config. A common way to do this is with the url-loader, which would convert the image to base64.
{
test: /\.(jpg|png)$/,
use: {
loader: "url-loader",
options: {
limit: 25000,
},
},
},
Important to note that this will also increase your bundle size.
I've created a repository which contains a React app (created with create-react-app) and a components directory which contains a simple Material UI button. The folder structure is:
/components
/react-app
Both directories are set up to use Yarn 2, and are not in a workspace (as I'm trying to simulate projects in separate directories and simplify my real world scenario).
I build the components:
$ cd ~/components && yarn build
I then Yarn link the components to the React app:
$ cd ~/react-app & yarn link ../components -r -p
This results in a modification to package.json file in the react-app directory:
{
"name": "react-app",
...
"resolutions": {
"components": "portal:../components"
}
}
My App.tsx file looks like this:
import './App.css';
import { Button } from 'components';
import React from 'react';
function App() {
return (
<Button>Test</Button>
);
}
export default App;
However, when I run the React app using yarn start I get the following error:
./src/App.tsx
Module not found: Your application tried to access components, but it isn't declared in your dependencies; this makes the require call ambiguous and unsound.
I'm not sure what I'm doing wrong. If I add an explicit reference to the components directory within dependencies (which I don't believe I should have to do because I've already linked it) such as:
"dependencies": {
"components": "portal:../components"
}
Then I get the error:
./src/App.tsx
Module not found: You attempted to import ~/react-app/.yarn/$$virtual/components-virtual-de9a8055ab/2/components which falls outside of the project src/ directory. Relative imports outside of src/ are not supported.
Surely, I don't have to eject the app and find a way to bypass this error?
EDIT: According to the Yarn documentation "Webpack 5 will support PnP natively, but if you use Webpack 4 you'll need to add the pnp-webpack-plugin plugin yourself". At the time of writing, the latest version of create-react-app relies on v3.4.1 of react-scripts which in turn relies on Webpack 4. I therefore ejected my app to inspect the Webpack configuration, and it appears that this plugin is already installed. It's therefore not a CRA/Webpack issue. I also upgraded my version of Node from v10.16.0 to v12.16.3 to no avail.
TLDR; Add the package as a dependency then modify your React setup to import files outside of the /src directory.
In my case, it doesn't look like yarn link is doing anything other than adding a resolutions entry in package.json, which according to the documentation is only used to specify the version to resolve. Perhaps my understanding of Yarn link is wrong (even though it held up in v1).
To fix the issue I had to add the reference to dependencies in package.json (even though I'd already run yarn link):
"dependencies": {
"components": "portal:../components"
}
This caused the aforementioned You attempted to import components which falls outside of the project src/ directory error. To resolve this we either need to eject the React app and disable the ModuleScopePlugin in Webpack (therefore allowing the import of files outside the /src folder), or use craco with custom configuration. I've created yarn-eject and craco branches to demonstrate both.
It's not a particularly elegant solution, and I'm hoping someone can post a better alternative. I switched to Yarn 2 so that I could utilise the "Improved Peer Dependency Links" feature (so that I'm only relying on one version of react across my applications and shared components packages). I'd rather not have to eject my React app or use custom configuration if possible.
I am fiddling around with the following local drf-react setup. I'm really new to react and javascript overall and got absolutely no idea why I cannot import axios or any other node module for that matter in my react components, except for the modules that already shipped with the cookiecutter project itself. Is this related to the react-dev-utils..? I would like to use webpack, but I fail to set it up properly. My frontend docker container won't compose, telling me to install webpack-cli. Help is much appreciated.
https://github.com/moritz91/drf-react-app
You should run the command npm install inside your frontend folder:
Open the terminal
Find the frontend folder
Inside of it run the command npm install
This command will install the dependencies related to your package.json file, which is inside the frontend folder.
Inside your React files you will put the whole path to the node_modules folder.
The idea of using node_modules is to make it easier to control your dependencies in your project. You should consider again using webpack to handle these files from node_modules.
Wepack has a module called resolve which you have to fill with a list of directories and inside React components you don't need to use the whole path anymore, because Webpack will understand where to look:
// ALIAS
resolve: {
extensions: ['.js', '.jsx', '.scss'],
modules: [
'YOUR SOURCE FOLDER',
'YOUR NODE MODULES FOLDER',
'ANY OTHER FOLDER'
]
}
From the docs:
Tell webpack what directories should be searched when resolving modules.
The documentation: https://webpack.js.org/configuration/resolve/
Also, I have an example using Webpack + Bootstrap 4.
You can use to build your own Webpack config for React and Redux.
https://github.com/italoborges/webpack-bootstrap4-es6
I am currently learning react and I have an app using webpack, babel and react. It seems react has two ways of writing it using either required or import. It also seems there is a lot more documentation on using import. How can I change my stack to use the import version?
The import and export statements are an ES6 standard. Right now, your setup is likely using Babel to transpile this into ES5. You can use one or the other, but import/export will soon become the standard, so adopting it is advised.
import is ES6 (or ES2015) standard. To use it, you need to install and activate the preset in babel.
Follow these steps:
Go to your project folder then type this: npm install --save-dev babel-cli babel-preset-env
Create a file named .babelrc (in case you have not created one) and insert this lines:
{
'presets': ['env', 'react']
}
I assume you have setup the webpack to work with babel.