Webpack 4 and Hashing for Long Term Caching - node.js

I'm trying to add long term caching to my project.I was using CommonsChunkPlugin but now I'm trying to migrate my project to webpack 4?
And as you know CommosChunkPlugin is dead now with webpack 4.
So I decided to try splitchunks optimization and I have some issues with that.
Here is my config file.
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HashedModuleIdsPlugin = require("webpack-hashed-module-id-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const merge = require('webpack-merge');
const common = require('./webpack.common.config.js');
const webpack= require('webpack');
var path = require("path");
module.exports = merge(common,{
entry: {
main: './src/app.js',
vendor: ['react','react-dom','redux-thunk']
},
output: {
path: path.resolve(__dirname, "dist"),
filename:"[name].[chunkhash].bundle.js",
chunkFilename:"[name].[chunkhash].chunk.js"
},
//devtool:'source-map',
mode: 'production',
module: {
rules: [
{
test: /\.scss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
}
]
},
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
chunks: 'all',
name: 'vendor',
test: 'vendor',
enforce: true
}
}
},
runtimeChunk: 'single'
},
plugins: [
new webpack.HashedModuleIdsPlugin({
// Options...
}),
new MiniCssExtractPlugin({
filename: "[name].[contenthash].css",
chunkFilename: "[name].[contenthash].chunk.css"
}),
new webpack.DefinePlugin({
'process.env':{
'NODE_ENV': JSON.stringify('production')
}
})
/*,
new webpack.SourceMapDevToolPlugin({
filename: '[name].js.map',
exclude: 'vendor'
})*/
]
});
Output is:
And if I import a new scss file to app.js file.
import './app.scss';
The new output is:
The hash of the vendor file changed as you can see.
But why it changed?
I only import a new scss file to my entry javascript file(app.js)? What is the affect of that to my vendor hash?
Note:TextArea.[chunkhash].js is loading from the dynamic import.But I ask about vendor.js here.It is not too important in this case.

As lukas-reineke already suggested, there is an open issue on GitHub.
But there is a very great at medium form Tim Sebastian about this topic:
Predictable long term caching with Webpack

Related

EMFILE: too many open files (lazyFs.open)

We have a React app builded using webpack. When we build our app we get about 1500 chunks with size of approximately 1-600 Kb. This build is published on a server running on CentOS 7 using serve. Then we run a set of similar Selenium tests which basically just do login-logout to our website with redirection to other React components. All components are implemented using lazy loading. After some time our app crashes with this exception:
Error: EMFILE: too many open files, open '/project_name/dist/npm.core-js~36af4842.30b32b47.chunk.js'
Emitted 'error' event at:
at lazyFs.open (internal/fs/streams.js:115:12)
at FSReqWrap.oncomplete (fs.js:141:20)
Here is our webpack-prod config:
const merge = require("webpack-merge");
const common = require("./webpack.common.js");
const webpack = require("webpack");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const TerserJSPlugin = require("terser-webpack-plugin");
module.exports = merge(common, {
mode: "production",
module: {
rules: [
{
test: /\.(css|scss)$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"]
}
]
},
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
minimize: true,
runtimeChunk: {
name: entrypoint => `runtimechunk~${entrypoint.name}`
},
splitChunks: {
chunks: "initial",
**minSize: 5000,
maxSize: 10000,**
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
default: false, // disable the built-in groups, default & vendors (vendors is overwritten below)
reactDom: {
test: /[\\/]node_modules[\\/]react-dom[\\/]/,
name: "vendor.react-dom",
enforce: true,
priority: 20
},
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// получает имя, то есть node_modules/packageName/not/this/part.js
// или node_modules/packageName
const packageName = module.context.match(
/[\\/]node_modules[\\/](.*?)([\\/]|$)/
)[1];
// имена npm-пакетов можно, не опасаясь проблем, использовать
// в URL, но некоторые серверы не любят символы наподобие #
return `npm.${packageName.replace("#", "")}`;
}
},
styles: {
name: 'styles',
test: /\.+(css)$/,
chunks: 'all',
},
},
}
},
plugins: [
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify("production")
}
}),
new webpack.HashedModuleIdsPlugin()
]
});
Here is our webpack-common config:
const fs = require('fs');
const gracefulFs = require('graceful-fs');
gracefulFs.gracefulify(fs);
const path = require('path');
const SERVER_URL = require('./src/Global');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
var CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
devServer: {
historyApiFallback: true
},
entry: {
'commons': ['string.prototype.startswith', 'react', 'react-dom'],
polyfills: './src/polyfills.js',
vendor: [
'react',
'redux',
'lodash',
],
app: './src/index.js'
},
output: {
filename: '[name].[hash:8].js',
chunkFilename: '[name].[chunkhash:8].chunk.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: { minimize: true }
}
]
},
{
test: /\.(png|jpg|jpeg|gif|ico|svg)$/,
exclude: [/\.inline\.svg$/],
use: ['url-loader']
},
{
test: /\.inline\.svg$/,
use: ['svg-react-loader']
}
]
},
plugins: [
//new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(),
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
}),
new CopyWebpackPlugin([
{ from: 'public/', to: 'img/' }
]),
],
externals: {
// global app config object
config: JSON.stringify({
apiUrl: SERVER_URL
}),
}
};
What we already tried:
1) Set ulimit -n 65536. Also tried setting to 2048 and 10000 - everytime the exception raises after some period of time. The more the value we set the longer it takes to raise an exception
2) Played with splitChunks.minSize and splitChunks.maxSize values: set them to bigger values and got less chunks with bigger size - the outcome is always the same
How can we fix this problem? Thanks in advance.

