Resolve module dependencies on remotely loaded React component - reactjs

TL;DR
I wish if there is a right way to resolve common module dependencies with Webpack on remotely loaded React component as shown at the following Gist: https://gist.github.com/tonytonyjan/616022dce75f8e6c1603bbeb94ec46a4
Details
It may seem uncommon, but we won't have access to all React components at build time.
We gonna have plugins that would be a Webpack bundled React components, deployed in some accessible endpoint.
And our webapp will display a page of available plugins, loaded at runtime.
The above Gist shows how to remotely load React component, but it resolves external dependencies by manually mapping modules on require function:
function require(name){
if(name == 'react') return React
else throw `You can't use modules other than "react" in remote component.`
}
I wish to know if there is a better way to graciously resolve all common external dependencies of remote components (asynchronously or not), without doing this manual mapping trick (some Webpack config).
For instance, plugin webpack.config.js would be something like below
module.exports = {
entry: {
PluginView: 'src/PluginView.js'
},
target: 'web',
output: {
path: path.join(__dirname, outputPath),
filename: 'PluginView.bundle.js',
libraryTarget: 'commonjs2'
},
module: {
loaders: [
{
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
}
}
]
},
externals: {
'react': 'React',
'otherThirdPartyLib': 'OtherThirdPartyLib' // any other common lib already available at our webapp
}
};
Any thoughts will be appreciated.
Thanks in advance.

Related

How to use google fonts in manually created React app?

I created React app manually (not create-react-app) one by one such as index.js, App.js, index.css, components folder etc because I am using React app as a separate app in the Django project.
And in order to use google fonts, I followed this answer.
But when I write #import url('https://fonts.googleapis.com/css?family=Josefin+Sans') in the index.css, it is giving me this error.
ERROR in ./src/index.css 1:0
Module parse failed: Unexpected character '#' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> #import url('https://fonts.googleapis.com/css?family=Josefin+Sans');
|
# ./src/index.js 4:0-21
webpack 5.15.0 compiled with 1 error in 999 ms
I think this error is related to webpack.config.js and it seems I need to add some rules in the module section related to css-loader, file-loader, or something else, but I am not sure how to write.
This is just my thought, I have no idea why this is happening.
webpack.config.js
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "./static/frontend"),
filename: "[name].js",
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
},
optimization: {
minimize: true,
},
plugins: [
new webpack.DefinePlugin({
"process.env" : {
NODE_ENV: JSON.stringify("production"),
}
})
]
};
How to solve this issue? (This issue has happened when I tried to use react-toastify also. So I removed it and used another alternative package.) How to use fonts in the non create-react-app based project?

sharing react between microfrontend bundles without CDN

