Gus Crawford
Gus Crawford

Reputation: 1037

Handlebars.compile throws an exception 'Error: You must pass a string or Handlebars AST to Handlebars.compile. You passed <html>...'

Premise

We've got handlebars running in a backend nodejs application for templating various messages that get sent.

Handlebars.compile throws this exception (when compiling templates from partials)

Error: You must pass a string or Handlebars AST to Handlebars.compile. You passed <html>
<head>
... extremely long markup
at Object.compile (/Users/guscrawford/rollick-management-console/deployd/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:501:11)
at HandlebarsEnvironment.hb.compile (/Users/guscrawford/rollick-management-console/deployd/node_modules/handlebars/dist/cjs/handlebars.js:39:40)
at Object.invokePartialWrapper [as invokePartial] (/Users/guscrawford/rollick-management-console/deployd/node_modules/handlebars/dist/cjs/handlebars/runtime.js:71:44)
... additional stack trace through to dpd, bluebird etc.

Cannot replicate through isolation

Go ahead and try setting up a scrap project: yarn add handlebars handlebars-helper-ternary handlebars-helpers handlebars.numeral

Then run this script in nodejs:

const   handlebars = require('handlebars'),
        numeralHelper = require('handlebars.numeral'),    
        ternaryHelper = require('handlebars-helper-ternary'),
        helpers = require('handlebars-helpers')({
        handlebars: handlebars
    });
console.log(`Testing...`);
const base = `
<html>
    <body style="font-family:'Segoe UI', Tahoma, Geneva, Verdana,     'sans-serif'; font-size: larger;">
    {{>@partial-block }}
    <td style="text-align: center; padding: 24px;">
    Copyright 2018 My Company, Inc. All rights reserved.

    </body>
</html>

`;
const inner = `
{{#>base}}
    {{subscriber.name}},

    {{member.name}} has received a notifier from {{subscriber.name}}.    

    Click the link below to review!. 
    <a href='{{link}}'>Go!</a>

    Thank you,
    My Company
{{/base}}
`;
numeralHelper.registerHelpers(handlebars);
handlebars.registerHelper('ternary', ternaryHelper);
handlebars.registerHelper("moduloIf", function (index_count, mod, block)     {

    if (index_count > 0 && parseInt(index_count) % (mod) === 0) {
        return block.fn(this);
    } else {
        return block.inverse(this);
    }
});

handlebars.registerHelper("substitute", function(a, options) {
  try {
    function index(obj,i) { return obj ? obj[i] : {} }
    let data = a.split('.').reduce(index, this);
    if (data && typeof data === 'string') return data;
    else return options.fn(this);
  } catch (e) {
    console.log('substitute helper:' + e);
  }
});
handlebars.registerPartial('base',base)
var output = handlebars.compile(inner)({name:'Gus'});
console.log('Output:');
console.log(output)

Further consideration

In actuality we have the handlebars require wrapped in another module with code run against the handlebars instance as illustrated in the sample script. We're exporting the handlebars instance.

Upvotes: 2

Views: 6658

Answers (3)

The Golden Dev
The Golden Dev

Reputation: 1

This solved it for me:

const template:Handlebars.TemplateDelegate | null = Handlebars.compile(new XMLSerializer().serializeToString(your html document or string));

console.log(template({your_key : "your_value"})) 

It probably converts a document type to an XML string representing a DOM tree.
Javascript is a bit hard to deal with.

Upvotes: 0

Gus Crawford
Gus Crawford

Reputation: 1037

The string was a Buffer

Despite logging typeof the template string I was passing as a string, the output of readFileAsync without passing an encoding is a raw node Buffer.

Duh

Upvotes: 13

Marcos Casagrande
Marcos Casagrande

Reputation: 40444

The error is clear, you're passing something that isn't a string or AST.

This is the only way handlebars throws that error.

if (input == null || (typeof input !== 'string' && input.type !== 'Program')) {
    throw new Exception('You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + input);
}

You're probably passing an object, with a toString method, that's why you see:

You passed <html>
<head>
... extremely long markup

const input = {
  toString() {
    return `<html>
    <head>`;
  }
}
console.log('You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + input);

Upvotes: 2

Related Questions