Skip to content

Commit f38c9ea

Browse files
author
Eli Skeggs
committed
feat: support pure helper definitions
This lets consumers tree-shake out helper definitions if no templates are used.
1 parent 961432d commit f38c9ea

File tree

2 files changed

+49
-14
lines changed

2 files changed

+49
-14
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ rollup({
6262
// Can be a string too.
6363
helpers: ['/utils/HandlebarsHelpers.js'], // Default: none
6464

65+
// Whether to register the defined helpers at template declaration in a way that would allow
66+
// the initialization call to be elided if the template is never used. Useful in a library
67+
// context where the templates might all get tree-shaken away, leaving no need for the
68+
// helpers. Does nothing if helpers is empty.
69+
helpersPureInitialize: true, // Default: false
70+
6571
// In case you want to compile files with other extensions.
6672
templateExtension: '.html', // Default: '.hbs'
6773

src/index.js

+43-14
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ const DEFAULT_HANDLEBARS_ID = path.relative(
1010
require.resolve('handlebars/runtime')
1111
);
1212

13+
const INTERNAL_INIT_ID = '\0handlebarsPlusHelpersInit';
14+
15+
const escapePath = (path) => path.replace(/\\/g, '\\\\');
16+
17+
const nonEmptyOr = (array, fallback) => (array.length ? array : fallback);
18+
const asArrayOr = (value, fallback) => nonEmptyOr([].concat(value || []), fallback);
19+
1320
/**
1421
* Constructs a Rollup plugin to compile Handlebars templates.
1522
*
@@ -74,7 +81,36 @@ function handlebars(options) {
7481
const Handlebars = options.handlebars.module || require('handlebars');
7582
const ImportScanner = require('./ImportScanner')(Handlebars);
7683

84+
const hbsImport = `import Handlebars from '${escapePath(options.handlebars.id)}';\n`;
85+
86+
const wrapTemplateDefinition = options.helpersPureInitialize
87+
? (defineTemplate, initExpr) =>
88+
defineTemplate((expr) => `(function() {${initExpr};return ${expr};})()`)
89+
: (defineTemplate, initExpr) => `${initExpr};\n${defineTemplate()}`;
90+
91+
// Support `helpers` being singular or plural.
92+
const helpers = asArrayOr(options.helpers, null);
93+
7794
return {
95+
resolveId: (id) => (helpers && id === INTERNAL_INIT_ID ? id : undefined),
96+
97+
load(id) {
98+
if (!helpers || id !== INTERNAL_INIT_ID) return;
99+
100+
let body = hbsImport;
101+
body += '';
102+
103+
const initExpr = helpers.map((helperPath, i) => {
104+
const ref = `Helpers${i}`;
105+
body += `import ${ref} from '${escapePath(helperPath)}';\n`;
106+
return ` ${ref}.__initialized || (${ref}(Handlebars), ${ref}.__initialized = true);\n`;
107+
});
108+
109+
body += `export default function() {\n${initExpr.join('')}}\n`;
110+
111+
return { code: body, map: { mappings: '' } };
112+
},
113+
78114
transform(code, id) {
79115
if (!id.endsWith(options.templateExtension)) return;
80116

@@ -95,28 +131,21 @@ function handlebars(options) {
95131
template = template.code;
96132
}
97133

98-
const escapePath = (path) => path.replace(/\\/g, '\\\\');
99-
100-
let body = `import Handlebars from '${escapePath(options.handlebars.id)}';\n`;
134+
let body = hbsImport;
101135
if (options.jquery) body += `import $ from '${escapePath(options.jquery)}';\n`;
102136

103-
if (options.helpers) {
104-
// Support `helpers` being singular or plural.
105-
[].concat(options.helpers).forEach((helpers, i) => {
106-
body += `import Helpers${i} from '${escapePath(helpers)}';\n`;
107-
body += `if (!Helpers${i}.__initialized) {\n`;
108-
body += ` Helpers${i}(Handlebars);\n`;
109-
body += ` Helpers${i}.__initialized = true;\n`;
110-
body += `}\n`;
111-
});
112-
}
137+
body += `import init from '${INTERNAL_INIT_ID}';\n`;
113138

114139
for (const partial of scanner.partials) {
115140
// Register the partial dependencies as partials.
116141
body += `import '${escapePath(partial)}${options.templateExtension}';\n`;
117142
}
118143

119-
body += `var Template = /*#__PURE__*/Handlebars.template(${template});\n`;
144+
body += wrapTemplateDefinition(
145+
(wrapExpression = (expr) => expr) =>
146+
`var Template = /*#__PURE__*/${wrapExpression(`Handlebars.template(${template})`)};\n`,
147+
'init()'
148+
);
120149

121150
if (options.isPartial(name)) {
122151
let partialName = id;

0 commit comments

Comments
 (0)