shannonmoeller / handlebars-layouts

Handlebars helpers which implement layout blocks similar to Jinja, Nunjucks (Swig), Pug (Jade), and Twig.
http://npm.im/handlebars-layouts
MIT License
361 stars 29 forks source link

Need help using this with webpack #52

Closed Paillat-dev closed 5 months ago

Paillat-dev commented 5 months ago

Hello, I am trying to use theese layouts with webpack, and have webpack build a hbs file to an html file, but experiencing many issues. I am quite new with thiw and with javascript in general, so I might be missing something very stupid.

Here is my webpack config:

// webpack.common.js
const path = require('path');

module.exports = {
  entry: {
    app: './src/js/app.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    clean: true,
    filename: './js/app.js',
  },
  module: {
    rules: [
      {
        test: /\.css$/,               // Targets all .css files
        include: path.resolve(__dirname, 'src/css'), // Adjust if your CSS files are located elsewhere
        use: [
          'style-loader', // Adds CSS to the DOM by injecting a <style> tag
          'css-loader',   // Interprets `@import` and `url()` like `import/require()` and resolves them
          'postcss-loader' // Processes CSS with PostCSS
        ]
      },
      {
        test: /\.hbs$/, // Handle .hbs files
        loader: 'handlebars-loader',
        options: {
          precompileOptions: {
            knownHelpersOnly: false
          },
          partialDirs: [
            './src/partials',
          ],
          inlineRequires: '/img/',
          runtime: path.resolve(__dirname, 'src/js/handlebars-runtime'),
        },
      }
    ]
  },
};
// webpack.config.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');

module.exports = merge(common, {
  mode: 'production',
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: './src/de.hbs',
    }),
    new CopyPlugin({
      patterns: [
        { from: './src/img', to: 'img' },
        { from: './src/css', to: 'css' },
        { from: './src/js/vendor', to: 'js/vendor' },
        { from: './src/icon.svg', to: 'icon.svg' },
        { from: './src/favicon.ico', to: 'favicon.ico' },
        { from: './src/robots.txt', to: 'robots.txt' },
        { from: './src/icon.png', to: 'icon.png' },
        { from: './src/404.html', to: '404.html' },
        { from: './src/site.webmanifest', to: 'site.webmanifest' },
      ],
    }),
  ],
});
// src/js/handlebars-runtime.js
const fs = require('fs');
const Handlebars = require('handlebars/runtime');
const layouts = require('handlebars-layouts');

Handlebars.registerHelper(layouts(Handlebars));
// partial languages is in src/js/partials/languages.hbs (../partials/languages.hbs)
const languages = fs.readFileSync('./src/partials/languages.hbs', 'utf8');
const base = fs.readFileSync('./src/partials/base.hbs', 'utf8');
Handlebars.registerPartial('languages', languages);
Handlebars.registerPartial('base', base);

module.exports = Handlebars;

This is based on the html boilerplate webstorm gave me.

And here is the error I am getting:

ERROR in Template execution failed: TypeError: handlebars.compile is not a function

ERROR in   TypeError: handlebars.compile is not a function

I would really appreciate any help on this matter

webdiscus commented 5 months ago

@Paillat-dev

if you want to use an extendable layout, so each page uses the same layout file, then you can try actual html-bundler-webpack-plugin. This plugin allow very easy to configure the webpack to render handlebars (and many other template engines).

Here is the webpack-starter-with-handlebars with the example exactly for your use case.

For example, there is the layout.hbs:

<!DOCTYPE html>
<html>
<head>
  {{! relative path to source scripts and styles files will be auto resolved and compiled }}
  <link href="./style.scss" rel="stylesheet">
  <script src="./main.js" defer="defer"></script>
</head>
<body>
  {{! if the block is not defined in a parent template, then output default content }}
  {{#block 'page_content'}}<p class="custom">default block content</p>{{/block}}
</body>
</html>

The page file index.hbs:

{{! define the block page_content which will be output in layout }}
{{#partial 'page_content'}}
  <h1>Hello World!</h1>
{{/partial}}

{{! extends the layout template with blocks defined above }}
{{> layout}}

The webpack config is very easy:

new HtmlBundlerPlugin({
  // automatically processing all pages from the directory
  entry: Path.join(__dirname, 'src/views/pages/'),
  // - OR - define each page manually
  entry: {
    'de': 'src/views/pages/de.hbs', // => dist/de.html
    'en': 'src/views/pages/en.hbs', // => dist/en.html
    // etc.
  },
  preprocessor: 'handlebars',
  preprocessorOptions: {
    partials: [
     'src/views/partials/',
     ],
  },
  js: {
    filename: 'js/[name].[contenthash:8].js',
  },
  css: {
    filename: 'css/[name].[contenthash:8].css',
  },
}),

P.S. you can create a small repo with your use case and I can help you to configure it.

Paillat-dev commented 5 months ago

Wow @webdiscus thanks very much for the details, It very much helps. I'll try to see what I can do, and let you know &/ close this issue. Thanks much for the help, you definitely gained a star!

webdiscus commented 5 months ago

@Paillat-dev if you have any questions create a discussion or an issue.

Paillat-dev commented 5 months ago

If anyone happens to come here and read this, just use the solution above :)