function escapeHtml(str) { if (str == null) return ""; return String(str) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } export function renderTemplate(template, data = {}, opts = { allowRaw: true }) { // 支持 nested keys (a.b.c) const get = (path) => path .split(".") .reduce((acc, k) => (acc == null ? undefined : acc[k]), data); return template.replace( /\{\{\{\s*([\w$.]+)\s*\}\}\}|\{\{\s*([\w$.]+)\s*\}\}/g, (m, rawKey, escKey) => { const key = rawKey || escKey; const val = get(key); if (rawKey) return opts.allowRaw ? val == null ? "" : String(val) : escapeHtml(val); return val == null ? "" : escapeHtml(val); } ); }