Search code examples
webpackrustviterollup

Webpack backend configuration to inject the bundles in a view template HTML page


I am trying to bundle all the js files into one file [name][hash].js and the same for CSS and assets. some thing the same as laravel-mix .

I see all bundlers generate their index.html file.

while all I need is to inject the generated css/JS files into my rust askama index.html file. BUT the idea is applicable for any backend view engine, the absence of a plugin means a lot of pain.

here is an example of my webpack.config.js which does not yield the result I need even with html-webpack-plugin which generates its own index.html in /dist.

import HtmlWebpackPlugin from 'html-webpack-plugin';

export default {
  mode: 'production',
  entry: '/assets/main.js',
  output: {
    filename: '[name].[contenthash].js',
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
  plugins: [new HtmlWebpackPlugin({ template: '/templates/index.html' })],
};

and here is my templates/index.html which is a backend file

<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{% block title %}
    {{ title }}{% endblock %}
  </title>
  {% block head %}{% endblock %}
</head>

<body>
  <div id="app"></div>
  <div id="content">
    {% block content %}
    <p>Dashboard</p>
    {% endblock %}
  </div>
</body>

</html>

I searched rollup and vite but the idea is difficult, I see all of these bundlers work their way not easily configured.


Solution

  • You can use the modern "html-bundler-webpack-plugin". The plugin resolves all source files of images, fonts, styles, scripts, etc., in any HTML template. The resolved references can be replaced with their output filenames or injected into HTML template.

    For example, add style and script source files directly is your HTML template:

    <html lang="en">
    
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>{% block title %}
        {{ title }}{% endblock %}
      </title>
    
      <!-- specify source files directly in HTML, relative to this file 
           or use webpack alias -->
      <link href="../assets/styles.scss" rel="stylesheet">
      <script src="../assets/main.js" defer="defer"></script>
    
      {% block head %}{% endblock %}
    </head>
    
    <body>
      <div id="app"></div>
      <div id="content">
        {% block content %}
        <p>Dashboard</p>
        {% endblock %}
      </div>
    
      <!-- source image file relative to this HTML file -->
      <img src="../assets/images/picture.png">
    </body>
    
    </html>
    

    webpack.config.js

    import HtmlBundlerPlugin from 'html-bundler-webpack-plugin';
    
    export default {
      mode: 'production',
      output: {
        // define the `path` as output directory of processed templates, defaults is `dist/`
      },
      plugins: [
        new HtmlBundlerPlugin({
          entry: {
            // define templates here
            // the key is output file path w/o extension, e.g.:
            index: 'templates/index.html', // => dist/index.html
            // - OR -
            'templates/index': 'templates/index.html', // => dist/templates/index.html
          },
          js: {
            // output filename of JavaScript, when inline is false (defaults)
            // filename: '[name].[contenthash:8].js',
            inline: true, // <= inject JS into HTML template
          },
          css: {
            // output filename of CSS, when inline is false (defaults)
            // filename: '[name].[contenthash:8].css',
            inline: true, // <= inject CSS into HTML template
          },
          // defaults used Eta (ESJ like) template engine to render into HTML
          preprocessor: false, // <= disable rendering into HTML to keep original template content
        }),
      ],
      module: {
        rules: [
          {
            test: /\.s?css$/i,
            use: ['css-loader', 'sass-loader'], // <= use SCSS to create one bundle file from many (S)CSS files
          },
          {
            test: /\.(ico|png|jp?g|webp|svg)$/,
            type: 'asset/inline', // <= inline images in their issuers (HTML, CSS)
          },
        ],
      },
    };
    

    If you will keep original template content and only inline JS/CSS/images, then disable the rendering using the preprocessor: false plugin option.

    If you will render any template into HTML, you can use one of supported templating engines "out of the box": Eta, EJS, Handlebars, Nunjucks, LiquidJS.

    The processed template will be looks like:

    <html lang="en">
    
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>{% block title %}
        {{ title }}{% endblock %}
      </title>
    
      <style>
        /* inlined CSS */
      </style>
    
      <script>
        /* inlined JS */
      </script>
    
      {% block head %}{% endblock %}
    </head>
    
    <body>
      <div id="app"></div>
      <div id="content">
        {% block content %}
        <p>Dashboard</p>
        {% endblock %}
      </div>
    
      <!-- inlined image (can be PNG, JPG, SVG, etc.) -->
      <img src="data:image/png;base64,iVBORw...">
    </body>
    
    </html>
    

    See:

    P.S. you can create a small repo with your base project files and I can help you configure it.