UNPKG

rollup-plugin-handlebars-precompiler

Version:

Rollup plugin to precompile Handlebars templates into JavaScript modules

346 lines (265 loc) 15.1 kB
# rollup-plugin-handlebars-precompiler [Rollup][] plugin to precompile [Handlebars][] templates into [JavaScript modules][] _**Note**: I still need to add a little bit more documentation, but the plugin is fully functional and tested._ Source: <https://github.com/mbland/rollup-plugin-handlebars-precompiler> [![License](https://img.shields.io/github/license/mbland/rollup-plugin-handlebars-precompiler.svg)](https://github.com/mbland/rollup-plugin-handlebars-precompiler/blob/main/LICENSE.txt) [![CI status](https://github.com/mbland/rollup-plugin-handlebars-precompiler/actions/workflows/run-tests.yaml/badge.svg)](https://github.com/mbland/rollup-plugin-handlebars-precompiler/actions/workflows/run-tests.yaml?branch=main) [![Test results](https://github.com/mbland/rollup-plugin-handlebars-precompiler/actions/workflows/publish-test-results.yaml/badge.svg)](https://github.com/mbland/rollup-plugin-handlebars-precompiler/actions/workflows/publish-test-results.yaml?branch=main) [![Coverage Status](https://coveralls.io/repos/github/mbland/rollup-plugin-handlebars-precompiler/badge.svg?branch=main)][coveralls-rphp] [![npm version](https://badge.fury.io/js/rollup-plugin-handlebars-precompiler.svg)][npm-rphp] ## Installation Add both this package and Handlebars to your project's `devDependencies`, e.g., using [pnpm][]: ```sh pnpm add -D handlebars rollup-plugin-handlebars-precompiler ``` Even though `handlebars` is a production dependency of this plugin, your project needs to install it directly. The JavaScript modules generated by this plugin need to access the Handlebars runtime at `node_modules/handlebars/lib/handlebars.runtime.js` and have it bundled into your own project. ## Features - Generates JavaScript/ECMAScript/ES6 modules (ESM) only. - [Modules are supported by all current browsers][esm-caniuse]. - [Modules are supported by all currently supported versions of Node.js][esm-node]. - Supports TypeScript type checking, including [Visual Studio Code JavaScript type checking][] and [IntelliJ IDEA/WebStorm JavaScript type checking][]. - Client code imports template files directly, as though they were JavaScript modules to begin with, as modules generated by this plugin will: - Import the Handlebars runtime - Import Handlebars helper files specified in the plugin configuration - Automatically detect and register [partials][] and automatically import them where needed - Provides a convenient syntax for both accessing individual top-level child nodes and adding the entire template to the DOM at once. - Flexible configuration for specifying which files include Handlebars templates and helpers, with reasonable defaults for discovering templates and partials. - Emits [Handlebars source maps][] unless explicitly disabled. ## Usage Each generated Handlebars template module exports two functions: - `RawTemplate()` emits the raw HTML string generated by applying a Handlebars template. - The default export, conventionally imported as `Template()`, emits a [DocumentFragment][] created from the result of `RawTemplate()`. This provides you with two options of using a given template: ```js import Template, { RawTemplate } from './component.hbs' const appElem = document.querySelector('#app') const context = { message: 'Hello, World!', url: 'https://en.wikipedia.org/wiki/%22Hello,_World!%22_program' } const compilerOpts = {} // Use the DocumentFragment to append the entire template to a DOM Node at once. // You can also extract individual element references from .children before // doing so. const tmpl = Template(context) const [ firstChild, secondChild ] = tmpl.children appElem.appendChild(tmpl) // Use the RawTemplate() string to render the template manually. const newElem = document.createElement('div') newElem.innerHTML = RawTemplate(context) appElem.appendChild(newElem) ``` Both `Template()` and `RawTemplate()` take an optional [Handlebars runtime options][] argument: ```js const runtimeOpts = { data: { '@foo': 'bar' } } const tmpl = Template(context, runtimeOpts) ``` ### Automatic helper and partial module imports The plugin configuration, described below, specifies which project files contain [custom helper function modules][custom-helpers] and [partial templates][partials]. These custom helper modules, and modules generated for explicitly used partials, are automatically imported by any generated modules that need them. There's no need to import them explicitly in any other code. ### Dynamic partials supported, but not automatically imported Any code using a template that contains [dynamic partials][] will need to import the generated modules for those partials directly. However, those generated modules will automatically register any imported partials via `Handlebars.registerPartial()`. ### TypeScript or TypeScript-based JavaScript type checking To enable TypeScript type checking, copy [lib/template.d.ts](./lib/template.d.ts) from this package into your project: ```sh cp node_modules/rollup-plugin-handlebars-precompiler/lib/template.d.ts . ``` This is an [ambient module][] defining the types exported from each generated module. Edit the contents to replace `.hbs` with your project's Handlebars template file extension if necessary. If you want, you can change the file name and locate it anywhere in your project that you wish (that TypeScript will find it). This is necessary because the precompiled modules are generated in _your_ project, not in `rollup-plugin-handlebars-precompiler`, so that's where TypeScript needs to find the type declarations. ### Configuration The function exported by this plugin takes a configuration object as an argument. For example, in a [vite.config.js][] configuration file: ```js import HandlebarsPrecompiler from 'rollup-plugin-handlebars-precompiler' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ HandlebarsPrecompiler({ helpers: ['components/helpers.js'] }) ] }) ``` All of the configuration parameters are optional: - **helpers** _(string[])_: paths to modules containing [custom Handlebars helper functions][custom-helpers] - **include** _(string | string[])_: one or more [picomatch patterns][] matching Handlebars template files to transform - _Default_: `['**/*.hbs', '**/*.handlebars', '**/*.mustache']` - **exclude** _(string | string[])_: one or more [picomatch patterns][] matching Handlebars template files to exclude from transformation - _Default_: `'node_modules/**'` - **partials** _(string | string[])_: one or more [picomatch patterns][] matching Handlebars template files containing partials - _Default_: `'**/_*'` - **partialName** _((string) => string)_: function to transform a partial file name into the name used to apply the partial in other templates - _Default_: 1. Extracts the basename 2. Removes the file extension, if present 3. Strips leading non-alphanumeric characters - _Example_: `components/_my_partial.hbs` yields `my_partial` - **partialPath** _((string, string) => string)_: function to transform a partial's name and that of the module importing it into its import path - _Default_: 1. Expects a partial to reside in the same directory as another Handlebars template that uses it 2. Adds `./_` prefix to the partial name 3. Adds the file extension of the module importing it - _Example_: (`my_partial`, `components/other_template.hbs`) yields `./_my_partial.hbs` - **compiler** _(Handlebars.PrecompileOptions)_: [Handlebars compiler options][] passed through to `Handlebars.parse()` and `Handlebars.precompile()` - **sourcemap** or **sourceMap** _(boolean)_: disables source map generation when false As for why both **sourcemap** and **sourceMap** are supported, it's because: - [Rollup - Plugin Development - Source Code Transformations][] specifies that source maps can be disabled via `sourceMap: false`. - [Rollup - Troubleshooting - Warning: "Sourcemap is likely to be incorrect"][] specifies that source maps can be disabled via `sourcemap: false`. ### Defining helper modules Modules specified by the configuration as containing [custom helpers][custom-helpers] should export a default function that takes the [Handlebars runtime object][runtime] as an argument. It should then call [Handlebars.registerHelper()][] or any other runtime functions as needed. Here's an example from [test/large/components/helpers.js][], adapted from [Handlebars - Expressions - Helpers with Hash Arguments][]: ```js export default function(Handlebars) { const linkHelper = function(text, options) { const attrs = Object.keys(options.hash).map(key => { return `${Handlebars.escapeExpression(key)}=` + `"${Handlebars.escapeExpression(options.hash[key])}"` }) return new Handlebars.SafeString( `<a ${attrs.join(' ')}>${Handlebars.escapeExpression(text)}</a>` ) } Handlebars.registerHelper('link', linkHelper) } ``` ### Defining a partial template discovery schema Partials are first identified as Handlebars templates via the **include** configuration option. Then the **partials**, **partialName**, and **partialPath** options define a schema used to identify partial template files, register them with the Handlebars runtime, and generate imports. The default behavior: - considers any Handlebars file name starting with `_` to contain a partial - expects a partial to reside in the same directory as the template that uses it - expects the partial to use the same Handlebars file extension as the template that includes it If you choose to use a different schema for organizing partials, make sure to update any of these configuration options as necessary. ## Development Uses [pnpm][] and [Vitest][] for building and testing. The [Vitest browser mode][] (using the [@vitest/browser][] plugin) enables all the tests to run in either the [jsdom environment][] or the browser unchanged. Uses [GitHub Actions][] for continuous integration. Developed using [Vim][], [Visual Studio Code][], and [IntelliJ IDEA][] interchangeably, depending on my inclination from moment to moment. ## Background I developed this while developing the frontend component of [mbland/tomcat-servlet-testing-example][], found under `strcalc/src/main/frontend`. ## Prior art There were two existing Rollup plugins for compiling Handlebars templates from which I learned to write this one. ### [rollup-plugin-handlebars][] This was the first Rollup plugin to support Handlebars. It's very brief and easy to understand, but doesn't support: - automatic helper imports - automatic partial detection, registration, and imports - source maps. It exports a function that emits the raw string from the applied template, but not one for instantiating it. `rollup-plugin-handlebars-precompiler` borrows its include/exclude filters for template discovery verbatim from this plugin. ### [rollup-plugin-handlebars-plus][] This plugin was derived from the first, but does support: - automatic helper imports - automatic partial detection, registration, and imports It does not support Handlebars source maps. TODO: more to come... ## Copyright Original work [rollup-plugin-handlebars][] &copy; 2016 Benjamin Legrand under the MIT License Original work [rollup-plugin-handlebars-plus][] &copy; 2016 Mixmax, Inc under the MIT License Derived work [rollup-plugin-handlebars-precompiler][] &copy; 2023 Mike Bland <mbland@acm.org> (<https://mike-bland.com/>) under the Mozilla Public License 2.0 ## Open Source Licenses This software is made available as [Open Source software][] under the [MIT License][] ("Original work") and the [Mozilla Public License 2.0][] ("Derived work"). For the text of the Mozilla Public License 2.0, see the [LICENSE.txt](./LICENSE.txt) file. See the [MPL 2.0 FAQ][mpl-faq] for a higher level explanation. [Rollup]: https://rollupjs.org/ [Handlebars]: https://handlebarsjs.com/ [JavaScript Modules]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules [coveralls-rphp]: https://coveralls.io/github/mbland/rollup-plugin-handlebars-precompiler?branch=main [npm-rphp]: https://www.npmjs.com/package/rollup-plugin-handlebars-precompiler [Handlebars source maps]: https://handlebarsjs.com/api-reference/compilation.html#handlebars-precompile-template-options [DocumentFragment]: https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment [Handlebars runtime options]: https://handlebarsjs.com/api-reference/runtime-options.html [custom-helpers]: https://handlebarsjs.com/guide/#custom-helpers [ambient module]: https://www.typescriptlang.org/docs/handbook/modules/reference.html#ambient-modules [runtime]: https://handlebarsjs.com/api-reference/runtime.html [Handlebars.registerHelper()]: https://handlebarsjs.com/api-reference/runtime.html#handlebars-registerhelper-name-helper [test/large/components/helpers.js]: ./test/large/components/helpers.js [Handlebars - Expressions - Helpers with Hash Arguments]: https://handlebarsjs.com/guide/expressions.html#helpers-with-hash-arguments [pnpm]: https://pnpm.io/ [esm-caniuse]: https://caniuse.com/es6-module [esm-node]: https://nodejs.org/docs/latest-v18.x/api/esm.html [Visual Studio Code JavaScript type checking]: https://code.visualstudio.com/docs/nodejs/working-with-javascript [IntelliJ IDEA/WebStorm JavaScript type checking]: https://blog.jetbrains.com/webstorm/2019/09/using-typescript-to-check-your-javascript-code/ [partials]: https://handlebarsjs.com/guide/partials.html [dynamic partials]: https://handlebarsjs.com/guide/partials.html#dynamic-partials [vite.config.js]: https://vitejs.dev/config/ [picomatch patterns]: https://github.com/micromatch/picomatch#globbing-features [Handlebars compiler options]: https://handlebarsjs.com/api-reference/compilation.html [Rollup - Plugin Development - Source Code Transformations]: https://rollupjs.org/plugin-development/#source-code-transformations [Rollup - Troubleshooting - Warning: "Sourcemap is likely to be incorrect"]: https://rollupjs.org/troubleshooting/#warning-sourcemap-is-likely-to-be-incorrect [Vitest]: https://vitest.dev/ [GitHub Actions]: https://docs.github.com/actions [mbland/tomcat-servlet-testing-example]: https://github.com/mbland/tomcat-servlet-testing-example [Vim]: https://www.vim.org/ [Visual Studio Code]: https://code.visualstudio.com/ [IntelliJ IDEA]: https://www.jetbrains.com/idea/ [Vitest browser mode]: https://vitest.dev/guide/browser.html [@vitest/browser]: https://www.npmjs.com/package/@vitest/browser [jsdom environment]: https://vitest.dev/guide/environment.html [rollup-plugin-handlebars]: https://github.com/benjilegnard/rollup-plugin-handlebars [rollup-plugin-handlebars-plus]: https://github.com/mixmaxhq/rollup-plugin-handlebars-plus [rollup-plugin-handlebars-precompiler]: https://github.com/mbland/rollup-plugin-handlebars-precompiler [Open Source software]: https://opensource.org/osd-annotated [MIT License]: https://opensource.org/license/mit/ [Mozilla Public License 2.0]: https://www.mozilla.org/MPL/ [mpl-faq]: https://www.mozilla.org/MPL/2.0/FAQ/