I am building an app using a microfrontends architecture with the Single-Spa framework.
I have:
A root config web app: Defines the layout of the apps and includes each microfrontend as an NPM dependency
N Microfrontends: React apps which are loaded into the root config app.
Because each microfrontend is using React, I don't want to bundle it everytime. Instead I want to have the root config app specify React as a dependency and then it can be loaded by each microfrontend.
I have declared react and react-dom as webpack externals so they are not bundled but I think this will only work if I include React from CDN in my index.html of the root config app. Is there a way to configure webpack so that it will include React in the root config and make it available to every microfrontend? I can't use public CDNs in my firm.
Thanks!
In the "Getting Started: Quick start" guide, we cover how to add shared dependencies, and the example specifically shows adding React and ReactDOM to the import map.
You should not try to bundle React into the root-config, since modules bundled into an application by Webpack are namespaced and not shared globally. Instead, per your requirement, you can host those React files in your own CDN or even serve them locally though the root-config because ultimately there are static files. The location of where those files are loaded from is not relevant to SystemJS as long as it can load them. This means you can go as simple as copying those files locally and serving them from the root-config server, to as complicated as setting up your own unpkg server.
Yes, we can do that not just for the React Package but with all the dependencies and share them between all the micro-frontend apps.
Webpack 5, provides the support of ModuleFederationPlugin in which you can define the list of packages. Example:-
Import Package.json in the Webpack configuration and add following plugin
const packageJson = require('../package.json');
{
plugins: [
new ModuleFederationPlugin({
name: <AppName>,
filename: 'remoteEntry.js',
exposes: {
'./App': <PathToFile>,
},
shared: {
...packageJson.dependencies,
}
}),
],
}
You can explicitly define only the packages name which you want to share.
Check out more about ModuleFederationPlugin
If you want to have flexibility in micro frontend, that is, if you want each micro frontend to be deployed independently of each other, you can use client-side intgration.
React
You can use a CDN for static files. (image, fonts, pdf, doc) Webpack module federation can be used for client-side integration.
Configuring an example webpack and module federation.
We divide the webpack config files into 3 parts. for common, development, production
webpack.common.js
module.exports = {
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-react', '#babel/preset-env'],
plugins: ['#babel/plugin-transform-runtime'],
},
},
},
],
},
};
webpack.dev.js
const { merge } = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const commonConfig = require('./webpack.common');
const packageJson = require('../package.json');
const devConfig = {
mode: 'development',
output: {
publicPath: 'http://localhost:8082/',
},
devServer: {
port: 8082,
historyApiFallback: {
index: 'index.html',
},
},
plugins: [
new ModuleFederationPlugin({
name: 'auth',
filename: 'remoteEntry.js',
exposes: {
'./AuthApp': './src/bootstrap',
},
shared: packageJson.dependencies,
}),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
};
module.exports = merge(commonConfig, devConfig);

Webpack+SemanticUI+React: process is not defined

I have found numerous posts about the Webpack error:
Uncaught ReferenceError: process is not defined
most of which suggest adding a plugin to the webpack.config.js:
plugins: [
// ...
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development')
}
}),
// ...
]
however this does not seem to do the trick in my case.
To make things easy, I have created a repo with the bare minimum to setup SemanticUI-React with Webpack, which should be straightforward to navigate. My config in webpack.config.js is heavily inspired from this recent tutorial which seems to have a lot of positive comments.
To reproduce the error, just clone the repo on your machine (I use yarn, but this should work with npm too):
git clone https://github.com/sheljohn/minimal-semantic-react
cd minimal-semantic-react/
yarn install
yarn run serve
which opens at localhost:3000, and the error can be seen in the developer console.
As far as I understand, it seems that when React loads, it is looking to determine whether production or development mode is set, using the variable process.env.NODE_ENV, which is undefined in the browser.
This might be related to the target field in the Webpack config (set to web by default); but since React is loaded from CDN, prior to the bundle, I guess it doesn't know about what WebPack is doing, which makes me perplex as to why adding a plugin in the config would change anything...
Hence my question: is it possible to use semantic-ui-react by declaring the big libs (React, ReactDOM, semantic) as externals? Everything works fine if I bundle them, but the bundle ends up around 4MB, which is quite big.
Additional Details
Error as seen in Chrome (OSX High Sierra, v66.0.3359.181, dev console):
react.development.js:14 Uncaught ReferenceError: process is not defined
at react.development.js:14
(anonymous) # react.development.js:14
and code excerpt at line 14:
if (process.env.NODE_ENV !== "production") {
File webpack.config.js
const path = require("path");
const webpack = require("webpack");
const publicFolder = path.resolve(__dirname, "public");
module.exports = {
entry: "./src/index.jsx",
target: "web",
output: {
path: publicFolder,
filename: "bundle.js"
},
devServer: {
contentBase: publicFolder,
port: 3000
},
externals: {
'jquery': 'jQuery',
'lodash': '_',
'react': 'React',
'react-dom': 'ReactDOM',
'semantic-ui-react': 'semantic-ui-react'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader'
}
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development')
}
}),
new webpack.HotModuleReplacementPlugin()
]
};
File .babelrc
{
"presets": ["env", "react"]
}
I think I finally solved this:
Mistake #1: I was using cjs versions of the React libs from cdnjs, when I should have been using umd instead. Although UMD style is ugly, it seems to work fine within browsers, whereas CommonJS uses require for example. See this post for a comparison of AMD / CommonJS / UMD.
Mistake #2: in webpack.config.js, the "name" for the external semantic-ui-react should be semanticUIReact (case sensitive). This is what is defined in the window global when the script is loaded from the CDN (e.g. like jQuery or React).
I updated the repository with these fixes, and you should be able to reproduce that working example on your machine. This repository contains the bare minimum needed to get SemanticUI, React and Webpack working together. This would have saved me a lot of time, so hopefully other people get to benefit from that!
Everything works fine if I bundle them, but the bundle ends up around 4MB, which is quite big.
It's because you bundle them in "development" mode. Try using "production" in your script instead, it will be much smaller.
"build": "webpack --mode production"
If you bundle everything in production, without specifying external, it will be better for a standalone app.

