How to watch certain node_modules changes with webpack-dev-server - webpack-dev-server

I'm currently experimenting with a monorepo architecture.
What I would like to do is in my web package where I run webpack dev server I'd like it to watch certain node_modules (symlinked local packages) for changes and trigger a "rebuild".
This way I'd be able to build dependencies separately and my browser would react to those changes.
My webpack config is the following:
var loaders = require('./../../../build/loaders-default');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: ['./src/index.ts'],
output: {
filename: 'build.js',
path: path.join(__dirname, 'dist')
},
resolve: {
extensions: ['.ts', '.js', '.json']
},
resolveLoader: {
modules: ['node_modules']
},
devtool: 'inline-source-map',
devServer: {
proxy: [
{
context: ['/api-v1/**', '/api-v2/**'],
target: 'https://other-server.example.com',
secure: false
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
inject: 'body',
hash: true
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
'window.jquery': 'jquery'
})
],
module:{
loaders: loaders
}
};
Loaders are just the usual stuff included.

You can config in in webpack.config file or in WebpackDevServer option, to watch for changes also in node_modules (i think that by default webpack watching for changes in all files)
https://webpack.js.org/configuration/watch/#watchoptions-ignored
in the following example webpack ignored all changes in node_modules folder except specific module.
watchOptions: {
ignored: [
/node_modules([\\]+|\/)+(?!some_npm_module_name)/,
/\some_npm_module_name([\\]+|\/)node_modules/
]
}
ignored[0] = Regex to ignore all node_modules that not started with some_npm_module_name
ignored[1] = Regex to ignore all node_modules inside some_npm_module_name
You may also used this link npm linked modules don’t find their dependencies

