Node.js + Webpack + Handlebars の環境でヘルパーが使えずハマった話
問題
AWS Lambda(Node.js)上で動かす関数内で、テンプレートエンジンとして Handlebars を使いたい場面がありました。
実装は TypeScript、ビルドには Webpack を使用していたため、handlebars-loaderを導入したのですが、
登録したはずのヘルパーが Missing helper というエラーで呼び出せない問題に遭遇しました。
問題の状況
hbs.registerHelper("suffix", suffix);
上記のようにヘルパーを登録しても、実行時に次のようなエラーが発生します。
Error: Missing helper: "suffix"
at Object.eval (webpack://hbs-webpack-test/./node_modules/handlebars/dist/cjs/handlebars/helpers/helper-missing.js?:19:13)
at Object.wrapper (webpack://hbs-webpack-test/./node_modules/handlebars/dist/cjs/handlebars/internal/wrapHelper.js?:15:19)
at Object.main (webpack://hbs-webpack-test/./view/template.hbs?:12:145)
at main (webpack://hbs-webpack-test/./node_modules/handlebars/dist/cjs/handlebars/runtime.js?:208:32)
at ret (webpack://hbs-webpack-test/./node_modules/handlebars/dist/cjs/handlebars/runtime.js?:212:12)
at getTemplate (webpack://hbs-webpack-test/./hbs.js?:12:13)
at Server.eval (webpack://hbs-webpack-test/./app.js?:10:15)
at Server.emit (node:events:520:28)
at parserOnIncoming (node:\_http_server:951:12)
at HTTPParser.parserOnHeadersComplete (node:\_http_common:128:17) {
description: undefined,
fileName: undefined,
lineNumber: undefined,
endLineNumber: undefined,
number: undefined
}
一方で、hbs.helpers には確かに登録されているため、登録自体は成功しているように見えます。

しかし Webpack でバンドルされた時点でテンプレートがコンパイル済みとなるため、 実行時に追加したヘルパーは参照されないという仕様のようです。
この点は以下の Issue でも言及されています:
実行時に登録してもダメだから loader のオプションにヘルパーを登録しておく仕掛けを入れておくと。
しかし 1 次情報からそのあたりの説明が見つけられなかった・・・🎃
解決策
registerHelper を使わず、handlebars-loader のオプションでヘルパーを登録する方法に切り替えることで解決しました。
module.exports = {
// 省略
module: {
rules: [{
test: /\.hbs$/,
loader: 'handlebars-loader',
options: {
runtime: path.resolve(**dirname, 'src', 'handlebars'),
helperDirs: path.resolve(\_\_dirname, 'src', 'helpers'),
precompileOptions: {
knownHelpersOnly: false,
},
},
},],
},
};
- helperDirs に指定したディレクトリ配下に、ヘルパー名と同じファイル名でファイルを作成する。
- 関数は default export する。
- 名前付き export やファイル名の不一致は動作しない。
また、別の方法としては、事前にヘルパーを登録済みの Handlebars インスタンスを runtime に指定することでも対応可能です。
検証用リポジトリ
最小構成で別途検証しました。
同様の事象を最小構成で再現できるリポジトリを用意しました。
環境
- webpack 5.74.0
- Nodejs 16.14.0
- handlebars 4.7.7
まとめ
- Webpack + Handlebars の組み合わせでは、registerHelper による登録は動作しない。
- handlebars-loader のオプション helperDirs で登録ディレクトリを指定するのが正解。
また、今回は使っていませんが、パーシャルも同じようなことになると思います。
webpack というものをもっと理解できれば今回の問題も上手く咀嚼できてくるのだろうなと思いました。
追記
サイト移転に伴う見直しにつき、一部の文言や構成を変更しております。