errorline1
errorline1

Reputation: 426

Way to generate HTML during build with handlebars

Using handlebars-loader, I'm looking to compile and then call the template function, so I can pass the generated HTML to other loaders, however whatever I try it seems I can only manage to get the templating function itself. For example

    {
      test: /\.hbs$/,
      loaders: [
        {
          loader: 'file-loader',
          options: {
            name: '[name].html'
          }
        },
        'extract-loader',
        'html-loader',
        'handlebars-loader'
      ],
      enforce: 'pre'
    },

Would be great, generate HTML, pass it through html-loader to get webpack's require goodness, and then extract the HTML to it's own files (example based on Webpacks html-loader documentation). However, if for example I run index.hbs into this, the output into index.html is

var Handlebars = require("<dir>/node_modules/handlebars/runtime.js");
function __default(obj) { return obj && (obj.__esModule ? obj["default"] : obj); }
module.exports = (Handlebars["default"] || Handlebars).template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
    var stack1;

  return ((stack1 = container.invokePartial(require("<dir>/src/pages/partials/head.hbs"),depth0,{"name":"head","data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "")
    + "<main>\n<p>"
    + container.escapeExpression(container.lambda((depth0 != null ? depth0.formatMessage : depth0), depth0))
    + "</p>\n</main>\n"
    + ((stack1 = container.invokePartial(require("<dir>/src/pages/partials/content-info.hbs"),depth0,{"name":"content-info","data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");
},"usePartial":true,"useData":true});

Note that I am also using babel-loader to load a file, which calls require(./index.hbs).

What I'd like to get is a normal HTML file, not the Handlebars function

Any suggestion for a direction to investigate would be great!

Upvotes: 1

Views: 4484

Answers (2)

Kaustubh Badrike
Kaustubh Badrike

Reputation: 609

Solved a similar problem using two separate build config:

  1. First generates html from handlebars using handlebars-webpack-plugin.
new HandlebarsPlugin({
    // path to hbs entry file(s). Also supports nested directories if write path.join(process.cwd(), "app", "src", "**", "*.hbs"),
    entry: path.join(process.cwd(), "handlebars", "*.hbs"),
    // output path and filename(s). This should lie within the webpacks output-folder
    // if ommited, the input filepath stripped of its extension will be used
    output: path.join(process.cwd(), "html", "[name].html"),
    // you can also add a [path] variable, which will emit the files with their relative path, like
    // output: path.join(process.cwd(), "build", [path], "[name].html"),

    // globbed path to partials, where folder/filename is unique
    partials: [
        path.join(process.cwd(), "handlebars", "partials", "**", "*.hbs")
    ],
})
  1. Second uses html-loader on the output of the first.
module.exports = {
    module: {
        rules: [
            {
                test: /\.html$/i,
                loader: 'html-loader',
                include: [
                    path.resolve(__dirname, "html"),
                ],
            },
            {
                test: /\.css$/i,
                type: 'asset/resource',
                include: [
                    path.resolve(__dirname, "css"),
                ],
            },
        ],
    },
    plugins: [
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: 'html/index.html',
        }),
    ],
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist'),
        clean: true,
    },
};

Upvotes: 1

Pietro Saccardi
Pietro Saccardi

Reputation: 2622

If I read your question correctly, you want to combine handlebars-loader HTML processing together with html-loader ability to detect dependencies from the HTML file.

The point is that handlebars need to be evaluated in a context, since it references external variables. That's why extracting content and using a file loader won't work; you need an intermediate loader or plugin that renders the handlebars.

You could require the HBS template file in your JS file, and then evaluate it there, as it's done in Handlebars Loader examples, but then again you don't have a file.

What I think is close to what you want, is to use the handlebars files as a templates for HTML Webpack Plugin. You could use an HTML Loader followed by Extract Loader and then Handlebars Loader, but you would be feeding handlebars entities to the HTML Loader, which is not very stable I suppose. Luckily, there is a plugin that combines the three, Handlebars Template Loader (npm install --save-dev handlebars handlebars-template-loader) and you can set it up as follows:

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

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      hash: false,
      inject: false,
      template: './src/index.hbs',
    }),
    // ...
  ],
  module: {
      {
        test: /\.hbs$/,
        use: ['handlebars-template-loader'],
      },
      // ...
    ]
  }
};

Now the HTML Webpack plugin will request the HBS file, which will be loaded through the Handlebars Template Loader, which will parse the HTML file and detect further dependencies, and then rendered in context by the HTML Webpack Plugin.

Upvotes: 2

Related Questions