@emailens/engine 0.11.1 → 0.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -390,6 +390,55 @@ declare function checkVml(html: string, options?: {
390
390
  positions?: boolean;
391
391
  }): VmlReport;
392
392
 
393
+ /**
394
+ * Resolve the Outlook branch of an email and translate its VML into CSS a
395
+ * browser can draw.
396
+ *
397
+ * Every preview of a Word-engine Outlook has, until now, rendered the wrong
398
+ * half of the email: the `<!--[if mso]>` blocks stayed commented and the
399
+ * `<!--[if !mso]>` fallback stayed live, so the screenshot showed precisely
400
+ * what Outlook does *not* render and hid what it does.
401
+ *
402
+ * Resolving the branch alone makes that worse rather than better. VML has been
403
+ * dead in browsers since IE9, so `<v:rect>` parses as an HTMLUnknownElement:
404
+ * `display:inline`, no box, no fill. The content inside would render as bare
405
+ * inline text. Resolution and translation therefore ship together and are not
406
+ * useful apart.
407
+ *
408
+ * ponytail: a mapping for the two shapes that occur in practice, not a VML
409
+ * renderer. Measured across a reported production email and MJML's output,
410
+ * `roundrect` and `rect` are the whole surface. Anything else is left alone
411
+ * rather than approximated.
412
+ *
413
+ * Deliberately NOT emulated: how Word *mis*-renders a nested shape. That is a
414
+ * real and confirmed failure (the container disappears and text stops drawing
415
+ * further down the document), but reproducing it means perturbing Chromium's
416
+ * layout into a picture that is wrong in a third way, different from both the
417
+ * correct render and Outlook's. `checkVml` reports it with a line number
418
+ * instead, which is more useful and cannot mislead.
419
+ */
420
+ /**
421
+ * `arcsize` is a VgFraction: a proportion of *half the shape's shorter side*,
422
+ * where 0% is square and 100% fully circular. Values outside that are clamped
423
+ * rather than rejected, which is why 120% and 100% draw the identical corner.
424
+ * Verified against Outlook Classic.
425
+ */
426
+ declare function arcsizeToRadius(arcsize: string, width: number, height: number): number;
427
+ /**
428
+ * Uncomment the Outlook-only blocks and delete the downlevel-revealed branch,
429
+ * leaving the markup a Word-engine client actually parses.
430
+ */
431
+ declare function resolveMsoBranch(html: string): string;
432
+ /** Translate the VML shapes a browser cannot draw into divs it can. */
433
+ declare function vmlToCss(html: string): string;
434
+ /**
435
+ * Render the branch a Word-engine Outlook actually sees.
436
+ *
437
+ * Returns the input unchanged when there is nothing Outlook-only in it, so an
438
+ * email without conditional comments is never rewritten for no reason.
439
+ */
440
+ declare function renderOutlookBranch(html: string): string;
441
+
393
442
  /**
394
443
  * Detect probable visual bugs in a stylized email: background images/gradients
395
444
  * with no colour fallback, and font stacks with no web-safe fallback. Each
@@ -674,4 +723,4 @@ declare function colorDistance(a: RGBA, b: RGBA): number;
674
723
  */
675
724
  declare function downlevelCSS(html: string): string;
676
725
 
