Search code examples
javascriptwebpacknunjuckshtml-webpack-plugin

Using nunjucks with htmlWebpackPlugin using dynamic vars in the template


I'm searching a way of using Nunjucks with the htmlWebpackPlugin to generate some html files on webpack compiling.

What I achieved so far

I managed to actually generate HTML from nunjucks template files through the nunjucks-html-loader but looking a bit closer to the code of said loader, the render method is called without sending vars to the templates.

So, for now with the following plugin config, I generate HTML without dynamically inserted vars

new HtmlWebpackPlugin({
  filename: path.join(__dirname, '/' + page.filename),
  template: 'nunjucks-html-loader!assets/templates/' + page.name + '.njk'
})

What I tried

For a testing purpose, I tried some changes on the node_module itself (I know, I know...) and changed

html = template.render(nunjucksContext);

into

html = template.render(nunjucksContext, { globals: global.globals });

Trying to define global.globals in my webpack.config.js file but this crashes with the following error

ERROR in Error: Child compilation failed: Module build failed: TypeError: parentFrame.push is not a function

which is beyond my comprehension.

What I want

Is to use an extendable template engine like nunjucks which allows me to structure my templates like the following

<html>
<!-- layout structure inherited from every template -->
</html>

Every page I make extends the layout and only overrides some blocks

What I try to avoid

Partials like for exemple

header file :

<html>
<!-- header layout -->

footer file

<!-- footer layout -->
</html>

Every page I make includes partials


So my question is : Is it even possible tu use a template engine supporting inheritance like nunjucks with the htmlWebpackPlugin or is it mandatory to use another one like ejs for exemple and chunking the layout into partials which I do not like?


Solution

  • Alright, so I found a workaround here with the nunjucks-isomorphic-loader which seems not super supported but still. It works for now !

    Here's my webPack config

    const path = require('path')
    const webpack = require('webpack')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    const DEV_ENV = process.env.NODE_ENV === 'dev'
    
    const wpConfig = {
      entry: './assets/js/app.js',
    
      output: {
        path: path.resolve('./dist/js'),
        filename: 'bundle.js'
      },
    
      module: {
        rules: [
          // Javascript
          {
            test: /\.js$/,
            exclude: /(node_modules)/,
            use: {
              loader: 'babel-loader'
            }
          },
    
          // Nunjucks - HTML
          {
            test: /\.njk$/,
            use: [
              {
                loader: 'nunjucks-isomorphic-loader',
                query: {
                  root: [path.resolve(__dirname, 'assets/templates')]
                }
              }
            ]
          }
        ]
      },
    
      plugins: [
        new webpack.DefinePlugin({
          DEV_ENV: DEV_ENV
        }),
    
        new HtmlWebpackPlugin({
          myOptions: { foo: 'bar' },
          filename: path.join(__dirname, '/' + page.filename),
          template: 'assets/templates/index.njk'
        })
      ]
    }
    
    module.exports = wpConfig
    

    having the following templates

    _layout.njk

    {% set vars = htmlWebpackPlugin.options.myOptions %}
    
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>{{ vars.foo }}</title>
      </head>
      <body>
        <header>
        {% block header %}
          <h1 class="header-logo">
            <a href="#">{{ vars.foo }}</a><!-- Outputs bar -->
          </h1>
        {% endblock %}
        </header>
    
        {% block content %}
    
        {% endblock %}
      </body>
    </html>
    

    index.njk

    {% extends "_layout.njk" %}
    
    {% block content %}
    here's the content of my `foo` var: {{ vars.foo }}
    {% endblock %}