Skip to content

Commit 21ead14

Browse files
committed
feat(experimental): add support for markdown in nunjucks preprocessor
1 parent 7427480 commit 21ead14

File tree

17 files changed

+210
-11
lines changed

17 files changed

+210
-11
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 4.22.0-alpha.0 (2025-07-31)
4+
5+
- feat(experimental): add support for markdown in nunjucks preprocessor
6+
37
## 4.21.1 (2025-07-21)
48

59
- docs: update base url of documentation pages in readme

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "html-bundler-webpack-plugin",
3-
"version": "4.21.1",
3+
"version": "4.22.0-alpha.0",
44
"description": "Generates complete single-page or multi-page website from source assets. Built-in support for Markdown, Eta, EJS, Handlebars, Nunjucks, Pug. Alternative to html-webpack-plugin.",
55
"keywords": [
66
"html",

src/Common/FileUtils.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ const readDirRecursiveSync = (dir, { fs, includes = [], excludes = [] }) => {
9292
* @param {string} root The root path for the file with an absolute path (e.g., /file.html)
9393
* @param {Array<string>} paths Resolve a file in these paths.
9494
* @param {Array<string>} extensions Resolve a file without an extension with these extensions.
95-
* @return {string|boolean} Returns resolved file otherwise returns false.
95+
* @return {string|false} Returns resolved file otherwise returns false.
9696
*/
9797
const resolveFile = (file, { fs, root = process.cwd(), paths = [], extensions = [] }) => {
9898
const resolveFileExt = (file, extensions = []) => {
@@ -131,8 +131,12 @@ const resolveFile = (file, { fs, root = process.cwd(), paths = [], extensions =
131131

132132
// test path/file.ext
133133
for (let filePath of paths) {
134-
if (!filePath.endsWith(path.sep)) filePath += path.sep;
135-
resolvedFile = resolveFileExt(filePath + file, extensions);
134+
if (!path.isAbsolute(filePath)) {
135+
filePath = path.join(root, filePath);
136+
}
137+
138+
resolvedFile = resolveFileExt(path.join(filePath, file), extensions);
139+
136140
if (resolvedFile) return resolvedFile;
137141
}
138142

src/Loader/Preprocessors/Nunjucks/index.js

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,60 @@
1+
const fs = require('fs');
12
const path = require('path');
23
const { makeTemplateId, stringifyJSON } = require('../../Utils');
3-
const { loadModule } = require('../../../Common/FileUtils');
4+
const { loadModule, resolveFile } = require('../../../Common/FileUtils');
5+
const MarkdownFilter = require('../../PreprocessorFilters/markdown');
6+
7+
const markdownExtension = function (nunjucks, { viewPaths }) {
8+
const name = 'includeMarkdown';
9+
10+
const markdownFilterOptions = {
11+
highlight: {
12+
use: {
13+
module: 'prismjs',
14+
options: {
15+
verbose: true, // display loaded dependencies
16+
},
17+
},
18+
},
19+
};
20+
21+
function IncludeMarkdown() {
22+
this.tags = [name];
23+
24+
this.parse = function (parser, nodes) {
25+
const tok = parser.nextToken();
26+
const args = parser.parseSignature(null, true);
27+
parser.advanceAfterBlockEnd(tok.value);
28+
return new nodes.CallExtension(this, 'run', args);
29+
};
30+
31+
this.run = function (context, filePath) {
32+
const file = resolveFile(filePath, { fs, paths: viewPaths, extensions: ['.md'] });
33+
34+
if (!file) {
35+
throw new Error(`Could not find the include file '${filePath}'`);
36+
}
37+
38+
const raw = fs.readFileSync(file, 'utf8');
39+
const html = MarkdownFilter.getInstance(markdownFilterOptions).apply(raw);
40+
41+
return new nunjucks.runtime.SafeString(html);
42+
};
43+
}
44+
45+
return {
46+
name,
47+
extension: new IncludeMarkdown(),
48+
};
49+
};
450

551
// node module name
652
const moduleName = 'nunjucks';
753

854
const preprocessor = (loaderContext, options = {}, { esModule, watch }) => {
955
const nunjucks = loadModule(moduleName);
10-
const env = new nunjucks.Environment();
56+
const { FileSystemLoader, Environment, runtime } = nunjucks;
57+
1158
const { rootContext } = loaderContext;
1259
const viewPaths = (options.views = [...new Set([...(options.views || []), rootContext])]);
1360
const async = options?.async === true;
@@ -27,8 +74,18 @@ const preprocessor = (loaderContext, options = {}, { esModule, watch }) => {
2774
nunjucks.installJinjaCompat();
2875
}
2976

30-
// set root template dirs, see the options https://mozilla.github.io/nunjucks/api.html#configure
31-
nunjucks.configure(viewPaths, options);
77+
const env = new Environment(
78+
// set root template dirs, see the options https://mozilla.github.io/nunjucks/api.html#configure
79+
new FileSystemLoader(viewPaths, options),
80+
options
81+
);
82+
83+
// register custom extensions
84+
const extensions = [markdownExtension(nunjucks, { viewPaths })];
85+
extensions.forEach(({ name, extension }) => {
86+
env.addExtension(name, extension);
87+
// console.log('[nunjucks] added extension:', name);
88+
});
3289

3390
return {
3491
/**
@@ -48,12 +105,12 @@ const preprocessor = (loaderContext, options = {}, { esModule, watch }) => {
48105
render: async
49106
? (content, { data }) =>
50107
new Promise((resolve, reject) => {
51-
nunjucks.renderString(content, data, (error, result) => {
108+
env.renderString(content, data, (error, result) => {
52109
if (!error) resolve(result);
53110
else reject(error);
54111
});
55112
})
56-
: (source, { data = {} }) => nunjucks.renderString(source, data),
113+
: (content, { data = {} }) => env.renderString(content, data),
57114

58115
/**
59116
* Compile template into template function.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
h1{color:#006400}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title></title>
5+
<!-- block for specific page styles -->
6+
7+
<link href="css/home.6a9c92dd.css" rel="stylesheet">
8+
9+
<!-- block for specific page scripts -->
10+
11+
<script src="js/home.b75acc46.js" defer="defer"></script>
12+
13+
</head>
14+
<body>
15+
<div>header</div>
16+
17+
<nav>Menu > Navigation</nav>
18+
19+
<main class="main-content">
20+
<!-- block for specific page content -->
21+
22+
<h1>Hello World</h1>
23+
<p>This is a <strong>Markdown</strong> file rendered inside Nunjucks.</p>
24+
25+
26+
</main>
27+
<div>footer</div>
28+
</body>
29+
</html>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log(">> home.js");
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div>footer</div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div>header</div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<nav>Menu > Navigation</nav>

0 commit comments

Comments
 (0)