@emailens/engine 0.11.6 → 0.11.8

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.
@@ -1,5 +1,5 @@
1
- import { x as InputFormat } from '../types-bvGDId6f.cjs';
2
- export { C as CompileError, a as CompileReactEmailOptions, S as SandboxStrategy, c as compileReactEmail } from '../react-email-DuZAgJtw.cjs';
1
+ import { x as InputFormat } from '../types-DiseE-UH.cjs';
2
+ export { C as CompileError, a as CompileReactEmailOptions, S as SandboxStrategy, c as compileReactEmail } from '../react-email-DRkmpgX-.cjs';
3
3
 
4
4
  /**
5
5
  * Compile an MJML source string into an HTML email string.
@@ -1,5 +1,5 @@
1
- import { x as InputFormat } from '../types-bvGDId6f.js';
2
- export { C as CompileError, a as CompileReactEmailOptions, S as SandboxStrategy, c as compileReactEmail } from '../react-email-BROBcC8u.js';
1
+ import { x as InputFormat } from '../types-DiseE-UH.js';
2
+ export { C as CompileError, a as CompileReactEmailOptions, S as SandboxStrategy, c as compileReactEmail } from '../react-email-B82A1R_K.js';
3
3
 
4
4
  /**
5
5
  * Compile an MJML source string into an HTML email string.
package/dist/index.cjs CHANGED
@@ -9887,8 +9887,10 @@ function arcsizeToRadius(arcsize, width, height) {
9887
9887
  if (!Number.isFinite(pct)) return 0;
9888
9888
  return Math.min(100, Math.max(0, pct)) / 100 * (Math.min(width, height) / 2);
9889
9889
  }
9890
+ var DOWNLEVEL_REVEALED = /<!--\[if[^\]]*!\s*(?:mso|vml)[^\]]*\]>\s*<!--(?:>|\s*-->)([\s\S]*?)<!--\s*<!\[endif\]-->/gi;
9891
+ var DOWNLEVEL_HIDDEN = /<!--\[if(?![^\]]*!)[^\]]*(?:mso|vml)[^\]]*\]>([\s\S]*?)<!\[endif\]-->/gi;
9890
9892
  function resolveMsoBranch(html) {
9891
- return html.replace(/<!--\[if\s*!\s*(?:mso|vml)[^\]]*\]><!-->([\s\S]*?)<!--<!\[endif\]-->/gi, "").replace(/<!--\[if[^\]]*(?:mso|vml)[^\]]*\]>([\s\S]*?)<!\[endif\]-->/gi, "$1");
9893
+ return html.replace(DOWNLEVEL_REVEALED, "").replace(DOWNLEVEL_HIDDEN, "$1");
9892
9894
  }
9893
9895
  function vmlToCss(html) {
9894
9896
  let out = html;
@@ -9957,10 +9959,23 @@ function vmlToCss(html) {
9957
9959
  solid ? `background-color:${solid}` : "",
9958
9960
  `padding:${padding}`
9959
9961
  ].filter(Boolean).join(";");
9962
+ if (/data-vml="/.test(content)) {
9963
+ return `<div data-vml="rect" data-vml-outlook="container-dropped">${content}</div>`;
9964
+ }
9960
9965
  return `<div data-vml="rect" style="${background}${box}">${content}</div>`;
9961
9966
  }
9962
9967
  );
9963
- return out.replace(/<\/?v:(?:fill|textbox|stroke|shadow|imagedata|path|formulas|handles)[^>]*?\/?>/gi, "");
9968
+ out = out.replace(/<\/?v:(?:fill|textbox|stroke|shadow|imagedata|path|formulas|handles)[^>]*?\/?>/gi, "");
9969
+ return blankLabelsAfterNestedShape(out);
9970
+ }
9971
+ function blankLabelsAfterNestedShape(html) {
9972
+ const at = html.indexOf('data-vml-outlook="container-dropped"');
9973
+ if (at === -1) return html;
9974
+ const tail = html.slice(at).replace(
9975
+ /(<div data-vml="roundrect")([^>]*>)([\s\S]*?)(<\/div>)/gi,
9976
+ (whole, open, rest, label, close) => label.trim() ? `${open}${rest.slice(0, -1)} data-vml-outlook="label-dropped">${close}` : whole
9977
+ );
9978
+ return html.slice(0, at) + tail;
9964
9979
  }
9965
9980
  function renderOutlookBranch(html) {
9966
9981
  if (!/<!--\[if/i.test(html)) return html;
@@ -10012,6 +10027,7 @@ var EMPTY_SPAM = { score: 100, level: "low", issues: [] };
10012
10027
  var EMPTY_LINKS = {
10013
10028
  totalLinks: 0,
10014
10029
  issues: [],
10030
+ links: [],
10015
10031
  breakdown: { https: 0, http: 0, mailto: 0, tel: 0, anchor: 0, javascript: 0, protocolRelative: 0, other: 0 }
10016
10032
  };
10017
10033
  var EMPTY_ACCESSIBILITY = { score: 100, issues: [] };
@@ -13112,6 +13128,7 @@ function isPlaceholderHref(href) {
13112
13128
  }
13113
13129
  function validateLinksFromDom($) {
13114
13130
  const issues = [];
13131
+ const inventory = [];
13115
13132
  const breakdown = { https: 0, http: 0, mailto: 0, tel: 0, anchor: 0, javascript: 0, protocolRelative: 0, other: 0 };
13116
13133
  const links = $("a");
13117
13134
  const totalLinks = links.length;
@@ -13121,13 +13138,22 @@ function validateLinksFromDom($) {
13121
13138
  rule: "no-links",
13122
13139
  message: "Email contains no links"
13123
13140
  });
13124
- return { totalLinks: 0, issues, breakdown };
13141
+ return { totalLinks: 0, issues, breakdown, links: [] };
13125
13142
  }
13126
13143
  const hrefCounts = /* @__PURE__ */ new Map();
