Alexandre Nypels
Alexandre Nypels

Reputation: 11

Splitting HTML code into multiple files using webpack

I'm trying desperately to split my HTML into multiple files using Webpack. I've tried the "<%= require() %>" method but doesn't work. Only work with my images. See my webpack config below. Basically what I wanna do is this:

file1.html

<h1>This is my file number 1</h1>

file 2.html

<h1>This is my file number 2</h1>
<%= require("file1.html") %>

And then webpack when rendering the index.html file, will bundle those two files together. This is my webpack.config.js:

const path = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const webpack = require("webpack");



module.exports = {
    entry: "./src/assets/js/index.js",
    mode: "development",
    output: {
        filename: '[name].[contenthash].js',
        path: path.resolve(__dirname, "dist"),
        clean: {
            keep: /images\//, // Keep these assets under 'ignored/dir'.
        },
    },
    devtool: 'inline-source-map',
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                          publicPath: "./"
                        }
                    },
                      {
                        loader: 'css-loader',
                        options: {importLoaders: 2, url: false},
                    },
                      {
                        loader: 'postcss-loader',
                        options: {
                          postcssOptions: {
                            config: path.resolve(__dirname, 'postcss.config.js'),
                          },
                        },
                    },
                ],
            }, 

            {
                test: /\.(png|jpe?g|gif|svg)$/i,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            filename: "[name].[hash:6].[ext]",
                            outputPath: 'images/',
                            emitFile: true,
                            esModule: false
                        },
                    },
                ], 
            },
        ],
    },
    plugins: [
        new HtmlWebpackPlugin({
          title: 'Caching',
          template: "./src/template.html"
        }),
        new MiniCssExtractPlugin({
            filename: "[name].[contenthash].css",
        }),
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery',
        }),
    ],
    optimization: {
        splitChunks: {
            cacheGroups: {
              vendor: {
                test: /[\\/]node_modules[\\/]/,
              },
            },
        },
    },
}

Any help is more than welcome. Thanks in advance!

Upvotes: 1

Views: 2213

Answers (1)

Harshal Patil
Harshal Patil

Reputation: 20930

It is not really possible out-of-box with either Webpack or html-webpack-plugin. However, you have multiple possible options to achieve this.

Option 1

Read the HTML files that you intend to inject into your main HTML and then use template context using templateParameters to pass your HTML content. For example:

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

const files = {
  file1: fs.readFileSync('file1.html', { encoding: 'utf-8' }),
  file2: fs.readFileSync('file1.html', { encoding: 'utf-8' })
};


module.exports = {
  // ...other configuration
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Caching',
      template: './src/template.html',
      templateParameters: {
        files
      }
    })
  ]
};

Your template.html file would be:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <div><%= files.file1 %></div>
    <div><%= files.file2 %></div>
  </body>
</html>

The limitation here is that you can have includes only inside the template.html file. You cannot have recursive include like file2.html from file1.html. But this will work only if you intend to generate single HTML file.

Option 2

You can use the plugins for html-webpack-plugin. This plugin provides a nice support for partials. And, then there are few more plugins like this and this that can be used. Again these plugins will help you augment your generated html template and not support recursive include inside the html files.

Option 3

Using html-loader in combination extract-loader. This approach is flexible and can be used in multiple ways including using posthtml but it involves lots of configuration. You will have to write your own plugin in combination with other template loader that supports partials like handlebar-loader and then emit the HTML file.

Updates on comments

There are the two options you can try. First do not try to require the images inside the html partial. Instead add image using root relative paths like:

<-- partial.html --!>
<img src="/my/root/relative/image.png" />

The use Webpack copy plugin to copy the images into your dist folder.

I have not fully tried it but the second option is to disable the loader of html-webpack-plugin. Instead use html-loader with the plugin like below. By introducing the html-loader, the HTML file will be processed, the images would be picked up by the file-loader before the final HTML is seen by the html plugin.

const config = {
  module: {
    rules: [
      {
        test: /\.html$/,
        loader: 'html-loader'
      }],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    })
  ]
};

Upvotes: 2

Related Questions