diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2e756284..b4d4f2b6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,24 @@
# Change log
+## 4.1.0 (2024-09-29)
+
+- feat: add supports the `require` of CommonJS and JSON files in EJS templates:
+ ```html
+ <% const data = require('./data.js') %>
+
Film: <%= data.title %>
+ Genre: <%= data.genre %>
+ ```
+ or
+ ```html
+ <% const data = require('./data.json') %>
+ Film: <%= data.title %>
+ Genre: <%= data.genre %>
+ ```
+- chore: update peerDependencies
+- test: refactor test cases for preprocessor
+
-## 4.0.0 Release (24-09-08)
+## 4.0.0 Release (2024-09-08)
### BREAKING CHANGES
diff --git a/package-lock.json b/package-lock.json
index 44ef956d..9e60c82f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "html-bundler-webpack-plugin",
- "version": "4.0.0",
+ "version": "4.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "html-bundler-webpack-plugin",
- "version": "4.0.0",
+ "version": "4.1.0",
"license": "ISC",
"dependencies": {
"@types/html-minifier-terser": "^7.0.2",
@@ -38,7 +38,7 @@
"handlebars": "^4.7.8",
"handlebars-layouts": "^3.1.4",
"jest": "^29.7.0",
- "liquidjs": "^10.16.7",
+ "liquidjs": "^10.17.0",
"markdown-it": "^14.1.0",
"mustache": "^4.2.0",
"normalize.css": "^8.0.1",
@@ -74,16 +74,16 @@
"url": "https://patreon.com/biodiscus"
},
"peerDependencies": {
- "ejs": ">=3.1.9",
- "favicons": ">=7.1.4",
- "handlebars": ">=4.7.7",
- "liquidjs": ">=10.7.0",
+ "ejs": ">=3.1.10",
+ "favicons": ">=7.2.0",
+ "handlebars": ">=4.7.8",
+ "liquidjs": ">=10.17.0",
"markdown-it": ">=12",
"mustache": ">=4.2.0",
"nunjucks": ">=3.2.3",
"parse5": ">=7.1.2",
"prismjs": ">=1.29.0",
- "pug": ">=3.0.2",
+ "pug": ">=3.0.3",
"twig": ">=1.17.1",
"webpack": ">=5.81.0"
},
@@ -9294,9 +9294,9 @@
}
},
"node_modules/liquidjs": {
- "version": "10.16.7",
- "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.16.7.tgz",
- "integrity": "sha512-vjlBDyPxFgUc6vJB+TbAMcxKKKcm4Ee0rj9Je9lcG1I0lr9xvtHgB/ZdNMNAgsPUvJLkLfdrKRd+KzQ5opPfNg==",
+ "version": "10.17.0",
+ "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.17.0.tgz",
+ "integrity": "sha512-M4MC5/nencttIJHirl5jFTkl7Yu+grIDLn3Qgl7BPAD3BsbTCQknDxlG5VXWRwslWIjk8lSZZjVq9LioILDk1Q==",
"dev": true,
"dependencies": {
"commander": "^10.0.0"
diff --git a/package.json b/package.json
index 4252a4a0..8496296e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "html-bundler-webpack-plugin",
- "version": "4.0.0",
+ "version": "4.1.0",
"description": "HTML Bundler Plugin for Webpack renders HTML templates containing source files of scripts, styles, images. Supports template engines: Eta, EJS, Handlebars, Nunjucks, Pug, TwigJS. Alternative to html-webpack-plugin.",
"keywords": [
"html",
@@ -92,16 +92,16 @@
"node": ">=16.20.0"
},
"peerDependencies": {
- "ejs": ">=3.1.9",
- "favicons": ">=7.1.4",
- "handlebars": ">=4.7.7",
- "liquidjs": ">=10.7.0",
+ "ejs": ">=3.1.10",
+ "favicons": ">=7.2.0",
+ "handlebars": ">=4.7.8",
+ "liquidjs": ">=10.17.0",
"markdown-it": ">=12",
"mustache": ">=4.2.0",
"nunjucks": ">=3.2.3",
"parse5": ">=7.1.2",
"prismjs": ">=1.29.0",
- "pug": ">=3.0.2",
+ "pug": ">=3.0.3",
"twig": ">=1.17.1",
"webpack": ">=5.81.0"
},
@@ -170,7 +170,7 @@
"handlebars": "^4.7.8",
"handlebars-layouts": "^3.1.4",
"jest": "^29.7.0",
- "liquidjs": "^10.16.7",
+ "liquidjs": "^10.17.0",
"markdown-it": "^14.1.0",
"mustache": "^4.2.0",
"normalize.css": "^8.0.1",
diff --git a/src/Loader/Preprocessors/Ejs/index.js b/src/Loader/Preprocessors/Ejs/index.js
index 16bc6d64..c57a0782 100644
--- a/src/Loader/Preprocessors/Ejs/index.js
+++ b/src/Loader/Preprocessors/Ejs/index.js
@@ -1,3 +1,5 @@
+const { readFileSync } = require('fs');
+const path = require('path');
const { loadModule } = require('../../../Common/FileUtils');
const { stringifyJSON } = require('../../Utils');
@@ -9,6 +11,19 @@ const includeRegexp = /include\((.+?)(?:\)|,\s*{(.+?)}\))/g;
// node module name
const moduleName = 'ejs';
+/**
+ * Require CommonJS or JSON file in EJS template.
+ *
+ * @param {string} file
+ * @param {string} dir
+ * @return {*}
+ */
+const requireFile = (file, dir) => {
+ const fullFilePath = path.join(dir, file);
+
+ return file.endsWith('.json') ? JSON.parse(readFileSync(fullFilePath, 'utf-8')) : require(fullFilePath);
+};
+
/**
* Transform the raw template source to a template function or HTML.
*
@@ -36,6 +51,10 @@ const preprocessor = (loaderContext, options) => {
* @return {string}
*/
render(source, { resourcePath, data = {} }) {
+ const contextPath = path.dirname(resourcePath);
+
+ data.require = (file) => requireFile(file, contextPath);
+
return Ejs.render(source, data, {
async: false,
root: rootContext, // root path for includes with an absolute path (e.g., /file.html)
diff --git a/test/cases/_preprocessor/ejs-require-js/expected/assets/img/apple.02a7c382.png b/test/cases/_preprocessor/ejs-require-js/expected/assets/img/apple.02a7c382.png
new file mode 100644
index 00000000..c3b5ce07
Binary files /dev/null and b/test/cases/_preprocessor/ejs-require-js/expected/assets/img/apple.02a7c382.png differ
diff --git a/test/cases/_preprocessor/ejs-require-js/expected/index.html b/test/cases/_preprocessor/ejs-require-js/expected/index.html
new file mode 100644
index 00000000..3c4b38e3
--- /dev/null
+++ b/test/cases/_preprocessor/ejs-require-js/expected/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+ Home
+
+
+ Breaking Bad
+
+
+ - Walter White
+
+ - Jesse Pinkman
+
+
+
+ footer
+
+
+
diff --git a/test/cases/_preprocessor/ejs-require-js/src/data.js b/test/cases/_preprocessor/ejs-require-js/src/data.js
new file mode 100644
index 00000000..56ef5116
--- /dev/null
+++ b/test/cases/_preprocessor/ejs-require-js/src/data.js
@@ -0,0 +1,5 @@
+module.exports = {
+ title: 'Home',
+ headline: 'Breaking Bad',
+ people: ['Walter White', 'Jesse Pinkman'],
+};
diff --git a/test/cases/_preprocessor/ejs-require-js/src/home.ejs b/test/cases/_preprocessor/ejs-require-js/src/home.ejs
new file mode 100644
index 00000000..910fb251
--- /dev/null
+++ b/test/cases/_preprocessor/ejs-require-js/src/home.ejs
@@ -0,0 +1,17 @@
+<% const data = require('./data.js') %>
+
+
+
+ <%= data.title %>
+
+
+ <%= data.headline %>
+
+ <% for (let i = 0; i < data.people.length; i++) {%>
+ - <%= data.people[i] %>
+ <% } %>
+
+
+ <%- include('partials/footer.html'); %>
+
+
diff --git a/test/cases/_preprocessor/ejs-require-js/src/partials/footer.html b/test/cases/_preprocessor/ejs-require-js/src/partials/footer.html
new file mode 100644
index 00000000..27dee2fa
--- /dev/null
+++ b/test/cases/_preprocessor/ejs-require-js/src/partials/footer.html
@@ -0,0 +1 @@
+footer
diff --git a/test/cases/_preprocessor/ejs-require-js/webpack.config.js b/test/cases/_preprocessor/ejs-require-js/webpack.config.js
new file mode 100644
index 00000000..48275b93
--- /dev/null
+++ b/test/cases/_preprocessor/ejs-require-js/webpack.config.js
@@ -0,0 +1,40 @@
+const path = require('path');
+const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');
+
+module.exports = {
+ mode: 'production',
+
+ output: {
+ path: path.join(__dirname, 'dist/'),
+ },
+
+ resolve: {
+ alias: {
+ '@images': path.join(__dirname, '../../../fixtures/images'),
+ },
+ },
+
+ plugins: [
+ new HtmlBundlerPlugin({
+ test: /\.(html|ejs)$/,
+ entry: {
+ index: {
+ import: './src/home.ejs',
+ },
+ },
+ preprocessor: 'ejs',
+ }),
+ ],
+
+ module: {
+ rules: [
+ {
+ test: /\.(png|svg|jpe?g|webp)$/i,
+ type: 'asset/resource',
+ generator: {
+ filename: 'assets/img/[name].[hash:8][ext]',
+ },
+ },
+ ],
+ },
+};
diff --git a/test/cases/_preprocessor/ejs-require-json/expected/assets/img/apple.02a7c382.png b/test/cases/_preprocessor/ejs-require-json/expected/assets/img/apple.02a7c382.png
new file mode 100644
index 00000000..c3b5ce07
Binary files /dev/null and b/test/cases/_preprocessor/ejs-require-json/expected/assets/img/apple.02a7c382.png differ
diff --git a/test/cases/_preprocessor/ejs-require-json/expected/index.html b/test/cases/_preprocessor/ejs-require-json/expected/index.html
new file mode 100644
index 00000000..3c4b38e3
--- /dev/null
+++ b/test/cases/_preprocessor/ejs-require-json/expected/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+ Home
+
+
+ Breaking Bad
+
+
+ - Walter White
+
+ - Jesse Pinkman
+
+
+
+ footer
+
+
+
diff --git a/test/cases/_preprocessor/ejs-require-json/src/data.json b/test/cases/_preprocessor/ejs-require-json/src/data.json
new file mode 100644
index 00000000..1d5b08af
--- /dev/null
+++ b/test/cases/_preprocessor/ejs-require-json/src/data.json
@@ -0,0 +1,5 @@
+{
+ "title": "Home",
+ "headline": "Breaking Bad",
+ "people": ["Walter White", "Jesse Pinkman"]
+}
diff --git a/test/cases/_preprocessor/ejs-require-json/src/home.ejs b/test/cases/_preprocessor/ejs-require-json/src/home.ejs
new file mode 100644
index 00000000..dfcabea9
--- /dev/null
+++ b/test/cases/_preprocessor/ejs-require-json/src/home.ejs
@@ -0,0 +1,17 @@
+<% const data = require('./data.json') %>
+
+
+
+ <%= data.title %>
+
+
+ <%= data.headline %>
+
+ <% for (let i = 0; i < data.people.length; i++) {%>
+ - <%= data.people[i] %>
+ <% } %>
+
+
+ <%- include('partials/footer.html'); %>
+
+
diff --git a/test/cases/_preprocessor/ejs-require-json/src/partials/footer.html b/test/cases/_preprocessor/ejs-require-json/src/partials/footer.html
new file mode 100644
index 00000000..27dee2fa
--- /dev/null
+++ b/test/cases/_preprocessor/ejs-require-json/src/partials/footer.html
@@ -0,0 +1 @@
+footer
diff --git a/test/cases/_preprocessor/ejs-require-json/webpack.config.js b/test/cases/_preprocessor/ejs-require-json/webpack.config.js
new file mode 100644
index 00000000..48275b93
--- /dev/null
+++ b/test/cases/_preprocessor/ejs-require-json/webpack.config.js
@@ -0,0 +1,40 @@
+const path = require('path');
+const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');
+
+module.exports = {
+ mode: 'production',
+
+ output: {
+ path: path.join(__dirname, 'dist/'),
+ },
+
+ resolve: {
+ alias: {
+ '@images': path.join(__dirname, '../../../fixtures/images'),
+ },
+ },
+
+ plugins: [
+ new HtmlBundlerPlugin({
+ test: /\.(html|ejs)$/,
+ entry: {
+ index: {
+ import: './src/home.ejs',
+ },
+ },
+ preprocessor: 'ejs',
+ }),
+ ],
+
+ module: {
+ rules: [
+ {
+ test: /\.(png|svg|jpe?g|webp)$/i,
+ type: 'asset/resource',
+ generator: {
+ filename: 'assets/img/[name].[hash:8][ext]',
+ },
+ },
+ ],
+ },
+};
diff --git a/test/integration-pug.test.js b/test/integration-pug.test.js
index 56904550..db414930 100644
--- a/test/integration-pug.test.js
+++ b/test/integration-pug.test.js
@@ -220,7 +220,10 @@ describe('pug-plugin tests', () => {
// special cases
test('resolve manifest.json via require', () => compareFiles('_pug/resolve-manifest.json-require'));
+
+ // TODO: fix github action issue
test('compile template function in js', () => compareFiles('_pug/js-tmpl-entry-js'));
+
test('inline js and css via query `?inline`', () => compareFiles('_pug/inline-js-css-query'));
test('inline CSS in style tag with attributes', () => compareFiles('_pug/inline-css-in-style-tag'));
diff --git a/test/preprocessor.test.js b/test/preprocessor.test.js
index 1d9c258a..1eca18a7 100644
--- a/test/preprocessor.test.js
+++ b/test/preprocessor.test.js
@@ -16,6 +16,8 @@ describe('EJS', () => {
test('option async', () => compareFiles('_preprocessor/ejs-option-async'));
test('option views', () => compareFiles('_preprocessor/ejs-option-views'));
test('custom render', () => compareFiles('_preprocessor/ejs-custom-render'));
+ test('require js', () => compareFiles('_preprocessor/ejs-require-js'));
+ test('require json', () => compareFiles('_preprocessor/ejs-require-json'));
// special cases
test('Template with CRLF line separator', () => compareFiles('_preprocessor/ejs-template-clrf'));