Skip to content
This repository was archived by the owner on Apr 10, 2026. It is now read-only.
This repository was archived by the owner on Apr 10, 2026. It is now read-only.

Ability to Dynamically Load handlebars helper functions #325

Description

@esatterwhite

Is your feature request related to a problem? Please describe.
Adding helper functions / blocks for handlebars is a tedious process as the are baked into this package with no way to load them.
One has to get a pull request merged into this repo and wait for it to be released and update razee deployments in existing environments to be able to use them.

Describe the solution you'd like
It would be nice to have a way to load functions dynamically into the controller. Generally, The easiest way is to private an environment variable that specifies a location to look for node modules and tries to load them. If it can, it adds them to the handlebars instance.

Additionally, it may be useful to have a config option that allows one to disable specific functions or sets of functions if it is deemed they shouldn't be used by a implements

- name: RAZEE_HANDLEBARS_HELPER_LOCATION
  value: /opt/hbs
- name: RAZEE_HANDLEBARS_HELPERS
  value: '*'  | 'builtin' | 'upper,lower,stringify,concat'

Additional context
This is what we do with our in-house razee tooling that we use to test templates before they are deployed
It really just tries to load files on disk. If it loads a function it adds it by name, if its an object and the value is a function it'll load all of the functions in the object where the key is the name of the helper.

there are a couple of options, but you can point it at:

  • an absolute or relative path to a directory
  • an absolute or relative path to a file
'use strict'

const path = require('path')
const {promisify, debuglog} = require('util')
const glob = promisify(require('glob'))

const log = debuglog('template-handlebars')

module.exports = loadHelpers
async function loadHelpers(dir, cwd = process.cwd()) {
  const out = {}
  if (!dir) return out
  const files = await glob(path.join(dir, '**', '*.js'), {cwd: cwd, absolute: true})
  for (const location of files) {
    log('loading helper file %s', location)
    const helper = require(location)
    switch (typeof helper) {
      case 'object': {
        for (const [name, fn] of Object.entries(helper)) {
          const value_type = typeof fn
          if (value_type !== 'function') {
            const error = new TypeError(
              `Object helpers must contain function properties. Got ${value_type}`
            )
            error.code = 'ETEMPLATEHELPER'
            error.location = location
            error.helper = name

            throw error
          }
          log('helper %s loaded', name)
          out[name] = fn
        }
        break
      }

      case 'function': {
        const name = helper.name
          ? helper.name.toLowerCase()
          : path.parse(location).name

        log('helper %s loaded', name)
        out[name] = helper
        break
      }

      default: {
        const error = new TypeError(
          `Unexpected handlebars helper type ${typeof helper}.`
            + ' Helpers must be functions, or an object containg functions'
        )
        error.code = 'ETEMPLATEHELPER'
        error.location = location
        throw error
      }
    }
  }
  return out
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions