using "externals" in "webpack" for loading from CDN - reactjs

I'm new in reactjs and webpack.
I want to load a library from CDN in my project using webpack external, like sample in https://webpack.js.org/configuration/externals/#externals
but it doesn't work.
can you help me?
It's my index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="stylesheet" href="https://cdn.cdncode.com/twitter- bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.cdncode.com/bootstrap-rtl/3.2.0-rc2/css/bootstrap-rtl.min.css"/>
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
</head>
<body class="content_main">
<div id="root"></div>
<script type="text/javascript" src="https://cdn.cdncode.com/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdn.cdncode.com/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script type="text/javascript" src="https://cdn.cdncode.com/html5shiv/r29/html5.min.js"></script>
<script type="text/javascript" src="https://cdn.cdncode.com/respond.js/1.4.2/respond.min.js"></script>
</script>
</body>
</html>
and it's my webpack.config.js
const webpack = require('webpack');
const path = require('path');
module.exports = {
entry: "./src/index.js",
output: {
path: "/js/",
publicPath: 'http://jwplayer.k-cdn.net/',
filename: "bundle.js"
},
module: {
loaders: [
{test: /\.js$/, loader: 'babel-loader', query: {presets: ['es2015', 'react']}},
{test: /\.css$/, loader: 'style-loader!css-loader'},
{test: /\.(png|svg|jpg|gif)$/, loader: 'file-loader'},
{test: /\.(woff|woff2|eot|ttf|otf)$/, loader: 'file-loader'}
]
},
resolve: {
extensions: ['.js']
},
externals: {
jquery: 'jQuery'
}
};

To execute $ function of JQuery into React, i did following steps.
1) Install JQuery from npm
npm install --save jquery
2) Changes in webpack.config.js, Remove external, add plugins
module.exports = {
entry: "./src/index.js",
output: {
path: "/js/",
publicPath: 'http://jwplayer.k-cdn.net/',
filename: "bundle.js"
},
module: {
loaders: [
{test: /\.js$/, loader: 'babel-loader', query: {presets: ['es2015', 'react']}},
{test: /\.css$/, loader: 'style-loader!css-loader'},
{test: /\.(png|svg|jpg|gif)$/, loader: 'file-loader'},
{test: /\.(woff|woff2|eot|ttf|otf)$/, loader: 'file-loader'}
]
},
resolve: {
extensions: ['.js']
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
}),
]
};
Now we can use $ in React

Related

Webpack doesn't work in prod but in dev env is fine

I am serving with NodeJS react as static but index.html cant render bundle.js file (in network tab it says that bundle.js is Not found) ;
Express looks like this:
app.get(/^\/ui\/(?!images|vendor|assets|packages)/, function (req, res, next) {
res.sendFile(path.join(__dirname, 'public/ui/index.html'))
});
app.use(express.static(path.join(__dirname, 'public')));
app.listen(port, function () {
console.log('App listening on port ' + port);
});
The folder structure looks like this:
the index html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Font Awesome CDN-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css"
integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g=="
crossorigin="anonymous" referrerpolicy="no-referrer"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Skenario Labs - Banking</title>
</head>
<body>
<div id="root"></div>
<script src="/bundle.js"></script>
</body>
Webpack common :
module.exports = {
entry: path.resolve(__dirname, '..', '/src/index.tsx'),
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
module: {
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
},
],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
"style-loader",
// Translates CSS into CommonJS
"css-loader",
// Compiles Sass to CSS
"sass-loader"
]
},
{
test: /\.(woff(2)?|eot|ttf|otf|)$/,
type: 'asset/inline',
},
{
test: /\.(png|jpe?g|gif|jp2|webp|ico|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
output: {
path: path.resolve(__dirname, '..', './build'),
filename: 'bundle.js',
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '..', './public/ui/index.html'),
favicon: "./public/ui/assets/images/favicon.png",
filename: 'index.html',
inject: 'body'
}),
],
stats: 'errors-only',
}
Ive tried to use it both ways :
<script src="/bundle.js"></script>
<script src="bundle.js"></script>
But then getting status 304 and the bundle.js inside shows white screen instead of JS

