itgalaxy / nunjucks-webpack-plugin

A webpack plugin for nunjucks.
MIT License
27 stars 12 forks source link

Multiple templates dynamically? #10

Open lexcode opened 6 years ago

lexcode commented 6 years ago

For multiple templates, how to achieve it without specifying manually the files in webpack.config.js?

import NunjucksWebpackPlugin from "nunjucks-webpack-plugin";

export default {
  plugins: [
    new NunjucksWebpackPlugin({
      templates: [
        {
          from: "/path/to/[name].njk",
          to: "[name].html"
        }
      ]
    })
  ]
};

Thanks

trickydisco78 commented 5 years ago

I'd love to know if you can do this or a glob pattern

romanmarkelov commented 5 years ago

For my task I decided so

const mix = require('laravel-mix');
const fs = require('fs');
const argv = require('yargs').option('env.html', {
  default: false
}).argv;

class NunjucksMixPlugin {
  register(src) {
    this.src = src;
  }

  dependencies() {
    this.requiresReload = true;
    return ['nunjucks', 'nunjucks-webpack-plugin'];
  }

  webpackConfig(config) {
    if (Config.production || !argv.env.html) {
      return;
    }
    const NunjucksWebpackPlugin = require('nunjucks-webpack-plugin');

    config.plugins.push(
      new NunjucksWebpackPlugin({
        templates: this.getAllTemplates()
      })
    );
  }

  getAllTemplates() {
    const templates = [];
    fs.readdirSync(this.src).forEach(file => {
      if (file.indexOf('.njk') !== -1) {
        templates.push({
          from: `${this.src}/${file}`,
          to: `html/${this.replaceFileExtension(file)}`
        });
      }
    });
    return templates;
  }

  replaceFileExtension(file) {
    const pos = file.lastIndexOf('.');
    return file.substr(0, pos < 0 ? file.length : pos) + '.html';
  }
}

mix.extend('nunjucksPlugin', new NunjucksMixPlugin());
lexcode commented 5 years ago

I end up doing this last year.

pages.js

/**
 * Generate HTML Plugins
 */

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

const pages = {
  generatePages: function generatePages(pagesPath) {
    const names = [];

    fs.readdirSync(pagesPath).map(file => {
      const pageName = path.parse(file).name;
      return names.push(pageName);
    });

    // console.log(names);

    return names.map(
      name =>
        new HtmlWebpackPlugin({
          // inject: 'body',
          filename: `${name}.html`,
          template: path.resolve(__dirname, `${pagesPath}/${name}.njk`),
        }),
    );
  },
};

module.exports = pages;

Then in webpack.config.js

  plugins: [
    ...pages.generatePages(path.resolve(__dirname, commonPaths.pagesPath)),
  ],
bnku commented 5 years ago

I rewrote the example from the last post for recursive traversal of directorie with templates.

pages.js

/**
 * Generate HTML Plugins
 */

const path = require('path');
const fs = require('fs');
const NunjucksWebpackPlugin = require('nunjucks-webpack-plugin');

const directories = []
function walkDir(dir, parrent = '') {
    var files = fs.readdirSync(dir)
    for(var x in files){
        var next = path.join(dir,files[x]);
        if(fs.lstatSync(next).isDirectory()==true){
            walkDir(next, parrent+'/'+files[x]);
        }else{
            directories.push([path.parse(files[x]).name, (parrent+'/').slice(1)])
        }
    }
    return directories
}

const pages = {
  generatePages: function generatePages(pagesPath) {

    return walkDir(pagesPath).map(
        name =>
            new NunjucksWebpackPlugin({
                templates: [{
                    from: `${pagesPath}/${name[1]}${name[0]}.njk`,
                    to: `${name[1]}${name[0]}.html`
                }]
            }),
        );
  }
};

module.exports = pages;

My webpack template - https://github.com/bnku/webpack-nunjucks-saas-template

levipadre commented 5 years ago

@lexcode , @bnku Both solution is really good, I was looking for the same solution for a while. I tried, everything is fine, I have only one problem. When I modify other files (e.g. src/templates/layout/layout.njk) it nothing happens, only when I modify page files (src/templates/pages/index.njk or src/templates/pages/news.njk). Also when I add a new page, it doesn't generate into html, I need to rerun again.

Here is my structure:

And I'm calling like this:

plugins: [
    ...nunjuckspages.generatePages(path.resolve(__dirname, 'src/templates/pages')),
]

What do I need to do to trigger it whenever a file changes?

bnku commented 5 years ago

@levipadre use it for your structure:

plugins: [
    ...nunjuckspages.generatePages(path.resolve(__dirname, 'src/templates')),
]

I used about the following structure for my project:

levipadre commented 5 years ago

Thank you @bnku! With this structure and path it is working indeed. Only one problem remains: the layouts folder and everything within it also have generated. How can I stop that?

bnku commented 5 years ago

@levipadre delete them in a separate script after the webpack is completed.

levipadre commented 5 years ago

@bnku I just tried with https://www.npmjs.com/package/remove-webpack-plugin but the removed folder came back somehow. Like nunjucks runs after RemoveWebpackPlugin.

const RemoveWebpackPlugin = require('remove-webpack-plugin');

plugins: [
    ...nunjucksConfig.generatePages(path.resolve(__dirname, './src/templates')),
    new RemoveWebpackPlugin('./pages/layouts/')
]
levipadre commented 5 years ago

Probably not the best way, but I could manage with:

const paths = RemoveWebpackPlugin = require('remove-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');

plugins: [
    ...nunjucksConfig.generatePages(path.resolve(__dirname, paths.src.templates)),

    new WebpackOnBuildPlugin(function(stats) {
        new RemoveWebpackPlugin('./pages/layouts/')
    })
]

Thank you all your help @bnku !

trickydisco78 commented 5 years ago

Anyone got a nice boilerplate that uses webpack 4, nunjucks and postcss?