2

I own the following pages:

  1. about
  2. contact
  3. home
  4. portfolio

And these pages are "templates", where nothing more, nothing less, I inject the head with index.js and index.scss in them, along with title, goals etc that are the same for all.

But in development mode, it still leaves the <%= htmlWebpackPlugin.tags.headTags %>

enter image description here

HTML TEMPLATE PAGE EXAMPLE:

<html lang="en">

<head>
    <%= htmlWebpackPlugin.tags.headTags %>
</head>

<body>
    X
</body>

</html>

WEBPACK CONFIG:

const path = require('path');
const AutoPrefixer = require('autoprefixer');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');

const HTML_WEBPACK_PLUGIN = {
    minify: true,
    cache: true,
    favicon: '',
    meta: {
        viewport: 'width=device-width, initial-scale=1',
    },
};

module.exports = {
    entry: './src/index.js',
    mode: 'production',
    plugins: [
        new HtmlWebpackPlugin({ ...HTML_WEBPACK_PLUGIN, title: 'HOME PAGE', filename: 'index.html', template: 'src/pages/home.html' }),
        new HtmlWebpackPlugin({ ...HTML_WEBPACK_PLUGIN, title: 'ABOUT PAGE', filename: 'about.html', template: 'src/pages/about.html' }),
        new HtmlWebpackPlugin({
            ...HTML_WEBPACK_PLUGIN,
            title: 'PORTFOLIO PAGE',
            filename: 'portfolio.html',
            template: 'src/pages/portfolio.html',
        }),
        new HtmlWebpackPlugin({
            ...HTML_WEBPACK_PLUGIN,
            title: 'CONTACT PAGE',
            filename: 'contact.html',
            template: 'src/pages/contact.html',
        }),
        new MiniCssExtractPlugin({
            filename: '[name].[contenthash].css',
            chunkFilename: '[id].[contenthash].css',
        }),
    ],
    devServer: {
        static: path.resolve(__dirname, 'dist'),
        port: 8080,
        hot: false,
        liveReload: true,
    },
    optimization: {
        runtimeChunk: 'single',
        minimizer: [
            new CssMinimizerPlugin({
                minify: CssMinimizerPlugin.cleanCssMinify,
                parallel: true,
            }),
            new TerserPlugin({
                test: /\.js(\?.*)?$/i,
                parallel: true,
                minify: TerserPlugin.uglifyJsMinify,
            }),
        ],
    },
    output: {
        clean: true,
        filename: '[name].[contenthash].js',
        chunkFilename: '[id].[contenthash].js',
        path: path.resolve(__dirname, 'dist'),
    },
    module: {
        rules: [
            {
                test: /\.m?js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        cacheDirectory: true,
                        cacheCompression: true,
                        presets: ['@babel/preset-env'],
                        plugins: ['@babel/plugin-transform-runtime'],
                    },
                },
            },
            {
                test: /\.html$/i,
                loader: 'html-loader',
            },
            {
                mimetype: 'image/svg+xml',
                scheme: 'data',
                type: 'asset/resource',
                generator: {
                    filename: 'icons/[hash].svg',
                },
            },
            {
                test: /\.(sa|sc|c)ss$/, // SASS AND CSS
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                    },
                    {
                        loader: 'css-loader',
                    },
                    {
                        loader: 'postcss-loader',
                        options: {
                            postcssOptions: {
                                plugins: () => [AutoPrefixer()],
                            },
                        },
                    },
                    {
                        loader: 'sass-loader',
                    },
                ],
            },
        ],
    },
};

1 Answer 1

0

From the Setting a loader using the module.rules syntax documentation, we know:

However this also means that in the following example webpack will use the html-loader for your template. This will cause html minification and it will also disable the ejs/lodash fallback loader.

You are using html-loader in module.rules, it will disable the ejs/lodash fallback loader of html-webpack-plugin. There is no template engine to process the <%= %> tag. That's why it still leaves <%= htmlWebpackPlugin.tags.headTags %>.

Solution: use the html-loader inline in the HTML template like below

require('html-loader!./partial.html').default

So that html-webpack-plugin can use ejs/lodash template engine to process the template special <%= %> tag and we can also use html-loader to process other parts of the HTML template.

E.g.

project structure:

$ tree -L 2 -I node_modules
.
├── dist
│   ├── 55b19870aff2e53d1fb1.png
│   ├── index.html
│   └── main.acd85cb0428d8156db50.js
├── package-lock.json
├── package.json
├── src
│   ├── body.html
│   ├── index.html
│   ├── index.js
│   └── logo.png
└── webpack.config.js

2 directories, 10 files

src/index.html:

<html lang="en">
    <head>
        <%= htmlWebpackPlugin.tags.headTags %>
    </head>

    <%= require('html-loader!./body.html').default %>
</html>

src/body.html:

<body>
    <img
        src="./logo.png"
        alt="logo"
    />
</body>

webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    mode: 'production',
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            inject: false,
        }),
    ],
    output: {
        clean: true,
        filename: '[name].[contenthash].js',
        path: path.resolve(__dirname, 'dist'),
    },
    module: {
        rules: [
            // {
            //  test: /\.html$/i,
            //  loader: 'html-loader',
            // },
        ],
    },
};

Build logs:

> webpack

assets by status 52.8 KiB [cached] 2 assets
asset index.html 163 bytes [emitted]
./src/index.js 37 bytes [built] [code generated]
webpack 5.88.2 compiled successfully in 300 ms

Output

dist/index.html:

<html lang="en">
    <head>
        <script
            defer="defer"
            src="main.acd85cb0428d8156db50.js"
        ></script>
    </head>
    <body>
        <img
            src="55b19870aff2e53d1fb1.png"
            alt="logo"
        />
    </body>
</html>

If we use html-loader in module.rules, the output of dist/index.html will be:

<html lang="en"><head> <%= htmlWebpackPlugin.tags.headTags %> </head> <%= require('html-loader!./body.html').default %> </html>

package versions:

"devDependencies": {
  "html-loader": "^4.2.0",
  "html-webpack-plugin": "^5.5.3",
  "webpack": "^5.80.0",
  "webpack-cli": "^5.0.2",
  "webpack-dev-server": "^4.15.1"
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.