677
- export { AI_FIX_SYSTEM_PROMPT, AT_RULE_FEATURES, AccessibilityIssue, AccessibilityReport, AiFixResult, AiProvider, type AuditOptions, type AuditReport, COMPOUND_VALUE_FEATURES, CSSWarning, CSS_FUNCTION_FEATURES, CSS_SUPPORT, CSS_SUPPORT_NOTES, CodeFix, type CreateSessionOptions, DeliverabilityReport, DesignReport, DiffResult, EMAIL_CLIENTS, EMPTY_DELIVERABILITY, EmailClient, type EmailSession, ExportPromptOptions, FEATURE_URLS, Framework, GENERIC_LINK_TEXT, type GenerateAiFixOptions, HTML_ELEMENT_FEATURES, ImageReport, InboxPreview, LinkReport, MAX_HTML_SIZE, MAX_WARNING_LOCATIONS, OverflowReport, type ParseOptions, type RGBA, type RenderContext, STRUCTURAL_FIX_PROPERTIES, SizeReport, SpamAnalysisOptions, SpamReport, SupportLevel, TemplateReport, TransformResult, VALUE_CAVEAT_PROPS, VisualReport, VmlReport, type WcagGrade, alphaBlend, analyzeEmail, analyzeImages, analyzeSpam, auditEmail, caveatApplies, checkAccessibility, checkDarkModeContrast, checkDarkStylesContrastFromDom, checkDesignConsistency, checkMobileContrast, checkOverflow, checkSize, checkTemplateVariables, checkVisual, checkVml, colorDistance, contrastRatio, createSession, diffResults, downlevelCSS, errorWarnings, extractInboxPreview, featureUrl, formatRgb, generateAiFix, generateCompatibilityScore, getClient, getCodeFix, getSuggestion, parseColor, relativeLuminance, rgbToOklab, simulateDarkMode, structuralWarnings, toPlainText, transformForAllClients, transformForClient, validateLinks, warningsForClient, wcagGrade };
726
+ export { AI_FIX_SYSTEM_PROMPT, AT_RULE_FEATURES, AccessibilityIssue, AccessibilityReport, AiFixResult, AiProvider, type AuditOptions, type AuditReport, COMPOUND_VALUE_FEATURES, CSSWarning, CSS_FUNCTION_FEATURES, CSS_SUPPORT, CSS_SUPPORT_NOTES, CodeFix, type CreateSessionOptions, DeliverabilityReport, DesignReport, DiffResult, EMAIL_CLIENTS, EMPTY_DELIVERABILITY, EmailClient, type EmailSession, ExportPromptOptions, FEATURE_URLS, Framework, GENERIC_LINK_TEXT, type GenerateAiFixOptions, HTML_ELEMENT_FEATURES, ImageReport, InboxPreview, LinkReport, MAX_HTML_SIZE, MAX_WARNING_LOCATIONS, OverflowReport, type ParseOptions, type RGBA, type RenderContext, STRUCTURAL_FIX_PROPERTIES, SizeReport, SpamAnalysisOptions, SpamReport, SupportLevel, TemplateReport, TransformResult, VALUE_CAVEAT_PROPS, VisualReport, VmlReport, type WcagGrade, alphaBlend, analyzeEmail, analyzeImages, analyzeSpam, arcsizeToRadius, auditEmail, caveatApplies, checkAccessibility, checkDarkModeContrast, checkDarkStylesContrastFromDom, checkDesignConsistency, checkMobileContrast, checkOverflow, checkSize, checkTemplateVariables, checkVisual, checkVml, colorDistance, contrastRatio, createSession, diffResults, downlevelCSS, errorWarnings, extractInboxPreview, featureUrl, formatRgb, generateAiFix, generateCompatibilityScore, getClient, getCodeFix, getSuggestion, parseColor, relativeLuminance, renderOutlookBranch, resolveMsoBranch, rgbToOklab, simulateDarkMode, structuralWarnings, toPlainText, transformForAllClients, transformForClient, validateLinks, vmlToCss, warningsForClient, wcagGrade };
package/dist/index.d.ts CHANGED
@@ -390,6 +390,55 @@ declare function checkVml(html: string, options?: {
390
390
  positions?: boolean;
391
391
  }): VmlReport;
392
392
 