webpack-hot-middleware only updates Css/Sass Once

I have a problem with webpack-hot-middleware and I'm not sure what I'm doing wrong.
In summary: Everytime I run the command node./ dev webpack runs and start to monitoring changes. This part is working great.
When I change my src / assets / js / index.js file, it refreshes the page after aply the changes. But with my src / assets / styles / index.scss file, it is only if that change is the first change I made after webpack start monitoring.
If I run node./ dev and change theindex.scss, the browser refresh after the changes are made in the output. On the second time, the browser does not refresh. Same happens if I change my index.js and tries to changeindex.scss.
In my Chrome console tab, it shows the following messages (when the page does not upload):
[HMR] bundle rebuilding client.js:242
[HMR] bundle rebuilt in 2407ms process-update.js:39
[HMR] Checking for updates on the server... process-update.js:110
[HMR] Nothing hot updated. process-update.js:119
[HMR] App is up to date.
Here is a Sample of my working code:
dev.js
const webpack = require('webpack');
const webpackMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const express = require('express');
const app = express();
const config = require('./webpack.dev.conf');
const DEFAULT_PORT = 3000;
const options = {
publicPath: config.output.publicPath
};
config.entry.main.push('webpack-hot-middleware/client?reload=true');
const compiler = webpack(config);
console.log('Starting the dev web server...');
app.use(webpackMiddleware(compiler, options));
app.use(webpackHotMiddleware(compiler));
app.listen(DEFAULT_PORT, (err) => {
if (err) {
console.log(err);
}
console.log('WebpackHotMiddleware is listening at http://localhost:3000/...');
});
webpack.dev.conf.js
const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const base = require('./webpack.base.conf');
const appHtmlTitle = 'Hello World';
process.env.NODE_ENV = 'development';
const dirSrc = path.join(__dirname, 'src');
process.noDeprecation = true;
module.exports = {
mode: 'development',
devtool: 'source-map',
output: {
path: path.join(__dirname, 'dev'),
publicPath: '/',
filename: 'assets/js/[name].js?[hash]'
},
optimization: {
splitChunks: {
chunks: 'all' // include all types of chunks
},
},
entry: {
main: [
path.join(dirSrc, 'assets', 'js', 'index'),
path.join(dirSrc, 'assets', 'styles', 'index.scss')
]
},
module: {
rules: [{
test: /\.html$/,
loader: 'html-loader',
options: { minimize: true }
},
{
enforce: 'pre',
test: /\.js$/,
exclude: [/node_modules/],
loader: 'eslint-loader'
},
{
test: /\.js?$/,
exclude: [/node_modules/],
loader: 'babel-loader'
},
// CSS / SASS
{
test: /\.(s)?css/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: { publicPath: '/' }
},
{ loader: 'css-loader' },
{ loader: 'sass-loader' }
]
},
// IMAGES
{
test: /\.(jpe?g|png|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
publicPath: '/',
name: 'assets/images/[name].[ext]'
}
}
]
},
// FONTS
{
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
loader: 'file-loader',
options: {
publicPath: '/',
name: 'assets/fonts/[name].[ext]'
}
}]
},
plugins: [
new CleanWebpackPlugin(['dev'], { verbose: false }),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new HtmlWebpackPlugin({
filename: path.join(__dirname, 'dev', 'index.html'),
template: 'src/index.ejs',
title: appHtmlTitle,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
}
}),
new MiniCssExtractPlugin({
publicPath: '/',
filename: 'assets/css/[name].css?[hash]'
}),
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(false)
})
]
};

How to use webpack to create a front end and a admin?

I am actually new on node.js. I am trying to create an app with admin and frontend using webpack and express. I have created an example app but now the problem is how I can create the app with the admin. Below I have shared my webpack config file
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: {
frontend: './src/index',
backend: './admin/index'
},
module: {
loaders: [
{
test: /\.js?$/, loader: 'babel', exclude: /node_modules/
},
{
test: /\.css$/,
loaders: [
'style?sourceMap',
'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]'
]
}
]
},
resolve: {
extensions: ['', '.js']
},
output: {
path: path.join(__dirname, '/public'),
publicPath: '/',
filename: 'bundle.js'
},
devServer: {
contentBase: './public',
hot: true
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
]
};
Webpack production configuration file
var config = require('./webpack.config.js');
var webpack = require('webpack');
config.plugins.push(
new webpack.DefinePlugin({
"process.env": {
"NODE_ENV": JSON.stringify("production")
}
})
);
config.plugins.push(
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
);
Is there any easy solution or what I can do now?

