Shipping a React application as a singular JS file - reactjs

I have an application created with create-react-app which needs to be shipped as a singular .js file, complete with its components and CSS style sheets. How can it be done? Without ejecting, if possible. The standard npm run build command only provides source code split into chunks.
Tried solving this issue with with react-rewire-app, but the build failed to complete.
My config-override.js is as follows:
const path = require('path');
module.exports = function override(config, env) {
config.entry = {
'my-app': './src/index.js'
}
config.module.output = {
path: path.resolve(__dirname, '../target/classes/static/'),
filename: '[name].js',
publicPath: '/'
}
return config
}
``

You're looking to make a widget or a library, likely, not an application, so CRA is the wrong tool. CRA is a tool for applications, where you definitely don't want everything bundled into a single file like you're asking for, as that would be very poor user experience.
For library/widget bundlers, I'd take a look at something like Microbundle: https://github.com/developit/microbundle. Should be very little config.
That being said, you cannot include CSS style sheets in JS. You can't even import them into JS. You need to ship them along with your JS and let your user handle importing it through HTML or what have you.

Related

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.

How to create multiple entry points(index.js) for a single react application?

I want to create two different builds for a single application. When I do an npm run build, it runs webpack and generates a build, and injects that build into the index.html file. What I ideally want to do is tell webpack to create one build from index.js and another build from index2.js. How do I achieve this? Looked into module federation, but wasn't able to figure out how to do this in a single application.
You can do this settings in webpack.config.js.
module.exports = {
entry: {
bundle1: "path for index1.js",
bundle2: "path for index2.js"
},
output: {
// `filename` provides a template for naming your bundles (remember to use `[name]`)
filename: '[name].js'
publicPath: 'Path for saving directory'
}};

Using components from an external directory in a Electron project with Webpack

I am trying to do this as simple as possible, I studied Yarn Workspaces for a while, but that's a solution that's currently doesn't work with Electron, there were simply too many issues.
I have am Electron project here: ./electron/
I have a directory with components here: ./common/
The components are developed in React/JSX, there is nothing really fancy about them. That said, I am using hooks (useXXX).
I tried many ways to include those components (ideally, I wanted to use Yarn Workspaces, but it only multiplied the number of issues), but they all failed. Which is why I would like to avoid using yarn link or workspaces or making the common a library, etc. I just want my Electron project to behave as if the files were under ./electron. That's it.
The closest I came to a solution is by using electron-webpack, and overriding it with this config:
module.exports = function(config) {
config = merge.smart(config, {
module: {
rules: [
{
test: /\.jsx?$/,
//include: /node_modules/,
include: Path.resolve(__dirname, '../common'),
loaders: ['react-hot-loader/webpack', 'babel-loader?presets[]=#babel/preset-react']
},
]
},
resolve: {
alias: {
'#common': Path.resolve(__dirname, '../common')
}
}
})
return config
}
I can import modules, and they work... except if I use hooks. And I am getting the "Invalid Hook Call Warning": https://reactjs.org/warnings/invalid-hook-call-warning.html.
I feel like that /common folder is not being compiled properly by babel, but the reality is that I have no idea where to look or what to try. I guess there is a solution for this, through that webpack config.
Thanks in advance for your help :)
I found the solution. That happens because the instance of React is different between /common and /electron.
The idea is to add an alias, like this:
'react': Path.resolve('./node_modules/react')
Of course, the same can be done for other modules which need to be exactly on the same instance. Don't hesitate to comment this if this answer it not perfectly right.
I wrestled more than a day with a similar problem. My project has a dependency on a module A that is itself bundled by Webpack (one that I authored myself). I externalised React from A (declaring it to be a commonjs2 module). This will exclude the React files from the library bundle.
My main program, running in the Electron Renderer process, uses React as well. I had Webpack include React into the bundle (no special configuration).
However, this produced the 'hooks' problem because of two instances of React in the runtime environment.
This is caused by these facts:
module A 'requires' React and this is resolved by the module system of Electron. So Electron takes React from node_modules;
the main program relies on the Webpack runtime to 'load' React from the bundle itself.
both Electron and the Webpack runtime have their own module cache...
My solution was to externalise React from the main program as well. This way, both the main program and module A get their React from Electron - a single instance in memory.
I tried any number of aliases, but that does not solve the problem as an alias only gives direction to the question of where to find the module code. It does nothing with respect to the problem of multiple module caches!
If you run into this problem with a module that you cannot control, find out if and how React is externalised. If it is not externalised, I think you cannot solve this problem in the context of Electron. If it is externalised as a global, put React into your .html file and make your main program depend on that as well.

Include only used imports in the packaged bundle ReactJS

I wanna use only one component from Material Ui library . I know i can import only one component using ES6 import but does webpack treeshake and remove other components from the library or include them in production .
Please help me.
Webpack from v2 onwards eliminates unused exports in two steps:
First, all ES6 module files are combined into a single bundle file in which exports that were not imported anywhere are not exported, anymore.
Second, the bundle is minified, while eliminating dead code. Therefore, entities that are neither exported nor used inside their modules do not appear in the minified bundle. Without the first step, dead code elimination would never remove exports.
Unused exports can only be reliably detected at build time if the module system has a static structure.
Webpack doesn't perform tree-shaking by itself. It relies on third party tools like UglifyJS to perform actual dead code elimination.
To do that, you would install it using
npm install --save-dev uglifyjs-webpack-plugin
And then adding it into the config:
webpack.config.js
const path = require('path');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new UglifyJSPlugin()
]
};
So when you add this config, your unused exports are not longer present in the minified build.
Webpack 5 comes with terser-webpack-plugin out of the box, hence you can just import it and configure as you wish.

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.

Resources