UPDATE: I'm currently using Next.js 11, and it seems this is no longer necessary.
Granted this question is not regarding Next.js or any specific framework, I'd like to post an answer related to Next.js here since I arrived here from Google as others might also.
Here is what worked for me in my next.config.js:
module.exports = {
// ...
webpackDevMiddleware: config => {
// Don't ignore all node modules.
config.watchOptions.ignored = config.watchOptions.ignored.filter(
ignore => !ignore.toString().includes('node_modules')
);
// Ignore all node modules except those here.
config.watchOptions.ignored = [
...config.watchOptions.ignored,
/node_modules\/(?!#orgname\/.+)/,
/\#orgname\/.+\/node_modules/
];
return config;
},
// ...
}
This targets a specific organization of packages. If you need to just target a specific package:
module.exports = {
// ...
webpackDevMiddleware: config => {
// Don't ignore all node modules.
config.watchOptions.ignored = config.watchOptions.ignored.filter(
ignore => !ignore.toString().includes('node_modules')
);
// Ignore all node modules except those here.
config.watchOptions.ignored = [
...config.watchOptions.ignored,
/node_modules([\\]+|\/)+(?!my-node-module)/,
/\my-node-module([\\]+|\/)node_modules/
];
return config;
},
// ...
}
This builds on Elhay's answer.

snapshot.managedPaths
A common use case for managedPaths would be to exclude some folders from node_modules, e.g. you want webpack to know that files in the node_modules/#azure/msal-browser folder are expected to change, which can be done with a regular expression like the one below:
module.exports = {
snapshot: {
managedPaths: [
/^(.+?[\\/]node_modules[\\/](?!(#azure[\\/]msal-browser))(#.+?[\\/])?.+?)[\\/]/,
],
},
};

Related

Module Federation, React, and Apollo 3 warnings

I'm building an app with micro-frontends using webpack 5's module federation plugin. Everything was fine until I started adding react hooks into my remote app. At that point I received errors about "invalid usage of hooks", i.e. I discovered I had TWO versions of react loaded, one from the remote and one from the app consuming the remote.
That problem was solved by adding a shared key to the ModuleFederationPlugin section of my webpack config that marked React as a singleton. Now everything compiles and seems to run just fine.
However, the webpack compiler is throwing some annoying warnings at me now. Its saying:
No required version specified and unable to automatically determine one. Unable to find required version for "react" in description file (/Users/myComputer/Development/myapp/node_modules/#apollo/client/react/context/package.json). It need to be in dependencies, devDependencies or peerDependencies.
Here is what my webpack config (in the remote) looks like currently:
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin')
const deps = require('./package.json').dependencies
module.exports = {
mode: 'development',
devServer: { port: 3001 },
entry: './src/index.tsx',
output: {
path: __dirname + '/dist/',
},
module: {
rules: [
{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json'],
},
use: 'ts-loader',
},
]
},
devtool: 'source-map',
plugins: [
new ModuleFederationPlugin(
{
name: 'myRemote',
filename: 'remoteEntry.js',
exposes: {
'./App':
'./src/App/App.tsx',
},
shared: {
'react': {
singleton: true,
version: deps['react'],
},
'react-dom': {
singleton: true,
version: deps['react-dom'],
},
},
}
),
new HtmlWebpackPlugin({
template:
'./index.html',
})
]
}
The consuming app's webpack config is almost the same, especially the shared section (there are some slight differences in that it declares the remotes).
What would be the way to tell webpack that the apollo package will be getting its react dependency from somewhere else? Or if thats not the right thing to tell webpack, what is and how can I get rid of these warnings?
Fixed my own problem by changing the key version to requiredVersion

Switch over to Create React App on Existing Project?

I have an existing application that I am using "webpack-serve" as it was recommended to me by the developer(at that time he was not going to update webpack-dev-server anymore).
Anyways now it is deprecated and not being used, I got to back to webpack-dev-server but I am thinking if I should just go through the effort and try to use something like "Create React App" as I don't really know if I can use these old wepack.js files I made for webpack-serve and they also don't seem to work 100% as everytime I try to build a production build it gives me a dev build.
webpack.common.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const webpack = require('webpack');
module.exports = {
entry: ["#babel/polyfill", "./src/index.js"],
output: {
// filename and path are required
filename: "main.js",
path: path.resolve(__dirname, "dist"),
publicPath: '/'
},
module: {
rules: [
{
// JSX and JS are all .js
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
}
},
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
use: [
{
loader: 'file-loader',
options: {}
}
]
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {}
}
]
}
]
},
plugins: [
new CleanWebpackPlugin(["dist"]),
new HtmlWebpackPlugin({
template: "./src/index.html"
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
]
};
webpack.dev
const path = require("path");
const merge = require("webpack-merge");
const convert = require("koa-connect");
const proxy = require("http-proxy-middleware");
const historyApiFallback = require("koa2-connect-history-api-fallback");
const common = require("./webpack.common.js");
module.exports = merge(common, {
// Provides process.env.NODE_ENV with value development.
// Enables NamedChunksPlugin and NamedModulesPlugin.
mode: "development",
devtool: "inline-source-map",
// configure `webpack-serve` options here
serve: {
// The path, or array of paths, from which static content will be served.
// Default: process.cwd()
// see https://github.com/webpack-contrib/webpack-serve#options
content: path.resolve(__dirname, "dist"),
add: (app, middleware, options) => {
// SPA are usually served through index.html so when the user refresh from another
// location say /about, the server will fail to GET anything from /about. We use
// HTML5 History API to change the requested location to the index we specified
app.use(historyApiFallback());
app.use(
convert(
// Although we are using HTML History API to redirect any sub-directory requests to index.html,
// the server is still requesting resources like JavaScript in relative paths,
// for example http://localhost:8080/users/main.js, therefore we need proxy to
// redirect all non-html sub-directory requests back to base path too
proxy(
// if pathname matches RegEx and is GET
(pathname, req) => pathname.match("/.*/") && req.method === "GET",
{
// options.target, required
target: "http://localhost:8080",
pathRewrite: {
"^/.*/": "/" // rewrite back to base path
}
}
)
)
);
}
},
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: ["style-loader", "css-loader", "sass-loader"]
}
]
}
});
webpack.prod
const merge = require("webpack-merge");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const common = require("./webpack.common.js");
module.exports = merge(common, {
// Provides process.env.NODE_ENV with value production.
// Enables FlagDependencyUsagePlugin, FlagIncludedChunksPlugin,
// ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin,
// SideEffectsFlagPlugin and UglifyJsPlugin.
mode: "production",
devtool: "source-map",
// see https://webpack.js.org/configuration/optimization/
optimization: {
// minimize default is true
minimizer: [
// Optimize/minimize CSS assets.
// Solves extract-text-webpack-plugin CSS duplication problem
// By default it uses cssnano but a custom CSS processor can be specified
new OptimizeCSSAssetsPlugin({})
]
},
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
// only use MiniCssExtractPlugin in production and without style-loader
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"]
}
]
},
plugins: [
// Mini CSS Extract plugin extracts CSS into separate files.
// It creates a CSS file per JS file which contains CSS.
// It supports On-Demand-Loading of CSS and SourceMaps.
// It requires webpack 4 to work.
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
}),
new BundleAnalyzerPlugin()
]
});
Edit
If I where to go over to Create React App how would I handle this stuff?
I have a .babelrc with
"presets": ["#babel/env", "#babel/react"],
"plugins": [
["#babel/plugin-proposal-decorators", { "legacy": true }],
"#babel/plugin-transform-object-assign",
"#babel/plugin-proposal-object-rest-spread",
"transform-class-properties",
"emotion"
]
I think react-app takes care of some of the stuff but not sure if all. I also have if you noticed in webpack.common I am pollying filling everything, would I just need "react-app-polyfill."?
How can I add another "dev mode"
"scripts": {
"dev": "cross-env NODE_ENV=dev webpack-serve --config webpack.dev.js --open",
"prod": "cross-env NODE_ENV=prod webpack -p --config webpack.prod.js",
"qa": "cross-env NODE_ENV=QA webpack --config webpack.prod.js"
},
I need to setup the Node_ENV for QA as I have a check to point to my api that changes in each enviroment.
it's a simple as below today:
npx create-react-app .
I've had to do something like this a couple times. This has been my approach:
create-react-app my-app-cra // clean slate
npm i [list of dependencies] // minus any build, compile, transpile, etc. dependencies
Copy over my src folder, preserving as much of the structure as possible
npm start // and keep fingers crossed! Typically, a bit of manual work is involved
To preserve your git history:
Copy your src to a folder outside your repo
Clean your repo git rm -rf
Perform the above steps (create react app, install deps, copy src back in)
git add // If you preserve your folder structure, git will find the copied over files (and will notice a possible change in path) and handles gracefully, preserving history.
Both create-react-app and webpack 4 are good options and very simple. In my opinion, create-react-app is the most practical.
In order to conserve your git history, I recommend:
create a branch and go to it.
install and save the dependencies and dev-dependencies of create-react-app. you will see them in your package.json file.
do the configuration. use the create-react-app repo as an example.
if it works fine, return to your master branch and merge this branch with the migration.
Execute npm i in order to install the dependencies you added from your branch.

How to use plugin to reduce size when import lodash

Here is my code : import _ from 'lodash';
I want to use babel-plugin-transform-imports to reduce size of folder when "yarn build".
But I don't know how to set up plugin and config in wepack.config.js
Thank you so much
There is three way to reduce:
1. When in development, you can include the folder where you are going to compile by config the loaders, so the file outside this folder won't be compiled. Loader config are like the following code:
{
test: /\.(js|mjs|jsx)$/,
include: path.resolve(__dirname,"src"),//important
use: {
loader: 'babel-loader'
}
}
2、 You can use DDL to pre-compile the third-party library.
e.g.
Firstly create vendor.js, that is to say you need to bundle it by another webpack config.
const webpack = require('webpack')
const library = '[name]_lib'
const path = require('path')
module.exports = {
mode:"production",
entry: {
vendors: ['lodash']
},
output: {
filename: '[name].dll.js',
path: path.join(__dirname,"dist/vendor"),
library
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, 'dist/[name]-manifest.json'),
// This must match the output.library option above
name: library
}),
]
}
And then you need to include mainfest.json in your project webpack config:
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: path.join(__dirname, 'dist/vendors-manifest.json'),
})
]
3、 You can use externals to exclude it, you can config it like:
externals : {
lodash : {
commonjs: 'lodash',
amd: 'lodash',
root: '_' // indicates global variable
}
}
And don't forget to include lodsh script in HTML, because webpack don't compile or include it in your bundle. If you don't include ,the broswer will throw an error.
You can check more usage at Webpack website:
https://webpack.js.org/configuration/externals/
https://webpack.js.org/plugins/dll-plugin/

Whitescreen of death after pulling from git repo (ReactJS, Nginx)

Whenever I perform a git pull from my master branch onto my server, all my React files seem to just disappear and the screen turns white.
The temporary workarounds I had found were:
Delete browser cookies, cache & site history, and then close the browser and try again.
Delete node_modules, npm install all react dependencies again
After a while, the site reappears and everything works as normal until the next time after a few pull requests, the problem appears again.
Any console I use on any browser shows no error messages at all.
After 2+ weeks of googling around, I can't seem to find anything that relates to this issue.
Here are my specs:
Ubuntu 16.04 server
Framework: React 16.2.0
webpack 1.12
nginx version: nginx/1.10.3 (Ubuntu)
git version 2.7.4
My webpack settings (for clarity, I compile all my react files with the command):
node_modules/.bin/webpack --config webpack.local.config.js
(local)
var path = require("path")
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')
var config = require('./webpack.base.config.js')
config.devtool = "#eval-source-map"
config.plugins = config.plugins.concat([
new BundleTracker({
filename: './webpack-stats-local.json'
}),
])
config.module.loaders.push({
test: /\.js[x]?$/,
exclude: /node_modules/,
loaders: ['react-hot-loader/webpack', 'babel'],
})
module.exports = config
(base)
var path = require("path")
var webpack = require('webpack')
module.exports = {
context: __dirname,
entry: {
App1: './path/to/App1/',
App2: './path/to/App2/',
// ...
App10: './path/to/App10/',
vendors: ['react'],
},
output: {
path: path.resolve('./backend/static/bundles/local/'),
filename: "[name]-[hash].js"
},
externals: {
"gettext":"gettext",
"django":"django",
}, // add all vendor libs
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js'),
],
module: {
loaders: []
},
resolve: {
modulesDirectories: ['node_modules', 'bower_components'],
extensions: ['', '.js', '.jsx']
},
}
Any help will be greatly appreciated
I solved this problem by updating Webpack to version 4 + updating the dependencies i used while getting rid of the ones i don't use.

Webpack : config npm modules load

I'm new with webpack and I didn't find a way to load my vendor libs from node_modules with their dependencies (order and main files [js, css]) into a separate bundle file.
I used to work with bower_components but according to this documentation I moved to npm.
Example
I have angular material in my system that depends on angular and his own css file (angular-material.css).
What did I try already
I used the webpack.optimize.CommonsChunkPlugin which did load the venodors js files, but in the order that I entered in the configuration file (see config file attached below) so if I would have put angular material before angular the run would fail.
I tried configuring the resolve object to read from the "bower.json" which has a main files description and dependencies configured in it but that didn't work as well.
My config file
let webpack = require("webpack");
let path = require("path");
// define preprocessor variables
const envOptions = {
NODE_ENV: "development",
DEBUG: true,
isProduction: false
};
// pass as JSON object into query string ?json=...
const envJson = require('querystring').encode({json:JSON.stringify(envOptions)});
module.exports = {
context: __dirname + "/app",
entry: {
app: "./app.ts",
vendor: [
"jquery",
"angular-animate",
"angular-aria",
"angular-messages",
"angular-resource",
"angular-sanitize",
"angular-ui-router",
"angular-cache",
"angularjs-toaster",
"angular-translate",
"angular-translate-handler-log",
"angular-translate-loader-static-files",
"angular-material",
"angular",
"angular-dynamic-locale",
"ng-file-upload",
"angular-permission",
"moment",
"angular-moment",
"angular-hotkeys",
"angular-scroll",
"jquery.scrollbar",
"lodash",
"ag-grid",
"angular-ui-tree"],
},
output: {
filename: "bundle.js"
},
module: {
loaders: [
{ test: /\.ts/, loader: ['ts-loader?transpileOnly=true', `ifdef-loader?${envJson}`], exclude: [nodeModulesDir], }
]
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery" : "jquery",
"moment": "moment",
"agGrid": "ag-grid"
}),
new webpack.optimize.CommonsChunkPlugin(
{ name: 'vendor', filename: 'vendor.bundle.js' , test: 'node_modules/'}
)
],
resolve:{
modules: ['node_modules'],
extensions: ['.ts','.js', '.css', '.scss'],
},
devtool: "#source-map"
};
What do I want to achieve
Webpack will generate 2 bundles:
vendor.bundle.js - a file that contains all the vendor main js
files in the correct loading order.
app.bundle.js - a file that contains all of my app's inner files.
vendor.bundle.css - a file with all the vender main css files
both vendor bundles should be as defined as in the bower.json dependencies.
My Webpack version is 2.1.0-beta.25

Resources