GithubHelp home page GithubHelp logo

vite-plugin-handlebars's Introduction

vite-plugin-handlebars

Vite support for Handlebars

Why?

I really like Vite as a simple static site bundler. It can handle bundling multiple HTML files, which is great, but lacks the ability out-of-the-box to share parts of those HTML files.

While a JS framework like React or Vue could be used to solve this problem, this is heavy-handed for a simple site that could be completely pre-rendered without a JS run-time of any kind.

Handlebars provides what we need to be able to stitch together multiple HTML files, interpolate variables, etc.

Installation

Start by installing the package like you would any other

yarn add -D vite-plugin-handlebars

It can then be added to your Vite configuration as a plugin:

// vite.config.js
import handlebars from 'vite-plugin-handlebars';

export default {
  plugins: [handlebars()],
};

Configuring the plugin is covered later in this guide.

Requirements

  • This plugin is intended to work with Vite 2
  • This plugin requires Node 14 or higher (due to usage of fs/promises)

Configuration

Defining Context

If you want to make use of Handlebars Context to inject variables into your HTML file, you'll need to define their values in the context object passed to the handlebars plugin:

<!-- index.html -->
<h1>{{title}}</h1>
// vite.config.js
import handlebars from 'vite-plugin-handlebars';

export default {
  plugins: [
    handlebars({
      context: {
        title: 'Hello, world!',
      },
    }),
  ],
};

This will result in <h1>Hello, world!</h1> in your output HTML file.

You can also provide a (asynchronous) function, either as the context key or any of the keys within the object, which will be evaluated to create the value that will be made available inside your page. This function is called with an identifier parameter based on the HTML file path which makes it possible to provide unique data to each HTML page in a multipage application setup.

// vite.config.js
import handlebars from 'vite-plugin-handlebars';

const pageData = {
  '/index.html': {
    title: 'Main Page',
  },
  '/nested/subpage.html': {
    title: 'Sub Page',
  },
};

export default {
  plugins: [
    handlebars({
      context(pagePath) {
        return pageData[pagePath];
      },
    }),
  ],
};

Partials

If you want to make use of partials in your HTML files, you must define the partialDirectory option for the handlebars plugin.

// vite.config.js
import { resolve } from 'path';
import handlebars from 'vite-plugin-handlebars';

export default {
  plugins: [
    handlebars({
      partialDirectory: resolve(__dirname, 'partials'),
    }),
  ],
};

If you want to use multiple partial folders, an array can be submitted.

Each file in these directories (.html or .hbs) will become registered as a partial. The name of the file is used to invoke it. So, with the above configuration and the following files:

<!-- partials/header.hbs -->
<header><a href="/">My Website</a></header>
<!-- index.html -->
{{> header }}

<h1>The Main Page</h1>

Your output website content would become:

<header><a href="/">My Website</a></header>

<h1>The Main Page</h1>

Make sure to review the quirks section for information on potentially-unexpected behavior.

Helpers

Custom helpers can be registered using the helpers configuration option:

// vite.config.js
import { resolve } from 'path';
import handlebars from 'vite-plugin-handlebars';

export default {
  plugins: [
    handlebars({
      helpers: {
        capitalize: (value) => value.toUpperCase(),
      },
    }),
  ],
};

For more information on helpers, see the Handlebars documentation.

Other Handlebars Options

All other Handlebars configuration options can also be passed through.

Each of these can also be passed through to the handlebars plugin:

// vite.config.js
import handlebars from 'vite-plugin-handlebars';

export default {
  plugins: [
    handlebars({
      compileOptions: {
        // Example config option: avoid auto-indenting partials
        preventIndent: true,
      },
      runtimeOptions: {
        // Example config option: define custom private @variables
        data: {
          foo: 'bar',
        },
      },
    }),
  ],
};

Disabling Browser Refresh on Partial Change

By default, any time a partial changes, your browser window will be full reloaded. If you want to disable this behavior, you can set reloadOnPartialChange to false:

// vite.config.js
import handlebars from 'vite-plugin-handlebars';

export default {
  plugins: [
    handlebars({
      reloadOnPartialChange: false,
    }),
  ],
};

Built-In Helpers

resolve-from-root