393
+ /**
394
+ * Resolve the Outlook branch of an email and translate its VML into CSS a
395
+ * browser can draw.
396
+ *
397
+ * Every preview of a Word-engine Outlook has, until now, rendered the wrong
398
+ * half of the email: the `<!--[if mso]>` blocks stayed commented and the
399
+ * `<!--[if !mso]>` fallback stayed live, so the screenshot showed precisely
400
+ * what Outlook does *not* render and hid what it does.
401
+ *
402
+ * Resolving the branch alone makes that worse rather than better. VML has been
403
+ * dead in browsers since IE9, so `<v:rect>` parses as an HTMLUnknownElement:
404
+ * `display:inline`, no box, no fill. The content inside would render as bare
405
+ * inline text. Resolution and translation therefore ship together and are not
406
+ * useful apart.
407
+ *
408
+ * ponytail: a mapping for the two shapes that occur in practice, not a VML
409
+ * renderer. Measured across a reported production email and MJML's output,
410
+ * `roundrect` and `rect` are the whole surface. Anything else is left alone
411
+ * rather than approximated.
412
+ *
413
+ * Deliberately NOT emulated: how Word *mis*-renders a nested shape. That is a
414
+ * real and confirmed failure (the container disappears and text stops drawing
415
+ * further down the document), but reproducing it means perturbing Chromium's
416
+ * layout into a picture that is wrong in a third way, different from both the
417
+ * correct render and Outlook's. `checkVml` reports it with a line number
418
+ * instead, which is more useful and cannot mislead.
419
+ */
420
+ /**
421
+ * `arcsize` is a VgFraction: a proportion of *half the shape's shorter side*,
422
+ * where 0% is square and 100% fully circular. Values outside that are clamped
423
+ * rather than rejected, which is why 120% and 100% draw the identical corner.
424
+ * Verified against Outlook Classic.
425
+ */
426
+ declare function arcsizeToRadius(arcsize: string, width: number, height: number): number;
427
+ /**
428
+ * Uncomment the Outlook-only blocks and delete the downlevel-revealed branch,
429
+ * leaving the markup a Word-engine client actually parses.
430
+ */
431
+ declare function resolveMsoBranch(html: string): string;
432
+ /** Translate the VML shapes a browser cannot draw into divs it can. */
433
+ declare function vmlToCss(html: string): string;
434
+ /**
435
+ * Render the branch a Word-engine Outlook actually sees.
436
+ *
437
+ * Returns the input unchanged when there is nothing Outlook-only in it, so an
438
+ * email without conditional comments is never rewritten for no reason.
439
+ */
440
+ declare function renderOutlookBranch(html: string): string;
441
+
393
442
  /**
394
443
  * Detect probable visual bugs in a stylized email: background images/gradients
395
444
  * with no colour fallback, and font stacks with no web-safe fallback. Each
@@ -674,4 +723,4 @@ declare function colorDistance(a: RGBA, b: RGBA): number;
674
723
  */
675
724
  declare function downlevelCSS(html: string): string;
676
725
 
