jantimon/html-webpack-plugin

Is it possible to inject HTML code built from `.ejs` template into js while building and bundling with webpack?

ar-IGT opened this issue · 3 comments

ar-IGT commented

Description

Is it possible to inject HTML code built from .ejs template into js while building and bundling with webpack?
I want to build using all three configs, but the least step is to copy build outputs from first and then second step into last step's entry.
In single step/config, I neeed to have styles and HTML code to be injected and minified into corresponding js files, which are need to be also minified and uglified.
Last config is to just copy everything into single file.

Unfortunately this config doesn't work as intented, because it results with empty mm-bundle.[contenthash].js file, although all build processess aren't disrupted by errors.

Config

const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');

const commonsAndVendorsConfig = {
  context: path.resolve(__dirname, '.'),
  devServer: {
    client: {
      logging: 'verbose',
      overlay: true,
    },
    static: {
      directory: path.join(__dirname, './dist'),
    },
    compress: true,
    port: 9001,
  },
  entry: {
    vendors: '../shared_libs/modules/vendors.js',
    commons: '../shared_libs/modules/commons.js',
  },
  mode: 'production',
  module: {
    rules: [
      {
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            ['@babel/preset-env']
          ]
        },
        test: /\.js$/
      }
    ]
  },
  name: 'vendors-and-commons',
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        test: /\.js$/i,
        minify: (file, sourceMap, minimizerOptions) => {
          const extractedComments = [];
          const { map, code } = require('uglify-js').minify(file, {});
          return { map, code, extractedComments };
        },
      })
    ]
  },
  output: {
    filename: 'js/[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/'
  },
  performance: {
    maxEntrypointSize: 512000,
    maxAssetSize: 512000
  },
  target: ['web', 'es5']
};

const multipleModulesConfig = {
  context: path.resolve(__dirname, '.'),
  dependencies: ['vendors-and-commons'],
  devServer: {
    client: {
      logging: 'verbose',
      overlay: true,
    },
    static: {
      directory: path.join(__dirname, './dist'),
    },
    compress: true,
    port: 9001,
  },
  entry: {
    xmg_mm_wrapper_bottom_banner: './modules/xgm_mm_wrapper_bottom_banner.js',
    xmg_mm_wrapper_overhead_display: './modules/xgm_mm_wrapper_overhead_display.js',
    xmg_media_scheduler: './modules/xmg_media_scheduler.js',
    xmg_window_manager: './modules/xmg_window_manager.js'
  },
  mode: 'production',
  module: {
    rules: [
      {
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            ['@babel/preset-env']
          ]
        },
        test: /\.js$/
      },
      {
        exclude: /node_modules/,
        test: /\.s[ac]ss$/i,
        use: [
          'css-loader',
          'sass-loader'
        ]
      }
    ]
  },
  name: 'multiple-mm-modules',
  optimization: {
    minimize: true,
    minimizer: [
      new CssMinimizerPlugin({
        test: /\.css$/i
      }),
      new TerserPlugin({
        test: /\.js$/i,
        minify: (file, sourceMap, minimizerOptions) => {
          const extractedComments = [];
          const { map, code } = require('uglify-js').minify(file, {});
          return { map, code, extractedComments };
        },
      })
    ]
  },
  output: {
    filename: 'js/[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/'
  },
  performance: {
    maxEntrypointSize: 512000,
    maxAssetSize: 512000
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'xmg_mm_wrapper_bottom_banner.html',
      scriptLoading: 'blocking',
      template: 'src/html/xmg_mm_wrapper_template.ejs',
      templateParameters: {
        bodyClass: 'marketing-base',
        htmlClass: 'marketing-base',
        initConsoleLog: 'MMWrapper start'
      }
    }),
    new HtmlWebpackPlugin({
      filename: 'xmg_mm_wrapper_overhead_display.html',
      scriptLoading: 'blocking',
      template: 'src/html/xmg_mm_wrapper_template.ejs',
      templateParameters: {
        bodyClass: 'marketing-base',
        htmlClass: 'marketing-base',
        initConsoleLog: 'MMWrapper start'
      }
    }),
    new HtmlWebpackPlugin({
      filename: 'xmg_media_scheduler.html',
      scriptLoading: 'blocking',
      template: 'src/html/xmg_mm_wrapper_template.ejs',
      templateParameters: {
        bodyClass: 'media-scheduler-base',
        htmlClass: 'media-scheduler-base',
        initConsoleLog: 'xmg.MediaScheduler start'
      }
    }),
    new HtmlWebpackPlugin({
      filename: 'xmg_window_manager.html',
      scriptLoading: 'blocking',
      template: 'src/html/xmg_mm_wrapper_template.ejs',
      templateParameters: {
        bodyClass: 'window-manager-base',
        htmlClass: 'window-manager-base',
        initConsoleLog: 'xmg.WindowManager start'
      }
    })
  ],
  target: ['web', 'es5']
};

const singleModuleConfig = {
  context: path.resolve(__dirname, '.'),
  dependencies: ['multiple-mm-modules'],
  devServer: {
    client: {
      logging: 'verbose',
      overlay: true,
    },
    static: {
      directory: path.join(__dirname, './dist'),
    },
    compress: true,
    port: 9001,
  },
  entry: {
    'mm-bundle': { import: './modules/mm-bundle.js' },
  },
  mode: 'production',
  module: {
    rules: [
      {
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            ['@babel/preset-env']
          ]
        },
        test: /\.js$/
      },
      {
        exclude: /node_modules/,
        test: /\.s[ac]ss$/i,
        use: [
          'css-loader',
        ]
      }
    ]
  },
  name: 'mm-bundle',
  optimization: {
    minimize: true,
    minimizer: [
      new CssMinimizerPlugin({
        test: /\.css$/i
      }),
      new TerserPlugin({
        test: /\.js$/i,
        minify: (file, sourceMap, minimizerOptions) => {
          const extractedComments = [];
          const { map, code } = require('uglify-js').minify(file, {});
          return { map, code, extractedComments };
        },
      })
    ]
  },
  output: {
    clean: true,
    filename: 'js/[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/'
  },
  performance: {
    maxEntrypointSize: 512000,
    maxAssetSize: 512000
  },
  target: ['web', 'es5']
};

module.exports = [commonsAndVendorsConfig, multipleModulesConfig, singleModuleConfig];

And here's template:

<html class="<%= htmlClass %>">
<head>
    <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
    <title>Window Manager</title>

    <script type='application/javascript'>
        console.log('<%= initConsoleLog %>');
    </script>
</head>

<body class="<%= bodyClass %>">
    <div class="mmContentContainer"></div>
</body>

</html>

Environment

Node.js v14.21.3, win32 10.0.19045
npm version: 6.14.18
webpack@5.88.1
html-webpack-plugin@5.5.3

Hello, you need loader for such behaviour. Do you want to prebuild templates?

What kind of loader, what specific plugin?

Do you want to prebuild templates

I don't know myself if I really need to prebuild the templates.
If there's no easier way, then I guess I want to prebuild them.

@ar-IGT I ask this because I want to understand, if you want to load ejs and build template on your client you don't need this plugin, if you want to use ejs as a template for your HTML files, you need to install ejs-loader