title: How to generate html template for static page from header/footer (and css/js inject) in Webpack 4 (lodash template not working)?
body: <p>I'm trying to build static html pages with webpack 4. I'm using <a href="https://webpack.js.org/plugins/html-webpack-plugin/" rel="nofollow noreferrer">html-webpack-plugin</a>. I want to define some <strong>header.html</strong> and <strong>footer.html</strong> and import them later in all html pages. But I want also then my output js and css files automatically inject to this header and footer. Something like this:</p>
  
  <p><strong>header.html:</strong></p>
  
  <pre><code>&lt;!DOCTYPE html&gt;
  &lt;html lang="en"&gt;
  &lt;head&gt;
      &lt;meta charset="utf-8"&gt;
      &lt;meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"&gt;
      &lt;title&gt;Some Title&lt;/title&gt;
      &lt;% for(var i=0; i &lt; htmlWebpackPlugin.files.css.length; i++) {%&gt;
          &lt;link type="text/css" rel="stylesheet" href="&lt;%= htmlWebpackPlugin.files.css[i] %&gt;"&gt;
      &lt;% } %&gt;
  &lt;/head&gt;
  &lt;body&gt;
  </code></pre>
  
  <p><strong>footer.html:</strong></p>
  
  <pre><code>    &lt;% for(var i=0; i &lt; htmlWebpackPlugin.files.js.length; i++) {%&gt;
          &lt;script type="text/javascript" src="&lt;%= htmlWebpackPlugin.files.js[i] %&gt;"&gt;&lt;/script&gt;
      &lt;% } %&gt;
  &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  
  <p><strong>index.hbs (or index.html):</strong></p>
  
  <pre><code>&lt;%= _.template(require('./../includes/header.html'))() %&gt;
  
      &lt;div class="content"&gt;
          &lt;img src="img/bg/desktop/bg-index-03.jpg" width="500" height="500"/&gt;
          &lt;div class="imgtest"&gt;&lt;/div&gt;
      &lt;/div&gt; &lt;!-- .content --&gt;
  
  &lt;%= _.template(require('./../includes/footer.html'))() %&gt;
  </code></pre>
  
  <p>But I get in <strong>dist/index.html</strong> some bug. These lines still the same and don't loading header/footer to index.html:</p>
  
  <pre><code>&lt;%= _.template(require('./../includes/header.html'))() %&gt;
  ...
  &lt;%= _.template(require('./../includes/footer.html'))() %&gt;
  </code></pre>
  
  <p>For some reason lodash doesn't work...</p>
  
  <h1>UPD:</h1>
  
  <p>I changed <code>src/html/views/index.hbs</code> to <code>src/html/views/index.html</code>
  (and in plugin)</p>
  
  <pre><code>new HtmlWebpackPlugin({
    template: './src/html/views/index.html',
  ...
  </code></pre>
  
  <p>Add <code>include</code> line here:</p>
  
  <pre><code>{
      test: /\.html$/,
      include: path.resolve(__dirname, 'src/html/includes'),
      use: ['html-loader']
  },
  </code></pre>
  
  <p>In header/footer I removed lodash code <code>&lt;% ... %&gt;</code>, leaving only clean html - so it work! Header/footer imported in index.html, but without css/js.</p>
  
  <p>If I return lodash back in footer.html (or header.html)</p>
  
  <pre><code>&lt;% for(var i=0; i &lt; htmlWebpackPlugin.files.js.length; i++) {%&gt;
      &lt;script type="text/javascript" src="&lt;%= htmlWebpackPlugin.files.js[i] %&gt;"&gt;&lt;/script&gt;
  &lt;% } %&gt;
  </code></pre>
  
  <p>I get error:</p>
  
  <pre><code>ERROR in Template execution failed: ReferenceError: htmlWebpackPlugin is not defined
  
  ERROR in   ReferenceError: htmlWebpackPlugin is not defined
  
    - lodash.templateSources[2]:10 eval
      lodash.templateSources[2]:10:19
  
    - index.html:102 
      D:/.../src/html/views/index.html:102:110
  
    - index.html:104 ./node_modules/html-webpack-plugin/lib/loader.js!./src/html/views/index.html.module.exports
      D:/.../src/html/views/index.html:104:3
  
    - index.js:393 
      [project]/[html-webpack-plugin]/index.js:393:16
  
    - runMicrotasks
  
    - task_queues.js:93 processTicksAndRejections
      internal/process/task_queues.js:93:5
  
    - async Promise.all
  </code></pre>
  
  <p><strong>Why it happens? What's wrong? How to make lodash works in header/footer?</strong></p>
  
  <p><strong>Or what is the best way to generate html templates?</strong></p>
  
  <p><strong>file tree:</strong></p>
  
  <pre><code>dist
  │   index.html
  ├───css
  │       main.css
  │       main.css.map
  ├───fonts
  ├───img
  ├───js
  │       main.js
  │       main.js.map
  │       vendors~main.js
  │       vendors~main.js.map
  src
  ├───favicon
  ├───fonts
  ├───html
  │   ├───includes
  │   │       footer.html
  │   │       header.html
  │   └───views
  │           index.hbs
  ├───img
  ├───js
  │       index.js
  ├───scss
  │       fonts.scss
  │       icomoon.scss
  │       style.scss
  package.json
  package-lock.json
  webpack.config.js
  </code></pre>
  
  <p><strong>webpack.config.js:</strong></p>
  
  <pre><code>const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const { CleanWebpackPlugin } = require('clean-webpack-plugin');
  const MiniCssExtractPlugin = require('mini-css-extract-plugin');
  const TerserWebpackPlugin = require('terser-webpack-plugin');
  const OptimizeCssAssetWebpackPlugin = require('optimize-css-assets-webpack-plugin');
  const CopyWebpackPlugin = require('copy-webpack-plugin');
  
  const isDev = process.env.NODE_ENV === 'development';
  const isProd = !isDev;
  
  const optimization = () =&gt; {
    const config = {
      splitChunks: {
        chunks: 'all'
      }
    }
    if (isProd) {
      config.minimizer = [
        new OptimizeCssAssetWebpackPlugin(),
        new TerserWebpackPlugin()
      ]
    }
    return config
  }
  
  const filename = ext =&gt; isDev ? `[name].${ext}` : `[name].[hash].${ext}`;
  
  const cssLoaders = extra =&gt; {
    const loaders = [
      {
        loader: MiniCssExtractPlugin.loader,
        options: {
          hmr: isDev,
          reloadAll: true
        },
      },
      {
        loader: 'css-loader',
        options: {
          url: false
        }
      }
    ];
    if (extra) {
      loaders.push(extra)
    }
    return loaders
  }
  
  const babelOptions = preset =&gt; {
    const opts = {
      presets: [
        '@babel/preset-env'
      ],
      plugins: [
        '@babel/plugin-proposal-class-properties'
      ]
    }
  
    if (preset) {
      opts.presets.push(preset)
    }
  
    return opts
  }
  
  module.exports = {
    mode: 'development',
    entry: [
      '@babel/polyfill',
      './src/js/index.js'
    ],
    output: {
      filename: 'js/' + filename('js'),
      path: path.resolve(__dirname, 'dist')
    },
    devServer: {
      port: 4250,
      hot: isDev
    },
    devtool: isDev ? 'source-map' : '',
    resolve: {
      //extensions: ['.js', '.json', '.png'],
      alias: {
        '@views': path.resolve(__dirname, 'src/html/views'),
        '@includes': path.resolve(__dirname, 'src/html/includes'),
        '@scss': path.resolve(__dirname, 'src/scss'),
        '@': path.resolve(__dirname, 'src'),
      }
    },
    optimization: optimization(),
    module: {
      rules: [
        {
          test: /\.html$/,
          //include: path.resolve(__dirname, 'src/html/includes'),
          use: ['html-loader']
        },
        {
          test: /\.hbs$/,
          loader: 'handlebars-loader'
        },
        {
          test: /\.js$/,
          exclude: /node_modules/,
          loader: {
            loader: 'babel-loader',
            options: babelOptions()
          }
        },
        {
          test: /\.css$/,
          use: cssLoaders()
        },
        {
          test: /\.s[ac]ss$/,
          use: cssLoaders('sass-loader')
        },
        {
          test: /\.(png|jpg|svg|gif)$/,
          exclude: path.resolve(__dirname, 'src/fonts'),
          use: [
            {
              loader: 'file-loader',
              options: {
                name: '[name].[ext]',
                outputPath: path.resolve(__dirname, 'dist/img')
              }
            }
          ]
        },
        {
          test: /\.(ttf|otf|svg|woff|woff2|eot)$/,
          exclude: path.resolve(__dirname, 'src/img'),
          use: [
            {
              loader: 'file-loader',
              options: {
                name: '[name].[ext]',
                outputPath: path.resolve(__dirname, 'dist/fonts')
              }
            }
          ]
        },
      ]
    },
    plugins: [
      new HtmlWebpackPlugin({
        template: './src/html/views/index.hbs',
        minify: {
          collapseWhitespace: isProd
        },
        inject: false
      }),
      new CleanWebpackPlugin(),
      new MiniCssExtractPlugin({
        filename: 'css/' + filename('css'),
      }),
      new CopyWebpackPlugin([
        {
          from: path.resolve(__dirname, 'src/favicon'),
          to: path.resolve(__dirname, 'dist')
        },
        {
          from: path.resolve(__dirname, 'src/fonts'),
          to: path.resolve(__dirname, 'dist/fonts')
        },
        {
          from: path.resolve(__dirname, 'src/img'),
          to: path.resolve(__dirname, 'dist/img')
        }
      ])
    ]
  };
  </code></pre>
  
  <p><strong>package.json</strong>:</p>
  
  <pre><code>{
    "name": "Name",
    "version": "1.0.0",
    "description": "",
    "private": true,
    "scripts": {
      "dev": "cross-env NODE_ENV=development webpack --mode development",
      "build": "cross-env NODE_ENV=production webpack --mode production",
      "watch": "cross-env NODE_ENV=development webpack --mode development --watch",
      "start": "cross-env NODE_ENV=development webpack-dev-server --mode development --open"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
      "@babel/core": "^7.9.0",
      "@babel/plugin-proposal-class-properties": "^7.8.3",
      "@babel/preset-env": "^7.9.5",
      "babel-loader": "^8.1.0",
      "clean-webpack-plugin": "^3.0.0",
      "copy-webpack-plugin": "^5.1.1",
      "cross-env": "^7.0.2",
      "css-loader": "^3.5.2",
      "file-loader": "^6.0.0",
      "html-loader": "^1.1.0",
      "html-webpack-plugin": "^4.2.0",
      "mini-css-extract-plugin": "^0.9.0",
      "node-sass": "^4.13.1",
      "optimize-css-assets-webpack-plugin": "^5.0.3",
      "raw-loader": "^4.0.1",
      "resolve-url-loader": "^3.1.1",
      "sass-loader": "^8.0.2",
      "style-loader": "^1.1.4",
      "terser-webpack-plugin": "^2.3.5",
      "webpack": "^4.42.1",
      "webpack-cli": "^3.3.11",
      "webpack-dev-server": "^3.10.3"
    },
    "browserslist": "defaults",
    "dependencies": {
      "@babel/polyfill": "^7.8.7",
      "bootstrap": "^4.4.1",
      "handlebars": "^4.7.6",
      "handlebars-loader": "^1.7.1",
      "jquery": "^3.5.0",
      "popper.js": "^1.16.1"
    }
  }
  </code></pre>