Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
/**
* Generate HTML files from Nunjucks
*/
const mix = require('laravel-mix'),
path = require('path'),
fs = require('fs'),
paths = require('./paths'),
HTMLWebpackPlugin = require('html-webpack-plugin');
var templateParameters = require('./data.json');
let env = 'development';
if (mix.inProduction()) {
env = 'production'
}
const directories = []
function walkDir(dir, parent = '') {
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, parent+'/'+files[x]);
} else {
directories.push([path.parse(files[x]).name, (parent+'/').slice(1)]);
}
}
return directories
}
const pages = {
generatePages: function generatePages(pagesPath) {
return walkDir(pagesPath).map(
name =>
new HTMLWebpackPlugin({
template: `${pagesPath}/${name[1]}${name[0]}.njk`,
filename: paths.pages.base + `/${name[1]}${name[0]}.html`,
templateParameters: templateParameters
})
);
}
};
module.exports = pages;
{
"base": {
"dir": "./pages",
"node": "node_modules",
"start": "index.html"
},
"src": {
"base": "./src",
"scss": "./src/scss",
"templates": "./src/templates",
"js": "./src/js",
"fonts": "./src/fonts",
"images": "./src/images"
},
"pages": {
"base": "./pages",
"assets": "./pages/assets",
"layouts": "./pages/layouts",
"block": "./pages/blocks",
"js": "./pages/assets/js",
"css": "./pages/assets/css",
"fonts": "./pages/assets/fonts",
"images": "./pages/assets/images",
"vendor": "./pages/assets/vendor"
},
"dist": {
"base": "./dist",
"css": "./dist/assets/css",
"js": "./dist/assets/js",
"pages": "./dist/pages",
"layouts": "./dist/pages/layouts",
"blocks": "./dist/pages/blocks",
"vendor": "./dist/assets/vendor",
"fonts": "./dist/assets/fonts",
"images": "./dist/assets/images"
}
}
const paths = require('./paths'),
mix = require('laravel-mix'),
RemoveWebpackPlugin = require('remove-webpack-plugin'),
nunjucksConfig = require('./nunjucks.config'),
path = require('path'),
WebpackOnBuildPlugin = require('on-build-webpack');
mix.webpackConfig({
module: {
rules: [
{
test: /\.njk$/,
use: [
{
loader: 'simple-nunjucks-loader'
}
]
}
]
},
plugins: [
...nunjucksConfig.generatePages(path.resolve(__dirname, paths.src.templates)),
]
})
@ogonkov

This comment has been minimized.

Copy link

@ogonkov ogonkov commented Feb 29, 2020

Add module.rules with simple-nunjucks-loader config

Replace nunjucks-webpack-plugin with html-webpack-plugin, replace NunjucksWebpackPlugin options like:

  • templates.from becomes template
  • templates.to becomes filename
@levipadre

This comment has been minimized.

Copy link
Owner Author

@levipadre levipadre commented Feb 29, 2020

Thank you @ogonkov, I updated the code (copied here too), but now I got a bunch of error now:

Html Webpack Plugin:
Error: Child compilation failed:
Module build failed (from ./node_modules/simple-nunjucks-loader/lib/loader.js):
Error: Template "./layouts/_layout.njk" not found

- Error: Template "./layouts/_layout.njk" not found

- compiler.js:79 childCompiler.runAsChild
  [html]/[html-webpack-plugin]/lib/compiler.js:79:16

- Compiler.js:343 compile
  [html]/[webpack]/lib/Compiler.js:343:11

- Compiler.js:681 hooks.afterCompile.callAsync.err
  [html]/[webpack]/lib/Compiler.js:681:15

- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compiler.js:678 compilation.seal.err
  [html]/[webpack]/lib/Compiler.js:678:31

- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compilation.js:1423 hooks.optimizeAssets.callAsync.err
  [html]/[webpack]/lib/Compilation.js:1423:35

- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compilation.js:1414 hooks.optimizeChunkAssets.callAsync.err
  [html]/[webpack]/lib/Compilation.js:1414:32

- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compilation.js:1409 hooks.additionalAssets.callAsync.err
  [html]/[webpack]/lib/Compilation.js:1409:36

- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compilation.js:1405 hooks.optimizeTree.callAsync.err
  [html]/[webpack]/lib/Compilation.js:1405:32

- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compilation.js:1342 Compilation.seal
  [html]/[webpack]/lib/Compilation.js:1342:27

- Compiler.js:675 compilation.finish.err
  [html]/[webpack]/lib/Compiler.js:675:18

- Compilation.js:1261 hooks.finishModules.callAsync.err
  [html]/[webpack]/lib/Compilation.js:1261:4

- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compilation.js:1253 Compilation.finish
  [html]/[webpack]/lib/Compilation.js:1253:28

- Compiler.js:672 hooks.make.callAsync.err
  [html]/[webpack]/lib/Compiler.js:672:17

- Compilation.js:1185 _addModuleChain
  [html]/[webpack]/lib/Compilation.js:1185:12

- Compilation.js:1097 processModuleDependencies.err
  [html]/[webpack]/lib/Compilation.js:1097:9