single spa react application chunk load only when needed

I have built a single spa react app, below is the index.html for the app
there are 2 application, app1 and app2, app1 loads initially, and app2 loads only when needed
index.html for single spa config of app1 and app2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=9;IE=10;IE=Edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Project</title>
<!-- app2 css -->
<link href='http://localhost:8003/app2.css' rel='stylesheet'/>
</head>
<body>
<script>
function randstr(prefix) {
return Math.random().toString(36).replace('0.', prefix || '');
}
var suffix = randstr('?v=');
document.write('<script src="config/config.js' + suffix + '"><\/script>');
document.write('<script type="systemjs-importmap" crossorigin="anonymous">' +
'{ \n"imports": {\n' +
' "#portal/config": "http://localhost:8001/index.js' + '",\n' +
' "#portal/app1": "http://localhost:8002/app1.js' + '",\n' + // this loads first, it has login page
' "#portal/app2": "http://localhost:8003/app2.js' + '" \n }\n}' + // app 2 which loads on click
'<\/script>');
</script>
<!-- load the vendor chunk of app2-->
<script src='http://localhost:8003/vendor.app2.js'></script>
<!-- Load the common deps-->
<script>
imports and loads dependencies of react, react-dom, lodash and others
</script>
<!-- Load the application -->
<script>
SystemJS.import('#portal/config')
</script>
<div id="app" class="site-container"></div>
</body>
</html>
in the above index.html, configured the apps by systemjs-importmap and everything works great.
As you can see in the importmap, importing the app2("#portal/app2": "http://localhost:8003/app2.js'), this will load only when needed, but this bundle is bit huge and i have tried to improve the performance by extracting the css and vendor chunk, minifying the js and css, which indeed improved the performance, those scripts are added as well (http://localhost:8003/vendor.app2.js , http://localhost:8003/app2.css)
webpack.config.js for app2
const webpack = require('webpack')
const path = require('path')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
entry: path.resolve(__dirname, 'src/app2.js'),
output: {
filename: 'app2.js',
library: 'app2',
libraryTarget: 'amd',
path: path.resolve(__dirname, 'build/app2'),
},
mode: 'production',
module: {
rules: [
{parser: {System: false}},
{
test: /\.js$/,
exclude: [path.resolve(__dirname, 'node_modules')],
loader: 'babel-loader',
options: {
presets: ["#babel/preset-env"]
}
},
{
test: /\.krem.css$/,
exclude: [path.resolve(__dirname, 'node_modules')],
use: [
{
loader: 'kremling-loader',
options: {
namespace: 'app2',
postcss: {
plugins: {
'autoprefixer': {},
},
},
},
},
],
},
{ test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'url-loader' },
{ test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'url-loader' },
// { test: /\.css$/, loader: "style-loader!css-loader"},
{ test: /\.css$/, use: [
MiniCssExtractPlugin.loader,
'css-loader',
]},
],
},
plugins: [
new CleanWebpackPlugin({
verbose: true,
cleanOnceBeforeBuildPatterns: [path.resolve(__dirname, 'build/app2')],
}),
new CopyWebpackPlugin({
patterns: [
{from: path.resolve(__dirname, 'src/ppsr.js')},
]}),
new TerserPlugin({
parallel: true,
cache: true
}),
new MiniCssExtractPlugin({
filename: '[name].css',
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
name: 'vendor',
chunks: 'all',
reuseExistingChunk: true,
priority: 1,
test: module =>
/[\\/]node_modules[\\/]/.test(module.context),
minChunks: 1,
minSize: 0,
},
},
},
minimize: true,
minimizer: [
new CssMinimizerPlugin(),
],
},
devtool: 'source-map',
externals: [
/^#portal\/*/,
/^lodash$/,
/^single-spa$/,
/^rxjs\/?.*$/,
/^react$/,
/^react\/lib.*/,
/^react-dom$/,
/.*react-dom.*/,
],
}
I get the build files vendor.app2.css, app2.css , app2.js files...only js loads when needed because it is imported by importmap, and the css and vendor are in global space(means loads irrespective of app2 is loaded or not)
Question:
How can I load the vendor and CSS same as js for app2 only when needed in single spa react?

