Search code examples
javascripthtmlwebpacklodash

Template in HTML, with Webpack, get error "variable" is not defined


I created template in index.html to generate html-code with js, code below. My Webpack configuration also below. When I run it with webpack-dev-server, I get error: title is not defined. Somehow webpack tries to resolve 'title' by self, instead of delegate it to 'lodash/template'. Please help me fix code, I'm in despair(.

import path from 'path';
import glob from 'glob';
import webpack from 'webpack';

import ExtractTextPlugin from 'extract-text-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';

const inProduction = process.env.mode === 'production';

export default {
  entry: {
    app: [
      './src/scripts/main.js',
    ],
  },
  output: {
    path: path.join(__dirname, 'build'),
    filename: '[name].[chunkhash].js',
  },
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/,
        use: ExtractTextPlugin.extract({
          use: ['css-loader', 'sass-loader'],
          fallback: 'style-loader',
        }),
      },
      {
        test: /\.js$/,
        use: 'babel-loader',
        exclude: '/node_modules',
      },
    ],
  },
  plugins: [
    new ExtractTextPlugin('[name].[chunkhash].css'),
    new webpack.LoaderOptionsPlugin({
      minimize: inProduction,
    }),
    new HtmlWebpackPlugin({
      template: path.join(__dirname, './src/index.html'),
    }),
  ],
};

import temp from 'lodash/template'
import data from './data';

const titlePicDiscHalf = temp(document.getElementById('titlePicDiscHalf').innerHTML);
document.write(titlePicDiscHalf({ title: 'Hello World!' }));
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  </head>
  <body>
    <script type="text/template" id="titlePicDiscHalf">
      <div class="titlePicDiscHalf">
        <div class="picture"></div>
        <div class="title"><%=title%></div>
        <div class="discription"></div>
        <div class="buttons"></div>
      </div>
    </script>
  </body>
</html>


Solution

  • The problem is that html-webpack-plugin uses the same template tags <%= %> to insert bundle information into template.

    You have two options.

    1. Change lodash.template delimiters

    You could change delimiters used by client-side lodash/template to something else, so Webpack would ignore it. For example:

    _.templateSettings.interpolate = /<\$=([\s\S]+?)\$>/g;
    

    Check out this demo.

    _.templateSettings.interpolate = /<\$=([\s\S]+?)\$>/g;
    
    const temp = _.template
    const titlePicDiscHalf = temp(document.getElementById('titlePicDiscHalf').innerHTML);
    
    document.write(titlePicDiscHalf({ title: 'Hello World!' }));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
    
    <script type="text/template" id="titlePicDiscHalf">
      <div class="titlePicDiscHalf">
        <div class="picture"></div>
        <div class="title">
          <$=title$>
        </div>
        <div class="discription"></div>
        <div class="buttons"></div>
      </div>
    </script>

    2. Change html-webpack-plugin delimiters

    Install ejs-loader separately and configure html-webpack-plugin to use it to load your template. There you can change delimiters to yours. It could look something like this:

    plugins: [
      new HtmlWebpackPlugin({
        template: './index.html.ejs',
      })
    ],
    module: {
      rules: [
        { test: /\.ejs$/, loader: 'ejs-loader', query: {
          interpolate: /<\$=([\s\S]+?)\$>/g,
          evaluate: /<\$([\s\S]+?)\$>/g,
        }},
      ]
    },
    

    Now, you can configure your template with two different set of delimiters, one for client bundle lodash template and another for html-webpack-plugin:

    <!doctype html>
    <html lang="en">
      <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <title><$= htmlWebpackPlugin.options.title $></title>
      </head>
      <body>
        <script type="text/template" id="titlePicDiscHalf">
          <div class="titlePicDiscHalf">
            <div class="picture"></div>
            <div class="title"><%= title %></div>
            <div class="discription"></div>
            <div class="buttons"></div>
          </div>
        </script>
      </body>
    </html>
    

    Note, <title><$= htmlWebpackPlugin.options.title $></title> is used by webpack, and <div class="title"><%= title %></div> is by client-side lodash.