677
- export { AI_FIX_SYSTEM_PROMPT, AT_RULE_FEATURES, AccessibilityIssue, AccessibilityReport, AiFixResult, AiProvider, type AuditOptions, type AuditReport, COMPOUND_VALUE_FEATURES, CSSWarning, CSS_FUNCTION_FEATURES, CSS_SUPPORT, CSS_SUPPORT_NOTES, CodeFix, type CreateSessionOptions, DeliverabilityReport, DesignReport, DiffResult, EMAIL_CLIENTS, EMPTY_DELIVERABILITY, EmailClient, type EmailSession, ExportPromptOptions, FEATURE_URLS, Framework, GENERIC_LINK_TEXT, type GenerateAiFixOptions, HTML_ELEMENT_FEATURES, ImageReport, InboxPreview, LinkReport, MAX_HTML_SIZE, MAX_WARNING_LOCATIONS, OverflowReport, type ParseOptions, type RGBA, type RenderContext, STRUCTURAL_FIX_PROPERTIES, SizeReport, SpamAnalysisOptions, SpamReport, SupportLevel, TemplateReport, TransformResult, VALUE_CAVEAT_PROPS, VisualReport, VmlReport, type WcagGrade, alphaBlend, analyzeEmail, analyzeImages, analyzeSpam, auditEmail, caveatApplies, checkAccessibility, checkDarkModeContrast, checkDarkStylesContrastFromDom, checkDesignConsistency, checkMobileContrast, checkOverflow, checkSize, checkTemplateVariables, checkVisual, checkVml, colorDistance, contrastRatio, createSession, diffResults, downlevelCSS, errorWarnings, extractInboxPreview, featureUrl, formatRgb, generateAiFix, generateCompatibilityScore, getClient, getCodeFix, getSuggestion, parseColor, relativeLuminance, rgbToOklab, simulateDarkMode, structuralWarnings, toPlainText, transformForAllClients, transformForClient, validateLinks, warningsForClient, wcagGrade };
726
+ export { AI_FIX_SYSTEM_PROMPT, AT_RULE_FEATURES, AccessibilityIssue, AccessibilityReport, AiFixResult, AiProvider, type AuditOptions, type AuditReport, COMPOUND_VALUE_FEATURES, CSSWarning, CSS_FUNCTION_FEATURES, CSS_SUPPORT, CSS_SUPPORT_NOTES, CodeFix, type CreateSessionOptions, DeliverabilityReport, DesignReport, DiffResult, EMAIL_CLIENTS, EMPTY_DELIVERABILITY, EmailClient, type EmailSession, ExportPromptOptions, FEATURE_URLS, Framework, GENERIC_LINK_TEXT, type GenerateAiFixOptions, HTML_ELEMENT_FEATURES, ImageReport, InboxPreview, LinkReport, MAX_HTML_SIZE, MAX_WARNING_LOCATIONS, OverflowReport, type ParseOptions, type RGBA, type RenderContext, STRUCTURAL_FIX_PROPERTIES, SizeReport, SpamAnalysisOptions, SpamReport, SupportLevel, TemplateReport, TransformResult, VALUE_CAVEAT_PROPS, VisualReport, VmlReport, type WcagGrade, alphaBlend, analyzeEmail, analyzeImages, analyzeSpam, arcsizeToRadius, auditEmail, caveatApplies, checkAccessibility, checkDarkModeContrast, checkDarkStylesContrastFromDom, checkDesignConsistency, checkMobileContrast, checkOverflow, checkSize, checkTemplateVariables, checkVisual, checkVml, colorDistance, contrastRatio, createSession, diffResults, downlevelCSS, errorWarnings, extractInboxPreview, featureUrl, formatRgb, generateAiFix, generateCompatibilityScore, getClient, getCodeFix, getSuggestion, parseColor, relativeLuminance, renderOutlookBranch, resolveMsoBranch, rgbToOklab, simulateDarkMode, structuralWarnings, toPlainText, transformForAllClients, transformForClient, validateLinks, vmlToCss, warningsForClient, wcagGrade };
package/dist/index.js CHANGED
@@ -9740,6 +9740,114 @@ function downlevelCSS(html) {
9740
9740
  return $.html();
9741
9741
  }
9742
9742
 
9743
+ // src/vml-render.ts
9744
+ function px(value) {
9745
+ if (!value) return void 0;
9746
+ const m = value.trim().match(/^(-?[\d.]+)(?:px)?$/i);
9747
+ return m ? parseFloat(m[1]) : void 0;
9748
+ }
9749
+ function styleProps(style) {
9750
+ const out = {};
9751
+ for (const decl of style.split(";")) {
9752
+ const i = decl.indexOf(":");
9753
+ if (i > 0) out[decl.slice(0, i).trim().toLowerCase()] = decl.slice(i + 1).trim();
9754
+ }
9755
+ return out;
9756
+ }
9757
+ function attrs(raw) {
9758
+ var _a, _b;
9759
+ const out = {};
9760
+ for (const m of raw.matchAll(/([\w:-]+)\s*=\s*"([^"]*)"|([\w:-]+)\s*=\s*'([^']*)'/g)) {
9761
+ out[((_a = m[1]) != null ? _a : m[3]).toLowerCase()] = (_b = m[2]) != null ? _b : m[4];
9762
+ }
9763
+ return out;
9764
+ }
9765
+ function arcsizeToRadius(arcsize, width, height) {
9766
+ const raw = arcsize.trim();
9767
+ const pct = raw.endsWith("%") ? parseFloat(raw) : parseFloat(raw) * 100;
9768
+ if (!Number.isFinite(pct)) return 0;
9769
+ return Math.min(100, Math.max(0, pct)) / 100 * (Math.min(width, height) / 2);
9770
+ }
9771
+ function resolveMsoBranch(html) {
9772
+ return html.replace(/<!--\[if\s*!\s*(?:mso|vml)[^\]]*\]><!-->([\s\S]*?)<!--<!\[endif\]-->/gi, "").replace(/<!--\[if[^\]]*(?:mso|vml)[^\]]*\]>([\s\S]*?)<!\[endif\]-->/gi, "$1");
9773
+ }
9774
+ function vmlToCss(html) {
9775
+ let out = html;
9776
+ out = out.replace(
9777
+ /<v:roundrect([^>]*)>([\s\S]*?)<\/v:roundrect>/gi,
9778
+ (_m, rawAttrs, inner) => {
9779
+ var _a, _b, _c, _d, _e, _f;
9780
+ const a = attrs(rawAttrs);
9781
+ const s = styleProps((_a = a.style) != null ? _a : "");
9782
+ const w = (_b = px(s.width)) != null ? _b : 0;
9783
+ const h = (_c = px(s.height)) != null ? _c : 0;
9784
+ const radius = a.arcsize ? arcsizeToRadius(a.arcsize, w, h) : 0.2 * (Math.min(w, h) / 2);
9785
+ const fill = (_d = a.fillcolor) != null ? _d : "transparent";
9786
+ const stroked = !(a.stroke === "f" || a.stroke === "false");
9787
+ const middle = /middle/i.test((_e = s["v-text-anchor"]) != null ? _e : "");
9788
+ const label = inner.replace(/<\/?(?:w:anchorlock|center)[^>]*>/gi, "").trim();
9789
+ const box = [
9790
+ w ? `width:${w}px` : "",
9791
+ h ? `height:${h}px` : "",
9792
+ `border-radius:${radius}px`,
9793
+ `background:${fill}`,
9794
+ stroked ? `border:1px solid ${(_f = a.strokecolor) != null ? _f : "#000"}` : "border:none",
9795
+ "display:inline-flex",
9796
+ "justify-content:center",
9797
+ `align-items:${middle ? "center" : "flex-start"}`,
9798
+ "overflow:hidden",
9799
+ "text-align:center"
9800
+ ].filter(Boolean).join(";");
9801
+ const href = a.href ? ` data-href="${a.href}"` : "";
9802
+ return `<div data-vml="roundrect"${href} style="${box}">${label}</div>`;
9803
+ }
9804
+ );
9805
+ out = out.replace(
9806
+ /<v:rect([^>]*)>([\s\S]*?)<\/v:rect>/gi,
9807
+ (_m, rawAttrs, inner) => {
9808
+ var _a, _b, _c, _d, _e, _f;
9809
+ const a = attrs(rawAttrs);
9810
+ const s = styleProps((_a = a.style) != null ? _a : "");
9811
+ const w = px(s.width);
9812
+ const h = px(s.height);
9813
+ const fillTag = inner.match(/<v:fill([^>]*?)\/?>/i);
9814
+ const f = fillTag ? attrs(fillTag[1]) : {};
9815
+ const type = ((_b = f.type) != null ? _b : "solid").toLowerCase();
9816
+ let background = "";
9817
+ if (f.src && type === "tile") {
9818
+ background = `background-image:url('${f.src}');background-repeat:repeat;`;
9819
+ } else if (f.src) {
9820
+ background = `background-image:url('${f.src}');background-size:cover;background-position:center;`;
9821
+ } else if (type === "gradient" || type === "gradientradial") {
9822
+ const from = (_d = (_c = f.color) != null ? _c : a.fillcolor) != null ? _d : "transparent";
9823
+ const to = (_e = f.color2) != null ? _e : from;
9824
+ const angle = f.angle ? `${parseFloat(f.angle)}deg` : "180deg";
9825
+ background = type === "gradientradial" ? `background-image:radial-gradient(${from},${to});` : `background-image:linear-gradient(${angle},${from},${to});`;
9826
+ }
9827
+ const solid = (_f = f.color) != null ? _f : a.fillcolor;
9828
+ const tb = inner.match(/<v:textbox([^>]*)>([\s\S]*?)<\/v:textbox>/i);
9829
+ const inset = tb ? attrs(tb[1]).inset : void 0;
9830
+ const padding = inset ? inset.split(",").map((p) => {
9831
+ var _a2;
9832
+ return `${(_a2 = px(p.trim())) != null ? _a2 : 0}px`;
9833
+ }).join(" ") : "0";
9834
+ const content = tb ? tb[2] : inner.replace(/<v:fill[^>]*?\/?>/gi, "");
9835
+ const box = [
9836
+ w !== void 0 ? `width:${w}px` : "",
9837
+ h !== void 0 ? `height:${h}px` : "",
9838
+ solid ? `background-color:${solid}` : "",
9839
+ `padding:${padding}`
9840
+ ].filter(Boolean).join(";");
9841
+ return `<div data-vml="rect" style="${background}${box}">${content}</div>`;
9842
+ }
9843
+ );
9844
+ return out.replace(/<\/?v:(?:fill|textbox|stroke|shadow|imagedata|path|formulas|handles)[^>]*?\/?>/gi, "");
9845
+ }
9846
+ function renderOutlookBranch(html) {
9847
+ if (!/<!--\[if/i.test(html)) return html;
9848
+ return vmlToCss(resolveMsoBranch(html));
9849
+ }
9850
+
9743
9851
  // src/constants.ts
9744
9852
  var MAX_HTML_SIZE = 2 * 1024 * 1024;
9745
9853
  var MAX_WARNING_LOCATIONS = 100;
@@ -10463,7 +10571,8 @@ function transformForClient(html, clientId, framework) {
10463
10571
  ]
10464
10572
  };