Webpack not emitting files to index.html when using dev server

I have been trying to setup webpack and react with typescript without the CRA jargon. The dev server doesn't emit the files to the index.html file to be viewed in the browser. The CRA template on eject has multiple js scripts for building and development mode but I want to omit that and keep the structure as clean and simple as possible
Package.json-
"scripts": {
"start": "webpack-dev-server --mode development --open --hot",
"build": "webpack --mode production"
}
The webpack config-
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
const CompressionPlugin = require('compression-webpack-plugin')
const HtmlWebpackChangeAssetsExtensionPlugin = require('html-webpack-change-assets-extension-plugin')
module.exports = {
entry: {
app: path.resolve(__dirname, 'src/index.tsx')
},
output: {
path: path.resolve(__dirname, 'dist'),
chunkFilename: '[name].[hash].chunk.js',
publicPath: '/codestage'
},
devtool: 'source-map',
resolve: {
extensions: ['.ts', '.tsx', '.js']
},
module: {
strictExportPresence: true,
rules: [
{
test: /\.js$/,
loader: require.resolve('babel-loader'),
exclude: /node_modules/,
options: {
compact: true
}
},
{
test: /\.tsx?$/,
include: path.join(__dirname, 'src'),
exclude: /node_modules/,
use: [
{ loader: 'babel-loader' },
{
loader: 'ts-loader',
options: {
transpileOnly: true
}
}
]
},
{ enforce: 'pre', test: /\.js$/, loader: 'source-map-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
jsExtension: '.gz',
filename: './index.html'
}),
new CompressionPlugin({
test: /\.js(\?.*)?$/i,
deleteOriginalAssets: true
}),
new ForkTsCheckerWebpackPlugin({ eslint: true }),
new HtmlWebpackChangeAssetsExtensionPlugin()
],
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
reuseExistingChunk: true,
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'initial'
}
}
}
}
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
The directory structure-
Html inspect in browser-
How to solve this?
Actually,dev server is a tool to develop and debug.Everything is built in memory.You can try npm run build script to emit files that can be used to deploy.

Why is React Chunking not working?

I am trying to chunk my react app using webpack, but nothing loads and it says vendor.js says 0 bytes. What am I doing wrong? I think there is something wrong in my index.html. vendor.js and index.js are both located in the src folder. I've already tried following this, but no luck https://github.com/webpack/webpack/issues/368
Webpack
var webpack = require('webpack');
module.exports = {
entry: {
app: './src/index.js',
vendor: './src/vendor.js'
},
output: {
path: __dirname,
publicPath: '/',
filename: '[name].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendor.js')
],
module: {
loaders: [{
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['react', 'es2015', 'stage-1']
}
}]
},
resolve: {
extensions: ['', '.js', '.jsx']
},
devServer: {
historyApiFallback: true,
contentBase: './'
}
};
index.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/style/style.css">
</head>
<body>
<div id="app"></div>
</body>
<script src="/bundle.js"></script>
</html>
You should add all of your common dependencies into vendor.js via import,
e.g jquery, lodash, post-css, jss or any others which you are using.
since you have not done that may be because of that your vendor.js file is of size 0 bytes.
Change your CommonsChunkPlugin to
new webpack.optimize.CommonsChunkPlugin({
name : 'vendor',
}),
Also you need to change your index.html
because in your webpack config you have
output: {
path: __dirname,
publicPath: '/',
filename: '[name].js'
},
Here
filename: '[name].js'
means files will be generated by webpack based on entry names as app.js and vendor.js
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/style/style.css">
</head>
<body>
<div id="app"></div>
</body>
<script src="/vendor.js"></script>
<script src="/app.js"></script>
</html>
change new webpack.optimize.CommonsChunkPlugin('vendor.js') to
new webpack.optimize.CommonsChunkPlugin('vendor','vendor.js', Infinity)
or better be new webpack.optimize.CommonsChunkPlugin('vendor','vendor.[chunkhash].js', Infinity)
I would recommend migrating to Webpack 2. Refer migrating guide

