@emailens/engine 0.11.7 → 0.11.9
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/compile/index.d.cts +2 -2
- package/dist/compile/index.d.ts +2 -2
- package/dist/index.cjs +70 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -9
- package/dist/index.d.ts +29 -9
- package/dist/index.js +70 -12
- package/dist/index.js.map +1 -1
- package/dist/{react-email-BROBcC8u.d.ts → react-email-B82A1R_K.d.ts} +1 -1
- package/dist/{react-email-DuZAgJtw.d.cts → react-email-DRkmpgX-.d.cts} +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/{types-bvGDId6f.d.cts → types-DiseE-UH.d.cts} +26 -1
- package/dist/{types-bvGDId6f.d.ts → types-DiseE-UH.d.ts} +26 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { E as EmailClient, F as Framework, T as TransformResult, C as CSSWarning, a as CodeFix, D as DiffResult, b as ExportPromptOptions, A as AiProvider, c as AiFixResult, S as SupportLevel, d as SpamAnalysisOptions, e as SpamReport, L as LinkReport, f as AccessibilityReport, g as AccessibilityIssue, I as ImageReport, h as InboxPreview, i as SizeReport, j as TemplateReport, O as OverflowReport, V as VmlReport, k as VisualReport, l as DesignReport, m as DeliverabilityReport } from './types-
|
|
2
|
-
export { B as BaseIssue, n as ClientTruncation, o as DeliverabilityCheck, p as DeliverabilityIssue, q as DesignIssue, r as EstimateOptions, s as ExportScope, t as FixType, u as ImageInfo, v as ImageIssue, w as InboxPreviewIssue, x as InputFormat, y as
|
|
1
|
+
import { E as EmailClient, F as Framework, T as TransformResult, C as CSSWarning, a as CodeFix, D as DiffResult, b as ExportPromptOptions, A as AiProvider, c as AiFixResult, S as SupportLevel, d as SpamAnalysisOptions, e as SpamReport, L as LinkReport, f as AccessibilityReport, g as AccessibilityIssue, I as ImageReport, h as InboxPreview, i as SizeReport, j as TemplateReport, O as OverflowReport, V as VmlReport, k as VisualReport, l as DesignReport, m as DeliverabilityReport } from './types-DiseE-UH.cjs';
|
|
2
|
+
export { B as BaseIssue, n as ClientTruncation, o as DeliverabilityCheck, p as DeliverabilityIssue, q as DesignIssue, r as EstimateOptions, s as ExportScope, t as FixType, u as ImageInfo, v as ImageIssue, w as InboxPreviewIssue, x as InputFormat, y as LinkInfo, z as LinkIssue, G as OverflowIssue, P as PreviewResult, H as Severity, J as SizeIssue, K as SourceLocation, M as SpamIssue, N as TemplateIssue, Q as TokenEstimate, R as TokenEstimateWithWarnings, U as VisualIssue, W as VmlIssue, X as estimateAiFixTokens, Y as generateFixPrompt, Z as heuristicTokenCount } from './types-DiseE-UH.cjs';
|
|
3
3
|
import * as cheerio from 'cheerio';
|
|
4
|
-
export { C as CompileError, a as CompileReactEmailOptions, S as SandboxStrategy } from './react-email-
|
|
4
|
+
export { C as CompileError, a as CompileReactEmailOptions, S as SandboxStrategy } from './react-email-DRkmpgX-.cjs';
|
|
5
5
|
|
|
6
6
|
declare const EMAIL_CLIENTS: EmailClient[];
|
|
7
7
|
declare function getClient(id: string): EmailClient | undefined;
|
|
@@ -410,12 +410,32 @@ declare function checkVml(html: string, options?: {
|
|
|
410
410
|
* `roundrect` and `rect` are the whole surface. Anything else is left alone
|
|
411
411
|
* rather than approximated.
|
|
412
412
|
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
413
|
+
* How Word *mis*-renders a nested shape is emulated, but only the parts with
|
|
414
|
+
* evidence behind them. This note used to say the failure was not emulated at
|
|
415
|
+
* all, on the grounds that reproducing it meant perturbing Chromium into a
|
|
416
|
+
* picture wrong in a third way. That argument does not survive contact with
|
|
417
|
+
* the specific effects:
|
|
418
|
+
*
|
|
419
|
+
* - the container's fill and geometry disappear -> drop the box. Exact.
|
|
420
|
+
* - VML shapes further down stop drawing text -> blank their labels.
|
|
421
|
+
* Exact, and scoped to
|
|
422
|
+
* shapes we created.
|
|
423
|
+
* - the table structure terminates early, so later
|
|
424
|
+
* content escapes the frame -> WITHDRAWN. It had no
|
|
425
|
+
* methodology, and the
|
|
426
|
+
* fixture built to
|
|
427
|
+
* measure it contradicts
|
|
428
|
+
* it.
|
|
429
|
+
*
|
|
430
|
+
* Two of the three needed no distortion at all; the third turned out not to be
|
|
431
|
+
* a rendering problem but an evidence problem. Refusing to draw any of it
|
|
432
|
+
* meant the preview showed a tidy box with a button in it, a picture no Word
|
|
433
|
+
* engine produces, sitting beside a finding that said the region was
|
|
434
|
+
* destroyed. Between a warning and a screenshot, people believe the
|
|
435
|
+
* screenshot.
|
|
436
|
+
*
|
|
437
|
+
* Affected regions carry `data-vml-outlook` so a surface can label them, since
|
|
438
|
+
* a blank button should read as "Outlook does this" and not as our bug.
|
|
419
439
|
*/
|
|
420
440
|
/**
|
|
421
441
|
* `arcsize` is a VgFraction: a proportion of *half the shape's shorter side*,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { E as EmailClient, F as Framework, T as TransformResult, C as CSSWarning, a as CodeFix, D as DiffResult, b as ExportPromptOptions, A as AiProvider, c as AiFixResult, S as SupportLevel, d as SpamAnalysisOptions, e as SpamReport, L as LinkReport, f as AccessibilityReport, g as AccessibilityIssue, I as ImageReport, h as InboxPreview, i as SizeReport, j as TemplateReport, O as OverflowReport, V as VmlReport, k as VisualReport, l as DesignReport, m as DeliverabilityReport } from './types-
|
|
2
|
-
export { B as BaseIssue, n as ClientTruncation, o as DeliverabilityCheck, p as DeliverabilityIssue, q as DesignIssue, r as EstimateOptions, s as ExportScope, t as FixType, u as ImageInfo, v as ImageIssue, w as InboxPreviewIssue, x as InputFormat, y as
|
|
1
|
+
import { E as EmailClient, F as Framework, T as TransformResult, C as CSSWarning, a as CodeFix, D as DiffResult, b as ExportPromptOptions, A as AiProvider, c as AiFixResult, S as SupportLevel, d as SpamAnalysisOptions, e as SpamReport, L as LinkReport, f as AccessibilityReport, g as AccessibilityIssue, I as ImageReport, h as InboxPreview, i as SizeReport, j as TemplateReport, O as OverflowReport, V as VmlReport, k as VisualReport, l as DesignReport, m as DeliverabilityReport } from './types-DiseE-UH.js';
|
|
2
|
+
export { B as BaseIssue, n as ClientTruncation, o as DeliverabilityCheck, p as DeliverabilityIssue, q as DesignIssue, r as EstimateOptions, s as ExportScope, t as FixType, u as ImageInfo, v as ImageIssue, w as InboxPreviewIssue, x as InputFormat, y as LinkInfo, z as LinkIssue, G as OverflowIssue, P as PreviewResult, H as Severity, J as SizeIssue, K as SourceLocation, M as SpamIssue, N as TemplateIssue, Q as TokenEstimate, R as TokenEstimateWithWarnings, U as VisualIssue, W as VmlIssue, X as estimateAiFixTokens, Y as generateFixPrompt, Z as heuristicTokenCount } from './types-DiseE-UH.js';
|
|
3
3
|
import * as cheerio from 'cheerio';
|
|
4
|
-
export { C as CompileError, a as CompileReactEmailOptions, S as SandboxStrategy } from './react-email-
|
|
4
|
+
export { C as CompileError, a as CompileReactEmailOptions, S as SandboxStrategy } from './react-email-B82A1R_K.js';
|
|
5
5
|
|
|
6
6
|
declare const EMAIL_CLIENTS: EmailClient[];
|
|
7
7
|
declare function getClient(id: string): EmailClient | undefined;
|
|
@@ -410,12 +410,32 @@ declare function checkVml(html: string, options?: {
|
|
|
410
410
|
* `roundrect` and `rect` are the whole surface. Anything else is left alone
|
|
411
411
|
* rather than approximated.
|
|
412
412
|
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
413
|
+
* How Word *mis*-renders a nested shape is emulated, but only the parts with
|
|
414
|
+
* evidence behind them. This note used to say the failure was not emulated at
|
|
415
|
+
* all, on the grounds that reproducing it meant perturbing Chromium into a
|
|
416
|
+
* picture wrong in a third way. That argument does not survive contact with
|
|
417
|
+
* the specific effects:
|
|
418
|
+
*
|
|
419
|
+
* - the container's fill and geometry disappear -> drop the box. Exact.
|
|
420
|
+
* - VML shapes further down stop drawing text -> blank their labels.
|
|
421
|
+
* Exact, and scoped to
|
|
422
|
+
* shapes we created.
|
|
423
|
+
* - the table structure terminates early, so later
|
|
424
|
+
* content escapes the frame -> WITHDRAWN. It had no
|
|
425
|
+
* methodology, and the
|
|
426
|
+
* fixture built to
|
|
427
|
+
* measure it contradicts
|
|
428
|
+
* it.
|
|
429
|
+
*
|
|
430
|
+
* Two of the three needed no distortion at all; the third turned out not to be
|
|
431
|
+
* a rendering problem but an evidence problem. Refusing to draw any of it
|
|
432
|
+
* meant the preview showed a tidy box with a button in it, a picture no Word
|
|
433
|
+
* engine produces, sitting beside a finding that said the region was
|
|
434
|
+
* destroyed. Between a warning and a screenshot, people believe the
|
|
435
|
+
* screenshot.
|
|
436
|
+
*
|
|
437
|
+
* Affected regions carry `data-vml-outlook` so a surface can label them, since
|
|
438
|
+
* a blank button should read as "Outlook does this" and not as our bug.
|
|
419
439
|
*/
|
|
420
440
|
/**
|
|
421
441
|
* `arcsize` is a VgFraction: a proportion of *half the shape's shorter side*,
|
package/dist/index.js
CHANGED
|
@@ -8949,10 +8949,6 @@ function splitTopLevel(value) {
|
|
|
8949
8949
|
return parts;
|
|
8950
8950
|
}
|
|
8951
8951
|
|
|
8952
|
-
// src/downlevel.ts
|
|
8953
|
-
import * as cheerio from "cheerio";
|
|
8954
|
-
import * as csstree from "css-tree";
|
|
8955
|
-
|
|
8956
8952
|
// src/color-utils.ts
|
|
8957
8953
|
var NAMED_COLORS = {
|
|
8958
8954
|
aliceblue: [240, 248, 255],
|
|
@@ -9390,6 +9386,8 @@ function colorDistance(a, b) {
|
|
|
9390
9386
|
}
|
|
9391
9387
|
|
|
9392
9388
|
// src/downlevel.ts
|
|
9389
|
+
import * as cheerio from "cheerio";
|
|
9390
|
+
import * as csstree from "css-tree";
|
|
9393
9391
|
var LOGICAL_PROPERTY_MAP = {
|
|
9394
9392
|
"padding-inline": ["padding-left", "padding-right"],
|
|
9395
9393
|
"padding-block": ["padding-top", "padding-bottom"],
|
|
@@ -9840,10 +9838,23 @@ function vmlToCss(html) {
|
|
|
9840
9838
|
solid ? `background-color:${solid}` : "",
|
|
9841
9839
|
`padding:${padding}`
|
|
9842
9840
|
].filter(Boolean).join(";");
|
|
9841
|
+
if (/data-vml="/.test(content)) {
|
|
9842
|
+
return `<div data-vml="rect" data-vml-outlook="container-dropped">${content}</div>`;
|
|
9843
|
+
}
|
|
9843
9844
|
return `<div data-vml="rect" style="${background}${box}">${content}</div>`;
|
|
9844
9845
|
}
|
|
9845
9846
|
);
|
|
9846
|
-
|
|
9847
|
+
out = out.replace(/<\/?v:(?:fill|textbox|stroke|shadow|imagedata|path|formulas|handles)[^>]*?\/?>/gi, "");
|
|
9848
|
+
return blankLabelsAfterNestedShape(out);
|
|
9849
|
+
}
|
|
9850
|
+
function blankLabelsAfterNestedShape(html) {
|
|
9851
|
+
const at = html.indexOf('data-vml-outlook="container-dropped"');
|
|
9852
|
+
if (at === -1) return html;
|
|
9853
|
+
const tail = html.slice(at).replace(
|
|
9854
|
+
/(<div data-vml="roundrect")([^>]*>)([\s\S]*?)(<\/div>)/gi,
|
|
9855
|
+
(whole, open, rest, label, close) => label.trim() ? `${open}${rest.slice(0, -1)} data-vml-outlook="label-dropped">${close}` : whole
|
|
9856
|
+
);
|
|
9857
|
+
return html.slice(0, at) + tail;
|
|
9847
9858
|
}
|
|
9848
9859
|
function renderOutlookBranch(html) {
|
|
9849
9860
|
if (!/<!--\[if/i.test(html)) return html;
|
|
@@ -9895,6 +9906,7 @@ var EMPTY_SPAM = { score: 100, level: "low", issues: [] };
|
|
|
9895
9906
|
var EMPTY_LINKS = {
|
|
9896
9907
|
totalLinks: 0,
|
|
9897
9908
|
issues: [],
|
|
9909
|
+
links: [],
|
|
9898
9910
|
breakdown: { https: 0, http: 0, mailto: 0, tel: 0, anchor: 0, javascript: 0, protocolRelative: 0, other: 0 }
|
|
9899
9911
|
};
|
|
9900
9912
|
var EMPTY_ACCESSIBILITY = { score: 100, issues: [] };
|
|
@@ -10274,13 +10286,17 @@ var CLIENT_CONFIGS = {
|
|
|
10274
10286
|
strippedProperties: OUTLOOK_WORD_UNSUPPORTED,
|
|
10275
10287
|
stripMode: "strip",
|
|
10276
10288
|
valueStrips: [
|
|
10277
|
-
|
|
10278
|
-
|
|
10289
|
+
// Gradients and image URLs alike: the Word engine paints neither. Only
|
|
10290
|
+
// VML does, and vmlToCss runs after this pass, so a hero built the
|
|
10291
|
+
// bulletproof way keeps the background this strip would otherwise take.
|
|
10292
|
+
{ prop: "background", pattern: /linear-gradient|radial-gradient|url\(/ },
|
|
10293
|
+
{ prop: "background-image", pattern: /linear-gradient|radial-gradient|url\(/ }
|
|
10279
10294
|
],
|
|
10280
10295
|
inlineAndStripStyles: false,
|
|
10281
10296
|
stripExternalStylesheets: false,
|
|
10282
10297
|
stripForms: false,
|
|
10283
10298
|
stripSvg: false,
|
|
10299
|
+
stripBackgroundAttribute: true,
|
|
10284
10300
|
additionalChecks: outlookWindowsAdditionalChecks
|
|
10285
10301
|
},
|
|
10286
10302
|
"outlook-web": {
|
|
@@ -10501,6 +10517,11 @@ function applyTransform(html, config, framework) {
|
|
|
10501
10517
|
$(el).replaceWith('<img alt="[SVG not supported]" />');
|
|
10502
10518
|
});
|
|
10503
10519
|
}
|
|
10520
|
+
if (config.stripBackgroundAttribute) {
|
|
10521
|
+
$("[background]").each((_, el) => {
|
|
10522
|
+
$(el).removeAttr("background");
|
|
10523
|
+
});
|
|
10524
|
+
}
|
|
10504
10525
|
if (config.strippedProperties.size > 0 || config.valueStrips && config.valueStrips.length > 0) {
|
|
10505
10526
|
$("[style]").each((_, el) => {
|
|
10506
10527
|
const style = $(el).attr("style") || "";
|
|
@@ -10525,7 +10546,9 @@ function applyTransform(html, config, framework) {
|
|
|
10525
10546
|
for (const vs of (_a = config.valueStrips) != null ? _a : []) {
|
|
10526
10547
|
if (prop === vs.prop && vs.pattern.test(value)) {
|
|
10527
10548
|
removed.push(prop);
|
|
10528
|
-
|
|
10549
|
+
const fallback = prop === "background" ? backgroundShorthandColor(value) : null;
|
|
10550
|
+
if (fallback) props.set(prop, fallback);
|
|
10551
|
+
else props.delete(prop);
|
|
10529
10552
|
return;
|
|
10530
10553
|
}
|
|
10531
10554
|
}
|
|
@@ -12995,6 +13018,7 @@ function isPlaceholderHref(href) {
|
|
|
12995
13018
|
}
|
|
12996
13019
|
function validateLinksFromDom($) {
|
|
12997
13020
|
const issues = [];
|
|
13021
|
+
const inventory = [];
|
|
12998
13022
|
const breakdown = { https: 0, http: 0, mailto: 0, tel: 0, anchor: 0, javascript: 0, protocolRelative: 0, other: 0 };
|
|
12999
13023
|
const links = $("a");
|
|
13000
13024
|
const totalLinks = links.length;
|
|
@@ -13004,13 +13028,22 @@ function validateLinksFromDom($) {
|
|
|
13004
13028
|
rule: "no-links",
|
|
13005
13029
|
message: "Email contains no links"
|
|
13006
13030
|
});
|
|
13007
|
-
return { totalLinks: 0, issues, breakdown };
|
|
13031
|
+
return { totalLinks: 0, issues, breakdown, links: [] };
|
|
13008
13032
|
}
|
|
13009
13033
|
const hrefCounts = /* @__PURE__ */ new Map();
|
|
13010
13034
|
links.each((_, el) => {
|
|
13011
13035
|
const href = $(el).attr("href") || "";
|
|
13012
13036
|
const text = $(el).text().trim();
|
|
13013
13037
|
const category = classifyHref(href);
|
|
13038
|
+
const entry = {
|
|
13039
|
+
href,
|
|
13040
|
+
text: text.slice(0, 80),
|
|
13041
|
+
scheme: category,
|
|
13042
|
+
isPlaceholder: isPlaceholderHref(href),
|
|
13043
|
+
issues: []
|
|
13044
|
+
};
|
|
13045
|
+
inventory.push(entry);
|
|
13046
|
+
const flag = (rule) => entry.issues.push(rule);
|
|
13014
13047
|
const elLoc = locOfElement(el);
|
|
13015
13048
|
const hrefLoc = href ? locOfAttr(el, "href") : elLoc;
|
|
13016
13049
|
switch (category) {
|
|
@@ -13043,6 +13076,7 @@ function validateLinksFromDom($) {
|
|
|
13043
13076
|
hrefCounts.set(href, (hrefCounts.get(href) || 0) + 1);
|
|
13044
13077
|
}
|
|
13045
13078
|
if (!href || !href.trim()) {
|
|
13079
|
+
flag("empty-href");
|
|
13046
13080
|
issues.push(__spreadValues({
|
|
13047
13081
|
severity: "error",
|
|
13048
13082
|
rule: "empty-href",
|
|
@@ -13052,6 +13086,7 @@ function validateLinksFromDom($) {
|
|
|
13052
13086
|
return;
|
|
13053
13087
|
}
|
|
13054
13088
|
if (category === "javascript" && !isPlaceholderHref(href)) {
|
|
13089
|
+
flag("javascript-href");
|
|
13055
13090
|
issues.push(__spreadValues({
|
|
13056
13091
|
severity: "error",
|
|
13057
13092
|
rule: "javascript-href",
|
|
@@ -13062,6 +13097,7 @@ function validateLinksFromDom($) {
|
|
|
13062
13097
|
return;
|
|
13063
13098
|
}
|
|
13064
13099
|
if (isPlaceholderHref(href)) {
|
|
13100
|
+
flag("placeholder-href");
|
|
13065
13101
|
issues.push(__spreadValues({
|
|
13066
13102
|
severity: "warning",
|
|
13067
13103
|
rule: "placeholder-href",
|
|
@@ -13072,6 +13108,7 @@ function validateLinksFromDom($) {
|
|
|
13072
13108
|
return;
|
|
13073
13109
|
}
|
|
13074
13110
|
if (category === "http") {
|
|
13111
|
+
flag("insecure-link");
|
|
13075
13112
|
issues.push(__spreadValues({
|
|
13076
13113
|
severity: "warning",
|
|
13077
13114
|
rule: "insecure-link",
|
|
@@ -13081,6 +13118,7 @@ function validateLinksFromDom($) {
|
|
|
13081
13118
|
}, hrefLoc ? { loc: hrefLoc } : {}));
|
|
13082
13119
|
}
|
|
13083
13120
|
if (category === "protocol-relative") {
|
|
13121
|
+
flag("protocol-relative");
|
|
13084
13122
|
issues.push(__spreadValues({
|
|
13085
13123
|
severity: "warning",
|
|
13086
13124
|
rule: "protocol-relative",
|
|
@@ -13090,6 +13128,7 @@ function validateLinksFromDom($) {
|
|
|
13090
13128
|
}, hrefLoc ? { loc: hrefLoc } : {}));
|
|
13091
13129
|
}
|
|
13092
13130
|
if (text && GENERIC_LINK_TEXT.has(text.toLowerCase())) {
|
|
13131
|
+
flag("generic-link-text");
|
|
13093
13132
|
issues.push(__spreadValues({
|
|
13094
13133
|
severity: "warning",
|
|
13095
13134
|
rule: "generic-link-text",
|
|
@@ -13099,6 +13138,7 @@ function validateLinksFromDom($) {
|
|
|
13099
13138
|
}, elLoc ? { loc: elLoc } : {}));
|
|
13100
13139
|
}
|
|
13101
13140
|
if (!text && !$(el).attr("aria-label") && !$(el).find("img[alt]").length) {
|
|
13141
|
+
flag("empty-link-text");
|
|
13102
13142
|
issues.push(__spreadValues({
|
|
13103
13143
|
severity: "error",
|
|
13104
13144
|
rule: "empty-link-text",
|
|
@@ -13107,6 +13147,7 @@ function validateLinksFromDom($) {
|
|
|
13107
13147
|
}, elLoc ? { loc: elLoc } : {}));
|
|
13108
13148
|
}
|
|
13109
13149
|
if (category === "mailto" && href.trim().toLowerCase() === "mailto:") {
|
|
13150
|
+
flag("empty-mailto");
|
|
13110
13151
|
issues.push(__spreadValues({
|
|
13111
13152
|
severity: "error",
|
|
13112
13153
|
rule: "empty-mailto",
|
|
@@ -13116,6 +13157,7 @@ function validateLinksFromDom($) {
|
|
|
13116
13157
|
}, hrefLoc ? { loc: hrefLoc } : {}));
|
|
13117
13158
|
}
|
|
13118
13159
|
if (category === "tel" && href.trim().toLowerCase() === "tel:") {
|
|
13160
|
+
flag("empty-tel");
|
|
13119
13161
|
issues.push(__spreadValues({
|
|
13120
13162
|
severity: "error",
|
|
13121
13163
|
rule: "empty-tel",
|
|
@@ -13125,6 +13167,7 @@ function validateLinksFromDom($) {
|
|
|
13125
13167
|
}, hrefLoc ? { loc: hrefLoc } : {}));
|
|
13126
13168
|
}
|
|
13127
13169
|
if (href.length > 2e3) {
|
|
13170
|
+
flag("long-url");
|
|
13128
13171
|
issues.push(__spreadValues({
|
|
13129
13172
|
severity: "info",
|
|
13130
13173
|
rule: "long-url",
|
|
@@ -13134,7 +13177,8 @@ function validateLinksFromDom($) {
|
|
|
13134
13177
|
}, hrefLoc ? { loc: hrefLoc } : {}));
|
|
13135
13178
|
}
|
|
13136
13179
|
});
|
|
13137
|
-
links.each((
|
|
13180
|
+
links.each((i, el) => {
|
|
13181
|
+
var _a;
|
|
13138
13182
|
const href = $(el).attr("href") || "";
|
|
13139
13183
|
const trimmed = href.trim();
|
|
13140
13184
|
if (trimmed.startsWith("#") && trimmed.length > 1) {
|
|
@@ -13142,6 +13186,7 @@ function validateLinksFromDom($) {
|
|
|
13142
13186
|
const target = $(`[id="${targetId}"]`);
|
|
13143
13187
|
const anchorLoc = locOfAttr(el, "href");
|
|
13144
13188
|
if (target.length === 0) {
|
|
13189
|
+
(_a = inventory[i]) == null ? void 0 : _a.issues.push("broken-anchor");
|
|
13145
13190
|
issues.push(__spreadValues({
|
|
13146
13191
|
severity: "error",
|
|
13147
13192
|
rule: "broken-anchor",
|
|
@@ -13162,7 +13207,7 @@ function validateLinksFromDom($) {
|
|
|
13162
13207
|
});
|
|
13163
13208
|
}
|
|
13164
13209
|
}
|
|
13165
|
-
return { totalLinks, issues, breakdown };
|
|
13210
|
+
return { totalLinks, issues, breakdown, links: inventory };
|
|
13166
13211
|
}
|
|
13167
13212
|
function validateLinks(html, options) {
|
|
13168
13213
|
return fromHtml(html, EMPTY_LINKS, validateLinksFromDom, options);
|
|
@@ -14701,7 +14746,20 @@ function checkVml(html, options) {
|
|
|
14701
14746
|
rule: "vml-nested-shape",
|
|
14702
14747
|
severity: "error",
|
|
14703
14748
|
message: `<v:${name}> is nested inside <v:${parent.name}>. Outlook does not support nesting one VML shape inside another.`,
|
|
14704
|
-
|
|
14749
|
+
// Two effects, not the three this used to claim. The withdrawn one
|
|
14750
|
+
// said the table structure terminates early so content after the
|
|
14751
|
+
// shape falls outside the email frame. It had no methodology behind
|
|
14752
|
+
// it, and `T1c-blast-radius.html`, the fixture built to measure
|
|
14753
|
+
// exactly this, contradicts it: the block after the nested shape
|
|
14754
|
+
// rendered in place inside the same table with its background and
|
|
14755
|
+
// borders intact, and only the VML labels were missing. The likely
|
|
14756
|
+
// origin is over-reading "layout breaks after it" from a run where a
|
|
14757
|
+
// 600px hero band vanished and everything below shifted up.
|
|
14758
|
+
//
|
|
14759
|
+
// Kept as a warning to whoever edits this next: the probe sentence
|
|
14760
|
+
// below covers the downstream text suppression only. Do not let it
|
|
14761
|
+
// lend its authority to a neighbouring clause again.
|
|
14762
|
+
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).`
|
|
14705
14763
|
}, loc));
|
|
14706
14764
|
}
|
|
14707
14765
|
if (!tag.selfClosing) openShapes.push(tag);
|