foundation/panini

Panini should expose the underlying Handlebars instance

flyingL123 opened this issue · 8 comments

I'm trying to create a custom helper that either returns a string or returns the output of a partial depending on a data setting. Here's what I have so far in src/helpers/snippet.js:

module.exports = function(filename, variableName) {
  if (this.global.is_dev) {
    return '%%' + variableName + '%%';
  }
  else {
    // Would like to return the output of the partial 'filename'
  }
}

As you can see, I would like to return the output of a partial in the else branch. However, after a lot of experimenting and source code reading, it seems like there is no way to access the Handlebars instance in order to find the partial.

For example, I found this explanation of how to do it:

handlebars-lang/handlebars.js#1057

The issue I'm having is there is no way to access something like panini.Handlebars or something like that. Can the underlying Handlebars instance be exposed, or is there some other way this is meant to be done?

Yeah, it seems like to return the result of a template, you have to compile it with Handlebars.compile. There's not an easy way right now to get the Handlebars context, especially because it's possible to initialize multiple Panini instances, so there's not just one Handlebars instance to grab.

This will probably require some add-on to the way helpers are defined to indicate "hey I want to access the Handlebars instance in this function".

This is kind of an ugly example, but maybe something like this:

module.exports = function(hbs) {
  return function(fileName, variableName) {
    if (this.global.is_dev) {
      return '%%' + variableName + '%%';
    } else {
      const template = hbs.compile('{{> filename }}');
      return new Handlebars.SafeString(template(this));
    }
  }
};

module.exports.context = true;

This isn't super elegant but you get the idea. A function returning a function makes sense to me, but then we also need a way to signal to Panini that the export is a function returning a helper, not the helper itself.

Thanks @gakimball. Just to make sure I am on the same page, even something as simple as returning a Handlebars.SafeString, as you show in your example, is not currently possibly from a custom panini helper, is it? As far as I can tell, you can't access Handlebars. That was what I meant to come across in my original question.

You can still import it even if it's not in your package.json, since it's a dependency of Panini. I don't think the environment matters, since Handlebars.SafeString is just an instruction to not escape output.

I see. I would have to import it from within the Panini dependency, which seems like not best practice:

https://stackoverflow.com/a/9736561/736864

Or I could require Handlebars myself, but that seems redundant. Having access to the underlying Handlebars instance seems like the best option.

My opinion is if Panini allows you to add Handlebars helpers, then it should allow you to access any of the underlying functionality of Handlebars you may need.

I'm not so familiar with the code so I need to look through it more to understand how this might be possible.

That SO answer was written before npm started installing dependencies flat. This means that both panini and handlebars are in your project's top-level node_modules folder, so you can write require('handlebars') and it will just work.

Is there something I am supposed to do to make that flat installation happen? Because I don't have handlebars in my node_modules directory, and when I add var Handlebars = require('handlebars'); to the top of my helper file I get an error:

Error when loading link.js as a Handlebars helper.
Panini: rendering error ocurred.

I created the project with: foundation new --framework emails

Is it possible that devDependencies or not installed flat? The npm docs say that secondary dependencies are installed flat. Does that not include devDependencies? I'm just confused why I don't have handlebars in my node_modules directory as you described.

I don't think there should be a difference, assuming you're using at least npm 3. Either way, you can add handlebars as an explicit top-level dependency to guarantee that handlebars shows up as a top-level module.