webpack hot reload for html files not working in multi entry files

136 views Asked by At

haiving a node js express based project and used webpack to run on windows

now issue is that it reflect changes immediately in css and js files immediately but not for html files

here how I run projecet in local, first build it and later run it.


> npm run build
> npm run start

I understand that this is occuring because I am working in produstion mode and files are getting served from dist folder but then how come css and js file changes works i.e. hot reload works but only for css and js , some config is missing ...

below are the content of relevant file

package.json

{
  "scripts": {
    "build": "webpack --mode production --config webpack.prod.config.js",
    "clean": "rd /s /q dist >nul 2>&1|echo.>null",
    "start": "node ./dist/scripts/server.js",
    "dev": "webpack --config webpack.dev.config.js --mode development --watch"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/xkeshav/canvas.git"
  },
    "dependencies": {
    "@babel/preset-env": "^7.20.2",
    "cors": "^2.8.5",
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "path": "^0.12.7",
    "request": "^2.88.2"
  },
  "devDependencies": {
    "@babel/core": "^7.20.7",
    "@babel/eslint-parser": "^7.22.9",
    "@babel/register": "^7.0.0",
    "babel-loader": "^9.1.0",
    "copy-webpack-plugin": "^11.0.0",
    "css-loader": "^6.7.3",
    "css-minimizer-webpack-plugin": "^5.0.1",
    "eslint": "^8.30.0",
    "eslint-webpack-plugin": "^3.2.0",
    "html-loader": "^4.2.0",
    "html-webpack-plugin": "^5.5.0",
    "http-proxy-middleware": "^2.0.6",
    "mini-css-extract-plugin": "^2.7.6",
    "style-loader": "^3.3.1",
    "webpack": "^5.88.2",
    "webpack-cli": "^5.1.4",
    "webpack-dev-middleware": "^6.0.1",
    "webpack-dev-server": "^4.15.1",
    "webpack-hot-middleware": "^2.25.4",
    "webpack-node-externals": "^3.0.0"
  }

}

webpack.prod.config.js


require("dotenv").config();
const path = require("path");
const nodeExternals = require("webpack-node-externals");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

const plugins = require("./webpack.plugins.config");
const modules = require("./webpack.modules.config");

const DIST_DIR = path.join(__dirname, "dist");

const isProd = process.env.MODE === "production";

module.exports = {
  entry: {
    index: ["./src/index.js"],
    draw: ["./src/scripts/draw.js", "./src/styles/draw.css"],
    varnmala: ["./src/scripts/varnmala.js", "./src/styles/varnmala.css"],
    canvas: ["./src/scripts/canvas.js", "./src/styles/canvas.css"],
    server: ["/src/server/server.js"]
  },
  performance: {
    hints: "warning"
  },
  devServer: {
    port: 8080,
    hot: "only",
    static: {
      directory: path.join(__dirname, "./"),
      serveIndex: true
    }
  },
  output: {
    path: DIST_DIR,
    publicPath: "/",
    filename: "scripts/[name].js",
    chunkFilename: "scripts/[name].js",
    assetModuleFilename: "assets/[hash][ext][query]",
    clean: true
  },
  mode: process.env.MODE || "none",
  target: "node",
  node: {
    __dirname: false,
    __filename: false
  },
  externals: [nodeExternals()],
  devtool: isProd ? "source-map" : "eval-source-map",
  plugins,
  module: modules,
  resolve: {
    extensions: [".html", ".js", ".json", ".css"]
  },
  ...(isProd && {
    optimization: {
      minimizer: [new CssMinimizerPlugin()]
    }
  })
};

webpack modules and plugins are written in separate file

webpack.modules.config.js

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const modules = {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: "babel-loader"
      }
    },
    { test: /\.html$/i, loader: "html-loader" },
    {
      test: /\.css$/,
      use: [
        MiniCssExtractPlugin.loader,
        {
          loader: "css-loader",
          options: {
            importLoaders: 2,
            esModule: false
          }
        }
      ]
    },
    {
      test: /\.(png|svg|jpg|gif)$/,
      type: "asset/resource"
    },
    {
      test: /\.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
      type: "asset/resource",
      dependency: { not: ["url"] },
      generator: {
        filename: "assets/fonts/[hash][ext][query]"
      }
    }
  ]
};

module.exports = modules;


webpack.plugins.config.js


require("dotenv").config();

const webpack = require("webpack");
const path = require("path");