How to use static js generated throught webpack for react app with es6

I am new to es6/react js and webpack and from last 6 days I am trying to create startkit for react app with es6 and webpack below is my webpack.config.js , I am successfully able to configure web pack dev server. My app is running on http:/localhost:8080/webpack-dev-server. When I am running npm build to generate bundle.js. if I am running my app only using localhost:8080 my app is running in chrome but giving error in mozilla (r.render is not function). Webpack is very confusing... Can we run file bundle.js file locally on file:// server. means like in normal html file if I include that bundle.js file it should work right?
My webpack.config.js
var path = require('path');
var webpack = require('webpack');
//var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
var mainPath = path.join(__dirname, 'app', 'index.js');
var buildPath = path.join(__dirname, 'dist/assets/');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var autoprefixer = require('autoprefixer')
var sassLoaders = [
'css-loader?sourceMap',
'postcss-loader',
'sass-loader?sourceMap&includePaths[]=' + path.join(__dirname, './app')
]
module.exports = {
// Makes sure errors in console map to the correct file
// and line number
devtool: 'cheap-module-source-map',
entry: {
'vendor': ['react','react-dom'],
"bundle":mainPath
},
module: {
loaders: [
{
test: [/\.js$/],
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015', 'react']
}
},
// //{ test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // use ! to chain loaders
// { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // use ! to chain loaders
// { test: /\.s?css$/, loaders: ['style', 'css', 'sass','css?sourceMap', 'sass?sourceMap'] }
{ test: /\.scss$/,
loader: ExtractTextPlugin.extract('style-loader', sassLoaders.join('!'))
},
{ test: /\.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/, loader: 'file-loader?name=/fonts/[name].[ext]' }
]
},
output: {
// We need to give Webpack a path. It does not actually need it,
// because files are kept in memory in webpack-dev-server, but an
// error will occur if nothing is specified. We use the buildPath
// as that points to where the files will eventually be bundled
// in production
path: buildPath,
filename: '[name].js',
publicPath: 'http://localhost:8080/assets'
},
plugins: [
// Pro-tip: Order matters here.
new ExtractTextPlugin('[name].css'), new webpack.optimize.CommonsChunkPlugin(['bundle', 'vendor'], '[name].js')
],
postcss: [
autoprefixer({
browsers: ['last 2 versions'],
//path: "./dist",
filename: '[name].js',
// Everything related to Webpack should go through a build path,
// localhost:8080/build. That makes proxying easier to handle
publicPath: '/dist/'
})
],
resolve: {
extensions: ['', '.js', '.jsx','.sass','.woff','.ttf','.eot','.svg'],
root: [path.join(__dirname, './app')]
},
watch:true
};
my index.html
<!DOCTYPE html>
<html>
<head>
<title>React Home Page</title>
<link rel="stylesheet" href="assets/bundle.css" />
</head>
<body>
<div id="react-app"></div>
<script type="text/javascript" src="assets/vendor.js"></script>
<script type="text/javascript" src="assets/bundle.js"></script>
</body>
</html>
You don't have to link it yourself, webpack does that for you.
the HtmlWebpackPlugin will make sure that the bundle is being linked in the file that you configure.
var path = require("path");
var HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./app/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "index_bundle.js"
},
module: {
rules: [
{ test: /\.(js)$/, use: "babel-loader" },
{ test: /\.css$/, use: ["style-loader", "css-loader"] }
]
},
plugins: [
new HtmlWebpackPlugin({
template: "app/index.html"
})
]
};

Resources