I'm working with the Handlebars library and developing on a function to retrieve all embedded variables. The function works with simple, non-logic, text. However, when there is if-statement the function only returns a subset of the varibles.
import Handlebars from "handlebars";
function getHandlebarsVariables(input){
const ast = Handlebars.parseWithoutProcessing(input);
return ast.body
.filter(({ type }) => type === 'MustacheStatement')
.map((statement) => statement.params[0]?.original || statement.path?.original);
};
const text1 = '{{name}} {{age}} {{#if isAdult}} {{ Adult }} {{/if}}';
const variables = getHandlebarsVariables(text1);
console.log(variables); // currently returns ['name', 'age'], I want to return ['name', 'age', 'Adult']
What I expect the function to return: ['name', 'age', 'Adult'] Currently I get: ['name', 'age']
I think there is more to a compiler than you are giving it credit for. Your filter and map logic would be sufficient if the parsed Handlebars template were a simple, flat Array; but such a Syntax Tree is not that - it has many levels which must be parsed using recursion.
I am offering the code below, not as a fully-functional, shippable program, but just as an example of how you could recursively traverse the Syntax Tree. This example works for the text1
input in your post, but I would not count on it to work for all Handlebars templates.
import Handlebars from "handlebars";
function getVariablesFromStatementsRecursive(statements) {
return statements.reduce((acc, statement) => {
const { type } = statement;
if ("BlockStatement" === type) {
const { inverse, program } = statement;
if (program?.body) {
acc = acc.concat(getVariablesFromStatementsRecursive(program.body));
}
if (inverse?.body) {
acc = acc.concat(getVariablesFromStatementsRecursive(inverse.body));
}
} else if ("MustacheStatement" === type) {
const { path } = statement;
if (path?.original) {
acc.push(path.original);
}
}
return acc;
}, []);
}
function getHandlebarsVariables(input) {
const ast = Handlebars.parseWithoutProcessing(input);
return getVariablesFromStatementsRecursive(ast.body);
}
const text1 = "{{name}} {{age}} {{#if isAdult}} {{ Adult }} {{/if}}";
const variables = getHandlebarsVariables(text1);
console.log(variables); // [ 'name', 'age', 'Adult' ]