@templatical/renderer
Advanced tools
+6
-0
@@ -68,2 +68,8 @@ import { CustomFont, Block, ColumnLayout, SpacingValue, CustomBlock, TemplateContent } from '@templatical/types'; | ||
| * Also handles `data-logic-merge-tag` attributes. | ||
| * | ||
| * Uses a single-pass linear scan instead of an `[^>]*…[^>]*` regex because | ||
| * the latter is polynomial-ReDoS over inputs that contain many `<span` | ||
| * starts but no closing `>` — the engine retries `[^>]*` at every span | ||
| * position. The scan below resolves each `<span>` open tag with a bounded | ||
| * `indexOf('>')`, keeping the work strictly O(n). | ||
| */ | ||
@@ -70,0 +76,0 @@ declare function convertMergeTagsToValues(html: string): string; |
+48
-10
@@ -8,3 +8,3 @@ // src/index.ts | ||
| description: "Render Templatical email templates to MJML", | ||
| version: "0.8.4", | ||
| version: "0.8.5", | ||
| bugs: "https://github.com/templatical/sdk/issues", | ||
@@ -171,12 +171,50 @@ dependencies: { | ||
| } | ||
| let result = html.replace( | ||
| /<span[^>]*\bdata-merge-tag="([^"]*)"[^>]*>.*?<\/span>/gs, | ||
| "$1" | ||
| return rewriteMergeTagSpans( | ||
| html, | ||
| (attrs) => findAttr(attrs, "data-merge-tag") ?? findAttr(attrs, "data-logic-merge-tag") | ||
| ); | ||
| result = result.replace( | ||
| /<span[^>]*\bdata-logic-merge-tag="([^"]*)"[^>]*>.*?<\/span>/gs, | ||
| "$1" | ||
| ); | ||
| return result; | ||
| } | ||
| function rewriteMergeTagSpans(html, extract) { | ||
| let out = ""; | ||
| let i = 0; | ||
| while (i < html.length) { | ||
| const open = html.indexOf("<span", i); | ||
| if (open === -1) { | ||
| out += html.substring(i); | ||
| break; | ||
| } | ||
| const afterTagName = html[open + 5]; | ||
| if (afterTagName !== ">" && afterTagName !== " " && afterTagName !== " " && afterTagName !== "\n" && afterTagName !== "\r" && afterTagName !== "/") { | ||
| out += html.substring(i, open + 5); | ||
| i = open + 5; | ||
| continue; | ||
| } | ||
| const openEnd = html.indexOf(">", open + 5); | ||
| if (openEnd === -1) { | ||
| out += html.substring(i); | ||
| break; | ||
| } | ||
| const closeStart = html.indexOf("</span>", openEnd + 1); | ||
| if (closeStart === -1) { | ||
| out += html.substring(i); | ||
| break; | ||
| } | ||
| const attrs = html.substring(open + 5, openEnd); | ||
| const replacement = extract(attrs); | ||
| if (replacement === null) { | ||
| out += html.substring(i, open + 5); | ||
| i = open + 5; | ||
| continue; | ||
| } | ||
| out += html.substring(i, open); | ||
| out += replacement; | ||
| i = closeStart + 7; | ||
| } | ||
| return out; | ||
| } | ||
| function findAttr(attrs, name) { | ||
| const pattern = new RegExp(`(?:^|\\s)${name}="([^"<>]*)"`); | ||
| const match = pattern.exec(attrs); | ||
| return match ? match[1] : null; | ||
| } | ||
@@ -272,3 +310,3 @@ // src/padding.ts | ||
| } | ||
| const stripped = block.content.replace(/<\/?p[^>]*>/gi, "").trim(); | ||
| const stripped = block.content.replace(/<\/?p\b[^<>]*>/gi, "").trim(); | ||
| if (stripped === "") { | ||
@@ -275,0 +313,0 @@ return ""; |
+2
-2
| { | ||
| "name": "@templatical/renderer", | ||
| "description": "Render Templatical email templates to MJML", | ||
| "version": "0.8.4", | ||
| "version": "0.8.5", | ||
| "bugs": "https://github.com/templatical/sdk/issues", | ||
| "dependencies": { | ||
| "@templatical/types": "0.8.4" | ||
| "@templatical/types": "0.8.5" | ||
| }, | ||
@@ -9,0 +9,0 @@ "devDependencies": { |
Sorry, the diff of this file is too big to display
464635
1.14%1112
4.12%+ Added
- Removed
Updated