const HtmlWebpackPlugin = require("html-webpack-plugin");
const ESLintPlugin = require("eslint-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyPlugin = require("copy-webpack-plugin");

const BUILD_DIR = path.join(__dirname, "dist");
const HTML_DIR = path.join(BUILD_DIR, "html");

const htmlPageNames = ["about", "canvas", "draw", "varnmala"];

const multipleHtmlPlugins = htmlPageNames.map(
  (name) =>
    new HtmlWebpackPlugin({
      template: `./src/html/${name}.html`, // relative path to the HTML files
      filename: `${HTML_DIR}/${name}.html`, // output HTML files
      chunks: [`${name}`] // respective JS files
    })
);

const esLintOptions = {
  extensions: [`js`],
  exclude: [`/node_modules/`],
  emitWarning: true,
  failOnError: false
};

const plugins = [
  new HtmlWebpackPlugin({
    template: "src/html/index.html",
    filename: `${HTML_DIR}/index.html`,
    chunks: ["index"],
    excludeChunks: ["server"],
    title: "HMR for index.html"
  }),

  new webpack.HotModuleReplacementPlugin(),
  new ESLintPlugin(esLintOptions),
  new webpack.NoEmitOnErrorsPlugin(),
  new MiniCssExtractPlugin({
    filename: "styles/[name].css"
  })
].concat(multipleHtmlPlugins);

//console.log(process.env.MODE);

module.exports = plugins;


you can see this is multiple entry project so added every page as an seprate entry

src/server/server.js

the server file which runs the express js server and also having in webpack entry configuration have below code ( note it has some webpck related configuration too )


// @ts-nocheck
import express from "express";
import path from "path";
import { webpack } from "webpack";

import webpackDevMiddleware from "webpack-dev-middleware";
import webpackHotMiddleware from "webpack-hot-middleware";
import { alphabetMapper } from "../mappers/alphabet.js";
const config = require("../../webpack.client.config.js");

const app = express();

const router = express.Router();

const currentDirectory = process.cwd(); // current directory

const DIST_DIR = path.join(path.resolve(currentDirectory, "dist"));

const HTML_DIR = path.join(DIST_DIR, "html");
const HTML_FILE = path.join(HTML_DIR, "index.html");
const compiler = webpack(config);

app.use(
  webpackDevMiddleware(compiler, {
    publicPath: config.output.publicPath
  })
);

app.use(webpackHotMiddleware(compiler));

app.use(express.static(HTML_DIR));

app.get("/home", (_, res) => {
  res.sendFile(HTML_FILE);
});

app.get("/about", (_, res) => {
  res.sendFile(path.join(HTML_DIR, "about.html"));
});

app.get("/draw", (_, res) => {
  res.sendFile(path.join(HTML_DIR, "draw.html"));
});

app.get("/varnmala", (_, res) => {
  res.sendFile(path.join(HTML_DIR, "varnmala.html"));
});

app.get("/canvas", (_, res) => {
  res.sendFile(path.join(HTML_DIR, "canvas.html"));
});

//const readJson = (fileName) => {
//  let jsonObjData = [];
//  try {
//    const jsonStringData = fs.readFileSync(path.join(DIST_DIR, "json", fileName));
//    jsonObjData = JSON.parse(jsonStringData);
//  } catch (err) {
//    console.log(err);
//  }
//  return jsonObjData;
//};

app.get("/bg/:key", (req, res) => {
  //console.log("params", req.params.key);
  //const fileData = readJson("bg.json");
  const [output] = alphabetMapper.filter((f) => f.key === req.params.key.toLowerCase());
  console.log("called ==>", { output });
  res.json({ success: "canvas bg called", url: req.url, output });
});

app.use("/", router);

const PORT = process.env.PORT || 3003;

app.listen(PORT, () => {
  console.log(`App listening to ${PORT}....`);
  console.log("Press Ctrl+C to quit.");
});

also complete code can be accesed on github

so how do I hot reaload HTML file changeas also, which module, config or sny settings is missing?

if I need to run development mode separate and tries with webpack.dev.config.js but do not proper configuration and unable to figure out how to run project in dev mode, just using --mode=development seems not eough, if someone can help me in this context then it will be very helpful. one can see the complete issue mentioned on webpack discussionpage but no one replied so far.


update

in develop branch, I have separate prod and dev webpack and also the server/index.js (dev) and server/server.js (prod) to check any other way, but did not work. here is

webpack.dev.config.js

require("dotenv").config();
const path = require("path");
const nodeExternals = require("webpack-node-externals");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

const plugins = require("./webpack.plugins.config");
const modules = require("./webpack.modules.config");

const BUILD_DIR = path.join(__dirname, "build");

module.exports = {
  entry: {
    index: ["./src/index.js"],
    draw: ["./src/scripts/draw.js", "./src/styles/draw.css"],
    varnmala: ["./src/scripts/varnmala.js", "./src/styles/varnmala.css"],
    canvas: ["./src/scripts/canvas.js", "./src/styles/canvas.css"],
    server: ["./src/server/server.js"]
  },

  devServer: {
    static: DIST_DIR,
    compress: true,
    port: 3000,
    historyApiFallback: true,
    open: true,
    hot: true
  },

  output: {
    path: BUILD_DIR,
    publicPath: BUILD_DIR,
    filename: "scripts/[name].js",
    chunkFilename: "scripts/[name].js",
    assetModuleFilename: "assets/[hash][ext][query]",
    clean: true
  },
  mode: "development",
  target: "node",
  node: {
    __dirname: false,
    __filename: false
  },
  externals: [nodeExternals()],
  devtool: "eval-source-map",
  plugins,
  module: modules,
  resolve: {
    extensions: [".html", ".js", ".json", ".css"]
  },
  optimization: {
    runtimeChunk: "single"
  }
};

0

There are 0 answers