npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

hamlet-plugin-template

v1.3.1

Published

A plugin template for hamlet

Readme

hamlet-plugin-template

Base template for creating Hamlet plugins.

Usage

  1. Rename the package in package.json (e.g. @scope/hamlet-feature or hamlet-plugin-feature).
  2. Add your Handlebars partials as .hbs (or .xml, .handlebars) files inside src/.
  3. Implement helpers, context, and set the namespace in index.js.
  4. Run npm run build:partials to generate lib/partials.js from your source files.
  5. Run the tests.
  6. Publish to npm.

End-user installation

npm install hamlet-plugin-template
// hamlet.config.js
import Sample from 'hamlet-plugin-template'

export default {
  plugins: [
    Sample(),
    // or with options:
    Sample({ myOption: true }),
  ],
}

Plugins are imported and invoked directly from hamlet.config.js, similar to Vite or Rollup plugins. Hamlet never resolves plugins by package name — it only consumes the object returned by the plugin factory.

Project structure

├── src/
│   ├── card.hbs        ← your partial source files
│   └── hello.hbs
├── lib/
│   └── partials.js     ← auto-generated, do not edit
├── scripts/
│   └── build-partials.js
├── index.js
└── package.json

Source files in src/ are compiled into lib/partials.js by npm run build:partials. This script runs automatically before publishing via the prepublishOnly hook.

Plugin contract

A plugin must export a default factory function that receives the user's options and returns an object with the following shape:

import { partials } from './lib/partials.js'

export default function hamletPlugin(options = {}) {
  return {
    namespace: 'Sample',
    partials,
    helpers: {},
    context: {},
  }
}

All keys except namespace are optional. A minimal valid plugin only needs a namespace.

namespace

Every plugin must define a unique namespace.

  • It must start with a letter.
  • It may contain only letters and digits.
  • It is used to prefix all exported partials and helpers automatically — you only use short names inside the plugin.
  • The namespace hamlet is reserved and cannot be used.

partials

An object of Handlebars partials keyed by short names (without the namespace prefix). Hamlet registers them as <namespace>.<name> automatically.

Add .hbs, .xml, or .handlebars files to src/ and run npm run build:partials to populate lib/partials.js. The file name becomes the partial key.

Example — src/card.hbs:

<div{{#if className}} class="{{className}}"{{/if}}>
  {{#if title}}<h2>{{title}}</h2>{{/if}}
  {{#if description}}<p>{{description}}</p>{{/if}}
</div>

This becomes partials.card, registered by Hamlet as Sample.card:

{{> Sample.card title="Hello"}}

helpers

An object of Handlebars helpers keyed by short camelCase names (without the namespace prefix). Hamlet registers them as <namespace><Name> automatically.

Example:

export default function hamletPlugin(options = {}) {
  return {
    namespace: 'Sample',
    helpers: {
      shout: value => value.toUpperCase(),
    },
  }
}

This registers the helper as SampleShout, usable in templates as:

{{SampleShout "hello"}}

Helper names cannot contain dots because Handlebars interprets them as property paths.

context

An optional plain object with data that the plugin wants to expose to all Handlebars templates. Hamlet merges it into the template context under the plugin's namespace key.

export default function hamletPlugin(options = {}) {
  return {
    namespace: 'Sample',
    context: {
      spriteUrl: options.spriteUrl ?? '/icons.svg',
      version: '2.0',
    },
  }
}

In any template, the data is accessible as {{<namespace>.<key>}}:

{{Sample.spriteUrl}}
{{Sample.version}}

The context is frozen after registration — mutations at runtime have no effect. If context is present but is not a plain object, it will be ignored and a warning will be printed.

Self-referencing context inside partials

When a partial needs to reference its own plugin's context, avoid hardcoding the namespace. Use the {{_self.key}} alias instead:

{{!-- src/card.hbs --}}
<img src="{{_self.spriteUrl}}"/>

build-partials.js replaces every {{_self. occurrence with {{<namespace>. at build time, so the published lib/partials.js always contains the correct namespace. This means renaming the namespace in index.js and re-running npm run build:partials is all that's needed — no partial file needs to be touched.

Name collisions

Hamlet follows a first registered wins policy.

If a plugin attempts to register a partial or helper whose name already exists (whether built-in to Hamlet, from another plugin, or from the user's project), the duplicate is silently skipped and a warning is printed.

Using Hamlet features inside plugin partials

Plugin partials can freely use Hamlet's built-in helpers and partials:

{{concat "Hello, " name}}

{{#switch value}}
  {{#case "post"}}...{{/case}}
  {{#default}}...{{/default}}
{{/switch}}

{{> hamlet.snippet}}

Scripts

| Script | Description | | ------ | ----------- | | npm run build:partials | Generates lib/partials.js from files in src/ | | npm test | Runs the test suite | | npm run lint | Lints the project | | npm run lint:fix | Lints and auto-fixes |

build:partials also runs automatically via prepublishOnly before every npm publish.

Tests

npm install
npm test

The test suite verifies that:

  • The plugin exports a valid factory function.
  • The returned object follows Hamlet's plugin contract.
  • A valid, non-reserved namespace is exported.
  • Partial and helper names are short (no namespace prefix) and follow Hamlet's naming rules.
  • No computed full name collides with Hamlet's reserved partials or helpers.
  • Partials are strings and compile without Handlebars syntax errors.
  • Helpers are functions (not nested objects).
  • If present, context is a plain object (not an array, not null).
  • No {{_self. references remain in compiled partials (i.e. build:partials was run).
  • No name is on Hamlet's blocked list (__proto__, constructor, prototype).

Feel free to extend the tests with plugin-specific behavior or option validation.

Linting

This template uses the same ESLint configuration as Hamlet itself (@antfu/eslint-config).

npm run lint
npm run lint:fix