- next_tick.js:61 process._tickCallback
  internal/process/next_tick.js:61:11
@ogonkov

This comment has been minimized.

Copy link

@ogonkov ogonkov commented Feb 29, 2020

Looks like you use relative paths to import templates. I'm afraid simple-nunjucks-loader supports only templates paths that is set from one of the folder in searchPaths option

https://www.npmjs.com/package/simple-nunjucks-loader#searchpaths

@levipadre

This comment has been minimized.

Copy link
Owner Author

@levipadre levipadre commented Feb 29, 2020

So nothing I can do? I added my paths.json file just in case.

@ogonkov

This comment has been minimized.

Copy link

@ogonkov ogonkov commented Feb 29, 2020

Where is layouts folder in your file structure?

@levipadre

This comment has been minimized.

Copy link
Owner Author

@levipadre levipadre commented Feb 29, 2020

In src/templates/layouts/
And every page (about, index, news) should go into pages/ as an html, like this:
Screenshot 2020-02-29 at 8 25 52

@ogonkov

This comment has been minimized.

Copy link

@ogonkov ogonkov commented Feb 29, 2020

You should adjust loader config to let nunjucks know where to search for templates, i think it should be src/templates

{
  loader: 'simple-nunjucks-loader',
  options: {
    searchPaths: [
      path.join(__dirname, 'src', 'templates')
    ]
  }
}
@levipadre

This comment has been minimized.

Copy link
Owner Author

@levipadre levipadre commented Feb 29, 2020

Something is definitely different now. It seems it went into the templates folder now, but doesn't want to recognise includes, like:
{% include "./_footer.njk" %}. I tried with {% include "_footer.njk" %} too, but I got this error:

Error: Child compilation failed:
Module build failed (from ./node_modules/simple-nunjucks-loader/lib/loader.js):
Error: Template "./_footer.njk" not found
@ogonkov

This comment has been minimized.

Copy link

@ogonkov ogonkov commented Feb 29, 2020

It should be relative to one of the path in searchPaths, not the file where it used. So in case of _footer.njk it should be layouts/_footer.njk

@levipadre

This comment has been minimized.

Copy link
Owner Author

@levipadre levipadre commented Feb 29, 2020

I just rewrote to {% include "layouts/_footer.njk" %}, now I got this:

Error: Template render error: (index.njk)
    Template render error: (./layouts/_layout.njk)
    TypeError: compiler.compile is not a function
  
  - index.njk:351 Object._prettifyError
    /Users/leventekosa/Projects/square/html/src/templates/index.njk:351:11
  
  - index.njk:2003 
    /Users/leventekosa/Projects/square/html/src/templates/index.njk:2003:19
  
  - index.njk:163 Template.root [as rootRenderFunc]
    /Users/leventekosa/Projects/square/html/src/templates/index.njk:163:3
  
  - index.njk:1992 Template.render
    /Users/leventekosa/Projects/square/html/src/templates/index.njk:1992:10
  
  - index.njk:1802 
    /Users/leventekosa/Projects/square/html/src/templates/index.njk:1802:27
  
  - index.njk:1751 createTemplate
    /Users/leventekosa/Projects/square/html/src/templates/index.njk:1751:9
  
  - index.njk:1763 handle
    /Users/leventekosa/Projects/square/html/src/templates/index.njk:1763:11
  
  - index.njk:1775 
    /Users/leventekosa/Projects/square/html/src/templates/index.njk:1775:9
  
  - index.njk:595 next
    /Users/leventekosa/Projects/square/html/src/templates/index.njk:595:7
  
  - index.njk:601 Object.asyncIter
    /Users/leventekosa/Projects/square/html/src/templates/index.njk:601:3

Sorry for all the trouble!

@ogonkov

This comment has been minimized.

Copy link

@ogonkov ogonkov commented Feb 29, 2020

What is this file looks like? Error above happen in nunjucks

@levipadre

This comment has been minimized.

Copy link
Owner Author

@levipadre levipadre commented Feb 29, 2020

Looks like we got a winner!
In the index.njk just had to use also {% extends "layouts/_layout.njk" %}
THANK YOU!

Can I send you something or any way I can say thanks?

@ogonkov

This comment has been minimized.

Copy link

@ogonkov ogonkov commented Feb 29, 2020

You can share your experience in your stackoverflow question, and accept my answer :)

@levipadre

This comment has been minimized.

Copy link
Owner Author

@levipadre levipadre commented Feb 29, 2020

Sure, that is default! Cheers again.

@ogonkov

This comment has been minimized.

Copy link

@ogonkov ogonkov commented Feb 29, 2020

I'm glad it works for you

@levipadre

This comment has been minimized.

Copy link
Owner Author

@levipadre levipadre commented Feb 29, 2020

Me too. Accepted you answer and starred your git page as well.

@levipadre

This comment has been minimized.

Copy link
Owner Author

@levipadre levipadre commented Mar 3, 2020

