Skip to content

Instantly share code, notes, and snippets.

@jmjuanes
Created April 3, 2023 16:12
Show Gist options
  • Save jmjuanes/fe79d87d5a98144c7e4ff46d005bac21 to your computer and use it in GitHub Desktop.
Save jmjuanes/fe79d87d5a98144c7e4ff46d005bac21 to your computer and use it in GitHub Desktop.
Mustache template engine
const escapedChars = {
"&": "&",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#039;",
};
const escape = str => {
return str.toString().replace(/[&<>\"']/g, m => escapedChars[m]);
};
const get = (ctx, fullPath) => {
return fullPath.split(".").reduce((p, k) => p?.[k], ctx) || "";
};
const compile = (tokens, output, ctx, index, section) => {
let i = index;
while (i < tokens.length) {
if (i % 2 === 0) {
output.push(tokens[i]);
}
else if (tokens[i].startsWith("!")) {
output.push(get(ctx, tokens[i].slice(1).trim()));
}
else if (tokens[i].startsWith("#")) {
const t = tokens[i].slice(1).trim();
const value = get(ctx, t);
const j = i + 1;
if (value && Array.isArray(value)) {
value.forEach((item, k) => {
i = compile(tokens, output, {"@item": item, "@index": k}, j, t);
});
}
else if (value && typeof value === "object") {
Object.keys(value).forEach(key => {
i = compile(tokens, output, {"@key": key, "@value": value[key]}, j, t);
});
}
else {
i = compile(tokens, !!value ? output : [], ctx, i + 1, t);
}
}
else if (tokens[i].startsWith("/")) {
if (tokens[i].slice(1).trim() !== section) {
throw new Error(`Unmatched section end: {{${tokens[i]}}}`);
}
break;
}
else {
output.push(escape(get(ctx, tokens[i].trim())));
}
i = i + 1;
}
return i;
};
const mustache = (str, data) => {
const output = [];
compile(str.split(/\{\{|\}\}/), output, data, 0, "");
return output.join("");
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment