Premise
We've got handlebars running in a backend nodejs application for templating various messages that get sent.
Handlebarspile throws this exception (when piling templates from partials)
Error: You must pass a string or Handlebars AST to Handlebarspile. You passed <html>
<head>
... extremely long markup
at Objectpile (/Users/guscrawford/rollick-management-console/deployd/node_modules/handlebars/dist/cjs/handlebars/piler/piler.js:501:11)
at HandlebarsEnvironment.hbpile (/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 = handlebarspile(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.
Premise
We've got handlebars running in a backend nodejs application for templating various messages that get sent.
Handlebars.pile throws this exception (when piling templates from partials)
Error: You must pass a string or Handlebars AST to Handlebars.pile. You passed <html>
<head>
... extremely long markup
at Object.pile (/Users/guscrawford/rollick-management-console/deployd/node_modules/handlebars/dist/cjs/handlebars/piler/piler.js:501:11)
at HandlebarsEnvironment.hb.pile (/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.pile(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.
3 Answers
Reset to default 13The 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
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.pile. 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.pile. You passed ' + input);
This solved it for me:
const template:Handlebars.TemplateDelegate | null = Handlebars.pile(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.