10465
10573
  }
10466
- const downleveled = downlevelCSS(html);
10574
+ const source = clientId === "outlook-windows-legacy" ? renderOutlookBranch(html) : html;
10575
+ const downleveled = downlevelCSS(source);
10467
10576
  return applyTransform(downleveled, config, framework);
10468
10577
  }
10469
10578
  function transformForAllClients(html, framework) {
@@ -10478,8 +10587,13 @@ function transformForAllClients(html, framework) {
10478
10587
  throw new Error(`HTML input exceeds ${MAX_HTML_SIZE / 1024}KB limit.`);
10479
10588
  }
10480
10589
  const downleveled = downlevelCSS(html);
10590
+ const outlookSource = /<!--\[if/i.test(html) ? downlevelCSS(renderOutlookBranch(html)) : downleveled;
10481
10591
  return Object.keys(CLIENT_CONFIGS).map(
10482
- (clientId) => applyTransform(downleveled, CLIENT_CONFIGS[clientId], framework)
10592
+ (clientId) => applyTransform(
10593
+ clientId === "outlook-windows-legacy" ? outlookSource : downleveled,
10594
+ CLIENT_CONFIGS[clientId],
10595
+ framework
10596
+ )
10483
10597
  );
10484
10598
  }
10485
10599
 
@@ -13201,9 +13315,9 @@ function mediaApplies(prelude, ctx) {
13201
13315
  const scheme = text.match(/prefers-color-scheme\s*:\s*(dark|light)/);
13202
13316
  if (scheme && scheme[1] === "dark" !== ctx.dark) return false;
13203
13317
  for (const m of text.matchAll(/(max|min)-width\s*:\s*([^)]+)/g)) {
13204
- const px = mediaPx(m[2]);
13205
- if (px === null) continue;
13206
- if (m[1] === "max" ? ctx.width > px : ctx.width < px) return false;
13318
+ const px2 = mediaPx(m[2]);
13319
+ if (px2 === null) continue;
13320
+ if (m[1] === "max" ? ctx.width > px2 : ctx.width < px2) return false;
13207
13321
  }
13208
13322
  return true;
13209
13323
  }
