Skip to content

Commit

Permalink
feat: add runtime option for the handlebars preprocessor
Browse files Browse the repository at this point in the history
  • Loading branch information
webdiscus committed Jul 23, 2024
1 parent 85ce3a5 commit 361ff6c
Show file tree
Hide file tree
Showing 16 changed files with 122 additions and 8 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change log

## 3.16.0 (2024-07-23)

- feat: add `runtime` option for the `handlebars` preprocessor
- test: add test for the `runtime` option
- docs: update readme

## 3.15.1 (2024-07-07)

- fix: resolving source file in a tag attribute when another attribute contains the `>` char, e.g.:
Expand Down
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
## HTML template as entry point

The **HTML Bundler** generates static HTML or [template function](#template-in-js) from [various templates](#template-engine) containing source files of scripts, styles, images, fonts and other resources, similar to how it works in [Vite](https://vitejs.dev/guide/#index-html-and-project-root).
This plugin allows using a template file as an [entry point](#option-entry).
This plugin looks at the template files in [entry](#option-entry) to start building the bundle.
The source files of dependencies (scripts, styles, etc.) can be defined directly in the template.

The plugin resolves source files of assets in templates and replaces them with correct output URLs in the generated HTML.
The resolved assets will be processed via Webpack plugins/loaders and placed into the output directory.
You can use a relative path or Webpack alias to a source file.

A template imported in JS will be compiled into [template function](#template-in-js). You can use the **template function** in JS to render the template with variables in runtime on the client-side in the browser.


This plugin is an **advanced replacement** of `html-webpack-plugin` and many other [plugins and loaders](#list-of-plugins).

<!--
Expand Down Expand Up @@ -169,6 +169,10 @@ Thank you to all our sponsors and patrons!
<img src="https://c10.patreonusercontent.com/4/patreon-media/p/user/43568167/0ef77126597d460c9505bdd0aea2eea9/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=7izh1FZTToAqf4Qks3Qrk8YcNbGymF-sBi0hkK_aJO8%3D" width="50" title="Raymond Ackloo" alt="patron" style="max-width: 100%;">
<p>Raymond<br>Ackloo</p>
</a></td>
<td style="border: 0"><a href="https://github.com/chkpnt">
<img src="https://github.com/avatars/u/1956979?s=50&amp;v=4" width="50" title="Gregor Dschung" alt="Gregor Dschung" style="max-width: 100%;">
<p>Gregor<br>Dschung</p>
</a></td>
</tr>
</table>

Expand Down Expand Up @@ -3502,6 +3506,12 @@ Include the partials in the `src/views/page/home.html` template with the `includ
The `include` helper automatically resolves `.html` and `.hbs` extensions, it can be omitted.
**The `runtime` option**
The path to the handlebars runtime library. The path can be absolute or relative to `node_modules` directory.
Defaults runtime file is `handlebars/runtime`.
This options is used only by import templates in JavaScript, in [compile mode](#compile-mode).
**The `partials` option**
Type: `Array<string>|Object` Default: `[]`
Expand Down Expand Up @@ -4476,6 +4486,8 @@ _./partials/star-button.html_
</button>
```

<a id="compile-mode" name="compile-mode"></a>

### Compile mode

The `compile` is the default mode for the template imported in JavaScript file.
Expand Down Expand Up @@ -4521,7 +4533,7 @@ _./partials/people.ejs_
`include` is supported
- [ejs](#loader-option-preprocessor-options-ejs) - generates a fast smallest pure template function w/o runtime (**recommended** for use on client-side)\
`include` is supported
- [handlebars](#loader-option-preprocessor-options-handlebars) - generates a precompiled template with runtime (~28KB)\
- [handlebars](#loader-option-preprocessor-options-handlebars) - generates a precompiled template with runtime (~18KB)\
`include` is NOT supported (yet)
- [nunjucks](#loader-option-preprocessor-options-nunjucks) - generates a precompiled template with runtime (~41KB)\
`include` is supported
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "html-bundler-webpack-plugin",
"version": "3.15.1",
"version": "3.16.0",
"description": "HTML bundler plugin for webpack handles a template as an entry point, extracts CSS and JS from their sources referenced in HTML, supports template engines like Eta, EJS, Handlebars, Nunjucks.",
"keywords": [
"html",
Expand Down
8 changes: 5 additions & 3 deletions src/Loader/Preprocessors/Handlebars/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const path = require('path');
const Dependency = require('../../Dependency');
const { escapeSequences, stringifyJSON } = require('../../Utils');
const { stringifyJSON } = require('../../Utils');
const { loadModule, readDirRecursiveSync } = require('../../../Common/FileUtils');
const { isWin, pathToPosix } = require('../../../Common/Helpers');

Expand All @@ -11,6 +11,10 @@ const preprocessor = (loaderContext, options) => {
const extensions = ['.html', '.hbs', '.handlebars'];
const includeFiles = [/\.(html|hbs|handlebars)$/i];
const root = options?.root || rootContext;
//const runtime = options?.runtime || 'handlebars/dist/handlebars.runtime.min';
const runtime = options?.runtime || 'handlebars/runtime';
// fix windows-like path
const runtimeFile = require.resolve(runtime).replace(/\\/g, '/');
let views = options?.views || rootContext;
let helpers = {};
let partials = {};
Expand Down Expand Up @@ -212,8 +216,6 @@ const preprocessor = (loaderContext, options) => {
* @return {string} The exported template function.
*/
export(precompiledTemplate, { data }) {
// fix windows-like path
const runtimeFile = require.resolve('handlebars/dist/handlebars.runtime.min').replace(/\\/g, '/');
const exportFunctionName = 'templateFn';
const exportCode = 'module.exports=';

Expand Down
2 changes: 1 addition & 1 deletion test/cases/js-tmpl-hbs-compile/expected/app.js

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>My Title</title>
<!-- load the rendered template in JS and add it into HTML in runtime -->
<script src="app.js" defer="defer"></script>
</head>
<body>
<div id="main"></div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import tmpl from './partials/content.hbs?lang=en';

const locals = {
name: 'World',
people: ['Alexa <Amazon>', 'Cortana <MS>', 'Siri <Apple>'],
};

document.getElementById('main').innerHTML = tmpl(locals);

console.log('>> app');
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<!-- load the rendered template in JS and add it into HTML in runtime -->
<script src="app.js" defer="defer"></script>
</head>
<body>
<div id="main"></div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<h1>Hello {{ name }}!</h1>
<div>Global data: title = "{{ title }}"</div>
<div>Query param: lang = "{{ lang }}"</div>

<ul>
{{#each people}}
<li>{{this}}</li>
{{/each}}
</ul>

<img src="@images/fig.png" srcset="@images/kiwi.png 300w, @images/pear.png 400w">
{{!-- {{TODO: add supports for include 'star'}} --}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<div class="star">++ Included partial ++</div>
<img src="@images/stern.svg">
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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({
entry: {
index: './src/index.hbs',
},
preprocessor: 'handlebars',
preprocessorOptions: {
//runtime: 'handlebars/runtime', // default runtime
runtime: 'handlebars/dist/handlebars.runtime.min', // test custom runtime file
views: ['src/partials'],
partials: ['src/partials'],
},
data: {
title: 'My Title',
},
}),
],

module: {
rules: [
{
test: /\.(ico|png|jp?g|svg)$/,
type: 'asset/resource',
generator: {
filename: 'img/[name].[hash:8][ext][query]',
},
},
],
},
};
1 change: 1 addition & 0 deletions test/integration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ describe('loader preprocessor options', () => {
test('handlebars partials', () => compareFiles('loader-option-preprocessor-handlebars-partials'));
test('handlebars partials path', () => compareFiles('loader-option-preprocessor-handlebars-partials-path'));
test('handlebars access @root', () => compareFiles('hbs-access-root-variable'));
test('handlebars runtime', () => compareFiles('loader-option-preprocessor-handlebars-runtime'));

test('liquid', () => compareFiles('loader-option-preprocessor-liquid'));
test('liquid async', () => compareFiles('loader-option-preprocessor-liquid-async'));
Expand Down

0 comments on commit 361ff6c

Please sign in to comment.