@emailens/engine 0.11.7 → 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
@@ -9959,10 +9959,23 @@ function vmlToCss(html) {
9959
9959
  solid ? `background-color:${solid}` : "",
9960
9960
  `padding:${padding}`
9961
9961
  ].filter(Boolean).join(";");
9962
+ if (/data-vml="/.test(content)) {
9963
+ return `<div data-vml="rect" data-vml-outlook="container-dropped">${content}</div>`;
9964
+ }
9962
9965
  return `<div data-vml="rect" style="${background}${box}">${content}</div>`;
9963
9966
  }
9964
9967
  );
9965
- 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;
9966
9979
  }
9967
9980
  function renderOutlookBranch(html) {
9968
9981
  if (!/<!--\[if/i.test(html)) return html;
@@ -10014,6 +10027,7 @@ var EMPTY_SPAM = { score: 100, level: "low", issues: [] };
10014
10027
  var EMPTY_LINKS = {
10015
10028
  totalLinks: 0,
10016
10029
  issues: [],
10030
+ links: [],
10017
10031
  breakdown: { https: 0, http: 0, mailto: 0, tel: 0, anchor: 0, javascript: 0, protocolRelative: 0, other: 0 }
10018
10032
  };
10019
10033
  var EMPTY_ACCESSIBILITY = { score: 100, issues: [] };
@@ -13114,6 +13128,7 @@ function isPlaceholderHref(href) {
13114
13128
  }
13115
13129
  function validateLinksFromDom($) {
13116
13130
  const issues = [];
13131
+ const inventory = [];
13117
13132
  const breakdown = { https: 0, http: 0, mailto: 0, tel: 0, anchor: 0, javascript: 0, protocolRelative: 0, other: 0 };
13118
13133
  const links = $("a");
13119
13134
  const totalLinks = links.length;
@@ -13123,13 +13138,22 @@ function validateLinksFromDom($) {
13123
13138
  rule: "no-links",
13124
13139
  message: "Email contains no links"
13125
13140
  });
13126
- return { totalLinks: 0, issues, breakdown };
13141
+ return { totalLinks: 0, issues, breakdown, links: [] };
13127
13142
  }
13128
13143
  const hrefCounts = /* @__PURE__ */ new Map();
13129
13144
  links.each((_, el) => {
13130
13145
  const href = $(el).attr("href") || "";
13131
13146
  const text = $(el).text().trim();
13132
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);
13133
13157
  const elLoc = locOfElement(el);
13134
13158
  const hrefLoc = href ? locOfAttr(el, "href") : elLoc;
13135
13159
  switch (category) {
@@ -13162,6 +13186,7 @@ function validateLinksFromDom($) {
13162
13186
  hrefCounts.set(href, (hrefCounts.get(href) || 0) + 1);
13163
13187
  }
13164
13188
  if (!href || !href.trim()) {
13189
+ flag("empty-href");
13165
13190
  issues.push(__spreadValues({
13166
13191
  severity: "error",
13167
13192
  rule: "empty-href",
@@ -13171,6 +13196,7 @@ function validateLinksFromDom($) {
13171
13196
  return;
13172
13197
  }
13173
13198
  if (category === "javascript" && !isPlaceholderHref(href)) {
13199
+ flag("javascript-href");
13174
13200
  issues.push(__spreadValues({
13175
13201
  severity: "error",
13176
13202
  rule: "javascript-href",
@@ -13181,6 +13207,7 @@ function validateLinksFromDom($) {
13181
13207
  return;
13182
13208
  }
13183
13209
  if (isPlaceholderHref(href)) {
13210
+ flag("placeholder-href");
13184
13211
  issues.push(__spreadValues({
13185
13212
  severity: "warning",
13186
13213
  rule: "placeholder-href",
@@ -13191,6 +13218,7 @@ function validateLinksFromDom($) {
13191
13218
  return;
13192
13219
  }
13193
13220
  if (category === "http") {
13221
+ flag("insecure-link");
13194
13222
  issues.push(__spreadValues({
13195
13223
  severity: "warning",
13196
13224
  rule: "insecure-link",
@@ -13200,6 +13228,7 @@ function validateLinksFromDom($) {
13200
13228
  }, hrefLoc ? { loc: hrefLoc } : {}));
13201
13229
  }
13202
13230
  if (category === "protocol-relative") {
13231
+ flag("protocol-relative");
13203
13232
  issues.push(__spreadValues({
13204
13233
  severity: "warning",
13205
13234
  rule: "protocol-relative",
@@ -13209,6 +13238,7 @@ function validateLinksFromDom($) {
13209
13238
  }, hrefLoc ? { loc: hrefLoc } : {}));
13210
13239
  }
13211
13240
  if (text && GENERIC_LINK_TEXT.has(text.toLowerCase())) {
13241
+ flag("generic-link-text");
13212
13242
  issues.push(__spreadValues({
13213
13243
  severity: "warning",
13214
13244
  rule: "generic-link-text",
@@ -13218,6 +13248,7 @@ function validateLinksFromDom($) {
13218
13248
  }, elLoc ? { loc: elLoc } : {}));
13219
13249
  }
13220
13250
  if (!text && !$(el).attr("aria-label") && !$(el).find("img[alt]").length) {
13251
+ flag("empty-link-text");
13221
13252
  issues.push(__spreadValues({
13222
13253
  severity: "error",
13223
13254
  rule: "empty-link-text",
@@ -13226,6 +13257,7 @@ function validateLinksFromDom($) {
13226
13257
  }, elLoc ? { loc: elLoc } : {}));
13227
13258
  }
13228
13259
  if (category === "mailto" && href.trim().toLowerCase() === "mailto:") {
13260
+ flag("empty-mailto");
13229
13261
  issues.push(__spreadValues({
13230
13262
  severity: "error",
13231
13263
  rule: "empty-mailto",
@@ -13235,6 +13267,7 @@ function validateLinksFromDom($) {
13235
13267
  }, hrefLoc ? { loc: hrefLoc } : {}));
13236
13268
  }
13237
13269
  if (category === "tel" && href.trim().toLowerCase() === "tel:") {
13270
+ flag("empty-tel");
13238
13271
  issues.push(__spreadValues({
13239
13272
  severity: "error",
13240
13273
  rule: "empty-tel",
@@ -13244,6 +13277,7 @@ function validateLinksFromDom($) {
13244
13277
  }, hrefLoc ? { loc: hrefLoc } : {}));
13245
13278
  }
13246
13279
  if (href.length > 2e3) {
13280
+ flag("long-url");
13247
13281
  issues.push(__spreadValues({
13248
13282
  severity: "info",
13249
13283
  rule: "long-url",
@@ -13253,7 +13287,8 @@ function validateLinksFromDom($) {
13253
13287
  }, hrefLoc ? { loc: hrefLoc } : {}));
13254
13288
  }
13255
13289
  });
13256
- links.each((_, el) => {
13290
+ links.each((i, el) => {
13291
+ var _a;
13257
13292
  const href = $(el).attr("href") || "";
13258
13293
  const trimmed = href.trim();
13259
13294
  if (trimmed.startsWith("#") && trimmed.length > 1) {
@@ -13261,6 +13296,7 @@ function validateLinksFromDom($) {
13261
13296
  const target = $(`[id="${targetId}"]`);
13262
13297
  const anchorLoc = locOfAttr(el, "href");
13263
13298
  if (target.length === 0) {
13299
+ (_a = inventory[i]) == null ? void 0 : _a.issues.push("broken-anchor");
13264
13300
  issues.push(__spreadValues({
13265
13301
  severity: "error",
13266
13302
  rule: "broken-anchor",
@@ -13281,7 +13317,7 @@ function validateLinksFromDom($) {
13281
13317
  });
13282
13318
  }
13283
13319
  }
13284
- return { totalLinks, issues, breakdown };
13320
+ return { totalLinks, issues, breakdown, links: inventory };
13285
13321
  }
13286
13322
  function validateLinks(html, options) {
13287
13323
  return fromHtml(html, EMPTY_LINKS, validateLinksFromDom, options);
@@ -14820,7 +14856,20 @@ function checkVml(html, options) {
14820
14856
  rule: "vml-nested-shape",
14821
14857
  severity: "error",
14822
14858
  message: `<v:${name}> is nested inside <v:${parent.name}>. Outlook does not support nesting one VML shape inside another.`,
14823
- 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).`
14824
14873
  }, loc));
14825
14874
  }
14826
14875
  if (!tag.selfClosing) openShapes.push(tag);