Separate client and server in Webpack

Goal. Configuring app, which has: React, Webpack and MongoDB.
So, I've already setup Webpack for React and tried import Mongoose. The problem: React client-side and Mongoose - server-side, and because of that Webpack must have configurations for both. Using this answer: https://stackoverflow.com/a/37391247/7479176 I tried to configure Webpack. After that, I tried import Mongoose in my server.jsx file, but it didn't work.
Question. How to configure Webpack, so I can work with MongoDB?
Edited. I figured out how to rid of warnings (see Warnings):
output: {
filename: 'bundle.node.js',
libraryTarget: 'commonjs',
path: path.resolve(__dirname, 'dist')
},
externals: [
/^(?!\.|\/).+/i
],
But, when I added code into server.jsx (see server.jsx), it didn't log message in console.
Webpack configurations:
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = [
{
entry: {
index: './src/app/app.jsx'
},
devtool: 'inline-source-map',
devServer: {
port: 3000,
host: 'localhost',
historyApiFallback: true,
noInfo: false,
stats: 'minimal',
hot: true, // Tell the dev-server we're using HMR
contentBase: path.resolve(__dirname, './dist'),
publicPath: '/'
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(), // Enable HMR
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
inject: 'body'
})
],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
},
{
entry: {
index: './src/server/server.jsx'
},
target: 'node',
output: {
filename: 'bundle.node.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
}
]
}
}
]
server.jsx:
import mongoose from 'mongoose'
import '../config/database.js'
mongoose.Promise = global.Promise
mongoose.connect(config.database)
// check connection
mongoose.connection.once('open', () => console.log('Connected to MongoDB'))
// check for db errors
mongoose.connection.on('error', err => console.log(err))
Warnings:
WARNING in ./node_modules/mongoose/lib/drivers/index.js
10:13-49 Critical dependency: the request of a dependency is an expression
WARNING in ./node_modules/require_optional/index.js
82:18-42 Critical dependency: the request of a dependency is an expression
WARNING in ./node_modules/require_optional/index.js
90:20-44 Critical dependency: the request of a dependency is an expression
WARNING in ./node_modules/require_optional/index.js
97:35-67 Critical dependency: the request of a dependency is an expression
WARNING in ./node_modules/es6-promise/dist/es6-promise.js
Module not found: Error: Can't resolve 'vertx' in 'D:\Projects\JavaScriptProjects\pizzaday\node_modules\es6-promise\dist'
# ./node_modules/es6-promise/dist/es6-promise.js 131:20-30
# ./node_modules/mongodb/lib/mongo_client.js
# ./node_modules/mongodb/index.js
# ./node_modules/mongoose/lib/index.js
# ./node_modules/mongoose/index.js
# ./src/server/server.jsx
Solution. I used webpack-dev-middleware and webpack-hot-middleware with basic Express server. I tried launch MongoDB with React on webpack-dev-server and thats was main problem.
I made new server.js within separate folder following advice from Neil Lunn and setup basic Express server with middleware and split Webpack config into 3 separate files common, dev and prod.
This fragment of code in server.js helped me to run server and client together with Webpack which bonded everything together:
app.use(webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath
}))
app.use(webpackHotMiddleware(compiler))
New Webpack config (webpack.common.js):
const webpack = require('webpack')
const path = require('path')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
app: [
'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
'./src/index.jsx'
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
template: './index.html',
filename: 'index.html',
inject: 'body'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
}

webpack only bundle firebase/database and not firebase/auth

I am using firebase in my react app. I only use the firebase/app and firebase/database modules, so I want webpack to bundle only what I used. However, webpack bundles everything in the firebase module. I cannot figure out how to tell webpack to ignore other modules.
I thought my issue was
import * as firebase from 'firebase';
However, I tried to import only bits and pieces of firebase.
import firebase from 'firebase/app';
import firebase from 'firebase/database';
but, webpack still bundles up auth.js and other unused modules.
Here is my webpack config file.
const path = require('path');
const webpack = require('webpack');
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
cache: true,
entry: {
main: "./src/swap.tsx",
vendor: [
'babel-polyfill',
'events',
'fbemitter',
'flux',
'react',
'react-dom'
]
},
output: {
filename: "[name].js",
path: __dirname + "/app/assets/javascripts",
chunkFilename: '[chunkhash].js'
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".jsx"],
modules: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'node_modules')
]
},
module: {
rules: [{
test: /\.ts(x?)$/,
exclude: /node_modules/,
use: [
{ loader: 'babel-loader' },
{ loader: 'awesome-typescript-loader' }
]
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.js'
}),
new webpack.optimize.UglifyJsPlugin(),
new LodashModuleReplacementPlugin(),
new BundleAnalyzerPlugin(),
]
};
Add firebase/app and firebase/database to your vendor bundle array

Resources