You can resolve a file path relative to the Vite root using the resolve-from-root helper. This assists with injecting other files, like linking to a CSS file, within a partial.

<!-- partials/head.hbs -->
<link rel='stylesheet' href='{{resolve-from-root "css/global.css"}}' />

Quirks

  • Assets included in a partial using a relative path will probably not work how you would first expect; the relative path is left alone, making it relative to the output file, not the partial itself. It's recommended that you use the resolve-from-root helper to ensure paths are resolved from the project root, rather than relative to a particular file.

vite-plugin-handlebars's People

Contributors

alexlafroscia avatar bohreromir avatar dependabot[bot] avatar naranjamecanica avatar nickgraffis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

vite-plugin-handlebars's Issues

[Security] Workflow ci.yml is using vulnerable action actions/checkout

The workflow ci.yml is referencing action actions/checkout using references v1. However this reference is missing the commit a6747255bd19d7a757dbdda8c654a9f84db19839 which may contain fix to the some vulnerability.
The vulnerability fix that is missing by actions version could be related to:
(1) CVE fix
(2) upgrade of vulnerable dependency
(3) fix to secret leak and others.
Please consider to update the reference to the action.

Nested Partials are Not Found

Unfortunately it doesn't work with 1.2.0 and 1.3.0. Maybe I missed smth, this is my config from the readme:

import { resolve } from 'path';
import handlebars from 'vite-plugin-handlebars';

export default {
  plugins: [
    handlebars({
      partialDirectory: resolve(__dirname, 'partials'),
    }),
  ],
};

`{{>head/head}}``this is in my startpage.html.

When I do a yarn build I got this error:

[vite:build-html] The partial head could not be found
file: C:/Projekte/Others/smth/pages/startpage/startpage.html
error during build:
Error: The partial head/head could not be found

Originally posted by @Chris2011 in #30 (comment)

Watch partial files for changes

I'm not sure exactly how this would work, but it would be really nice to (somehow) inform Vite of the fact that the Handlebars partials are part of the "dependency graph" of the page being viewed in the browser.

Currently, it seems like the changes are processed immediately, but the browser doesn't actually refresh in real-time.

I'm not actually sure why the dev server picks up the changes to a partial without re-starting. Maybe Vite is monitoring the entire local directory? Or is just re-running the bundling step on every browser refresh? Not sure right now.


  • Automatically trigger browser refresh when a partial changes

pagePath not properly resolved?

I have a setup with this configuration:

import { resolve } from "path";
import handlebars from "vite-plugin-handlebars";

const pageData = {
  "/src/pages/index.html": {
    title: "Homepage"
  },
  "/src/pages/subpage/index.html": {
    title: "Subpage"
  }
};

const handlebarsOptions = {
  context(pagePath) {
    console.log("pagePath =", pagePath);
    return pageData[pagePath];
  },
  partialDirectory: resolve(__dirname, "src/pages/partials")
};

export default {
  plugins: [handlebars(handlebarsOptions)]
};

A codesandbox is here:
https://codesandbox.io/s/vite-with-handlebars-61te7

As the terminal shows, the console.log does not pick up the current configuration.

What am I doing wrong?

feature request: ability to import as precompiled templates

It would be great if there were an option to return a precompiled template instead of raw html.

This would allow handlebar files to be imported and the resulting precompiled templates reused with different contexts.

import template from './template.hbs`

const htmlJoe = template({ name: 'Joe' })
const htmlJane = template({ name: 'Jane' })

Partials only work on index.html

I basically have the same issue raised in this stack overflow, but the one response does not solve my issue -> https://stackoverflow.com/questions/74877312/vite-plugin-handlebars-not-working-with-multipage-set-up

I can only get partials to work in the index.html file at my project's root, which seems to make this plugin not very useful as only one single file in my project can receive partials. How can we use this plugin in order to use partials in both the index.html file at the root of the project and files in the public directory?

Here is my vite.config:

import { defineConfig } from 'vite'
import handlebars from 'vite-plugin-handlebars';
import { resolve } from 'path';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [handlebars({
    partialDirectory: resolve(__dirname, 'public/partials')
  })],
})

Here is my project structure:

├── package.json
├── vite.config.js
├── index.html
└── public
    ├── about
        ├── about.html
    ├── contact
        ├── contact.html
    └── partials
        ├── header.hbs
        └── footer.hbs

Basically, {{> header }} works as expected on the index.html page, but shows up as raw text on the about.html page and the contact.html pages. Am I missing something obvious in my config?I built my project using the Vanilla JS scaffold right from the Vite docs.

Upgrade to support Vite 5

Thank you for your awesome work on this plug-in.

After upgrading to Vite 5, the following warnings appear after startup:

plugin 'handlebars' uses deprecated 'enforce' option. Use 'order' option instead.
plugin 'handlebars' uses deprecated 'transform' option. Use 'handler' option instead.

Reloading not happening when partial changes (windows 10)

When I hit save the therminal shows [vite] page reload partiasl/fine.html but the browser never reloads.

I did a investigation I found out that there is a check if the file changed must trigger a full-reload, and that check is broken. At least at windows.

The check:
image

The values (set and filename) being checked:
image

For some reason, the slashes are the being misplaced.

I have noticed that the problem appear immediately after the partial filename has been added to the set

image

image

My vite config is the following:

export default defineConfig({
  root: "src",
  assetsInclude: ["/src/partials/*"],
  plugins: [
    handlebarsPlugin({
      partialDirectory: resolve(__dirname, "src/partials"),
    }),
  ],
});

Suggestion:
Remove the check partialsSet.has(file), looks like its no necessary

resolve-from-root return physic path on windows

OS: Windows 11
Node v20.9.0 & v20.11.1

It works fine on Mac OS, however the output for {{resolve-from-root ''relative/path/to/file.ext""}} is totally wrong on Windows. It will be return "C:\relative\path\to\file.ext"

Support `.hbs` extensions for entry files

As far as I can tell, Vite makes a hard assumption that the file it starts to resolve linked files from is an HTML file. There is code I've seen internally that validates that the extension is .html.

It would be nice if we could use .hbs for these files, if we're going to have Handlebars syntax in them, just for the purposes of syntax highlighting and the like.

I'm not sure what allowing non-html files at the "top level" would look like, but it would be really nice to support!

wrong path in dist when the public folder is used

Expected: suppose we have a file public/test/index.html, the file should be copied to dist/test/index.html.
Current Beavior: it's copied to dist/public/test/index.html.
Config:

import { resolve } from 'path'
import { defineConfig } from 'vite'
import handlebars from 'vite-plugin-handlebars';

export default defineConfig({
  plugins: [
    handlebars({partialDirectory: resolve(__dirname, 'partials')})
  ],
  build: {
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'index.html'),
        standards: resolve(__dirname, 'public/test/index.html')
      }
    }
  }
})

Support relative links to assets from partials

I would really like to be able to support relative links in partials that are resolved relative to the partial itself rather than the file that the partial is injected into. This would make it nicer to keep, for example, CSS and JS files related to that partial in a directory with the partial itself.

Ideally, this would happen by Vite exposing the required logic to resolve these links in such a way that they can be run on the .hbs partial files. I looked into this a bit, and that doesn't seem possible (at this time, at least).

We could parse and re-write the links within this plugin, but it would be really nice to re-use the logic from Vite here instead...

I looked into running transformIndexHtml on each partial, which did not work initially but is probably the best way to go. I'm not sure if there would be pitfalls here due to the plugin potentially running the hbs transform on the partials before they are actually injected, though. There's investigation to do here!

Another option would be to provide a helper that solves this. It would do something similar to the currently-provided (and intended-to-be-removed) resolve-from-root helper, but something like resolve-relative, that handles re-writing the path. This is probably easier than trying to run transformIndexHtml on each partial before injecting them.

[Security] Workflow ci.yml is using vulnerable action actions/checkout

The workflow ci.yml is referencing action actions/checkout using references v1. However this reference is missing the commit a6747255bd19d7a757dbdda8c654a9f84db19839 which may contain fix to the some vulnerability.
The vulnerability fix that is missing by actions version could be related to:
(1) CVE fix
(2) upgrade of vulnerable dependency
(3) fix to secret leak and others.
Please consider to update the reference to the action.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.