13127
13144
  links.each((_, el) => {
13128
13145
  const href = $(el).attr("href") || "";
13129
13146
  const text = $(el).text().trim();
13130
13147
  const category = classifyHref(href);
13148
+ const entry = {
13149
+ href,
13150
+ text: text.slice(0, 80),
13151
+ scheme: category,
13152
+ isPlaceholder: isPlaceholderHref(href),
13153
+ issues: []
13154
+ };
13155
+ inventory.push(entry);
13156
+ const flag = (rule) => entry.issues.push(rule);
13131
13157
  const elLoc = locOfElement(el);
13132
13158
  const hrefLoc = href ? locOfAttr(el, "href") : elLoc;
13133
13159
  switch (category) {
@@ -13160,6 +13186,7 @@ function validateLinksFromDom($) {
13160
13186
  hrefCounts.set(href, (hrefCounts.get(href) || 0) + 1);
13161
13187
  }
13162
13188
  if (!href || !href.trim()) {
13189
+ flag("empty-href");
13163
13190
  issues.push(__spreadValues({
13164
13191
  severity: "error",
13165
13192
  rule: "empty-href",
@@ -13169,6 +13196,7 @@ function validateLinksFromDom($) {
13169
13196
  return;
13170
13197
  }
13171
13198
  if (category === "javascript" && !isPlaceholderHref(href)) {
13199
+ flag("javascript-href");
13172
13200
  issues.push(__spreadValues({
13173
13201
  severity: "error",
13174
13202
  rule: "javascript-href",
@@ -13179,6 +13207,7 @@ function validateLinksFromDom($) {
13179
13207
  return;
13180
13208
  }
13181
13209
  if (isPlaceholderHref(href)) {
13210
+ flag("placeholder-href");
13182
13211
  issues.push(__spreadValues({
13183
13212
  severity: "warning",
13184
13213
  rule: "placeholder-href",
@@ -13189,6 +13218,7 @@ function validateLinksFromDom($) {
13189
13218
  return;
13190
13219
  }
13191
13220
  if (category === "http") {
13221
+ flag("insecure-link");
13192
13222
  issues.push(__spreadValues({
13193
13223
  severity: "warning",
13194
13224
  rule: "insecure-link",
@@ -13198,6 +13228,7 @@ function validateLinksFromDom($) {
13198
13228
  }, hrefLoc ? { loc: hrefLoc } : {}));
13199
13229
  }
13200
13230
  if (category === "protocol-relative") {
13231
+ flag("protocol-relative");
13201
13232
  issues.push(__spreadValues({
13202
13233
  severity: "warning",
13203
13234
  rule: "protocol-relative",
@@ -13207,6 +13238,7 @@ function validateLinksFromDom($) {
13207
13238
  }, hrefLoc ? { loc: hrefLoc } : {}));
13208
13239
  }
13209
13240
  if (text && GENERIC_LINK_TEXT.has(text.toLowerCase())) {
13241
+ flag("generic-link-text");
13210
13242
  issues.push(__spreadValues({
13211
13243
  severity: "warning",
13212
13244
  rule: "generic-link-text",
@@ -13216,6 +13248,7 @@ function validateLinksFromDom($) {
13216
13248
  }, elLoc ? { loc: elLoc } : {}));
13217
13249
  }
13218
13250
  if (!text && !$(el).attr("aria-label") && !$(el).find("img[alt]").length) {
13251
+ flag("empty-link-text");
13219
13252
  issues.push(__spreadValues({
13220
13253
  severity: "error",
13221
13254
  rule: "empty-link-text",
@@ -13224,6 +13257,7 @@ function validateLinksFromDom($) {
13224
13257
  }, elLoc ? { loc: elLoc } : {}));
13225
13258
  }
13226
13259
  if (category === "mailto" && href.trim().toLowerCase() === "mailto:") {
13260
+ flag("empty-mailto");
13227
13261
  issues.push(__spreadValues({
13228
13262
  severity: "error",
13229
13263
  rule: "empty-mailto",
@@ -13233,6 +13267,7 @@ function validateLinksFromDom($) {
13233
13267
  }, hrefLoc ? { loc: hrefLoc } : {}));
13234
13268
  }
13235
13269
  if (category === "tel" && href.trim().toLowerCase() === "tel:") {
13270
+ flag("empty-tel");
13236
13271
  issues.push(__spreadValues({
13237
13272
  severity: "error",
13238
13273
  rule: "empty-tel",
@@ -13242,6 +13277,7 @@ function validateLinksFromDom($) {
13242
13277
  }, hrefLoc ? { loc: hrefLoc } : {}));
13243
13278
  }
13244
13279
  if (href.length > 2e3) {
13280
+ flag("long-url");
13245
13281
  issues.push(__spreadValues({
13246
13282
  severity: "info",
13247
13283
  rule: "long-url",
@@ -13251,7 +13287,8 @@ function validateLinksFromDom($) {
13251
13287
  }, hrefLoc ? { loc: hrefLoc } : {}));
13252
13288
  }
13253
13289
  });
13254
- links.each((_, el) => {
13290
+ links.each((i, el) => {
13291
+ var _a;
13255
13292
  const href = $(el).attr("href") || "";
13256
13293
  const trimmed = href.trim();
13257
13294
  if (trimmed.startsWith("#") && trimmed.length > 1) {
@@ -13259,6 +13296,7 @@ function validateLinksFromDom($) {
13259
13296
  const target = $(`[id="${targetId}"]`);
13260
13297
  const anchorLoc = locOfAttr(el, "href");
13261
13298
  if (target.length === 0) {
13299
+ (_a = inventory[i]) == null ? void 0 : _a.issues.push("broken-anchor");
13262
13300
  issues.push(__spreadValues({
13263
13301
  severity: "error",
13264
13302
  rule: "broken-anchor",
@@ -13279,7 +13317,7 @@ function validateLinksFromDom($) {
13279
13317
  });
13280
13318
  }
13281
13319
  }
13282
- return { totalLinks, issues, breakdown };
13320
+ return { totalLinks, issues, breakdown, links: inventory };
13283
13321
  }
13284
13322
  function validateLinks(html, options) {
13285
13323
  return fromHtml(html, EMPTY_LINKS, validateLinksFromDom, options);
@@ -14818,7 +14856,20 @@ function checkVml(html, options) {
14818
14856
  rule: "vml-nested-shape",
14819
14857
  severity: "error",
14820
14858
  message: `<v:${name}> is nested inside <v:${parent.name}>. Outlook does not support nesting one VML shape inside another.`,
14821
- detail: `Verified in Outlook Classic, and the damage is not local. Three things happen: the containing <v:${parent.name}> does not render at all (its fill and everything inside it disappear, leaving the inner shape stranded); the table structure around it terminates early, so content *after* the shape falls out of the layout and renders outside the email frame; and every VML shape further down the email stops drawing its text, so buttons and headings after this point ship as blank coloured blocks. Confirmed with byte-identical probes either side of one nested shape: the one before it renders its labels, the one after it does not. Every other client renders the HTML fallback correctly, which is why none of this is visible outside the Word engine. Fix: lift the inner shape out of <v:${parent.name}>, or drop the container shape and keep the inner one (a framed background can degrade to a solid fill colour on the <td> instead).`
14859
+ // Two effects, not the three this used to claim. The withdrawn one
14860
+ // said the table structure terminates early so content after the
14861
+ // shape falls outside the email frame. It had no methodology behind
14862
+ // it, and `T1c-blast-radius.html`, the fixture built to measure
14863
+ // exactly this, contradicts it: the block after the nested shape
14864
+ // rendered in place inside the same table with its background and
14865
+ // borders intact, and only the VML labels were missing. The likely
14866
+ // origin is over-reading "layout breaks after it" from a run where a
14867
+ // 600px hero band vanished and everything below shifted up.
14868
+ //
14869
+ // Kept as a warning to whoever edits this next: the probe sentence
14870
+ // below covers the downstream text suppression only. Do not let it
14871
+ // lend its authority to a neighbouring clause again.
14872
+ detail: `Verified in Outlook Classic, and the damage is not local. Two things happen: the containing <v:${parent.name}> does not render at all (its fill and everything inside it disappear, leaving the inner shape stranded); and every VML shape further down the email stops drawing its text, so buttons and headings after this point ship as blank coloured blocks. That second effect was confirmed with byte-identical probes either side of one nested shape: the one before it renders its labels, the one after it does not. Every other client renders the HTML fallback correctly, which is why none of this is visible outside the Word engine. Fix: lift the inner shape out of <v:${parent.name}>, or drop the container shape and keep the inner one (a framed background can degrade to a solid fill colour on the <td> instead).`
14822
14873
  }, loc));
14823
14874
  }
14824
14875
  if (!tag.selfClosing) openShapes.push(tag);