Skip to content

Commit

Permalink
Merge pull request #15 from yoichiro/support-transforming-static-files
Browse files Browse the repository at this point in the history
Support transforming static files
  • Loading branch information
yoichiro authored Sep 30, 2024
2 parents 9e5738e + 1dda61e commit b951007
Show file tree
Hide file tree
Showing 9 changed files with 256 additions and 21 deletions.
116 changes: 113 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ This is a plugin that provides convenient features for using Handlebars with Vit

## Features

By using this plugin, you can import Handlebars template files as modules in projects that use Vite.
With this plugin, the following features are enabled in projects using Vite:

For example, suppose you have the following Handlebars template file (`templates/card.hbs`):
* Handlebars template files can be imported within `.ts` or `.js` files, which is particularly useful in SPA (Single Page Architecture).
* During the build process, index files is processed as a Handlebars template file. This feature is especially useful in MPA (Multi Page Architecture).

### Example of SPA

As an example of SPA, suppose you have the following Handlebars template file (`templates/card.hbs`):

```html
<div class="card">
Expand Down Expand Up @@ -43,6 +48,68 @@ Then, include it in the `card.hbs` file as follows:

This plugin automatically loads the `footer.hbs` file and includes it in the `card.hbs` file.

### Example of MPA

As an example of MPA, suppose you have the following index file (`index.html`):

```html
<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
<p>{{ upper-case message }}</p>
{{> footer}}
</body>
</html>
```

Also, suppose you have the following partial file (`partials/footer.hbs`):

```html
<div class="footer">
<p>Powered by Vite and Handlebars.</p>
</div>
```

Next, create the following `vite.config.js` file:

```javascript
import { defineConfig } from 'vite';
import handlebarsPlugin from '@yoichiro/vite-plugin-handlebars';
import path from 'path';

export default defineConfig({
plugins: [
handlebarsPlugin({
partialDirectoryPath: path.resolve(__dirname, 'partials'),
transformIndexHtmlOptions: {
context: () => {
message: 'Hello, world!'
},
helpers: {
'upper-case': (str: string) => str.toUpperCase(),
},
},
})
]
});
```

As a result, the `index.html` file generated by running `vite build` will be as follows:

```html
<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
<p>HELLO, WORLD!</p>
<div class="footer">
<p>Powered by Vite and Handlebars.</p>
</div>
</body>
</html>
```

# Installation

```shell
Expand Down Expand Up @@ -73,6 +140,7 @@ This plugin can be configured with the following options:
* `partialDirectoryPath` (string) - Specifies the path to the directory containing partial template files to be included in Handlebars template files. If omitted, partial template files are not registered.
* `optimizePartialRegistration` (boolean) - Set to true to optimize the partial registration. This option is effective only when `partialsDirectoryPath` is set. If omitted, the plugin does not optimize the partial registration. If true, the plugin does not register the partials that are already registered.
* `compileOptions` (object) - Specifies the options to be passed to the Handlebars compiler. If omitted, the default options are used.
* `transformIndexHtmlOptions` (object) - This is an option to specify when treating the index file as a Handlebars template file during the Vite build process. If this option is not specified, the index file will not be transformed.

These options can be specified as arguments to the `handlebarsPlugin` function. Below is an example that specifies `handlebars` as the template file extension and `templates/partials` as the directory containing partial template files.

Expand All @@ -95,6 +163,48 @@ export default defineConfig({

The `compileOptions` are the various options applied when compiling template files in Handlebars. For details on each option, please refer to the [Handlebars documentation](https://handlebarsjs.com/api-reference/compilation.html#handlebars-compile-template-options).

When building with the Vite build or preview command, if you want to treat the index file (typically the `index.html` file) as a Handlebars template file and perform a transformation, specify the `transformIndexHtmlOptions` option. This option is an object that can contain the following properties:

* `context` (object or function) - Specifies the context object to be passed to the Handlebars template file. If a function is specified, the function is called and the return value is used as the context object.
* `helpers` (object) - Specifies the helper functions to be passed to the Handlebars template file.

If you want to transform the index file as a Handlebars template file without registering any context or helpers, specify an empty object as shown below.

```javascript
// vite.config.js
import { defineConfig } from 'vite';
import handlebarsPlugin from '@yoichiro/vite-plugin-handlebars';
import path from 'path';

export default defineConfig({
plugins: [
handlebarsPlugin({
templateFileExtension: 'handlebars',
partialDirectoryPath: path.resolve(__dirname, 'templates', 'partials'),
transformIndexHtmlOptions: {},
})
]
});
```

Please note that the `context` option and `helpers` option specified in the `transformIndexHtmlOptions` are only applied during Vite's build process. They are not applied when importing Handlebars template files using import in `.ts` or `.js` files.

Additionally, when there are multiple index files in an MPA, use `rollupOptions` as shown below.

```javascript
export default defineConfig({
...
build: {
rollupOptions: {
input: {
index: resolve(__dirname, 'index.html'),
alternative: resolve(__dirname, 'alternative.html'),
},
},
},
});
```

# Samples

The `integration` directory contains a sample Vite project using this plugin. You can start this sample project by following these steps:
Expand All @@ -103,7 +213,7 @@ The `integration` directory contains a sample Vite project using this plugin. Yo
$ git clone https://github.com/yoichiro/vite-plugin-handlebars.git
$ cd vite-plugin-handlebars
$ npm install
$ npm run integration-test
$ npm run integration-preview
```

After the development server starts, please access `http://localhost:5173` in your web browser.
Expand Down
12 changes: 12 additions & 0 deletions integration/alternative.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<title>Integration Test `alternative` Page</title>
</head>
<body>
<div>{{> static }}</div>
<div>
<a href="index.html">Go to the `index` page.</a>
</div>
</body>
</html>
6 changes: 5 additions & 1 deletion integration/index.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<title>Integration Test Web Page</title>
<title>Integration Test `index` Page</title>
<script type="module" src="src/app.js"></script>
</head>
<body>
<div id="app">This will be replaced dynamically.</div>
<div>{{> static }}</div>
<div>
<a href="alternative.html">Go to the `alternative` page.</a>
</div>
</body>
</html>
1 change: 1 addition & 0 deletions integration/partials/static.handlebars
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p>This is {{ upper-case keyword }} partial.</p>
16 changes: 16 additions & 0 deletions integration/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ export default defineConfig({
templateFileExtension: '.handlebars',
partialsDirectoryPath: resolve(__dirname, 'partials'),
optimizePartialRegistration: true,
transformIndexHtmlOptions: {
context: async () => {
return Promise.resolve({ keyword: 'static' });
},
helpers: {
'upper-case': (str: string) => str.toUpperCase(),
},
},
}),
],
build: {
rollupOptions: {
input: {
index: resolve(__dirname, 'index.html'),
alternative: resolve(__dirname, 'alternative.html'),
},
},
},
});
9 changes: 5 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@yoichiro/vite-plugin-handlebars",
"version": "1.3.0",
"version": "1.4.0",
"author": {
"name": "Yoichiro Tanaka",
"email": "[email protected]",
Expand All @@ -25,10 +25,13 @@
"node": "^18.0.0 || >=20.0.0"
},
"scripts": {
"type-check": "tsc --noEmit",
"build": "tsc",
"prepare": "npm run build",
"test": "vitest",
"integration-test": "npm run build && vite --config integration/vite.config.ts"
"integration-dev": "npm run build && vite --config integration/vite.config.ts",
"integration-build": "npm run build && vite build --config integration/vite.config.ts",
"integration-preview": "npm run build && vite preview --config integration/vite.config.ts"
},
"types": "dist/index.d.ts",
"license": "MIT",
Expand Down
42 changes: 41 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Plugin } from 'vite';
import { IndexHtmlTransformContext, Plugin } from 'vite';
import {
decideTemplateFileExtension,
handleHotUpdate,
transform,
transformIndexHtml,
} from './internal.js';
import { HelperDeclareSpec } from 'handlebars';

/** This plugin supports a variety of options to customize the behavior of the Handlebars compiler. */
export type CompileOptions = {
Expand Down Expand Up @@ -50,6 +52,23 @@ export type CompileOptions = {
explicitPartialContext?: boolean;
};

/**
* The Handlebars context for the index file transformation.
* The context can be a plain object or a function that returns a plain object.
* If the context is a function, it can also be an async function.
*/
export type HandlebarsContext =
| Record<string, unknown>
| (() => Record<string, unknown> | Promise<Record<string, unknown>>);

/** The option for the index file transformation. */
export type TransformIndexHtmlOptions = {
/** The Handlebars context for the index file transformation. */
context?: HandlebarsContext;
/** The Handlebars helpers for the index file transformation. */
helpers?: HelperDeclareSpec;
};

/** The options for the Handlebars plugin. */
export type HandlebarsPluginOptions = {
/** The file extension of the Handlebars template files. Default is '.hbs'. */
Expand All @@ -69,6 +88,11 @@ export type HandlebarsPluginOptions = {
optimizePartialRegistration?: boolean;
/** The compile options for the Handlebars compiler. */
compileOptions?: CompileOptions;
/**
* The option for the index file transformation.
* If omitted, the plugin does not transform the index file.
*/
transformIndexHtmlOptions?: TransformIndexHtmlOptions | undefined;
};

/**
Expand Down Expand Up @@ -110,5 +134,21 @@ export default function handlebarsPlugin(
}
handleHotUpdate(file, server);
},
transformIndexHtml: {
order: 'pre',
handler(html: string, context: IndexHtmlTransformContext) {
if (options.transformIndexHtmlOptions === undefined) {
return html;
}
return transformIndexHtml(
html,
context,
templateFileExtension,
options.partialsDirectoryPath,
options.compileOptions,
options.transformIndexHtmlOptions
);
},
},
};
}
Loading

0 comments on commit b951007

Please sign in to comment.