Hi @ogonkov, it's me again.
Just one last thing. Is there any way to pass environment?
With NunjucksWebpackPlugin I could do this:

                new NunjucksWebpackPlugin({
                    templates: [{
                        from: `${pagesPath}/${name[1]}${name[0]}.njk`,
                        to: paths.pages.base + `/${name[1]}${name[0]}.html`,
                        context: { 
                            environment: env
                        }
                    }]
                })

So at the end I can do this in njk:
{% if environment == 'production' %}
...
{% endif %}

Thanks!

@ogonkov

This comment has been minimized.

Copy link

@ogonkov ogonkov commented Mar 3, 2020

Looks like you want globals

@levipadre

This comment has been minimized.

Copy link
Owner Author

@levipadre levipadre commented Mar 3, 2020

Thanks. I don't know if I did right, but it doesn't work for me.

loader: 'simple-nunjucks-loader',
options: {
    searchPaths: path.join(__dirname, 'src', 'templates'),
    lstripBlocks: true,
    autoescape: true,
    trimBlocks: true,
    globals: {
        globalEnv: 'development'
    }
}

And I wanted to render in njk, but it's empty:
<p>Env: {{globalEnv}}</p>

The other thing, that simple-nunjucks-loader add the css and js file to the html itself, but I'm doing that manually. Can I disable that function somehow?

@ogonkov

This comment has been minimized.

Copy link

@ogonkov ogonkov commented Mar 3, 2020

You should export your string as module to have it in globals

// global-env.js

module.exports = 'development';

// config
loader: 'simple-nunjucks-loader',
options: {
    globals: {
        globalEnv: path.join(__dirname, 'global-env.js')
    }
}

Globals is useful when you need to have some globally available functions. Loader also track changing in global modules to trigger html rebuild, that the reason why it looks so complicated.

Another option is just pass globalEnv within your data.json.

As for inserting html and css to html. It's doing by html-webpack-plugin. I guess setting chunks to false should disable this behaviour https://github.com/jantimon/html-webpack-plugin#options

@levipadre

This comment has been minimized.

Copy link
Owner Author

@levipadre levipadre commented Mar 4, 2020

Thanks again! It was inject: false. I managed the environment as well.

@levipadre

This comment has been minimized.

Copy link
Owner Author

@levipadre levipadre commented Mar 8, 2020

Do you think is that also a html-webpack-plugin issue?
When I create a new .njk file under templates, nothing happens, I need to restart the server to make generate the .html file.
If I have the file already and I rename it, it crashes instead of renaming the html file and I got this error:

Html Webpack Plugin:

Error: Child compilation failed:
Module build failed (from ./node_modules/simple-nunjucks-loader/lib/loader.js):
Error: ENOENT: no such file or directory, open '/Users/leventekosa/Projects/square/html/src/templates/news.njk':
Error: ENOENT: no such file or directory, open '/Users/leventekosa/Projects/square/html/src/templates/news.njk'

- compiler.js:79 childCompiler.runAsChild
  [html]/[html-webpack-plugin]/lib/compiler.js:79:16

- Compiler.js:343 compile
  [html]/[webpack]/lib/Compiler.js:343:11

- Compiler.js:681 hooks.afterCompile.callAsync.err
  [html]/[webpack]/lib/Compiler.js:681:15

- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compiler.js:678 compilation.seal.err
  [html]/[webpack]/lib/Compiler.js:678:31

- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compilation.js:1423 hooks.optimizeAssets.callAsync.err
  [html]/[webpack]/lib/Compilation.js:1423:35

- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compilation.js:1414 hooks.optimizeChunkAssets.callAsync.err
  [html]/[webpack]/lib/Compilation.js:1414:32

- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compilation.js:1409 hooks.additionalAssets.callAsync.err
  [html]/[webpack]/lib/Compilation.js:1409:36


- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compilation.js:1405 hooks.optimizeTree.callAsync.err
  [html]/[webpack]/lib/Compilation.js:1405:32

- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compilation.js:1342 Compilation.seal
  [html]/[webpack]/lib/Compilation.js:1342:27

- Compiler.js:675 compilation.finish.err
  [html]/[webpack]/lib/Compiler.js:675:18

- Compilation.js:1261 hooks.finishModules.callAsync.err
  [html]/[webpack]/lib/Compilation.js:1261:4

- Hook.js:154 AsyncSeriesHook.lazyCompileHook
  [html]/[tapable]/lib/Hook.js:154:20

- Compilation.js:1253 Compilation.finish
  [html]/[webpack]/lib/Compilation.js:1253:28

- Compiler.js:672 hooks.make.callAsync.err
  [html]/[webpack]/lib/Compiler.js:672:17

- Compilation.js:1185 _addModuleChain
  [html]/[webpack]/lib/Compilation.js:1185:12

- Compilation.js:1097 processModuleDependencies.err
  [html]/[webpack]/lib/Compilation.js:1097:9

- next_tick.js:61 process._tickCallback
  internal/process/next_tick.js:61:11

And finally when I delete a file, also crashes.

@ogonkov

This comment has been minimized.

Copy link

@ogonkov ogonkov commented Mar 8, 2020

I think it's relate to how webpack config works. Webpack trying to use obsolete config.

I'm afraid webpack doesn't reload config, when entry point files changes. There definitely should be some solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment