Output multiple React apps from one Webpack config - reactjs

I'm working on a web app that has a companion WordPress site. Let's assume domains like:
app.site.com
blog.site.com
What I'd like to do is have my webpack 4 configuration output the bundle.js but also generate something like header.js that includes a single <Header /> component (not the complete app).
Then I'd add a script tag to blog.site.com like so:
<script src="https://app.site.com/build/header.js" />`
and expect it to mount the app's <Header /> in the blog's #dom-element
How can this be achieved?

Create the another entry point for header component. Quoting from webpack docs:
module.exports = {
entry: {
bundle: './src/app.js',
header: './src/header.js'
},
output: {
filename: '[name].js',
path: __dirname + '/build'
}
};
This will create bundle.js from your inital entry point. And also header.js from the Header component.

Related

React app injecting into js instead of index.html

I would like to create a React app that will not be a standalone site, but rather an extension to an existing site. The site is built using asp.net and I cannot make edits to it. I do have an entry with javascript and can run code such as the following:
$(document).onload(() => {
$(body).parent().append(`
<script src='{...}'></script>
`);
});
Is there any way I can change the standard Index.html output from the default create-react-app to a .js file and have the react chunks added into the append? It does not necessarily need to follow the above direction, but it's as far as I got.
I believe you could just attach it to the body using ReactDOM.render
ReactDOM.render(
<React>
<App />
</React>,
document.getElementByTagName('body')
)
But you might have to switch away from create-react-app to using webpack, and then use something like HTMLWebpackPlugin within your webpack.config.js to specify the entry point
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
target: 'web',
mode: 'development',
entry: './src/index.jsx',
devtool: "source-map",
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
filename: "[name]/[name].js",
},
...
new HtmlWebpackPlugin({
template: './html/index.html',
filename: './index.html'
}),
...
}
You would have to install html-webpack-plugin as well as webpack and anything you need with npm install html-webpackplugin webpack ...
If you look up tutorials on this, try to find React 17 and Webpack 5 they're fairly new. Webpack 5 has some differences with hot reloading compared to Webpack 4

React: Lerna React hooks error on monorepo

I have a problem with using Lerna.
My folders structure are like shown below:
packages
myapp
shared
myapp represents a create-react-app structure and shared contains some functions that is returning as below:
import React from 'react'
import Card from '#material-ui/core/Card';
function Hello(){
return (
<Card>
This is a test
</Card>
)
}
When I use material ui components instead of DIV's i get error that says:
Invalid hook call. Hooks can only be called inside of the body of a function component.
But when I use div and other html elements it works fine.
Maybe I got this error because of babel loader? I don't know what to do and how to setup things.
This happens because you have multiple versions of React installed, and more than one get's bundled into the app.
Try to add this to your webpack config:
config.resolve.alias = {
'react': path.join(__dirname, '..', '..', '..', 'node_modules', 'react'), // Adjust for your path to root node_modules
};

How to import from different bundle created with webpack

I have two project using webpack. Now I want to bring one project as module of other project. I can get the two bundle created but don't know how to import from the other bundle.
Elaborating a bit:-
Lets say the other file from which i want to import looks like as follows:-
index2.js (Bundled as bundleTwo)
import SomeCompoent from "./components/SomeCompoent/SomeCompoent";
module.exports = {SomeCompoent}
and in the file (is in another bundle - bundleOne) below I want to import the component (somecomponent):-
index1.js (in bundleOne)
import {SomeCompoent} from "bundleTwo";
but here bundleTwo is undefiend
Any help is highly appreciated
One way that I have figured out myself, is that using alias this can be achieved.
To make this line import {SomeCompoent} from "bundleTwo"; work, bundleTwo can be defined in alias :-
config:{
resolve: {
alias: {
"bundleTwo": path.join(__dirname, "<path_to_the_bundleTwo>")
}
....
If you want to use webpack only,then just set the libraryTarget to 'umd' in bundletwo webpack configuration.
In order to be able to import this module, you need to export your bundle.
output: {
libraryTarget: 'umd',// make the bundle export
filename: "index.js",
path: path.resolve(__dirname, "dist"),
}
However, this can also be achieved by just using Babel to transpile your ES6 code to ES5 code.
babel index2.js --out-file dist/index2.js
Now set the main in package.json to "dist/index2.js"
Now you can use it like
import {SomeCompoent} from "bundleTwo";
You can also create a gulp script for that
gulp.task('js', function () {
return gulp.src(['packages/**/*.js', "!**/*.test.js"])
.pipe(babel({
plugins: ['transform-runtime']
}))
.pipe(gulp.dest('dist'));
});

import CSS and JS files using Webpack

I have a directory structure like this:
and inside node_modules:
>node_modules
>./bin
>webpack.config.js
>bootstrap
>bootstrap.css
>bootstrap.js
I need to generate separate CSS and JS bundles like this:
custom-styles.css, custom-js.js, style-libs.css, js-libs.js
where style-libs and js-libs should contain syles and js files of all libraries like bootstrap and jQuery. Here's what I have done so far:
webpack.config.js:
const path = require('path');
const basedir = path.join(__dirname, '../../client');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const stylesPath = path.join(__dirname, '../bootstrap/dist/css');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
watch: true,
// Script to bundle using webpack
entry: path.join(basedir, 'src', 'Client.js'),
// Output directory and bundled file
output: {
path: path.join(basedir, 'dist'),
filename: 'app.js'
},
// Configure module loaders (for JS ES6, JSX, etc.)
module: {
// Babel loader for JS(X) files, presets configured in .babelrc
loaders: [
{
test: /\.jsx?$/,
loader: 'babel',
babelrc: false,
query: {
presets: ["es2015", "stage-0", "react"],
cacheDirectory: true // TODO: only on development
}
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
},
]
},
// Set plugins (for index.html, optimizations, etc.)
plugins: [
// Generate index.html
new HtmlWebpackPlugin({
template: path.join(basedir, 'src', 'index.html'),
filename: 'index.html'
}),
new ExtractTextPlugin(stylesPath + "/bootstrap.css", {
allChunks: true,
})
]
};
Client.js
import * as p from 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App.jsx';
ReactDOM.render(<App />, document.getElementById('app'));
I am able to run the app and render all of the components correctly except loading the external JS and CSS file using webpack.
I'm not much experienced with webpack and find it really difficult it wrap my hear around it. There're are a few simple questions:
1- Is this configuration correct? If yes, then how can I include my CSS and JS files in components using ES6. Something like import keyword.
2- Should I even be using webpack for CSS files?
3- How to specify individual directories for input and their respective output files in webpack? Something like all-custom.js should be output for custom1.js and custom2.js?
I know these are some very basic question and I tried Google but didn't find a single tutorial for Webpack that is simple and targets beginners.
After playing out with Webpack in multiple projects, I figured out how Webpack loads stuff. Since the question is still unanswered, I decided to do it myself for anybody with same need.
Directory structure
->assets
->css
->my-style-1.css //custom styling file 1
->my-style-2.css //custom styling file 2
->src
->app
->app.js
->variables.js
->libs.js //require all js libraries here
->styles-custom.js //require all custom css files here
->styles-libs.js //require all style libraries here
->node_modules
->index.html
->package.json
->webpack.config.js
Bundle 1 (main code of app)
app.js: assuming this is main file and app starts from here
var msgs = require('./variables');
//similarly import other js files you need in this bundle
//your application code here...
document.getElementById('heading').innerText = msgs.foo;
document.getElementById('sub-heading').innerText = msgs.bar;
Bundle 2 (js modules)
libs.js: this file will require all modules needed
require('bootstrap');
//similarly import other js libraries you need in this bundle
Bundle 3 (external css files)
styles-libs.js: this file will require all external css files
require('bootstrap/dist/css/bootstrap.css');
//similarly import other css libraries you need in this bundle
Bundle 4 (custom css files)
styles-custom.js: this file will require all custom css files
require('../assets/css/my-style-1.css');
require('../assets/css/my-style-2.css');
//similarly import other css files you need in this bundle
webpack.config.js
const path = require('path');
const webpack = require('webpack');
const extractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
'app': './src/app/app.js', //specifying bundle with custom js files
'libs': './src/libs.js', //specifying bundle with js libraries
'styles-custom': './src/styles-custom.js', //specifying bundle with custom css files
'styles-libs': './src/styles-libs.js', //specifying bundle with css libraries
},
module: {
loaders: [
//used for loading css files
{
test: /\.css$/,
loader: extractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: 'css-loader?sourceMap' })
},
//used for loading fonts and images
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file-loader?name=assets/[name].[hash].[ext]'
}
]
},
output: {
path: path.resolve(__dirname, 'dist'), //directory for output files
filename: '[name].js' //using [name] will create a bundle with same file name as source
},
plugins: [
new extractTextPlugin('[name].css'), //is used for generating css file bundles
//use this for adding jquery
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jQuery'
})
]
}
index.html
<head>
<link rel="stylesheet" href="dist/styles-libs.css" />
<link rel="stylesheet" href="dist/styles-custom.css" />
</head>
<body>
<h2 id="heading"></h2>
<h3>
<label id="sub-heading" class="label label-info"></label>
</h3>
<script src="dist/libs.js"></script>
<script src="dist/app.js"></script>
</body>
You can include css & JS files using import in es6 in the source files in your project. example:
import './style.css';
import Style from './path/style.js';
NB. Generally You need to code in es5 in webpack.config.js file. If you want to use es6 just follow the link How can I use ES6 in webpack.config.js?
You can use https://github.com/webpack/css-loader for CSS configuration.
You can use code splitting in webpack and specify multiple entry point but that will generate multiple output files. Have a look at multiple entry point section of that following link.
https://webpack.github.io/docs/code-splitting.html

How to config minified React+ReactDOM with Webpack

After too many unsuccessful trials my question is: What is the proper way to setup Webpack so that:
Use react.min.js + react-dom.min.js - not the npm installed sources
Don't parse/com them again, just bundle with my own components.
"React" and "ReactDOM" variables can be used from all .jsx files.
The tutorials and guides I found didn't work - or maybe I did some errors. Usually I got error in browser developer tools about missing variable React.
My aim is just to save parsing/bundling time. Now I parse React from scratch every time I bundle my app. And it takes tens of seconds on a slowish computer. In watch mode it is faster, but I find I'm doing unnecessary work.
Any ideas with recent React versions?
Assuming you have a webpack.config.js that looks something like this:
module.exports = {
entry: "./entry.js",
output: {
path: __dirname,
filename: "bundle.js"
},
module: {
loaders: [
...
]
}
};
You just need to specify React and ReactDOM as external dependencies (from the docs):
module.exports = {
entry: "./entry.js",
output: {
path: __dirname,
filename: "bundle.js"
},
module: {
loaders: [
...
]
},
externals: {
// "node/npm module name": "name of exported library variable"
"react": "React",
"react-dom": "ReactDOM"
}
};
The key point about the externals section is that the key is the name of the module you want to reference, and the value is the name of the variable that the library exposes when used in a <script> tag.
In this example, using the following two script tags:
<script src="https://fb.me/react-0.14.6.js"></script>
<script src="https://fb.me/react-dom-0.14.6.js"></script>
results in two top-level variables being created: React and ReactDOM.
With the above externals configuration, anytime in your source code you have a require('react'), it will return the value of the global variable React instead of bundling react with your output.
However, in order to do this the page that includes your bundle must include the referenced libraries (in this case react and react-dom) before including your bundle.
Hope that helps!
*edit*
Okay I see what you're trying to do. The webpack configuration option you want is module.noParse.
This disables parsing by webpack. Therefore you cannot use dependencies. This may be useful for prepackaged libraries.
For example:
{
module: {
noParse: [
/XModule[\\\/]file\.js$/,
path.join(__dirname, "web_modules", "XModule2")
]
}
}
So you'd have your react.min.js, react-dom.min.js, and jquery.min.js files in some folder (say ./prebuilt), and then you'd require them like any other local module:
var react = require('./prebuilt/react.min');
And the entry in webpack.config.js would look something like this (untested):
{
module: {
noParse: [
/prebuilt[\\\/].*\.js$/
]
}
}
The [\\\/] mess is for matching paths on both Windows and OSX/Linux.

Resources