@@ -13424,10 +13538,10 @@ function resolveBackground($, el, cascade) {
13424
13538
  function isLargeText(size, weight) {
13425
13539
  const match = size == null ? void 0 : size.match(/^(\d+(?:\.\d+)?)(px|pt)/i);
13426
13540
  if (!match) return false;
13427
- const px = match[2].toLowerCase() === "pt" ? parseFloat(match[1]) * 1.333 : parseFloat(match[1]);
13541
+ const px2 = match[2].toLowerCase() === "pt" ? parseFloat(match[1]) * 1.333 : parseFloat(match[1]);
13428
13542
  const w = weight == null ? void 0 : weight.trim().toLowerCase();
13429
13543
  const bold = w === "bold" || w === "bolder" || !!w && parseInt(w, 10) >= 700;
13430
- return px >= 18 || px >= 14 && bold;
13544
+ return px2 >= 18 || px2 >= 14 && bold;
13431
13545
  }
13432
13546
  function inherited($, el, cascade, prop) {
13433
13547
  var _a, _b;
@@ -14148,8 +14262,8 @@ function checkTemplateVariablesFromDom($, source) {
14148
14262
  const attrSelectors = ["[href]", "[src]", "[alt]"];
14149
14263
  for (const sel of attrSelectors) {
14150
14264
  $(sel).each((_, el) => {
14151
- const attrs = ["href", "src", "alt"];
14152
- for (const attr2 of attrs) {
14265
+ const attrs2 = ["href", "src", "alt"];
14266
+ for (const attr2 of attrs2) {
14153
14267
  const value = $(el).attr(attr2);
14154
14268
  if (!value) continue;
14155
14269
  for (const [pattern, label] of TEMPLATE_VARIABLE_PATTERNS) {
@@ -14389,9 +14503,9 @@ function locAt(source, offset, length) {
14389
14503
  length
14390
14504
  };
14391
14505
  }
14392
- function attr(attrs, name) {
14506
+ function attr(attrs2, name) {
14393
14507
  var _a;
14394
- const m = attrs.match(
14508
+ const m = attrs2.match(
14395
14509
  new RegExp(`(?:^|\\s)${name}\\s*=\\s*"([^"]*)"|(?:^|\\s)${name}\\s*=\\s*'([^']*)'`, "i")
14396
14510
  );
14397
14511
  return m ? (_a = m[1]) != null ? _a : m[2] : void 0;
@@ -14699,8 +14813,8 @@ function radiusLengths(value) {
14699
14813
  return lengths.filter((v) => {
14700
14814
  if (/^0(?:px|rem|em|%)?$/.test(v)) return false;
14701
14815
  if (v === "50%") return false;
14702
- const px = parseFloat(v);
14703
- return !(v.endsWith("px") && Number.isFinite(px) && px >= 500);
14816
+ const px2 = parseFloat(v);
14817
+ return !(v.endsWith("px") && Number.isFinite(px2) && px2 >= 500);
14704
14818
  });
14705
14819
  }
14706
14820
  function collect($) {
@@ -15095,6 +15209,7 @@ export {
15095
15209
  analyzeEmail,
15096
15210
  analyzeImages,
15097
15211
  analyzeSpam,
15212
+ arcsizeToRadius,
15098
15213
  auditEmail,
15099
15214
  caveatApplies,
15100
15215
  checkAccessibility,
@@ -15126,6 +15241,8 @@ export {
15126
15241
  heuristicTokenCount,
15127
15242
  parseColor,
15128
15243
  relativeLuminance,
15244
+ renderOutlookBranch,
15245
+ resolveMsoBranch,
15129
15246
  rgbToOklab,
15130
15247
  simulateDarkMode,
15131
15248
  structuralWarnings,
@@ -15133,6 +15250,7 @@ export {
15133
15250
  transformForAllClients,
15134
15251
  transformForClient,
15135
15252
  validateLinks,
15253
+ vmlToCss,
15136
15254
  warningsForClient,
15137
15255
  wcagGrade
15138
15256
  };