Module not found: Error: Cannot resolve module 'fs'

I'm making a react app using Babel and Webpack and I want to use the file-exists package from npm. I already installed and saved the package as a dependency for my project. After running npm start I get this error:
ERROR in ./~/file-exists/index.js
Module not found: Error: Cannot resolve module 'fs' in C:\GitHub\CryptoPrices\node_modules\file-exists
# ./~/file-exists/index.js 3:9-22
file-exists uses fs as a dependency but for some reason it is not working. Npm starts and runs without any issues if I don't require file-exists anywhere.
here is my webpack config file:
module.exports = {
entry: [
'./src/index.js'
],
output: {
path: __dirname,
publicPath: '/',
filename: 'bundle.js'
},
module: {
loaders: [{
// exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['react', 'es2015', 'stage-1']
}
}]
},
resolve: {
extensions: ['', '.js', '.jsx']
},
devServer: {
historyApiFallback: true,
contentBase: './'
}
};
I'm new to Webpack and Babel so I'm a little bit lost.
It looks like you're calling the fs file-exists method in your index.js file. I'm not sure in what context you're calling the method, but this looks like a client-side call to a server-side method. I ran into a similar issue just recently.
From what I understand, the main issue seems to be that you can't call server-side (Node) methods in browser-interpreted client-side (front end) code. You have to call them from a server.
Webpack can load the fs module code into your front end, but the browser can't actually interpret those Node methods and run them; only a Node environment can do that. (more here)
You could fix the core issue by modifying the call to fs methods to happen somewhere server-side, or by finding an equivalent browser-supported package that offers the same functionality as the fs methods you need but that can be run in a browser.
A quick search for "fs module for browser" brings up a variety of options that might work for what you need, like fs-web, browserify-fs or filer.
Here is a similar question with some insight.
Use fs module in React.js,node.js, webpack, babel,express
node: {
fs: 'empty'
}
try to add the code above to your webpack config file and the error should disappear.

Cannot resolve module 'react' when using npm link and webpack

I am writing an external module that I would like to require in my project (react-list)
my module is a react component and obviously I have this in top of my file
var React = require('react');
I'm using npm link, and in my project I'm getting an error from webpack that react doesn't exist inside the node_modules directory INSIDE the external module
ERROR in /web/react-list/dist/List/List.js
Module not found: Error: Cannot resolve module 'react' in /web/react-list/dist/List
why isn't it requiring the react component from my projects node_modules directory?
edit: added my webpack, had to redact the files and directories
'use strict';
module.exports = {
context: __dirname,
entry: {
....
},
output: {
path: '../public/js/pages',
filename: "[name].js"
},
devtool: 'source-map',
resolve: {
modulesDirectories: ["node_modules", ......],
extensions: ['', '.js', '.jsx']
},
plugins: [],
module: {
loaders: [
{
test: /.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ["react", "es2015-loose"]
}
}
]
}
};
edit 2: I found a workaround without using npm link, and simply installing the required package using npm install and then removing the dist directory and replacing it with a soft link on my own without npm's help. Obviously this isn't an ideal way to develop.
edit 3: I reach the dreaded refs must have owner issue https://facebook.github.io/react/warnings/refs-must-have-owner.html error if I try to install the dependencies on my package directory, so its either this package doesn't find a react directory or it does but with another react version and nothing works...

Resources